@honest-pitches/pitch-sdk 0.5.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -11,6 +11,72 @@ and this project adheres to
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ### Fixed
15
+
16
+ - **`client.pitches.list is not a function` on every auth-required
17
+ list path (2026-07-28).** The `list` alias existed in local
18
+ source under the 0.5.1 version number but was never published
19
+ to npmjs (the 0.5.1 tarball only had `find`). Bumped to
20
+ **0.5.2** so a fresh `npm install @honest-pitches/pitch-sdk`
21
+ actually gets the alias. TSDoc updated to say stable-since
22
+ 0.5.2 (honest about the 0.5.1 publish gap). Detail:
23
+ control-repo
24
+ [`CHANGELOG.d/2026-07-28-sdk-list-alias-and-creatorid-filter.md`](../CHANGELOG.d/2026-07-28-sdk-list-alias-and-creatorid-filter.md).
25
+
26
+ ### Fixes
27
+
28
+ - **`videoSource` type widening to keep `pnpm run build:deploy:all`
29
+ green (commit `86f7ab1`, 2026-07-27).** The 2026-07-27 bug #2
30
+ commit `7f8b748` (`feat(pitcher-sdk): forward
31
+ videoSource/videoExternalId/videoExternalUrl + video helpers on
32
+ create/update`) widened `CreatePitchInput.videoSource` to a
33
+ 3-literal union (`"bunny_video_id" | "external_url" | "none" |
34
+ null`) AND added an `input.videoSource !== ""` guard at
35
+ `src/pitches.ts:215`. The guard's intent — drop empty-string
36
+ form-field values from the wire body — is correct, but the
37
+ type doesn't permit `""`, so tsc flagged the comparison as
38
+ unreachable (TS2367) and `pnpm run build:deploy:all` failed at
39
+ the SDK build step. **Fix:** widen the union to add `""` on
40
+ both `CreatePitchInput` and `UpdatePitchInput` (the latter for
41
+ symmetry, since the spread-forward site could see an
42
+ empty-string value too). `""` now lives in the canonical
43
+ "do not overwrite" club alongside `null` and absent, matching
44
+ the MCP server's `raw[key] !== ""` guard at `pitch-write`.
45
+ Two pre-existing typecheck failures from the same
46
+ `7f8b748` commit were also repaired:
47
+ - **9× TS2741** — pitch-test sections were missing the
48
+ required `mediaIds: string[]` field (the `PitchSection`
49
+ contract has required it since at least the v0.3.0
50
+ platform-libs cut). Added `mediaIds: []` to all three
51
+ test-section triples.
52
+ - **1× TS7006** — `Headers.forEach((v, k) => …)` had
53
+ implicit-any parameters because the outer
54
+ `vi.fn(async (input: any, init?: any) => …)` signature
55
+ leaves `init` untyped, so the `instanceof Headers`
56
+ narrowing does not propagate to `init.headers`. Added
57
+ explicit `(v: string, k: string)` types.
58
+
59
+ ### Tests
60
+
61
+ - **New regression test** (2026-07-27):
62
+ `pitches.test.ts#PitchesApi.create — body builder
63
+ does NOT forward videoSource/videoExternalId/videoExternalUrl
64
+ when set to empty string (regression: form-field hygiene)`.
65
+ Pins the `!== ""` guard's runtime behaviour — without it, a
66
+ future refactor that drops the guard would silently start
67
+ writing `""` to the server, which the server then rejects or
68
+ misinterprets depending on column nullability.
69
+ - **Test counts:** `pnpm test` 47/47 vitest (was 46/46);
70
+ `pnpm test:ci` 47 vitest + 54 contract assertions across 9
71
+ contract suites, 0 regressions.
72
+
73
+ ### Version
74
+
75
+ - **No version bump in this commit.** The bug #2 commit
76
+ (`7f8b748`) was unversioned at HEAD; this fix lands on top.
77
+ The next operator-pushed SDK tag should cut `v0.5.2` to ship
78
+ both fixes together.
79
+
14
80
  Next package version: `0.4.1`. See
15
81
  `CHANGELOG.d/2026-07-19-package-rename.md` for the rename to
16
82
  `@honest-pitches/pitch-sdk`.
package/dist/index.cjs CHANGED
@@ -16849,10 +16849,37 @@ var PitchesApi = class {
16849
16849
  intendedAudience: input.intendedAudience ?? "",
16850
16850
  visibility: input.visibility ?? "private"
16851
16851
  };
16852
+ if (input.honestyDisclosure != null && input.honestyDisclosure !== "") {
16853
+ payload.honestyDisclosure = String(input.honestyDisclosure).slice(0, 2e3);
16854
+ }
16852
16855
  if (input.categorySlug != null) payload.categorySlug = input.categorySlug;
16853
16856
  if (input.priceNote != null && input.priceNote !== "") {
16854
16857
  payload.priceNote = String(input.priceNote).slice(0, 64);
16855
16858
  }
16859
+ if (input.videoSource != null && input.videoSource !== "") {
16860
+ payload.videoSource = input.videoSource;
16861
+ }
16862
+ if (input.videoExternalId != null && input.videoExternalId !== "") {
16863
+ payload.videoExternalId = String(input.videoExternalId);
16864
+ }
16865
+ if (input.videoExternalUrl != null && input.videoExternalUrl !== "") {
16866
+ payload.videoExternalUrl = String(input.videoExternalUrl);
16867
+ }
16868
+ if (input.bunnyVideoId != null && input.bunnyVideoId !== "") {
16869
+ payload.bunnyVideoId = String(input.bunnyVideoId);
16870
+ }
16871
+ if (input.primaryVideo != null && input.primaryVideo !== "") {
16872
+ payload.primaryVideo = String(input.primaryVideo);
16873
+ }
16874
+ if (input.primaryVideoId != null && input.primaryVideoId !== "") {
16875
+ payload.primaryVideoId = String(input.primaryVideoId);
16876
+ }
16877
+ if (input.honestyDisclosure != null && input.honestyDisclosure !== "") {
16878
+ payload.honestyDisclosure = String(input.honestyDisclosure).slice(0, 2e3);
16879
+ }
16880
+ if (input.dryRun === true) {
16881
+ payload.dryRun = true;
16882
+ }
16856
16883
  const doc = await fetchPitches(this.config, {
16857
16884
  method: "POST",
16858
16885
  path: "/pitches",
@@ -16870,7 +16897,9 @@ var PitchesApi = class {
16870
16897
  async find(opts = {}) {
16871
16898
  const params = new URLSearchParams();
16872
16899
  if (opts.status) params.set("status", opts.status);
16900
+ if (opts.creatorId) params.set("creatorId", opts.creatorId);
16873
16901
  if (typeof opts.limit === "number") params.set("limit", String(opts.limit));
16902
+ if (opts.cursor) params.set("cursor", opts.cursor);
16874
16903
  const qs = params.toString();
16875
16904
  const docs = await fetchPitches(this.config, {
16876
16905
  method: "GET",
@@ -16878,6 +16907,20 @@ var PitchesApi = class {
16878
16907
  });
16879
16908
  return docs.map((d) => rowToPitch(d));
16880
16909
  }
16910
+ /**
16911
+ * Public alias for {@link PitchesApi.find}. Stable on the public
16912
+ * npm registry since `@honest-pitches/pitch-sdk@0.5.2` (2026-07-28).
16913
+ * If you see "client.pitches.list is not a function" you are on
16914
+ * an older SDK — bump to `>=0.5.2`. (The 0.5.1 tarball on npmjs
16915
+ * never shipped this alias even though the source landed under
16916
+ * that version number locally; 0.5.2 is the first published cut.)
16917
+ *
16918
+ * (Added 2026-07-27; published 2026-07-28 after the agent bug
16919
+ * report against CLI 0.5.1 / hosted MCP `list_pitches`.)
16920
+ */
16921
+ async list(opts = {}) {
16922
+ return this.find(opts);
16923
+ }
16881
16924
  async update(id, input) {
16882
16925
  const doc = await fetchPitches(this.config, {
16883
16926
  method: "PATCH",
@@ -16886,6 +16929,43 @@ var PitchesApi = class {
16886
16929
  });
16887
16930
  return rowToPitch(doc);
16888
16931
  }
16932
+ /**
16933
+ * Move a pitch through its lifecycle. The returned row carries BOTH
16934
+ * `status` (row-level persisted) and `lifecycle` (computed from
16935
+ * `status + previewToken` server-side).
16936
+ *
16937
+ * Status vs lifecycle (2026-07-28 FlexTape retest doc bundle):
16938
+ * `lifecycle` is the canonical, computed view of where the row sits
16939
+ * (`draft | preview | live | expired | archived`). It is derived
16940
+ * from `status + previewToken` server-side in
16941
+ * `honest-pitches-studio-api/legacy/pitch-write/src/handler.ts`.
16942
+ * Branch on `lifecycle` for terminal-state checks.
16943
+ *
16944
+ * The mapping is:
16945
+ * - `lifecycle: "draft"` ⟷ `status: "draft"` (no previewToken)
16946
+ * - `lifecycle: "preview"` ⟷ `status: "draft"` (previewToken set)
16947
+ * - `lifecycle: "live"` ⟷ `status: "live"`
16948
+ * - `lifecycle: "expired"` ⟷ `status: "expired"`
16949
+ * - `lifecycle: "archived"` ⟷ `status: "removed"` (intentional vocab split)
16950
+ *
16951
+ * Token gate (Finding #17, FlexTape retest):
16952
+ * `to: "preview"` requires a `previewToken` that matches the row's
16953
+ * stored token. The server returns
16954
+ * `400 previewToken is required to transition to preview`
16955
+ * when the token is missing or wrong.
16956
+ *
16957
+ * `to: "draft"` from an `archived` row ALSO requires the same
16958
+ * `previewToken` (the token is the only proof the caller owns the
16959
+ * unarchive). There is NO `archived → preview` single-step path —
16960
+ * the supported path is `archived → draft` (token required) and
16961
+ * then `draft → preview` (which may issue a NEW token).
16962
+ *
16963
+ * Visibility on archive (Finding #17, FlexTape retest):
16964
+ * `to: "archived"` does NOT touch `visibility` — the row keeps
16965
+ * whatever visibility it had at the time of the archive (usually
16966
+ * `"public"`, but `"unlisted"` is preserved too). Only `to: "live"`
16967
+ * assigns `visibility: "public"`.
16968
+ */
16889
16969
  async transition(id, input) {
16890
16970
  const doc = await fetchPitches(this.config, {
16891
16971
  method: "POST",
@@ -16913,11 +16993,56 @@ var PitchesApi = class {
16913
16993
  });
16914
16994
  return doc;
16915
16995
  }
16996
+ /**
16997
+ * List every media asset for a pitch, joining two server-side sources:
16998
+ *
16999
+ * - `pitch_assets` rows with `pitchId === id` (the explicit
17000
+ * uploads via `POST /pitcher/pitches/:id/media`).
17001
+ * - The auto-generated `thumbnailFileId` and `ogImageFileId` fields
17002
+ * on the pitch row itself (synthesised by `pitch-write`'s
17003
+ * `to=live` path; they live on the row, NOT in `pitch_assets`).
17004
+ *
17005
+ * Each result carries a `source: "pitch_assets" | "pitch_row"`
17006
+ * discriminator so consumers can tell the two apart. The server
17007
+ * may synthesise `pitch_row` rows with `sizeBytes: 0` (the auto
17008
+ * images don't carry an upload byte count — the `cdnUrl` is the
17009
+ * canonical handle).
17010
+ *
17011
+ * Added 2026-07-28 to address Bug #12 of the FlexTape retest brief
17012
+ * (`testing-feedback/2026-07-27-flextape-rerun-3surface-retest.md`).
17013
+ * Prior to this, `hp pitches media list <id>` returned `[]` for
17014
+ * every pitch whose only assets were auto-generated.
17015
+ */
17016
+ async listMedia(id) {
17017
+ return fetchPitches(this.config, {
17018
+ method: "GET",
17019
+ path: `/pitches/${encodeURIComponent(id)}/media`
17020
+ });
17021
+ }
16916
17022
  /**
16917
17023
  * Restore an archived pitch to `draft`. Same as `transition(id, {
16918
17024
  * to: "draft", previewToken })` but with a placeholder previewToken
16919
17025
  * if the caller didn't pass one. The server's "to=draft" path
16920
17026
  * mirrors the existing "to=preview" pre-condition.
17027
+ *
17028
+ * Status vs lifecycle (2026-07-28 FlexTape retest doc bundle):
17029
+ * `pitches.lifecycle` is the canonical, computed view of where
17030
+ * the row sits (`draft, preview, live, expired, archived`). It is
17031
+ * derived from `status + previewToken` server-side
17032
+ * (`honest-pitches-studio-api/legacy/pitch-write/src/handler.ts`).
17033
+ * Branch on `lifecycle` for terminal-state checks.
17034
+ *
17035
+ * The mapping is:
17036
+ * - `lifecycle: "preview"` ⟷ `status: "draft"` (previewToken set)
17037
+ * - `lifecycle: "live"` ⟷ `status: "live"`
17038
+ * - `lifecycle: "archived"` ⟷ `status: "removed"` (intentional vocab split)
17039
+ *
17040
+ * There is NO direct `archived → preview` path — the server only
17041
+ * exposes `archived → draft`. To resume preview after a draft,
17042
+ * transition twice (`archived → draft` then `draft → preview` with
17043
+ * a new preview token), or use `transition(id, { to: "preview",
17044
+ * previewToken })` directly if your `lifecycle` is `draft` (NOT
17045
+ * `archived`).
16921
17046
  */
16922
17047
  async unarchive(id, opts = {}) {
16923
17048
  return this.transition(id, {