@antiphony/shared 0.5.0 → 0.6.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.
package/README.md CHANGED
@@ -34,7 +34,7 @@ The root re-exports the most-used pieces (audio records + views, codecs, NSIDs,
34
34
  | `@antiphony/shared/errors` | Shared error types and helpers. |
35
35
  | `@antiphony/shared/utils` | Pure shared utilities (projection/date/sanitization helpers). |
36
36
  | `@antiphony/shared/observability` | Logging/error-reporting helpers (`./observability/report-error` for just the reporter). |
37
- | `@antiphony/shared/types/*` | Individual type modules: `audio`, `blob`, `processing`, `records`. |
37
+ | `@antiphony/shared/types/*` | Individual type modules: `audio`, `blob`, `processing`, `records`, `url`. |
38
38
 
39
39
  ## The records this models
40
40
 
@@ -94,6 +94,30 @@ var ProcessingStateSchema = ResolvedProcessingSchema.extend({
94
94
  * (i.e. trim). Absent when the variant's duration matches the original.
95
95
  */
96
96
  processedDurationMs: zod.z.number().int().min(0).optional(),
97
+ /**
98
+ * Which denoiser produced the variant's denoise contribution — provenance,
99
+ * the counterpart to a transcript record's `model`.
100
+ *
101
+ * Lives here because a cleaned variant, unlike a transcript, has no record
102
+ * of its own to carry it: it is a blob CID on this state. Without it,
103
+ * changing denoisers leaves no way to tell which variants predate the
104
+ * switch, so nothing can identify what to re-run.
105
+ *
106
+ * Named for the STAGE, not the variant (`processedModel`), because it
107
+ * describes one link of the byte-mutating chain rather than the composed
108
+ * artifact. Trim contributes to the same variant and has no model, and a
109
+ * later external link would want its own field rather than to overwrite
110
+ * this one.
111
+ *
112
+ * Written on every successful denoise, never cleared — it moves with
113
+ * `processedBlobCid`, which is only ever set, never reset. A denoise that
114
+ * FAILS leaves both alone, which is correct: the variant still holds the
115
+ * previous denoiser's output, so the previous model still describes it.
116
+ *
117
+ * Internal, like the other variant fields — `toProcessingView` projects
118
+ * stages only, so this never reaches a client.
119
+ */
120
+ denoiseModel: zod.z.string().optional(),
97
121
  /**
98
122
  * Peaks for the processed variant, once the `waveform` stage completes.
99
123
  * Same normalization and bounds as `embed.waveform` (0–100, max 1000), so
@@ -125,11 +149,14 @@ var ProcessingStateSchema = ResolvedProcessingSchema.extend({
125
149
  updatedAt: FirestoreTimestampSchema
126
150
  });
127
151
  var ProcessingViewSchema = ProcessingStageMapSchema;
152
+ function httpsUrl() {
153
+ return zod.z.string().trim().url().regex(/^https?:\/\//i, { message: "URL must use the http or https scheme" });
154
+ }
128
155
 
129
156
  // types/audio.ts
130
157
  var StrongRefSchema = zod.z.object({
131
- uri: zod.z.string().regex(/^at:\/\/.+/, "Must be an at:// URI"),
132
- cid: zod.z.string()
158
+ uri: zod.z.string().regex(/^at:\/\/.+/, "Must be an at:// URI").max(512),
159
+ cid: zod.z.string().max(128)
133
160
  });
134
161
  var ReplyRefSchema = zod.z.object({
135
162
  root: StrongRefSchema,
@@ -171,8 +198,13 @@ var AudioEmbedViewSchema = zod.z.object({
171
198
  * it to change (trim removes leading/trailing silence), and should render
172
199
  * these three as a set rather than caching them independently. The record's
173
200
  * originals are immutable and unaffected; this is a read-time resolution.
201
+ *
202
+ * Restricted to `http:`/`https:` (`httpsUrl()`), not any URL `new URL()`
203
+ * will parse. This is the value clients hand to `<audio src>`, so a
204
+ * `javascript:` or `data:` URL reaching one is stored XSS — and a bare
205
+ * `.url()` accepts both. See `types/url.ts`.
174
206
  */
175
- url: zod.z.string().url(),
207
+ url: httpsUrl(),
176
208
  durationMs: zod.z.number().int().min(0).optional(),
177
209
  // `alt` is copied from the stored embed; keep the same bounds so a view can
178
210
  // never carry a larger payload than the record allows.
@@ -276,7 +308,13 @@ zod.z.object({
276
308
  zod.z.object({
277
309
  handle: zod.z.string().min(3).max(20).optional(),
278
310
  usageIntent: zod.z.string().max(100).optional(),
279
- rssFeed: zod.z.string().url().optional()
311
+ /**
312
+ * Feed URL, restricted to `http:`/`https:` for the same reason
313
+ * `AudioEmbedView.url` is: an app renders this as an `<a href>`, and a bare
314
+ * `.url()` would let a `javascript:` URL through. A feed is a fetchable web
315
+ * document, so no legitimate value loses anything to the restriction.
316
+ */
317
+ rssFeed: httpsUrl().optional()
280
318
  });
281
319
  var ViewerStateSchema = zod.z.object({
282
320
  /** True when the authenticated caller authored this post. */
@@ -291,10 +329,15 @@ var ViewerStateSchema = zod.z.object({
291
329
  replyDisabledReason: zod.z.enum(["unauthenticated", "not_a_participant"]).optional()
292
330
  });
293
331
  var PostRecordPublicSchema = zod.z.object({
294
- text: zod.z.string(),
295
- title: zod.z.string().optional(),
332
+ // `text`, `title` and `langs` keep the record's bounds, for the same reason
333
+ // `AudioEmbedViewSchema.alt` does: a view must never be able to carry a
334
+ // larger payload than the record it projects. The write path already
335
+ // enforces these, so no stored post can exceed them — stating them here
336
+ // keeps the published contract honest rather than adding a new constraint.
337
+ text: zod.z.string().max(3e3),
338
+ title: zod.z.string().max(3e3).optional(),
296
339
  reply: ReplyRefSchema.optional(),
297
- langs: zod.z.array(zod.z.string()).optional(),
340
+ langs: zod.z.array(zod.z.string()).max(3).optional(),
298
341
  selfLabels: zod.z.array(zod.z.string()).optional(),
299
342
  createdAt: FirestoreTimestampSchema
300
343
  });
@@ -325,9 +368,9 @@ var CreateAudioPostRequestSchema = zod.z.object({
325
368
  /** Present ⇒ this is a reply (StrongRef root + parent). */
326
369
  reply: ReplyRefSchema.optional(),
327
370
  /** BCP-47 language tags. */
328
- langs: zod.z.array(zod.z.string()).max(3).optional(),
371
+ langs: zod.z.array(zod.z.string().max(35)).max(3).optional(),
329
372
  /** Author self-label values (content warnings). */
330
- selfLabels: zod.z.array(zod.z.string()).optional(),
373
+ selfLabels: zod.z.array(zod.z.string().max(128)).max(10).optional(),
331
374
  /**
332
375
  * Opt-in audio processing for this post's audio (denoise / trim /
333
376
  * transcribe / waveform). All default off. Stages the deployment can't
@@ -53,9 +53,9 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
53
53
  mimeType: string;
54
54
  size: number;
55
55
  };
56
- waveform?: number[] | undefined;
57
56
  durationMs?: number | undefined;
58
57
  alt?: string | undefined;
58
+ waveform?: number[] | undefined;
59
59
  }, {
60
60
  $type: "dev.antiphony.embed.audio";
61
61
  audio: {
@@ -66,9 +66,9 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
66
66
  mimeType: string;
67
67
  size: number;
68
68
  };
69
- waveform?: number[] | undefined;
70
69
  durationMs?: number | undefined;
71
70
  alt?: string | undefined;
71
+ waveform?: number[] | undefined;
72
72
  }>>;
73
73
  /** Present ⇒ this is a reply (StrongRef root + parent). */
74
74
  reply: z.ZodOptional<z.ZodObject<{
@@ -130,37 +130,20 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
130
130
  waveform: z.ZodOptional<z.ZodBoolean>;
131
131
  reprocess: z.ZodOptional<z.ZodBoolean>;
132
132
  }, "strip", z.ZodTypeAny, {
133
+ waveform?: boolean | undefined;
134
+ transcribe?: boolean | undefined;
133
135
  denoise?: boolean | undefined;
134
136
  trim?: boolean | undefined;
135
- transcribe?: boolean | undefined;
136
- waveform?: boolean | undefined;
137
137
  reprocess?: boolean | undefined;
138
138
  }, {
139
+ waveform?: boolean | undefined;
140
+ transcribe?: boolean | undefined;
139
141
  denoise?: boolean | undefined;
140
142
  trim?: boolean | undefined;
141
- transcribe?: boolean | undefined;
142
- waveform?: boolean | undefined;
143
143
  reprocess?: boolean | undefined;
144
144
  }>>;
145
145
  }, "strip", z.ZodTypeAny, {
146
146
  text: string;
147
- processing?: {
148
- denoise?: boolean | undefined;
149
- trim?: boolean | undefined;
150
- transcribe?: boolean | undefined;
151
- waveform?: boolean | undefined;
152
- reprocess?: boolean | undefined;
153
- } | undefined;
154
- reply?: {
155
- root: {
156
- uri: string;
157
- cid: string;
158
- };
159
- parent: {
160
- uri: string;
161
- cid: string;
162
- };
163
- } | undefined;
164
147
  title?: string | undefined;
165
148
  embed?: {
166
149
  $type: "dev.antiphony.embed.audio";
@@ -172,20 +155,9 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
172
155
  mimeType: string;
173
156
  size: number;
174
157
  };
175
- waveform?: number[] | undefined;
176
158
  durationMs?: number | undefined;
177
159
  alt?: string | undefined;
178
- } | undefined;
179
- langs?: string[] | undefined;
180
- selfLabels?: string[] | undefined;
181
- }, {
182
- text?: string | undefined;
183
- processing?: {
184
- denoise?: boolean | undefined;
185
- trim?: boolean | undefined;
186
- transcribe?: boolean | undefined;
187
- waveform?: boolean | undefined;
188
- reprocess?: boolean | undefined;
160
+ waveform?: number[] | undefined;
189
161
  } | undefined;
190
162
  reply?: {
191
163
  root: {
@@ -197,6 +169,17 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
197
169
  cid: string;
198
170
  };
199
171
  } | undefined;
172
+ langs?: string[] | undefined;
173
+ selfLabels?: string[] | undefined;
174
+ processing?: {
175
+ waveform?: boolean | undefined;
176
+ transcribe?: boolean | undefined;
177
+ denoise?: boolean | undefined;
178
+ trim?: boolean | undefined;
179
+ reprocess?: boolean | undefined;
180
+ } | undefined;
181
+ }, {
182
+ text?: string | undefined;
200
183
  title?: string | undefined;
201
184
  embed?: {
202
185
  $type: "dev.antiphony.embed.audio";
@@ -208,20 +191,9 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
208
191
  mimeType: string;
209
192
  size: number;
210
193
  };
211
- waveform?: number[] | undefined;
212
194
  durationMs?: number | undefined;
213
195
  alt?: string | undefined;
214
- } | undefined;
215
- langs?: string[] | undefined;
216
- selfLabels?: string[] | undefined;
217
- }>, {
218
- text: string;
219
- processing?: {
220
- denoise?: boolean | undefined;
221
- trim?: boolean | undefined;
222
- transcribe?: boolean | undefined;
223
- waveform?: boolean | undefined;
224
- reprocess?: boolean | undefined;
196
+ waveform?: number[] | undefined;
225
197
  } | undefined;
226
198
  reply?: {
227
199
  root: {
@@ -233,6 +205,17 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
233
205
  cid: string;
234
206
  };
235
207
  } | undefined;
208
+ langs?: string[] | undefined;
209
+ selfLabels?: string[] | undefined;
210
+ processing?: {
211
+ waveform?: boolean | undefined;
212
+ transcribe?: boolean | undefined;
213
+ denoise?: boolean | undefined;
214
+ trim?: boolean | undefined;
215
+ reprocess?: boolean | undefined;
216
+ } | undefined;
217
+ }>, {
218
+ text: string;
236
219
  title?: string | undefined;
237
220
  embed?: {
238
221
  $type: "dev.antiphony.embed.audio";
@@ -244,20 +227,9 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
244
227
  mimeType: string;
245
228
  size: number;
246
229
  };
247
- waveform?: number[] | undefined;
248
230
  durationMs?: number | undefined;
249
231
  alt?: string | undefined;
250
- } | undefined;
251
- langs?: string[] | undefined;
252
- selfLabels?: string[] | undefined;
253
- }, {
254
- text?: string | undefined;
255
- processing?: {
256
- denoise?: boolean | undefined;
257
- trim?: boolean | undefined;
258
- transcribe?: boolean | undefined;
259
- waveform?: boolean | undefined;
260
- reprocess?: boolean | undefined;
232
+ waveform?: number[] | undefined;
261
233
  } | undefined;
262
234
  reply?: {
263
235
  root: {
@@ -269,6 +241,17 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
269
241
  cid: string;
270
242
  };
271
243
  } | undefined;
244
+ langs?: string[] | undefined;
245
+ selfLabels?: string[] | undefined;
246
+ processing?: {
247
+ waveform?: boolean | undefined;
248
+ transcribe?: boolean | undefined;
249
+ denoise?: boolean | undefined;
250
+ trim?: boolean | undefined;
251
+ reprocess?: boolean | undefined;
252
+ } | undefined;
253
+ }, {
254
+ text?: string | undefined;
272
255
  title?: string | undefined;
273
256
  embed?: {
274
257
  $type: "dev.antiphony.embed.audio";
@@ -280,20 +263,9 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
280
263
  mimeType: string;
281
264
  size: number;
282
265
  };
283
- waveform?: number[] | undefined;
284
266
  durationMs?: number | undefined;
285
267
  alt?: string | undefined;
286
- } | undefined;
287
- langs?: string[] | undefined;
288
- selfLabels?: string[] | undefined;
289
- }>, {
290
- text: string;
291
- processing?: {
292
- denoise?: boolean | undefined;
293
- trim?: boolean | undefined;
294
- transcribe?: boolean | undefined;
295
- waveform?: boolean | undefined;
296
- reprocess?: boolean | undefined;
268
+ waveform?: number[] | undefined;
297
269
  } | undefined;
298
270
  reply?: {
299
271
  root: {
@@ -305,6 +277,17 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
305
277
  cid: string;
306
278
  };
307
279
  } | undefined;
280
+ langs?: string[] | undefined;
281
+ selfLabels?: string[] | undefined;
282
+ processing?: {
283
+ waveform?: boolean | undefined;
284
+ transcribe?: boolean | undefined;
285
+ denoise?: boolean | undefined;
286
+ trim?: boolean | undefined;
287
+ reprocess?: boolean | undefined;
288
+ } | undefined;
289
+ }>, {
290
+ text: string;
308
291
  title?: string | undefined;
309
292
  embed?: {
310
293
  $type: "dev.antiphony.embed.audio";
@@ -316,20 +299,9 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
316
299
  mimeType: string;
317
300
  size: number;
318
301
  };
319
- waveform?: number[] | undefined;
320
302
  durationMs?: number | undefined;
321
303
  alt?: string | undefined;
322
- } | undefined;
323
- langs?: string[] | undefined;
324
- selfLabels?: string[] | undefined;
325
- }, {
326
- text?: string | undefined;
327
- processing?: {
328
- denoise?: boolean | undefined;
329
- trim?: boolean | undefined;
330
- transcribe?: boolean | undefined;
331
- waveform?: boolean | undefined;
332
- reprocess?: boolean | undefined;
304
+ waveform?: number[] | undefined;
333
305
  } | undefined;
334
306
  reply?: {
335
307
  root: {
@@ -341,6 +313,17 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
341
313
  cid: string;
342
314
  };
343
315
  } | undefined;
316
+ langs?: string[] | undefined;
317
+ selfLabels?: string[] | undefined;
318
+ processing?: {
319
+ waveform?: boolean | undefined;
320
+ transcribe?: boolean | undefined;
321
+ denoise?: boolean | undefined;
322
+ trim?: boolean | undefined;
323
+ reprocess?: boolean | undefined;
324
+ } | undefined;
325
+ }, {
326
+ text?: string | undefined;
344
327
  title?: string | undefined;
345
328
  embed?: {
346
329
  $type: "dev.antiphony.embed.audio";
@@ -352,12 +335,29 @@ declare const CreateAudioPostRequestSchema: z.ZodEffects<z.ZodEffects<z.ZodObjec
352
335
  mimeType: string;
353
336
  size: number;
354
337
  };
355
- waveform?: number[] | undefined;
356
338
  durationMs?: number | undefined;
357
339
  alt?: string | undefined;
340
+ waveform?: number[] | undefined;
341
+ } | undefined;
342
+ reply?: {
343
+ root: {
344
+ uri: string;
345
+ cid: string;
346
+ };
347
+ parent: {
348
+ uri: string;
349
+ cid: string;
350
+ };
358
351
  } | undefined;
359
352
  langs?: string[] | undefined;
360
353
  selfLabels?: string[] | undefined;
354
+ processing?: {
355
+ waveform?: boolean | undefined;
356
+ transcribe?: boolean | undefined;
357
+ denoise?: boolean | undefined;
358
+ trim?: boolean | undefined;
359
+ reprocess?: boolean | undefined;
360
+ } | undefined;
361
361
  }>;
362
362
  type CreateAudioPostRequest = z.infer<typeof CreateAudioPostRequestSchema>;
363
363
  /**
@@ -379,32 +379,32 @@ declare const PatchAudioPostRequestSchema: z.ZodObject<{
379
379
  waveform: z.ZodOptional<z.ZodBoolean>;
380
380
  reprocess: z.ZodOptional<z.ZodBoolean>;
381
381
  }, "strip", z.ZodTypeAny, {
382
+ waveform?: boolean | undefined;
383
+ transcribe?: boolean | undefined;
382
384
  denoise?: boolean | undefined;
383
385
  trim?: boolean | undefined;
384
- transcribe?: boolean | undefined;
385
- waveform?: boolean | undefined;
386
386
  reprocess?: boolean | undefined;
387
387
  }, {
388
+ waveform?: boolean | undefined;
389
+ transcribe?: boolean | undefined;
388
390
  denoise?: boolean | undefined;
389
391
  trim?: boolean | undefined;
390
- transcribe?: boolean | undefined;
391
- waveform?: boolean | undefined;
392
392
  reprocess?: boolean | undefined;
393
393
  }>;
394
394
  }, "strip", z.ZodTypeAny, {
395
395
  processing: {
396
+ waveform?: boolean | undefined;
397
+ transcribe?: boolean | undefined;
396
398
  denoise?: boolean | undefined;
397
399
  trim?: boolean | undefined;
398
- transcribe?: boolean | undefined;
399
- waveform?: boolean | undefined;
400
400
  reprocess?: boolean | undefined;
401
401
  };
402
402
  }, {
403
403
  processing: {
404
+ waveform?: boolean | undefined;
405
+ transcribe?: boolean | undefined;
404
406
  denoise?: boolean | undefined;
405
407
  trim?: boolean | undefined;
406
- transcribe?: boolean | undefined;
407
- waveform?: boolean | undefined;
408
408
  reprocess?: boolean | undefined;
409
409
  };
410
410
  }>;
@@ -111,6 +111,30 @@ var ProcessingStateSchema = ResolvedProcessingSchema.extend({
111
111
  * (i.e. trim). Absent when the variant's duration matches the original.
112
112
  */
113
113
  processedDurationMs: zod.z.number().int().min(0).optional(),
114
+ /**
115
+ * Which denoiser produced the variant's denoise contribution — provenance,
116
+ * the counterpart to a transcript record's `model`.
117
+ *
118
+ * Lives here because a cleaned variant, unlike a transcript, has no record
119
+ * of its own to carry it: it is a blob CID on this state. Without it,
120
+ * changing denoisers leaves no way to tell which variants predate the
121
+ * switch, so nothing can identify what to re-run.
122
+ *
123
+ * Named for the STAGE, not the variant (`processedModel`), because it
124
+ * describes one link of the byte-mutating chain rather than the composed
125
+ * artifact. Trim contributes to the same variant and has no model, and a
126
+ * later external link would want its own field rather than to overwrite
127
+ * this one.
128
+ *
129
+ * Written on every successful denoise, never cleared — it moves with
130
+ * `processedBlobCid`, which is only ever set, never reset. A denoise that
131
+ * FAILS leaves both alone, which is correct: the variant still holds the
132
+ * previous denoiser's output, so the previous model still describes it.
133
+ *
134
+ * Internal, like the other variant fields — `toProcessingView` projects
135
+ * stages only, so this never reaches a client.
136
+ */
137
+ denoiseModel: zod.z.string().optional(),
114
138
  /**
115
139
  * Peaks for the processed variant, once the `waveform` stage completes.
116
140
  * Same normalization and bounds as `embed.waveform` (0–100, max 1000), so
@@ -160,11 +184,14 @@ function resolveAudioVariant(canonical, state) {
160
184
  waveform: peaksAreCurrent ? state.waveformPeaks : canonical.waveform
161
185
  };
162
186
  }
187
+ function httpsUrl() {
188
+ return zod.z.string().trim().url().regex(/^https?:\/\//i, { message: "URL must use the http or https scheme" });
189
+ }
163
190
 
164
191
  // types/audio.ts
165
192
  var StrongRefSchema = zod.z.object({
166
- uri: zod.z.string().regex(/^at:\/\/.+/, "Must be an at:// URI"),
167
- cid: zod.z.string()
193
+ uri: zod.z.string().regex(/^at:\/\/.+/, "Must be an at:// URI").max(512),
194
+ cid: zod.z.string().max(128)
168
195
  });
169
196
  var ReplyRefSchema = zod.z.object({
170
197
  root: StrongRefSchema,
@@ -206,8 +233,13 @@ var AudioEmbedViewSchema = zod.z.object({
206
233
  * it to change (trim removes leading/trailing silence), and should render
207
234
  * these three as a set rather than caching them independently. The record's
208
235
  * originals are immutable and unaffected; this is a read-time resolution.
236
+ *
237
+ * Restricted to `http:`/`https:` (`httpsUrl()`), not any URL `new URL()`
238
+ * will parse. This is the value clients hand to `<audio src>`, so a
239
+ * `javascript:` or `data:` URL reaching one is stored XSS — and a bare
240
+ * `.url()` accepts both. See `types/url.ts`.
209
241
  */
210
- url: zod.z.string().url(),
242
+ url: httpsUrl(),
211
243
  durationMs: zod.z.number().int().min(0).optional(),
212
244
  // `alt` is copied from the stored embed; keep the same bounds so a view can
213
245
  // never carry a larger payload than the record allows.
@@ -311,7 +343,13 @@ var TranscriptEnrichmentRecordSchema = zod.z.object({
311
343
  var ActorProfileRecordSchema = zod.z.object({
312
344
  handle: zod.z.string().min(3).max(20).optional(),
313
345
  usageIntent: zod.z.string().max(100).optional(),
314
- rssFeed: zod.z.string().url().optional()
346
+ /**
347
+ * Feed URL, restricted to `http:`/`https:` for the same reason
348
+ * `AudioEmbedView.url` is: an app renders this as an `<a href>`, and a bare
349
+ * `.url()` would let a `javascript:` URL through. A feed is a fetchable web
350
+ * document, so no legitimate value loses anything to the restriction.
351
+ */
352
+ rssFeed: httpsUrl().optional()
315
353
  });
316
354
  var ViewerStateSchema = zod.z.object({
317
355
  /** True when the authenticated caller authored this post. */
@@ -326,10 +364,15 @@ var ViewerStateSchema = zod.z.object({
326
364
  replyDisabledReason: zod.z.enum(["unauthenticated", "not_a_participant"]).optional()
327
365
  });
328
366
  var PostRecordPublicSchema = zod.z.object({
329
- text: zod.z.string(),
330
- title: zod.z.string().optional(),
367
+ // `text`, `title` and `langs` keep the record's bounds, for the same reason
368
+ // `AudioEmbedViewSchema.alt` does: a view must never be able to carry a
369
+ // larger payload than the record it projects. The write path already
370
+ // enforces these, so no stored post can exceed them — stating them here
371
+ // keeps the published contract honest rather than adding a new constraint.
372
+ text: zod.z.string().max(3e3),
373
+ title: zod.z.string().max(3e3).optional(),
331
374
  reply: ReplyRefSchema.optional(),
332
- langs: zod.z.array(zod.z.string()).optional(),
375
+ langs: zod.z.array(zod.z.string()).max(3).optional(),
333
376
  selfLabels: zod.z.array(zod.z.string()).optional(),
334
377
  createdAt: FirestoreTimestampSchema
335
378
  });
@@ -360,9 +403,9 @@ var CreateAudioPostRequestSchema = zod.z.object({
360
403
  /** Present ⇒ this is a reply (StrongRef root + parent). */
361
404
  reply: ReplyRefSchema.optional(),
362
405
  /** BCP-47 language tags. */
363
- langs: zod.z.array(zod.z.string()).max(3).optional(),
406
+ langs: zod.z.array(zod.z.string().max(35)).max(3).optional(),
364
407
  /** Author self-label values (content warnings). */
365
- selfLabels: zod.z.array(zod.z.string()).optional(),
408
+ selfLabels: zod.z.array(zod.z.string().max(128)).max(10).optional(),
366
409
  /**
367
410
  * Opt-in audio processing for this post's audio (denoise / trim /
368
411
  * transcribe / waveform). All default off. Stages the deployment can't
@@ -401,6 +444,15 @@ var EMBED_NSID = {
401
444
  Audio: "dev.antiphony.embed.audio",
402
445
  RecordWithAudio: "dev.antiphony.embed.recordWithAudio"
403
446
  };
447
+ var XRPC_NSID = {
448
+ // Queries (GET).
449
+ GetPost: "dev.antiphony.audio.getPost",
450
+ GetThread: "dev.antiphony.audio.getThread",
451
+ GetPlaybackUrl: "dev.antiphony.audio.getPlaybackUrl",
452
+ // Procedures (POST).
453
+ CreatePost: "dev.antiphony.audio.createPost",
454
+ ReprocessPost: "dev.antiphony.audio.reprocessPost"
455
+ };
404
456
  var COLLECTIONS = {
405
457
  // One post collection + the transcript enrichment namespace.
406
458
  [NSID.AudioPost]: "posts",
@@ -532,7 +584,9 @@ exports.TranscriptSegmentSchema = TranscriptSegmentSchema;
532
584
  exports.UnauthorizedError = UnauthorizedError;
533
585
  exports.ValidationError = ValidationError;
534
586
  exports.ViewerStateSchema = ViewerStateSchema;
587
+ exports.XRPC_NSID = XRPC_NSID;
535
588
  exports.buildReportedErrorEvent = buildReportedErrorEvent;
589
+ exports.httpsUrl = httpsUrl;
536
590
  exports.isFirestoreTimestamp = isFirestoreTimestamp;
537
591
  exports.reportError = reportError;
538
592
  exports.resolveAudioVariant = resolveAudioVariant;