@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 +1 -1
- package/dist/cjs/api-codecs.cjs +20 -6
- package/dist/cjs/api-codecs.d.cts +92 -92
- package/dist/cjs/index.cjs +21 -6
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/types/audio.cjs +18 -4
- package/dist/cjs/types/audio.d.cts +213 -197
- package/dist/cjs/types/processing.d.cts +20 -20
- package/dist/cjs/types/url.cjs +10 -0
- package/dist/cjs/types/url.d.cts +70 -0
- package/dist/esm/api-codecs.js +4 -3
- package/dist/esm/{chunk-UYCXRNTK.js → chunk-6EJLU4LG.js} +3 -3
- package/dist/esm/chunk-KMQWUFMV.js +8 -0
- package/dist/esm/{chunk-J2HE6PE2.js → chunk-QQ5GOLYW.js} +17 -5
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +5 -4
- package/dist/esm/types/audio.d.ts +16 -0
- package/dist/esm/types/audio.js +3 -2
- package/dist/esm/types/url.d.ts +70 -0
- package/dist/esm/types/url.js +2 -0
- package/package.json +6 -4
|
@@ -80,16 +80,16 @@ declare const ProcessingRequestSchema: z.ZodObject<{
|
|
|
80
80
|
*/
|
|
81
81
|
reprocess: z.ZodOptional<z.ZodBoolean>;
|
|
82
82
|
}, "strip", z.ZodTypeAny, {
|
|
83
|
+
waveform?: boolean | undefined;
|
|
84
|
+
transcribe?: boolean | undefined;
|
|
83
85
|
denoise?: boolean | undefined;
|
|
84
86
|
trim?: boolean | undefined;
|
|
85
|
-
transcribe?: boolean | undefined;
|
|
86
|
-
waveform?: boolean | undefined;
|
|
87
87
|
reprocess?: boolean | undefined;
|
|
88
88
|
}, {
|
|
89
|
+
waveform?: boolean | undefined;
|
|
90
|
+
transcribe?: boolean | undefined;
|
|
89
91
|
denoise?: boolean | undefined;
|
|
90
92
|
trim?: boolean | undefined;
|
|
91
|
-
transcribe?: boolean | undefined;
|
|
92
|
-
waveform?: boolean | undefined;
|
|
93
93
|
reprocess?: boolean | undefined;
|
|
94
94
|
}>;
|
|
95
95
|
type ProcessingRequest = z.infer<typeof ProcessingRequestSchema>;
|
|
@@ -104,15 +104,15 @@ declare const ProcessingStageMapSchema: z.ZodObject<{
|
|
|
104
104
|
trim: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
|
|
105
105
|
waveform: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
|
|
106
106
|
}, "strip", z.ZodTypeAny, {
|
|
107
|
+
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
108
|
+
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
107
109
|
denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
108
110
|
trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
109
|
-
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
110
|
-
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
111
111
|
}, {
|
|
112
|
+
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
113
|
+
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
112
114
|
denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
113
115
|
trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
114
|
-
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
115
|
-
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
116
116
|
}>;
|
|
117
117
|
type ProcessingStageMap = z.infer<typeof ProcessingStageMapSchema>;
|
|
118
118
|
/**
|
|
@@ -133,16 +133,16 @@ declare const ResolvedProcessingSchema: z.ZodObject<{
|
|
|
133
133
|
} & {
|
|
134
134
|
reprocess: z.ZodOptional<z.ZodBoolean>;
|
|
135
135
|
}, "strip", z.ZodTypeAny, {
|
|
136
|
+
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
137
|
+
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
136
138
|
denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
137
139
|
trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
138
|
-
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
139
|
-
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
140
140
|
reprocess?: boolean | undefined;
|
|
141
141
|
}, {
|
|
142
|
+
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
143
|
+
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
142
144
|
denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
143
145
|
trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
144
|
-
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
145
|
-
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
146
146
|
reprocess?: boolean | undefined;
|
|
147
147
|
}>;
|
|
148
148
|
type ResolvedProcessing = z.infer<typeof ResolvedProcessingSchema>;
|
|
@@ -245,10 +245,10 @@ declare const ProcessingStateSchema: z.ZodObject<{
|
|
|
245
245
|
updatedAt: z.ZodEffects<z.ZodUnion<[z.ZodType<unknown, z.ZodTypeDef, unknown>, z.ZodString, z.ZodNumber, z.ZodDate]>, Date, unknown>;
|
|
246
246
|
}, "strip", z.ZodTypeAny, {
|
|
247
247
|
updatedAt: Date;
|
|
248
|
+
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
249
|
+
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
248
250
|
denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
249
251
|
trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
250
|
-
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
251
|
-
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
252
252
|
reprocess?: boolean | undefined;
|
|
253
253
|
processedBlobCid?: string | undefined;
|
|
254
254
|
processedMimeType?: string | undefined;
|
|
@@ -257,10 +257,10 @@ declare const ProcessingStateSchema: z.ZodObject<{
|
|
|
257
257
|
waveformPeaks?: number[] | undefined;
|
|
258
258
|
leaseUntil?: Date | undefined;
|
|
259
259
|
}, {
|
|
260
|
+
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
261
|
+
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
260
262
|
denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
261
263
|
trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
262
|
-
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
263
|
-
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
264
264
|
reprocess?: boolean | undefined;
|
|
265
265
|
processedBlobCid?: string | undefined;
|
|
266
266
|
processedMimeType?: string | undefined;
|
|
@@ -282,15 +282,15 @@ declare const ProcessingViewSchema: z.ZodObject<{
|
|
|
282
282
|
trim: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
|
|
283
283
|
waveform: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
|
|
284
284
|
}, "strip", z.ZodTypeAny, {
|
|
285
|
+
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
286
|
+
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
285
287
|
denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
286
288
|
trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
287
|
-
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
288
|
-
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
289
289
|
}, {
|
|
290
|
+
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
291
|
+
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
290
292
|
denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
291
293
|
trim?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
292
|
-
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
293
|
-
waveform?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
294
294
|
}>;
|
|
295
295
|
type ProcessingView = z.infer<typeof ProcessingViewSchema>;
|
|
296
296
|
/**
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Scheme-restricted URL schema: an absolute `http:`/`https:` URL, trimmed.
|
|
5
|
+
*
|
|
6
|
+
* ## Why this exists
|
|
7
|
+
*
|
|
8
|
+
* `z.string().url()` is `new URL()` under the hood, and `new URL()` accepts
|
|
9
|
+
* ANY scheme. All of these parse clean through a bare `.url()`:
|
|
10
|
+
*
|
|
11
|
+
* javascript:alert(1)
|
|
12
|
+
* data:text/html,<script>alert(1)</script>
|
|
13
|
+
* file:///etc/passwd
|
|
14
|
+
* vbscript:msgbox(1)
|
|
15
|
+
* blob:https://evil.example/x
|
|
16
|
+
*
|
|
17
|
+
* That is harmless for a value nobody dereferences and dangerous for one that
|
|
18
|
+
* clients do. `AudioEmbedView.url` is the field a player puts in `<audio src>`;
|
|
19
|
+
* `ActorProfileRecord.rssFeed` is the field an app renders as `<a href>`. A URL
|
|
20
|
+
* that reaches either of those unchecked is a stored-XSS shape — written once,
|
|
21
|
+
* fired for every viewer afterwards.
|
|
22
|
+
*
|
|
23
|
+
* So every URL field in this contract goes through this helper, and none of
|
|
24
|
+
* them uses a bare `.url()`. The `antiphony/no-bare-zod-url` ESLint rule
|
|
25
|
+
* enforces that, because "remember to use the helper" is not a control.
|
|
26
|
+
*
|
|
27
|
+
* ## Why `http:` is allowed, despite the name
|
|
28
|
+
*
|
|
29
|
+
* The name says https because https is what a deployment should serve; the rule
|
|
30
|
+
* admits `http:` because a self-hosted or local Antiphony is reached at
|
|
31
|
+
* `http://localhost:8787`, and `AudioEmbedView.url` is built from exactly that
|
|
32
|
+
* base (`ANTIPHONY_PUBLIC_BASE_URL`). Excluding plain http would make the
|
|
33
|
+
* contract unparseable in development for no safety gained: what this closes is
|
|
34
|
+
* scheme confusion, not transport confidentiality. Transport is a deployment
|
|
35
|
+
* concern, and the deployment that matters is https already.
|
|
36
|
+
*
|
|
37
|
+
* ## Why a string check and not a `.refine()`
|
|
38
|
+
*
|
|
39
|
+
* Two reasons, both practical:
|
|
40
|
+
*
|
|
41
|
+
* - **It stays a `ZodString`.** A `.refine()` wraps the schema in `ZodEffects`,
|
|
42
|
+
* which costs consumers `.extend()`-friendliness and makes the OpenAPI
|
|
43
|
+
* generator emit a weaker schema. As a plain string check the constraint
|
|
44
|
+
* survives into `openapi.json` as a `pattern`, so a consumer in another
|
|
45
|
+
* language reads the rule instead of having to already know it.
|
|
46
|
+
* - **It is checked against the raw string, not a parsed `protocol`.** HTML
|
|
47
|
+
* strips tabs and newlines out of URL attributes before dereferencing them,
|
|
48
|
+
* so a scheme split by a tab is a live `javascript:` URL in a browser — and
|
|
49
|
+
* one `new URL()` parses happily. Requiring the string to LITERALLY begin
|
|
50
|
+
* `http://` or `https://` rejects that whole family without enumerating it.
|
|
51
|
+
*
|
|
52
|
+
* `.trim()` runs first so surrounding whitespace is normalised away rather than
|
|
53
|
+
* sneaking a scheme past the anchor. It is a normalisation, not a loosening:
|
|
54
|
+
* bare `.url()` accepted padded input too, it just kept the padding.
|
|
55
|
+
*
|
|
56
|
+
* One fidelity note on that generated `pattern`: JSON Schema has no way to
|
|
57
|
+
* express a case-insensitive regex, so `openapi.json` documents
|
|
58
|
+
* `^https?:\/\/` without the `i` flag. `HTTPS://x` therefore passes here and
|
|
59
|
+
* fails a strict reading of the document. Real URLs carry a lowercase scheme,
|
|
60
|
+
* and the alternative — dropping `i` — would reject input for cosmetics, so the
|
|
61
|
+
* runtime stays the more permissive of the two on that one axis.
|
|
62
|
+
*
|
|
63
|
+
* Usage:
|
|
64
|
+
*
|
|
65
|
+
* url: httpsUrl()
|
|
66
|
+
* rssFeed: httpsUrl().optional()
|
|
67
|
+
*/
|
|
68
|
+
declare function httpsUrl(): z.ZodString;
|
|
69
|
+
|
|
70
|
+
export { httpsUrl };
|
package/dist/esm/api-codecs.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export { CreateAudioPostRequestSchema, PatchAudioPostRequestSchema } from './chunk-
|
|
2
|
-
import './chunk-
|
|
1
|
+
export { CreateAudioPostRequestSchema, PatchAudioPostRequestSchema } from './chunk-6EJLU4LG.js';
|
|
2
|
+
import './chunk-QQ5GOLYW.js';
|
|
3
|
+
import './chunk-SMK4OZNU.js';
|
|
3
4
|
import './chunk-AKIWUNNK.js';
|
|
4
5
|
import './chunk-D655OH2I.js';
|
|
5
|
-
import './chunk-
|
|
6
|
+
import './chunk-KMQWUFMV.js';
|
|
6
7
|
import './chunk-5JBD5THX.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReplyRefSchema, AudioEmbedSchema } from './chunk-
|
|
1
|
+
import { ReplyRefSchema, AudioEmbedSchema } from './chunk-QQ5GOLYW.js';
|
|
2
2
|
import { ProcessingRequestSchema } from './chunk-AKIWUNNK.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
@@ -12,9 +12,9 @@ var CreateAudioPostRequestSchema = z.object({
|
|
|
12
12
|
/** Present ⇒ this is a reply (StrongRef root + parent). */
|
|
13
13
|
reply: ReplyRefSchema.optional(),
|
|
14
14
|
/** BCP-47 language tags. */
|
|
15
|
-
langs: z.array(z.string()).max(3).optional(),
|
|
15
|
+
langs: z.array(z.string().max(35)).max(3).optional(),
|
|
16
16
|
/** Author self-label values (content warnings). */
|
|
17
|
-
selfLabels: z.array(z.string()).optional(),
|
|
17
|
+
selfLabels: z.array(z.string().max(128)).max(10).optional(),
|
|
18
18
|
/**
|
|
19
19
|
* Opt-in audio processing for this post's audio (denoise / trim /
|
|
20
20
|
* transcribe / waveform). All default off. Stages the deployment can't
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import { BlobRefSchema } from './chunk-SMK4OZNU.js';
|
|
1
2
|
import { ProcessingViewSchema, ProcessingStateSchema } from './chunk-AKIWUNNK.js';
|
|
2
3
|
import { FirestoreTimestampSchema } from './chunk-D655OH2I.js';
|
|
3
|
-
import {
|
|
4
|
+
import { httpsUrl } from './chunk-KMQWUFMV.js';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
|
|
6
7
|
var StrongRefSchema = z.object({
|
|
7
|
-
uri: z.string().regex(/^at:\/\/.+/, "Must be an at:// URI"),
|
|
8
|
-
cid: z.string()
|
|
8
|
+
uri: z.string().regex(/^at:\/\/.+/, "Must be an at:// URI").max(512),
|
|
9
|
+
cid: z.string().max(128)
|
|
9
10
|
});
|
|
10
11
|
var ReplyRefSchema = z.object({
|
|
11
12
|
root: StrongRefSchema,
|
|
@@ -47,8 +48,13 @@ var AudioEmbedViewSchema = z.object({
|
|
|
47
48
|
* it to change (trim removes leading/trailing silence), and should render
|
|
48
49
|
* these three as a set rather than caching them independently. The record's
|
|
49
50
|
* originals are immutable and unaffected; this is a read-time resolution.
|
|
51
|
+
*
|
|
52
|
+
* Restricted to `http:`/`https:` (`httpsUrl()`), not any URL `new URL()`
|
|
53
|
+
* will parse. This is the value clients hand to `<audio src>`, so a
|
|
54
|
+
* `javascript:` or `data:` URL reaching one is stored XSS — and a bare
|
|
55
|
+
* `.url()` accepts both. See `types/url.ts`.
|
|
50
56
|
*/
|
|
51
|
-
url:
|
|
57
|
+
url: httpsUrl(),
|
|
52
58
|
durationMs: z.number().int().min(0).optional(),
|
|
53
59
|
// `alt` is copied from the stored embed; keep the same bounds so a view can
|
|
54
60
|
// never carry a larger payload than the record allows.
|
|
@@ -152,7 +158,13 @@ var TranscriptEnrichmentRecordSchema = z.object({
|
|
|
152
158
|
var ActorProfileRecordSchema = z.object({
|
|
153
159
|
handle: z.string().min(3).max(20).optional(),
|
|
154
160
|
usageIntent: z.string().max(100).optional(),
|
|
155
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Feed URL, restricted to `http:`/`https:` for the same reason
|
|
163
|
+
* `AudioEmbedView.url` is: an app renders this as an `<a href>`, and a bare
|
|
164
|
+
* `.url()` would let a `javascript:` URL through. A feed is a fetchable web
|
|
165
|
+
* document, so no legitimate value loses anything to the restriction.
|
|
166
|
+
*/
|
|
167
|
+
rssFeed: httpsUrl().optional()
|
|
156
168
|
});
|
|
157
169
|
var ViewerStateSchema = z.object({
|
|
158
170
|
/** True when the authenticated caller authored this post. */
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { FirestoreTimestamp, FirestoreTimestampSchema } from './types/records.js
|
|
|
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.js';
|
|
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.js';
|
|
5
5
|
export { BlobRef, BlobRefSchema } from './types/blob.js';
|
|
6
|
+
export { httpsUrl } from './types/url.js';
|
|
6
7
|
export { COLLECTIONS, EMBED_NSID, NSID, NsidValue, StoredNsidValue, XRPC_NSID, XrpcNsidValue } from './nsid.js';
|
|
7
8
|
export { ConflictError, ForbiddenError, NotFoundError, RateLimitError, ServiceError, UnauthorizedError, ValidationError } from './errors/index.js';
|
|
8
9
|
export { isFirestoreTimestamp } from './utils/index.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { CreateAudioPostRequestSchema, PatchAudioPostRequestSchema } from './chunk-UYCXRNTK.js';
|
|
1
|
+
export { CreateAudioPostRequestSchema, PatchAudioPostRequestSchema } from './chunk-6EJLU4LG.js';
|
|
3
2
|
export { COLLECTIONS, EMBED_NSID, NSID, XRPC_NSID } from './chunk-F5RKN3GY.js';
|
|
4
3
|
export { ConflictError, ForbiddenError, NotFoundError, RateLimitError, ServiceError, UnauthorizedError, ValidationError } from './chunk-7LW2FHLD.js';
|
|
5
4
|
import './chunk-72G3LBUQ.js';
|
|
6
5
|
export { buildReportedErrorEvent, reportError } from './chunk-BNNLHRH7.js';
|
|
7
|
-
export {
|
|
6
|
+
export { isFirestoreTimestamp } from './chunk-24KIXQZK.js';
|
|
7
|
+
export { ActorProfileRecordSchema, AudioEmbedSchema, AudioEmbedViewSchema, AudioPostRecordSchema, AudioPostViewSchema, PostRecordPublicSchema, ReplyRefSchema, StrongRefSchema, TimedTranscriptSchema, TranscriptEnrichmentRecordSchema, TranscriptSegmentSchema, ViewerStateSchema } from './chunk-QQ5GOLYW.js';
|
|
8
|
+
export { BlobRefSchema } from './chunk-SMK4OZNU.js';
|
|
8
9
|
export { BYTE_MUTATING_STAGES, DERIVED_STAGES, PROCESSING_STAGES, ProcessingRequestSchema, ProcessingStageMapSchema, ProcessingStageSchema, ProcessingStageStatusSchema, ProcessingStateSchema, ProcessingViewSchema, ResolvedProcessingSchema, resolveAudioVariant, toProcessingView } from './chunk-AKIWUNNK.js';
|
|
9
10
|
export { FirestoreTimestampSchema } from './chunk-D655OH2I.js';
|
|
10
|
-
export {
|
|
11
|
+
export { httpsUrl } from './chunk-KMQWUFMV.js';
|
|
11
12
|
import './chunk-5JBD5THX.js';
|
|
@@ -228,6 +228,11 @@ declare const AudioEmbedViewSchema: z.ZodObject<{
|
|
|
228
228
|
* it to change (trim removes leading/trailing silence), and should render
|
|
229
229
|
* these three as a set rather than caching them independently. The record's
|
|
230
230
|
* originals are immutable and unaffected; this is a read-time resolution.
|
|
231
|
+
*
|
|
232
|
+
* Restricted to `http:`/`https:` (`httpsUrl()`), not any URL `new URL()`
|
|
233
|
+
* will parse. This is the value clients hand to `<audio src>`, so a
|
|
234
|
+
* `javascript:` or `data:` URL reaching one is stored XSS — and a bare
|
|
235
|
+
* `.url()` accepts both. See `types/url.ts`.
|
|
231
236
|
*/
|
|
232
237
|
url: z.ZodString;
|
|
233
238
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -870,6 +875,12 @@ type TranscriptEnrichmentRecord = z.infer<typeof TranscriptEnrichmentRecordSchem
|
|
|
870
875
|
declare const ActorProfileRecordSchema: z.ZodObject<{
|
|
871
876
|
handle: z.ZodOptional<z.ZodString>;
|
|
872
877
|
usageIntent: z.ZodOptional<z.ZodString>;
|
|
878
|
+
/**
|
|
879
|
+
* Feed URL, restricted to `http:`/`https:` for the same reason
|
|
880
|
+
* `AudioEmbedView.url` is: an app renders this as an `<a href>`, and a bare
|
|
881
|
+
* `.url()` would let a `javascript:` URL through. A feed is a fetchable web
|
|
882
|
+
* document, so no legitimate value loses anything to the restriction.
|
|
883
|
+
*/
|
|
873
884
|
rssFeed: z.ZodOptional<z.ZodString>;
|
|
874
885
|
}, "strip", z.ZodTypeAny, {
|
|
875
886
|
handle?: string | undefined;
|
|
@@ -1107,6 +1118,11 @@ declare const AudioPostViewSchema: z.ZodObject<{
|
|
|
1107
1118
|
* it to change (trim removes leading/trailing silence), and should render
|
|
1108
1119
|
* these three as a set rather than caching them independently. The record's
|
|
1109
1120
|
* originals are immutable and unaffected; this is a read-time resolution.
|
|
1121
|
+
*
|
|
1122
|
+
* Restricted to `http:`/`https:` (`httpsUrl()`), not any URL `new URL()`
|
|
1123
|
+
* will parse. This is the value clients hand to `<audio src>`, so a
|
|
1124
|
+
* `javascript:` or `data:` URL reaching one is stored XSS — and a bare
|
|
1125
|
+
* `.url()` accepts both. See `types/url.ts`.
|
|
1110
1126
|
*/
|
|
1111
1127
|
url: z.ZodString;
|
|
1112
1128
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
package/dist/esm/types/audio.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { ActorProfileRecordSchema, AudioEmbedSchema, AudioEmbedViewSchema, AudioPostRecordSchema, AudioPostViewSchema, PostRecordPublicSchema, ReplyRefSchema, StrongRefSchema, TimedTranscriptSchema, TranscriptEnrichmentRecordSchema, TranscriptSegmentSchema, ViewerStateSchema } from '../chunk-
|
|
1
|
+
export { ActorProfileRecordSchema, AudioEmbedSchema, AudioEmbedViewSchema, AudioPostRecordSchema, AudioPostViewSchema, PostRecordPublicSchema, ReplyRefSchema, StrongRefSchema, TimedTranscriptSchema, TranscriptEnrichmentRecordSchema, TranscriptSegmentSchema, ViewerStateSchema } from '../chunk-QQ5GOLYW.js';
|
|
2
|
+
import '../chunk-SMK4OZNU.js';
|
|
2
3
|
import '../chunk-AKIWUNNK.js';
|
|
3
4
|
import '../chunk-D655OH2I.js';
|
|
4
|
-
import '../chunk-
|
|
5
|
+
import '../chunk-KMQWUFMV.js';
|
|
5
6
|
import '../chunk-5JBD5THX.js';
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Scheme-restricted URL schema: an absolute `http:`/`https:` URL, trimmed.
|
|
5
|
+
*
|
|
6
|
+
* ## Why this exists
|
|
7
|
+
*
|
|
8
|
+
* `z.string().url()` is `new URL()` under the hood, and `new URL()` accepts
|
|
9
|
+
* ANY scheme. All of these parse clean through a bare `.url()`:
|
|
10
|
+
*
|
|
11
|
+
* javascript:alert(1)
|
|
12
|
+
* data:text/html,<script>alert(1)</script>
|
|
13
|
+
* file:///etc/passwd
|
|
14
|
+
* vbscript:msgbox(1)
|
|
15
|
+
* blob:https://evil.example/x
|
|
16
|
+
*
|
|
17
|
+
* That is harmless for a value nobody dereferences and dangerous for one that
|
|
18
|
+
* clients do. `AudioEmbedView.url` is the field a player puts in `<audio src>`;
|
|
19
|
+
* `ActorProfileRecord.rssFeed` is the field an app renders as `<a href>`. A URL
|
|
20
|
+
* that reaches either of those unchecked is a stored-XSS shape — written once,
|
|
21
|
+
* fired for every viewer afterwards.
|
|
22
|
+
*
|
|
23
|
+
* So every URL field in this contract goes through this helper, and none of
|
|
24
|
+
* them uses a bare `.url()`. The `antiphony/no-bare-zod-url` ESLint rule
|
|
25
|
+
* enforces that, because "remember to use the helper" is not a control.
|
|
26
|
+
*
|
|
27
|
+
* ## Why `http:` is allowed, despite the name
|
|
28
|
+
*
|
|
29
|
+
* The name says https because https is what a deployment should serve; the rule
|
|
30
|
+
* admits `http:` because a self-hosted or local Antiphony is reached at
|
|
31
|
+
* `http://localhost:8787`, and `AudioEmbedView.url` is built from exactly that
|
|
32
|
+
* base (`ANTIPHONY_PUBLIC_BASE_URL`). Excluding plain http would make the
|
|
33
|
+
* contract unparseable in development for no safety gained: what this closes is
|
|
34
|
+
* scheme confusion, not transport confidentiality. Transport is a deployment
|
|
35
|
+
* concern, and the deployment that matters is https already.
|
|
36
|
+
*
|
|
37
|
+
* ## Why a string check and not a `.refine()`
|
|
38
|
+
*
|
|
39
|
+
* Two reasons, both practical:
|
|
40
|
+
*
|
|
41
|
+
* - **It stays a `ZodString`.** A `.refine()` wraps the schema in `ZodEffects`,
|
|
42
|
+
* which costs consumers `.extend()`-friendliness and makes the OpenAPI
|
|
43
|
+
* generator emit a weaker schema. As a plain string check the constraint
|
|
44
|
+
* survives into `openapi.json` as a `pattern`, so a consumer in another
|
|
45
|
+
* language reads the rule instead of having to already know it.
|
|
46
|
+
* - **It is checked against the raw string, not a parsed `protocol`.** HTML
|
|
47
|
+
* strips tabs and newlines out of URL attributes before dereferencing them,
|
|
48
|
+
* so a scheme split by a tab is a live `javascript:` URL in a browser — and
|
|
49
|
+
* one `new URL()` parses happily. Requiring the string to LITERALLY begin
|
|
50
|
+
* `http://` or `https://` rejects that whole family without enumerating it.
|
|
51
|
+
*
|
|
52
|
+
* `.trim()` runs first so surrounding whitespace is normalised away rather than
|
|
53
|
+
* sneaking a scheme past the anchor. It is a normalisation, not a loosening:
|
|
54
|
+
* bare `.url()` accepted padded input too, it just kept the padding.
|
|
55
|
+
*
|
|
56
|
+
* One fidelity note on that generated `pattern`: JSON Schema has no way to
|
|
57
|
+
* express a case-insensitive regex, so `openapi.json` documents
|
|
58
|
+
* `^https?:\/\/` without the `i` flag. `HTTPS://x` therefore passes here and
|
|
59
|
+
* fails a strict reading of the document. Real URLs carry a lowercase scheme,
|
|
60
|
+
* and the alternative — dropping `i` — would reject input for cosmetics, so the
|
|
61
|
+
* runtime stays the more permissive of the two on that one axis.
|
|
62
|
+
*
|
|
63
|
+
* Usage:
|
|
64
|
+
*
|
|
65
|
+
* url: httpsUrl()
|
|
66
|
+
* rssFeed: httpsUrl().optional()
|
|
67
|
+
*/
|
|
68
|
+
declare function httpsUrl(): z.ZodString;
|
|
69
|
+
|
|
70
|
+
export { httpsUrl };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antiphony/shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Shared types, Zod schemas, and codecs for Antiphony — open audio call-and-response infrastructure and AT Protocol lexicons.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://docs.antiphony.dev",
|
|
@@ -137,14 +137,16 @@
|
|
|
137
137
|
"lint": "eslint . --max-warnings=0",
|
|
138
138
|
"test": "vitest run"
|
|
139
139
|
},
|
|
140
|
-
"
|
|
140
|
+
"//peerDependencies": "zod is a PEER, not a dependency, and the build has always assumed as much: tsup.config.ts marks it `external` so that there is ONE zod module instance shared with the consumer — the comment there spells out the consequence, that `@hono/zod-openapi`'s `.openapi()` extension never reaches schemas defined here if the instance is duplicated. Declaring it under `dependencies` said the opposite, that this package brings its own, and the contradiction was not theoretical: a consumer pinned to zod 3.22.4 against this package's `^3.24.1` gets TWO instances, 3.22.4 hoisted and 3.25.76 nested here. That is a live hazard inside zod 3, not a hedge against zod 4. As a peer, npm resolves one instance and says so when it cannot. The range is `^3.24.1` and is deliberately NOT widened to include `^4.0.0`: the schemas here are written against the zod 3 API, #148 verified that a zod 4 consumer breaks at parse time, and a peer range that overstates what the code supports is exactly the failure mode `@hono/zod-openapi` demonstrated with its `zod: >=3.0.0` — a range npm cannot fault, since 3.25.76 satisfies it, while the code underneath needed `^3.20.2`. Widen it only when the schemas actually move. ⚠️ SHIPS IN 0.6.0, which is unreleased — this changes the install contract, so a consumer that did not declare zod itself must now do so (npm 7+ auto-installs peers, but a consumer with `legacy-peer-deps` set does not). Vox Pop already declares zod in every workspace, so it is a no-op there.",
|
|
141
|
+
"peerDependencies": {
|
|
141
142
|
"zod": "^3.24.1"
|
|
142
143
|
},
|
|
143
144
|
"devDependencies": {
|
|
144
145
|
"@eslint/js": "^9.39.2",
|
|
145
146
|
"tsup": "^8.5.1",
|
|
146
|
-
"typescript-eslint": "^8.
|
|
147
|
+
"typescript-eslint": "^8.70.0",
|
|
147
148
|
"typescript": "^5.7.2",
|
|
148
|
-
"vitest": "^4.1.9"
|
|
149
|
+
"vitest": "^4.1.9",
|
|
150
|
+
"zod": "^3.24.1"
|
|
149
151
|
}
|
|
150
152
|
}
|