@antiphony/shared 0.3.0 → 0.5.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 +4 -4
- package/dist/cjs/api-codecs.cjs +149 -142
- package/dist/cjs/api-codecs.d.cts +157 -68
- package/dist/cjs/index.cjs +187 -192
- package/dist/cjs/index.d.cts +310 -7
- package/dist/cjs/nsid.cjs +6 -9
- package/dist/cjs/nsid.d.cts +7 -8
- package/dist/cjs/types/audio.cjs +133 -119
- package/dist/cjs/types/audio.d.cts +343 -199
- package/dist/cjs/types/blob.cjs +5 -9
- package/dist/cjs/types/blob.d.cts +21 -21
- package/dist/cjs/types/records.cjs +0 -45
- package/dist/cjs/types/records.d.cts +1 -103
- package/dist/esm/api-codecs.d.ts +187 -98
- package/dist/esm/api-codecs.js +4 -5
- package/dist/esm/chunk-D655OH2I.js +35 -0
- package/dist/esm/chunk-F5ZKFJL2.js +39 -0
- package/dist/esm/{chunk-6LROFBKK.js → chunk-SBYNIQRZ.js} +7 -9
- package/dist/esm/chunk-SMK4OZNU.js +17 -0
- package/dist/esm/chunk-TVOXIQKF.js +294 -0
- package/dist/esm/index.d.ts +310 -7
- package/dist/esm/index.js +5 -7
- package/dist/esm/nsid.d.ts +7 -8
- package/dist/esm/nsid.js +1 -1
- package/dist/esm/types/audio.d.ts +420 -276
- package/dist/esm/types/audio.js +3 -4
- package/dist/esm/types/blob.d.ts +21 -21
- package/dist/esm/types/blob.js +1 -1
- package/dist/esm/types/records.d.ts +1 -103
- package/dist/esm/types/records.js +1 -1
- package/package.json +94 -26
- package/dist/cjs/types/api.cjs +0 -166
- package/dist/cjs/types/api.d.cts +0 -242
- package/dist/cjs/types/views.cjs +0 -179
- package/dist/cjs/types/views.d.cts +0 -726
- package/dist/esm/chunk-5EHV73BA.js +0 -20
- package/dist/esm/chunk-F7IHVM34.js +0 -144
- package/dist/esm/chunk-FDM5FDN4.js +0 -46
- package/dist/esm/chunk-IHIBHQBJ.js +0 -78
- package/dist/esm/chunk-R6SBR3IG.js +0 -97
- package/dist/esm/chunk-Y6HNNRVD.js +0 -12
- package/dist/esm/types/api.d.ts +0 -242
- package/dist/esm/types/api.js +0 -4
- package/dist/esm/types/views.d.ts +0 -726
- package/dist/esm/types/views.js +0 -3
package/dist/cjs/types/audio.cjs
CHANGED
|
@@ -6,18 +6,15 @@ var zod = require('zod');
|
|
|
6
6
|
var BlobRefSchema = zod.z.object({
|
|
7
7
|
/** Discriminator for AT Protocol type system */
|
|
8
8
|
$type: zod.z.literal("blob"),
|
|
9
|
-
/**
|
|
10
|
-
ref: zod.z.
|
|
9
|
+
/** IPLD link to the blob bytes: the content CID. */
|
|
10
|
+
ref: zod.z.object({
|
|
11
|
+
$link: zod.z.string().min(1)
|
|
12
|
+
}),
|
|
11
13
|
/** MIME type of the blob (e.g., 'audio/webm') */
|
|
12
14
|
mimeType: zod.z.string(),
|
|
13
15
|
/** Size of the blob in bytes */
|
|
14
|
-
size: zod.z.number()
|
|
16
|
+
size: zod.z.number().int().min(0)
|
|
15
17
|
});
|
|
16
|
-
function httpsUrl() {
|
|
17
|
-
return zod.z.string().trim().url().refine((u) => /^https?:\/\//i.test(u), {
|
|
18
|
-
message: "URL must use the http or https scheme"
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
18
|
var FirestoreTimestampSchema = zod.z.union([
|
|
22
19
|
zod.z.custom((data) => {
|
|
23
20
|
return data && typeof data === "object" && (typeof data.toDate === "function" || "seconds" in data && "nanoseconds" in data);
|
|
@@ -48,118 +45,86 @@ var FirestoreTimestampSchema = zod.z.union([
|
|
|
48
45
|
}
|
|
49
46
|
return date;
|
|
50
47
|
});
|
|
48
|
+
var ProcessingStageStatusSchema = zod.z.enum(["pending", "ready", "failed", "skipped"]);
|
|
49
|
+
var PROCESSING_STAGES = ["denoise", "trim", "transcribe", "waveform"];
|
|
50
|
+
zod.z.enum(PROCESSING_STAGES);
|
|
51
51
|
zod.z.object({
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
/** User stated intent (e.g. "Podcaster", "Listener") */
|
|
57
|
-
usageIntent: zod.z.string().nullable().optional(),
|
|
58
|
-
/** Domain for federated handle support. */
|
|
59
|
-
domain: zod.z.string().default("antiphony.dev"),
|
|
52
|
+
transcribe: zod.z.boolean().optional(),
|
|
53
|
+
denoise: zod.z.boolean().optional(),
|
|
54
|
+
trim: zod.z.boolean().optional(),
|
|
55
|
+
waveform: zod.z.boolean().optional(),
|
|
60
56
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
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.
|
|
65
64
|
*/
|
|
66
|
-
|
|
67
|
-
/** Short bio/description — nullable for the same reason as displayName. */
|
|
68
|
-
bio: zod.z.string().max(160).nullable().optional(),
|
|
69
|
-
/** URL to avatar image — nullable for the same reason as displayName. */
|
|
70
|
-
avatarUrl: zod.z.string().url().nullable().optional(),
|
|
71
|
-
/** Optional personal website surfaced on the public profile. */
|
|
72
|
-
website: httpsUrl().nullable().optional(),
|
|
73
|
-
/** Up to 5 additional public links (label + URL) shown under the bio. */
|
|
74
|
-
links: zod.z.array(zod.z.object({
|
|
75
|
-
label: zod.z.string().min(1).max(40),
|
|
76
|
-
url: httpsUrl()
|
|
77
|
-
})).max(5).optional(),
|
|
78
|
-
/** When true and a Bluesky identity is linked, surfaces it on the public profile. */
|
|
79
|
-
showBlueskyPublicly: zod.z.boolean().optional(),
|
|
80
|
-
/** Server timestamp of creation */
|
|
81
|
-
createdAt: FirestoreTimestampSchema,
|
|
82
|
-
/** Individual account tier — free or creator_pro */
|
|
83
|
-
tier: zod.z.enum(["free", "creator_pro"]).default("free"),
|
|
84
|
-
/** Account status. Deactivated accounts retain data but are excluded from lookups. */
|
|
85
|
-
status: zod.z.enum(["active", "deactivated"]).default("active"),
|
|
86
|
-
/** Timestamp when the account was deactivated (soft deleted) */
|
|
87
|
-
deactivatedAt: FirestoreTimestampSchema.optional()
|
|
65
|
+
reprocess: zod.z.boolean().optional()
|
|
88
66
|
});
|
|
89
|
-
var
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
// `.optional()` alone rejects `null`. Consumers already use truthy
|
|
95
|
-
// checks / `??` / `||`, so widening the type to include `null` is safe.
|
|
96
|
-
displayName: zod.z.string().nullable().optional(),
|
|
97
|
-
avatarUrl: zod.z.string().nullable().optional(),
|
|
98
|
-
bio: zod.z.string().nullable().optional(),
|
|
99
|
-
/** Personal website link surfaced on the public profile. */
|
|
100
|
-
website: zod.z.string().nullable().optional(),
|
|
101
|
-
/** Public links (label + URL) shown under the bio. */
|
|
102
|
-
links: zod.z.array(zod.z.object({
|
|
103
|
-
label: zod.z.string(),
|
|
104
|
-
url: zod.z.string()
|
|
105
|
-
})).optional(),
|
|
106
|
-
/**
|
|
107
|
-
* AT Protocol identity, surfaced on the public profile only when the user
|
|
108
|
-
* opts in (`UserRecord.showBlueskyPublicly === true`). Projection happens
|
|
109
|
-
* in the user dependency layer; this schema simply allows the field.
|
|
110
|
-
*/
|
|
111
|
-
bluesky: zod.z.object({
|
|
112
|
-
handle: zod.z.string(),
|
|
113
|
-
did: zod.z.string()
|
|
114
|
-
}).optional(),
|
|
115
|
-
stats: zod.z.object({
|
|
116
|
-
followers: zod.z.number().default(0),
|
|
117
|
-
following: zod.z.number().default(0),
|
|
118
|
-
prompts: zod.z.number().default(0)
|
|
119
|
-
}).optional(),
|
|
120
|
-
badges: zod.z.array(zod.z.string()).optional(),
|
|
121
|
-
isVerified: zod.z.boolean().optional(),
|
|
122
|
-
createdAt: FirestoreTimestampSchema.optional()
|
|
67
|
+
var ProcessingStageMapSchema = zod.z.object({
|
|
68
|
+
transcribe: ProcessingStageStatusSchema.optional(),
|
|
69
|
+
denoise: ProcessingStageStatusSchema.optional(),
|
|
70
|
+
trim: ProcessingStageStatusSchema.optional(),
|
|
71
|
+
waveform: ProcessingStageStatusSchema.optional()
|
|
123
72
|
});
|
|
124
|
-
var
|
|
125
|
-
|
|
126
|
-
bluesky: zod.z.object({
|
|
127
|
-
handle: zod.z.string(),
|
|
128
|
-
did: zod.z.string()
|
|
129
|
-
}).optional(),
|
|
130
|
-
usageIntent: zod.z.string().nullable().optional()
|
|
73
|
+
var ResolvedProcessingSchema = ProcessingStageMapSchema.extend({
|
|
74
|
+
reprocess: zod.z.boolean().optional()
|
|
131
75
|
});
|
|
132
|
-
var
|
|
133
|
-
phoneNumber: zod.z.string().nullable().optional(),
|
|
134
|
-
email: zod.z.string().optional(),
|
|
135
|
-
lastSeenAt: FirestoreTimestampSchema.optional(),
|
|
136
|
-
lastActiveAt: FirestoreTimestampSchema.optional(),
|
|
137
|
-
unreadReplyCount: zod.z.number().default(0),
|
|
138
|
-
newReplierCount: zod.z.number().default(0),
|
|
76
|
+
var ProcessingStateSchema = ResolvedProcessingSchema.extend({
|
|
139
77
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
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.
|
|
143
82
|
*/
|
|
144
|
-
|
|
83
|
+
processedBlobCid: zod.z.string().optional(),
|
|
145
84
|
/**
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
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.
|
|
149
90
|
*/
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
+
* Peaks for the processed variant, once the `waveform` stage completes.
|
|
99
|
+
* Same normalization and bounds as `embed.waveform` (0–100, max 1000), so
|
|
100
|
+
* a view can never carry a larger payload than the record allows.
|
|
101
|
+
*/
|
|
102
|
+
waveformPeaks: zod.z.array(zod.z.number().int().min(0).max(100)).max(1e3).optional(),
|
|
103
|
+
/**
|
|
104
|
+
* When the current runner's exclusive claim on this post expires.
|
|
105
|
+
*
|
|
106
|
+
* Queue delivery is at-least-once, so the same job can arrive twice and
|
|
107
|
+
* run CONCURRENTLY. `process()` is idempotent under sequential retry — it
|
|
108
|
+
* acts on `pending` and re-does nothing already settled — but two passes
|
|
109
|
+
* interleaved is a different failure: both read the same `pending` state,
|
|
110
|
+
* both bill the provider for the same stage, and both write
|
|
111
|
+
* `processedBlobCid`, so the surviving variant is whichever finished last
|
|
112
|
+
* and the other's blob is orphaned.
|
|
113
|
+
*
|
|
114
|
+
* A runner claims this field transactionally before doing any work and
|
|
115
|
+
* clears it when finished; a second runner finding it unexpired declines
|
|
116
|
+
* and returns. It is an EXPIRY, not a boolean lock, because the holder can
|
|
117
|
+
* die mid-run (instance recycled, process killed) with no chance to
|
|
118
|
+
* release — a plain flag would strand the post permanently, where a lapsed
|
|
119
|
+
* lease lets the next delivery pick it up.
|
|
120
|
+
*
|
|
121
|
+
* Internal, like the variant fields above: `toProcessingView` projects
|
|
122
|
+
* stages only, so this never reaches a client.
|
|
123
|
+
*/
|
|
124
|
+
leaseUntil: FirestoreTimestampSchema.optional(),
|
|
125
|
+
updatedAt: FirestoreTimestampSchema
|
|
162
126
|
});
|
|
127
|
+
var ProcessingViewSchema = ProcessingStageMapSchema;
|
|
163
128
|
|
|
164
129
|
// types/audio.ts
|
|
165
130
|
var StrongRefSchema = zod.z.object({
|
|
@@ -172,7 +137,7 @@ var ReplyRefSchema = zod.z.object({
|
|
|
172
137
|
});
|
|
173
138
|
var AudioEmbedSchema = zod.z.object({
|
|
174
139
|
$type: zod.z.literal("dev.antiphony.embed.audio"),
|
|
175
|
-
/** The audio bytes as a content-addressed
|
|
140
|
+
/** The audio bytes as a content-addressed blob ref (`ref.$link` = CID). */
|
|
176
141
|
audio: BlobRefSchema,
|
|
177
142
|
/** Duration in MILLISECONDS (platform-wide unit; not seconds). */
|
|
178
143
|
durationMs: zod.z.number().int().min(0).optional(),
|
|
@@ -195,18 +160,47 @@ var TimedTranscriptSchema = zod.z.object({
|
|
|
195
160
|
});
|
|
196
161
|
var AudioEmbedViewSchema = zod.z.object({
|
|
197
162
|
$type: zod.z.literal("dev.antiphony.embed.audio#view"),
|
|
163
|
+
/**
|
|
164
|
+
* `url`, `durationMs` and `waveform` are RESOLVED, not copied: once
|
|
165
|
+
* processing has produced an audio variant they describe that variant
|
|
166
|
+
* rather than the bytes the client uploaded. They always agree with one
|
|
167
|
+
* another — a duration and a set of peaks are only meaningful against the
|
|
168
|
+
* audio `url` actually points at.
|
|
169
|
+
*
|
|
170
|
+
* A client that stored `durationMs` at upload time should therefore expect
|
|
171
|
+
* it to change (trim removes leading/trailing silence), and should render
|
|
172
|
+
* these three as a set rather than caching them independently. The record's
|
|
173
|
+
* originals are immutable and unaffected; this is a read-time resolution.
|
|
174
|
+
*/
|
|
198
175
|
url: zod.z.string().url(),
|
|
199
176
|
durationMs: zod.z.number().int().min(0).optional(),
|
|
200
|
-
// `alt
|
|
201
|
-
//
|
|
177
|
+
// `alt` is copied from the stored embed; keep the same bounds so a view can
|
|
178
|
+
// never carry a larger payload than the record allows.
|
|
202
179
|
alt: zod.z.string().max(1e4).optional(),
|
|
203
180
|
waveform: zod.z.array(zod.z.number().int().min(0).max(100)).max(1e3).optional(),
|
|
204
181
|
/** Lifted from the transcript enrichment record; absent until transcription completes. */
|
|
205
|
-
transcript: TimedTranscriptSchema.optional()
|
|
182
|
+
transcript: TimedTranscriptSchema.optional(),
|
|
183
|
+
/**
|
|
184
|
+
* Per-stage audio-processing status (denoise / trim / transcribe /
|
|
185
|
+
* waveform), when the app opted into processing. Absent otherwise. A
|
|
186
|
+
* `pending` stage means the client should poll (or re-render) for the
|
|
187
|
+
* result — including a stage that returns to `pending` after having been
|
|
188
|
+
* `ready`, which is how a recompute surfaces. Once a byte-mutating stage
|
|
189
|
+
* completes, `url`/`durationMs`/`waveform` above already resolve to the
|
|
190
|
+
* processed audio variant. See `types/processing.ts`.
|
|
191
|
+
*/
|
|
192
|
+
processing: ProcessingViewSchema.optional()
|
|
206
193
|
});
|
|
207
194
|
var AudioPostRecordSchema = zod.z.object({
|
|
208
195
|
/** Storage id (rkey/doc id). */
|
|
209
196
|
id: zod.z.string(),
|
|
197
|
+
/**
|
|
198
|
+
* Content CID of the canonical lexicon record (CIDv1, dag-cbor, sha2-256
|
|
199
|
+
* — the AT Protocol record-CID rule). Computed at write time over the
|
|
200
|
+
* lexicon projection (public fields only, NOT the storage/tenancy fields
|
|
201
|
+
* below), so StrongRefs built from it are verifiable content addresses.
|
|
202
|
+
*/
|
|
203
|
+
cid: zod.z.string(),
|
|
210
204
|
// --- Tenancy + facets (storage-indexed; NOT in the lexicon) ---
|
|
211
205
|
/** Origin app that created this record — the multi-tenant isolation key. */
|
|
212
206
|
originAppId: zod.z.string(),
|
|
@@ -224,9 +218,24 @@ var AudioPostRecordSchema = zod.z.object({
|
|
|
224
218
|
* opened the branch. Set on replies (deduped, 1–2 ids); absent on prompts
|
|
225
219
|
* (a prompt's repliers are the app's audience policy, not a fixed pair).
|
|
226
220
|
* Inherited down the branch so reply gating is an O(1) field check, never a
|
|
227
|
-
* thread walk.
|
|
221
|
+
* thread walk.
|
|
228
222
|
*/
|
|
229
223
|
threadParticipants: zod.z.array(zod.z.string()).optional(),
|
|
224
|
+
/**
|
|
225
|
+
* Author of this reply's thread ROOT (the prompt) — the reply's recipient,
|
|
226
|
+
* the person whose "replies to me" feed it lands in. Denormalized at write
|
|
227
|
+
* time so "replies whose root author is X" is a cheap composite-index query
|
|
228
|
+
* (see `queryByRootAuthor`). Set on replies (`kind === 'reply'`); absent on
|
|
229
|
+
* prompts. Storage-layer facet, NOT in the public lexicon or the record CID.
|
|
230
|
+
*/
|
|
231
|
+
rootAuthorId: zod.z.string().optional(),
|
|
232
|
+
/**
|
|
233
|
+
* Async audio-processing state (transcribe / denoise), present iff the app
|
|
234
|
+
* opted into processing on create. Mutated by the processing worker after
|
|
235
|
+
* the post is created — storage-layer, NOT in the lexicon or the record
|
|
236
|
+
* CID. See `types/processing.ts`.
|
|
237
|
+
*/
|
|
238
|
+
processing: ProcessingStateSchema.optional(),
|
|
230
239
|
// --- Lexicon fields (public contract) ---
|
|
231
240
|
/** User-authored text (bsky-semantic). May be empty for pure-audio posts. NEVER the transcript. */
|
|
232
241
|
text: zod.z.string().max(3e3),
|
|
@@ -245,10 +254,11 @@ var AudioPostRecordSchema = zod.z.object({
|
|
|
245
254
|
}).refine(
|
|
246
255
|
// `kind` is denormalized from `reply` presence at write time; enforce the
|
|
247
256
|
// invariant so an inconsistent record can't be written or read silently.
|
|
248
|
-
// A reply has `reply
|
|
249
|
-
|
|
257
|
+
// A reply has `reply`, no `title`, and a stamped `rootAuthorId` (its
|
|
258
|
+
// recipient facet); a prompt has none of those.
|
|
259
|
+
(r) => r.kind === "reply" ? !!r.reply && r.title === void 0 && r.rootAuthorId !== void 0 : !r.reply && r.rootAuthorId === void 0,
|
|
250
260
|
{
|
|
251
|
-
message: "kind must match reply presence: 'reply' \u21D2 reply set
|
|
261
|
+
message: "kind must match reply presence: 'reply' \u21D2 reply set, no title, rootAuthorId set; 'prompt' \u21D2 no reply, no rootAuthorId",
|
|
252
262
|
path: ["kind"]
|
|
253
263
|
}
|
|
254
264
|
);
|
|
@@ -291,9 +301,13 @@ var PostRecordPublicSchema = zod.z.object({
|
|
|
291
301
|
var AudioPostViewSchema = zod.z.object({
|
|
292
302
|
/** at:// URI (or internal ref) identifying the post. */
|
|
293
303
|
uri: zod.z.string(),
|
|
294
|
-
|
|
304
|
+
/** Content CID of the canonical record (see `AudioPostRecordSchema.cid`). */
|
|
305
|
+
cid: zod.z.string(),
|
|
295
306
|
kind: zod.z.enum(["prompt", "reply"]),
|
|
296
|
-
|
|
307
|
+
/** The acting actor's app-scoped id — an opaque attribution ref, not a profile. */
|
|
308
|
+
authorId: zod.z.string(),
|
|
309
|
+
/** The author's app-asserted AT Protocol DID, when the caller provided one. */
|
|
310
|
+
authorDid: zod.z.string().optional(),
|
|
297
311
|
record: PostRecordPublicSchema,
|
|
298
312
|
/** Hydrated audio embed (signed URL + lifted transcript). */
|
|
299
313
|
embed: AudioEmbedViewSchema.optional(),
|