@chidchanun/bcp 0.2.16 → 0.2.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +150 -408
- package/docs/README.md +36 -38
- package/docs/api-manifest.json +27 -19
- package/docs/api-reference.md +257 -305
- package/docs/deployment-platform-v2.md +449 -0
- package/docs/docs-web-manifest.json +7 -3
- package/docs/observability-v3.md +402 -0
- package/docs/platform-manifest.json +30 -4
- package/docs/releases/0.2.17.md +166 -0
- package/docs/releases/0.2.18.md +136 -0
- package/package.json +11 -6
- package/packages/bundler/src/client-boundary.ts +1 -0
- package/packages/client/src/auth.mjs +1391 -0
- package/packages/client/src/config.mjs +1132 -0
- package/packages/client/src/deployment.mjs +609 -0
- package/packages/client/src/deployment.ts +20 -0
- package/packages/client/src/observability.mjs +1251 -0
- package/packages/client/src/observability.ts +37 -0
- package/packages/client/src/server.mjs +5615 -0
- package/packages/server/src/deployment.ts +936 -0
- package/packages/server/src/middleware.mjs +631 -0
- package/packages/server/src/observability-v3.ts +878 -0
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
// packages/server/src/deployment.ts
|
|
2
|
+
import {
|
|
3
|
+
randomUUID
|
|
4
|
+
} from "node:crypto";
|
|
5
|
+
|
|
6
|
+
// packages/server/src/production-hardening.ts
|
|
7
|
+
var nextShutdownHookId = 1;
|
|
8
|
+
var shutdownHooks = /* @__PURE__ */ new Map();
|
|
9
|
+
function registerShutdownHook(hook, options = {}) {
|
|
10
|
+
if (typeof hook !== "function") {
|
|
11
|
+
throw new TypeError(
|
|
12
|
+
"BCP Framework: shutdown hook must be a function."
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
const id = nextShutdownHookId++;
|
|
16
|
+
const name = normalizeHookName(
|
|
17
|
+
options.name,
|
|
18
|
+
id
|
|
19
|
+
);
|
|
20
|
+
shutdownHooks.set(
|
|
21
|
+
id,
|
|
22
|
+
{
|
|
23
|
+
id,
|
|
24
|
+
name,
|
|
25
|
+
hook
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
return () => {
|
|
29
|
+
shutdownHooks.delete(
|
|
30
|
+
id
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function normalizeHookName(value, id) {
|
|
35
|
+
const normalized = value?.trim();
|
|
36
|
+
if (!normalized) {
|
|
37
|
+
return `hook-${id}`;
|
|
38
|
+
}
|
|
39
|
+
if (normalized.length > 128 || /[\r\n]/.test(
|
|
40
|
+
normalized
|
|
41
|
+
)) {
|
|
42
|
+
throw new TypeError(
|
|
43
|
+
"BCP Framework: shutdown hook name must be at most 128 characters without line breaks."
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return normalized;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// packages/server/src/deployment.ts
|
|
50
|
+
var DEFAULT_SHUTDOWN_TIMEOUT_MS = 1e4;
|
|
51
|
+
var DEFAULT_READINESS_TIMEOUT_MS = 5e3;
|
|
52
|
+
var DEFAULT_SIGNALS = [
|
|
53
|
+
"SIGTERM",
|
|
54
|
+
"SIGINT"
|
|
55
|
+
];
|
|
56
|
+
function createDeploymentRuntime(options) {
|
|
57
|
+
const environment = options.environment ?? process.env;
|
|
58
|
+
const now = options.now ?? Date.now;
|
|
59
|
+
const idFactory = options.idFactory ?? randomUUID;
|
|
60
|
+
const shutdownTimeoutMs = positiveInteger(
|
|
61
|
+
options.shutdownTimeoutMs ?? parseOptionalPositiveInteger(
|
|
62
|
+
environment.BCP_SHUTDOWN_TIMEOUT_MS
|
|
63
|
+
) ?? DEFAULT_SHUTDOWN_TIMEOUT_MS,
|
|
64
|
+
"shutdownTimeoutMs"
|
|
65
|
+
);
|
|
66
|
+
const readinessTimeoutMs = positiveInteger(
|
|
67
|
+
options.readinessTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS,
|
|
68
|
+
"readinessTimeoutMs"
|
|
69
|
+
);
|
|
70
|
+
const startedAtMs = now();
|
|
71
|
+
assertTimestamp(
|
|
72
|
+
startedAtMs,
|
|
73
|
+
"runtime start timestamp"
|
|
74
|
+
);
|
|
75
|
+
const metadata = {
|
|
76
|
+
serviceName: normalizeName(
|
|
77
|
+
options.serviceName,
|
|
78
|
+
"serviceName"
|
|
79
|
+
),
|
|
80
|
+
...normalizeOptionalText(
|
|
81
|
+
options.version
|
|
82
|
+
) ? {
|
|
83
|
+
version: normalizeOptionalText(
|
|
84
|
+
options.version
|
|
85
|
+
)
|
|
86
|
+
} : {},
|
|
87
|
+
deploymentId: normalizeName(
|
|
88
|
+
options.deploymentId ?? environment.BCP_DEPLOYMENT_ID ?? idFactory(),
|
|
89
|
+
"deploymentId"
|
|
90
|
+
),
|
|
91
|
+
...normalizeOptionalText(
|
|
92
|
+
options.instanceId ?? environment.BCP_INSTANCE_ID
|
|
93
|
+
) ? {
|
|
94
|
+
instanceId: normalizeOptionalText(
|
|
95
|
+
options.instanceId ?? environment.BCP_INSTANCE_ID
|
|
96
|
+
)
|
|
97
|
+
} : {},
|
|
98
|
+
...normalizeOptionalText(
|
|
99
|
+
options.release ?? environment.BCP_RELEASE
|
|
100
|
+
) ? {
|
|
101
|
+
release: normalizeOptionalText(
|
|
102
|
+
options.release ?? environment.BCP_RELEASE
|
|
103
|
+
)
|
|
104
|
+
} : {},
|
|
105
|
+
...normalizeOptionalText(
|
|
106
|
+
options.environmentName ?? environment.NODE_ENV
|
|
107
|
+
) ? {
|
|
108
|
+
environment: normalizeOptionalText(
|
|
109
|
+
options.environmentName ?? environment.NODE_ENV
|
|
110
|
+
)
|
|
111
|
+
} : {},
|
|
112
|
+
startedAt: new Date(startedAtMs).toISOString(),
|
|
113
|
+
pid: process.pid,
|
|
114
|
+
nodeVersion: process.version,
|
|
115
|
+
platform: process.platform,
|
|
116
|
+
arch: process.arch
|
|
117
|
+
};
|
|
118
|
+
const abortController = new AbortController();
|
|
119
|
+
const resourceEntries = [];
|
|
120
|
+
const names = /* @__PURE__ */ new Set();
|
|
121
|
+
let state = "idle";
|
|
122
|
+
let startPromise;
|
|
123
|
+
let shutdownPromise;
|
|
124
|
+
const runtime = {
|
|
125
|
+
metadata,
|
|
126
|
+
get state() {
|
|
127
|
+
return state;
|
|
128
|
+
},
|
|
129
|
+
addResource(resource) {
|
|
130
|
+
if (state !== "idle") {
|
|
131
|
+
throw new Error(
|
|
132
|
+
"BCP Deployment: resources can only be registered before runtime start."
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
const name = normalizeName(
|
|
136
|
+
resource.name,
|
|
137
|
+
"resource name"
|
|
138
|
+
);
|
|
139
|
+
if (names.has(name)) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`BCP Deployment: resource "${name}" is already registered.`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
const normalized = {
|
|
145
|
+
...resource,
|
|
146
|
+
name
|
|
147
|
+
};
|
|
148
|
+
const entry = {
|
|
149
|
+
resource: normalized,
|
|
150
|
+
status: {
|
|
151
|
+
name,
|
|
152
|
+
state: "registered"
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
names.add(name);
|
|
156
|
+
resourceEntries.push(entry);
|
|
157
|
+
return () => {
|
|
158
|
+
if (state !== "idle") {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const index = resourceEntries.indexOf(
|
|
162
|
+
entry
|
|
163
|
+
);
|
|
164
|
+
if (index >= 0) {
|
|
165
|
+
resourceEntries.splice(
|
|
166
|
+
index,
|
|
167
|
+
1
|
|
168
|
+
);
|
|
169
|
+
names.delete(name);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
},
|
|
173
|
+
resources() {
|
|
174
|
+
return resourceEntries.map(
|
|
175
|
+
(entry) => ({
|
|
176
|
+
...entry.status
|
|
177
|
+
})
|
|
178
|
+
);
|
|
179
|
+
},
|
|
180
|
+
async start() {
|
|
181
|
+
if (state === "ready") {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (startPromise) {
|
|
185
|
+
return startPromise;
|
|
186
|
+
}
|
|
187
|
+
if (state === "draining" || state === "stopped") {
|
|
188
|
+
throw new Error(
|
|
189
|
+
"BCP Deployment: stopped runtime cannot be started again."
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
if (state === "failed") {
|
|
193
|
+
throw new Error(
|
|
194
|
+
"BCP Deployment: failed runtime cannot be started again."
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
startPromise = startResources();
|
|
198
|
+
return startPromise;
|
|
199
|
+
},
|
|
200
|
+
async readiness() {
|
|
201
|
+
const checkedAtMs = now();
|
|
202
|
+
assertTimestamp(
|
|
203
|
+
checkedAtMs,
|
|
204
|
+
"readiness timestamp"
|
|
205
|
+
);
|
|
206
|
+
const results = [];
|
|
207
|
+
for (const entry of resourceEntries) {
|
|
208
|
+
if (entry.status.state !== "started") {
|
|
209
|
+
results.push({
|
|
210
|
+
name: entry.resource.name,
|
|
211
|
+
ok: false,
|
|
212
|
+
durationMs: 0,
|
|
213
|
+
detail: `Resource state is ${entry.status.state}.`
|
|
214
|
+
});
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (!entry.resource.ready) {
|
|
218
|
+
results.push({
|
|
219
|
+
name: entry.resource.name,
|
|
220
|
+
ok: true,
|
|
221
|
+
durationMs: 0
|
|
222
|
+
});
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const checkStarted = performance.now();
|
|
226
|
+
try {
|
|
227
|
+
const value = await withTimeout(
|
|
228
|
+
Promise.resolve(
|
|
229
|
+
entry.resource.ready(
|
|
230
|
+
createContext()
|
|
231
|
+
)
|
|
232
|
+
),
|
|
233
|
+
readinessTimeoutMs,
|
|
234
|
+
`readiness check for ${entry.resource.name}`
|
|
235
|
+
);
|
|
236
|
+
const normalized = typeof value === "boolean" ? {
|
|
237
|
+
ok: value
|
|
238
|
+
} : value;
|
|
239
|
+
results.push({
|
|
240
|
+
name: entry.resource.name,
|
|
241
|
+
ok: normalized.ok === true,
|
|
242
|
+
durationMs: elapsedMilliseconds(
|
|
243
|
+
checkStarted
|
|
244
|
+
),
|
|
245
|
+
...normalized.detail ? {
|
|
246
|
+
detail: normalized.detail
|
|
247
|
+
} : {}
|
|
248
|
+
});
|
|
249
|
+
} catch (error) {
|
|
250
|
+
results.push({
|
|
251
|
+
name: entry.resource.name,
|
|
252
|
+
ok: false,
|
|
253
|
+
durationMs: elapsedMilliseconds(
|
|
254
|
+
checkStarted
|
|
255
|
+
),
|
|
256
|
+
detail: errorMessage(error)
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
ok: state === "ready" && results.every(
|
|
262
|
+
(item) => item.ok
|
|
263
|
+
),
|
|
264
|
+
state,
|
|
265
|
+
checkedAt: new Date(
|
|
266
|
+
checkedAtMs
|
|
267
|
+
).toISOString(),
|
|
268
|
+
resources: results
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
async diagnostics() {
|
|
272
|
+
const resources = [];
|
|
273
|
+
for (const entry of resourceEntries) {
|
|
274
|
+
let details;
|
|
275
|
+
let error;
|
|
276
|
+
if (entry.resource.diagnostics) {
|
|
277
|
+
try {
|
|
278
|
+
details = await entry.resource.diagnostics(
|
|
279
|
+
createContext()
|
|
280
|
+
);
|
|
281
|
+
} catch (diagnosticError) {
|
|
282
|
+
error = errorMessage(
|
|
283
|
+
diagnosticError
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
resources.push({
|
|
288
|
+
name: entry.resource.name,
|
|
289
|
+
lifecycle: {
|
|
290
|
+
...entry.status
|
|
291
|
+
},
|
|
292
|
+
...details ? {
|
|
293
|
+
details
|
|
294
|
+
} : {},
|
|
295
|
+
...error ? {
|
|
296
|
+
error
|
|
297
|
+
} : {}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
metadata: {
|
|
302
|
+
...metadata
|
|
303
|
+
},
|
|
304
|
+
state,
|
|
305
|
+
uptimeSeconds: Math.max(
|
|
306
|
+
0,
|
|
307
|
+
(now() - startedAtMs) / 1e3
|
|
308
|
+
),
|
|
309
|
+
resources
|
|
310
|
+
};
|
|
311
|
+
},
|
|
312
|
+
async shutdown(_options = {}) {
|
|
313
|
+
if (shutdownPromise) {
|
|
314
|
+
return shutdownPromise;
|
|
315
|
+
}
|
|
316
|
+
if (state === "stopped") {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
shutdownPromise = stopResources();
|
|
320
|
+
return shutdownPromise;
|
|
321
|
+
},
|
|
322
|
+
installSignalHandlers(signalOptions = {}) {
|
|
323
|
+
const signals = normalizeSignals(
|
|
324
|
+
signalOptions.signals ?? DEFAULT_SIGNALS
|
|
325
|
+
);
|
|
326
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
327
|
+
for (const signal of signals) {
|
|
328
|
+
const handler = () => {
|
|
329
|
+
void runtime.shutdown({
|
|
330
|
+
reason: signal
|
|
331
|
+
}).then(
|
|
332
|
+
() => {
|
|
333
|
+
if (signalOptions.setExitCode !== false) {
|
|
334
|
+
process.exitCode = 0;
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
() => {
|
|
338
|
+
process.exitCode = 1;
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
};
|
|
342
|
+
handlers.set(
|
|
343
|
+
signal,
|
|
344
|
+
handler
|
|
345
|
+
);
|
|
346
|
+
process.on(
|
|
347
|
+
signal,
|
|
348
|
+
handler
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
return () => {
|
|
352
|
+
for (const [
|
|
353
|
+
signal,
|
|
354
|
+
handler
|
|
355
|
+
] of handlers) {
|
|
356
|
+
process.off(
|
|
357
|
+
signal,
|
|
358
|
+
handler
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
handlers.clear();
|
|
362
|
+
};
|
|
363
|
+
},
|
|
364
|
+
registerShutdownHook(name = `deployment:${metadata.serviceName}`) {
|
|
365
|
+
return registerShutdownHook(
|
|
366
|
+
() => runtime.shutdown({
|
|
367
|
+
reason: "framework-shutdown"
|
|
368
|
+
}),
|
|
369
|
+
{
|
|
370
|
+
name
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
for (const resource of options.resources ?? []) {
|
|
376
|
+
runtime.addResource(resource);
|
|
377
|
+
}
|
|
378
|
+
return runtime;
|
|
379
|
+
async function startResources() {
|
|
380
|
+
state = "starting";
|
|
381
|
+
try {
|
|
382
|
+
for (const entry of resourceEntries) {
|
|
383
|
+
entry.status = {
|
|
384
|
+
name: entry.resource.name,
|
|
385
|
+
state: "starting"
|
|
386
|
+
};
|
|
387
|
+
try {
|
|
388
|
+
await entry.resource.start?.(
|
|
389
|
+
createContext()
|
|
390
|
+
);
|
|
391
|
+
entry.status = {
|
|
392
|
+
name: entry.resource.name,
|
|
393
|
+
state: "started",
|
|
394
|
+
startedAt: new Date(
|
|
395
|
+
now()
|
|
396
|
+
).toISOString()
|
|
397
|
+
};
|
|
398
|
+
} catch (error) {
|
|
399
|
+
entry.status = {
|
|
400
|
+
name: entry.resource.name,
|
|
401
|
+
state: "failed",
|
|
402
|
+
error: errorMessage(error)
|
|
403
|
+
};
|
|
404
|
+
throw new Error(
|
|
405
|
+
`BCP Deployment: resource "${entry.resource.name}" failed to start. ${errorMessage(error)}`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
state = "ready";
|
|
410
|
+
} catch (error) {
|
|
411
|
+
state = "failed";
|
|
412
|
+
abortController.abort(error);
|
|
413
|
+
await stopStartedResources(
|
|
414
|
+
shutdownTimeoutMs
|
|
415
|
+
);
|
|
416
|
+
throw error;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async function stopResources() {
|
|
420
|
+
state = "draining";
|
|
421
|
+
if (!abortController.signal.aborted) {
|
|
422
|
+
abortController.abort(
|
|
423
|
+
new Error(
|
|
424
|
+
"BCP Deployment: runtime is shutting down."
|
|
425
|
+
)
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
const failures = await stopStartedResources(
|
|
429
|
+
shutdownTimeoutMs
|
|
430
|
+
);
|
|
431
|
+
state = failures.length > 0 ? "failed" : "stopped";
|
|
432
|
+
if (failures.length > 0) {
|
|
433
|
+
throw new AggregateError(
|
|
434
|
+
failures,
|
|
435
|
+
"BCP Deployment: one or more resources failed to stop."
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
async function stopStartedResources(timeoutMs) {
|
|
440
|
+
const failures = [];
|
|
441
|
+
const deadline = Date.now() + timeoutMs;
|
|
442
|
+
for (const entry of [...resourceEntries].reverse()) {
|
|
443
|
+
if (entry.status.state !== "started") {
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
entry.status = {
|
|
447
|
+
...entry.status,
|
|
448
|
+
state: "stopping"
|
|
449
|
+
};
|
|
450
|
+
try {
|
|
451
|
+
const remaining = Math.max(
|
|
452
|
+
1,
|
|
453
|
+
deadline - Date.now()
|
|
454
|
+
);
|
|
455
|
+
await withTimeout(
|
|
456
|
+
Promise.resolve(
|
|
457
|
+
entry.resource.stop?.(
|
|
458
|
+
createContext()
|
|
459
|
+
)
|
|
460
|
+
),
|
|
461
|
+
remaining,
|
|
462
|
+
`shutdown of ${entry.resource.name}`
|
|
463
|
+
);
|
|
464
|
+
entry.status = {
|
|
465
|
+
...entry.status,
|
|
466
|
+
state: "stopped",
|
|
467
|
+
stoppedAt: new Date(
|
|
468
|
+
now()
|
|
469
|
+
).toISOString()
|
|
470
|
+
};
|
|
471
|
+
} catch (error) {
|
|
472
|
+
const message = errorMessage(error);
|
|
473
|
+
entry.status = {
|
|
474
|
+
...entry.status,
|
|
475
|
+
state: "failed",
|
|
476
|
+
error: message
|
|
477
|
+
};
|
|
478
|
+
failures.push(
|
|
479
|
+
new Error(
|
|
480
|
+
`BCP Deployment: resource "${entry.resource.name}" failed to stop. ${message}`
|
|
481
|
+
)
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return failures;
|
|
486
|
+
}
|
|
487
|
+
function createContext() {
|
|
488
|
+
return {
|
|
489
|
+
metadata,
|
|
490
|
+
signal: abortController.signal
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
function createDeploymentReadinessResponse(runtime) {
|
|
495
|
+
return runtime.readiness().then(
|
|
496
|
+
(report) => Response.json(
|
|
497
|
+
report,
|
|
498
|
+
{
|
|
499
|
+
status: report.ok ? 200 : 503,
|
|
500
|
+
headers: {
|
|
501
|
+
"cache-control": "no-store"
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
)
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
async function createDeploymentDiagnosticsResponse(runtime) {
|
|
508
|
+
return Response.json(
|
|
509
|
+
await runtime.diagnostics(),
|
|
510
|
+
{
|
|
511
|
+
status: 200,
|
|
512
|
+
headers: {
|
|
513
|
+
"cache-control": "no-store"
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
function normalizeSignals(signals) {
|
|
519
|
+
return Array.from(
|
|
520
|
+
new Set(signals)
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
function normalizeName(value, field) {
|
|
524
|
+
const normalized = String(value ?? "").trim();
|
|
525
|
+
if (!normalized) {
|
|
526
|
+
throw new TypeError(
|
|
527
|
+
`BCP Deployment: ${field} must be a non-empty string.`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
if (normalized.length > 256 || /[\r\n]/.test(normalized)) {
|
|
531
|
+
throw new TypeError(
|
|
532
|
+
`BCP Deployment: ${field} must be at most 256 characters without line breaks.`
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
return normalized;
|
|
536
|
+
}
|
|
537
|
+
function normalizeOptionalText(value) {
|
|
538
|
+
if (value === void 0) {
|
|
539
|
+
return void 0;
|
|
540
|
+
}
|
|
541
|
+
const normalized = value.trim();
|
|
542
|
+
return normalized || void 0;
|
|
543
|
+
}
|
|
544
|
+
function positiveInteger(value, field) {
|
|
545
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
546
|
+
throw new TypeError(
|
|
547
|
+
`BCP Deployment: ${field} must be a positive safe integer.`
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
return value;
|
|
551
|
+
}
|
|
552
|
+
function parseOptionalPositiveInteger(value) {
|
|
553
|
+
if (value === void 0) {
|
|
554
|
+
return void 0;
|
|
555
|
+
}
|
|
556
|
+
const parsed = Number(value.trim());
|
|
557
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
558
|
+
throw new TypeError(
|
|
559
|
+
"BCP Deployment: BCP_SHUTDOWN_TIMEOUT_MS must be a positive safe integer."
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
return parsed;
|
|
563
|
+
}
|
|
564
|
+
function assertTimestamp(value, field) {
|
|
565
|
+
if (!Number.isFinite(value)) {
|
|
566
|
+
throw new TypeError(
|
|
567
|
+
`BCP Deployment: ${field} must be a finite number.`
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
function errorMessage(error) {
|
|
572
|
+
return error instanceof Error ? error.message : String(error);
|
|
573
|
+
}
|
|
574
|
+
function elapsedMilliseconds(startedAt) {
|
|
575
|
+
return Math.max(
|
|
576
|
+
0,
|
|
577
|
+
performance.now() - startedAt
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
function withTimeout(promise, timeoutMs, label) {
|
|
581
|
+
return new Promise(
|
|
582
|
+
(resolve, reject) => {
|
|
583
|
+
const timer = setTimeout(
|
|
584
|
+
() => reject(
|
|
585
|
+
new Error(
|
|
586
|
+
`BCP Deployment: ${label} timed out after ${timeoutMs}ms.`
|
|
587
|
+
)
|
|
588
|
+
),
|
|
589
|
+
timeoutMs
|
|
590
|
+
);
|
|
591
|
+
timer.unref?.();
|
|
592
|
+
promise.then(
|
|
593
|
+
(value) => {
|
|
594
|
+
clearTimeout(timer);
|
|
595
|
+
resolve(value);
|
|
596
|
+
},
|
|
597
|
+
(error) => {
|
|
598
|
+
clearTimeout(timer);
|
|
599
|
+
reject(error);
|
|
600
|
+
}
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
export {
|
|
606
|
+
createDeploymentDiagnosticsResponse,
|
|
607
|
+
createDeploymentReadinessResponse,
|
|
608
|
+
createDeploymentRuntime
|
|
609
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createDeploymentDiagnosticsResponse,
|
|
3
|
+
createDeploymentReadinessResponse,
|
|
4
|
+
createDeploymentRuntime,
|
|
5
|
+
|
|
6
|
+
type DeploymentDiagnosticsReport,
|
|
7
|
+
type DeploymentDiagnosticsResource,
|
|
8
|
+
type DeploymentMetadata,
|
|
9
|
+
type DeploymentReadinessItem,
|
|
10
|
+
type DeploymentReadinessReport,
|
|
11
|
+
type DeploymentReadinessResult,
|
|
12
|
+
type DeploymentResource,
|
|
13
|
+
type DeploymentResourceContext,
|
|
14
|
+
type DeploymentResourceStatus,
|
|
15
|
+
type DeploymentRuntime,
|
|
16
|
+
type DeploymentRuntimeOptions,
|
|
17
|
+
type DeploymentRuntimeState,
|
|
18
|
+
type DeploymentShutdownOptions,
|
|
19
|
+
type DeploymentSignalOptions,
|
|
20
|
+
} from "../../server/src/deployment.js";
|