@objectstack/service-sms 16.1.0 → 17.0.0-rc.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/CHANGELOG.md CHANGED
@@ -1,5 +1,370 @@
1
1
  # @objectstack/service-sms
2
2
 
3
+ ## 17.0.0-rc.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 2e836de: chore(packaging): CHANGELOG.md ships in every npm tarball (#4261)
8
+
9
+ The AGENTS.md post-task checklist requires breaking changesets to carry their
10
+ FROM → TO migration because "this text ships to consumers as `CHANGELOG.md`
11
+ inside the npm package and is what an upgrading agent greps after the tombstone
12
+ error." That delivery path was severed for 68 of the 69 publishable packages:
13
+ npm packs `package.json` / `README*` / `LICENSE*` unconditionally but — unlike
14
+ older npm versions — not `CHANGELOG.md`, and the canonical
15
+ `"files": ["dist", "README.md"]` whitelist never named it. Measured on npm
16
+ 10.9.7: `npm pack --dry-run` on `@objectstack/types` shipped 3 files while its
17
+ 70KB `CHANGELOG.md` stayed behind. Only `@objectstack/spec` listed it
18
+ explicitly.
19
+
20
+ The tombstone-error scenario is precisely the one where the repo is out of
21
+ reach — the upgrading agent has `node_modules` and nothing else — so the
22
+ migration text has to ride in the tarball. Every publishable package now
23
+ declares `CHANGELOG.md` in `files`, and the canonical whitelist is
24
+ `["dist", "README.md", "CHANGELOG.md"]`.
25
+
26
+ The other half is the gate: `check:published-files` gains a fifth invariant,
27
+ COMPLETE — a whitelist that fails to cover `CHANGELOG.md` fails the
28
+ always-required lint job, so the next package cannot silently sever the path
29
+ again. `@objectstack/spec`'s per-package EXTRA_ENTRIES exemption dissolves
30
+ into the canonical set.
31
+
32
+ Consumer-visible change: one more file per install (the package's changelog,
33
+ e.g. 70.8KB for `@objectstack/types`), and `grep -r "removed key"
34
+ node_modules/@objectstack/*/CHANGELOG.md` now finds the migration it was
35
+ promised.
36
+
37
+ - be7360c: chore(plugins,services): declare `providesServices` on the 20 remaining init-time service providers (ADR-0116 follow-up, #4131)
38
+
39
+ ADR-0116 gave the kernel a declared ordering contract, but only
40
+ `ObjectQLPlugin` and `MetadataPlugin` had declared what their `init()`
41
+ registers. The pre-Phase-1 ordering check can only _name a provider_ for
42
+ services someone declared, so its coverage was two plugins wide.
43
+
44
+ An audit of every plugin's `init()` body (brace-matched, comments stripped,
45
+ each call classified by whether it sits inside a `try`/`if`) found 20 plugins
46
+ that register a service on every path without declaring it. All 20 now
47
+ declare `providesServices`. Purely additive: no ordering changes, no new
48
+ failure modes — a `providesServices` entry only lets the kernel say _who_
49
+ provides a service when it reports a misordering, and enriches the Phase-1
50
+ `getService` miss diagnostic.
51
+
52
+ Three needed a closer read before declaring, because they register the same
53
+ service from several branches (`cache`, `queue`, `job`): each early-return
54
+ branch plus the fallback registers it, so every path does — the declaration
55
+ is honest. ADR-0116's rule that a _conditionally_ registered service must
56
+ never be declared is unchanged and was applied throughout.
57
+
58
+ The same audit found 12 plugins that hard-resolve a service during `init()`
59
+ (11 of them `manifest`) without declaring `requiresServices`. None is a live
60
+ exposure — every one already declares a hard `dependencies` entry on the
61
+ provider, so the kernel orders them correctly today. Those are tracked
62
+ separately: with a hard dependency in place, `requiresServices` mostly
63
+ restates what the kernel already enforces, and its real value is on
64
+ _soft_-dependency consumers, of which `AppPlugin` is currently the only one.
65
+
66
+ - cc2de0e: chore(packaging): 20 packages stop publishing their sources, tests and build tooling (#4248)
67
+
68
+ These 20 packages declared no `files` field, so npm fell back to packing the
69
+ whole package directory. `npm pack --dry-run` on `@objectstack/plugin-webhooks`
70
+ listed **21 files** — 15 under `src/`, three of them unit tests
71
+ (`auto-enqueuer.test.ts`, `bootstrap-declared-webhooks.test.ts`, …), plus the
72
+ build-time `scripts/i18n-extract.config.ts`. `dist/` lands on top of that at
73
+ publish time rather than instead of it, so consumers were installing the
74
+ TypeScript sources and the test suite alongside the artifact they asked for.
75
+
76
+ Each now declares `"files": ["dist", "README.md"]`, matching the 29 packages
77
+ that already did. Nothing a consumer imports moves: every `main` / `types` /
78
+ `exports` target in all 20 already resolved inside `dist/`, which the new
79
+ `check:published-files` guard verifies rather than assumes. The visible change
80
+ is a smaller install and a smaller dependency-scanning surface — `npm pack` on
81
+ `@objectstack/plugin-webhooks` now yields 2 files plus `dist/`.
82
+
83
+ The other half of the fix is the gate. Half the packages declaring `files` and
84
+ half not was the #3786 shape — a hand-copied convention with nothing enforcing
85
+ it, where whoever forgets the line gets no signal at all. `check:published-files`
86
+ (new, wired into the always-required `lint` job) holds every non-private
87
+ workspace package to four invariants: `files` is **declared**; it is
88
+ **sufficient** (covers every entry point, so tightening a whitelist cannot ship
89
+ a package that fails to resolve); it is **minimal** (admits no test, test-harness
90
+ config or build script); and anything beyond `dist` + `README.md` is
91
+ **registered** with a reason, reconciled in both directions so a stale exemption
92
+ is an error rather than dead text. `@objectstack/spec` is the one package with
93
+ registered extras — its `.zod.ts` sources, JSON Schemas, liveness ledgers and
94
+ `CHANGELOG.md` are product, not build input.
95
+
96
+ This also closes an assumption #4206 was resting on. Excluding `<pkg>/scripts/**`
97
+ from the docs-drift implementation test is sound only while no package publishes
98
+ `scripts/` as runtime code; that held, but it held because someone read all three
99
+ offenders by hand. It is now checked on every PR.
100
+
101
+ - Updated dependencies [6a67d7a]
102
+ - Updated dependencies [0ecc656]
103
+ - Updated dependencies [06772eb]
104
+ - Updated dependencies [270650f]
105
+ - Updated dependencies [3aef718]
106
+ - Updated dependencies [1ea6bce]
107
+ - Updated dependencies [c1dcacd]
108
+ - Updated dependencies [ad303ed]
109
+ - Updated dependencies [32ccb23]
110
+ - Updated dependencies [f5a4ef0]
111
+ - Updated dependencies [2d3e255]
112
+ - Updated dependencies [7d7521f]
113
+ - Updated dependencies [5dc4d02]
114
+ - Updated dependencies [05154a1]
115
+ - Updated dependencies [9b6fe7c]
116
+ - Updated dependencies [8c711fb]
117
+ - Updated dependencies [09e4547]
118
+ - Updated dependencies [91f4c78]
119
+ - Updated dependencies [820eff9]
120
+ - Updated dependencies [8d895ff]
121
+ - Updated dependencies [f6472d7]
122
+ - Updated dependencies [78caf51]
123
+ - Updated dependencies [62a789b]
124
+ - Updated dependencies [789ad63]
125
+ - Updated dependencies [2af1988]
126
+ - Updated dependencies [0af50a3]
127
+ - Updated dependencies [2e836de]
128
+ - Updated dependencies [12a19a8]
129
+ - Updated dependencies [41dcda3]
130
+ - Updated dependencies [c8124e5]
131
+ - Updated dependencies [a1a4140]
132
+ - Updated dependencies [217e2e6]
133
+ - Updated dependencies [86a71d1]
134
+ - Updated dependencies [d5c75e2]
135
+ - Updated dependencies [03d26f7]
136
+ - Updated dependencies [4384921]
137
+ - Updated dependencies [3c628ce]
138
+ - Updated dependencies [7cb922e]
139
+ - Updated dependencies [1d22114]
140
+ - Updated dependencies [b5f9397]
141
+ - Updated dependencies [ed77493]
142
+ - Updated dependencies [58a03d2]
143
+ - Updated dependencies [dc530b4]
144
+ - Updated dependencies [e59786e]
145
+ - Updated dependencies [bcf1112]
146
+ - Updated dependencies [9774b78]
147
+ - Updated dependencies [b07d829]
148
+ - Updated dependencies [a648e96]
149
+ - Updated dependencies [a47ac06]
150
+ - Updated dependencies [e4c61a7]
151
+ - Updated dependencies [cc60165]
152
+ - Updated dependencies [081aa6f]
153
+ - Updated dependencies [91f4c78]
154
+ - Updated dependencies [e8d0c21]
155
+ - Updated dependencies [45dc446]
156
+ - Updated dependencies [c1d44f7]
157
+ - Updated dependencies [ab9fb5c]
158
+ - Updated dependencies [f985b3f]
159
+ - Updated dependencies [9a4932a]
160
+ - Updated dependencies [f9fc874]
161
+ - Updated dependencies [011b386]
162
+ - Updated dependencies [7777e8f]
163
+ - Updated dependencies [507b92a]
164
+ - Updated dependencies [7309c81]
165
+ - Updated dependencies [20bc1ec]
166
+ - Updated dependencies [90c2b15]
167
+ - Updated dependencies [42eeb7d]
168
+ - Updated dependencies [01e124d]
169
+ - Updated dependencies [7ce02eb]
170
+ - Updated dependencies [a13827e]
171
+ - Updated dependencies [7733604]
172
+ - Updated dependencies [40e420f]
173
+ - Updated dependencies [d13004a]
174
+ - Updated dependencies [be7360c]
175
+ - Updated dependencies [5b47ab5]
176
+ - Updated dependencies [b09d8d9]
177
+ - Updated dependencies [b09d8d9]
178
+ - Updated dependencies [8675db6]
179
+ - Updated dependencies [b09d8d9]
180
+ - Updated dependencies [3eb1b2b]
181
+ - Updated dependencies [59b85c0]
182
+ - Updated dependencies [6e357ed]
183
+ - Updated dependencies [d6938bf]
184
+ - Updated dependencies [31e0be9]
185
+ - Updated dependencies [4bfd455]
186
+ - Updated dependencies [ffd2ce2]
187
+ - Updated dependencies [62f8017]
188
+ - Updated dependencies [a831df1]
189
+ - Updated dependencies [f752ee3]
190
+ - Updated dependencies [a1b61e0]
191
+ - Updated dependencies [cd6b9f2]
192
+ - Updated dependencies [2cb6d3c]
193
+ - Updated dependencies [af2a095]
194
+ - Updated dependencies [ec796d5]
195
+ - Updated dependencies [e87fea1]
196
+ - Updated dependencies [c65e529]
197
+ - Updated dependencies [3ca34c1]
198
+ - Updated dependencies [239c3a3]
199
+ - Updated dependencies [94a0bbc]
200
+ - Updated dependencies [d6bfb3d]
201
+ - Updated dependencies [a2266a6]
202
+ - Updated dependencies [d25a0ec]
203
+ - Updated dependencies [667b83e]
204
+ - Updated dependencies [627b188]
205
+ - Updated dependencies [8d4eae7]
206
+ - Updated dependencies [857a6cf]
207
+ - Updated dependencies [65a3a84]
208
+ - Updated dependencies [ccd9397]
209
+ - Updated dependencies [bca935b]
210
+ - Updated dependencies [d92c72d]
211
+ - Updated dependencies [c54c822]
212
+ - Updated dependencies [8dcc0f5]
213
+ - Updated dependencies [75b9e51]
214
+ - Updated dependencies [0a2f233]
215
+ - Updated dependencies [8621cdd]
216
+ - Updated dependencies [6f23667]
217
+ - Updated dependencies [5d21a48]
218
+ - Updated dependencies [19365b7]
219
+ - Updated dependencies [b7ed26d]
220
+ - Updated dependencies [b3a3d83]
221
+ - Updated dependencies [7a55913]
222
+ - Updated dependencies [35accbf]
223
+ - Updated dependencies [6038de7]
224
+ - Updated dependencies [eb95d97]
225
+ - Updated dependencies [e4c2dc8]
226
+ - Updated dependencies [1bd2795]
227
+ - Updated dependencies [8186a70]
228
+ - Updated dependencies [a329cca]
229
+ - Updated dependencies [6eec18c]
230
+ - Updated dependencies [4d7bebf]
231
+ - Updated dependencies [821ac7a]
232
+ - Updated dependencies [8f81731]
233
+ - Updated dependencies [8b50cb3]
234
+ - Updated dependencies [8c2db68]
235
+ - Updated dependencies [22b5e54]
236
+ - Updated dependencies [0166bd5]
237
+ - Updated dependencies [9b702dc]
238
+ - Updated dependencies [ab16331]
239
+ - @objectstack/spec@17.0.0-rc.1
240
+ - @objectstack/core@17.0.0-rc.1
241
+
242
+ ## 17.0.0-rc.0
243
+
244
+ ### Patch Changes
245
+
246
+ - Updated dependencies [50616d9]
247
+ - Updated dependencies [08b5a3d]
248
+ - Updated dependencies [d99aeb3]
249
+ - Updated dependencies [4727eb8]
250
+ - Updated dependencies [f63cd09]
251
+ - Updated dependencies [fa3d0cf]
252
+ - Updated dependencies [af5a224]
253
+ - Updated dependencies [71f76e1]
254
+ - Updated dependencies [37b1346]
255
+ - Updated dependencies [99736a0]
256
+ - Updated dependencies [fe67e34]
257
+ - Updated dependencies [fdb4f50]
258
+ - Updated dependencies [1bd5652]
259
+ - Updated dependencies [14252d3]
260
+ - Updated dependencies [7fb436c]
261
+ - Updated dependencies [879ea13]
262
+ - Updated dependencies [201b31f]
263
+ - Updated dependencies [e2616e0]
264
+ - Updated dependencies [6fdc5c6]
265
+ - Updated dependencies [8b9d71e]
266
+ - Updated dependencies [33f5e23]
267
+ - Updated dependencies [259af21]
268
+ - Updated dependencies [587fc91]
269
+ - Updated dependencies [1986594]
270
+ - Updated dependencies [ad4af62]
271
+ - Updated dependencies [d44dbfa]
272
+ - Updated dependencies [474fe39]
273
+ - Updated dependencies [0bc685a]
274
+ - Updated dependencies [b949059]
275
+ - Updated dependencies [be1c52c]
276
+ - Updated dependencies [c5ff96d]
277
+ - Updated dependencies [84e7be9]
278
+ - Updated dependencies [a6c3f38]
279
+ - Updated dependencies [debc23a]
280
+ - Updated dependencies [0f8ad09]
281
+ - Updated dependencies [8f9689f]
282
+ - Updated dependencies [57a3bb3]
283
+ - Updated dependencies [5f9a987]
284
+ - Updated dependencies [db02d47]
285
+ - Updated dependencies [0bfdf46]
286
+ - Updated dependencies [376a061]
287
+ - Updated dependencies [7c7e246]
288
+ - Updated dependencies [f35cdc5]
289
+ - Updated dependencies [9ea2bc5]
290
+ - Updated dependencies [c2d9098]
291
+ - Updated dependencies [a227ed7]
292
+ - Updated dependencies [9613396]
293
+ - Updated dependencies [e47b342]
294
+ - Updated dependencies [4ed7ed4]
295
+ - Updated dependencies [2fa4ca1]
296
+ - Updated dependencies [f5a2320]
297
+ - Updated dependencies [deb538f]
298
+ - Updated dependencies [5b89711]
299
+ - Updated dependencies [0c8a22f]
300
+ - Updated dependencies [763931e]
301
+ - Updated dependencies [de9af8a]
302
+ - Updated dependencies [c4df271]
303
+ - Updated dependencies [a41ba5c]
304
+ - Updated dependencies [189854c]
305
+ - Updated dependencies [0e3a226]
306
+ - Updated dependencies [1d4756e]
307
+ - Updated dependencies [720c5ad]
308
+ - Updated dependencies [a8d1e24]
309
+ - Updated dependencies [41642b0]
310
+ - Updated dependencies [4cca74c]
311
+ - Updated dependencies [88ef03e]
312
+ - Updated dependencies [9e2caf3]
313
+ - Updated dependencies [81ce41a]
314
+ - Updated dependencies [85e1e4e]
315
+ - Updated dependencies [dac6a08]
316
+ - Updated dependencies [394b7a1]
317
+ - Updated dependencies [677b591]
318
+ - Updated dependencies [d77d1b7]
319
+ - Updated dependencies [5b79a34]
320
+ - Updated dependencies [c757854]
321
+ - Updated dependencies [0045682]
322
+ - Updated dependencies [2a5f04a]
323
+ - Updated dependencies [4f740b0]
324
+ - Updated dependencies [67452d1]
325
+ - Updated dependencies [0fc6219]
326
+ - Updated dependencies [605e190]
327
+ - Updated dependencies [c6c59f1]
328
+ - Updated dependencies [b0e78a8]
329
+ - Updated dependencies [f31cc8d]
330
+ - Updated dependencies [f343dc4]
331
+ - Updated dependencies [8269e32]
332
+ - Updated dependencies [74f7339]
333
+ - Updated dependencies [a6c35a2]
334
+ - Updated dependencies [c2f1002]
335
+ - Updated dependencies [f163028]
336
+ - Updated dependencies [f07808c]
337
+ - Updated dependencies [7ffc3d3]
338
+ - Updated dependencies [88346ba]
339
+ - Updated dependencies [4631592]
340
+ - Updated dependencies [32ff033]
341
+ - Updated dependencies [5ac93d4]
342
+ - Updated dependencies [93f267f]
343
+ - Updated dependencies [0024abf]
344
+ - Updated dependencies [acbf364]
345
+ - Updated dependencies [7687f7b]
346
+ - Updated dependencies [1659072]
347
+ - Updated dependencies [abceb0d]
348
+ - Updated dependencies [0c302a7]
349
+ - Updated dependencies [6633337]
350
+ - Updated dependencies [f00d8d4]
351
+ - Updated dependencies [503be86]
352
+ - Updated dependencies [cde1975]
353
+ - Updated dependencies [0bc685a]
354
+ - Updated dependencies [11949fc]
355
+ - Updated dependencies [b098b0e]
356
+ - Updated dependencies [4d00b13]
357
+ - Updated dependencies [57bab76]
358
+ - Updated dependencies [b90086a]
359
+ - Updated dependencies [b95577a]
360
+ - Updated dependencies [83c161f]
361
+ - Updated dependencies [d8c4957]
362
+ - Updated dependencies [f24cb83]
363
+ - Updated dependencies [5dbbb92]
364
+ - Updated dependencies [69f1dfd]
365
+ - @objectstack/spec@17.0.0-rc.0
366
+ - @objectstack/core@17.0.0-rc.0
367
+
3
368
  ## 16.1.0
4
369
 
5
370
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -179,6 +179,11 @@ interface SmsServicePluginOptions {
179
179
  */
180
180
  declare class SmsServicePlugin implements Plugin {
181
181
  name: string;
182
+ /**
183
+ * Services init() registers on every path (ADR-0116, #4131) — lets the
184
+ * kernel name this plugin when a consumer requires one before it inits.
185
+ */
186
+ providesServices: string[];
182
187
  version: string;
183
188
  type: "standard";
184
189
  private readonly options;
package/dist/index.d.ts CHANGED
@@ -179,6 +179,11 @@ interface SmsServicePluginOptions {
179
179
  */
180
180
  declare class SmsServicePlugin implements Plugin {
181
181
  name: string;
182
+ /**
183
+ * Services init() registers on every path (ADR-0116, #4131) — lets the
184
+ * kernel name this plugin when a consumer requires one before it inits.
185
+ */
186
+ providesServices: string[];
182
187
  version: string;
183
188
  type: "standard";
184
189
  private readonly options;
package/dist/index.js CHANGED
@@ -291,6 +291,11 @@ function providerFromSettings(values) {
291
291
  var SmsServicePlugin = class {
292
292
  constructor(options = {}) {
293
293
  this.name = "com.objectstack.service.sms";
294
+ /**
295
+ * Services init() registers on every path (ADR-0116, #4131) — lets the
296
+ * kernel name this plugin when a consumer requires one before it inits.
297
+ */
298
+ this.providesServices = ["sms"];
294
299
  this.version = "1.0.0";
295
300
  this.type = "standard";
296
301
  this.options = options;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/sms-service.ts","../src/transports/aliyun.ts","../src/transports/twilio.ts","../src/transports/index.ts","../src/sms-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport {\n SmsService,\n LogSmsTransport,\n maskPhoneNumber,\n normalizeSmsRecipient,\n type SmsServiceOptions,\n} from './sms-service.js';\nexport { SmsServicePlugin, type SmsServicePluginOptions } from './sms-plugin.js';\nexport {\n makeSmsTransport,\n AliyunSmsTransport,\n TwilioSmsTransport,\n type SmsProviderTag,\n type MakeSmsTransportOptions,\n type AliyunSmsTransportOptions,\n type TwilioSmsTransportOptions,\n} from './transports/index.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n ISmsService,\n ISmsTransport,\n NormalizedSmsMessage,\n SendSmsInput,\n SendSmsResult,\n SmsTransportSendResult,\n} from '@objectstack/spec/contracts';\n\n/**\n * Normalize + validate a recipient phone number. Accepts E.164 and common\n * human formats (spaces / dashes / dots / parens are stripped). Returns\n * `undefined` when the result doesn't look like a phone number.\n *\n * Same shape rule as plugin-auth's `normalizePhoneNumber` — 6-15 digits\n * with an optional leading `+` (kept local: the two packages must not\n * depend on each other).\n */\nexport function normalizeSmsRecipient(raw: string): string | undefined {\n const stripped = String(raw ?? '').replace(/[\\s\\-().]/g, '');\n return /^\\+?[0-9]{6,15}$/.test(stripped) ? stripped : undefined;\n}\n\n/**\n * Mask a phone number for log lines: keep the prefix (country-code-ish) and\n * the last two digits, hide the middle. `+8613812345678` → `+8613******78`.\n * SMS logging policy: masked recipient + status ONLY — never the body\n * (OTP codes travel in it; see the ISmsService contract header).\n */\nexport function maskPhoneNumber(phone: string): string {\n const p = String(phone ?? '');\n if (p.length <= 6) return `${p.slice(0, 2)}****`;\n return `${p.slice(0, 5)}${'*'.repeat(Math.max(2, p.length - 7))}${p.slice(-2)}`;\n}\n\n/**\n * Development transport — never actually sends. Logs the masked recipient\n * (and, OUTSIDE production only, the message body so local OTP flows are\n * testable) and returns a synthetic message id.\n *\n * The production-body suppression is a hard rule from #2780: OTP codes must\n * never land in logs. In dev the body IS the delivery — same pattern as the\n * auth magic-link / invitation URLs, which are printed in dev only.\n */\nexport class LogSmsTransport implements ISmsTransport {\n private counter = 0;\n constructor(private readonly logger?: { info: (msg: string, meta?: any) => void }) {}\n async send(message: NormalizedSmsMessage): Promise<SmsTransportSendResult> {\n const messageId = `dev-sms-${Date.now()}-${++this.counter}`;\n const dev = (globalThis as any)?.process?.env?.NODE_ENV !== 'production';\n this.logger?.info(\n `[LogSmsTransport] would send SMS to ${maskPhoneNumber(message.to)}` +\n (dev ? ` — body: ${message.body}` : ' (body suppressed outside dev)'),\n { messageId },\n );\n return { messageId, response: 'logged' };\n }\n}\n\nexport interface SmsServiceOptions {\n transport: ISmsTransport;\n /**\n * Whether `transport` is a real provider (Aliyun / Twilio / injected).\n * `false` = development log fallback; surfaced via `isConfigured()` so\n * consumers can gate SMS-dependent features in production.\n */\n configured: boolean;\n /** Retry attempts on transport throw. Default 0 (no retry). */\n retries?: number;\n /** Logger for diagnostic output. NEVER receives message bodies. */\n logger?: {\n info: (msg: string, meta?: any) => void;\n warn: (msg: string, meta?: any) => void;\n };\n}\n\n/**\n * Concrete ISmsService implementation.\n *\n * Flow: validate + normalize input → transport.send() (with optional\n * retry) → SendSmsResult. Deliberately NO persistence and NO body logging —\n * see the contract header in `@objectstack/spec/contracts/sms-service.ts`.\n */\nexport class SmsService implements ISmsService {\n constructor(public options: SmsServiceOptions) {\n if (!options.transport) throw new Error('SmsService: transport is required');\n }\n\n /**\n * Hot-swap the underlying transport (settings namespace changed). The\n * `configured` flag travels with the transport.\n */\n setTransport(transport: ISmsTransport, configured: boolean): void {\n this.options.transport = transport;\n this.options.configured = configured;\n }\n\n isConfigured(): boolean {\n return this.options.configured;\n }\n\n async send(input: SendSmsInput): Promise<SendSmsResult> {\n const to = normalizeSmsRecipient(input?.to ?? '');\n if (!to) {\n throw new Error(`VALIDATION_FAILED: '${maskPhoneNumber(String(input?.to ?? ''))}' is not a valid phone number`);\n }\n const body = String(input?.body ?? '').trim();\n if (!body && !input?.templateId && !input?.templateParams) {\n throw new Error('VALIDATION_FAILED: body (or templateId/templateParams) is required');\n }\n\n const normalized: NormalizedSmsMessage = {\n to,\n body,\n ...(input.templateId ? { templateId: input.templateId } : {}),\n ...(input.templateParams ? { templateParams: input.templateParams } : {}),\n };\n\n const id = `sms-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n const maxAttempts = (this.options.retries ?? 0) + 1;\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const result = await this.options.transport.send(normalized);\n this.options.logger?.info(\n `[SmsService] sent to ${maskPhoneNumber(to)} (messageId=${result.messageId})`,\n );\n return { id, status: 'sent', messageId: result.messageId };\n } catch (err) {\n lastError = err;\n if (attempt < maxAttempts) {\n await new Promise((r) => setTimeout(r, Math.min(2000, 100 * 2 ** (attempt - 1))));\n }\n }\n }\n const errMessage = String((lastError as Error)?.message ?? lastError ?? 'send failed').slice(0, 500);\n this.options.logger?.warn(\n `[SmsService] send to ${maskPhoneNumber(to)} failed: ${errMessage}`,\n );\n return { id, status: 'failed', error: errMessage };\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { createHash, createHmac, randomUUID } from 'node:crypto';\nimport type { ISmsTransport, NormalizedSmsMessage, SmsTransportSendResult } from '@objectstack/spec/contracts';\n\nexport interface AliyunSmsTransportOptions {\n accessKeyId: string;\n accessKeySecret: string;\n /** 短信签名 SignName — the registered sender signature, e.g. `阿里云短信测试`. */\n signName: string;\n /**\n * Default 模板 TemplateCode used when the input carries no `templateId`\n * (Aliyun only delivers pre-registered templates — free-form bodies are\n * refused by the API). A catch-all template with a single `${content}`\n * variable makes generic notification sends possible.\n */\n defaultTemplateCode?: string;\n /** API endpoint host. Default `dysmsapi.aliyuncs.com`. */\n endpoint?: string;\n /** Injectable fetch for tests. */\n fetchImpl?: typeof fetch;\n}\n\nconst API_VERSION = '2017-05-25';\nconst ALGORITHM = 'ACS3-HMAC-SHA256';\n\nconst sha256Hex = (s: string): string => createHash('sha256').update(s, 'utf8').digest('hex');\nconst hmac256Hex = (key: string, s: string): string => createHmac('sha256', key).update(s, 'utf8').digest('hex');\n\n/** RFC 3986 percent-encoding (Aliyun requires `%20`, `%2A`, `%7E` handling). */\nconst encode = (s: string): string =>\n encodeURIComponent(s)\n .replace(/\\+/g, '%20')\n .replace(/\\*/g, '%2A')\n .replace(/%7E/g, '~');\n\n/**\n * Aliyun SMS (dysmsapi `SendSms`) transport, signed with the current\n * ACS3-HMAC-SHA256 scheme — plain `fetch` + `node:crypto`, no vendor SDK.\n *\n * Aliyun is template-only: the transport sends `templateId` (falling back to\n * the configured default TemplateCode) with `templateParams` (falling back to\n * `{ content: body }` for the catch-all-template pattern). The rendered\n * `body` itself is never transmitted outside `TemplateParam`.\n */\nexport class AliyunSmsTransport implements ISmsTransport {\n private readonly endpoint: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(private readonly options: AliyunSmsTransportOptions) {\n if (!options.accessKeyId || !options.accessKeySecret) {\n throw new Error('AliyunSmsTransport: accessKeyId and accessKeySecret are required');\n }\n if (!options.signName) {\n throw new Error('AliyunSmsTransport: signName is required');\n }\n this.endpoint = options.endpoint ?? 'dysmsapi.aliyuncs.com';\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async send(message: NormalizedSmsMessage): Promise<SmsTransportSendResult> {\n const templateCode = message.templateId ?? this.options.defaultTemplateCode;\n if (!templateCode) {\n throw new Error(\n 'AliyunSmsTransport: Aliyun requires a template — pass templateId or configure a default template code',\n );\n }\n const templateParam = JSON.stringify(message.templateParams ?? { content: message.body });\n\n const query: Record<string, string> = {\n PhoneNumbers: message.to,\n SignName: this.options.signName,\n TemplateCode: templateCode,\n TemplateParam: templateParam,\n };\n const canonicalQuery = Object.keys(query)\n .sort()\n .map((k) => `${encode(k)}=${encode(query[k])}`)\n .join('&');\n\n const bodyHash = sha256Hex(''); // POST with all parameters in the query string\n const headers: Record<string, string> = {\n host: this.endpoint,\n 'x-acs-action': 'SendSms',\n 'x-acs-content-sha256': bodyHash,\n 'x-acs-date': new Date().toISOString().replace(/\\.\\d{3}Z$/, 'Z'),\n 'x-acs-signature-nonce': randomUUID(),\n 'x-acs-version': API_VERSION,\n };\n const signedHeaderNames = Object.keys(headers).sort();\n const canonicalHeaders = signedHeaderNames.map((k) => `${k}:${headers[k].trim()}\\n`).join('');\n const signedHeaders = signedHeaderNames.join(';');\n\n const canonicalRequest = ['POST', '/', canonicalQuery, canonicalHeaders, signedHeaders, bodyHash].join('\\n');\n const stringToSign = `${ALGORITHM}\\n${sha256Hex(canonicalRequest)}`;\n const signature = hmac256Hex(this.options.accessKeySecret, stringToSign);\n\n const response = await this.fetchImpl(`https://${this.endpoint}/?${canonicalQuery}`, {\n method: 'POST',\n headers: {\n ...headers,\n Authorization: `${ALGORITHM} Credential=${this.options.accessKeyId},SignedHeaders=${signedHeaders},Signature=${signature}`,\n },\n });\n\n let payload: any = {};\n try { payload = await response.json(); } catch { /* non-JSON error body */ }\n if (!response.ok || payload?.Code !== 'OK') {\n const code = payload?.Code ?? `HTTP_${response.status}`;\n const detail = payload?.Message ?? response.statusText ?? 'request failed';\n throw new Error(`Aliyun SendSms failed (${code}): ${detail}`);\n }\n return { messageId: String(payload.BizId ?? payload.RequestId ?? ''), response: payload.RequestId };\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { ISmsTransport, NormalizedSmsMessage, SmsTransportSendResult } from '@objectstack/spec/contracts';\n\nexport interface TwilioSmsTransportOptions {\n accountSid: string;\n authToken: string;\n /** Sender number (E.164). One of `from` / `messagingServiceSid` is required. */\n from?: string;\n /** Twilio Messaging Service SID (alternative to a fixed `from` number). */\n messagingServiceSid?: string;\n /** API base URL override (tests). Default `https://api.twilio.com`. */\n baseUrl?: string;\n /** Injectable fetch for tests. */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * Twilio Programmable Messaging transport — a single `POST\n * /2010-04-01/Accounts/{sid}/Messages.json` with HTTP Basic auth. Plain\n * `fetch`, no vendor SDK. Free-form: delivers `body` verbatim.\n */\nexport class TwilioSmsTransport implements ISmsTransport {\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(private readonly options: TwilioSmsTransportOptions) {\n if (!options.accountSid || !options.authToken) {\n throw new Error('TwilioSmsTransport: accountSid and authToken are required');\n }\n if (!options.from && !options.messagingServiceSid) {\n throw new Error('TwilioSmsTransport: one of from / messagingServiceSid is required');\n }\n this.baseUrl = (options.baseUrl ?? 'https://api.twilio.com').replace(/\\/$/, '');\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async send(message: NormalizedSmsMessage): Promise<SmsTransportSendResult> {\n const form = new URLSearchParams({\n To: message.to,\n Body: message.body,\n ...(this.options.messagingServiceSid\n ? { MessagingServiceSid: this.options.messagingServiceSid }\n : { From: this.options.from as string }),\n });\n const auth = Buffer.from(`${this.options.accountSid}:${this.options.authToken}`).toString('base64');\n const response = await this.fetchImpl(\n `${this.baseUrl}/2010-04-01/Accounts/${encodeURIComponent(this.options.accountSid)}/Messages.json`,\n {\n method: 'POST',\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: form.toString(),\n },\n );\n\n let payload: any = {};\n try { payload = await response.json(); } catch { /* non-JSON error body */ }\n if (!response.ok) {\n const code = payload?.code ?? `HTTP_${response.status}`;\n const detail = payload?.message ?? response.statusText ?? 'request failed';\n throw new Error(`Twilio send failed (${code}): ${detail}`);\n }\n return { messageId: String(payload.sid ?? ''), response: payload.status };\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { ISmsTransport } from '@objectstack/spec/contracts';\nimport { LogSmsTransport } from '../sms-service.js';\nimport { AliyunSmsTransport } from './aliyun.js';\nimport { TwilioSmsTransport } from './twilio.js';\n\nexport { AliyunSmsTransport, type AliyunSmsTransportOptions } from './aliyun.js';\nexport { TwilioSmsTransport, type TwilioSmsTransportOptions } from './twilio.js';\n\nexport type SmsProviderTag = 'log' | 'aliyun' | 'twilio';\n\nexport interface MakeSmsTransportOptions {\n provider: SmsProviderTag;\n /** Provider-specific credentials/options (see the transport option types). */\n options?: Record<string, unknown>;\n logger?: { info: (msg: string, meta?: any) => void };\n}\n\n/**\n * Build an ISmsTransport from a provider tag + opts. Used by\n * SmsServicePlugin to materialise the transport selected by config /\n * the `sms` settings namespace.\n *\n * Throws when a non-`log` provider is missing required credentials.\n */\nexport function makeSmsTransport(opts: MakeSmsTransportOptions): ISmsTransport {\n const { provider, options = {}, logger } = opts;\n switch (provider) {\n case 'log':\n return new LogSmsTransport(logger);\n case 'aliyun':\n return new AliyunSmsTransport(options as any);\n case 'twilio':\n return new TwilioSmsTransport(options as any);\n default:\n throw new Error(`makeSmsTransport: unknown provider '${provider}'`);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { ISmsTransport } from '@objectstack/spec/contracts';\nimport { SmsService, LogSmsTransport, maskPhoneNumber, normalizeSmsRecipient } from './sms-service.js';\nimport { makeSmsTransport, type SmsProviderTag } from './transports/index.js';\n\n/**\n * Plugin configuration. Mirrors EmailServicePluginOptions: a directly\n * injected transport wins, then `provider` + credentials, then the\n * development `LogSmsTransport` fallback (no real send).\n */\nexport interface SmsServicePluginOptions {\n /** Pluggable delivery transport. Overrides `provider`/credentials. */\n transport?: ISmsTransport;\n /** Provider tag — `'log' | 'aliyun' | 'twilio'`. Default `'log'`. */\n provider?: SmsProviderTag;\n /** Provider-specific credentials/options (see transport option types). */\n providerOptions?: Record<string, unknown>;\n /** Retry attempts on transport throw. Default 0. */\n retries?: number;\n}\n\n/** Translate an `sms` settings-namespace snapshot into transport inputs. */\nfunction providerFromSettings(values: Record<string, unknown>): {\n provider: SmsProviderTag;\n options: Record<string, unknown>;\n missing?: string;\n} {\n const provider = String(values.provider ?? 'log') as SmsProviderTag;\n const str = (k: string): string | undefined => {\n const v = values[k];\n return typeof v === 'string' && v.trim().length > 0 ? v.trim() : undefined;\n };\n if (provider === 'aliyun') {\n const accessKeyId = str('aliyun_access_key_id');\n const accessKeySecret = str('aliyun_access_key_secret');\n const signName = str('aliyun_sign_name');\n if (!accessKeyId || !accessKeySecret || !signName) {\n return { provider, options: {}, missing: 'aliyun_access_key_id / aliyun_access_key_secret / aliyun_sign_name' };\n }\n return {\n provider,\n options: {\n accessKeyId,\n accessKeySecret,\n signName,\n ...(str('aliyun_template_code') ? { defaultTemplateCode: str('aliyun_template_code') } : {}),\n },\n };\n }\n if (provider === 'twilio') {\n const accountSid = str('twilio_account_sid');\n const authToken = str('twilio_auth_token');\n const from = str('twilio_from_number');\n const messagingServiceSid = str('twilio_messaging_service_sid');\n if (!accountSid || !authToken || (!from && !messagingServiceSid)) {\n return { provider, options: {}, missing: 'twilio_account_sid / twilio_auth_token / twilio_from_number (or messaging service SID)' };\n }\n return {\n provider,\n options: {\n accountSid,\n authToken,\n ...(from ? { from } : {}),\n ...(messagingServiceSid ? { messagingServiceSid } : {}),\n },\n };\n }\n return { provider: 'log', options: {} };\n}\n\n/**\n * SmsServicePlugin — registers the `sms` service (#2780).\n *\n * Lifecycle:\n * - `init`: build transport (injected → provider+credentials →\n * LogSmsTransport fallback); register the SmsService so dependents\n * (auth OTP, the messaging `sms` channel) can resolve it.\n * - `start` (kernel:ready): bind the `sms` settings namespace so the\n * admin UI can live-swap the provider without a restart, and register\n * the `sms/test` action. Env-locked keys (OS_SMS_*) still win at the\n * settings-resolver level.\n *\n * Deliberately NO persistence objects: SMS bodies carry OTP codes — see the\n * ISmsService contract header.\n */\nexport class SmsServicePlugin implements Plugin {\n name = 'com.objectstack.service.sms';\n version = '1.0.0';\n type = 'standard' as const;\n\n private readonly options: SmsServicePluginOptions;\n private service?: SmsService;\n\n constructor(options: SmsServicePluginOptions = {}) {\n this.options = options;\n }\n\n private resolveInitialTransport(ctx: PluginContext): { transport: ISmsTransport; configured: boolean } {\n if (this.options.transport) return { transport: this.options.transport, configured: true };\n const provider = this.options.provider ?? 'log';\n if (provider === 'log') return { transport: new LogSmsTransport(ctx.logger), configured: false };\n try {\n return {\n transport: makeSmsTransport({ provider, options: this.options.providerOptions, logger: ctx.logger }),\n configured: true,\n };\n } catch (err: any) {\n // Incomplete constructor credentials must not take the kernel down —\n // fall back to the dev transport; the settings bind (kernel:ready)\n // can still swap in a working provider.\n ctx.logger.warn(\n `SmsServicePlugin: provider='${provider}' selected but transport build failed (${err?.message ?? err}) — falling back to LogSmsTransport.`,\n );\n return { transport: new LogSmsTransport(ctx.logger), configured: false };\n }\n }\n\n async init(ctx: PluginContext): Promise<void> {\n const { transport, configured } = this.resolveInitialTransport(ctx);\n if (!configured) {\n ctx.logger.info('SmsServicePlugin: no provider configured — using LogSmsTransport (SMS will NOT be sent)');\n } else {\n ctx.logger.info(`SmsServicePlugin: using '${this.options.provider ?? 'custom'}' provider`);\n }\n this.service = new SmsService({\n transport,\n configured,\n retries: this.options.retries,\n logger: ctx.logger,\n });\n ctx.registerService('sms', this.service);\n ctx.logger.info('SmsServicePlugin: sms service registered');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n ctx.hook('kernel:ready', async () => {\n if (!this.service) return;\n // A host-injected transport is authoritative — settings only manage\n // the provider-tag path.\n if (this.options.transport) return;\n try {\n const settings = ctx.getService<any>('settings');\n if (!settings || typeof settings.getNamespace !== 'function') return;\n\n const applySettings = async () => {\n try {\n const payload = await settings.getNamespace('sms');\n const values: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(payload.values as Record<string, any>)) {\n values[k] = v?.value;\n }\n this.applySmsSettings(values, ctx);\n } catch (err: any) {\n ctx.logger.warn('SmsServicePlugin: failed to apply sms settings: ' + (err?.message ?? err));\n }\n };\n await applySettings();\n if (typeof settings.subscribe === 'function') {\n settings.subscribe('sms', () => { void applySettings(); });\n ctx.logger.info('SmsServicePlugin: bound to settings:changed for namespace=sms');\n }\n\n // `sms/test` action — validate the (possibly unsaved) form values by\n // sending a real test message through a one-shot transport, mirroring\n // the `mail/test` handler in EmailServicePlugin.\n if (typeof settings.registerAction === 'function') {\n const svc = this.service;\n settings.registerAction('sms', 'test', async ({ values, payload, ctx: actionCtx }: any) => {\n const overrides = (payload && typeof payload === 'object' && payload.values && typeof payload.values === 'object')\n ? payload.values\n : (payload ?? {});\n const merged: Record<string, unknown> = { ...(values ?? {}), ...overrides };\n const rawTo = (actionCtx?.body?.to as string | undefined) ?? (payload?.to as string | undefined);\n const to = rawTo ? normalizeSmsRecipient(rawTo) : undefined;\n if (!to) {\n return { ok: false, severity: 'error', message: 'Provide a valid \"to\" phone number (E.164 recommended).' };\n }\n\n const resolved = providerFromSettings(merged);\n if (resolved.missing) {\n return { ok: false, severity: 'error', message: `${resolved.provider}: missing ${resolved.missing}.` };\n }\n\n let target: SmsService = svc;\n if (resolved.provider !== 'log') {\n try {\n const transport = makeSmsTransport({ provider: resolved.provider, options: resolved.options, logger: ctx.logger });\n target = new SmsService({ transport, configured: true, logger: ctx.logger });\n } catch (err: any) {\n return { ok: false, severity: 'error', message: `Failed to build ${resolved.provider} transport: ${err?.message ?? String(err)}` };\n }\n }\n\n try {\n const result = await target.send({\n to,\n body: 'ObjectStack SMS test message.',\n templateParams: { content: 'ObjectStack SMS test message.' },\n });\n if (result.status === 'failed') {\n return { ok: false, severity: 'error', message: result.error ?? 'Send failed.' };\n }\n return {\n ok: true,\n severity: 'info',\n message: `Sent test SMS to ${maskPhoneNumber(to)} via ${resolved.provider} (id=${result.messageId ?? result.id}).`,\n };\n } catch (err: any) {\n return { ok: false, severity: 'error', message: err?.message ?? String(err) };\n }\n });\n }\n } catch {\n // settings service not registered — constructor opts remain authoritative.\n }\n });\n }\n\n /**\n * Translate the `sms` settings snapshot into a transport and hot-swap it\n * on the running SmsService. Incomplete credentials keep the previous\n * transport (with a warning) so a half-saved form can't break delivery.\n */\n private applySmsSettings(values: Record<string, unknown>, ctx: PluginContext): void {\n if (!this.service) return;\n const resolved = providerFromSettings(values);\n if (resolved.missing) {\n ctx.logger.warn(\n `SmsServicePlugin: provider='${resolved.provider}' selected but ${resolved.missing} is empty — transport NOT rebuilt.`,\n );\n return;\n }\n if (resolved.provider === 'log') {\n // Downgrade to the dev transport only when the operator explicitly\n // selected `log`; an unset namespace keeps the constructor opts.\n if (values.provider === 'log') {\n this.service.setTransport(new LogSmsTransport(ctx.logger), false);\n ctx.logger.info('SmsServicePlugin: sms settings applied (provider=log; SMS will NOT be sent).');\n }\n return;\n }\n try {\n const transport = makeSmsTransport({ provider: resolved.provider, options: resolved.options, logger: ctx.logger });\n this.service.setTransport(transport, true);\n ctx.logger.info(`SmsServicePlugin: transport rebuilt from settings (provider=${resolved.provider}).`);\n } catch (err: any) {\n ctx.logger.warn('SmsServicePlugin: failed to rebuild transport: ' + (err?.message ?? err));\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/sms-service.ts","../src/transports/aliyun.ts","../src/transports/twilio.ts","../src/transports/index.ts","../src/sms-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport {\n SmsService,\n LogSmsTransport,\n maskPhoneNumber,\n normalizeSmsRecipient,\n type SmsServiceOptions,\n} from './sms-service.js';\nexport { SmsServicePlugin, type SmsServicePluginOptions } from './sms-plugin.js';\nexport {\n makeSmsTransport,\n AliyunSmsTransport,\n TwilioSmsTransport,\n type SmsProviderTag,\n type MakeSmsTransportOptions,\n type AliyunSmsTransportOptions,\n type TwilioSmsTransportOptions,\n} from './transports/index.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n ISmsService,\n ISmsTransport,\n NormalizedSmsMessage,\n SendSmsInput,\n SendSmsResult,\n SmsTransportSendResult,\n} from '@objectstack/spec/contracts';\n\n/**\n * Normalize + validate a recipient phone number. Accepts E.164 and common\n * human formats (spaces / dashes / dots / parens are stripped). Returns\n * `undefined` when the result doesn't look like a phone number.\n *\n * Same shape rule as plugin-auth's `normalizePhoneNumber` — 6-15 digits\n * with an optional leading `+` (kept local: the two packages must not\n * depend on each other).\n */\nexport function normalizeSmsRecipient(raw: string): string | undefined {\n const stripped = String(raw ?? '').replace(/[\\s\\-().]/g, '');\n return /^\\+?[0-9]{6,15}$/.test(stripped) ? stripped : undefined;\n}\n\n/**\n * Mask a phone number for log lines: keep the prefix (country-code-ish) and\n * the last two digits, hide the middle. `+8613812345678` → `+8613******78`.\n * SMS logging policy: masked recipient + status ONLY — never the body\n * (OTP codes travel in it; see the ISmsService contract header).\n */\nexport function maskPhoneNumber(phone: string): string {\n const p = String(phone ?? '');\n if (p.length <= 6) return `${p.slice(0, 2)}****`;\n return `${p.slice(0, 5)}${'*'.repeat(Math.max(2, p.length - 7))}${p.slice(-2)}`;\n}\n\n/**\n * Development transport — never actually sends. Logs the masked recipient\n * (and, OUTSIDE production only, the message body so local OTP flows are\n * testable) and returns a synthetic message id.\n *\n * The production-body suppression is a hard rule from #2780: OTP codes must\n * never land in logs. In dev the body IS the delivery — same pattern as the\n * auth magic-link / invitation URLs, which are printed in dev only.\n */\nexport class LogSmsTransport implements ISmsTransport {\n private counter = 0;\n constructor(private readonly logger?: { info: (msg: string, meta?: any) => void }) {}\n async send(message: NormalizedSmsMessage): Promise<SmsTransportSendResult> {\n const messageId = `dev-sms-${Date.now()}-${++this.counter}`;\n const dev = (globalThis as any)?.process?.env?.NODE_ENV !== 'production';\n this.logger?.info(\n `[LogSmsTransport] would send SMS to ${maskPhoneNumber(message.to)}` +\n (dev ? ` — body: ${message.body}` : ' (body suppressed outside dev)'),\n { messageId },\n );\n return { messageId, response: 'logged' };\n }\n}\n\nexport interface SmsServiceOptions {\n transport: ISmsTransport;\n /**\n * Whether `transport` is a real provider (Aliyun / Twilio / injected).\n * `false` = development log fallback; surfaced via `isConfigured()` so\n * consumers can gate SMS-dependent features in production.\n */\n configured: boolean;\n /** Retry attempts on transport throw. Default 0 (no retry). */\n retries?: number;\n /** Logger for diagnostic output. NEVER receives message bodies. */\n logger?: {\n info: (msg: string, meta?: any) => void;\n warn: (msg: string, meta?: any) => void;\n };\n}\n\n/**\n * Concrete ISmsService implementation.\n *\n * Flow: validate + normalize input → transport.send() (with optional\n * retry) → SendSmsResult. Deliberately NO persistence and NO body logging —\n * see the contract header in `@objectstack/spec/contracts/sms-service.ts`.\n */\nexport class SmsService implements ISmsService {\n constructor(public options: SmsServiceOptions) {\n if (!options.transport) throw new Error('SmsService: transport is required');\n }\n\n /**\n * Hot-swap the underlying transport (settings namespace changed). The\n * `configured` flag travels with the transport.\n */\n setTransport(transport: ISmsTransport, configured: boolean): void {\n this.options.transport = transport;\n this.options.configured = configured;\n }\n\n isConfigured(): boolean {\n return this.options.configured;\n }\n\n async send(input: SendSmsInput): Promise<SendSmsResult> {\n const to = normalizeSmsRecipient(input?.to ?? '');\n if (!to) {\n throw new Error(`VALIDATION_FAILED: '${maskPhoneNumber(String(input?.to ?? ''))}' is not a valid phone number`);\n }\n const body = String(input?.body ?? '').trim();\n if (!body && !input?.templateId && !input?.templateParams) {\n throw new Error('VALIDATION_FAILED: body (or templateId/templateParams) is required');\n }\n\n const normalized: NormalizedSmsMessage = {\n to,\n body,\n ...(input.templateId ? { templateId: input.templateId } : {}),\n ...(input.templateParams ? { templateParams: input.templateParams } : {}),\n };\n\n const id = `sms-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n const maxAttempts = (this.options.retries ?? 0) + 1;\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const result = await this.options.transport.send(normalized);\n this.options.logger?.info(\n `[SmsService] sent to ${maskPhoneNumber(to)} (messageId=${result.messageId})`,\n );\n return { id, status: 'sent', messageId: result.messageId };\n } catch (err) {\n lastError = err;\n if (attempt < maxAttempts) {\n await new Promise((r) => setTimeout(r, Math.min(2000, 100 * 2 ** (attempt - 1))));\n }\n }\n }\n const errMessage = String((lastError as Error)?.message ?? lastError ?? 'send failed').slice(0, 500);\n this.options.logger?.warn(\n `[SmsService] send to ${maskPhoneNumber(to)} failed: ${errMessage}`,\n );\n return { id, status: 'failed', error: errMessage };\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { createHash, createHmac, randomUUID } from 'node:crypto';\nimport type { ISmsTransport, NormalizedSmsMessage, SmsTransportSendResult } from '@objectstack/spec/contracts';\n\nexport interface AliyunSmsTransportOptions {\n accessKeyId: string;\n accessKeySecret: string;\n /** 短信签名 SignName — the registered sender signature, e.g. `阿里云短信测试`. */\n signName: string;\n /**\n * Default 模板 TemplateCode used when the input carries no `templateId`\n * (Aliyun only delivers pre-registered templates — free-form bodies are\n * refused by the API). A catch-all template with a single `${content}`\n * variable makes generic notification sends possible.\n */\n defaultTemplateCode?: string;\n /** API endpoint host. Default `dysmsapi.aliyuncs.com`. */\n endpoint?: string;\n /** Injectable fetch for tests. */\n fetchImpl?: typeof fetch;\n}\n\nconst API_VERSION = '2017-05-25';\nconst ALGORITHM = 'ACS3-HMAC-SHA256';\n\nconst sha256Hex = (s: string): string => createHash('sha256').update(s, 'utf8').digest('hex');\nconst hmac256Hex = (key: string, s: string): string => createHmac('sha256', key).update(s, 'utf8').digest('hex');\n\n/** RFC 3986 percent-encoding (Aliyun requires `%20`, `%2A`, `%7E` handling). */\nconst encode = (s: string): string =>\n encodeURIComponent(s)\n .replace(/\\+/g, '%20')\n .replace(/\\*/g, '%2A')\n .replace(/%7E/g, '~');\n\n/**\n * Aliyun SMS (dysmsapi `SendSms`) transport, signed with the current\n * ACS3-HMAC-SHA256 scheme — plain `fetch` + `node:crypto`, no vendor SDK.\n *\n * Aliyun is template-only: the transport sends `templateId` (falling back to\n * the configured default TemplateCode) with `templateParams` (falling back to\n * `{ content: body }` for the catch-all-template pattern). The rendered\n * `body` itself is never transmitted outside `TemplateParam`.\n */\nexport class AliyunSmsTransport implements ISmsTransport {\n private readonly endpoint: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(private readonly options: AliyunSmsTransportOptions) {\n if (!options.accessKeyId || !options.accessKeySecret) {\n throw new Error('AliyunSmsTransport: accessKeyId and accessKeySecret are required');\n }\n if (!options.signName) {\n throw new Error('AliyunSmsTransport: signName is required');\n }\n this.endpoint = options.endpoint ?? 'dysmsapi.aliyuncs.com';\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async send(message: NormalizedSmsMessage): Promise<SmsTransportSendResult> {\n const templateCode = message.templateId ?? this.options.defaultTemplateCode;\n if (!templateCode) {\n throw new Error(\n 'AliyunSmsTransport: Aliyun requires a template — pass templateId or configure a default template code',\n );\n }\n const templateParam = JSON.stringify(message.templateParams ?? { content: message.body });\n\n const query: Record<string, string> = {\n PhoneNumbers: message.to,\n SignName: this.options.signName,\n TemplateCode: templateCode,\n TemplateParam: templateParam,\n };\n const canonicalQuery = Object.keys(query)\n .sort()\n .map((k) => `${encode(k)}=${encode(query[k])}`)\n .join('&');\n\n const bodyHash = sha256Hex(''); // POST with all parameters in the query string\n const headers: Record<string, string> = {\n host: this.endpoint,\n 'x-acs-action': 'SendSms',\n 'x-acs-content-sha256': bodyHash,\n 'x-acs-date': new Date().toISOString().replace(/\\.\\d{3}Z$/, 'Z'),\n 'x-acs-signature-nonce': randomUUID(),\n 'x-acs-version': API_VERSION,\n };\n const signedHeaderNames = Object.keys(headers).sort();\n const canonicalHeaders = signedHeaderNames.map((k) => `${k}:${headers[k].trim()}\\n`).join('');\n const signedHeaders = signedHeaderNames.join(';');\n\n const canonicalRequest = ['POST', '/', canonicalQuery, canonicalHeaders, signedHeaders, bodyHash].join('\\n');\n const stringToSign = `${ALGORITHM}\\n${sha256Hex(canonicalRequest)}`;\n const signature = hmac256Hex(this.options.accessKeySecret, stringToSign);\n\n const response = await this.fetchImpl(`https://${this.endpoint}/?${canonicalQuery}`, {\n method: 'POST',\n headers: {\n ...headers,\n Authorization: `${ALGORITHM} Credential=${this.options.accessKeyId},SignedHeaders=${signedHeaders},Signature=${signature}`,\n },\n });\n\n let payload: any = {};\n try { payload = await response.json(); } catch { /* non-JSON error body */ }\n if (!response.ok || payload?.Code !== 'OK') {\n const code = payload?.Code ?? `HTTP_${response.status}`;\n const detail = payload?.Message ?? response.statusText ?? 'request failed';\n throw new Error(`Aliyun SendSms failed (${code}): ${detail}`);\n }\n return { messageId: String(payload.BizId ?? payload.RequestId ?? ''), response: payload.RequestId };\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { ISmsTransport, NormalizedSmsMessage, SmsTransportSendResult } from '@objectstack/spec/contracts';\n\nexport interface TwilioSmsTransportOptions {\n accountSid: string;\n authToken: string;\n /** Sender number (E.164). One of `from` / `messagingServiceSid` is required. */\n from?: string;\n /** Twilio Messaging Service SID (alternative to a fixed `from` number). */\n messagingServiceSid?: string;\n /** API base URL override (tests). Default `https://api.twilio.com`. */\n baseUrl?: string;\n /** Injectable fetch for tests. */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * Twilio Programmable Messaging transport — a single `POST\n * /2010-04-01/Accounts/{sid}/Messages.json` with HTTP Basic auth. Plain\n * `fetch`, no vendor SDK. Free-form: delivers `body` verbatim.\n */\nexport class TwilioSmsTransport implements ISmsTransport {\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(private readonly options: TwilioSmsTransportOptions) {\n if (!options.accountSid || !options.authToken) {\n throw new Error('TwilioSmsTransport: accountSid and authToken are required');\n }\n if (!options.from && !options.messagingServiceSid) {\n throw new Error('TwilioSmsTransport: one of from / messagingServiceSid is required');\n }\n this.baseUrl = (options.baseUrl ?? 'https://api.twilio.com').replace(/\\/$/, '');\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async send(message: NormalizedSmsMessage): Promise<SmsTransportSendResult> {\n const form = new URLSearchParams({\n To: message.to,\n Body: message.body,\n ...(this.options.messagingServiceSid\n ? { MessagingServiceSid: this.options.messagingServiceSid }\n : { From: this.options.from as string }),\n });\n const auth = Buffer.from(`${this.options.accountSid}:${this.options.authToken}`).toString('base64');\n const response = await this.fetchImpl(\n `${this.baseUrl}/2010-04-01/Accounts/${encodeURIComponent(this.options.accountSid)}/Messages.json`,\n {\n method: 'POST',\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: form.toString(),\n },\n );\n\n let payload: any = {};\n try { payload = await response.json(); } catch { /* non-JSON error body */ }\n if (!response.ok) {\n const code = payload?.code ?? `HTTP_${response.status}`;\n const detail = payload?.message ?? response.statusText ?? 'request failed';\n throw new Error(`Twilio send failed (${code}): ${detail}`);\n }\n return { messageId: String(payload.sid ?? ''), response: payload.status };\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { ISmsTransport } from '@objectstack/spec/contracts';\nimport { LogSmsTransport } from '../sms-service.js';\nimport { AliyunSmsTransport } from './aliyun.js';\nimport { TwilioSmsTransport } from './twilio.js';\n\nexport { AliyunSmsTransport, type AliyunSmsTransportOptions } from './aliyun.js';\nexport { TwilioSmsTransport, type TwilioSmsTransportOptions } from './twilio.js';\n\nexport type SmsProviderTag = 'log' | 'aliyun' | 'twilio';\n\nexport interface MakeSmsTransportOptions {\n provider: SmsProviderTag;\n /** Provider-specific credentials/options (see the transport option types). */\n options?: Record<string, unknown>;\n logger?: { info: (msg: string, meta?: any) => void };\n}\n\n/**\n * Build an ISmsTransport from a provider tag + opts. Used by\n * SmsServicePlugin to materialise the transport selected by config /\n * the `sms` settings namespace.\n *\n * Throws when a non-`log` provider is missing required credentials.\n */\nexport function makeSmsTransport(opts: MakeSmsTransportOptions): ISmsTransport {\n const { provider, options = {}, logger } = opts;\n switch (provider) {\n case 'log':\n return new LogSmsTransport(logger);\n case 'aliyun':\n return new AliyunSmsTransport(options as any);\n case 'twilio':\n return new TwilioSmsTransport(options as any);\n default:\n throw new Error(`makeSmsTransport: unknown provider '${provider}'`);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { ISmsTransport } from '@objectstack/spec/contracts';\nimport { SmsService, LogSmsTransport, maskPhoneNumber, normalizeSmsRecipient } from './sms-service.js';\nimport { makeSmsTransport, type SmsProviderTag } from './transports/index.js';\n\n/**\n * Plugin configuration. Mirrors EmailServicePluginOptions: a directly\n * injected transport wins, then `provider` + credentials, then the\n * development `LogSmsTransport` fallback (no real send).\n */\nexport interface SmsServicePluginOptions {\n /** Pluggable delivery transport. Overrides `provider`/credentials. */\n transport?: ISmsTransport;\n /** Provider tag — `'log' | 'aliyun' | 'twilio'`. Default `'log'`. */\n provider?: SmsProviderTag;\n /** Provider-specific credentials/options (see transport option types). */\n providerOptions?: Record<string, unknown>;\n /** Retry attempts on transport throw. Default 0. */\n retries?: number;\n}\n\n/** Translate an `sms` settings-namespace snapshot into transport inputs. */\nfunction providerFromSettings(values: Record<string, unknown>): {\n provider: SmsProviderTag;\n options: Record<string, unknown>;\n missing?: string;\n} {\n const provider = String(values.provider ?? 'log') as SmsProviderTag;\n const str = (k: string): string | undefined => {\n const v = values[k];\n return typeof v === 'string' && v.trim().length > 0 ? v.trim() : undefined;\n };\n if (provider === 'aliyun') {\n const accessKeyId = str('aliyun_access_key_id');\n const accessKeySecret = str('aliyun_access_key_secret');\n const signName = str('aliyun_sign_name');\n if (!accessKeyId || !accessKeySecret || !signName) {\n return { provider, options: {}, missing: 'aliyun_access_key_id / aliyun_access_key_secret / aliyun_sign_name' };\n }\n return {\n provider,\n options: {\n accessKeyId,\n accessKeySecret,\n signName,\n ...(str('aliyun_template_code') ? { defaultTemplateCode: str('aliyun_template_code') } : {}),\n },\n };\n }\n if (provider === 'twilio') {\n const accountSid = str('twilio_account_sid');\n const authToken = str('twilio_auth_token');\n const from = str('twilio_from_number');\n const messagingServiceSid = str('twilio_messaging_service_sid');\n if (!accountSid || !authToken || (!from && !messagingServiceSid)) {\n return { provider, options: {}, missing: 'twilio_account_sid / twilio_auth_token / twilio_from_number (or messaging service SID)' };\n }\n return {\n provider,\n options: {\n accountSid,\n authToken,\n ...(from ? { from } : {}),\n ...(messagingServiceSid ? { messagingServiceSid } : {}),\n },\n };\n }\n return { provider: 'log', options: {} };\n}\n\n/**\n * SmsServicePlugin — registers the `sms` service (#2780).\n *\n * Lifecycle:\n * - `init`: build transport (injected → provider+credentials →\n * LogSmsTransport fallback); register the SmsService so dependents\n * (auth OTP, the messaging `sms` channel) can resolve it.\n * - `start` (kernel:ready): bind the `sms` settings namespace so the\n * admin UI can live-swap the provider without a restart, and register\n * the `sms/test` action. Env-locked keys (OS_SMS_*) still win at the\n * settings-resolver level.\n *\n * Deliberately NO persistence objects: SMS bodies carry OTP codes — see the\n * ISmsService contract header.\n */\nexport class SmsServicePlugin implements Plugin {\n name = 'com.objectstack.service.sms';\n /**\n * Services init() registers on every path (ADR-0116, #4131) — lets the\n * kernel name this plugin when a consumer requires one before it inits.\n */\n providesServices = ['sms'];\n version = '1.0.0';\n type = 'standard' as const;\n\n private readonly options: SmsServicePluginOptions;\n private service?: SmsService;\n\n constructor(options: SmsServicePluginOptions = {}) {\n this.options = options;\n }\n\n private resolveInitialTransport(ctx: PluginContext): { transport: ISmsTransport; configured: boolean } {\n if (this.options.transport) return { transport: this.options.transport, configured: true };\n const provider = this.options.provider ?? 'log';\n if (provider === 'log') return { transport: new LogSmsTransport(ctx.logger), configured: false };\n try {\n return {\n transport: makeSmsTransport({ provider, options: this.options.providerOptions, logger: ctx.logger }),\n configured: true,\n };\n } catch (err: any) {\n // Incomplete constructor credentials must not take the kernel down —\n // fall back to the dev transport; the settings bind (kernel:ready)\n // can still swap in a working provider.\n ctx.logger.warn(\n `SmsServicePlugin: provider='${provider}' selected but transport build failed (${err?.message ?? err}) — falling back to LogSmsTransport.`,\n );\n return { transport: new LogSmsTransport(ctx.logger), configured: false };\n }\n }\n\n async init(ctx: PluginContext): Promise<void> {\n const { transport, configured } = this.resolveInitialTransport(ctx);\n if (!configured) {\n ctx.logger.info('SmsServicePlugin: no provider configured — using LogSmsTransport (SMS will NOT be sent)');\n } else {\n ctx.logger.info(`SmsServicePlugin: using '${this.options.provider ?? 'custom'}' provider`);\n }\n this.service = new SmsService({\n transport,\n configured,\n retries: this.options.retries,\n logger: ctx.logger,\n });\n ctx.registerService('sms', this.service);\n ctx.logger.info('SmsServicePlugin: sms service registered');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n ctx.hook('kernel:ready', async () => {\n if (!this.service) return;\n // A host-injected transport is authoritative — settings only manage\n // the provider-tag path.\n if (this.options.transport) return;\n try {\n const settings = ctx.getService<any>('settings');\n if (!settings || typeof settings.getNamespace !== 'function') return;\n\n const applySettings = async () => {\n try {\n const payload = await settings.getNamespace('sms');\n const values: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(payload.values as Record<string, any>)) {\n values[k] = v?.value;\n }\n this.applySmsSettings(values, ctx);\n } catch (err: any) {\n ctx.logger.warn('SmsServicePlugin: failed to apply sms settings: ' + (err?.message ?? err));\n }\n };\n await applySettings();\n if (typeof settings.subscribe === 'function') {\n settings.subscribe('sms', () => { void applySettings(); });\n ctx.logger.info('SmsServicePlugin: bound to settings:changed for namespace=sms');\n }\n\n // `sms/test` action — validate the (possibly unsaved) form values by\n // sending a real test message through a one-shot transport, mirroring\n // the `mail/test` handler in EmailServicePlugin.\n if (typeof settings.registerAction === 'function') {\n const svc = this.service;\n settings.registerAction('sms', 'test', async ({ values, payload, ctx: actionCtx }: any) => {\n const overrides = (payload && typeof payload === 'object' && payload.values && typeof payload.values === 'object')\n ? payload.values\n : (payload ?? {});\n const merged: Record<string, unknown> = { ...(values ?? {}), ...overrides };\n const rawTo = (actionCtx?.body?.to as string | undefined) ?? (payload?.to as string | undefined);\n const to = rawTo ? normalizeSmsRecipient(rawTo) : undefined;\n if (!to) {\n return { ok: false, severity: 'error', message: 'Provide a valid \"to\" phone number (E.164 recommended).' };\n }\n\n const resolved = providerFromSettings(merged);\n if (resolved.missing) {\n return { ok: false, severity: 'error', message: `${resolved.provider}: missing ${resolved.missing}.` };\n }\n\n let target: SmsService = svc;\n if (resolved.provider !== 'log') {\n try {\n const transport = makeSmsTransport({ provider: resolved.provider, options: resolved.options, logger: ctx.logger });\n target = new SmsService({ transport, configured: true, logger: ctx.logger });\n } catch (err: any) {\n return { ok: false, severity: 'error', message: `Failed to build ${resolved.provider} transport: ${err?.message ?? String(err)}` };\n }\n }\n\n try {\n const result = await target.send({\n to,\n body: 'ObjectStack SMS test message.',\n templateParams: { content: 'ObjectStack SMS test message.' },\n });\n if (result.status === 'failed') {\n return { ok: false, severity: 'error', message: result.error ?? 'Send failed.' };\n }\n return {\n ok: true,\n severity: 'info',\n message: `Sent test SMS to ${maskPhoneNumber(to)} via ${resolved.provider} (id=${result.messageId ?? result.id}).`,\n };\n } catch (err: any) {\n return { ok: false, severity: 'error', message: err?.message ?? String(err) };\n }\n });\n }\n } catch {\n // settings service not registered — constructor opts remain authoritative.\n }\n });\n }\n\n /**\n * Translate the `sms` settings snapshot into a transport and hot-swap it\n * on the running SmsService. Incomplete credentials keep the previous\n * transport (with a warning) so a half-saved form can't break delivery.\n */\n private applySmsSettings(values: Record<string, unknown>, ctx: PluginContext): void {\n if (!this.service) return;\n const resolved = providerFromSettings(values);\n if (resolved.missing) {\n ctx.logger.warn(\n `SmsServicePlugin: provider='${resolved.provider}' selected but ${resolved.missing} is empty — transport NOT rebuilt.`,\n );\n return;\n }\n if (resolved.provider === 'log') {\n // Downgrade to the dev transport only when the operator explicitly\n // selected `log`; an unset namespace keeps the constructor opts.\n if (values.provider === 'log') {\n this.service.setTransport(new LogSmsTransport(ctx.logger), false);\n ctx.logger.info('SmsServicePlugin: sms settings applied (provider=log; SMS will NOT be sent).');\n }\n return;\n }\n try {\n const transport = makeSmsTransport({ provider: resolved.provider, options: resolved.options, logger: ctx.logger });\n this.service.setTransport(transport, true);\n ctx.logger.info(`SmsServicePlugin: transport rebuilt from settings (provider=${resolved.provider}).`);\n } catch (err: any) {\n ctx.logger.warn('SmsServicePlugin: failed to rebuild transport: ' + (err?.message ?? err));\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACoBO,SAAS,sBAAsB,KAAiC;AACrE,QAAM,WAAW,OAAO,OAAO,EAAE,EAAE,QAAQ,cAAc,EAAE;AAC3D,SAAO,mBAAmB,KAAK,QAAQ,IAAI,WAAW;AACxD;AAQO,SAAS,gBAAgB,OAAuB;AACrD,QAAM,IAAI,OAAO,SAAS,EAAE;AAC5B,MAAI,EAAE,UAAU,EAAG,QAAO,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1C,SAAO,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,OAAO,KAAK,IAAI,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC;AAC/E;AAWO,IAAM,kBAAN,MAA+C;AAAA,EAEpD,YAA6B,QAAsD;AAAtD;AAD7B,SAAQ,UAAU;AAAA,EACkE;AAAA,EACpF,MAAM,KAAK,SAAgE;AACzE,UAAM,YAAY,WAAW,KAAK,IAAI,CAAC,IAAI,EAAE,KAAK,OAAO;AACzD,UAAM,MAAO,YAAoB,SAAS,KAAK,aAAa;AAC5D,SAAK,QAAQ;AAAA,MACX,uCAAuC,gBAAgB,QAAQ,EAAE,CAAC,MACjE,MAAM,iBAAY,QAAQ,IAAI,KAAK;AAAA,MACpC,EAAE,UAAU;AAAA,IACd;AACA,WAAO,EAAE,WAAW,UAAU,SAAS;AAAA,EACzC;AACF;AA0BO,IAAM,aAAN,MAAwC;AAAA,EAC7C,YAAmB,SAA4B;AAA5B;AACjB,QAAI,CAAC,QAAQ,UAAW,OAAM,IAAI,MAAM,mCAAmC;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,WAA0B,YAA2B;AAChE,SAAK,QAAQ,YAAY;AACzB,SAAK,QAAQ,aAAa;AAAA,EAC5B;AAAA,EAEA,eAAwB;AACtB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEA,MAAM,KAAK,OAA6C;AACtD,UAAM,KAAK,sBAAsB,OAAO,MAAM,EAAE;AAChD,QAAI,CAAC,IAAI;AACP,YAAM,IAAI,MAAM,uBAAuB,gBAAgB,OAAO,OAAO,MAAM,EAAE,CAAC,CAAC,+BAA+B;AAAA,IAChH;AACA,UAAM,OAAO,OAAO,OAAO,QAAQ,EAAE,EAAE,KAAK;AAC5C,QAAI,CAAC,QAAQ,CAAC,OAAO,cAAc,CAAC,OAAO,gBAAgB;AACzD,YAAM,IAAI,MAAM,oEAAoE;AAAA,IACtF;AAEA,UAAM,aAAmC;AAAA,MACvC;AAAA,MACA;AAAA,MACA,GAAI,MAAM,aAAa,EAAE,YAAY,MAAM,WAAW,IAAI,CAAC;AAAA,MAC3D,GAAI,MAAM,iBAAiB,EAAE,gBAAgB,MAAM,eAAe,IAAI,CAAC;AAAA,IACzE;AAEA,UAAM,KAAK,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACvE,UAAM,eAAe,KAAK,QAAQ,WAAW,KAAK;AAClD,QAAI;AACJ,aAAS,UAAU,GAAG,WAAW,aAAa,WAAW;AACvD,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,QAAQ,UAAU,KAAK,UAAU;AAC3D,aAAK,QAAQ,QAAQ;AAAA,UACnB,wBAAwB,gBAAgB,EAAE,CAAC,eAAe,OAAO,SAAS;AAAA,QAC5E;AACA,eAAO,EAAE,IAAI,QAAQ,QAAQ,WAAW,OAAO,UAAU;AAAA,MAC3D,SAAS,KAAK;AACZ,oBAAY;AACZ,YAAI,UAAU,aAAa;AACzB,gBAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,KAAK,IAAI,KAAM,MAAM,MAAM,UAAU,EAAE,CAAC,CAAC;AAAA,QAClF;AAAA,MACF;AAAA,IACF;AACA,UAAM,aAAa,OAAQ,WAAqB,WAAW,aAAa,aAAa,EAAE,MAAM,GAAG,GAAG;AACnG,SAAK,QAAQ,QAAQ;AAAA,MACnB,wBAAwB,gBAAgB,EAAE,CAAC,YAAY,UAAU;AAAA,IACnE;AACA,WAAO,EAAE,IAAI,QAAQ,UAAU,OAAO,WAAW;AAAA,EACnD;AACF;;;AC7IA,yBAAmD;AAqBnD,IAAM,cAAc;AACpB,IAAM,YAAY;AAElB,IAAM,YAAY,CAAC,UAAsB,+BAAW,QAAQ,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,KAAK;AAC5F,IAAM,aAAa,CAAC,KAAa,UAAsB,+BAAW,UAAU,GAAG,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,KAAK;AAG/G,IAAM,SAAS,CAAC,MACd,mBAAmB,CAAC,EACjB,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,KAAK,EACpB,QAAQ,QAAQ,GAAG;AAWjB,IAAM,qBAAN,MAAkD;AAAA,EAIvD,YAA6B,SAAoC;AAApC;AAC3B,QAAI,CAAC,QAAQ,eAAe,CAAC,QAAQ,iBAAiB;AACpD,YAAM,IAAI,MAAM,kEAAkE;AAAA,IACpF;AACA,QAAI,CAAC,QAAQ,UAAU;AACrB,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AACA,SAAK,WAAW,QAAQ,YAAY;AACpC,SAAK,YAAY,QAAQ,aAAa;AAAA,EACxC;AAAA,EAEA,MAAM,KAAK,SAAgE;AACzE,UAAM,eAAe,QAAQ,cAAc,KAAK,QAAQ;AACxD,QAAI,CAAC,cAAc;AACjB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,gBAAgB,KAAK,UAAU,QAAQ,kBAAkB,EAAE,SAAS,QAAQ,KAAK,CAAC;AAExF,UAAM,QAAgC;AAAA,MACpC,cAAc,QAAQ;AAAA,MACtB,UAAU,KAAK,QAAQ;AAAA,MACvB,cAAc;AAAA,MACd,eAAe;AAAA,IACjB;AACA,UAAM,iBAAiB,OAAO,KAAK,KAAK,EACrC,KAAK,EACL,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC,IAAI,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,EAC7C,KAAK,GAAG;AAEX,UAAM,WAAW,UAAU,EAAE;AAC7B,UAAM,UAAkC;AAAA,MACtC,MAAM,KAAK;AAAA,MACX,gBAAgB;AAAA,MAChB,wBAAwB;AAAA,MACxB,eAAc,oBAAI,KAAK,GAAE,YAAY,EAAE,QAAQ,aAAa,GAAG;AAAA,MAC/D,6BAAyB,+BAAW;AAAA,MACpC,iBAAiB;AAAA,IACnB;AACA,UAAM,oBAAoB,OAAO,KAAK,OAAO,EAAE,KAAK;AACpD,UAAM,mBAAmB,kBAAkB,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,EAAE,KAAK,CAAC;AAAA,CAAI,EAAE,KAAK,EAAE;AAC5F,UAAM,gBAAgB,kBAAkB,KAAK,GAAG;AAEhD,UAAM,mBAAmB,CAAC,QAAQ,KAAK,gBAAgB,kBAAkB,eAAe,QAAQ,EAAE,KAAK,IAAI;AAC3G,UAAM,eAAe,GAAG,SAAS;AAAA,EAAK,UAAU,gBAAgB,CAAC;AACjE,UAAM,YAAY,WAAW,KAAK,QAAQ,iBAAiB,YAAY;AAEvE,UAAM,WAAW,MAAM,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAK,cAAc,IAAI;AAAA,MACnF,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,GAAG;AAAA,QACH,eAAe,GAAG,SAAS,eAAe,KAAK,QAAQ,WAAW,kBAAkB,aAAa,cAAc,SAAS;AAAA,MAC1H;AAAA,IACF,CAAC;AAED,QAAI,UAAe,CAAC;AACpB,QAAI;AAAE,gBAAU,MAAM,SAAS,KAAK;AAAA,IAAG,QAAQ;AAAA,IAA4B;AAC3E,QAAI,CAAC,SAAS,MAAM,SAAS,SAAS,MAAM;AAC1C,YAAM,OAAO,SAAS,QAAQ,QAAQ,SAAS,MAAM;AACrD,YAAM,SAAS,SAAS,WAAW,SAAS,cAAc;AAC1D,YAAM,IAAI,MAAM,0BAA0B,IAAI,MAAM,MAAM,EAAE;AAAA,IAC9D;AACA,WAAO,EAAE,WAAW,OAAO,QAAQ,SAAS,QAAQ,aAAa,EAAE,GAAG,UAAU,QAAQ,UAAU;AAAA,EACpG;AACF;;;AC5FO,IAAM,qBAAN,MAAkD;AAAA,EAIvD,YAA6B,SAAoC;AAApC;AAC3B,QAAI,CAAC,QAAQ,cAAc,CAAC,QAAQ,WAAW;AAC7C,YAAM,IAAI,MAAM,2DAA2D;AAAA,IAC7E;AACA,QAAI,CAAC,QAAQ,QAAQ,CAAC,QAAQ,qBAAqB;AACjD,YAAM,IAAI,MAAM,mEAAmE;AAAA,IACrF;AACA,SAAK,WAAW,QAAQ,WAAW,0BAA0B,QAAQ,OAAO,EAAE;AAC9E,SAAK,YAAY,QAAQ,aAAa;AAAA,EACxC;AAAA,EAEA,MAAM,KAAK,SAAgE;AACzE,UAAM,OAAO,IAAI,gBAAgB;AAAA,MAC/B,IAAI,QAAQ;AAAA,MACZ,MAAM,QAAQ;AAAA,MACd,GAAI,KAAK,QAAQ,sBACb,EAAE,qBAAqB,KAAK,QAAQ,oBAAoB,IACxD,EAAE,MAAM,KAAK,QAAQ,KAAe;AAAA,IAC1C,CAAC;AACD,UAAM,OAAO,OAAO,KAAK,GAAG,KAAK,QAAQ,UAAU,IAAI,KAAK,QAAQ,SAAS,EAAE,EAAE,SAAS,QAAQ;AAClG,UAAM,WAAW,MAAM,KAAK;AAAA,MAC1B,GAAG,KAAK,OAAO,wBAAwB,mBAAmB,KAAK,QAAQ,UAAU,CAAC;AAAA,MAClF;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,SAAS,IAAI;AAAA,UAC5B,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,SAAS;AAAA,MACtB;AAAA,IACF;AAEA,QAAI,UAAe,CAAC;AACpB,QAAI;AAAE,gBAAU,MAAM,SAAS,KAAK;AAAA,IAAG,QAAQ;AAAA,IAA4B;AAC3E,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,SAAS,QAAQ,QAAQ,SAAS,MAAM;AACrD,YAAM,SAAS,SAAS,WAAW,SAAS,cAAc;AAC1D,YAAM,IAAI,MAAM,uBAAuB,IAAI,MAAM,MAAM,EAAE;AAAA,IAC3D;AACA,WAAO,EAAE,WAAW,OAAO,QAAQ,OAAO,EAAE,GAAG,UAAU,QAAQ,OAAO;AAAA,EAC1E;AACF;;;ACzCO,SAAS,iBAAiB,MAA8C;AAC7E,QAAM,EAAE,UAAU,UAAU,CAAC,GAAG,OAAO,IAAI;AAC3C,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO,IAAI,gBAAgB,MAAM;AAAA,IACnC,KAAK;AACH,aAAO,IAAI,mBAAmB,OAAc;AAAA,IAC9C,KAAK;AACH,aAAO,IAAI,mBAAmB,OAAc;AAAA,IAC9C;AACE,YAAM,IAAI,MAAM,uCAAuC,QAAQ,GAAG;AAAA,EACtE;AACF;;;ACdA,SAAS,qBAAqB,QAI5B;AACA,QAAM,WAAW,OAAO,OAAO,YAAY,KAAK;AAChD,QAAM,MAAM,CAAC,MAAkC;AAC7C,UAAM,IAAI,OAAO,CAAC;AAClB,WAAO,OAAO,MAAM,YAAY,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,KAAK,IAAI;AAAA,EACnE;AACA,MAAI,aAAa,UAAU;AACzB,UAAM,cAAc,IAAI,sBAAsB;AAC9C,UAAM,kBAAkB,IAAI,0BAA0B;AACtD,UAAM,WAAW,IAAI,kBAAkB;AACvC,QAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC,UAAU;AACjD,aAAO,EAAE,UAAU,SAAS,CAAC,GAAG,SAAS,qEAAqE;AAAA,IAChH;AACA,WAAO;AAAA,MACL;AAAA,MACA,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAI,IAAI,sBAAsB,IAAI,EAAE,qBAAqB,IAAI,sBAAsB,EAAE,IAAI,CAAC;AAAA,MAC5F;AAAA,IACF;AAAA,EACF;AACA,MAAI,aAAa,UAAU;AACzB,UAAM,aAAa,IAAI,oBAAoB;AAC3C,UAAM,YAAY,IAAI,mBAAmB;AACzC,UAAM,OAAO,IAAI,oBAAoB;AACrC,UAAM,sBAAsB,IAAI,8BAA8B;AAC9D,QAAI,CAAC,cAAc,CAAC,aAAc,CAAC,QAAQ,CAAC,qBAAsB;AAChE,aAAO,EAAE,UAAU,SAAS,CAAC,GAAG,SAAS,yFAAyF;AAAA,IACpI;AACA,WAAO;AAAA,MACL;AAAA,MACA,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,QACvB,GAAI,sBAAsB,EAAE,oBAAoB,IAAI,CAAC;AAAA,MACvD;AAAA,IACF;AAAA,EACF;AACA,SAAO,EAAE,UAAU,OAAO,SAAS,CAAC,EAAE;AACxC;AAiBO,IAAM,mBAAN,MAAyC;AAAA,EAa9C,YAAY,UAAmC,CAAC,GAAG;AAZnD,gBAAO;AAKP;AAAA;AAAA;AAAA;AAAA,4BAAmB,CAAC,KAAK;AACzB,mBAAU;AACV,gBAAO;AAML,SAAK,UAAU;AAAA,EACjB;AAAA,EAEQ,wBAAwB,KAAuE;AACrG,QAAI,KAAK,QAAQ,UAAW,QAAO,EAAE,WAAW,KAAK,QAAQ,WAAW,YAAY,KAAK;AACzF,UAAM,WAAW,KAAK,QAAQ,YAAY;AAC1C,QAAI,aAAa,MAAO,QAAO,EAAE,WAAW,IAAI,gBAAgB,IAAI,MAAM,GAAG,YAAY,MAAM;AAC/F,QAAI;AACF,aAAO;AAAA,QACL,WAAW,iBAAiB,EAAE,UAAU,SAAS,KAAK,QAAQ,iBAAiB,QAAQ,IAAI,OAAO,CAAC;AAAA,QACnG,YAAY;AAAA,MACd;AAAA,IACF,SAAS,KAAU;AAIjB,UAAI,OAAO;AAAA,QACT,+BAA+B,QAAQ,0CAA0C,KAAK,WAAW,GAAG;AAAA,MACtG;AACA,aAAO,EAAE,WAAW,IAAI,gBAAgB,IAAI,MAAM,GAAG,YAAY,MAAM;AAAA,IACzE;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,KAAmC;AAC5C,UAAM,EAAE,WAAW,WAAW,IAAI,KAAK,wBAAwB,GAAG;AAClE,QAAI,CAAC,YAAY;AACf,UAAI,OAAO,KAAK,8FAAyF;AAAA,IAC3G,OAAO;AACL,UAAI,OAAO,KAAK,4BAA4B,KAAK,QAAQ,YAAY,QAAQ,YAAY;AAAA,IAC3F;AACA,SAAK,UAAU,IAAI,WAAW;AAAA,MAC5B;AAAA,MACA;AAAA,MACA,SAAS,KAAK,QAAQ;AAAA,MACtB,QAAQ,IAAI;AAAA,IACd,CAAC;AACD,QAAI,gBAAgB,OAAO,KAAK,OAAO;AACvC,QAAI,OAAO,KAAK,0CAA0C;AAAA,EAC5D;AAAA,EAEA,MAAM,MAAM,KAAmC;AAC7C,QAAI,KAAK,gBAAgB,YAAY;AACnC,UAAI,CAAC,KAAK,QAAS;AAGnB,UAAI,KAAK,QAAQ,UAAW;AAC5B,UAAI;AACF,cAAM,WAAW,IAAI,WAAgB,UAAU;AAC/C,YAAI,CAAC,YAAY,OAAO,SAAS,iBAAiB,WAAY;AAE9D,cAAM,gBAAgB,YAAY;AAChC,cAAI;AACF,kBAAM,UAAU,MAAM,SAAS,aAAa,KAAK;AACjD,kBAAM,SAAkC,CAAC;AACzC,uBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,QAAQ,MAA6B,GAAG;AAC1E,qBAAO,CAAC,IAAI,GAAG;AAAA,YACjB;AACA,iBAAK,iBAAiB,QAAQ,GAAG;AAAA,UACnC,SAAS,KAAU;AACjB,gBAAI,OAAO,KAAK,sDAAsD,KAAK,WAAW,IAAI;AAAA,UAC5F;AAAA,QACF;AACA,cAAM,cAAc;AACpB,YAAI,OAAO,SAAS,cAAc,YAAY;AAC5C,mBAAS,UAAU,OAAO,MAAM;AAAE,iBAAK,cAAc;AAAA,UAAG,CAAC;AACzD,cAAI,OAAO,KAAK,+DAA+D;AAAA,QACjF;AAKA,YAAI,OAAO,SAAS,mBAAmB,YAAY;AACjD,gBAAM,MAAM,KAAK;AACjB,mBAAS,eAAe,OAAO,QAAQ,OAAO,EAAE,QAAQ,SAAS,KAAK,UAAU,MAAW;AACzF,kBAAM,YAAa,WAAW,OAAO,YAAY,YAAY,QAAQ,UAAU,OAAO,QAAQ,WAAW,WACrG,QAAQ,SACP,WAAW,CAAC;AACjB,kBAAM,SAAkC,EAAE,GAAI,UAAU,CAAC,GAAI,GAAG,UAAU;AAC1E,kBAAM,QAAS,WAAW,MAAM,MAA8B,SAAS;AACvE,kBAAM,KAAK,QAAQ,sBAAsB,KAAK,IAAI;AAClD,gBAAI,CAAC,IAAI;AACP,qBAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,yDAAyD;AAAA,YAC3G;AAEA,kBAAM,WAAW,qBAAqB,MAAM;AAC5C,gBAAI,SAAS,SAAS;AACpB,qBAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,GAAG,SAAS,QAAQ,aAAa,SAAS,OAAO,IAAI;AAAA,YACvG;AAEA,gBAAI,SAAqB;AACzB,gBAAI,SAAS,aAAa,OAAO;AAC/B,kBAAI;AACF,sBAAM,YAAY,iBAAiB,EAAE,UAAU,SAAS,UAAU,SAAS,SAAS,SAAS,QAAQ,IAAI,OAAO,CAAC;AACjH,yBAAS,IAAI,WAAW,EAAE,WAAW,YAAY,MAAM,QAAQ,IAAI,OAAO,CAAC;AAAA,cAC7E,SAAS,KAAU;AACjB,uBAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,mBAAmB,SAAS,QAAQ,eAAe,KAAK,WAAW,OAAO,GAAG,CAAC,GAAG;AAAA,cACnI;AAAA,YACF;AAEA,gBAAI;AACF,oBAAM,SAAS,MAAM,OAAO,KAAK;AAAA,gBAC/B;AAAA,gBACA,MAAM;AAAA,gBACN,gBAAgB,EAAE,SAAS,gCAAgC;AAAA,cAC7D,CAAC;AACD,kBAAI,OAAO,WAAW,UAAU;AAC9B,uBAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,OAAO,SAAS,eAAe;AAAA,cACjF;AACA,qBAAO;AAAA,gBACL,IAAI;AAAA,gBACJ,UAAU;AAAA,gBACV,SAAS,oBAAoB,gBAAgB,EAAE,CAAC,QAAQ,SAAS,QAAQ,QAAQ,OAAO,aAAa,OAAO,EAAE;AAAA,cAChH;AAAA,YACF,SAAS,KAAU;AACjB,qBAAO,EAAE,IAAI,OAAO,UAAU,SAAS,SAAS,KAAK,WAAW,OAAO,GAAG,EAAE;AAAA,YAC9E;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBAAiB,QAAiC,KAA0B;AAClF,QAAI,CAAC,KAAK,QAAS;AACnB,UAAM,WAAW,qBAAqB,MAAM;AAC5C,QAAI,SAAS,SAAS;AACpB,UAAI,OAAO;AAAA,QACT,+BAA+B,SAAS,QAAQ,kBAAkB,SAAS,OAAO;AAAA,MACpF;AACA;AAAA,IACF;AACA,QAAI,SAAS,aAAa,OAAO;AAG/B,UAAI,OAAO,aAAa,OAAO;AAC7B,aAAK,QAAQ,aAAa,IAAI,gBAAgB,IAAI,MAAM,GAAG,KAAK;AAChE,YAAI,OAAO,KAAK,8EAA8E;AAAA,MAChG;AACA;AAAA,IACF;AACA,QAAI;AACF,YAAM,YAAY,iBAAiB,EAAE,UAAU,SAAS,UAAU,SAAS,SAAS,SAAS,QAAQ,IAAI,OAAO,CAAC;AACjH,WAAK,QAAQ,aAAa,WAAW,IAAI;AACzC,UAAI,OAAO,KAAK,+DAA+D,SAAS,QAAQ,IAAI;AAAA,IACtG,SAAS,KAAU;AACjB,UAAI,OAAO,KAAK,qDAAqD,KAAK,WAAW,IAAI;AAAA,IAC3F;AAAA,EACF;AACF;","names":[]}
package/dist/index.mjs CHANGED
@@ -258,6 +258,11 @@ function providerFromSettings(values) {
258
258
  var SmsServicePlugin = class {
259
259
  constructor(options = {}) {
260
260
  this.name = "com.objectstack.service.sms";
261
+ /**
262
+ * Services init() registers on every path (ADR-0116, #4131) — lets the
263
+ * kernel name this plugin when a consumer requires one before it inits.
264
+ */
265
+ this.providesServices = ["sms"];
261
266
  this.version = "1.0.0";
262
267
  this.type = "standard";
263
268
  this.options = options;