@antiphony/shared 0.3.0 → 0.4.0

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.
Files changed (44) hide show
  1. package/README.md +4 -4
  2. package/dist/cjs/api-codecs.cjs +74 -147
  3. package/dist/cjs/api-codecs.d.cts +105 -60
  4. package/dist/cjs/index.cjs +84 -197
  5. package/dist/cjs/index.d.cts +89 -7
  6. package/dist/cjs/nsid.cjs +6 -9
  7. package/dist/cjs/nsid.d.cts +7 -8
  8. package/dist/cjs/types/audio.cjs +60 -124
  9. package/dist/cjs/types/audio.d.cts +226 -185
  10. package/dist/cjs/types/blob.cjs +5 -9
  11. package/dist/cjs/types/blob.d.cts +21 -21
  12. package/dist/cjs/types/records.cjs +0 -45
  13. package/dist/cjs/types/records.d.cts +1 -103
  14. package/dist/esm/api-codecs.d.ts +135 -90
  15. package/dist/esm/api-codecs.js +4 -5
  16. package/dist/esm/chunk-D655OH2I.js +35 -0
  17. package/dist/esm/chunk-RUUHUNZ6.js +37 -0
  18. package/dist/esm/{chunk-6LROFBKK.js → chunk-SBYNIQRZ.js} +7 -9
  19. package/dist/esm/chunk-SMK4OZNU.js +17 -0
  20. package/dist/esm/{chunk-F7IHVM34.js → chunk-WL2GSPGC.js} +64 -12
  21. package/dist/esm/index.d.ts +89 -7
  22. package/dist/esm/index.js +5 -7
  23. package/dist/esm/nsid.d.ts +7 -8
  24. package/dist/esm/nsid.js +1 -1
  25. package/dist/esm/types/audio.d.ts +303 -262
  26. package/dist/esm/types/audio.js +3 -4
  27. package/dist/esm/types/blob.d.ts +21 -21
  28. package/dist/esm/types/blob.js +1 -1
  29. package/dist/esm/types/records.d.ts +1 -103
  30. package/dist/esm/types/records.js +1 -1
  31. package/package.json +90 -26
  32. package/dist/cjs/types/api.cjs +0 -166
  33. package/dist/cjs/types/api.d.cts +0 -242
  34. package/dist/cjs/types/views.cjs +0 -179
  35. package/dist/cjs/types/views.d.cts +0 -726
  36. package/dist/esm/chunk-5EHV73BA.js +0 -20
  37. package/dist/esm/chunk-FDM5FDN4.js +0 -46
  38. package/dist/esm/chunk-IHIBHQBJ.js +0 -78
  39. package/dist/esm/chunk-R6SBR3IG.js +0 -97
  40. package/dist/esm/chunk-Y6HNNRVD.js +0 -12
  41. package/dist/esm/types/api.d.ts +0 -242
  42. package/dist/esm/types/api.js +0 -4
  43. package/dist/esm/types/views.d.ts +0 -726
  44. package/dist/esm/types/views.js +0 -3
@@ -6,18 +6,15 @@ var zod = require('zod');
6
6
  var BlobRefSchema = zod.z.object({
7
7
  /** Discriminator for AT Protocol type system */
8
8
  $type: zod.z.literal("blob"),
9
- /** Content Identifier (CID) or URL pointing to the blob */
10
- ref: zod.z.string(),
9
+ /** IPLD link to the blob bytes: the content CID. */
10
+ ref: zod.z.object({
11
+ $link: zod.z.string().min(1)
12
+ }),
11
13
  /** MIME type of the blob (e.g., 'audio/webm') */
12
14
  mimeType: zod.z.string(),
13
15
  /** Size of the blob in bytes */
14
- size: zod.z.number()
16
+ size: zod.z.number().int().min(0)
15
17
  });
16
- function httpsUrl() {
17
- return zod.z.string().trim().url().refine((u) => /^https?:\/\//i.test(u), {
18
- message: "URL must use the http or https scheme"
19
- });
20
- }
21
18
  var FirestoreTimestampSchema = zod.z.union([
22
19
  zod.z.custom((data) => {
23
20
  return data && typeof data === "object" && (typeof data.toDate === "function" || "seconds" in data && "nanoseconds" in data);
@@ -48,117 +45,21 @@ var FirestoreTimestampSchema = zod.z.union([
48
45
  }
49
46
  return date;
50
47
  });
48
+ var ProcessingStageStatusSchema = zod.z.enum(["pending", "ready", "failed", "skipped"]);
51
49
  zod.z.object({
52
- /** Unique Firebase UID */
53
- id: zod.z.string(),
54
- /** Public handle (e.g. @brad). Optional for Lite Users. */
55
- handle: zod.z.string().min(3).max(20).regex(/^[a-zA-Z0-9_]+$/).nullable().optional(),
56
- /** User stated intent (e.g. "Podcaster", "Listener") */
57
- usageIntent: zod.z.string().nullable().optional(),
58
- /** Domain for federated handle support. */
59
- domain: zod.z.string().default("antiphony.dev"),
60
- /**
61
- * Display Name (e.g. "Brad Thorson"). Nullable: Firestore stores `null`
62
- * when the user clears this field via the settings form, and the schema
63
- * must match storage reality or `UserRecordSchema.parse` (in
64
- * `getUserRecordByUid`) will throw.
65
- */
66
- displayName: zod.z.string().max(50).nullable().optional(),
67
- /** Short bio/description — nullable for the same reason as displayName. */
68
- bio: zod.z.string().max(160).nullable().optional(),
69
- /** URL to avatar image — nullable for the same reason as displayName. */
70
- avatarUrl: zod.z.string().url().nullable().optional(),
71
- /** Optional personal website surfaced on the public profile. */
72
- website: httpsUrl().nullable().optional(),
73
- /** Up to 5 additional public links (label + URL) shown under the bio. */
74
- links: zod.z.array(zod.z.object({
75
- label: zod.z.string().min(1).max(40),
76
- url: httpsUrl()
77
- })).max(5).optional(),
78
- /** When true and a Bluesky identity is linked, surfaces it on the public profile. */
79
- showBlueskyPublicly: zod.z.boolean().optional(),
80
- /** Server timestamp of creation */
81
- createdAt: FirestoreTimestampSchema,
82
- /** Individual account tier — free or creator_pro */
83
- tier: zod.z.enum(["free", "creator_pro"]).default("free"),
84
- /** Account status. Deactivated accounts retain data but are excluded from lookups. */
85
- status: zod.z.enum(["active", "deactivated"]).default("active"),
86
- /** Timestamp when the account was deactivated (soft deleted) */
87
- deactivatedAt: FirestoreTimestampSchema.optional()
88
- });
89
- var ProfileViewBasicSchema = zod.z.object({
90
- id: zod.z.string(),
91
- handle: zod.z.string().nullable().optional(),
92
- // `displayName` and `bio` are `.nullable()` — Firestore stores `null` for
93
- // empty values on these fields (see users-dependencies.ts), and Zod's
94
- // `.optional()` alone rejects `null`. Consumers already use truthy
95
- // checks / `??` / `||`, so widening the type to include `null` is safe.
96
- displayName: zod.z.string().nullable().optional(),
97
- avatarUrl: zod.z.string().nullable().optional(),
98
- bio: zod.z.string().nullable().optional(),
99
- /** Personal website link surfaced on the public profile. */
100
- website: zod.z.string().nullable().optional(),
101
- /** Public links (label + URL) shown under the bio. */
102
- links: zod.z.array(zod.z.object({
103
- label: zod.z.string(),
104
- url: zod.z.string()
105
- })).optional(),
106
- /**
107
- * AT Protocol identity, surfaced on the public profile only when the user
108
- * opts in (`UserRecord.showBlueskyPublicly === true`). Projection happens
109
- * in the user dependency layer; this schema simply allows the field.
110
- */
111
- bluesky: zod.z.object({
112
- handle: zod.z.string(),
113
- did: zod.z.string()
114
- }).optional(),
115
- stats: zod.z.object({
116
- followers: zod.z.number().default(0),
117
- following: zod.z.number().default(0),
118
- prompts: zod.z.number().default(0)
119
- }).optional(),
120
- badges: zod.z.array(zod.z.string()).optional(),
121
- isVerified: zod.z.boolean().optional(),
122
- createdAt: FirestoreTimestampSchema.optional()
50
+ transcribe: zod.z.boolean().optional(),
51
+ denoise: zod.z.boolean().optional()
123
52
  });
124
- var ProfileViewDetailedSchema = ProfileViewBasicSchema.extend({
125
- /** AT Protocol Identity link */
126
- bluesky: zod.z.object({
127
- handle: zod.z.string(),
128
- did: zod.z.string()
129
- }).optional(),
130
- usageIntent: zod.z.string().nullable().optional()
53
+ var ProcessingStateSchema = zod.z.object({
54
+ transcribe: ProcessingStageStatusSchema.optional(),
55
+ denoise: ProcessingStageStatusSchema.optional(),
56
+ /** Content CID of the denoised audio variant, once `denoise === 'ready'`. */
57
+ denoisedBlobCid: zod.z.string().optional(),
58
+ updatedAt: FirestoreTimestampSchema
131
59
  });
132
- var ProfileViewSelfSchema = ProfileViewDetailedSchema.extend({
133
- phoneNumber: zod.z.string().nullable().optional(),
134
- email: zod.z.string().optional(),
135
- lastSeenAt: FirestoreTimestampSchema.optional(),
136
- lastActiveAt: FirestoreTimestampSchema.optional(),
137
- unreadReplyCount: zod.z.number().default(0),
138
- newReplierCount: zod.z.number().default(0),
139
- /**
140
- * Account tier from UserRecord — surfaced on the self profile so the
141
- * client can gate paid features. Optional for legacy docs without the
142
- * field; consumers should treat missing as `'free'`.
143
- */
144
- tier: zod.z.enum(["free", "creator_pro"]).optional(),
145
- /**
146
- * Surfaces the linked Bluesky identity (handle + DID) on the public profile
147
- * when true. Persisted on UserRecord; exposed in self/detailed views so the
148
- * settings form can render the toggle's current state.
149
- */
150
- showBlueskyPublicly: zod.z.boolean().optional(),
151
- settings: zod.z.object({
152
- notifications: zod.z.boolean().optional(),
153
- theme: zod.z.string().optional()
154
- }).optional()
155
- });
156
- ProfileViewSelfSchema.extend({
157
- blockedUsers: zod.z.array(zod.z.string()).optional(),
158
- followers: zod.z.array(zod.z.string()).optional(),
159
- following: zod.z.array(zod.z.string()).optional(),
160
- reportCount: zod.z.number().optional(),
161
- isBanned: zod.z.boolean().optional()
60
+ var ProcessingViewSchema = zod.z.object({
61
+ transcribe: ProcessingStageStatusSchema.optional(),
62
+ denoise: ProcessingStageStatusSchema.optional()
162
63
  });
163
64
 
164
65
  // types/audio.ts
@@ -172,7 +73,7 @@ var ReplyRefSchema = zod.z.object({
172
73
  });
173
74
  var AudioEmbedSchema = zod.z.object({
174
75
  $type: zod.z.literal("dev.antiphony.embed.audio"),
175
- /** The audio bytes as a content-addressed storage ref (CID/path). */
76
+ /** The audio bytes as a content-addressed blob ref (`ref.$link` = CID). */
176
77
  audio: BlobRefSchema,
177
78
  /** Duration in MILLISECONDS (platform-wide unit; not seconds). */
178
79
  durationMs: zod.z.number().int().min(0).optional(),
@@ -202,11 +103,26 @@ var AudioEmbedViewSchema = zod.z.object({
202
103
  alt: zod.z.string().max(1e4).optional(),
203
104
  waveform: zod.z.array(zod.z.number().int().min(0).max(100)).max(1e3).optional(),
204
105
  /** Lifted from the transcript enrichment record; absent until transcription completes. */
205
- transcript: TimedTranscriptSchema.optional()
106
+ transcript: TimedTranscriptSchema.optional(),
107
+ /**
108
+ * Per-stage audio-processing status (transcribe / denoise), when the app
109
+ * opted into processing on create. Absent otherwise. A `pending` stage
110
+ * means the client should poll (or re-render) for the result. When
111
+ * `denoise === 'ready'`, `url` above already resolves to the cleaned
112
+ * audio variant. See `types/processing.ts`.
113
+ */
114
+ processing: ProcessingViewSchema.optional()
206
115
  });
207
116
  var AudioPostRecordSchema = zod.z.object({
208
117
  /** Storage id (rkey/doc id). */
209
118
  id: zod.z.string(),
119
+ /**
120
+ * Content CID of the canonical lexicon record (CIDv1, dag-cbor, sha2-256
121
+ * — the AT Protocol record-CID rule). Computed at write time over the
122
+ * lexicon projection (public fields only, NOT the storage/tenancy fields
123
+ * below), so StrongRefs built from it are verifiable content addresses.
124
+ */
125
+ cid: zod.z.string(),
210
126
  // --- Tenancy + facets (storage-indexed; NOT in the lexicon) ---
211
127
  /** Origin app that created this record — the multi-tenant isolation key. */
212
128
  originAppId: zod.z.string(),
@@ -224,9 +140,24 @@ var AudioPostRecordSchema = zod.z.object({
224
140
  * opened the branch. Set on replies (deduped, 1–2 ids); absent on prompts
225
141
  * (a prompt's repliers are the app's audience policy, not a fixed pair).
226
142
  * Inherited down the branch so reply gating is an O(1) field check, never a
227
- * thread walk. See `specs/antiphony-data-model.md` §6 "Reply gating".
143
+ * thread walk.
228
144
  */
229
145
  threadParticipants: zod.z.array(zod.z.string()).optional(),
146
+ /**
147
+ * Author of this reply's thread ROOT (the prompt) — the reply's recipient,
148
+ * the person whose "replies to me" feed it lands in. Denormalized at write
149
+ * time so "replies whose root author is X" is a cheap composite-index query
150
+ * (see `queryByRootAuthor`). Set on replies (`kind === 'reply'`); absent on
151
+ * prompts. Storage-layer facet, NOT in the public lexicon or the record CID.
152
+ */
153
+ rootAuthorId: zod.z.string().optional(),
154
+ /**
155
+ * Async audio-processing state (transcribe / denoise), present iff the app
156
+ * opted into processing on create. Mutated by the processing worker after
157
+ * the post is created — storage-layer, NOT in the lexicon or the record
158
+ * CID. See `types/processing.ts`.
159
+ */
160
+ processing: ProcessingStateSchema.optional(),
230
161
  // --- Lexicon fields (public contract) ---
231
162
  /** User-authored text (bsky-semantic). May be empty for pure-audio posts. NEVER the transcript. */
232
163
  text: zod.z.string().max(3e3),
@@ -245,10 +176,11 @@ var AudioPostRecordSchema = zod.z.object({
245
176
  }).refine(
246
177
  // `kind` is denormalized from `reply` presence at write time; enforce the
247
178
  // invariant so an inconsistent record can't be written or read silently.
248
- // A reply has `reply` and no `title`; a prompt has neither a `reply`.
249
- (r) => r.kind === "reply" ? !!r.reply && r.title === void 0 : !r.reply,
179
+ // A reply has `reply`, no `title`, and a stamped `rootAuthorId` (its
180
+ // recipient facet); a prompt has none of those.
181
+ (r) => r.kind === "reply" ? !!r.reply && r.title === void 0 && r.rootAuthorId !== void 0 : !r.reply && r.rootAuthorId === void 0,
250
182
  {
251
- message: "kind must match reply presence: 'reply' \u21D2 reply set & no title; 'prompt' \u21D2 no reply",
183
+ message: "kind must match reply presence: 'reply' \u21D2 reply set, no title, rootAuthorId set; 'prompt' \u21D2 no reply, no rootAuthorId",
252
184
  path: ["kind"]
253
185
  }
254
186
  );
@@ -291,9 +223,13 @@ var PostRecordPublicSchema = zod.z.object({
291
223
  var AudioPostViewSchema = zod.z.object({
292
224
  /** at:// URI (or internal ref) identifying the post. */
293
225
  uri: zod.z.string(),
294
- cid: zod.z.string().optional(),
226
+ /** Content CID of the canonical record (see `AudioPostRecordSchema.cid`). */
227
+ cid: zod.z.string(),
295
228
  kind: zod.z.enum(["prompt", "reply"]),
296
- author: ProfileViewBasicSchema,
229
+ /** The acting actor's app-scoped id — an opaque attribution ref, not a profile. */
230
+ authorId: zod.z.string(),
231
+ /** The author's app-asserted AT Protocol DID, when the caller provided one. */
232
+ authorDid: zod.z.string().optional(),
297
233
  record: PostRecordPublicSchema,
298
234
  /** Hydrated audio embed (signed URL + lifted transcript). */
299
235
  embed: AudioEmbedViewSchema.optional(),