sms-panel-manager 0.0.0-stage → 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.
@@ -0,0 +1,363 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('crypto');
4
+ var mongoose = require('mongoose');
5
+
6
+ // src/mongoose/idempotency.ts
7
+ var IdempotencyConflictError = class extends Error {
8
+ code = "idempotency_conflict";
9
+ constructor() {
10
+ super("The idempotency key was already used with another request.");
11
+ this.name = "IdempotencyConflictError";
12
+ }
13
+ };
14
+ function isDuplicateKeyError(error) {
15
+ return typeof error === "object" && error !== null && "code" in error && error.code === 11e3;
16
+ }
17
+ function fingerprintsEqual(left, right) {
18
+ const leftBuffer = Buffer.from(left);
19
+ const rightBuffer = Buffer.from(right);
20
+ return leftBuffer.byteLength === rightBuffer.byteLength && crypto.timingSafeEqual(leftBuffer, rightBuffer);
21
+ }
22
+ var MongooseIdempotencyStore = class {
23
+ constructor(model) {
24
+ this.model = model;
25
+ }
26
+ model;
27
+ async reserve(input) {
28
+ try {
29
+ const created = await this.model.create({
30
+ ...input,
31
+ state: "reserved",
32
+ currentAttemptNumber: 0
33
+ });
34
+ return {
35
+ created: true,
36
+ operation: created.toObject()
37
+ };
38
+ } catch (error) {
39
+ if (!isDuplicateKeyError(error)) throw error;
40
+ const existing = await this.model.findOne({
41
+ scopeKey: input.scopeKey,
42
+ actionKey: input.actionKey,
43
+ idempotencyKey: input.idempotencyKey
44
+ }).select("+requestFingerprint +recipientFingerprint").lean().exec();
45
+ if (existing === null) throw error;
46
+ if (!fingerprintsEqual(
47
+ existing.requestFingerprint,
48
+ input.requestFingerprint
49
+ )) {
50
+ throw new IdempotencyConflictError();
51
+ }
52
+ return {
53
+ created: false,
54
+ operation: existing
55
+ };
56
+ }
57
+ }
58
+ };
59
+ var OperationStateConflictError = class extends Error {
60
+ code = "operation_state_conflict";
61
+ constructor(message) {
62
+ super(message);
63
+ this.name = "OperationStateConflictError";
64
+ }
65
+ };
66
+ function snapshot(record) {
67
+ return {
68
+ id: String(record._id),
69
+ state: record.state,
70
+ ...record.providerMessageId === void 0 || record.providerMessageId === null ? {} : { providerMessageId: record.providerMessageId }
71
+ };
72
+ }
73
+ function completion(result) {
74
+ switch (result.status) {
75
+ case "accepted":
76
+ return {
77
+ operationState: "submitted",
78
+ attemptState: "accepted",
79
+ providerMessageId: result.providerMessageId
80
+ };
81
+ case "rejected":
82
+ return {
83
+ operationState: "failed",
84
+ attemptState: "rejected",
85
+ normalizedError: { code: result.code }
86
+ };
87
+ case "pre-acceptance-failure":
88
+ return {
89
+ operationState: "failed",
90
+ attemptState: "pre_acceptance_failure",
91
+ normalizedError: { code: result.code }
92
+ };
93
+ case "unknown":
94
+ return {
95
+ operationState: "unknown",
96
+ attemptState: "unknown",
97
+ normalizedError: { code: result.code }
98
+ };
99
+ }
100
+ }
101
+ var MongooseSendOperationStore = class {
102
+ constructor(models) {
103
+ this.models = models;
104
+ this.idempotency = new MongooseIdempotencyStore(models.SendOperation);
105
+ }
106
+ models;
107
+ idempotency;
108
+ async reserve(input) {
109
+ const result = await this.idempotency.reserve(input);
110
+ return { created: result.created, operation: snapshot(result.operation) };
111
+ }
112
+ async beginAttempt(input) {
113
+ if (!mongoose.Types.ObjectId.isValid(input.operationId) || !mongoose.Types.ObjectId.isValid(input.providerConfigurationId)) {
114
+ throw new OperationStateConflictError(
115
+ "Invalid operation or provider identifier."
116
+ );
117
+ }
118
+ const operation = await this.models.SendOperation.findOneAndUpdate(
119
+ { _id: input.operationId, state: "reserved", currentAttemptNumber: 0 },
120
+ { $set: { state: "processing" }, $inc: { currentAttemptNumber: 1 } },
121
+ { returnDocument: "after" }
122
+ ).lean().exec();
123
+ if (operation === null) {
124
+ throw new OperationStateConflictError(
125
+ "Operation is not available for dispatch."
126
+ );
127
+ }
128
+ try {
129
+ await this.models.SendAttempt.create({
130
+ operationId: operation._id,
131
+ attemptNumber: 1,
132
+ providerConfigurationId: new mongoose.Types.ObjectId(
133
+ input.providerConfigurationId
134
+ ),
135
+ adapterId: input.adapterId,
136
+ state: "dispatching",
137
+ startedAt: /* @__PURE__ */ new Date(),
138
+ expiresAt: input.expiresAt
139
+ });
140
+ } catch (cause) {
141
+ await this.models.SendOperation.updateOne(
142
+ { _id: operation._id, state: "processing", currentAttemptNumber: 1 },
143
+ { $set: { state: "reserved", currentAttemptNumber: 0 } }
144
+ ).exec();
145
+ throw cause;
146
+ }
147
+ }
148
+ async completeAttempt(operationId, result) {
149
+ if (!mongoose.Types.ObjectId.isValid(operationId)) {
150
+ throw new OperationStateConflictError("Invalid operation identifier.");
151
+ }
152
+ const mapped = completion(result);
153
+ const operation = await this.models.SendOperation.findOneAndUpdate(
154
+ { _id: operationId, state: "processing", currentAttemptNumber: 1 },
155
+ {
156
+ $set: {
157
+ state: mapped.operationState,
158
+ ...mapped.providerMessageId === void 0 ? {} : { providerMessageId: mapped.providerMessageId },
159
+ ...mapped.normalizedError === void 0 ? {} : { normalizedError: mapped.normalizedError }
160
+ }
161
+ },
162
+ { returnDocument: "after" }
163
+ ).lean().exec();
164
+ if (operation === null) {
165
+ throw new OperationStateConflictError(
166
+ "Logical operation could not be completed."
167
+ );
168
+ }
169
+ await this.models.SendAttempt.updateOne(
170
+ { operationId, attemptNumber: 1, state: "dispatching" },
171
+ {
172
+ $set: {
173
+ state: mapped.attemptState,
174
+ completedAt: /* @__PURE__ */ new Date(),
175
+ ...mapped.providerMessageId === void 0 ? {} : { providerMessageId: mapped.providerMessageId },
176
+ ...mapped.normalizedError === void 0 ? {} : { normalizedError: mapped.normalizedError }
177
+ }
178
+ }
179
+ ).exec();
180
+ return snapshot(operation);
181
+ }
182
+ };
183
+ var encryptedCredentialSchema = new mongoose.Schema(
184
+ {
185
+ algorithm: { type: String, required: true, enum: ["aes-256-gcm"] },
186
+ keyId: { type: String, required: true },
187
+ configurationId: { type: String, required: true },
188
+ adapterId: { type: String, required: true },
189
+ scopeKey: { type: String, required: true },
190
+ credentialRevision: { type: Number, required: true },
191
+ nonce: { type: String, required: true, select: false },
192
+ ciphertext: { type: String, required: true, select: false },
193
+ authTag: { type: String, required: true, select: false },
194
+ schemaVersion: { type: Number, required: true }
195
+ },
196
+ { _id: false }
197
+ );
198
+ var providerConfigurationSchema = new mongoose.Schema(
199
+ {
200
+ scopeKey: { type: String, required: true },
201
+ adapterId: { type: String, required: true },
202
+ label: { type: String, required: true },
203
+ encryptedCredentials: { type: encryptedCredentialSchema, select: false },
204
+ credentialRevision: { type: Number, required: true, default: 0 },
205
+ verification: {
206
+ status: {
207
+ type: String,
208
+ required: true,
209
+ enum: ["unverified", "verified", "invalid", "unavailable"],
210
+ default: "unverified"
211
+ },
212
+ method: String,
213
+ verifiedAt: Date,
214
+ safeErrorCode: String
215
+ },
216
+ enabled: { type: Boolean, required: true, default: false },
217
+ version: { type: Number, required: true, default: 1 }
218
+ },
219
+ { timestamps: true, minimize: false }
220
+ );
221
+ providerConfigurationSchema.index({ scopeKey: 1, label: 1 }, { unique: true });
222
+ providerConfigurationSchema.index({ scopeKey: 1, adapterId: 1, enabled: 1 });
223
+ var actionConfigurationSchema = new mongoose.Schema(
224
+ {
225
+ scopeKey: { type: String, required: true },
226
+ actionKey: { type: String, required: true },
227
+ providerConfigurationId: {
228
+ type: mongoose.Schema.Types.ObjectId,
229
+ required: true
230
+ },
231
+ templateReference: { type: mongoose.Schema.Types.Mixed, required: true },
232
+ bindings: { type: [mongoose.Schema.Types.Mixed], required: true, default: [] },
233
+ validation: {
234
+ level: {
235
+ type: String,
236
+ required: true,
237
+ enum: ["local", "remote", "deferred"]
238
+ },
239
+ remoteTemplateFingerprint: String,
240
+ validatedAt: { type: Date, required: true },
241
+ warnings: { type: [String], required: true, default: [] }
242
+ },
243
+ enabled: { type: Boolean, required: true, default: false },
244
+ version: { type: Number, required: true, default: 1 }
245
+ },
246
+ { timestamps: true, minimize: false }
247
+ );
248
+ actionConfigurationSchema.index(
249
+ { scopeKey: 1, actionKey: 1 },
250
+ { unique: true }
251
+ );
252
+ var sendOperationSchema = new mongoose.Schema(
253
+ {
254
+ scopeKey: { type: String, required: true },
255
+ actionKey: { type: String, required: true },
256
+ actionDefinitionVersion: { type: String, required: true },
257
+ configurationVersion: { type: Number, required: true },
258
+ idempotencyKey: { type: String, required: true },
259
+ requestFingerprint: { type: String, required: true, select: false },
260
+ recipientFingerprint: { type: String, required: true, select: false },
261
+ state: {
262
+ type: String,
263
+ required: true,
264
+ enum: [
265
+ "reserved",
266
+ "processing",
267
+ "submitted",
268
+ "delivered",
269
+ "failed",
270
+ "unknown"
271
+ ],
272
+ default: "reserved"
273
+ },
274
+ currentAttemptNumber: { type: Number, required: true, default: 0 },
275
+ providerMessageId: String,
276
+ normalizedError: mongoose.Schema.Types.Mixed,
277
+ expiresAt: { type: Date, required: true }
278
+ },
279
+ { timestamps: true, minimize: false }
280
+ );
281
+ sendOperationSchema.index(
282
+ { scopeKey: 1, actionKey: 1, idempotencyKey: 1 },
283
+ { unique: true }
284
+ );
285
+ sendOperationSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
286
+ var sendAttemptSchema = new mongoose.Schema(
287
+ {
288
+ operationId: { type: mongoose.Schema.Types.ObjectId, required: true },
289
+ attemptNumber: { type: Number, required: true },
290
+ providerConfigurationId: {
291
+ type: mongoose.Schema.Types.ObjectId,
292
+ required: true
293
+ },
294
+ adapterId: { type: String, required: true },
295
+ state: {
296
+ type: String,
297
+ required: true,
298
+ enum: [
299
+ "created",
300
+ "dispatching",
301
+ "accepted",
302
+ "delivered",
303
+ "rejected",
304
+ "pre_acceptance_failure",
305
+ "unknown"
306
+ ]
307
+ },
308
+ startedAt: { type: Date, required: true },
309
+ completedAt: Date,
310
+ durationMs: Number,
311
+ providerMessageId: String,
312
+ normalizedError: mongoose.Schema.Types.Mixed,
313
+ safeDiagnostics: mongoose.Schema.Types.Mixed,
314
+ expiresAt: { type: Date, required: true }
315
+ },
316
+ { timestamps: true, minimize: false }
317
+ );
318
+ sendAttemptSchema.index({ operationId: 1, attemptNumber: 1 }, { unique: true });
319
+ sendAttemptSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
320
+ function modelFor(connection, name, schema, collection) {
321
+ return connection.models[name] ?? connection.model(name, schema, collection);
322
+ }
323
+ function createSmsMongooseModels(connection, options = {}) {
324
+ const prefix = options.collectionPrefix ?? "sms";
325
+ if (!/^[a-zA-Z0-9_-]+$/.test(prefix)) {
326
+ throw new TypeError("Collection prefix contains unsupported characters.");
327
+ }
328
+ const modelPrefix = prefix.replace(/[^a-zA-Z0-9]/g, "_");
329
+ return {
330
+ ProviderConfiguration: modelFor(
331
+ connection,
332
+ `${modelPrefix}_SmsProviderConfiguration`,
333
+ providerConfigurationSchema,
334
+ `${prefix}_provider_configurations`
335
+ ),
336
+ ActionConfiguration: modelFor(
337
+ connection,
338
+ `${modelPrefix}_SmsActionConfiguration`,
339
+ actionConfigurationSchema,
340
+ `${prefix}_action_configurations`
341
+ ),
342
+ SendOperation: modelFor(
343
+ connection,
344
+ `${modelPrefix}_SmsSendOperation`,
345
+ sendOperationSchema,
346
+ `${prefix}_send_operations`
347
+ ),
348
+ SendAttempt: modelFor(
349
+ connection,
350
+ `${modelPrefix}_SmsSendAttempt`,
351
+ sendAttemptSchema,
352
+ `${prefix}_send_attempts`
353
+ )
354
+ };
355
+ }
356
+
357
+ exports.IdempotencyConflictError = IdempotencyConflictError;
358
+ exports.MongooseIdempotencyStore = MongooseIdempotencyStore;
359
+ exports.MongooseSendOperationStore = MongooseSendOperationStore;
360
+ exports.OperationStateConflictError = OperationStateConflictError;
361
+ exports.createSmsMongooseModels = createSmsMongooseModels;
362
+ //# sourceMappingURL=mongoose.cjs.map
363
+ //# sourceMappingURL=mongoose.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mongoose/idempotency.ts","../src/mongoose/operation-store.ts","../src/mongoose/models.ts"],"names":["timingSafeEqual","Types","Schema"],"mappings":"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","file":"mongoose.cjs","sourcesContent":["import { timingSafeEqual } from \"node:crypto\";\n\nimport type { Model } from \"mongoose\";\n\nimport type { SendOperationRecord } from \"./models.js\";\n\nexport class IdempotencyConflictError extends Error {\n readonly code = \"idempotency_conflict\";\n\n constructor() {\n super(\"The idempotency key was already used with another request.\");\n this.name = \"IdempotencyConflictError\";\n }\n}\n\nexport interface ReserveOperationInput {\n readonly scopeKey: string;\n readonly actionKey: string;\n readonly actionDefinitionVersion: string;\n readonly configurationVersion: number;\n readonly idempotencyKey: string;\n readonly requestFingerprint: string;\n readonly recipientFingerprint: string;\n readonly expiresAt: Date;\n}\n\nexport interface ReservedOperation {\n readonly created: boolean;\n readonly operation: SendOperationRecord & { readonly _id: unknown };\n}\n\nfunction isDuplicateKeyError(error: unknown): error is { code: 11000 } {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n error.code === 11000\n );\n}\n\nfunction fingerprintsEqual(left: string, right: string): boolean {\n const leftBuffer = Buffer.from(left);\n const rightBuffer = Buffer.from(right);\n return (\n leftBuffer.byteLength === rightBuffer.byteLength &&\n timingSafeEqual(leftBuffer, rightBuffer)\n );\n}\n\nexport class MongooseIdempotencyStore {\n constructor(private readonly model: Model<SendOperationRecord>) {}\n\n async reserve(input: ReserveOperationInput): Promise<ReservedOperation> {\n try {\n const created = await this.model.create({\n ...input,\n state: \"reserved\",\n currentAttemptNumber: 0,\n });\n return {\n created: true,\n operation: created.toObject(),\n };\n } catch (error) {\n if (!isDuplicateKeyError(error)) throw error;\n\n const existing = await this.model\n .findOne({\n scopeKey: input.scopeKey,\n actionKey: input.actionKey,\n idempotencyKey: input.idempotencyKey,\n })\n .select(\"+requestFingerprint +recipientFingerprint\")\n .lean()\n .exec();\n\n if (existing === null) throw error;\n if (\n !fingerprintsEqual(\n existing.requestFingerprint,\n input.requestFingerprint,\n )\n ) {\n throw new IdempotencyConflictError();\n }\n\n return {\n created: false,\n operation: existing,\n };\n }\n }\n}\n","import { Types } from \"mongoose\";\n\nimport type {\n OperationSnapshot,\n SendOperationStore,\n} from \"../core/orchestrator.js\";\nimport type { ProviderSubmissionResult } from \"../core/provider.js\";\nimport {\n MongooseIdempotencyStore,\n type ReserveOperationInput,\n} from \"./idempotency.js\";\nimport type { SmsMongooseModels } from \"./models.js\";\n\nexport class OperationStateConflictError extends Error {\n readonly code = \"operation_state_conflict\";\n\n constructor(message: string) {\n super(message);\n this.name = \"OperationStateConflictError\";\n }\n}\n\nfunction snapshot(record: {\n readonly _id: unknown;\n readonly state: OperationSnapshot[\"state\"];\n readonly providerMessageId?: string | null;\n}): OperationSnapshot {\n return {\n id: String(record._id),\n state: record.state,\n ...(record.providerMessageId === undefined ||\n record.providerMessageId === null\n ? {}\n : { providerMessageId: record.providerMessageId }),\n };\n}\n\nfunction completion(result: ProviderSubmissionResult): {\n operationState: OperationSnapshot[\"state\"];\n attemptState: \"accepted\" | \"rejected\" | \"pre_acceptance_failure\" | \"unknown\";\n providerMessageId?: string;\n normalizedError?: { code: string };\n} {\n switch (result.status) {\n case \"accepted\":\n return {\n operationState: \"submitted\",\n attemptState: \"accepted\",\n providerMessageId: result.providerMessageId,\n };\n case \"rejected\":\n return {\n operationState: \"failed\",\n attemptState: \"rejected\",\n normalizedError: { code: result.code },\n };\n case \"pre-acceptance-failure\":\n return {\n operationState: \"failed\",\n attemptState: \"pre_acceptance_failure\",\n normalizedError: { code: result.code },\n };\n case \"unknown\":\n return {\n operationState: \"unknown\",\n attemptState: \"unknown\",\n normalizedError: { code: result.code },\n };\n }\n}\n\nexport class MongooseSendOperationStore implements SendOperationStore {\n private readonly idempotency: MongooseIdempotencyStore;\n\n constructor(private readonly models: SmsMongooseModels) {\n this.idempotency = new MongooseIdempotencyStore(models.SendOperation);\n }\n\n async reserve(input: ReserveOperationInput) {\n const result = await this.idempotency.reserve(input);\n return { created: result.created, operation: snapshot(result.operation) };\n }\n\n async beginAttempt(input: {\n readonly operationId: string;\n readonly providerConfigurationId: string;\n readonly adapterId: string;\n readonly expiresAt: Date;\n }): Promise<void> {\n if (\n !Types.ObjectId.isValid(input.operationId) ||\n !Types.ObjectId.isValid(input.providerConfigurationId)\n ) {\n throw new OperationStateConflictError(\n \"Invalid operation or provider identifier.\",\n );\n }\n\n const operation = await this.models.SendOperation.findOneAndUpdate(\n { _id: input.operationId, state: \"reserved\", currentAttemptNumber: 0 },\n { $set: { state: \"processing\" }, $inc: { currentAttemptNumber: 1 } },\n { returnDocument: \"after\" },\n )\n .lean()\n .exec();\n if (operation === null) {\n throw new OperationStateConflictError(\n \"Operation is not available for dispatch.\",\n );\n }\n\n try {\n await this.models.SendAttempt.create({\n operationId: operation._id,\n attemptNumber: 1,\n providerConfigurationId: new Types.ObjectId(\n input.providerConfigurationId,\n ),\n adapterId: input.adapterId,\n state: \"dispatching\",\n startedAt: new Date(),\n expiresAt: input.expiresAt,\n });\n } catch (cause) {\n await this.models.SendOperation.updateOne(\n { _id: operation._id, state: \"processing\", currentAttemptNumber: 1 },\n { $set: { state: \"reserved\", currentAttemptNumber: 0 } },\n ).exec();\n throw cause;\n }\n }\n\n async completeAttempt(\n operationId: string,\n result: ProviderSubmissionResult,\n ): Promise<OperationSnapshot> {\n if (!Types.ObjectId.isValid(operationId)) {\n throw new OperationStateConflictError(\"Invalid operation identifier.\");\n }\n const mapped = completion(result);\n const operation = await this.models.SendOperation.findOneAndUpdate(\n { _id: operationId, state: \"processing\", currentAttemptNumber: 1 },\n {\n $set: {\n state: mapped.operationState,\n ...(mapped.providerMessageId === undefined\n ? {}\n : { providerMessageId: mapped.providerMessageId }),\n ...(mapped.normalizedError === undefined\n ? {}\n : { normalizedError: mapped.normalizedError }),\n },\n },\n { returnDocument: \"after\" },\n )\n .lean()\n .exec();\n if (operation === null) {\n throw new OperationStateConflictError(\n \"Logical operation could not be completed.\",\n );\n }\n\n await this.models.SendAttempt.updateOne(\n { operationId, attemptNumber: 1, state: \"dispatching\" },\n {\n $set: {\n state: mapped.attemptState,\n completedAt: new Date(),\n ...(mapped.providerMessageId === undefined\n ? {}\n : { providerMessageId: mapped.providerMessageId }),\n ...(mapped.normalizedError === undefined\n ? {}\n : { normalizedError: mapped.normalizedError }),\n },\n },\n ).exec();\n return snapshot(operation);\n }\n}\n","import { Schema, type Connection, type Model, type Types } from \"mongoose\";\n\nimport type { EncryptedCredentialEnvelope } from \"../security/encryption.js\";\n\nexport interface ProviderConfigurationRecord {\n scopeKey: string;\n adapterId: string;\n label: string;\n encryptedCredentials?: EncryptedCredentialEnvelope;\n credentialRevision: number;\n verification: {\n status: \"unverified\" | \"verified\" | \"invalid\" | \"unavailable\";\n method?: string;\n verifiedAt?: Date;\n safeErrorCode?: string;\n };\n enabled: boolean;\n version: number;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface ActionConfigurationRecord {\n scopeKey: string;\n actionKey: string;\n providerConfigurationId: Types.ObjectId;\n templateReference: unknown;\n bindings: unknown[];\n validation: {\n level: \"local\" | \"remote\" | \"deferred\";\n remoteTemplateFingerprint?: string;\n validatedAt: Date;\n warnings: string[];\n };\n enabled: boolean;\n version: number;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport type SendOperationState =\n \"reserved\" | \"processing\" | \"submitted\" | \"delivered\" | \"failed\" | \"unknown\";\n\nexport interface SendOperationRecord {\n scopeKey: string;\n actionKey: string;\n actionDefinitionVersion: string;\n configurationVersion: number;\n idempotencyKey: string;\n requestFingerprint: string;\n recipientFingerprint: string;\n state: SendOperationState;\n currentAttemptNumber: number;\n providerMessageId?: string;\n normalizedError?: unknown;\n expiresAt: Date;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface SendAttemptRecord {\n operationId: Types.ObjectId;\n attemptNumber: number;\n providerConfigurationId: Types.ObjectId;\n adapterId: string;\n state:\n | \"created\"\n | \"dispatching\"\n | \"accepted\"\n | \"delivered\"\n | \"rejected\"\n | \"pre_acceptance_failure\"\n | \"unknown\";\n startedAt: Date;\n completedAt?: Date;\n durationMs?: number;\n providerMessageId?: string;\n normalizedError?: unknown;\n safeDiagnostics?: unknown;\n expiresAt: Date;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface SmsMongooseModels {\n ProviderConfiguration: Model<ProviderConfigurationRecord>;\n ActionConfiguration: Model<ActionConfigurationRecord>;\n SendOperation: Model<SendOperationRecord>;\n SendAttempt: Model<SendAttemptRecord>;\n}\n\nconst encryptedCredentialSchema = new Schema<EncryptedCredentialEnvelope>(\n {\n algorithm: { type: String, required: true, enum: [\"aes-256-gcm\"] },\n keyId: { type: String, required: true },\n configurationId: { type: String, required: true },\n adapterId: { type: String, required: true },\n scopeKey: { type: String, required: true },\n credentialRevision: { type: Number, required: true },\n nonce: { type: String, required: true, select: false },\n ciphertext: { type: String, required: true, select: false },\n authTag: { type: String, required: true, select: false },\n schemaVersion: { type: Number, required: true },\n },\n { _id: false },\n);\n\nconst providerConfigurationSchema = new Schema<ProviderConfigurationRecord>(\n {\n scopeKey: { type: String, required: true },\n adapterId: { type: String, required: true },\n label: { type: String, required: true },\n encryptedCredentials: { type: encryptedCredentialSchema, select: false },\n credentialRevision: { type: Number, required: true, default: 0 },\n verification: {\n status: {\n type: String,\n required: true,\n enum: [\"unverified\", \"verified\", \"invalid\", \"unavailable\"],\n default: \"unverified\",\n },\n method: String,\n verifiedAt: Date,\n safeErrorCode: String,\n },\n enabled: { type: Boolean, required: true, default: false },\n version: { type: Number, required: true, default: 1 },\n },\n { timestamps: true, minimize: false },\n);\nproviderConfigurationSchema.index({ scopeKey: 1, label: 1 }, { unique: true });\nproviderConfigurationSchema.index({ scopeKey: 1, adapterId: 1, enabled: 1 });\n\nconst actionConfigurationSchema = new Schema<ActionConfigurationRecord>(\n {\n scopeKey: { type: String, required: true },\n actionKey: { type: String, required: true },\n providerConfigurationId: {\n type: Schema.Types.ObjectId,\n required: true,\n },\n templateReference: { type: Schema.Types.Mixed, required: true },\n bindings: { type: [Schema.Types.Mixed], required: true, default: [] },\n validation: {\n level: {\n type: String,\n required: true,\n enum: [\"local\", \"remote\", \"deferred\"],\n },\n remoteTemplateFingerprint: String,\n validatedAt: { type: Date, required: true },\n warnings: { type: [String], required: true, default: [] },\n },\n enabled: { type: Boolean, required: true, default: false },\n version: { type: Number, required: true, default: 1 },\n },\n { timestamps: true, minimize: false },\n);\nactionConfigurationSchema.index(\n { scopeKey: 1, actionKey: 1 },\n { unique: true },\n);\n\nconst sendOperationSchema = new Schema<SendOperationRecord>(\n {\n scopeKey: { type: String, required: true },\n actionKey: { type: String, required: true },\n actionDefinitionVersion: { type: String, required: true },\n configurationVersion: { type: Number, required: true },\n idempotencyKey: { type: String, required: true },\n requestFingerprint: { type: String, required: true, select: false },\n recipientFingerprint: { type: String, required: true, select: false },\n state: {\n type: String,\n required: true,\n enum: [\n \"reserved\",\n \"processing\",\n \"submitted\",\n \"delivered\",\n \"failed\",\n \"unknown\",\n ],\n default: \"reserved\",\n },\n currentAttemptNumber: { type: Number, required: true, default: 0 },\n providerMessageId: String,\n normalizedError: Schema.Types.Mixed,\n expiresAt: { type: Date, required: true },\n },\n { timestamps: true, minimize: false },\n);\nsendOperationSchema.index(\n { scopeKey: 1, actionKey: 1, idempotencyKey: 1 },\n { unique: true },\n);\nsendOperationSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });\n\nconst sendAttemptSchema = new Schema<SendAttemptRecord>(\n {\n operationId: { type: Schema.Types.ObjectId, required: true },\n attemptNumber: { type: Number, required: true },\n providerConfigurationId: {\n type: Schema.Types.ObjectId,\n required: true,\n },\n adapterId: { type: String, required: true },\n state: {\n type: String,\n required: true,\n enum: [\n \"created\",\n \"dispatching\",\n \"accepted\",\n \"delivered\",\n \"rejected\",\n \"pre_acceptance_failure\",\n \"unknown\",\n ],\n },\n startedAt: { type: Date, required: true },\n completedAt: Date,\n durationMs: Number,\n providerMessageId: String,\n normalizedError: Schema.Types.Mixed,\n safeDiagnostics: Schema.Types.Mixed,\n expiresAt: { type: Date, required: true },\n },\n { timestamps: true, minimize: false },\n);\nsendAttemptSchema.index({ operationId: 1, attemptNumber: 1 }, { unique: true });\nsendAttemptSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });\n\nfunction modelFor<T>(\n connection: Connection,\n name: string,\n schema: Schema<T>,\n collection: string,\n): Model<T> {\n return (\n (connection.models[name] as Model<T> | undefined) ??\n connection.model<T>(name, schema, collection)\n );\n}\n\nexport function createSmsMongooseModels(\n connection: Connection,\n options: { readonly collectionPrefix?: string } = {},\n): SmsMongooseModels {\n const prefix = options.collectionPrefix ?? \"sms\";\n if (!/^[a-zA-Z0-9_-]+$/.test(prefix)) {\n throw new TypeError(\"Collection prefix contains unsupported characters.\");\n }\n const modelPrefix = prefix.replace(/[^a-zA-Z0-9]/g, \"_\");\n\n return {\n ProviderConfiguration: modelFor(\n connection,\n `${modelPrefix}_SmsProviderConfiguration`,\n providerConfigurationSchema,\n `${prefix}_provider_configurations`,\n ),\n ActionConfiguration: modelFor(\n connection,\n `${modelPrefix}_SmsActionConfiguration`,\n actionConfigurationSchema,\n `${prefix}_action_configurations`,\n ),\n SendOperation: modelFor(\n connection,\n `${modelPrefix}_SmsSendOperation`,\n sendOperationSchema,\n `${prefix}_send_operations`,\n ),\n SendAttempt: modelFor(\n connection,\n `${modelPrefix}_SmsSendAttempt`,\n sendAttemptSchema,\n `${prefix}_send_attempts`,\n ),\n };\n}\n"]}
@@ -0,0 +1,132 @@
1
+ import { Types, Model, Connection } from 'mongoose';
2
+ import { EncryptedCredentialEnvelope, SendOperationStore, OperationSnapshot } from './index.cjs';
3
+ import { ProviderSubmissionResult } from './index.cjs';
4
+ import './manager-B3b12wET.cjs';
5
+ import 'zod';
6
+
7
+ interface ProviderConfigurationRecord {
8
+ scopeKey: string;
9
+ adapterId: string;
10
+ label: string;
11
+ encryptedCredentials?: EncryptedCredentialEnvelope;
12
+ credentialRevision: number;
13
+ verification: {
14
+ status: "unverified" | "verified" | "invalid" | "unavailable";
15
+ method?: string;
16
+ verifiedAt?: Date;
17
+ safeErrorCode?: string;
18
+ };
19
+ enabled: boolean;
20
+ version: number;
21
+ createdAt: Date;
22
+ updatedAt: Date;
23
+ }
24
+ interface ActionConfigurationRecord {
25
+ scopeKey: string;
26
+ actionKey: string;
27
+ providerConfigurationId: Types.ObjectId;
28
+ templateReference: unknown;
29
+ bindings: unknown[];
30
+ validation: {
31
+ level: "local" | "remote" | "deferred";
32
+ remoteTemplateFingerprint?: string;
33
+ validatedAt: Date;
34
+ warnings: string[];
35
+ };
36
+ enabled: boolean;
37
+ version: number;
38
+ createdAt: Date;
39
+ updatedAt: Date;
40
+ }
41
+ type SendOperationState = "reserved" | "processing" | "submitted" | "delivered" | "failed" | "unknown";
42
+ interface SendOperationRecord {
43
+ scopeKey: string;
44
+ actionKey: string;
45
+ actionDefinitionVersion: string;
46
+ configurationVersion: number;
47
+ idempotencyKey: string;
48
+ requestFingerprint: string;
49
+ recipientFingerprint: string;
50
+ state: SendOperationState;
51
+ currentAttemptNumber: number;
52
+ providerMessageId?: string;
53
+ normalizedError?: unknown;
54
+ expiresAt: Date;
55
+ createdAt: Date;
56
+ updatedAt: Date;
57
+ }
58
+ interface SendAttemptRecord {
59
+ operationId: Types.ObjectId;
60
+ attemptNumber: number;
61
+ providerConfigurationId: Types.ObjectId;
62
+ adapterId: string;
63
+ state: "created" | "dispatching" | "accepted" | "delivered" | "rejected" | "pre_acceptance_failure" | "unknown";
64
+ startedAt: Date;
65
+ completedAt?: Date;
66
+ durationMs?: number;
67
+ providerMessageId?: string;
68
+ normalizedError?: unknown;
69
+ safeDiagnostics?: unknown;
70
+ expiresAt: Date;
71
+ createdAt: Date;
72
+ updatedAt: Date;
73
+ }
74
+ interface SmsMongooseModels {
75
+ ProviderConfiguration: Model<ProviderConfigurationRecord>;
76
+ ActionConfiguration: Model<ActionConfigurationRecord>;
77
+ SendOperation: Model<SendOperationRecord>;
78
+ SendAttempt: Model<SendAttemptRecord>;
79
+ }
80
+ declare function createSmsMongooseModels(connection: Connection, options?: {
81
+ readonly collectionPrefix?: string;
82
+ }): SmsMongooseModels;
83
+
84
+ declare class IdempotencyConflictError extends Error {
85
+ readonly code = "idempotency_conflict";
86
+ constructor();
87
+ }
88
+ interface ReserveOperationInput {
89
+ readonly scopeKey: string;
90
+ readonly actionKey: string;
91
+ readonly actionDefinitionVersion: string;
92
+ readonly configurationVersion: number;
93
+ readonly idempotencyKey: string;
94
+ readonly requestFingerprint: string;
95
+ readonly recipientFingerprint: string;
96
+ readonly expiresAt: Date;
97
+ }
98
+ interface ReservedOperation {
99
+ readonly created: boolean;
100
+ readonly operation: SendOperationRecord & {
101
+ readonly _id: unknown;
102
+ };
103
+ }
104
+ declare class MongooseIdempotencyStore {
105
+ private readonly model;
106
+ constructor(model: Model<SendOperationRecord>);
107
+ reserve(input: ReserveOperationInput): Promise<ReservedOperation>;
108
+ }
109
+
110
+ declare class OperationStateConflictError extends Error {
111
+ readonly code = "operation_state_conflict";
112
+ constructor(message: string);
113
+ }
114
+ declare class MongooseSendOperationStore implements SendOperationStore {
115
+ private readonly models;
116
+ private readonly idempotency;
117
+ constructor(models: SmsMongooseModels);
118
+ reserve(input: ReserveOperationInput): Promise<{
119
+ created: boolean;
120
+ operation: OperationSnapshot;
121
+ }>;
122
+ beginAttempt(input: {
123
+ readonly operationId: string;
124
+ readonly providerConfigurationId: string;
125
+ readonly adapterId: string;
126
+ readonly expiresAt: Date;
127
+ }): Promise<void>;
128
+ completeAttempt(operationId: string, result: ProviderSubmissionResult): Promise<OperationSnapshot>;
129
+ }
130
+
131
+ export { IdempotencyConflictError, MongooseIdempotencyStore, MongooseSendOperationStore, OperationStateConflictError, createSmsMongooseModels };
132
+ export type { ActionConfigurationRecord, ProviderConfigurationRecord, ReserveOperationInput, ReservedOperation, SendAttemptRecord, SendOperationRecord, SendOperationState, SmsMongooseModels };