@antiphony/shared 0.5.1 → 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
 
@@ -149,11 +149,14 @@ var ProcessingStateSchema = ResolvedProcessingSchema.extend({
149
149
  updatedAt: FirestoreTimestampSchema
150
150
  });
151
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
+ }
152
155
 
153
156
  // types/audio.ts
154
157
  var StrongRefSchema = zod.z.object({
155
- uri: zod.z.string().regex(/^at:\/\/.+/, "Must be an at:// URI"),
156
- 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)
157
160
  });
158
161
  var ReplyRefSchema = zod.z.object({
159
162
  root: StrongRefSchema,
@@ -195,8 +198,13 @@ var AudioEmbedViewSchema = zod.z.object({
195
198
  * it to change (trim removes leading/trailing silence), and should render
196
199
  * these three as a set rather than caching them independently. The record's
197
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`.
198
206
  */
199
- url: zod.z.string().url(),
207
+ url: httpsUrl(),
200
208
  durationMs: zod.z.number().int().min(0).optional(),
201
209
  // `alt` is copied from the stored embed; keep the same bounds so a view can
202
210
  // never carry a larger payload than the record allows.
@@ -300,7 +308,13 @@ zod.z.object({
300
308
  zod.z.object({
301
309
  handle: zod.z.string().min(3).max(20).optional(),
302
310
  usageIntent: zod.z.string().max(100).optional(),
303
- 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()
304
318
  });
305
319
  var ViewerStateSchema = zod.z.object({
306
320
  /** True when the authenticated caller authored this post. */
@@ -354,9 +368,9 @@ var CreateAudioPostRequestSchema = zod.z.object({
354
368
  /** Present ⇒ this is a reply (StrongRef root + parent). */
355
369
  reply: ReplyRefSchema.optional(),
356
370
  /** BCP-47 language tags. */
357
- langs: zod.z.array(zod.z.string()).max(3).optional(),
371
+ langs: zod.z.array(zod.z.string().max(35)).max(3).optional(),
358
372
  /** Author self-label values (content warnings). */
359
- selfLabels: zod.z.array(zod.z.string()).optional(),
373
+ selfLabels: zod.z.array(zod.z.string().max(128)).max(10).optional(),
360
374
  /**
361
375
  * Opt-in audio processing for this post's audio (denoise / trim /
362
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
  }>;
@@ -184,11 +184,14 @@ function resolveAudioVariant(canonical, state) {
184
184
  waveform: peaksAreCurrent ? state.waveformPeaks : canonical.waveform
185
185
  };
186
186
  }
187
+ function httpsUrl() {
188
+ return zod.z.string().trim().url().regex(/^https?:\/\//i, { message: "URL must use the http or https scheme" });
189
+ }
187
190
 
188
191
  // types/audio.ts
189
192
  var StrongRefSchema = zod.z.object({
190
- uri: zod.z.string().regex(/^at:\/\/.+/, "Must be an at:// URI"),
191
- 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)
192
195
  });
193
196
  var ReplyRefSchema = zod.z.object({
194
197
  root: StrongRefSchema,
@@ -230,8 +233,13 @@ var AudioEmbedViewSchema = zod.z.object({
230
233
  * it to change (trim removes leading/trailing silence), and should render
231
234
  * these three as a set rather than caching them independently. The record's
232
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`.
233
241
  */
234
- url: zod.z.string().url(),
242
+ url: httpsUrl(),
235
243
  durationMs: zod.z.number().int().min(0).optional(),
236
244
  // `alt` is copied from the stored embed; keep the same bounds so a view can
237
245
  // never carry a larger payload than the record allows.
@@ -335,7 +343,13 @@ var TranscriptEnrichmentRecordSchema = zod.z.object({
335
343
  var ActorProfileRecordSchema = zod.z.object({
336
344
  handle: zod.z.string().min(3).max(20).optional(),
337
345
  usageIntent: zod.z.string().max(100).optional(),
338
- 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()
339
353
  });
340
354
  var ViewerStateSchema = zod.z.object({
341
355
  /** True when the authenticated caller authored this post. */
@@ -389,9 +403,9 @@ var CreateAudioPostRequestSchema = zod.z.object({
389
403
  /** Present ⇒ this is a reply (StrongRef root + parent). */
390
404
  reply: ReplyRefSchema.optional(),
391
405
  /** BCP-47 language tags. */
392
- langs: zod.z.array(zod.z.string()).max(3).optional(),
406
+ langs: zod.z.array(zod.z.string().max(35)).max(3).optional(),
393
407
  /** Author self-label values (content warnings). */
394
- selfLabels: zod.z.array(zod.z.string()).optional(),
408
+ selfLabels: zod.z.array(zod.z.string().max(128)).max(10).optional(),
395
409
  /**
396
410
  * Opt-in audio processing for this post's audio (denoise / trim /
397
411
  * transcribe / waveform). All default off. Stages the deployment can't
@@ -572,6 +586,7 @@ exports.ValidationError = ValidationError;
572
586
  exports.ViewerStateSchema = ViewerStateSchema;
573
587
  exports.XRPC_NSID = XRPC_NSID;
574
588
  exports.buildReportedErrorEvent = buildReportedErrorEvent;
589
+ exports.httpsUrl = httpsUrl;
575
590
  exports.isFirestoreTimestamp = isFirestoreTimestamp;
576
591
  exports.reportError = reportError;
577
592
  exports.resolveAudioVariant = resolveAudioVariant;
@@ -3,6 +3,7 @@ export { FirestoreTimestamp, FirestoreTimestampSchema } from './types/records.cj
3
3
  export { ActorProfileRecord, ActorProfileRecordSchema, AudioEmbed, AudioEmbedSchema, AudioEmbedView, AudioEmbedViewSchema, AudioPostRecord, AudioPostRecordSchema, AudioPostView, AudioPostViewSchema, PostRecordPublic, PostRecordPublicSchema, ReplyRef, ReplyRefSchema, StrongRef, StrongRefSchema, TimedTranscript, TimedTranscriptSchema, TranscriptEnrichmentRecord, TranscriptEnrichmentRecordSchema, TranscriptSegment, TranscriptSegmentSchema, ViewerState, ViewerStateSchema } from './types/audio.cjs';
4
4
  export { BYTE_MUTATING_STAGES, CanonicalAudioFields, DERIVED_STAGES, PROCESSING_STAGES, ProcessingRequest, ProcessingRequestSchema, ProcessingStage, ProcessingStageMap, ProcessingStageMapSchema, ProcessingStageSchema, ProcessingStageStatus, ProcessingStageStatusSchema, ProcessingState, ProcessingStateSchema, ProcessingView, ProcessingViewSchema, ResolvedProcessing, ResolvedProcessingSchema, resolveAudioVariant, toProcessingView } from './types/processing.cjs';
5
5
  export { BlobRef, BlobRefSchema } from './types/blob.cjs';
6
+ export { httpsUrl } from './types/url.cjs';
6
7
  export { COLLECTIONS, EMBED_NSID, NSID, NsidValue, StoredNsidValue, XRPC_NSID, XrpcNsidValue } from './nsid.cjs';
7
8
  export { ConflictError, ForbiddenError, NotFoundError, RateLimitError, ServiceError, UnauthorizedError, ValidationError } from './errors/index.cjs';
8
9
  export { isFirestoreTimestamp } from './utils/index.cjs';
@@ -149,11 +149,14 @@ var ProcessingStateSchema = ResolvedProcessingSchema.extend({
149
149
  updatedAt: FirestoreTimestampSchema
150
150
  });
151
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
+ }
152
155
 
153
156
  // types/audio.ts
154
157
  var StrongRefSchema = zod.z.object({
155
- uri: zod.z.string().regex(/^at:\/\/.+/, "Must be an at:// URI"),
156
- 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)
157
160
  });
158
161
  var ReplyRefSchema = zod.z.object({
159
162
  root: StrongRefSchema,
@@ -195,8 +198,13 @@ var AudioEmbedViewSchema = zod.z.object({
195
198
  * it to change (trim removes leading/trailing silence), and should render
196
199
  * these three as a set rather than caching them independently. The record's
197
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`.
198
206
  */
199
- url: zod.z.string().url(),
207
+ url: httpsUrl(),
200
208
  durationMs: zod.z.number().int().min(0).optional(),
201
209
  // `alt` is copied from the stored embed; keep the same bounds so a view can
202
210
  // never carry a larger payload than the record allows.
@@ -300,7 +308,13 @@ var TranscriptEnrichmentRecordSchema = zod.z.object({
300
308
  var ActorProfileRecordSchema = zod.z.object({
301
309
  handle: zod.z.string().min(3).max(20).optional(),
302
310
  usageIntent: zod.z.string().max(100).optional(),
303
- 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()
304
318
  });
305
319
  var ViewerStateSchema = zod.z.object({
306
320
  /** True when the authenticated caller authored this post. */