Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .fern/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
"skipResponseValidation": true,
"namespaceExport": "Speechify"
},
"originGitCommit": "9f50f669dbb3d0db633a6d904db8a5077b94764f",
"originGitCommitIsDirty": true,
"originGitCommit": "d18d0fff37ec3446bf44071a930fcad840b961e6",
"originGitCommitIsDirty": false,
"invokedBy": "ci",
"ciProvider": "unknown",
"sdkVersion": "2.0.1"
"ciProvider": "github",
"sdkVersion": "3.0.2"
}
70 changes: 70 additions & 0 deletions .fernignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,75 @@ release-please-config.json
.release-please-manifest.json
CHANGELOG.md

# Manual workflow_dispatch publisher for an already-tagged release whose
# automatic publish did not run. Not generated by Fern — keep it listed or
# regen deletes the recovery path.
.github/workflows/manual-publish.yml

# Release plumbing runbook. Not generated by Fern.
AGENTS.md

# NOTE: src/version.ts and src/BaseClient.ts are deliberately NOT listed. Both
# carry the SDK version as a hardcoded literal, and release-please can only
# rewrite those through an `x-release-please-version` marker comment that regen
# strips every time. The version strings are stamped from the release tag in
# release-please.yml instead, so neither file needs shielding and Fern can keep
# ownership of both.

# NOTE: package.json is deliberately NOT listed. Fern owns its `exports` map
# and dependency ranges, and freezing it would silently drop new subpath
# exports on regen. The cost is that regen keeps reintroducing a lowercase
# `repository.url`, which fails `npm publish --provenance` with a 422 — the
# pre-publish assertion in release-please.yml catches that before it ships.

# ignore context7 config file
context7.json

# SSE streaming fixes for POST /v1/audio/stream/with-timestamps. Each entry
# below records what has to land upstream before it can be deleted again.

# Passes `eventDiscriminator: "type"` to core.Stream, which routes the response
# through iterSseEvents() instead of the defective iterDataMessages(). The live
# API sends a populated `event: speech.chunk` name, so the discriminator is
# correct. Without it, regen drops back to `{ type: "sse" }` and silently loses
# multi-line `data:` concatenation, the trailing flush that carries speech.done
# and its billable_characters_count, and CRLF handling.
# Remove once SpeechifyInc/speechify-api declares the SSE event discriminator on
# the with-timestamps endpoint (response-stream `format: sse` plus
# `event-discriminator: type`) so the generator emits it.
# COST: this freezes the whole audio resource client. Future API changes to any
# audio endpoint will not land on regen while this entry is listed.
src/api/resources/audio/client/Client.ts

# Fixes abort handling in core.Stream. The generated version assigns a private
# AbortController that nothing ever reads, and registers an abort listener that
# closes over `this`, so the handler is a no-op and a reused AbortSignal retains
# every Stream built from it. The shielded version holds the signal and polls
# `aborted`, raising AbortError rather than ending iteration as a silent
# truncated success.
# This is generator template code, not API surface, so no change to
# SpeechifyInc/speechify-api can fix it. Remove once the fix ships in
# fernapi/fern-typescript-sdk (generated here at 3.70.1) and regen emits it.
src/core/stream/Stream.ts

# Covers the shielded Stream.ts. Holds an abort test that actually fails when
# abort breaks (the generated one passed on its own `break` statement), plus
# coverage for the SSE shape the with-timestamps endpoint really sends.
# Remove together with src/core/stream/Stream.ts once the generator fix lands.
tests/unit/stream/Stream.test.ts

# Streaming example narrows on `item.type` and handles speech.error explicitly.
# A mid-stream speech.error is yielded as a plain event, never thrown, because
# the 200 is already committed by then. The generated example reported that as a
# successful call with truncated audio. Also shows the
# Buffer.from(audio, "base64") decode the SDK does not do for the caller.
# Remove once the upstream endpoint example in SpeechifyInc/speechify-api
# carries the same handling, or once the SDKs agree to raise speech.error from
# the iterator, which is a cross-SDK decision affecting the Python SDK too.
README.md

# Same streaming example fix as README.md, in the generated endpoint reference.
# COST: this freezes the entire reference. New endpoints, parameter changes and
# description edits will not appear here on regen while this entry is listed.
# Remove on the same upstream change as README.md.
reference.md
44 changes: 43 additions & 1 deletion .github/workflows/manual-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,51 @@ jobs:
registry-url: "https://registry.npmjs.org"
- name: Enable corepack
run: corepack enable
# The expected repository URL is derived from the repo this workflow runs
# in, so a rename or a re-org can never leave a stale literal here.
- name: Assert version + repository casing
env:
EXPECTED_VERSION: ${{ inputs.expected_version }}
EXPECTED_REPOSITORY: ${{ github.repository }}
run: |
node -e 'const p=require("./package.json"); if (p.version !== "${{ inputs.expected_version }}") throw new Error("version "+p.version); if (p.repository.url !== "git+https://github.com/SpeechifyInc/speechify-api-sdk-typescript.git") throw new Error("repository.url "+p.repository.url)'
cat > "${RUNNER_TEMP}/assert-manual-publish.cjs" <<'NODE'
const fs = require("node:fs");
const path = require("node:path");

const packageJson = JSON.parse(
fs.readFileSync(path.join(process.cwd(), "package.json"), "utf8"),
);
const expectedVersion = process.env.EXPECTED_VERSION;
const expectedRepositoryUrl = `git+https://github.com/${process.env.EXPECTED_REPOSITORY}.git`;

const failures = [];
if (packageJson.version !== expectedVersion) {
failures.push(
`package.json version is "${packageJson.version}", expected "${expectedVersion}"`,
);
}
if (packageJson.repository?.url !== expectedRepositoryUrl) {
failures.push(
`package.json repository.url is "${packageJson.repository?.url}", expected ` +
`"${expectedRepositoryUrl}" — npm publish --provenance rejects a ` +
`case-inexact URL with HTTP 422`,
);
}

console.log(`package.json version: ${packageJson.version}`);
console.log(`package.json repository.url: ${packageJson.repository?.url}`);
console.log(`expected version: ${expectedVersion}`);
console.log(`expected repository.url: ${expectedRepositoryUrl}`);

if (failures.length > 0) {
console.error("\nRefusing to publish:");
for (const failure of failures) {
console.error(` - ${failure}`);
}
process.exit(1);
}
NODE
node "${RUNNER_TEMP}/assert-manual-publish.cjs"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
Expand Down
Loading
Loading