@antiphony/shared 0.4.0 → 0.5.1
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/dist/cjs/api-codecs.cjs +130 -21
- package/dist/cjs/api-codecs.d.cts +67 -23
- package/dist/cjs/index.cjs +168 -21
- package/dist/cjs/index.d.cts +3 -86
- package/dist/cjs/nsid.cjs +10 -0
- package/dist/cjs/nsid.d.cts +25 -1
- package/dist/cjs/types/audio.cjs +125 -18
- package/dist/cjs/types/audio.d.cts +157 -47
- package/dist/cjs/types/processing.cjs +174 -0
- package/dist/cjs/types/processing.d.cts +336 -0
- package/dist/esm/api-codecs.d.ts +101 -57
- package/dist/esm/api-codecs.js +3 -2
- package/dist/esm/chunk-AKIWUNNK.js +129 -0
- package/dist/esm/{chunk-SBYNIQRZ.js → chunk-F5RKN3GY.js} +10 -1
- package/dist/esm/{chunk-WL2GSPGC.js → chunk-J2HE6PE2.js} +31 -29
- package/dist/esm/{chunk-RUUHUNZ6.js → chunk-UYCXRNTK.js} +7 -4
- package/dist/esm/index.d.ts +3 -86
- package/dist/esm/index.js +4 -3
- package/dist/esm/nsid.d.ts +25 -1
- package/dist/esm/nsid.js +1 -1
- package/dist/esm/types/audio.d.ts +268 -158
- package/dist/esm/types/audio.js +2 -1
- package/dist/esm/types/processing.d.ts +336 -0
- package/dist/esm/types/processing.js +3 -0
- package/package.json +6 -2
package/dist/cjs/index.cjs
CHANGED
|
@@ -61,21 +61,129 @@ var FirestoreTimestampSchema = zod.z.union([
|
|
|
61
61
|
return date;
|
|
62
62
|
});
|
|
63
63
|
var ProcessingStageStatusSchema = zod.z.enum(["pending", "ready", "failed", "skipped"]);
|
|
64
|
+
var PROCESSING_STAGES = ["denoise", "trim", "transcribe", "waveform"];
|
|
65
|
+
var ProcessingStageSchema = zod.z.enum(PROCESSING_STAGES);
|
|
66
|
+
var BYTE_MUTATING_STAGES = ["denoise", "trim"];
|
|
67
|
+
var DERIVED_STAGES = ["transcribe", "waveform"];
|
|
64
68
|
var ProcessingRequestSchema = zod.z.object({
|
|
65
69
|
transcribe: zod.z.boolean().optional(),
|
|
66
|
-
denoise: zod.z.boolean().optional()
|
|
70
|
+
denoise: zod.z.boolean().optional(),
|
|
71
|
+
trim: zod.z.boolean().optional(),
|
|
72
|
+
waveform: zod.z.boolean().optional(),
|
|
73
|
+
/**
|
|
74
|
+
* Whether a completed byte-mutating stage should invalidate and recompute
|
|
75
|
+
* the derived artifacts that describe the old audio. Defaults to **true**
|
|
76
|
+
* — a transcript of superseded audio is wrong, not merely stale.
|
|
77
|
+
*
|
|
78
|
+
* `false` opts out, for an app that would rather keep the existing
|
|
79
|
+
* transcript than pay to regenerate it. It does NOT name a stage, so a
|
|
80
|
+
* request carrying only `reprocess` requests no work.
|
|
81
|
+
*/
|
|
82
|
+
reprocess: zod.z.boolean().optional()
|
|
67
83
|
});
|
|
68
|
-
var
|
|
84
|
+
var ProcessingStageMapSchema = zod.z.object({
|
|
69
85
|
transcribe: ProcessingStageStatusSchema.optional(),
|
|
70
86
|
denoise: ProcessingStageStatusSchema.optional(),
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
updatedAt: FirestoreTimestampSchema
|
|
87
|
+
trim: ProcessingStageStatusSchema.optional(),
|
|
88
|
+
waveform: ProcessingStageStatusSchema.optional()
|
|
74
89
|
});
|
|
75
|
-
var
|
|
76
|
-
|
|
77
|
-
|
|
90
|
+
var ResolvedProcessingSchema = ProcessingStageMapSchema.extend({
|
|
91
|
+
reprocess: zod.z.boolean().optional()
|
|
92
|
+
});
|
|
93
|
+
var ProcessingStateSchema = ResolvedProcessingSchema.extend({
|
|
94
|
+
/**
|
|
95
|
+
* Content CID of the processed audio variant — the composed output of every
|
|
96
|
+
* byte-mutating stage that has completed. The record's own
|
|
97
|
+
* `embed.audio.ref.$link` stays the ORIGINAL CID (immutable content
|
|
98
|
+
* address); only the read-time view swaps playback to this variant.
|
|
99
|
+
*/
|
|
100
|
+
processedBlobCid: zod.z.string().optional(),
|
|
101
|
+
/**
|
|
102
|
+
* MIME type of the processed variant. Present because providers may
|
|
103
|
+
* TRANSCODE — the ElevenLabs Voice Isolator returns MP3 regardless of what
|
|
104
|
+
* it is given — so the variant's type cannot be assumed to match
|
|
105
|
+
* `embed.audio.mimeType`. Anything reading the variant's bytes must use
|
|
106
|
+
* this, not the embed's.
|
|
107
|
+
*/
|
|
108
|
+
processedMimeType: zod.z.string().optional(),
|
|
109
|
+
/**
|
|
110
|
+
* Duration of the processed variant, when a byte-mutating stage changed it
|
|
111
|
+
* (i.e. trim). Absent when the variant's duration matches the original.
|
|
112
|
+
*/
|
|
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(),
|
|
138
|
+
/**
|
|
139
|
+
* Peaks for the processed variant, once the `waveform` stage completes.
|
|
140
|
+
* Same normalization and bounds as `embed.waveform` (0–100, max 1000), so
|
|
141
|
+
* a view can never carry a larger payload than the record allows.
|
|
142
|
+
*/
|
|
143
|
+
waveformPeaks: zod.z.array(zod.z.number().int().min(0).max(100)).max(1e3).optional(),
|
|
144
|
+
/**
|
|
145
|
+
* When the current runner's exclusive claim on this post expires.
|
|
146
|
+
*
|
|
147
|
+
* Queue delivery is at-least-once, so the same job can arrive twice and
|
|
148
|
+
* run CONCURRENTLY. `process()` is idempotent under sequential retry — it
|
|
149
|
+
* acts on `pending` and re-does nothing already settled — but two passes
|
|
150
|
+
* interleaved is a different failure: both read the same `pending` state,
|
|
151
|
+
* both bill the provider for the same stage, and both write
|
|
152
|
+
* `processedBlobCid`, so the surviving variant is whichever finished last
|
|
153
|
+
* and the other's blob is orphaned.
|
|
154
|
+
*
|
|
155
|
+
* A runner claims this field transactionally before doing any work and
|
|
156
|
+
* clears it when finished; a second runner finding it unexpired declines
|
|
157
|
+
* and returns. It is an EXPIRY, not a boolean lock, because the holder can
|
|
158
|
+
* die mid-run (instance recycled, process killed) with no chance to
|
|
159
|
+
* release — a plain flag would strand the post permanently, where a lapsed
|
|
160
|
+
* lease lets the next delivery pick it up.
|
|
161
|
+
*
|
|
162
|
+
* Internal, like the variant fields above: `toProcessingView` projects
|
|
163
|
+
* stages only, so this never reaches a client.
|
|
164
|
+
*/
|
|
165
|
+
leaseUntil: FirestoreTimestampSchema.optional(),
|
|
166
|
+
updatedAt: FirestoreTimestampSchema
|
|
78
167
|
});
|
|
168
|
+
var ProcessingViewSchema = ProcessingStageMapSchema;
|
|
169
|
+
function toProcessingView(state) {
|
|
170
|
+
const view = {};
|
|
171
|
+
for (const stage of PROCESSING_STAGES) {
|
|
172
|
+
if (state[stage] !== void 0) view[stage] = state[stage];
|
|
173
|
+
}
|
|
174
|
+
return view;
|
|
175
|
+
}
|
|
176
|
+
function resolveAudioVariant(canonical, state) {
|
|
177
|
+
var _a;
|
|
178
|
+
if (!state) return canonical;
|
|
179
|
+
const hasVariant = state.processedBlobCid !== void 0;
|
|
180
|
+
const peaksAreCurrent = state.waveform === "ready" && state.waveformPeaks !== void 0;
|
|
181
|
+
return {
|
|
182
|
+
blobCid: hasVariant ? state.processedBlobCid : canonical.blobCid,
|
|
183
|
+
durationMs: hasVariant ? (_a = state.processedDurationMs) != null ? _a : canonical.durationMs : canonical.durationMs,
|
|
184
|
+
waveform: peaksAreCurrent ? state.waveformPeaks : canonical.waveform
|
|
185
|
+
};
|
|
186
|
+
}
|
|
79
187
|
|
|
80
188
|
// types/audio.ts
|
|
81
189
|
var StrongRefSchema = zod.z.object({
|
|
@@ -111,20 +219,34 @@ var TimedTranscriptSchema = zod.z.object({
|
|
|
111
219
|
});
|
|
112
220
|
var AudioEmbedViewSchema = zod.z.object({
|
|
113
221
|
$type: zod.z.literal("dev.antiphony.embed.audio#view"),
|
|
222
|
+
/**
|
|
223
|
+
* `url`, `durationMs` and `waveform` are RESOLVED, not copied: once
|
|
224
|
+
* processing has produced an audio variant they describe that variant
|
|
225
|
+
* rather than the bytes the client uploaded. They always agree with one
|
|
226
|
+
* another — a duration and a set of peaks are only meaningful against the
|
|
227
|
+
* audio `url` actually points at.
|
|
228
|
+
*
|
|
229
|
+
* A client that stored `durationMs` at upload time should therefore expect
|
|
230
|
+
* it to change (trim removes leading/trailing silence), and should render
|
|
231
|
+
* these three as a set rather than caching them independently. The record's
|
|
232
|
+
* originals are immutable and unaffected; this is a read-time resolution.
|
|
233
|
+
*/
|
|
114
234
|
url: zod.z.string().url(),
|
|
115
235
|
durationMs: zod.z.number().int().min(0).optional(),
|
|
116
|
-
// `alt
|
|
117
|
-
//
|
|
236
|
+
// `alt` is copied from the stored embed; keep the same bounds so a view can
|
|
237
|
+
// never carry a larger payload than the record allows.
|
|
118
238
|
alt: zod.z.string().max(1e4).optional(),
|
|
119
239
|
waveform: zod.z.array(zod.z.number().int().min(0).max(100)).max(1e3).optional(),
|
|
120
240
|
/** Lifted from the transcript enrichment record; absent until transcription completes. */
|
|
121
241
|
transcript: TimedTranscriptSchema.optional(),
|
|
122
242
|
/**
|
|
123
|
-
* Per-stage audio-processing status (
|
|
124
|
-
* opted into processing
|
|
125
|
-
* means the client should poll (or re-render) for the
|
|
126
|
-
*
|
|
127
|
-
*
|
|
243
|
+
* Per-stage audio-processing status (denoise / trim / transcribe /
|
|
244
|
+
* waveform), when the app opted into processing. Absent otherwise. A
|
|
245
|
+
* `pending` stage means the client should poll (or re-render) for the
|
|
246
|
+
* result — including a stage that returns to `pending` after having been
|
|
247
|
+
* `ready`, which is how a recompute surfaces. Once a byte-mutating stage
|
|
248
|
+
* completes, `url`/`durationMs`/`waveform` above already resolve to the
|
|
249
|
+
* processed audio variant. See `types/processing.ts`.
|
|
128
250
|
*/
|
|
129
251
|
processing: ProcessingViewSchema.optional()
|
|
130
252
|
});
|
|
@@ -228,10 +350,15 @@ var ViewerStateSchema = zod.z.object({
|
|
|
228
350
|
replyDisabledReason: zod.z.enum(["unauthenticated", "not_a_participant"]).optional()
|
|
229
351
|
});
|
|
230
352
|
var PostRecordPublicSchema = zod.z.object({
|
|
231
|
-
text
|
|
232
|
-
|
|
353
|
+
// `text`, `title` and `langs` keep the record's bounds, for the same reason
|
|
354
|
+
// `AudioEmbedViewSchema.alt` does: a view must never be able to carry a
|
|
355
|
+
// larger payload than the record it projects. The write path already
|
|
356
|
+
// enforces these, so no stored post can exceed them — stating them here
|
|
357
|
+
// keeps the published contract honest rather than adding a new constraint.
|
|
358
|
+
text: zod.z.string().max(3e3),
|
|
359
|
+
title: zod.z.string().max(3e3).optional(),
|
|
233
360
|
reply: ReplyRefSchema.optional(),
|
|
234
|
-
langs: zod.z.array(zod.z.string()).optional(),
|
|
361
|
+
langs: zod.z.array(zod.z.string()).max(3).optional(),
|
|
235
362
|
selfLabels: zod.z.array(zod.z.string()).optional(),
|
|
236
363
|
createdAt: FirestoreTimestampSchema
|
|
237
364
|
});
|
|
@@ -266,9 +393,11 @@ var CreateAudioPostRequestSchema = zod.z.object({
|
|
|
266
393
|
/** Author self-label values (content warnings). */
|
|
267
394
|
selfLabels: zod.z.array(zod.z.string()).optional(),
|
|
268
395
|
/**
|
|
269
|
-
* Opt-in audio processing for this post's audio (
|
|
270
|
-
*
|
|
271
|
-
* `skipped` on the view rather than failing the
|
|
396
|
+
* Opt-in audio processing for this post's audio (denoise / trim /
|
|
397
|
+
* transcribe / waveform). All default off. Stages the deployment can't
|
|
398
|
+
* provide come back marked `skipped` on the view rather than failing the
|
|
399
|
+
* create. A multi-stage request runs denoise → trim → (transcribe,
|
|
400
|
+
* waveform); request stages individually to override that order. See
|
|
272
401
|
* `types/processing.ts`.
|
|
273
402
|
*/
|
|
274
403
|
processing: ProcessingRequestSchema.optional()
|
|
@@ -301,6 +430,15 @@ var EMBED_NSID = {
|
|
|
301
430
|
Audio: "dev.antiphony.embed.audio",
|
|
302
431
|
RecordWithAudio: "dev.antiphony.embed.recordWithAudio"
|
|
303
432
|
};
|
|
433
|
+
var XRPC_NSID = {
|
|
434
|
+
// Queries (GET).
|
|
435
|
+
GetPost: "dev.antiphony.audio.getPost",
|
|
436
|
+
GetThread: "dev.antiphony.audio.getThread",
|
|
437
|
+
GetPlaybackUrl: "dev.antiphony.audio.getPlaybackUrl",
|
|
438
|
+
// Procedures (POST).
|
|
439
|
+
CreatePost: "dev.antiphony.audio.createPost",
|
|
440
|
+
ReprocessPost: "dev.antiphony.audio.reprocessPost"
|
|
441
|
+
};
|
|
304
442
|
var COLLECTIONS = {
|
|
305
443
|
// One post collection + the transcript enrichment namespace.
|
|
306
444
|
[NSID.AudioPost]: "posts",
|
|
@@ -401,23 +539,29 @@ exports.AudioEmbedSchema = AudioEmbedSchema;
|
|
|
401
539
|
exports.AudioEmbedViewSchema = AudioEmbedViewSchema;
|
|
402
540
|
exports.AudioPostRecordSchema = AudioPostRecordSchema;
|
|
403
541
|
exports.AudioPostViewSchema = AudioPostViewSchema;
|
|
542
|
+
exports.BYTE_MUTATING_STAGES = BYTE_MUTATING_STAGES;
|
|
404
543
|
exports.BlobRefSchema = BlobRefSchema;
|
|
405
544
|
exports.COLLECTIONS = COLLECTIONS;
|
|
406
545
|
exports.ConflictError = ConflictError;
|
|
407
546
|
exports.CreateAudioPostRequestSchema = CreateAudioPostRequestSchema;
|
|
547
|
+
exports.DERIVED_STAGES = DERIVED_STAGES;
|
|
408
548
|
exports.EMBED_NSID = EMBED_NSID;
|
|
409
549
|
exports.FirestoreTimestampSchema = FirestoreTimestampSchema;
|
|
410
550
|
exports.ForbiddenError = ForbiddenError;
|
|
411
551
|
exports.NSID = NSID;
|
|
412
552
|
exports.NotFoundError = NotFoundError;
|
|
553
|
+
exports.PROCESSING_STAGES = PROCESSING_STAGES;
|
|
413
554
|
exports.PatchAudioPostRequestSchema = PatchAudioPostRequestSchema;
|
|
414
555
|
exports.PostRecordPublicSchema = PostRecordPublicSchema;
|
|
415
556
|
exports.ProcessingRequestSchema = ProcessingRequestSchema;
|
|
557
|
+
exports.ProcessingStageMapSchema = ProcessingStageMapSchema;
|
|
558
|
+
exports.ProcessingStageSchema = ProcessingStageSchema;
|
|
416
559
|
exports.ProcessingStageStatusSchema = ProcessingStageStatusSchema;
|
|
417
560
|
exports.ProcessingStateSchema = ProcessingStateSchema;
|
|
418
561
|
exports.ProcessingViewSchema = ProcessingViewSchema;
|
|
419
562
|
exports.RateLimitError = RateLimitError;
|
|
420
563
|
exports.ReplyRefSchema = ReplyRefSchema;
|
|
564
|
+
exports.ResolvedProcessingSchema = ResolvedProcessingSchema;
|
|
421
565
|
exports.ServiceError = ServiceError;
|
|
422
566
|
exports.StrongRefSchema = StrongRefSchema;
|
|
423
567
|
exports.TimedTranscriptSchema = TimedTranscriptSchema;
|
|
@@ -426,6 +570,9 @@ exports.TranscriptSegmentSchema = TranscriptSegmentSchema;
|
|
|
426
570
|
exports.UnauthorizedError = UnauthorizedError;
|
|
427
571
|
exports.ValidationError = ValidationError;
|
|
428
572
|
exports.ViewerStateSchema = ViewerStateSchema;
|
|
573
|
+
exports.XRPC_NSID = XRPC_NSID;
|
|
429
574
|
exports.buildReportedErrorEvent = buildReportedErrorEvent;
|
|
430
575
|
exports.isFirestoreTimestamp = isFirestoreTimestamp;
|
|
431
576
|
exports.reportError = reportError;
|
|
577
|
+
exports.resolveAudioVariant = resolveAudioVariant;
|
|
578
|
+
exports.toProcessingView = toProcessingView;
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -1,93 +1,10 @@
|
|
|
1
1
|
export { CreateAudioPostRequest, CreateAudioPostRequestSchema, PatchAudioPostRequest, PatchAudioPostRequestSchema } from './api-codecs.cjs';
|
|
2
2
|
export { FirestoreTimestamp, FirestoreTimestampSchema } from './types/records.cjs';
|
|
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 { COLLECTIONS, EMBED_NSID, NSID, NsidValue, StoredNsidValue } from './nsid.cjs';
|
|
6
|
+
export { COLLECTIONS, EMBED_NSID, NSID, NsidValue, StoredNsidValue, XRPC_NSID, XrpcNsidValue } from './nsid.cjs';
|
|
7
7
|
export { ConflictError, ForbiddenError, NotFoundError, RateLimitError, ServiceError, UnauthorizedError, ValidationError } from './errors/index.cjs';
|
|
8
8
|
export { isFirestoreTimestamp } from './utils/index.cjs';
|
|
9
9
|
export { ReportedErrorEvent, buildReportedErrorEvent, reportError } from './observability/report-error.cjs';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Audio hygiene / enrichment processing (B5).
|
|
13
|
-
*
|
|
14
|
-
* Antiphony can, when the calling app opts in, run two pieces of audio
|
|
15
|
-
* processing on a post's audio: **transcription** (machine transcript, the
|
|
16
|
-
* `dev.antiphony.audio.transcript` enrichment) and **denoise** (a cleaned
|
|
17
|
-
* audio variant for playback). Both are OFF by default — the app asks for
|
|
18
|
-
* them per post via `CreateAudioPostRequest.processing`.
|
|
19
|
-
*
|
|
20
|
-
* The work runs asynchronously (outside the create request), so a post
|
|
21
|
-
* carries a mutable `processing` state that starts `pending` and settles to
|
|
22
|
-
* `ready`/`failed`/`skipped` per stage. That state is a storage-layer field —
|
|
23
|
-
* it is NOT part of the canonical lexicon record and never enters the record
|
|
24
|
-
* CID (like `kind` and `threadParticipants`), so processing can update it
|
|
25
|
-
* without changing the post's content address.
|
|
26
|
-
*/
|
|
27
|
-
/**
|
|
28
|
-
* Per-stage status.
|
|
29
|
-
* - `pending` — requested, not yet done (the worker acts on these).
|
|
30
|
-
* - `ready` — completed.
|
|
31
|
-
* - `failed` — attempted and errored.
|
|
32
|
-
* - `skipped` — requested but this deployment has no provider for it.
|
|
33
|
-
*/
|
|
34
|
-
declare const ProcessingStageStatusSchema: z.ZodEnum<["pending", "ready", "failed", "skipped"]>;
|
|
35
|
-
type ProcessingStageStatus = z.infer<typeof ProcessingStageStatusSchema>;
|
|
36
|
-
/**
|
|
37
|
-
* What the calling app opts into, on `CreateAudioPostRequest`. Both default
|
|
38
|
-
* off; only `true` values request a stage.
|
|
39
|
-
*/
|
|
40
|
-
declare const ProcessingRequestSchema: z.ZodObject<{
|
|
41
|
-
transcribe: z.ZodOptional<z.ZodBoolean>;
|
|
42
|
-
denoise: z.ZodOptional<z.ZodBoolean>;
|
|
43
|
-
}, "strip", z.ZodTypeAny, {
|
|
44
|
-
transcribe?: boolean | undefined;
|
|
45
|
-
denoise?: boolean | undefined;
|
|
46
|
-
}, {
|
|
47
|
-
transcribe?: boolean | undefined;
|
|
48
|
-
denoise?: boolean | undefined;
|
|
49
|
-
}>;
|
|
50
|
-
type ProcessingRequest = z.infer<typeof ProcessingRequestSchema>;
|
|
51
|
-
/**
|
|
52
|
-
* Stored processing state on the post record (storage-layer; not in the CID).
|
|
53
|
-
* A stage key is present iff that stage was requested. `denoisedBlobCid`
|
|
54
|
-
* points at the cleaned audio variant once denoise completes — the record's
|
|
55
|
-
* own `embed.audio.ref.$link` stays the ORIGINAL CID (immutable content
|
|
56
|
-
* address); only the read-time view swaps playback to the cleaned variant.
|
|
57
|
-
*/
|
|
58
|
-
declare const ProcessingStateSchema: z.ZodObject<{
|
|
59
|
-
transcribe: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
|
|
60
|
-
denoise: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
|
|
61
|
-
/** Content CID of the denoised audio variant, once `denoise === 'ready'`. */
|
|
62
|
-
denoisedBlobCid: z.ZodOptional<z.ZodString>;
|
|
63
|
-
updatedAt: z.ZodEffects<z.ZodUnion<[z.ZodType<unknown, z.ZodTypeDef, unknown>, z.ZodString, z.ZodNumber, z.ZodDate]>, Date, unknown>;
|
|
64
|
-
}, "strip", z.ZodTypeAny, {
|
|
65
|
-
updatedAt: Date;
|
|
66
|
-
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
67
|
-
denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
68
|
-
denoisedBlobCid?: string | undefined;
|
|
69
|
-
}, {
|
|
70
|
-
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
71
|
-
denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
72
|
-
denoisedBlobCid?: string | undefined;
|
|
73
|
-
updatedAt?: unknown;
|
|
74
|
-
}>;
|
|
75
|
-
type ProcessingState = z.infer<typeof ProcessingStateSchema>;
|
|
76
|
-
/**
|
|
77
|
-
* The processing status surfaced on the hydrated view — the per-stage status
|
|
78
|
-
* only (no internal storage fields). Absent when no processing was requested.
|
|
79
|
-
* Clients treat any `pending` stage as "still working".
|
|
80
|
-
*/
|
|
81
|
-
declare const ProcessingViewSchema: z.ZodObject<{
|
|
82
|
-
transcribe: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
|
|
83
|
-
denoise: z.ZodOptional<z.ZodEnum<["pending", "ready", "failed", "skipped"]>>;
|
|
84
|
-
}, "strip", z.ZodTypeAny, {
|
|
85
|
-
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
86
|
-
denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
87
|
-
}, {
|
|
88
|
-
transcribe?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
89
|
-
denoise?: "pending" | "ready" | "failed" | "skipped" | undefined;
|
|
90
|
-
}>;
|
|
91
|
-
type ProcessingView = z.infer<typeof ProcessingViewSchema>;
|
|
92
|
-
|
|
93
|
-
export { type ProcessingRequest, ProcessingRequestSchema, type ProcessingStageStatus, ProcessingStageStatusSchema, type ProcessingState, ProcessingStateSchema, type ProcessingView, ProcessingViewSchema };
|
|
10
|
+
import 'zod';
|
package/dist/cjs/nsid.cjs
CHANGED
|
@@ -16,6 +16,15 @@ var EMBED_NSID = {
|
|
|
16
16
|
Audio: "dev.antiphony.embed.audio",
|
|
17
17
|
RecordWithAudio: "dev.antiphony.embed.recordWithAudio"
|
|
18
18
|
};
|
|
19
|
+
var XRPC_NSID = {
|
|
20
|
+
// Queries (GET).
|
|
21
|
+
GetPost: "dev.antiphony.audio.getPost",
|
|
22
|
+
GetThread: "dev.antiphony.audio.getThread",
|
|
23
|
+
GetPlaybackUrl: "dev.antiphony.audio.getPlaybackUrl",
|
|
24
|
+
// Procedures (POST).
|
|
25
|
+
CreatePost: "dev.antiphony.audio.createPost",
|
|
26
|
+
ReprocessPost: "dev.antiphony.audio.reprocessPost"
|
|
27
|
+
};
|
|
19
28
|
var COLLECTIONS = {
|
|
20
29
|
// One post collection + the transcript enrichment namespace.
|
|
21
30
|
[NSID.AudioPost]: "posts",
|
|
@@ -25,3 +34,4 @@ var COLLECTIONS = {
|
|
|
25
34
|
exports.COLLECTIONS = COLLECTIONS;
|
|
26
35
|
exports.EMBED_NSID = EMBED_NSID;
|
|
27
36
|
exports.NSID = NSID;
|
|
37
|
+
exports.XRPC_NSID = XRPC_NSID;
|
package/dist/cjs/nsid.d.cts
CHANGED
|
@@ -23,6 +23,30 @@ declare const EMBED_NSID: {
|
|
|
23
23
|
readonly Audio: "dev.antiphony.embed.audio";
|
|
24
24
|
readonly RecordWithAudio: "dev.antiphony.embed.recordWithAudio";
|
|
25
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* XRPC method NSIDs — the `/xrpc/<nsid>` surface (see
|
|
28
|
+
* specs/xrpc-and-atproto-lex-strategy.md).
|
|
29
|
+
*
|
|
30
|
+
* These are **siblings** of the record NSIDs in `NSID`, not children of them.
|
|
31
|
+
* A method shares the authority segment (`dev.antiphony.audio`) with the record
|
|
32
|
+
* but never nests under the record's own name: the record is
|
|
33
|
+
* `dev.antiphony.audio.post` and the query that fetches it is
|
|
34
|
+
* `dev.antiphony.audio.getPost`. Deriving one by appending to the other
|
|
35
|
+
* produces `dev.antiphony.audio.post.getPost`, a different and undefined
|
|
36
|
+
* namespace — hence a separate map rather than a helper over `NSID`.
|
|
37
|
+
*
|
|
38
|
+
* Queries are `GET`, procedures are `POST`; the grouping below follows that
|
|
39
|
+
* split because it is also the auth split (procedures always require an acting
|
|
40
|
+
* actor, queries may be viewer-less).
|
|
41
|
+
*/
|
|
42
|
+
declare const XRPC_NSID: {
|
|
43
|
+
readonly GetPost: "dev.antiphony.audio.getPost";
|
|
44
|
+
readonly GetThread: "dev.antiphony.audio.getThread";
|
|
45
|
+
readonly GetPlaybackUrl: "dev.antiphony.audio.getPlaybackUrl";
|
|
46
|
+
readonly CreatePost: "dev.antiphony.audio.createPost";
|
|
47
|
+
readonly ReprocessPost: "dev.antiphony.audio.reprocessPost";
|
|
48
|
+
};
|
|
49
|
+
type XrpcNsidValue = typeof XRPC_NSID[keyof typeof XRPC_NSID];
|
|
26
50
|
/**
|
|
27
51
|
* Maps the STORED AT Protocol record-type NSIDs to Firestore collection
|
|
28
52
|
* names. When migrating to a PDS, this mapping becomes the adapter layer.
|
|
@@ -30,4 +54,4 @@ declare const EMBED_NSID: {
|
|
|
30
54
|
*/
|
|
31
55
|
declare const COLLECTIONS: Record<StoredNsidValue, string>;
|
|
32
56
|
|
|
33
|
-
export { COLLECTIONS, EMBED_NSID, NSID, type NsidValue, type StoredNsidValue };
|
|
57
|
+
export { COLLECTIONS, EMBED_NSID, NSID, type NsidValue, type StoredNsidValue, XRPC_NSID, type XrpcNsidValue };
|
package/dist/cjs/types/audio.cjs
CHANGED
|
@@ -46,21 +46,109 @@ var FirestoreTimestampSchema = zod.z.union([
|
|
|
46
46
|
return date;
|
|
47
47
|
});
|
|
48
48
|
var ProcessingStageStatusSchema = zod.z.enum(["pending", "ready", "failed", "skipped"]);
|
|
49
|
+
var PROCESSING_STAGES = ["denoise", "trim", "transcribe", "waveform"];
|
|
50
|
+
zod.z.enum(PROCESSING_STAGES);
|
|
49
51
|
zod.z.object({
|
|
50
52
|
transcribe: zod.z.boolean().optional(),
|
|
51
|
-
denoise: zod.z.boolean().optional()
|
|
53
|
+
denoise: zod.z.boolean().optional(),
|
|
54
|
+
trim: zod.z.boolean().optional(),
|
|
55
|
+
waveform: zod.z.boolean().optional(),
|
|
56
|
+
/**
|
|
57
|
+
* Whether a completed byte-mutating stage should invalidate and recompute
|
|
58
|
+
* the derived artifacts that describe the old audio. Defaults to **true**
|
|
59
|
+
* — a transcript of superseded audio is wrong, not merely stale.
|
|
60
|
+
*
|
|
61
|
+
* `false` opts out, for an app that would rather keep the existing
|
|
62
|
+
* transcript than pay to regenerate it. It does NOT name a stage, so a
|
|
63
|
+
* request carrying only `reprocess` requests no work.
|
|
64
|
+
*/
|
|
65
|
+
reprocess: zod.z.boolean().optional()
|
|
52
66
|
});
|
|
53
|
-
var
|
|
67
|
+
var ProcessingStageMapSchema = zod.z.object({
|
|
54
68
|
transcribe: ProcessingStageStatusSchema.optional(),
|
|
55
69
|
denoise: ProcessingStageStatusSchema.optional(),
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
updatedAt: FirestoreTimestampSchema
|
|
70
|
+
trim: ProcessingStageStatusSchema.optional(),
|
|
71
|
+
waveform: ProcessingStageStatusSchema.optional()
|
|
59
72
|
});
|
|
60
|
-
var
|
|
61
|
-
|
|
62
|
-
denoise: ProcessingStageStatusSchema.optional()
|
|
73
|
+
var ResolvedProcessingSchema = ProcessingStageMapSchema.extend({
|
|
74
|
+
reprocess: zod.z.boolean().optional()
|
|
63
75
|
});
|
|
76
|
+
var ProcessingStateSchema = ResolvedProcessingSchema.extend({
|
|
77
|
+
/**
|
|
78
|
+
* Content CID of the processed audio variant — the composed output of every
|
|
79
|
+
* byte-mutating stage that has completed. The record's own
|
|
80
|
+
* `embed.audio.ref.$link` stays the ORIGINAL CID (immutable content
|
|
81
|
+
* address); only the read-time view swaps playback to this variant.
|
|
82
|
+
*/
|
|
83
|
+
processedBlobCid: zod.z.string().optional(),
|
|
84
|
+
/**
|
|
85
|
+
* MIME type of the processed variant. Present because providers may
|
|
86
|
+
* TRANSCODE — the ElevenLabs Voice Isolator returns MP3 regardless of what
|
|
87
|
+
* it is given — so the variant's type cannot be assumed to match
|
|
88
|
+
* `embed.audio.mimeType`. Anything reading the variant's bytes must use
|
|
89
|
+
* this, not the embed's.
|
|
90
|
+
*/
|
|
91
|
+
processedMimeType: zod.z.string().optional(),
|
|
92
|
+
/**
|
|
93
|
+
* Duration of the processed variant, when a byte-mutating stage changed it
|
|
94
|
+
* (i.e. trim). Absent when the variant's duration matches the original.
|
|
95
|
+
*/
|
|
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(),
|
|
121
|
+
/**
|
|
122
|
+
* Peaks for the processed variant, once the `waveform` stage completes.
|
|
123
|
+
* Same normalization and bounds as `embed.waveform` (0–100, max 1000), so
|
|
124
|
+
* a view can never carry a larger payload than the record allows.
|
|
125
|
+
*/
|
|
126
|
+
waveformPeaks: zod.z.array(zod.z.number().int().min(0).max(100)).max(1e3).optional(),
|
|
127
|
+
/**
|
|
128
|
+
* When the current runner's exclusive claim on this post expires.
|
|
129
|
+
*
|
|
130
|
+
* Queue delivery is at-least-once, so the same job can arrive twice and
|
|
131
|
+
* run CONCURRENTLY. `process()` is idempotent under sequential retry — it
|
|
132
|
+
* acts on `pending` and re-does nothing already settled — but two passes
|
|
133
|
+
* interleaved is a different failure: both read the same `pending` state,
|
|
134
|
+
* both bill the provider for the same stage, and both write
|
|
135
|
+
* `processedBlobCid`, so the surviving variant is whichever finished last
|
|
136
|
+
* and the other's blob is orphaned.
|
|
137
|
+
*
|
|
138
|
+
* A runner claims this field transactionally before doing any work and
|
|
139
|
+
* clears it when finished; a second runner finding it unexpired declines
|
|
140
|
+
* and returns. It is an EXPIRY, not a boolean lock, because the holder can
|
|
141
|
+
* die mid-run (instance recycled, process killed) with no chance to
|
|
142
|
+
* release — a plain flag would strand the post permanently, where a lapsed
|
|
143
|
+
* lease lets the next delivery pick it up.
|
|
144
|
+
*
|
|
145
|
+
* Internal, like the variant fields above: `toProcessingView` projects
|
|
146
|
+
* stages only, so this never reaches a client.
|
|
147
|
+
*/
|
|
148
|
+
leaseUntil: FirestoreTimestampSchema.optional(),
|
|
149
|
+
updatedAt: FirestoreTimestampSchema
|
|
150
|
+
});
|
|
151
|
+
var ProcessingViewSchema = ProcessingStageMapSchema;
|
|
64
152
|
|
|
65
153
|
// types/audio.ts
|
|
66
154
|
var StrongRefSchema = zod.z.object({
|
|
@@ -96,20 +184,34 @@ var TimedTranscriptSchema = zod.z.object({
|
|
|
96
184
|
});
|
|
97
185
|
var AudioEmbedViewSchema = zod.z.object({
|
|
98
186
|
$type: zod.z.literal("dev.antiphony.embed.audio#view"),
|
|
187
|
+
/**
|
|
188
|
+
* `url`, `durationMs` and `waveform` are RESOLVED, not copied: once
|
|
189
|
+
* processing has produced an audio variant they describe that variant
|
|
190
|
+
* rather than the bytes the client uploaded. They always agree with one
|
|
191
|
+
* another — a duration and a set of peaks are only meaningful against the
|
|
192
|
+
* audio `url` actually points at.
|
|
193
|
+
*
|
|
194
|
+
* A client that stored `durationMs` at upload time should therefore expect
|
|
195
|
+
* it to change (trim removes leading/trailing silence), and should render
|
|
196
|
+
* these three as a set rather than caching them independently. The record's
|
|
197
|
+
* originals are immutable and unaffected; this is a read-time resolution.
|
|
198
|
+
*/
|
|
99
199
|
url: zod.z.string().url(),
|
|
100
200
|
durationMs: zod.z.number().int().min(0).optional(),
|
|
101
|
-
// `alt
|
|
102
|
-
//
|
|
201
|
+
// `alt` is copied from the stored embed; keep the same bounds so a view can
|
|
202
|
+
// never carry a larger payload than the record allows.
|
|
103
203
|
alt: zod.z.string().max(1e4).optional(),
|
|
104
204
|
waveform: zod.z.array(zod.z.number().int().min(0).max(100)).max(1e3).optional(),
|
|
105
205
|
/** Lifted from the transcript enrichment record; absent until transcription completes. */
|
|
106
206
|
transcript: TimedTranscriptSchema.optional(),
|
|
107
207
|
/**
|
|
108
|
-
* Per-stage audio-processing status (
|
|
109
|
-
* opted into processing
|
|
110
|
-
* means the client should poll (or re-render) for the
|
|
111
|
-
*
|
|
112
|
-
*
|
|
208
|
+
* Per-stage audio-processing status (denoise / trim / transcribe /
|
|
209
|
+
* waveform), when the app opted into processing. Absent otherwise. A
|
|
210
|
+
* `pending` stage means the client should poll (or re-render) for the
|
|
211
|
+
* result — including a stage that returns to `pending` after having been
|
|
212
|
+
* `ready`, which is how a recompute surfaces. Once a byte-mutating stage
|
|
213
|
+
* completes, `url`/`durationMs`/`waveform` above already resolve to the
|
|
214
|
+
* processed audio variant. See `types/processing.ts`.
|
|
113
215
|
*/
|
|
114
216
|
processing: ProcessingViewSchema.optional()
|
|
115
217
|
});
|
|
@@ -213,10 +315,15 @@ var ViewerStateSchema = zod.z.object({
|
|
|
213
315
|
replyDisabledReason: zod.z.enum(["unauthenticated", "not_a_participant"]).optional()
|
|
214
316
|
});
|
|
215
317
|
var PostRecordPublicSchema = zod.z.object({
|
|
216
|
-
text
|
|
217
|
-
|
|
318
|
+
// `text`, `title` and `langs` keep the record's bounds, for the same reason
|
|
319
|
+
// `AudioEmbedViewSchema.alt` does: a view must never be able to carry a
|
|
320
|
+
// larger payload than the record it projects. The write path already
|
|
321
|
+
// enforces these, so no stored post can exceed them — stating them here
|
|
322
|
+
// keeps the published contract honest rather than adding a new constraint.
|
|
323
|
+
text: zod.z.string().max(3e3),
|
|
324
|
+
title: zod.z.string().max(3e3).optional(),
|
|
218
325
|
reply: ReplyRefSchema.optional(),
|
|
219
|
-
langs: zod.z.array(zod.z.string()).optional(),
|
|
326
|
+
langs: zod.z.array(zod.z.string()).max(3).optional(),
|
|
220
327
|
selfLabels: zod.z.array(zod.z.string()).optional(),
|
|
221
328
|
createdAt: FirestoreTimestampSchema
|
|
222
329
|
});
|