@antiphony/shared 0.1.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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/cjs/api-codecs.cjs +971 -0
  4. package/dist/cjs/api-codecs.d.cts +542 -0
  5. package/dist/cjs/errors/index.cjs +56 -0
  6. package/dist/cjs/errors/index.d.cts +45 -0
  7. package/dist/cjs/index.cjs +1276 -0
  8. package/dist/cjs/index.d.cts +11 -0
  9. package/dist/cjs/nsid.cjs +37 -0
  10. package/dist/cjs/nsid.d.cts +42 -0
  11. package/dist/cjs/observability/index.cjs +58 -0
  12. package/dist/cjs/observability/index.d.cts +1 -0
  13. package/dist/cjs/observability/report-error.cjs +58 -0
  14. package/dist/cjs/observability/report-error.d.cts +48 -0
  15. package/dist/cjs/types/api.cjs +726 -0
  16. package/dist/cjs/types/api.d.cts +494 -0
  17. package/dist/cjs/types/audio.cjs +835 -0
  18. package/dist/cjs/types/audio.d.cts +1149 -0
  19. package/dist/cjs/types/blob.cjs +23 -0
  20. package/dist/cjs/types/blob.d.cts +45 -0
  21. package/dist/cjs/types/channels.cjs +170 -0
  22. package/dist/cjs/types/channels.d.cts +262 -0
  23. package/dist/cjs/types/records.cjs +407 -0
  24. package/dist/cjs/types/records.d.cts +840 -0
  25. package/dist/cjs/types/storage.cjs +414 -0
  26. package/dist/cjs/types/storage.d.cts +197 -0
  27. package/dist/cjs/types/views.cjs +820 -0
  28. package/dist/cjs/types/views.d.cts +9806 -0
  29. package/dist/cjs/utils/index.cjs +8 -0
  30. package/dist/cjs/utils/index.d.cts +7 -0
  31. package/dist/esm/api-codecs.d.ts +542 -0
  32. package/dist/esm/api-codecs.js +6 -0
  33. package/dist/esm/chunk-24KIXQZK.js +6 -0
  34. package/dist/esm/chunk-3WZJXJNU.js +378 -0
  35. package/dist/esm/chunk-5EHV73BA.js +20 -0
  36. package/dist/esm/chunk-72G3LBUQ.js +1 -0
  37. package/dist/esm/chunk-7LW2FHLD.js +48 -0
  38. package/dist/esm/chunk-7V44CPRR.js +132 -0
  39. package/dist/esm/chunk-BUNMR4ZO.js +127 -0
  40. package/dist/esm/chunk-EA4OONDV.js +380 -0
  41. package/dist/esm/chunk-F2CANZZ7.js +40 -0
  42. package/dist/esm/chunk-ORMEWXMH.js +33 -0
  43. package/dist/esm/chunk-TIZRJORN.js +24 -0
  44. package/dist/esm/chunk-XDBKR6LW.js +32 -0
  45. package/dist/esm/errors/index.d.ts +45 -0
  46. package/dist/esm/errors/index.js +2 -0
  47. package/dist/esm/index.d.ts +11 -0
  48. package/dist/esm/index.js +12 -0
  49. package/dist/esm/nsid.d.ts +42 -0
  50. package/dist/esm/nsid.js +2 -0
  51. package/dist/esm/observability/index.d.ts +1 -0
  52. package/dist/esm/observability/index.js +3 -0
  53. package/dist/esm/observability/report-error.d.ts +48 -0
  54. package/dist/esm/observability/report-error.js +2 -0
  55. package/dist/esm/types/api.d.ts +494 -0
  56. package/dist/esm/types/api.js +5 -0
  57. package/dist/esm/types/audio.d.ts +1149 -0
  58. package/dist/esm/types/audio.js +5 -0
  59. package/dist/esm/types/blob.d.ts +45 -0
  60. package/dist/esm/types/blob.js +2 -0
  61. package/dist/esm/types/channels.d.ts +262 -0
  62. package/dist/esm/types/channels.js +162 -0
  63. package/dist/esm/types/records.d.ts +840 -0
  64. package/dist/esm/types/records.js +3 -0
  65. package/dist/esm/types/storage.d.ts +197 -0
  66. package/dist/esm/types/storage.js +26 -0
  67. package/dist/esm/types/views.d.ts +9806 -0
  68. package/dist/esm/types/views.js +4 -0
  69. package/dist/esm/utils/index.d.ts +7 -0
  70. package/dist/esm/utils/index.js +2 -0
  71. package/package.json +82 -0
@@ -0,0 +1,407 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+
5
+ // types/records.ts
6
+ var BlobRefSchema = zod.z.object({
7
+ /** Discriminator for AT Protocol type system */
8
+ $type: zod.z.literal("blob"),
9
+ /** Content Identifier (CID) or URL pointing to the blob */
10
+ ref: zod.z.string(),
11
+ /** MIME type of the blob (e.g., 'audio/webm') */
12
+ mimeType: zod.z.string(),
13
+ /** Size of the blob in bytes */
14
+ size: zod.z.number()
15
+ });
16
+
17
+ // types/records.ts
18
+ function httpsUrl() {
19
+ return zod.z.string().trim().url().refine((u) => /^https?:\/\//i.test(u), {
20
+ message: "URL must use the http or https scheme"
21
+ });
22
+ }
23
+ var FirestoreTimestampSchema = zod.z.union([
24
+ zod.z.custom((data) => {
25
+ return data && typeof data === "object" && (typeof data.toDate === "function" || "seconds" in data && "nanoseconds" in data);
26
+ }),
27
+ zod.z.string(),
28
+ zod.z.number(),
29
+ zod.z.date()
30
+ ]).transform((data, ctx) => {
31
+ let date;
32
+ if (data instanceof Date) {
33
+ date = data;
34
+ } else if (typeof data === "string") {
35
+ date = new Date(data);
36
+ } else if (typeof data === "number") {
37
+ date = new Date(data);
38
+ } else if (typeof data.toDate === "function") {
39
+ date = data.toDate();
40
+ } else {
41
+ const timestamp = data;
42
+ date = new Date(timestamp.seconds * 1e3 + timestamp.nanoseconds / 1e6);
43
+ }
44
+ if (Number.isNaN(date.getTime())) {
45
+ ctx.addIssue({
46
+ code: zod.z.ZodIssueCode.custom,
47
+ message: "FirestoreTimestamp coerced to Invalid Date"
48
+ });
49
+ return zod.z.NEVER;
50
+ }
51
+ return date;
52
+ });
53
+ var UserRecordSchema = zod.z.object({
54
+ /** Unique Firebase UID */
55
+ id: zod.z.string(),
56
+ /** Public handle (e.g. @brad). Optional for Lite Users. */
57
+ handle: zod.z.string().min(3).max(20).regex(/^[a-zA-Z0-9_]+$/).nullable().optional(),
58
+ /** User stated intent (e.g. "Podcaster", "Listener") */
59
+ usageIntent: zod.z.string().nullable().optional(),
60
+ /**
61
+ * Domain for federated handle support.
62
+ * Defaults to 'voxpop.com'.
63
+ */
64
+ domain: zod.z.string().default("voxpop.com"),
65
+ /**
66
+ * Display Name (e.g. "Brad Thorson"). Nullable: Firestore stores `null`
67
+ * when the user clears this field via the settings form, and the schema
68
+ * must match storage reality or `UserRecordSchema.parse` (in
69
+ * `getUserRecordByUid`) will throw.
70
+ */
71
+ displayName: zod.z.string().max(50).nullable().optional(),
72
+ /** Short bio/description — nullable for the same reason as displayName. */
73
+ bio: zod.z.string().max(160).nullable().optional(),
74
+ /** URL to avatar image — nullable for the same reason as displayName. */
75
+ avatarUrl: zod.z.string().url().nullable().optional(),
76
+ /** Optional personal website surfaced on the public profile. */
77
+ website: httpsUrl().nullable().optional(),
78
+ /** Up to 5 additional public links (label + URL) shown under the bio. */
79
+ links: zod.z.array(zod.z.object({
80
+ label: zod.z.string().min(1).max(40),
81
+ url: httpsUrl()
82
+ })).max(5).optional(),
83
+ /** When true and a Bluesky identity is linked, surfaces it on the public profile. */
84
+ showBlueskyPublicly: zod.z.boolean().optional(),
85
+ /** Server timestamp of creation */
86
+ createdAt: FirestoreTimestampSchema,
87
+ /** Individual account tier — free or creator_pro */
88
+ tier: zod.z.enum(["free", "creator_pro"]).default("free"),
89
+ /** Account status. Deactivated accounts retain data but are excluded from lookups. */
90
+ status: zod.z.enum(["active", "deactivated"]).default("active"),
91
+ /** Timestamp when the account was deactivated (soft deleted) */
92
+ deactivatedAt: FirestoreTimestampSchema.optional(),
93
+ /**
94
+ * Denormalized org memberships — { orgId: role } for fast lookup.
95
+ * Source of truth is organizations/{orgId}/members/{userId}.
96
+ * Kept in sync by Cloud Function trigger.
97
+ */
98
+ orgMemberships: zod.z.record(zod.z.string(), zod.z.enum(["owner", "admin", "member"])).optional()
99
+ });
100
+ var PromptRecordSchema = zod.z.object({
101
+ /** Unique Prompt ID */
102
+ id: zod.z.string(),
103
+ /** ID of the User who created this prompt. Always a user ID, never an org ID. @see UserRecord */
104
+ authorId: zod.z.string(),
105
+ /** Organization context this prompt belongs to (null = personal/no org) */
106
+ orgId: zod.z.string().nullable().optional(),
107
+ /** The main text of the question/prompt */
108
+ title: zod.z.string().min(3),
109
+ /** Optional extra context */
110
+ description: zod.z.string().nullable().optional(),
111
+ /** User who created this prompt (differs from authorId when org-owned) */
112
+ createdBy: zod.z.string().optional(),
113
+ /** URL to the recorded audio file (GCS) */
114
+ audioUrl: zod.z.string().url().or(zod.z.literal("")),
115
+ /** AT Protocol blob reference (future replacement for audioUrl) */
116
+ audio: BlobRefSchema.optional(),
117
+ /**
118
+ * AT Protocol URI returned by the publisher after a successful
119
+ * `repo.putRecord` against the author's PDS — e.g.
120
+ * `at://did:plc:abc123/com.voxpop.audio.prompt/3kj4...`. Optional,
121
+ * no migration: existing rows leave it unset. Format-validated so
122
+ * malformed strings surface at the schema-parse boundary rather than
123
+ * being silently stored.
124
+ */
125
+ atprotoUri: zod.z.string().regex(/^at:\/\/.+/).optional(),
126
+ /** Server timestamp of creation */
127
+ createdAt: FirestoreTimestampSchema,
128
+ /**
129
+ * Life-cycle status.
130
+ * - `live`: Visible and accepting replies.
131
+ * - `archived`: Visible but closed for new replies.
132
+ * - `deleted`: Soft deleted.
133
+ */
134
+ status: zod.z.enum(["live", "archived", "deleted"]).default("live"),
135
+ /** AI Enrichment Fields */
136
+ aiStatus: zod.z.enum(["pending", "complete", "error"]).optional(),
137
+ aiError: zod.z.string().optional(),
138
+ aiSummary: zod.z.string().optional(),
139
+ aiLabels: zod.z.array(zod.z.string()).optional(),
140
+ transcription: zod.z.string().optional(),
141
+ /** Pre-computed waveform peaks (normalized 0–1) for instant audio visualization */
142
+ waveformPeaks: zod.z.array(zod.z.number()).optional(),
143
+ /** Social Share Video Fields */
144
+ socialVideoUrl: zod.z.string().url().optional(),
145
+ socialVideoStoragePath: zod.z.string().optional(),
146
+ socialVideoStatus: zod.z.enum(["pending", "complete", "error"]).optional(),
147
+ socialVideoError: zod.z.string().optional(),
148
+ /** The audio URL/path used to generate the current video (for cache invalidation) */
149
+ socialVideoSourceAudio: zod.z.string().optional()
150
+ });
151
+ var ReplyRecordSchema = zod.z.object({
152
+ /** Unique Reply ID */
153
+ id: zod.z.string(),
154
+ /** The Prompt being replied to. @see PromptRecord */
155
+ promptId: zod.z.string(),
156
+ /** The User who replied. @see UserRecord */
157
+ authorId: zod.z.string(),
158
+ /** URL to the recorded audio file (GCS) */
159
+ audioUrl: zod.z.string().url(),
160
+ /** AT Protocol blob reference (future replacement for audioUrl) */
161
+ audio: BlobRefSchema.optional(),
162
+ /** Server timestamp of creation */
163
+ createdAt: FirestoreTimestampSchema,
164
+ /**
165
+ * Life-cycle status.
166
+ * - `live`: Visible and accepting replies.
167
+ * - `archived`: Visible but closed for new replies.
168
+ * - `deleted`: Soft deleted.
169
+ */
170
+ status: zod.z.enum(["live", "archived", "deleted"]).default("live"),
171
+ /**
172
+ * Pre-computed waveform peaks (normalized 0–1) for instant audio
173
+ * visualization. **Stays on canonical** (does NOT move to the
174
+ * enrichment doc) — produced by a plain ffmpeg pass at ingestion,
175
+ * not an AI step; a self-hoster without paid-tier AI still needs
176
+ * this on every reply for the audio player. See
177
+ * `specs/ai-enrichment-split.md` § 5.
178
+ */
179
+ waveformPeaks: zod.z.array(zod.z.number()).optional(),
180
+ /**
181
+ * Duration of the audio in seconds, computed server-side from
182
+ * ffmpeg. **Stays on canonical** (same reasoning as `waveformPeaks`
183
+ * above — ingestion ffmpeg output, not an AI enrichment). See
184
+ * `specs/ai-enrichment-split.md` § 5.
185
+ */
186
+ audioDurationSec: zod.z.number().optional()
187
+ });
188
+ var ReplyEnrichmentRecordSchema = zod.z.object({
189
+ id: zod.z.string(),
190
+ /** Private notes by the prompt author about this reply. */
191
+ notes: zod.z.string().optional(),
192
+ // === AI-enrichment fields (sole source of truth post Stage 4) ===
193
+ //
194
+ // These lifted off canonical in Stage 4 of `specs/ai-enrichment-split.md`.
195
+ // Writers (`functions/`) route AI updates here via the split-write
196
+ // helper in `functions/src/services/replyEnrichmentDualWrite.ts`;
197
+ // readers source them via the hydrator's enrichment branch (see
198
+ // `packages/core/services/hydration.ts`).
199
+ // --- AI core (Gemini-generated) ---
200
+ aiStatus: zod.z.enum(["pending", "complete", "error", "skipped_too_short"]).optional(),
201
+ aiError: zod.z.string().optional(),
202
+ aiSummary: zod.z.string().optional(),
203
+ aiLabels: zod.z.array(zod.z.string()).optional(),
204
+ transcription: zod.z.string().optional(),
205
+ sentiment: zod.z.enum(["Positive", "Negative", "Neutral"]).optional(),
206
+ /** Must match the widened enum in `ReplyRecordSchema.energyLevel`. */
207
+ energyLevel: zod.z.enum(["High", "Low", "Neutral"]).optional(),
208
+ engagementScore: zod.z.number().min(1).max(10).optional(),
209
+ // --- Voice isolation (ElevenLabs, paid tier) ---
210
+ /** Noise-reduced audio URL — replaces `audioUrl` for downstream players when present. PUBLIC. */
211
+ enhancedAudioUrl: zod.z.string().url().optional(),
212
+ /** Storage path companion to `enhancedAudioUrl` — private. */
213
+ enhancedStoragePath: zod.z.string().optional(),
214
+ // --- Social-share video (paid tier) ---
215
+ //
216
+ // All `socialVideo*` fields are creator-only. The URL points at the
217
+ // generated artifact (a video file the creator can download and post
218
+ // to social media); the reply detail page does NOT render it.
219
+ socialVideoUrl: zod.z.string().url().optional(),
220
+ socialVideoStoragePath: zod.z.string().optional(),
221
+ socialVideoStatus: zod.z.enum(["pending", "complete", "error"]).optional(),
222
+ socialVideoError: zod.z.string().optional(),
223
+ /** The audio URL/path used to generate the current video (for cache invalidation). */
224
+ socialVideoSourceAudio: zod.z.string().optional()
225
+ });
226
+ var PersonEnrichmentRecordSchema = zod.z.object({
227
+ /** Composite key `${viewerUid}_${targetUid}`. */
228
+ id: zod.z.string(),
229
+ /** Per-viewer CRM notes about the target. */
230
+ notes: zod.z.string().optional(),
231
+ /** Per-viewer freeform tags about the target. */
232
+ tags: zod.z.array(zod.z.string()).optional(),
233
+ // === Identity-merge: alias set ===
234
+ //
235
+ // Other identifiers the viewer has declared to be the SAME person as
236
+ // this entry's `targetUid`. vCard/URI-aligned: each entry is a
237
+ // scheme-prefixed identifier, so one field generalizes across networks
238
+ // AND across the future contact-sync import (which carries phone/email):
239
+ // "uid:<firebaseUid>" | "did:plc:…" | "handle:bob.bsky.social"
240
+ // | "tel:+15551234567" | "mailto:x@example.com"
241
+ // Mirrors vCard 4.0's `UID` + `CLIENTPIDMAP`/`PID` source-tracking
242
+ // (RFC 6350) — the standard answer to "the same person from multiple
243
+ // sources". See `specs/people-lexicon-prior-art.md`. The People
244
+ // read-path collapses any replier whose "uid:<uid>" appears here into
245
+ // this primary entry. Empty until the merge feature is used.
246
+ aliases: zod.z.array(zod.z.string()).optional(),
247
+ lastUpdated: FirestoreTimestampSchema.optional()
248
+ });
249
+ var SipEnrichmentSchema = zod.z.object({
250
+ sipUri: zod.z.string(),
251
+ sipUsername: zod.z.string(),
252
+ sipSecret: zod.z.string(),
253
+ provider: zod.z.enum(["twilio", "plivo", "internal"])
254
+ });
255
+ var CallForwardingConfigSchema = zod.z.object({
256
+ /** User's personal phone number (E.164) */
257
+ phoneNumber: zod.z.string(),
258
+ /** Line type from Twilio Lookup v2 */
259
+ lineType: zod.z.string().nullable(),
260
+ /** Carrier name from Twilio Lookup v2 */
261
+ carrier: zod.z.string().nullable(),
262
+ /** free = shared Twilio number (ForwardedFrom routing), paid = dedicated number */
263
+ tier: zod.z.enum(["free", "paid"]),
264
+ /** The VoxPop Twilio number calls forward to (E.164) */
265
+ voxpopNumber: zod.z.string(),
266
+ /** Twilio Phone Number SID (paid tier only) */
267
+ twilioNumberSid: zod.z.string().nullable().optional(),
268
+ /** Forwarding verification state */
269
+ verificationStatus: zod.z.enum(["pending", "verifying", "verified", "failed"]).default("pending"),
270
+ /** Last verification attempt timestamp */
271
+ lastVerificationAt: FirestoreTimestampSchema.optional(),
272
+ /** Number of verification attempts */
273
+ verificationAttempts: zod.z.number().default(0),
274
+ /** Reason for verification failure */
275
+ failureReason: zod.z.string().nullable().optional(),
276
+ /** Whether call forwarding is active */
277
+ enabled: zod.z.boolean().default(false),
278
+ createdAt: FirestoreTimestampSchema,
279
+ updatedAt: FirestoreTimestampSchema
280
+ });
281
+ var ConnectorTypeSchema = zod.z.enum(["telephony"]);
282
+ var ConnectorStatusSchema = zod.z.object({
283
+ /** Coarse, generic lifecycle the control-plane UI can render without
284
+ * connector-specific knowledge. */
285
+ state: zod.z.enum(["unconfigured", "pending", "active", "error", "disabled"]).default("unconfigured"),
286
+ detail: zod.z.string().nullable().optional(),
287
+ /** Opaque, connector-specific status detail (e.g. telephony's
288
+ * `{ verificationStatus, verificationAttempts, failureReason }`). Connector-
289
+ * owned like the rest of `status` — never user-writable. Nullable so a doc
290
+ * that stored `data: null` round-trips without a parse error. */
291
+ data: zod.z.record(zod.z.unknown()).nullable().optional(),
292
+ updatedAt: FirestoreTimestampSchema.optional()
293
+ });
294
+ var ConnectorConfigRecordSchema = zod.z.object({
295
+ connectorType: ConnectorTypeSchema,
296
+ /** The owning user. Org-scoping (ownerId as an orgId) is a later step. */
297
+ ownerId: zod.z.string(),
298
+ /** Opaque, connector-specific blob. Core does not interpret this. */
299
+ settings: zod.z.record(zod.z.unknown()).default({}),
300
+ /** Reference into a secret store (e.g. Secret Manager key). Never a raw secret. */
301
+ secretRef: zod.z.string().nullable().optional(),
302
+ enabled: zod.z.boolean().default(false),
303
+ status: ConnectorStatusSchema.default({ state: "unconfigured" }),
304
+ createdAt: FirestoreTimestampSchema,
305
+ updatedAt: FirestoreTimestampSchema
306
+ });
307
+ var ScreeningRuleRecordSchema = zod.z.object({
308
+ id: zod.z.string(),
309
+ ownerId: zod.z.string(),
310
+ /** The caller number this rule matches (E.164). Format-enforced at the
311
+ * record level so any writer (API, future contact-sync/callback) can't
312
+ * persist a malformed number. */
313
+ e164: zod.z.string().regex(/^\+[1-9]\d{6,14}$/, "Must be an E.164 phone number"),
314
+ /** Display label, e.g. "Mom" / "Delta Airlines". null = unlabeled. */
315
+ label: zod.z.string().nullable().optional(),
316
+ /** `allow` = ring through; `screen` = async voicemail. */
317
+ action: zod.z.enum(["allow", "screen"]),
318
+ /** Provenance. `manual` = user-created; the others are future writers. */
319
+ source: zod.z.enum(["manual", "contact-sync", "callback"]),
320
+ /** null = permanent; a date = self-expiring exception. */
321
+ expiresAt: FirestoreTimestampSchema.nullable().optional(),
322
+ createdAt: FirestoreTimestampSchema
323
+ });
324
+ var OrganizationRecordSchema = zod.z.object({
325
+ id: zod.z.string(),
326
+ /** Display name of the organization */
327
+ name: zod.z.string().min(3).max(50),
328
+ /**
329
+ * Unique handle for the organization (e.g. voxpop.com/@mypodcast).
330
+ * Used for public URLs.
331
+ */
332
+ slug: zod.z.string().min(3).max(30).regex(/^[a-z0-9-]+$/),
333
+ /** URL to avatar/logo */
334
+ avatarUrl: zod.z.string().url().optional(),
335
+ /** URL to the podcast RSS feed */
336
+ rssFeedUrl: zod.z.string().url().optional(),
337
+ /** External website URL */
338
+ websiteUrl: httpsUrl().optional(),
339
+ /** Description or tagline */
340
+ description: zod.z.string().optional(),
341
+ /** Owner ID (User ID) */
342
+ ownerId: zod.z.string(),
343
+ /** Server timestamp of creation */
344
+ createdAt: FirestoreTimestampSchema,
345
+ /** Verified domain (e.g., "acme.com") — enables auto-join, enterprise features */
346
+ domain: zod.z.string().nullable().optional(),
347
+ /** Whether the domain has been verified via DNS TXT record */
348
+ domainVerified: zod.z.boolean().default(false),
349
+ /** DNS verification token (stored server-side until verification completes) */
350
+ domainVerificationToken: zod.z.string().optional(),
351
+ /** Billing email — required for paid orgs, where invoices go */
352
+ billingEmail: zod.z.string().email().nullable().optional(),
353
+ /**
354
+ * Tier determines isolation and feature set. Orgs are meant to be paid
355
+ * (business or enterprise); free/pro exist on the enum for the individual track.
356
+ * Default is provisionally `business` (the entry paid tier) — it's assigned at
357
+ * creation with no subscription behind it yet, so it stays provisional until billing
358
+ * promotes/reconciles the org. See docs/tech-debt.md "Org tier billing reconciliation".
359
+ */
360
+ tier: zod.z.enum(["free", "pro", "business", "enterprise"]).default("business"),
361
+ /** Stripe Customer ID */
362
+ stripeCustomerId: zod.z.string().optional(),
363
+ /** Subscription Status */
364
+ subscriptionStatus: zod.z.enum(["active", "trialing", "past_due", "canceled", "unpaid"]).optional()
365
+ });
366
+ var OrganizationMemberRecordSchema = zod.z.object({
367
+ orgId: zod.z.string(),
368
+ userId: zod.z.string(),
369
+ /** Role in the organization */
370
+ role: zod.z.enum(["owner", "admin", "member"]),
371
+ /** Server timestamp of joining */
372
+ joinedAt: FirestoreTimestampSchema,
373
+ invitedBy: zod.z.string().optional()
374
+ });
375
+ var OrgInviteRecordSchema = zod.z.object({
376
+ id: zod.z.string(),
377
+ orgId: zod.z.string(),
378
+ /** Email the invite was sent to */
379
+ email: zod.z.string().email(),
380
+ /** Role to assign on acceptance */
381
+ role: zod.z.enum(["admin", "member"]),
382
+ /** User ID of who sent the invite */
383
+ invitedBy: zod.z.string(),
384
+ /** Invite lifecycle status */
385
+ status: zod.z.enum(["pending", "accepted", "expired", "revoked"]).default("pending"),
386
+ /** Server timestamp of creation */
387
+ createdAt: FirestoreTimestampSchema,
388
+ /** When this invite expires */
389
+ expiresAt: FirestoreTimestampSchema
390
+ });
391
+
392
+ exports.CallForwardingConfigSchema = CallForwardingConfigSchema;
393
+ exports.ConnectorConfigRecordSchema = ConnectorConfigRecordSchema;
394
+ exports.ConnectorStatusSchema = ConnectorStatusSchema;
395
+ exports.ConnectorTypeSchema = ConnectorTypeSchema;
396
+ exports.FirestoreTimestampSchema = FirestoreTimestampSchema;
397
+ exports.OrgInviteRecordSchema = OrgInviteRecordSchema;
398
+ exports.OrganizationMemberRecordSchema = OrganizationMemberRecordSchema;
399
+ exports.OrganizationRecordSchema = OrganizationRecordSchema;
400
+ exports.PersonEnrichmentRecordSchema = PersonEnrichmentRecordSchema;
401
+ exports.PromptRecordSchema = PromptRecordSchema;
402
+ exports.ReplyEnrichmentRecordSchema = ReplyEnrichmentRecordSchema;
403
+ exports.ReplyRecordSchema = ReplyRecordSchema;
404
+ exports.ScreeningRuleRecordSchema = ScreeningRuleRecordSchema;
405
+ exports.SipEnrichmentSchema = SipEnrichmentSchema;
406
+ exports.UserRecordSchema = UserRecordSchema;
407
+ exports.httpsUrl = httpsUrl;