@happyvertical/smrt-app-runtime 0.53.2 → 0.53.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/AGENTS.md +40 -0
  2. package/README.md +101 -0
  3. package/dist/application-identity.d.ts +43 -0
  4. package/dist/application-identity.d.ts.map +1 -0
  5. package/dist/chunks/src-DZGOVGsU.js +2258 -0
  6. package/dist/chunks/src-DZGOVGsU.js.map +1 -0
  7. package/dist/deployed-runtime.d.ts +11 -0
  8. package/dist/deployed-runtime.d.ts.map +1 -1
  9. package/dist/error-code.d.ts +3 -0
  10. package/dist/error-code.d.ts.map +1 -0
  11. package/dist/index.d.ts +6 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +2 -1831
  14. package/dist/manifest.json +1 -1
  15. package/dist/operation-lock.d.ts +28 -0
  16. package/dist/operation-lock.d.ts.map +1 -0
  17. package/dist/provider-readiness.d.ts +48 -0
  18. package/dist/provider-readiness.d.ts.map +1 -0
  19. package/dist/smrt-knowledge.json +9 -6
  20. package/dist/stale-reclaim.d.ts +33 -0
  21. package/dist/stale-reclaim.d.ts.map +1 -0
  22. package/dist/state-root.d.ts +42 -0
  23. package/dist/state-root.d.ts.map +1 -0
  24. package/dist/sveltekit/handlers.d.ts +83 -0
  25. package/dist/sveltekit/handlers.d.ts.map +1 -0
  26. package/dist/sveltekit/index.d.ts +20 -0
  27. package/dist/sveltekit/index.d.ts.map +1 -0
  28. package/dist/sveltekit/owner-setup.d.ts +102 -0
  29. package/dist/sveltekit/owner-setup.d.ts.map +1 -0
  30. package/dist/sveltekit/runtime.d.ts +277 -0
  31. package/dist/sveltekit/runtime.d.ts.map +1 -0
  32. package/dist/sveltekit/tenancy.d.ts +59 -0
  33. package/dist/sveltekit/tenancy.d.ts.map +1 -0
  34. package/dist/sveltekit.d.ts +2 -0
  35. package/dist/sveltekit.d.ts.map +1 -0
  36. package/dist/sveltekit.js +704 -0
  37. package/dist/sveltekit.js.map +1 -0
  38. package/dist/writer-lease.d.ts +36 -0
  39. package/dist/writer-lease.d.ts.map +1 -0
  40. package/package.json +22 -8
  41. package/dist/index.js.map +0 -1
@@ -0,0 +1,2258 @@
1
+ import { execFile } from "node:child_process";
2
+ import { createHash, createHmac, randomBytes, randomUUID } from "node:crypto";
3
+ import { constants, link, lstat, mkdir, open, readdir, realpath, rmdir, unlink } from "node:fs/promises";
4
+ import { homedir, platform } from "node:os";
5
+ import { dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
6
+ import { promisify } from "node:util";
7
+ import { Worker } from "node:worker_threads";
8
+ import { resolveApplicationRuntime } from "@happyvertical/smrt-config";
9
+ import { ScheduleRunner, TaskRunner } from "@happyvertical/smrt-jobs";
10
+ import { Person, ProfileTypeCollection } from "@happyvertical/smrt-profiles";
11
+ import { withSystemContext } from "@happyvertical/smrt-tenancy";
12
+ import { DEFAULT_ROLE_SLUGS, MembershipCollection, MembershipStatus, RoleCollection, SessionCollection, SessionService, TenantService, UserCollection, UserStatus } from "@happyvertical/smrt-users";
13
+ import { getDatabase } from "@happyvertical/sql";
14
+ import { chmodSync, closeSync, constants as constants$1, existsSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
15
+ import { AsyncLocalStorage } from "node:async_hooks";
16
+ import { DatabaseSync } from "node:sqlite";
17
+ import { registerHooks } from "node:module";
18
+ import { fileURLToPath, pathToFileURL } from "node:url";
19
+ //#region src/application-identity.ts
20
+ function resolveApplicationId(options = {}) {
21
+ if (options.explicitId) return validateApplicationId(options.explicitId);
22
+ let packageName = options.packageName;
23
+ if (packageName === void 0) {
24
+ const sourceRoot = options.sourceRoot ?? process.cwd();
25
+ packageName = JSON.parse(readFileSync(join(sourceRoot, "package.json"), "utf8")).name;
26
+ }
27
+ if (typeof packageName !== "string" || packageName.trim() === "") throw new Error("package.json must declare a non-empty package name.");
28
+ return encodeApplicationId(packageName);
29
+ }
30
+ function databaseTargetIdentity(value) {
31
+ if (!value) return null;
32
+ try {
33
+ const target = new URL(value);
34
+ const sslMode = target.searchParams.get("sslmode");
35
+ target.username = "";
36
+ target.password = "";
37
+ target.search = "";
38
+ target.hash = "";
39
+ if (sslMode) target.searchParams.set("sslmode", sslMode);
40
+ return target.toString();
41
+ } catch {
42
+ return resolve(value);
43
+ }
44
+ }
45
+ function runtimeConfigurationFingerprint(runtime, environment = process.env) {
46
+ return createHash("sha256").update(JSON.stringify({
47
+ profile: runtime.profile,
48
+ providers: runtime.providers,
49
+ databaseTarget: databaseTargetIdentity(environment.DATABASE_URL),
50
+ host: environment.HOST || null,
51
+ port: environment.PORT || null,
52
+ origin: environment.ORIGIN || null,
53
+ backgroundJobs: environment.SMRT_BACKGROUND_JOBS === "true",
54
+ readinessModules: {
55
+ authentication: environment.SMRT_AUTH_READINESS_MODULE || null,
56
+ assets: environment.SMRT_ASSETS_READINESS_MODULE || null,
57
+ secrets: environment.SMRT_SECRETS_READINESS_MODULE || null
58
+ },
59
+ mcpAuthorization: {
60
+ resource: environment.SMRT_MCP_RESOURCE || null,
61
+ issuer: environment.SMRT_MCP_ISSUER || null,
62
+ jwksUri: environment.SMRT_MCP_JWKS_URI || null,
63
+ scopes: environment.SMRT_MCP_SCOPES || null
64
+ }
65
+ })).digest("hex");
66
+ }
67
+ //#endregion
68
+ //#region src/deployed-runtime.ts
69
+ var DeployedRuntimeError = class extends Error {
70
+ constructor(code, message, component) {
71
+ super(message);
72
+ this.code = code;
73
+ this.component = component;
74
+ this.name = "DeployedRuntimeError";
75
+ }
76
+ code;
77
+ component;
78
+ };
79
+ var DeployedRuntimeCleanupError = class extends DeployedRuntimeError {
80
+ constructor(cleanup) {
81
+ super("provider_unavailable", "The database connection could not be closed after startup failed; retry cleanup and inspect the provider's private logs.", "database");
82
+ this.cleanup = cleanup;
83
+ this.name = "DeployedRuntimeCleanupError";
84
+ }
85
+ cleanup;
86
+ cleaned = false;
87
+ cleanupAttempt;
88
+ async retryCleanup() {
89
+ if (this.cleaned) return;
90
+ if (!this.cleanupAttempt) this.cleanupAttempt = Promise.resolve().then(() => this.cleanup()).then(() => {
91
+ this.cleaned = true;
92
+ }).catch(() => {
93
+ throw this;
94
+ }).finally(() => {
95
+ this.cleanupAttempt = void 0;
96
+ });
97
+ return this.cleanupAttempt;
98
+ }
99
+ };
100
+ function isSessionTenantAuthorized(context) {
101
+ const membership = context.membership;
102
+ const authorization = context.tenantAuthorization;
103
+ const membershipId = authorization?.membershipId;
104
+ const validMembershipId = typeof membershipId === "string" && membershipId.trim().length > 0;
105
+ if (membership?.isActive() === true) return validMembershipId && membership.id === membershipId && authorization?.inheritedFromTenantId === null;
106
+ if (membership === null) return validMembershipId && typeof authorization?.inheritedFromTenantId === "string" && authorization.inheritedFromTenantId.trim().length > 0;
107
+ return false;
108
+ }
109
+ async function initializeDeployedApplicationRuntime(options) {
110
+ const resolvedRuntime = resolveDeployedRuntime(options);
111
+ const bindings = validateBindings(options, resolvedRuntime);
112
+ const prepareDatabase = snapshotPrepareDatabase(options);
113
+ await checkProvider("authentication", () => bindings.authentication.readiness());
114
+ await checkProvider("assets", () => bindings.assets.readiness());
115
+ await checkProvider("secrets", () => bindings.secrets.readiness());
116
+ let db;
117
+ try {
118
+ db = await bindings.database.connect();
119
+ } catch {
120
+ throw unavailable("database");
121
+ }
122
+ let validDatabase = false;
123
+ try {
124
+ validDatabase = isDatabaseInterface(db);
125
+ } catch {}
126
+ if (!validDatabase) await cleanupAfterStartupFailure(bindings.database, db, new DeployedRuntimeError("invalid_configuration", "The database adapter did not return a valid database connection.", "database"));
127
+ try {
128
+ await checkDatabase(bindings.database, db);
129
+ } catch (error) {
130
+ await cleanupAfterStartupFailure(bindings.database, db, error);
131
+ }
132
+ try {
133
+ await prepareDatabase?.(db);
134
+ } catch {
135
+ await cleanupAfterStartupFailure(bindings.database, db, new DeployedRuntimeError("provider_unavailable", "The PostgreSQL migration step failed; inspect the application migration logs.", "database"));
136
+ }
137
+ return new InitializedDeployedApplicationRuntime(db, resolvedRuntime, bindings);
138
+ }
139
+ function resolveDeployedRuntime(options) {
140
+ let plainOptions = false;
141
+ try {
142
+ plainOptions = isPlainRecord(options);
143
+ } catch {}
144
+ if (!plainOptions) throw new DeployedRuntimeError("invalid_configuration", "Deployed runtime options must be an object.");
145
+ let profile;
146
+ let providers;
147
+ try {
148
+ profile = options.profile;
149
+ providers = options.providers;
150
+ } catch {
151
+ throw new DeployedRuntimeError("invalid_configuration", "Deployed runtime options could not be read safely.");
152
+ }
153
+ if (profile !== "self-hosted" && profile !== "cloud") throw new DeployedRuntimeError("invalid_configuration", "Deployed runtime initialization requires the self-hosted or cloud profile.");
154
+ try {
155
+ return resolveApplicationRuntime({
156
+ profile,
157
+ providers
158
+ });
159
+ } catch {
160
+ throw new DeployedRuntimeError("invalid_configuration", "Deployed runtime provider options could not be read safely.");
161
+ }
162
+ }
163
+ function validateBindings(options, runtime) {
164
+ const database = requireObjectBinding(readBindingOption(options, "database", "database"), "database");
165
+ let engine;
166
+ let connect;
167
+ let readiness;
168
+ let close;
169
+ try {
170
+ engine = database.engine;
171
+ connect = database.connect;
172
+ readiness = database.readiness;
173
+ close = database.close;
174
+ } catch {
175
+ throw invalidBinding("database");
176
+ }
177
+ requireFunction(connect, "database", "connect");
178
+ requireOptionalFunction(readiness, "database", "readiness");
179
+ requireFunction(close, "database", "close");
180
+ if (engine !== runtime.providers.database.engine) throw mismatch("database", runtime.providers.database.engine);
181
+ return {
182
+ database: Object.freeze({
183
+ engine,
184
+ connect: bindCallback(connect, database, "database"),
185
+ readiness: typeof readiness === "function" ? bindCallback(readiness, database, "database") : void 0,
186
+ close: bindCallback(close, database, "database")
187
+ }),
188
+ authentication: requireProviderBinding(readBindingOption(options, "authentication", "authentication"), "authentication", runtime.providers.authentication.provider),
189
+ assets: requireProviderBinding(readBindingOption(options, "assets", "assets"), "assets", runtime.providers.assets.provider),
190
+ secrets: requireProviderBinding(readBindingOption(options, "secrets", "secrets"), "secrets", runtime.providers.secrets.provider)
191
+ };
192
+ }
193
+ function snapshotPrepareDatabase(options) {
194
+ let prepareDatabase;
195
+ try {
196
+ prepareDatabase = options.prepareDatabase;
197
+ } catch {
198
+ throw invalidBinding("database");
199
+ }
200
+ requireOptionalFunction(prepareDatabase, "database", "prepareDatabase");
201
+ return typeof prepareDatabase === "function" ? bindCallback(prepareDatabase, options, "database") : void 0;
202
+ }
203
+ function readBindingOption(options, field, component) {
204
+ try {
205
+ return options[field];
206
+ } catch {
207
+ throw invalidBinding(component);
208
+ }
209
+ }
210
+ function requireProviderBinding(value, component, expectedProvider) {
211
+ const binding = requireObjectBinding(value, component);
212
+ let provider;
213
+ let readiness;
214
+ try {
215
+ provider = binding.provider;
216
+ readiness = binding.readiness;
217
+ } catch {
218
+ throw invalidBinding(component);
219
+ }
220
+ if (provider !== expectedProvider) throw mismatch(component, expectedProvider);
221
+ requireFunction(readiness, component, "readiness");
222
+ return Object.freeze({
223
+ provider,
224
+ readiness: bindCallback(readiness, binding, component)
225
+ });
226
+ }
227
+ function requireObjectBinding(value, component) {
228
+ let valid = false;
229
+ try {
230
+ valid = typeof value === "object" && value !== null && !Array.isArray(value);
231
+ } catch {}
232
+ if (!valid) throw new DeployedRuntimeError("invalid_configuration", `The ${component} provider binding is required.`, component);
233
+ return value;
234
+ }
235
+ function invalidBinding(component) {
236
+ return new DeployedRuntimeError("invalid_configuration", `The ${component} provider binding could not be read safely.`, component);
237
+ }
238
+ function bindCallback(callback, receiver, component) {
239
+ try {
240
+ return Reflect.apply(Function.prototype.bind, callback, [receiver]);
241
+ } catch {
242
+ throw invalidBinding(component);
243
+ }
244
+ }
245
+ function requireFunction(value, component, field) {
246
+ if (typeof value !== "function") throw new DeployedRuntimeError("invalid_configuration", `The ${component} provider requires a ${field} callback.`, component);
247
+ }
248
+ function requireOptionalFunction(value, component, field) {
249
+ if (value !== void 0) requireFunction(value, component, field);
250
+ }
251
+ function mismatch(component, expectedProvider) {
252
+ return new DeployedRuntimeError("provider_mismatch", `The ${component} binding does not match the selected ${expectedProvider} provider.`, component);
253
+ }
254
+ function unavailable(component) {
255
+ return new DeployedRuntimeError("provider_unavailable", `The ${component} provider is not ready; inspect that provider's private logs.`, component);
256
+ }
257
+ async function checkProvider(component, check) {
258
+ try {
259
+ await check();
260
+ } catch {
261
+ throw unavailable(component);
262
+ }
263
+ }
264
+ async function checkDatabase(binding, db) {
265
+ try {
266
+ await binding.readiness?.(db);
267
+ if (!isValidPostgresProbe(await db.query("SELECT current_setting('server_version_num') AS smrt_postgres_version"))) throw new Error("invalid probe");
268
+ } catch {
269
+ throw unavailable("database");
270
+ }
271
+ }
272
+ var InitializedDeployedApplicationRuntime = class {
273
+ constructor(db, resolvedRuntime, bindings) {
274
+ this.db = db;
275
+ this.resolvedRuntime = resolvedRuntime;
276
+ this.bindings = bindings;
277
+ this.snapshot = createDiagnostics(resolvedRuntime);
278
+ this.sessionService = new SessionService({ db });
279
+ }
280
+ db;
281
+ resolvedRuntime;
282
+ bindings;
283
+ state = "running";
284
+ closeAttempt;
285
+ pendingReadinessChecks = /* @__PURE__ */ new Set();
286
+ pendingSessionRestorations = /* @__PURE__ */ new Set();
287
+ pendingWorkerInitializations = /* @__PURE__ */ new Set();
288
+ pendingWorkerOperations = /* @__PURE__ */ new Set();
289
+ lifecycleOperationContext = new AsyncLocalStorage();
290
+ activeLifecycleOperations = /* @__PURE__ */ new Set();
291
+ workerCleanups = /* @__PURE__ */ new Set();
292
+ sessionService;
293
+ sessionServiceReady;
294
+ snapshot;
295
+ async restoreSession(sessionId) {
296
+ this.assertRunning();
297
+ const operation = this.beginLifecycleOperation(() => this.restoreSessionWhileRunning(sessionId));
298
+ this.pendingSessionRestorations.add(operation);
299
+ try {
300
+ return await operation;
301
+ } finally {
302
+ this.pendingSessionRestorations.delete(operation);
303
+ }
304
+ }
305
+ async restoreSessionWhileRunning(sessionId) {
306
+ let context;
307
+ try {
308
+ await this.initializeSessionService();
309
+ context = await this.sessionService.loadSessionContext(sessionId);
310
+ } catch {
311
+ throw unavailable("authentication");
312
+ }
313
+ this.assertRunning();
314
+ if (context && this.resolvedRuntime.providers.tenancy.context === "required") {
315
+ let tenantId;
316
+ try {
317
+ tenantId = context.tenantId;
318
+ } catch {
319
+ throw unavailable("authentication");
320
+ }
321
+ if (typeof tenantId !== "string" || tenantId.trim().length === 0) throw new DeployedRuntimeError("tenant_context_required", "The authenticated session does not include the required tenant context.", "authentication");
322
+ let tenantAuthorized = false;
323
+ try {
324
+ tenantAuthorized = isSessionTenantAuthorized(context);
325
+ } catch {
326
+ throw unavailable("authentication");
327
+ }
328
+ if (!tenantAuthorized) throw new DeployedRuntimeError("tenant_context_unauthorized", "The authenticated session is not authorized for its tenant context.", "authentication");
329
+ }
330
+ return context;
331
+ }
332
+ async createTaskWorker(config = {}) {
333
+ this.assertRunning();
334
+ const operation = this.beginLifecycleOperation(() => this.initializeTaskWorker(config));
335
+ this.pendingWorkerInitializations.add(operation);
336
+ try {
337
+ const runner = await operation;
338
+ return this.retainWorker(runner);
339
+ } finally {
340
+ this.pendingWorkerInitializations.delete(operation);
341
+ }
342
+ }
343
+ async createScheduleWorker(config = {}) {
344
+ this.assertRunning();
345
+ const operation = this.beginLifecycleOperation(() => this.initializeScheduleWorker(config));
346
+ this.pendingWorkerInitializations.add(operation);
347
+ try {
348
+ const runner = await operation;
349
+ return this.retainWorker(runner);
350
+ } finally {
351
+ this.pendingWorkerInitializations.delete(operation);
352
+ }
353
+ }
354
+ diagnostics() {
355
+ return this.snapshot;
356
+ }
357
+ health() {
358
+ return Object.freeze({
359
+ schemaVersion: 1,
360
+ profile: this.resolvedRuntime.profile,
361
+ status: this.state === "running" ? "healthy" : "stopped"
362
+ });
363
+ }
364
+ async readiness() {
365
+ if (this.state !== "running") return createStoppedReadiness(this.resolvedRuntime.profile);
366
+ const operation = this.beginLifecycleOperation(() => this.checkReadinessWhileRunning());
367
+ this.pendingReadinessChecks.add(operation);
368
+ try {
369
+ return await operation;
370
+ } finally {
371
+ this.pendingReadinessChecks.delete(operation);
372
+ }
373
+ }
374
+ async checkReadinessWhileRunning() {
375
+ const results = await Promise.all([
376
+ probe(() => checkDatabase(this.bindings.database, this.db)),
377
+ probe(() => this.bindings.authentication.readiness()),
378
+ probe(() => this.bindings.assets.readiness()),
379
+ probe(() => this.bindings.secrets.readiness())
380
+ ]);
381
+ if (this.state !== "running") return createStoppedReadiness(this.resolvedRuntime.profile);
382
+ const [database, authentication, assets, secrets] = results;
383
+ return freezeReadiness(this.resolvedRuntime.profile, {
384
+ database,
385
+ authentication,
386
+ assets,
387
+ secrets
388
+ }, results.every((result) => result.status === "ready"));
389
+ }
390
+ async close() {
391
+ const operationToken = this.lifecycleOperationContext.getStore();
392
+ const requestedFromLifecycleOperation = operationToken !== void 0 && this.activeLifecycleOperations.has(operationToken);
393
+ if (this.state === "stopped") return;
394
+ if (this.closeAttempt) {
395
+ if (requestedFromLifecycleOperation) return;
396
+ return this.closeAttempt;
397
+ }
398
+ this.state = "closing";
399
+ const pendingOperations = [
400
+ ...this.pendingReadinessChecks,
401
+ ...this.pendingSessionRestorations,
402
+ ...this.pendingWorkerInitializations,
403
+ ...this.pendingWorkerOperations
404
+ ];
405
+ const shutdownToken = /* @__PURE__ */ Symbol("deployed-runtime-shutdown");
406
+ this.activeLifecycleOperations.add(shutdownToken);
407
+ const attempt = Promise.resolve().then(() => this.lifecycleOperationContext.run(shutdownToken, async () => {
408
+ await Promise.allSettled(pendingOperations);
409
+ await this.cleanupWorkers();
410
+ await this.closeConnection();
411
+ })).finally(() => {
412
+ this.activeLifecycleOperations.delete(shutdownToken);
413
+ });
414
+ this.closeAttempt = attempt.finally(() => {
415
+ this.closeAttempt = void 0;
416
+ });
417
+ if (requestedFromLifecycleOperation) {
418
+ this.closeAttempt.catch(() => void 0);
419
+ return;
420
+ }
421
+ return this.closeAttempt;
422
+ }
423
+ async initializeTaskWorker(config) {
424
+ let runner;
425
+ try {
426
+ runner = new TaskRunner(config);
427
+ await runner.initialize(this.db);
428
+ } catch {
429
+ if (runner) await this.stopOrRetainUnreturnedWorker(runner);
430
+ throw unavailable("database");
431
+ }
432
+ if (this.state !== "running") {
433
+ await this.stopOrRetainUnreturnedWorker(runner);
434
+ this.assertRunning();
435
+ }
436
+ return runner;
437
+ }
438
+ async initializeScheduleWorker(config) {
439
+ let runner;
440
+ try {
441
+ runner = new ScheduleRunner(config);
442
+ await runner.initialize(this.db);
443
+ } catch {
444
+ if (runner) await this.stopOrRetainUnreturnedWorker(runner);
445
+ throw unavailable("database");
446
+ }
447
+ if (this.state !== "running") {
448
+ await this.stopOrRetainUnreturnedWorker(runner);
449
+ this.assertRunning();
450
+ }
451
+ return runner;
452
+ }
453
+ async stopOrRetainUnreturnedWorker(runner) {
454
+ const cleanup = () => runner.stop();
455
+ try {
456
+ await cleanup();
457
+ } catch {
458
+ this.workerCleanups.add(cleanup);
459
+ }
460
+ }
461
+ retainWorker(runner) {
462
+ const start = runner.start.bind(runner);
463
+ const stop = runner.stop.bind(runner);
464
+ let operationTail = Promise.resolve();
465
+ const queueOperation = (callback) => {
466
+ const previous = operationTail;
467
+ const operation = this.beginLifecycleOperation(async () => {
468
+ await previous;
469
+ await callback();
470
+ });
471
+ operationTail = operation.catch(() => void 0);
472
+ this.pendingWorkerOperations.add(operation);
473
+ operation.then(() => {
474
+ this.pendingWorkerOperations.delete(operation);
475
+ }, () => {
476
+ this.pendingWorkerOperations.delete(operation);
477
+ });
478
+ return operation;
479
+ };
480
+ const ownedStart = async () => {
481
+ this.assertRunning();
482
+ return queueOperation(async () => {
483
+ this.assertRunning();
484
+ try {
485
+ await start();
486
+ this.assertRunning();
487
+ } catch {
488
+ if (this.state === "running") try {
489
+ await stop();
490
+ } catch {}
491
+ if (this.state !== "running") this.assertRunning();
492
+ throw unavailable("database");
493
+ }
494
+ });
495
+ };
496
+ const ownedStop = () => queueOperation(async () => {
497
+ try {
498
+ await stop();
499
+ } catch {
500
+ throw unavailable("database");
501
+ }
502
+ });
503
+ Object.defineProperties(runner, {
504
+ start: {
505
+ configurable: false,
506
+ enumerable: false,
507
+ value: ownedStart,
508
+ writable: false
509
+ },
510
+ stop: {
511
+ configurable: false,
512
+ enumerable: false,
513
+ value: ownedStop,
514
+ writable: false
515
+ }
516
+ });
517
+ this.workerCleanups.add(ownedStop);
518
+ return runner;
519
+ }
520
+ async cleanupWorkers() {
521
+ const cleanups = [...this.workerCleanups];
522
+ const results = await Promise.allSettled(cleanups.map((cleanup) => Promise.resolve().then(cleanup)));
523
+ let cleanupFailed = false;
524
+ for (const [index, result] of results.entries()) if (result.status === "fulfilled") {
525
+ const cleanup = cleanups[index];
526
+ if (cleanup) this.workerCleanups.delete(cleanup);
527
+ } else cleanupFailed = true;
528
+ if (cleanupFailed) {
529
+ this.state = "close-failed";
530
+ throw unavailable("database");
531
+ }
532
+ }
533
+ initializeSessionService() {
534
+ if (!this.sessionServiceReady) this.sessionServiceReady = this.sessionService.initialize().catch((error) => {
535
+ this.sessionServiceReady = void 0;
536
+ throw error;
537
+ });
538
+ return this.sessionServiceReady;
539
+ }
540
+ beginLifecycleOperation(operation) {
541
+ const token = /* @__PURE__ */ Symbol("deployed-runtime-operation");
542
+ this.activeLifecycleOperations.add(token);
543
+ return Promise.resolve().then(() => this.lifecycleOperationContext.run(token, operation)).finally(() => {
544
+ this.activeLifecycleOperations.delete(token);
545
+ });
546
+ }
547
+ async closeConnection() {
548
+ try {
549
+ await this.bindings.database.close(this.db);
550
+ this.state = "stopped";
551
+ } catch {
552
+ this.state = "close-failed";
553
+ throw new DeployedRuntimeError("provider_unavailable", "The database connection could not be closed; inspect the provider's private logs.", "database");
554
+ }
555
+ }
556
+ assertRunning() {
557
+ if (this.state !== "running") throw new DeployedRuntimeError("runtime_stopped", "The deployed application runtime has already stopped.");
558
+ }
559
+ };
560
+ function createDiagnostics(runtime) {
561
+ const provider = (value) => Object.freeze({
562
+ provider: value,
563
+ configured: true
564
+ });
565
+ return deepFreeze$1({
566
+ schemaVersion: 1,
567
+ runtime,
568
+ providers: {
569
+ database: provider(runtime.providers.database.engine),
570
+ authentication: provider(runtime.providers.authentication.provider),
571
+ assets: provider(runtime.providers.assets.provider),
572
+ secrets: provider(runtime.providers.secrets.provider)
573
+ },
574
+ tenancy: {
575
+ mode: runtime.providers.tenancy.mode,
576
+ context: runtime.providers.tenancy.context,
577
+ isolation: runtime.providers.tenancy.isolation,
578
+ rootTenantFallback: "disabled"
579
+ },
580
+ workers: {
581
+ topology: runtime.providers.jobs.topology,
582
+ taskProcess: "separate",
583
+ scheduleProcess: "separate",
584
+ replicas: runtime.providers.jobs.topology === "scalable" ? "horizontal" : "operator-managed"
585
+ },
586
+ secretValuesIncluded: false
587
+ });
588
+ }
589
+ async function probe(check) {
590
+ try {
591
+ await check();
592
+ return Object.freeze({ status: "ready" });
593
+ } catch {
594
+ return Object.freeze({ status: "not-ready" });
595
+ }
596
+ }
597
+ function createStoppedReadiness(profile) {
598
+ const stopped = Object.freeze({ status: "not-ready" });
599
+ return freezeReadiness(profile, {
600
+ database: stopped,
601
+ authentication: stopped,
602
+ assets: stopped,
603
+ secrets: stopped
604
+ }, false);
605
+ }
606
+ function freezeReadiness(profile, components, ready) {
607
+ return deepFreeze$1({
608
+ schemaVersion: 1,
609
+ profile,
610
+ status: ready ? "ready" : "not-ready",
611
+ components,
612
+ secretValuesIncluded: false
613
+ });
614
+ }
615
+ function isPlainRecord(value) {
616
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
617
+ const prototype = Object.getPrototypeOf(value);
618
+ return prototype === Object.prototype || prototype === null;
619
+ }
620
+ function isDatabaseInterface(value) {
621
+ return typeof value === "object" && value !== null && typeof value.query === "function";
622
+ }
623
+ function isValidPostgresProbe(value) {
624
+ if (typeof value !== "object" || value === null) return false;
625
+ const rows = value.rows;
626
+ if (!Array.isArray(rows) || rows.length !== 1) return false;
627
+ const row = rows[0];
628
+ if (typeof row !== "object" || row === null) return false;
629
+ const version = row.smrt_postgres_version;
630
+ return typeof version === "string" && /^\d+$/.test(version);
631
+ }
632
+ async function cleanupAfterStartupFailure(binding, db, startupError) {
633
+ const cleanup = () => binding.close(db);
634
+ try {
635
+ await cleanup();
636
+ } catch {
637
+ throw new DeployedRuntimeCleanupError(cleanup);
638
+ }
639
+ throw startupError;
640
+ }
641
+ function deepFreeze$1(value, seen = /* @__PURE__ */ new WeakSet()) {
642
+ if (value && typeof value === "object") {
643
+ if (seen.has(value)) return value;
644
+ seen.add(value);
645
+ if (!Object.isFrozen(value)) Object.freeze(value);
646
+ for (const child of Object.values(value)) deepFreeze$1(child, seen);
647
+ }
648
+ return value;
649
+ }
650
+ //#endregion
651
+ //#region src/error-code.ts
652
+ function errorCode(error) {
653
+ return error && typeof error === "object" && "code" in error && typeof error.code === "string" ? error.code : void 0;
654
+ }
655
+ //#endregion
656
+ //#region src/stale-reclaim.ts
657
+ var RECLAIM_MUTEX_FILE = ".smrt-lock-reclaim.sqlite";
658
+ var RECLAIM_WAIT_MS = 2e3;
659
+ function withReclaimMutex(stateRoot, callback) {
660
+ const mutexPath = join(stateRoot, RECLAIM_MUTEX_FILE);
661
+ try {
662
+ const details = lstatSync(mutexPath);
663
+ if (details.isSymbolicLink() || !details.isFile()) throw new Error("The application lock reclaim mutex is unsafe.");
664
+ } catch (error) {
665
+ if (errorCode(error) !== "ENOENT") throw error;
666
+ }
667
+ const database = new DatabaseSync(mutexPath, { timeout: RECLAIM_WAIT_MS });
668
+ try {
669
+ try {
670
+ chmodSync(mutexPath, 384);
671
+ } catch {}
672
+ try {
673
+ database.exec("BEGIN EXCLUSIVE");
674
+ } catch {
675
+ throw new Error("Another process is reclaiming a stale application lock; retry the operation.");
676
+ }
677
+ try {
678
+ return callback();
679
+ } finally {
680
+ database.exec("ROLLBACK");
681
+ }
682
+ } finally {
683
+ database.close();
684
+ }
685
+ }
686
+ function reclaimStaleRecord(path, observed, isStale) {
687
+ return withReclaimMutex(dirname(path), () => {
688
+ let current;
689
+ try {
690
+ current = readFileSync(path, "utf8");
691
+ } catch (error) {
692
+ if (errorCode(error) === "ENOENT") return "gone";
693
+ throw error;
694
+ }
695
+ if (current !== observed || !isStale(current)) return "changed";
696
+ rmSync(path);
697
+ return "removed";
698
+ });
699
+ }
700
+ //#endregion
701
+ //#region src/operation-lock.ts
702
+ function processExists(pid) {
703
+ try {
704
+ process.kill(pid, 0);
705
+ return true;
706
+ } catch (error) {
707
+ const code = errorCode(error);
708
+ return code === "EPERM" || code === "EACCES";
709
+ }
710
+ }
711
+ function lockOwnerPid(contents) {
712
+ try {
713
+ const pid = JSON.parse(contents)?.pid;
714
+ return Number.isSafeInteger(pid) && pid >= 1 ? pid : null;
715
+ } catch {
716
+ return null;
717
+ }
718
+ }
719
+ function isStaleOperationLock(contents) {
720
+ const pid = lockOwnerPid(contents);
721
+ return pid !== null && !processExists(pid);
722
+ }
723
+ async function withOperationLock(stateRoot, operation, callback) {
724
+ mkdirSync(stateRoot, {
725
+ recursive: true,
726
+ mode: 448
727
+ });
728
+ const path = join(stateRoot, "operation.lock");
729
+ const instance = randomBytes(16).toString("hex");
730
+ let descriptor;
731
+ for (let attempt = 0; attempt < 2; attempt += 1) try {
732
+ descriptor = openSync(path, "wx", 384);
733
+ try {
734
+ writeFileSync(descriptor, `${JSON.stringify({
735
+ schemaVersion: 1,
736
+ pid: process.pid,
737
+ operation,
738
+ instance
739
+ })}
740
+ `);
741
+ } catch (error) {
742
+ closeSync(descriptor);
743
+ descriptor = void 0;
744
+ rmSync(path, { force: true });
745
+ throw error;
746
+ }
747
+ break;
748
+ } catch (error) {
749
+ if (errorCode(error) !== "EEXIST") throw error;
750
+ let observed = null;
751
+ try {
752
+ observed = readFileSync(path, "utf8");
753
+ } catch {}
754
+ const ownerPid = observed === null ? null : lockOwnerPid(observed);
755
+ if (ownerPid === null) throw new Error("An application operation lock exists but cannot be verified; inspect the private state directory.");
756
+ if (processExists(ownerPid)) throw new Error(`Another application operation is active (process ${ownerPid}).`);
757
+ reclaimStaleRecord(path, observed, isStaleOperationLock);
758
+ }
759
+ if (descriptor === void 0) throw new Error("The application operation lock could not be acquired.");
760
+ try {
761
+ return await callback({
762
+ instance,
763
+ path
764
+ });
765
+ } finally {
766
+ closeSync(descriptor);
767
+ try {
768
+ const current = JSON.parse(readFileSync(path, "utf8"));
769
+ if (current.pid === process.pid && current.instance === instance) rmSync(path, { force: true });
770
+ } catch {}
771
+ }
772
+ }
773
+ //#endregion
774
+ //#region src/provider-readiness.ts
775
+ var PROVIDER_READINESS_SETTINGS = Object.freeze({
776
+ authentication: "SMRT_AUTH_READINESS_MODULE",
777
+ assets: "SMRT_ASSETS_READINESS_MODULE",
778
+ secrets: "SMRT_SECRETS_READINESS_MODULE"
779
+ });
780
+ function findInstalledPackage(sourceRoot, packageName) {
781
+ let directory = resolve(sourceRoot);
782
+ for (;;) {
783
+ const candidate = join(directory, "node_modules", packageName);
784
+ if (existsSync(join(candidate, "package.json"))) return candidate;
785
+ const parent = dirname(directory);
786
+ if (parent === directory) return null;
787
+ directory = parent;
788
+ }
789
+ }
790
+ function packageNameOf(specifier) {
791
+ const parts = specifier.split("/");
792
+ return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
793
+ }
794
+ var RESOLVER_MODULE = "data:text/javascript,export default (s) => import.meta.resolve(s)";
795
+ async function resolveFromApplication(specifier, sourceRoot) {
796
+ const parentURL = pathToFileURL(join(resolve(sourceRoot), "package.json")).href;
797
+ const token = `smrt-app-readiness-${randomUUID()}`;
798
+ const hooks = registerHooks({ resolve(requested, context, nextResolve) {
799
+ return requested === token ? nextResolve(specifier, {
800
+ ...context,
801
+ parentURL
802
+ }) : nextResolve(requested, context);
803
+ } });
804
+ try {
805
+ return (await import(
806
+ /* @vite-ignore */
807
+ RESOLVER_MODULE
808
+ )).default(token);
809
+ } finally {
810
+ hooks.deregister();
811
+ }
812
+ }
813
+ async function resolveReadinessModule(specifier, sourceRoot) {
814
+ if (/^[a-z][a-z0-9+.-]*:/i.test(specifier) && !isAbsolute(specifier)) return specifier;
815
+ if (isAbsolute(specifier)) return pathToFileURL(specifier).href;
816
+ if (specifier.startsWith("./") || specifier.startsWith("../")) return pathToFileURL(resolve(sourceRoot, specifier)).href;
817
+ const installed = findInstalledPackage(sourceRoot, packageNameOf(specifier));
818
+ if (!installed) throw new Error(`${specifier} is not installed in the application.`);
819
+ const packageRoot = realpathSync(installed);
820
+ const resolvedPath = realpathSync(fileURLToPath(await resolveFromApplication(specifier, sourceRoot)));
821
+ if (resolvedPath !== packageRoot && !resolvedPath.startsWith(`${packageRoot}${sep}`)) throw new Error(`${specifier} resolved outside its installed package; refusing to load it.`);
822
+ return pathToFileURL(resolvedPath).href;
823
+ }
824
+ function createProviderReadinessProbe(component, context, options = {}) {
825
+ return async () => {
826
+ const setting = PROVIDER_READINESS_SETTINGS[component];
827
+ const specifier = (options.environment ?? process.env)[setting];
828
+ if (!specifier) throw new Error(`${setting} must name an installed provider readiness module.`);
829
+ const module = await import(
830
+ /* @vite-ignore */
831
+ await resolveReadinessModule(specifier, options.sourceRoot ?? process.cwd())
832
+ );
833
+ const probe = module.checkReadiness || module.default;
834
+ if (typeof probe !== "function") throw new Error(`${setting} does not export a readiness probe.`);
835
+ const result = await probe({
836
+ component,
837
+ ...context
838
+ });
839
+ if (result !== true && (typeof result !== "object" || result === null || result.ready !== true)) throw new Error(`${component} provider readiness check failed.`);
840
+ };
841
+ }
842
+ //#endregion
843
+ //#region src/runtime-diagnostics.ts
844
+ var RUNTIME_DIAGNOSTICS_SCHEMA_VERSION = 1;
845
+ var MAX_RUNTIME_DIAGNOSTIC_TOOLS = 64;
846
+ var MAX_RUNTIME_DIAGNOSTIC_ERRORS = 8;
847
+ var RUNTIME_WORKER_FRESHNESS_SECONDS = 120;
848
+ var CAPABILITY_IDS = [
849
+ "asset-storage",
850
+ "authentication",
851
+ "background-jobs",
852
+ "database",
853
+ "paid-capabilities",
854
+ "secret-storage"
855
+ ];
856
+ var ERROR_CODES = [
857
+ "authentication_unavailable",
858
+ "database_unavailable",
859
+ "migration_required",
860
+ "provider_unavailable",
861
+ "runtime_stopped",
862
+ "worker_stale"
863
+ ];
864
+ var PROHIBITED_IDENTIFIER_PARTS = /* @__PURE__ */ new Set([
865
+ "authorization",
866
+ "cookie",
867
+ "constructor",
868
+ "credential",
869
+ "email",
870
+ "hash",
871
+ "key",
872
+ "log",
873
+ "logs",
874
+ "password",
875
+ "path",
876
+ "record",
877
+ "prototype",
878
+ "secret",
879
+ "session",
880
+ "stack",
881
+ "tenant",
882
+ "token",
883
+ "url",
884
+ "user"
885
+ ]);
886
+ function projectRuntimeDiagnostics(input) {
887
+ const source = plainRecord(input);
888
+ const observedAtValue = read(source, "observedAt");
889
+ const observedAtMilliseconds = timestampMilliseconds(observedAtValue);
890
+ const observedAt = coarseTimestamp(observedAtValue) ?? EPOCH;
891
+ const profile = runtimeProfile(read(source, "profile"));
892
+ const worker = projectWorker(read(source, "worker"), observedAt, observedAtMilliseconds);
893
+ const capabilities = projectCapabilities(read(source, "capabilities"));
894
+ const tools = canonicalTools(read(source, "toolNames"));
895
+ return deepFreeze({
896
+ schemaVersion: 1,
897
+ profile,
898
+ health: oneOf(read(source, "health"), HEALTH_VALUES, "unknown"),
899
+ schema: projectSchema(read(source, "schema")),
900
+ capabilities,
901
+ tools: {
902
+ names: tools,
903
+ count: tools.length,
904
+ digest: createHash("sha256").update(tools.join("\n")).digest("hex")
905
+ },
906
+ operationalDifferences: {
907
+ backgroundJobs: capabilityStatus(capabilities, "background-jobs") === "available" ? "enabled" : "disabled",
908
+ workerTopology: worker.topology
909
+ },
910
+ worker,
911
+ recentErrors: projectErrors(read(source, "recentErrors"))
912
+ });
913
+ }
914
+ var EPOCH = "1970-01-01T00:00:00.000Z";
915
+ var PROFILES = [
916
+ "local",
917
+ "self-hosted",
918
+ "cloud",
919
+ "unknown"
920
+ ];
921
+ var HEALTH_VALUES = [
922
+ "healthy",
923
+ "degraded",
924
+ "stopped",
925
+ "unknown"
926
+ ];
927
+ var SCHEMA_VALUES = [
928
+ "ready",
929
+ "not-ready",
930
+ "unknown"
931
+ ];
932
+ var MIGRATION_VALUES = [
933
+ "current",
934
+ "failed",
935
+ "pending",
936
+ "unknown"
937
+ ];
938
+ var CAPABILITY_VALUES = [
939
+ "available",
940
+ "disabled",
941
+ "unavailable",
942
+ "unknown"
943
+ ];
944
+ var WORKER_TOPOLOGIES = [
945
+ "embedded",
946
+ "external",
947
+ "inline",
948
+ "scalable"
949
+ ];
950
+ function runtimeProfile(value) {
951
+ return oneOf(value, PROFILES, "unknown");
952
+ }
953
+ function projectSchema(value) {
954
+ const source = plainRecord(value);
955
+ return {
956
+ status: oneOf(read(source, "status"), SCHEMA_VALUES, "unknown"),
957
+ migrations: oneOf(read(source, "migrations"), MIGRATION_VALUES, "unknown")
958
+ };
959
+ }
960
+ function projectCapabilities(value) {
961
+ const source = plainRecord(value);
962
+ return CAPABILITY_IDS.map((id) => ({
963
+ id,
964
+ status: oneOf(read(source, id), CAPABILITY_VALUES, "unknown")
965
+ }));
966
+ }
967
+ function canonicalTools(value) {
968
+ const names = /* @__PURE__ */ new Set();
969
+ for (const candidate of safeArrayItems(value, 256)) {
970
+ if (typeof candidate !== "string" || !isPublicIdentifier(candidate)) continue;
971
+ names.add(candidate);
972
+ }
973
+ return [...names].sort().slice(0, 64);
974
+ }
975
+ function isPublicIdentifier(value) {
976
+ if (value.length < 1 || value.length > 80 || !/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/.test(value)) return false;
977
+ return !value.split(/[._-]/).some((part) => PROHIBITED_IDENTIFIER_PARTS.has(part));
978
+ }
979
+ function projectWorker(value, observedAt, observedAtMilliseconds) {
980
+ const source = plainRecord(value);
981
+ const topology = oneOf(read(source, "topology"), WORKER_TOPOLOGIES, "inline");
982
+ const required = read(source, "required") === true;
983
+ const heartbeatValue = read(source, "heartbeatAt");
984
+ const heartbeatMilliseconds = timestampMilliseconds(heartbeatValue);
985
+ const heartbeatAt = coarseTimestamp(heartbeatValue);
986
+ let liveness = "not-required";
987
+ if (required) {
988
+ liveness = "unknown";
989
+ if (heartbeatMilliseconds !== null && observedAtMilliseconds !== null) {
990
+ const ageSeconds = (observedAtMilliseconds - heartbeatMilliseconds) / 1e3;
991
+ if (ageSeconds >= 0) liveness = ageSeconds <= 120 ? "alive" : "stale";
992
+ }
993
+ }
994
+ return {
995
+ topology,
996
+ liveness,
997
+ heartbeatAt,
998
+ observedAt
999
+ };
1000
+ }
1001
+ function projectErrors(value) {
1002
+ const errors = [];
1003
+ for (const candidate of safeArrayItems(value, 32)) {
1004
+ const source = plainRecord(candidate);
1005
+ const at = coarseTimestamp(read(source, "at"));
1006
+ if (!at) continue;
1007
+ const rawCode = read(source, "code");
1008
+ const code = ERROR_CODES.includes(rawCode) ? rawCode : "runtime_error";
1009
+ errors.push({
1010
+ code,
1011
+ at
1012
+ });
1013
+ }
1014
+ return errors.sort((left, right) => left.at === right.at ? left.code.localeCompare(right.code) : right.at.localeCompare(left.at)).slice(0, 8);
1015
+ }
1016
+ function capabilityStatus(capabilities, id) {
1017
+ return capabilities.find((capability) => capability.id === id)?.status ?? "unknown";
1018
+ }
1019
+ function coarseTimestamp(value) {
1020
+ const milliseconds = timestampMilliseconds(value);
1021
+ if (milliseconds === null) return null;
1022
+ const minute = Math.floor(milliseconds / 6e4) * 6e4;
1023
+ return new Date(minute).toISOString();
1024
+ }
1025
+ function timestampMilliseconds(value) {
1026
+ try {
1027
+ if (!(typeof value === "string" || value instanceof Date)) return null;
1028
+ const milliseconds = value instanceof Date ? value.getTime() : Date.parse(value);
1029
+ return Number.isFinite(milliseconds) ? milliseconds : null;
1030
+ } catch {
1031
+ return null;
1032
+ }
1033
+ }
1034
+ function oneOf(value, values, fallback) {
1035
+ return typeof value === "string" && values.includes(value) ? value : fallback;
1036
+ }
1037
+ function plainRecord(value) {
1038
+ try {
1039
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
1040
+ const prototype = Object.getPrototypeOf(value);
1041
+ return prototype === Object.prototype || prototype === null ? value : null;
1042
+ } catch {
1043
+ return null;
1044
+ }
1045
+ }
1046
+ function read(source, key) {
1047
+ try {
1048
+ if (!source || !Object.hasOwn(source, key)) return;
1049
+ return source[key];
1050
+ } catch {
1051
+ return;
1052
+ }
1053
+ }
1054
+ function safeArrayItems(value, maximum) {
1055
+ try {
1056
+ if (!Array.isArray(value)) return [];
1057
+ const length = Math.min(value.length, maximum);
1058
+ if (!Number.isSafeInteger(length) || length < 0) return [];
1059
+ const items = [];
1060
+ for (let index = 0; index < length; index += 1) try {
1061
+ items.push(value[index]);
1062
+ } catch {
1063
+ items.push(void 0);
1064
+ }
1065
+ return items;
1066
+ } catch {
1067
+ return [];
1068
+ }
1069
+ }
1070
+ function deepFreeze(value) {
1071
+ if (value && typeof value === "object") {
1072
+ Object.freeze(value);
1073
+ for (const child of Object.values(value)) if (child && typeof child === "object" && !Object.isFrozen(child)) deepFreeze(child);
1074
+ }
1075
+ return value;
1076
+ }
1077
+ //#endregion
1078
+ //#region src/state-root.ts
1079
+ function resolveApplicationStateRoot(options) {
1080
+ const paths = resolveLocalRuntimePaths({
1081
+ appId: options.appId,
1082
+ dataDirectory: options.dataDirectory,
1083
+ sourceRoot: options.sourceRoot
1084
+ });
1085
+ const platformName = options.platformName || platform();
1086
+ const homeDirectory = options.homeDirectory || homedir();
1087
+ const environment = options.environment || process.env;
1088
+ const stateBase = platformName === "darwin" ? join(homeDirectory, "Library", "Application Support") : platformName === "win32" ? environment.LOCALAPPDATA || homeDirectory : environment.XDG_STATE_HOME || join(homeDirectory, ".local", "state");
1089
+ const dataIdentity = createHash("sha256").update(resolve(paths.root)).digest("hex").slice(0, 12);
1090
+ return resolve(stateBase, `.${options.appId}-${dataIdentity}-state`);
1091
+ }
1092
+ function isInside$1(parent, child) {
1093
+ const relative = child.slice(parent.length);
1094
+ return child === parent || child.startsWith(parent) && (relative.startsWith("/") || relative.startsWith("\\"));
1095
+ }
1096
+ function prepareApplicationStateRoot(options) {
1097
+ const sourceRoot = realpathSync(resolve(options.sourceRoot || process.cwd()));
1098
+ const stateRoot = resolveApplicationStateRoot(options);
1099
+ if (isInside$1(sourceRoot, stateRoot) || isInside$1(stateRoot, sourceRoot)) throw new Error("Application state must remain outside the source tree.");
1100
+ const currentUid = options.currentUid ?? process.getuid?.();
1101
+ let component = parse(stateRoot).root;
1102
+ for (const part of stateRoot.slice(component.length).split(/[\\/]+/).filter(Boolean)) {
1103
+ component = join(component, part);
1104
+ try {
1105
+ const details = lstatSync(component);
1106
+ if (details.isSymbolicLink() || !details.isDirectory()) throw new Error(`Application state path component is unsafe: ${component}`);
1107
+ const sharedStickyRoot = details.uid === 0 && (details.mode & 512) !== 0;
1108
+ if (currentUid !== void 0 && (details.uid !== currentUid && details.uid !== 0 || (details.mode & 18) !== 0 && !sharedStickyRoot)) throw new Error(`Application state path lacks trusted custody: ${component}`);
1109
+ } catch (error) {
1110
+ if (errorCode(error) !== "ENOENT") throw error;
1111
+ try {
1112
+ mkdirSync(component, { mode: 448 });
1113
+ } catch (mkdirError) {
1114
+ if (errorCode(mkdirError) !== "EEXIST") throw mkdirError;
1115
+ }
1116
+ const created = lstatSync(component);
1117
+ if (created.isSymbolicLink() || !created.isDirectory() || currentUid !== void 0 && created.uid !== currentUid || (created.mode & 511) !== 448) throw new Error(`Application state path component is unsafe: ${component}`);
1118
+ }
1119
+ }
1120
+ const rootDetails = lstatSync(stateRoot);
1121
+ if (rootDetails.isSymbolicLink() || !rootDetails.isDirectory() || currentUid !== void 0 && rootDetails.uid !== currentUid || (rootDetails.mode & 511) !== 448) throw new Error("Application state root must be current-user-owned mode 0700.");
1122
+ const markerPath = join(stateRoot, `.smrt-state-${validateApplicationId(options.appId)}`);
1123
+ let descriptor;
1124
+ try {
1125
+ descriptor = openSync(markerPath, constants$1.O_WRONLY | constants$1.O_CREAT | constants$1.O_EXCL, 384);
1126
+ } catch (error) {
1127
+ if (errorCode(error) !== "EEXIST") throw error;
1128
+ const marker = lstatSync(markerPath);
1129
+ if (marker.isSymbolicLink() || !marker.isFile() || marker.size !== 0 || currentUid !== void 0 && marker.uid !== currentUid || (marker.mode & 511) !== 384) throw new Error("Application state marker is unsafe.");
1130
+ } finally {
1131
+ if (descriptor !== void 0) closeSync(descriptor);
1132
+ }
1133
+ return stateRoot;
1134
+ }
1135
+ //#endregion
1136
+ //#region src/writer-lease.ts
1137
+ function validateRecord(record) {
1138
+ const candidate = record;
1139
+ if (candidate?.schemaVersion !== 1 || !Number.isSafeInteger(candidate.pid) || candidate.pid < 1 || typeof candidate.instance !== "string" || !/^[a-f0-9]{32}$/.test(candidate.instance)) throw new Error("The application writer lease is malformed.");
1140
+ return candidate;
1141
+ }
1142
+ function readRecord(pathOrDescriptor) {
1143
+ return validateRecord(JSON.parse(readFileSync(pathOrDescriptor, "utf8")));
1144
+ }
1145
+ function isStaleWriterLease(contents) {
1146
+ try {
1147
+ return !processExists(validateRecord(JSON.parse(contents)).pid);
1148
+ } catch {
1149
+ return false;
1150
+ }
1151
+ }
1152
+ function readActiveWriterLease(stateRoot) {
1153
+ const path = join(stateRoot, "writer.lease");
1154
+ for (let attempt = 0; attempt < 4; attempt += 1) {
1155
+ let observed;
1156
+ let record;
1157
+ try {
1158
+ observed = readFileSync(path, "utf8");
1159
+ record = validateRecord(JSON.parse(observed));
1160
+ } catch (error) {
1161
+ if (errorCode(error) === "ENOENT") return null;
1162
+ throw new Error("The application writer lease cannot be verified; inspect the private state directory.");
1163
+ }
1164
+ if (processExists(record.pid)) return record;
1165
+ if (reclaimStaleRecord(path, observed, isStaleWriterLease) !== "changed") return null;
1166
+ }
1167
+ throw new Error("The application writer lease changed repeatedly and cannot be verified.");
1168
+ }
1169
+ function acquireWriterLease(stateRoot, options = {}) {
1170
+ mkdirSync(stateRoot, {
1171
+ recursive: true,
1172
+ mode: 448
1173
+ });
1174
+ const path = join(stateRoot, "writer.lease");
1175
+ const active = readActiveWriterLease(stateRoot);
1176
+ if (active?.pid === process.pid) return { release() {} };
1177
+ if (active) throw new Error(`Another application writer is active (process ${active.pid}).`);
1178
+ const instance = randomBytes(16).toString("hex");
1179
+ let descriptor;
1180
+ for (let attempt = 0; attempt < 2; attempt += 1) try {
1181
+ descriptor = openSync(path, "wx", 384);
1182
+ writeFileSync(descriptor, `${JSON.stringify({
1183
+ schemaVersion: 1,
1184
+ pid: process.pid,
1185
+ instance
1186
+ })}
1187
+ `);
1188
+ break;
1189
+ } catch (error) {
1190
+ if (descriptor !== void 0) {
1191
+ closeSync(descriptor);
1192
+ descriptor = void 0;
1193
+ }
1194
+ if (errorCode(error) === "EEXIST") {
1195
+ const owner = readActiveWriterLease(stateRoot);
1196
+ if (!owner && attempt === 0) continue;
1197
+ throw new Error(`Another application writer is active${owner ? ` (process ${owner.pid})` : ""}.`);
1198
+ }
1199
+ rmSync(path, { force: true });
1200
+ throw error;
1201
+ }
1202
+ if (descriptor === void 0) throw new Error("The application writer lease could not be acquired.");
1203
+ closeSync(descriptor);
1204
+ let released = false;
1205
+ const release = () => {
1206
+ if (released) return;
1207
+ released = true;
1208
+ process.removeListener("exit", release);
1209
+ try {
1210
+ const current = readRecord(path);
1211
+ if (current.pid === process.pid && current.instance === instance) rmSync(path, { force: true });
1212
+ } catch {}
1213
+ };
1214
+ const operationPath = join(stateRoot, "operation.lock");
1215
+ let observedOperation = null;
1216
+ try {
1217
+ observedOperation = readFileSync(operationPath, "utf8");
1218
+ } catch (error) {
1219
+ if (errorCode(error) !== "ENOENT") {
1220
+ release();
1221
+ throw error;
1222
+ }
1223
+ }
1224
+ if (observedOperation !== null) {
1225
+ let operation;
1226
+ try {
1227
+ operation = JSON.parse(observedOperation);
1228
+ } catch {
1229
+ operation = null;
1230
+ }
1231
+ if (!operation || !Number.isSafeInteger(operation.pid) || operation.pid < 1 || typeof operation.instance !== "string") {
1232
+ release();
1233
+ throw new Error("The application operation lock cannot be verified.");
1234
+ }
1235
+ if (!processExists(operation.pid)) {
1236
+ let outcome;
1237
+ try {
1238
+ outcome = reclaimStaleRecord(operationPath, observedOperation, isStaleOperationLock);
1239
+ } catch (error) {
1240
+ release();
1241
+ throw error;
1242
+ }
1243
+ if (outcome === "changed") {
1244
+ release();
1245
+ throw new Error("The application operation lock changed unexpectedly.");
1246
+ }
1247
+ } else if (operation.instance !== options.operationInstance) {
1248
+ release();
1249
+ throw new Error("An application operation is active; wait for it to finish before starting a writer.");
1250
+ }
1251
+ }
1252
+ process.once("exit", release);
1253
+ return { release };
1254
+ }
1255
+ //#endregion
1256
+ //#region src/index.ts
1257
+ var DEFAULT_BIND_HOST = "127.0.0.1";
1258
+ var DEFAULT_BOOTSTRAP_TTL_SECONDS = 600;
1259
+ var MAX_BOOTSTRAP_TTL_SECONDS = 900;
1260
+ var DEFAULT_SESSION_TTL_SECONDS = 10080 * 60;
1261
+ var BOOTSTRAP_TABLE = "_smrt_local_owner_bootstrap";
1262
+ var SECRET_FILE_NAME = "application.secret";
1263
+ var DATABASE_FILE_NAME = "application.sqlite";
1264
+ var ROOT_MARKER_PREFIX = ".smrt-local-runtime-";
1265
+ var PREFLIGHT_RETRY_LIMIT = 4;
1266
+ var INITIALIZATION_LOCK_TIMEOUT_MS = 12e4;
1267
+ var INITIALIZATION_LOCK_WORKER = `
1268
+ const { parentPort, workerData } = require('node:worker_threads');
1269
+ let database;
1270
+ let transaction;
1271
+ (async () => {
1272
+ try {
1273
+ const { getDatabase } = await import(workerData.sqlModuleUrl);
1274
+ database = await getDatabase({
1275
+ type: 'sqlite',
1276
+ url: workerData.path,
1277
+ secureFile: {
1278
+ driver: 'node:sqlite',
1279
+ custody: 'trusted-parent',
1280
+ root: workerData.custodyRoot,
1281
+ },
1282
+ transactionQueueTimeout: workerData.timeout,
1283
+ });
1284
+ if (!database.beginTransaction) {
1285
+ throw new Error('Secure SQLite does not expose beginTransaction().');
1286
+ }
1287
+ await database.query('PRAGMA busy_timeout = ' + workerData.timeout);
1288
+ transaction = await database.beginTransaction();
1289
+ parentPort.postMessage({ type: 'acquired' });
1290
+ } catch (error) {
1291
+ try { await database?.close?.(); } catch {}
1292
+ parentPort.postMessage({ type: 'error', message: String(error) });
1293
+ parentPort.close();
1294
+ return;
1295
+ }
1296
+ parentPort.once('message', async (message) => {
1297
+ if (message?.type !== 'release') return;
1298
+ let releaseError;
1299
+ try { await transaction.rollback(); } catch (error) { releaseError = String(error); }
1300
+ try { await database.close?.(); } catch (error) { releaseError ??= String(error); }
1301
+ parentPort.postMessage({ type: 'released', error: releaseError });
1302
+ parentPort.close();
1303
+ });
1304
+ })().catch((error) => {
1305
+ parentPort.postMessage({ type: 'error', message: String(error) });
1306
+ parentPort.close();
1307
+ });
1308
+ `;
1309
+ var execFileAsync = promisify(execFile);
1310
+ async function prepareLocalDatabaseStorage(options) {
1311
+ const paths = resolveLocalRuntimePaths(options);
1312
+ const sourceRoot = resolve(options.sourceRoot ?? process.cwd());
1313
+ const appId = validateApplicationId(options.appId);
1314
+ await preflightLocalApplicationRoot(paths.root, sourceRoot, appId);
1315
+ const initializationLock = await acquireInitializationLock(paths.root);
1316
+ let db;
1317
+ let failure;
1318
+ try {
1319
+ await preflightLocalApplicationRoot(paths.root, sourceRoot, appId);
1320
+ db = (await acquireLocalDatabase(paths, sourceRoot, appId)).db;
1321
+ await db.close?.();
1322
+ db = void 0;
1323
+ } catch (error) {
1324
+ failure = db ? await closeDatabaseAfterFailure(db, error) : error;
1325
+ db = void 0;
1326
+ }
1327
+ try {
1328
+ await initializationLock.release();
1329
+ } catch (releaseError) {
1330
+ failure = failure ? preservePrimaryFailure(failure, releaseError, "initializationLockReleaseError", "Local database storage preparation and initialization-lock release both failed.") : releaseError;
1331
+ }
1332
+ if (failure) throw failure;
1333
+ return paths;
1334
+ }
1335
+ async function validateLocalDatabaseStorage(options) {
1336
+ const paths = resolveLocalRuntimePaths(options);
1337
+ const sourceRoot = resolve(options.sourceRoot ?? process.cwd());
1338
+ const appId = validateApplicationId(options.appId);
1339
+ await preflightLocalApplicationRoot(paths.root, sourceRoot, appId);
1340
+ const canonicalSourceRoot = await realpath(sourceRoot);
1341
+ await validateRootMarker(resolve(paths.root, `${ROOT_MARKER_PREFIX}${appId}`), canonicalSourceRoot);
1342
+ await validateExistingPathChain(paths.database, canonicalSourceRoot, "file");
1343
+ const database = await lstat(paths.database);
1344
+ const currentUid = process.getuid?.();
1345
+ if (database.isSymbolicLink() || !database.isFile() || currentUid !== void 0 && database.uid !== currentUid || (database.mode & 511) !== 384) throw unsafeFilesystemEntry(`Local application database must be a current-user-owned mode-0600 file: ${paths.database}`);
1346
+ return paths;
1347
+ }
1348
+ var MIGRATION_FAILED_MESSAGE = "The application migration step failed; run pnpm app:setup and inspect the private migration logs.";
1349
+ var LocalRuntimeError = class extends Error {
1350
+ constructor(code, message) {
1351
+ super(message);
1352
+ this.code = code;
1353
+ this.name = "LocalRuntimeError";
1354
+ }
1355
+ code;
1356
+ };
1357
+ function resolveLocalRuntimePaths(options) {
1358
+ const appId = validateApplicationId(options.appId);
1359
+ const runtimePlatform = options.platform ?? platform();
1360
+ const runtimeHome = options.homeDirectory ?? homedir();
1361
+ const defaultRoot = defaultApplicationDataRoot(runtimePlatform, runtimeHome, options.env ?? process.env);
1362
+ const root = resolve(options.dataDirectory ?? resolve(defaultRoot, appId));
1363
+ const sourceRoot = resolve(options.sourceRoot ?? process.cwd());
1364
+ if (sameFilesystemPath(root, runtimeHome, runtimePlatform) || sameFilesystemPath(root, parse(root).root, runtimePlatform) || isInside(sourceRoot, root, runtimePlatform) || isInside(root, sourceRoot, runtimePlatform)) throw new LocalRuntimeError("invalid_configuration", "Local application data must be a dedicated directory that does not overlap the source tree.");
1365
+ return Object.freeze({
1366
+ root,
1367
+ database: resolve(root, DATABASE_FILE_NAME),
1368
+ assets: resolve(root, "assets"),
1369
+ secrets: resolve(root, "secrets"),
1370
+ applicationSecret: resolve(root, "secrets", SECRET_FILE_NAME)
1371
+ });
1372
+ }
1373
+ async function initializeLocalApplicationRuntime(options) {
1374
+ const bindHost = normalizeLoopbackHost(options.bindHost ?? DEFAULT_BIND_HOST);
1375
+ const bootstrapTtlSeconds = validateBootstrapTtl(options.bootstrapTtlSeconds ?? DEFAULT_BOOTSTRAP_TTL_SECONDS);
1376
+ const sessionTtlSeconds = validateSessionTtl(options.sessionTtlSeconds ?? DEFAULT_SESSION_TTL_SECONDS);
1377
+ const resolvedRuntime = resolveApplicationRuntime({
1378
+ profile: "local",
1379
+ providers: options.providers
1380
+ });
1381
+ const paths = resolveLocalRuntimePaths(options);
1382
+ const sourceRoot = resolve(options.sourceRoot ?? process.cwd());
1383
+ const appId = validateApplicationId(options.appId);
1384
+ await preflightLocalApplicationRoot(paths.root, sourceRoot, appId);
1385
+ const initializationLock = await acquireInitializationLock(paths.root);
1386
+ let db;
1387
+ let initialization;
1388
+ let initializationFailure;
1389
+ let initializationFailed = false;
1390
+ try {
1391
+ await preflightLocalApplicationRoot(paths.root, sourceRoot, appId);
1392
+ const acquired = await acquireLocalDatabase(paths, sourceRoot, appId);
1393
+ db = acquired.db;
1394
+ const { canonicalSourceRoot } = acquired;
1395
+ await prepareLocalFilesystem(paths, canonicalSourceRoot);
1396
+ await tuneLocalSqlite(db);
1397
+ try {
1398
+ await options.prepareDatabase?.(db);
1399
+ } catch (migrationFailure) {
1400
+ throw migrationFailed(migrationFailure);
1401
+ }
1402
+ await ensureBootstrapTable(db);
1403
+ const runtime = new InitializedLocalApplicationRuntime({
1404
+ db,
1405
+ paths,
1406
+ bindHost,
1407
+ resolvedRuntime,
1408
+ bootstrapTtlSeconds,
1409
+ sessionTtlSeconds,
1410
+ backgroundJobs: options.backgroundJobs === true,
1411
+ paidCapabilities: options.paidCapabilities === true,
1412
+ now: options.now ?? (() => /* @__PURE__ */ new Date()),
1413
+ canonicalSourceRoot
1414
+ });
1415
+ initialization = {
1416
+ runtime,
1417
+ bootstrap: await runtime.ensureBootstrapInvitation(),
1418
+ diagnostics: await runtime.diagnostics()
1419
+ };
1420
+ } catch (error) {
1421
+ initializationFailed = true;
1422
+ initializationFailure = db ? await closeDatabaseAfterFailure(db, error) : error;
1423
+ db = void 0;
1424
+ }
1425
+ try {
1426
+ await initializationLock.release();
1427
+ } catch (releaseError) {
1428
+ if (initializationFailed) initializationFailure = preservePrimaryFailure(initializationFailure, releaseError, "initializationLockReleaseError", "Local runtime initialization and initialization-lock release both failed.");
1429
+ else if (db) {
1430
+ initializationFailed = true;
1431
+ initializationFailure = await closeDatabaseAfterFailure(db, releaseError);
1432
+ db = void 0;
1433
+ } else {
1434
+ initializationFailed = true;
1435
+ initializationFailure = releaseError;
1436
+ }
1437
+ }
1438
+ if (initializationFailed) throw initializationFailure;
1439
+ if (!initialization) throw new Error("Local runtime initialization completed without a result.");
1440
+ return initialization;
1441
+ }
1442
+ var InitializedLocalApplicationRuntime = class {
1443
+ db;
1444
+ paths;
1445
+ bindHost;
1446
+ resolvedRuntime;
1447
+ backgroundJobsEnabled;
1448
+ paidCapabilitiesEnabled;
1449
+ bootstrapTtlSeconds;
1450
+ sessionTtlSeconds;
1451
+ now;
1452
+ canonicalSourceRoot;
1453
+ constructor(state) {
1454
+ this.db = state.db;
1455
+ this.paths = state.paths;
1456
+ this.bindHost = state.bindHost;
1457
+ this.resolvedRuntime = state.resolvedRuntime;
1458
+ this.bootstrapTtlSeconds = state.bootstrapTtlSeconds;
1459
+ this.sessionTtlSeconds = state.sessionTtlSeconds;
1460
+ this.backgroundJobsEnabled = state.backgroundJobs;
1461
+ this.paidCapabilitiesEnabled = state.paidCapabilities;
1462
+ this.now = state.now;
1463
+ this.canonicalSourceRoot = state.canonicalSourceRoot;
1464
+ }
1465
+ /**
1466
+ * Issue the first short-lived owner invitation when none is active.
1467
+ * Repeated startup never rotates an unexpired token or duplicates an owner.
1468
+ */
1469
+ async ensureBootstrapInvitation() {
1470
+ if (await findExistingOwner(this.db)) return null;
1471
+ const current = await readBootstrapRow(this.db);
1472
+ if (current?.consumed_at != null) return null;
1473
+ const now = this.now();
1474
+ const currentExpiry = parseStoredDate(current?.expires_at);
1475
+ if (current && current.consumed_at == null && currentExpiry !== null && currentExpiry.getTime() > now.getTime()) return null;
1476
+ return this.issueBootstrapInvitation(false);
1477
+ }
1478
+ /** Explicit recovery when an installer lost the one-time plaintext token. */
1479
+ async rotateBootstrapInvitation() {
1480
+ if (await findExistingOwner(this.db)) throw new LocalRuntimeError("bootstrap_claimed", "The local owner has already been claimed.");
1481
+ if ((await readBootstrapRow(this.db))?.consumed_at != null) throw new LocalRuntimeError("bootstrap_claimed", "The local owner bootstrap invitation has already been consumed.");
1482
+ const invitation = await this.issueBootstrapInvitation(true);
1483
+ if (!invitation) {
1484
+ if ((await readBootstrapRow(this.db))?.consumed_at != null) throw new LocalRuntimeError("bootstrap_claimed", "The local owner bootstrap invitation has already been consumed.");
1485
+ throw new LocalRuntimeError("bootstrap_unavailable", "A local owner bootstrap invitation could not be issued.");
1486
+ }
1487
+ return invitation;
1488
+ }
1489
+ /**
1490
+ * Consume a bootstrap token and atomically create normal identity/RBAC/session
1491
+ * records. The token transition and every record write share one transaction.
1492
+ */
1493
+ async claimOwner(input) {
1494
+ validateOwnerInput(input);
1495
+ if (await findExistingOwner(this.db)) throw new LocalRuntimeError("bootstrap_claimed", "The local owner has already been claimed.");
1496
+ if (typeof this.db.transaction !== "function") throw new LocalRuntimeError("bootstrap_unavailable", "Owner bootstrap requires a database adapter with transaction support.");
1497
+ const tokenHash = await this.hashBootstrapToken(input.token);
1498
+ const now = this.now();
1499
+ const nowIso = now.toISOString();
1500
+ if (!await isBootstrapTokenClaimable(this.db, tokenHash, nowIso)) await throwBootstrapClaimFailure(this.db, now);
1501
+ await (await RoleCollection.create({ db: this.db })).seedSystemRoles({ seedPermissions: true });
1502
+ return this.db.transaction(async (tx) => {
1503
+ if ((await tx.query(`UPDATE ${BOOTSTRAP_TABLE}
1504
+ SET consumed_at = ?
1505
+ WHERE slot = 1
1506
+ AND token_hash = ?
1507
+ AND consumed_at IS NULL
1508
+ AND expires_at > ?
1509
+ RETURNING expires_at`, nowIso, tokenHash, nowIso)).rows.length === 0) await throwBootstrapClaimFailure(tx, now);
1510
+ if (await findExistingOwner(tx)) throw new LocalRuntimeError("bootstrap_claimed", "The local owner has already been claimed.");
1511
+ return createLocalOwnerRecords(tx, input, this.sessionTtlSeconds);
1512
+ });
1513
+ }
1514
+ /** Restore the normal server-side session created by owner bootstrap. */
1515
+ async restoreSession(sessionId) {
1516
+ const service = new SessionService({ db: this.db });
1517
+ await service.initialize();
1518
+ return service.loadSessionContext(sessionId);
1519
+ }
1520
+ /**
1521
+ * Create an in-process persisted-job runner. Background work is default-off
1522
+ * and must be explicitly enabled by runtime configuration.
1523
+ */
1524
+ async createEmbeddedJobRunner(config = {}) {
1525
+ if (!this.backgroundJobsEnabled) throw new LocalRuntimeError("capability_disabled", "Background jobs are disabled; enable them explicitly for this local application.");
1526
+ if (this.resolvedRuntime.providers.jobs.topology !== "embedded") throw new LocalRuntimeError("invalid_configuration", "An embedded runner requires the local embedded job topology.");
1527
+ const runner = new TaskRunner({
1528
+ concurrency: 1,
1529
+ retention: false,
1530
+ ...config
1531
+ });
1532
+ await runner.initialize(this.db);
1533
+ return runner;
1534
+ }
1535
+ /** Return a deterministic snapshot that never reads secret file contents. */
1536
+ async diagnostics() {
1537
+ const owner = await findExistingOwner(this.db);
1538
+ const bootstrap = await readBootstrapRow(this.db);
1539
+ const now = this.now();
1540
+ const expiresAt = parseStoredDate(bootstrap?.expires_at);
1541
+ let status = "unavailable";
1542
+ if (owner || bootstrap?.consumed_at != null) status = "claimed";
1543
+ else if (expiresAt && expiresAt.getTime() > now.getTime()) status = "available";
1544
+ else if (expiresAt) status = "expired";
1545
+ return Object.freeze({
1546
+ schemaVersion: 1,
1547
+ runtime: this.resolvedRuntime,
1548
+ bind: Object.freeze({
1549
+ host: this.bindHost,
1550
+ loopback: true
1551
+ }),
1552
+ paths: this.paths,
1553
+ storage: Object.freeze({
1554
+ database: "sqlite-file",
1555
+ assets: "user-owned-directory",
1556
+ secrets: "mode-0600-local-file"
1557
+ }),
1558
+ bootstrap: Object.freeze({
1559
+ status,
1560
+ expiresAt: expiresAt?.toISOString() ?? null,
1561
+ tokenValuesIncluded: false,
1562
+ tokenHashesIncluded: false
1563
+ }),
1564
+ tenancy: Object.freeze({
1565
+ mode: "real-default-tenant",
1566
+ ui: "hidden"
1567
+ }),
1568
+ jobs: Object.freeze({
1569
+ topology: this.resolvedRuntime.providers.jobs.topology,
1570
+ backgroundEnabled: this.backgroundJobsEnabled
1571
+ }),
1572
+ paidCapabilitiesEnabled: this.paidCapabilitiesEnabled,
1573
+ secretValuesIncluded: false
1574
+ });
1575
+ }
1576
+ async issueBootstrapInvitation(replaceActive) {
1577
+ const token = randomBytes(32).toString("base64url");
1578
+ const tokenHash = await this.hashBootstrapToken(token);
1579
+ const now = this.now();
1580
+ const expiresAt = new Date(now.getTime() + this.bootstrapTtlSeconds * 1e3);
1581
+ if ((await this.db.query(`INSERT INTO ${BOOTSTRAP_TABLE}
1582
+ (slot, token_hash, expires_at, consumed_at, created_at)
1583
+ VALUES (1, ?, ?, NULL, ?)
1584
+ ON CONFLICT(slot) DO UPDATE SET
1585
+ token_hash = excluded.token_hash,
1586
+ expires_at = excluded.expires_at,
1587
+ consumed_at = NULL,
1588
+ created_at = excluded.created_at
1589
+ WHERE ${BOOTSTRAP_TABLE}.consumed_at IS NULL
1590
+ AND (? = 1 OR ${BOOTSTRAP_TABLE}.expires_at <= ?)
1591
+ RETURNING token_hash`, tokenHash, expiresAt.toISOString(), now.toISOString(), replaceActive ? 1 : 0, now.toISOString())).rows[0]?.token_hash !== tokenHash) return null;
1592
+ return Object.freeze({
1593
+ token,
1594
+ expiresAt: expiresAt.toISOString()
1595
+ });
1596
+ }
1597
+ async hashBootstrapToken(token) {
1598
+ return createHmac("sha256", (await validateApplicationSecret(this.paths.applicationSecret, this.canonicalSourceRoot)).trim()).update(token).digest("hex");
1599
+ }
1600
+ };
1601
+ async function createLocalOwnerRecords(db, input, sessionTtlSeconds) {
1602
+ const profileTypes = await ProfileTypeCollection.create({ db });
1603
+ const person = new Person({
1604
+ db,
1605
+ typeId: requireId((await withSystemContext(() => profileTypes.getOrCreateGlobalBySlug("person", {
1606
+ name: "Person",
1607
+ description: "An individual person"
1608
+ }))).id, "Person profile type"),
1609
+ tenantId: null,
1610
+ name: input.name.trim(),
1611
+ email: input.email.trim()
1612
+ });
1613
+ await person.initialize();
1614
+ await person.save();
1615
+ const user = await (await UserCollection.create({ db })).create({
1616
+ profileId: requireId(person.id, "Person"),
1617
+ email: input.email.trim(),
1618
+ status: UserStatus.ACTIVE,
1619
+ lastLoginAt: /* @__PURE__ */ new Date()
1620
+ });
1621
+ await user.save();
1622
+ const tenantService = new TenantService({ db }, {
1623
+ mode: "required",
1624
+ maxTenants: 1,
1625
+ defaultName: input.tenantName?.trim() || "My Workspace"
1626
+ });
1627
+ await tenantService.initialize();
1628
+ const { tenant, membership } = await tenantService.createTenantWithOwnership(requireId(user.id, "User"), input.tenantName?.trim() || "My Workspace", { slug: "default" });
1629
+ const session = await (await SessionCollection.create({ db })).createSession({
1630
+ userId: requireId(user.id, "User"),
1631
+ tenantId: requireId(tenant.id, "Tenant"),
1632
+ ttl: sessionTtlSeconds,
1633
+ userAgent: input.userAgent,
1634
+ ipAddress: input.ipAddress,
1635
+ data: { localOwner: true }
1636
+ });
1637
+ return Object.freeze({
1638
+ profileId: requireId(person.id, "Person"),
1639
+ userId: requireId(user.id, "User"),
1640
+ tenantId: requireId(tenant.id, "Tenant"),
1641
+ membershipId: requireId(membership.id, "Membership"),
1642
+ sessionId: requireId(session.id, "Session")
1643
+ });
1644
+ }
1645
+ async function findExistingOwner(db) {
1646
+ const ownerRole = await (await RoleCollection.create({ db })).findSystemRoleBySlug(DEFAULT_ROLE_SLUGS.OWNER);
1647
+ if (!ownerRole?.id) return null;
1648
+ const active = (await (await MembershipCollection.create({ db })).findByRole(ownerRole.id)).find((membership) => membership.status === MembershipStatus.ACTIVE && typeof membership.userId === "string" && typeof membership.tenantId === "string");
1649
+ return active ? {
1650
+ userId: active.userId,
1651
+ tenantId: active.tenantId
1652
+ } : null;
1653
+ }
1654
+ async function throwBootstrapClaimFailure(db, now) {
1655
+ const row = await readBootstrapRow(db);
1656
+ if (!row) throw new LocalRuntimeError("bootstrap_unavailable", "No local owner bootstrap invitation is available.");
1657
+ if (row.consumed_at != null) throw new LocalRuntimeError("bootstrap_claimed", "The local owner bootstrap invitation has already been consumed.");
1658
+ const expiresAt = parseStoredDate(row.expires_at);
1659
+ if (!expiresAt || expiresAt.getTime() <= now.getTime()) throw new LocalRuntimeError("bootstrap_expired", "The local owner bootstrap invitation has expired.");
1660
+ throw new LocalRuntimeError("bootstrap_invalid", "The local owner bootstrap token is invalid.");
1661
+ }
1662
+ async function isBootstrapTokenClaimable(db, tokenHash, nowIso) {
1663
+ return (await db.query(`SELECT 1 AS claimable
1664
+ FROM ${BOOTSTRAP_TABLE}
1665
+ WHERE slot = 1
1666
+ AND token_hash = ?
1667
+ AND consumed_at IS NULL
1668
+ AND expires_at > ?
1669
+ LIMIT 1`, tokenHash, nowIso)).rows.length > 0;
1670
+ }
1671
+ async function readBootstrapRow(db) {
1672
+ return (await db.query(`SELECT expires_at, consumed_at
1673
+ FROM ${BOOTSTRAP_TABLE}
1674
+ WHERE slot = 1
1675
+ LIMIT 1`)).rows[0] ?? null;
1676
+ }
1677
+ async function ensureBootstrapTable(db) {
1678
+ await db.query(`CREATE TABLE IF NOT EXISTS ${BOOTSTRAP_TABLE} (
1679
+ slot INTEGER PRIMARY KEY CHECK (slot = 1),
1680
+ token_hash TEXT NOT NULL,
1681
+ expires_at TEXT NOT NULL,
1682
+ consumed_at TEXT NULL,
1683
+ created_at TEXT NOT NULL
1684
+ )`);
1685
+ }
1686
+ async function tuneLocalSqlite(db) {
1687
+ await db.query("PRAGMA foreign_keys = ON");
1688
+ await db.query("PRAGMA journal_mode = WAL");
1689
+ await db.query("PRAGMA synchronous = FULL");
1690
+ await db.query("PRAGMA busy_timeout = 5000");
1691
+ }
1692
+ async function preflightLocalApplicationRoot(root, sourceRoot, appId) {
1693
+ for (let attempt = 0; attempt < PREFLIGHT_RETRY_LIMIT; attempt += 1) try {
1694
+ await preflightLocalApplicationRootOnce(root, sourceRoot, appId);
1695
+ return;
1696
+ } catch (error) {
1697
+ if (!isMissingFile(error) && !hasMissingFileCause(error)) throw error;
1698
+ if (attempt === PREFLIGHT_RETRY_LIMIT - 1) throw unsafeFilesystemEntry("Local application data changed repeatedly during read-only preflight.", error);
1699
+ await new Promise((resolveRetry) => setImmediate(resolveRetry));
1700
+ }
1701
+ }
1702
+ async function preflightLocalApplicationRootOnce(root, sourceRoot, appId) {
1703
+ requireNoFollowSupport();
1704
+ const canonicalSourceRoot = await realpath(sourceRoot);
1705
+ const currentUid = process.getuid?.();
1706
+ if (currentUid === void 0) throw unsafeFilesystemEntry("Local runtime storage requires a numeric current-user identity.");
1707
+ if (isInside(canonicalSourceRoot, root) || isInside(root, canonicalSourceRoot)) throw unsafeFilesystemEntry("Local application data must remain outside the canonical source tree.");
1708
+ let rootExists = true;
1709
+ for (const component of absolutePathComponents(root)) {
1710
+ let details;
1711
+ try {
1712
+ details = await lstat(component);
1713
+ } catch (error) {
1714
+ if (!isMissingFile(error)) throw error;
1715
+ rootExists = false;
1716
+ break;
1717
+ }
1718
+ if (details.isSymbolicLink() || !details.isDirectory()) throw unsafeFilesystemEntry(`Local runtime path component must be a real directory: ${component}`);
1719
+ await assertCanonicalEntry(component, canonicalSourceRoot);
1720
+ const sharedStickyRoot = details.uid === 0 && (details.mode & 512) !== 0;
1721
+ if (details.uid !== currentUid && details.uid !== 0 || (details.mode & 18) !== 0 && !sharedStickyRoot) throw unsafeFilesystemEntry(`Local runtime path ancestry does not have trusted-parent custody: ${component}`);
1722
+ await assertNoPermissiveDarwinAcl(component);
1723
+ }
1724
+ if (!rootExists) return;
1725
+ const rootDetails = await lstat(root);
1726
+ if (rootDetails.uid !== currentUid || (rootDetails.mode & 511) !== 448) throw unsafeFilesystemEntry(`Existing local application data root must already be owned by the current user with mode 0700: ${root}`);
1727
+ await preflightDedicatedRoot(root, canonicalSourceRoot, appId);
1728
+ }
1729
+ async function preflightDedicatedRoot(root, canonicalSourceRoot, appId) {
1730
+ const finalMarker = resolve(root, `${ROOT_MARKER_PREFIX}${appId}`);
1731
+ const pendingMarker = resolve(root, `${ROOT_MARKER_PREFIX}${appId}.pending`);
1732
+ const entries = await readdir(root);
1733
+ const finalMarkerName = finalMarker.slice(root.length + 1);
1734
+ const pendingMarkerName = pendingMarker.slice(root.length + 1);
1735
+ const hasFinalMarker = entries.includes(finalMarkerName);
1736
+ const hasPendingMarker = entries.includes(pendingMarkerName);
1737
+ if (hasFinalMarker) {
1738
+ await validateRootMarker(finalMarker, canonicalSourceRoot);
1739
+ if (hasPendingMarker) await validateRootMarker(pendingMarker, canonicalSourceRoot);
1740
+ return;
1741
+ }
1742
+ if (hasPendingMarker) {
1743
+ await validateRootMarker(pendingMarker, canonicalSourceRoot);
1744
+ const permittedInterruptedEntries = /* @__PURE__ */ new Set([
1745
+ pendingMarkerName,
1746
+ DATABASE_FILE_NAME,
1747
+ `${DATABASE_FILE_NAME}-shm`,
1748
+ `${DATABASE_FILE_NAME}-wal`
1749
+ ]);
1750
+ if (entries.some((entry) => !permittedInterruptedEntries.has(entry))) throw unsafeFilesystemEntry("Interrupted local application root contains unexpected artifacts.");
1751
+ return;
1752
+ }
1753
+ if (entries.length > 0) throw unsafeFilesystemEntry("Existing local application data root is populated but has no valid ownership marker.");
1754
+ }
1755
+ async function assertNoPermissiveDarwinAcl(path) {
1756
+ if (platform() !== "darwin") return;
1757
+ let listing;
1758
+ try {
1759
+ listing = (await execFileAsync("/bin/ls", [
1760
+ "-lde",
1761
+ "--",
1762
+ path
1763
+ ], {
1764
+ encoding: "utf8",
1765
+ env: {
1766
+ ...process.env,
1767
+ LC_ALL: "C"
1768
+ }
1769
+ })).stdout;
1770
+ } catch (error) {
1771
+ throw unsafeFilesystemEntry(`Local runtime could not inspect macOS access controls: ${path}`, error);
1772
+ }
1773
+ for (const line of listing.split("\n")) {
1774
+ if (!/^\s*\d+:/.test(line)) continue;
1775
+ if (line.match(/^\s*\d+:\s+.+\s+(allow|deny)\s+\S.*$/)?.[1] !== "deny") throw unsafeFilesystemEntry(`Local runtime path contains a permissive macOS access control list: ${path}`);
1776
+ }
1777
+ }
1778
+ async function acquireLocalDatabase(paths, sourceRoot, appId) {
1779
+ requireNoFollowSupport();
1780
+ const canonicalSourceRoot = await realpath(sourceRoot);
1781
+ const createdDirectories = await ensureSafeDirectoryTree(paths.root, canonicalSourceRoot, true);
1782
+ const prepared = await prepareDedicatedRoot(paths.root, canonicalSourceRoot, appId, createdDirectories);
1783
+ let db;
1784
+ try {
1785
+ db = await getDatabase({
1786
+ type: "sqlite",
1787
+ url: paths.database,
1788
+ secureFile: {
1789
+ driver: "node:sqlite",
1790
+ custody: "trusted-parent",
1791
+ root: paths.root
1792
+ }
1793
+ });
1794
+ await enforcePrivateDatabaseMode(paths.database, canonicalSourceRoot);
1795
+ } catch (error) {
1796
+ if (db) throw await closeDatabaseAfterFailure(db, error);
1797
+ if (prepared.pendingMarkerCreatedByAttempt) {
1798
+ await removeMarkerIfPresent(prepared.pendingMarker, canonicalSourceRoot);
1799
+ await removeCreatedDirectories(createdDirectories);
1800
+ }
1801
+ throw error;
1802
+ }
1803
+ if (!db) throw new Error("Local runtime database acquisition returned no database.");
1804
+ try {
1805
+ await publishRootMarker(prepared.finalMarker, canonicalSourceRoot);
1806
+ await removeMarkerIfPresent(prepared.pendingMarker, canonicalSourceRoot);
1807
+ } catch (error) {
1808
+ throw await closeDatabaseAfterFailure(db, error);
1809
+ }
1810
+ return {
1811
+ canonicalSourceRoot,
1812
+ db
1813
+ };
1814
+ }
1815
+ async function enforcePrivateDatabaseMode(path, canonicalSourceRoot) {
1816
+ const database = await openSafeFile(path, constants.O_RDWR, 384, "Local application database", canonicalSourceRoot);
1817
+ try {
1818
+ await database.chmod(384);
1819
+ await database.sync();
1820
+ } finally {
1821
+ await database.close();
1822
+ }
1823
+ }
1824
+ async function closeDatabaseAfterFailure(db, primaryFailure) {
1825
+ try {
1826
+ await db.close?.();
1827
+ } catch (cleanupFailure) {
1828
+ return preservePrimaryFailure(primaryFailure, cleanupFailure, "databaseCleanupError", "Local runtime initialization and database cleanup both failed.");
1829
+ }
1830
+ return primaryFailure;
1831
+ }
1832
+ function migrationFailed(cause) {
1833
+ const failure = new LocalRuntimeError("migration_failed", MIGRATION_FAILED_MESSAGE);
1834
+ Object.defineProperty(failure, "cause", {
1835
+ value: cause,
1836
+ configurable: true
1837
+ });
1838
+ return failure;
1839
+ }
1840
+ function preservePrimaryFailure(primaryFailure, secondaryFailure, secondaryProperty, aggregateMessage) {
1841
+ if (primaryFailure instanceof Error) {
1842
+ try {
1843
+ Object.defineProperty(primaryFailure, secondaryProperty, {
1844
+ value: secondaryFailure,
1845
+ configurable: true
1846
+ });
1847
+ } catch {}
1848
+ return primaryFailure;
1849
+ }
1850
+ return new AggregateError([primaryFailure, secondaryFailure], aggregateMessage);
1851
+ }
1852
+ async function prepareLocalFilesystem(paths, canonicalSourceRoot) {
1853
+ await ensureSafeDirectoryTree(paths.assets, canonicalSourceRoot);
1854
+ await ensureSafeDirectoryTree(paths.secrets, canonicalSourceRoot);
1855
+ await ensureApplicationSecret(paths.applicationSecret, canonicalSourceRoot);
1856
+ }
1857
+ async function prepareDedicatedRoot(path, canonicalSourceRoot, appId, createdDirectories) {
1858
+ const finalMarker = resolve(path, `${ROOT_MARKER_PREFIX}${appId}`);
1859
+ const pendingMarker = resolve(path, `${ROOT_MARKER_PREFIX}${appId}.pending`);
1860
+ const entries = await readdir(path);
1861
+ const hasFinalMarker = entries.includes(finalMarker.slice(path.length + 1));
1862
+ const hasPendingMarker = entries.includes(pendingMarker.slice(path.length + 1));
1863
+ if (hasFinalMarker) {
1864
+ await validateRootMarker(finalMarker, canonicalSourceRoot);
1865
+ if (hasPendingMarker) await validateRootMarker(pendingMarker, canonicalSourceRoot);
1866
+ return {
1867
+ createdDirectories,
1868
+ pendingMarker: hasPendingMarker ? pendingMarker : null,
1869
+ pendingMarkerCreatedByAttempt: false,
1870
+ finalMarker
1871
+ };
1872
+ }
1873
+ if (hasPendingMarker) {
1874
+ await validateRootMarker(pendingMarker, canonicalSourceRoot);
1875
+ const permittedInterruptedEntries = /* @__PURE__ */ new Set([
1876
+ pendingMarker.slice(path.length + 1),
1877
+ DATABASE_FILE_NAME,
1878
+ `${DATABASE_FILE_NAME}-shm`,
1879
+ `${DATABASE_FILE_NAME}-wal`
1880
+ ]);
1881
+ if (entries.some((entry) => !permittedInterruptedEntries.has(entry))) throw unsafeFilesystemEntry("Interrupted local application root contains unexpected artifacts.");
1882
+ return {
1883
+ createdDirectories,
1884
+ pendingMarker,
1885
+ pendingMarkerCreatedByAttempt: false,
1886
+ finalMarker
1887
+ };
1888
+ }
1889
+ if (entries.length > 0) throw unsafeFilesystemEntry("Existing local application data root is populated but has no valid ownership marker.");
1890
+ return {
1891
+ createdDirectories,
1892
+ pendingMarker,
1893
+ pendingMarkerCreatedByAttempt: await publishRootMarker(pendingMarker, canonicalSourceRoot),
1894
+ finalMarker
1895
+ };
1896
+ }
1897
+ async function acquireInitializationLock(root) {
1898
+ const currentUid = process.getuid?.();
1899
+ if (currentUid === void 0) throw unsafeFilesystemEntry("Local runtime initialization locks require a numeric current-user identity.");
1900
+ const lockRoot = resolve(await realpath("/tmp"), `.smrt-${currentUid}`);
1901
+ try {
1902
+ await mkdir(lockRoot, { mode: 448 });
1903
+ } catch (error) {
1904
+ if (!isFileAlreadyExists(error)) throw error;
1905
+ }
1906
+ const lockRootDetails = await lstat(lockRoot);
1907
+ if (!lockRootDetails.isDirectory() || lockRootDetails.isSymbolicLink() || lockRootDetails.uid !== currentUid || (lockRootDetails.mode & 511) !== 448) throw unsafeFilesystemEntry(`Local runtime lock directory must be current-user-owned with mode 0700: ${lockRoot}`);
1908
+ const custodyRoot = resolve(lockRoot, createHash("sha256").update(normalizeFilesystemIdentity(root)).digest("hex").slice(0, 32));
1909
+ try {
1910
+ await mkdir(custodyRoot, { mode: 448 });
1911
+ } catch (error) {
1912
+ if (!isFileAlreadyExists(error)) throw error;
1913
+ }
1914
+ return acquireInitializationDatabaseLease(resolve(custodyRoot, "initialization.sqlite"), custodyRoot);
1915
+ }
1916
+ async function acquireInitializationDatabaseLease(path, custodyRoot) {
1917
+ const worker = new Worker(INITIALIZATION_LOCK_WORKER, {
1918
+ eval: true,
1919
+ workerData: {
1920
+ path,
1921
+ custodyRoot,
1922
+ timeout: INITIALIZATION_LOCK_TIMEOUT_MS,
1923
+ sqlModuleUrl: import.meta.resolve("@happyvertical/sql")
1924
+ }
1925
+ });
1926
+ const acquisitionFailure = (cause) => {
1927
+ const failure = new LocalRuntimeError("invalid_configuration", "Could not acquire the local runtime initialization lease within two minutes.");
1928
+ Object.defineProperty(failure, "cause", { value: cause });
1929
+ return failure;
1930
+ };
1931
+ let workerError;
1932
+ const exited = new Promise((_resolve, reject) => {
1933
+ worker.once("error", (error) => {
1934
+ workerError = error;
1935
+ });
1936
+ worker.once("exit", (code) => {
1937
+ reject(acquisitionFailure(workerError ?? /* @__PURE__ */ new Error(`Initialization lock worker exited ${code}.`)));
1938
+ });
1939
+ });
1940
+ const acquired = new Promise((resolvePromise, reject) => {
1941
+ const onMessage = (message) => {
1942
+ if (message.type === "acquired") {
1943
+ worker.off("message", onMessage);
1944
+ resolvePromise();
1945
+ } else if (message.type === "error") {
1946
+ worker.off("message", onMessage);
1947
+ reject(acquisitionFailure(new Error(message.message)));
1948
+ }
1949
+ };
1950
+ worker.on("message", onMessage);
1951
+ });
1952
+ await Promise.race([acquired, exited]);
1953
+ let released = false;
1954
+ return { release: async () => {
1955
+ if (released) return;
1956
+ released = true;
1957
+ const releaseCompleted = new Promise((resolvePromise, reject) => {
1958
+ const onMessage = (message) => {
1959
+ if (message.type !== "released") return;
1960
+ worker.off("message", onMessage);
1961
+ if (message.error) reject(new Error(message.error));
1962
+ else resolvePromise();
1963
+ };
1964
+ worker.on("message", onMessage);
1965
+ worker.postMessage({ type: "release" });
1966
+ });
1967
+ try {
1968
+ await Promise.race([releaseCompleted, exited]);
1969
+ } finally {
1970
+ await worker.terminate();
1971
+ }
1972
+ } };
1973
+ }
1974
+ async function ensureSafeDirectoryTree(path, canonicalSourceRoot, dedicatedRoot = false) {
1975
+ const components = absolutePathComponents(path);
1976
+ const createdDirectories = [];
1977
+ for (const component of components) {
1978
+ let created = false;
1979
+ try {
1980
+ const details = await lstat(component);
1981
+ if (details.isSymbolicLink() || !details.isDirectory()) throw unsafeFilesystemEntry(`Local runtime path component must be a real directory: ${component}`);
1982
+ } catch (error) {
1983
+ if (!isMissingFile(error)) throw error;
1984
+ try {
1985
+ await mkdir(component, { mode: 448 });
1986
+ created = true;
1987
+ createdDirectories.push(component);
1988
+ } catch (mkdirError) {
1989
+ if (!isFileAlreadyExists(mkdirError)) throw mkdirError;
1990
+ }
1991
+ }
1992
+ const directory = await openSafeDirectory(component, canonicalSourceRoot);
1993
+ try {
1994
+ await assertCanonicalEntry(component, canonicalSourceRoot);
1995
+ if (component === path && dedicatedRoot && !created) await assertExistingDedicatedRoot(directory, component, canonicalSourceRoot);
1996
+ if (created || component === path && !dedicatedRoot) await directory.chmod(448);
1997
+ } finally {
1998
+ await directory.close();
1999
+ }
2000
+ }
2001
+ await validateExistingPathChain(path, canonicalSourceRoot, "directory");
2002
+ return createdDirectories;
2003
+ }
2004
+ async function assertExistingDedicatedRoot(directory, path, canonicalSourceRoot) {
2005
+ const canonical = await realpath(path);
2006
+ if (isInside(canonicalSourceRoot, canonical) || isInside(canonical, canonicalSourceRoot)) throw unsafeFilesystemEntry("Local application data must be a dedicated directory that does not overlap the canonical source tree.");
2007
+ const details = await directory.stat();
2008
+ const currentUid = process.getuid?.();
2009
+ if (currentUid === void 0 || details.uid !== currentUid || (details.mode & 511) !== 448) throw unsafeFilesystemEntry(`Existing local application data root must already be owned by the current user with mode 0700: ${path}`);
2010
+ }
2011
+ async function publishRootMarker(path, canonicalSourceRoot) {
2012
+ let marker;
2013
+ let created = false;
2014
+ try {
2015
+ marker = await openSafeFile(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 384, "Local application root marker", canonicalSourceRoot);
2016
+ created = true;
2017
+ await marker.sync();
2018
+ } catch (error) {
2019
+ if (!isFileAlreadyExists(error)) throw error;
2020
+ } finally {
2021
+ await marker?.close();
2022
+ }
2023
+ await validateRootMarker(path, canonicalSourceRoot);
2024
+ await syncDirectory(resolve(path, ".."), canonicalSourceRoot);
2025
+ return created;
2026
+ }
2027
+ async function validateRootMarker(path, canonicalSourceRoot) {
2028
+ await validateExistingPathChain(path, canonicalSourceRoot, "file");
2029
+ const marker = await openSafeFile(path, constants.O_RDONLY, 384, "Local application root marker", canonicalSourceRoot);
2030
+ try {
2031
+ const details = await marker.stat();
2032
+ const currentUid = process.getuid?.();
2033
+ if (currentUid === void 0 || details.uid !== currentUid || (details.mode & 511) !== 384 || details.size !== 0) throw unsafeFilesystemEntry(`Local application root marker must be an empty current-user-owned mode-0600 file: ${path}`);
2034
+ } finally {
2035
+ await marker.close();
2036
+ }
2037
+ }
2038
+ async function removeMarkerIfPresent(path, canonicalSourceRoot) {
2039
+ if (path === null) return;
2040
+ try {
2041
+ await validateRootMarker(path, canonicalSourceRoot);
2042
+ await unlink(path);
2043
+ await syncDirectory(resolve(path, ".."), canonicalSourceRoot);
2044
+ } catch (error) {
2045
+ if (!isMissingFile(error) && !hasMissingFileCause(error)) throw error;
2046
+ }
2047
+ }
2048
+ async function removeCreatedDirectories(paths) {
2049
+ for (const path of paths.toReversed()) try {
2050
+ await rmdir(path);
2051
+ } catch (error) {
2052
+ if (!isMissingFile(error) && !isDirectoryNotEmpty(error)) throw error;
2053
+ }
2054
+ }
2055
+ async function ensureApplicationSecret(path, canonicalSourceRoot) {
2056
+ const directoryPath = resolve(path, "..");
2057
+ const temporaryPath = resolve(directoryPath, `.${SECRET_FILE_NAME}.tmp-${randomBytes(16).toString("hex")}`);
2058
+ let temporary;
2059
+ let failure;
2060
+ try {
2061
+ temporary = await openSafeFile(temporaryPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 384, "Temporary application secret", canonicalSourceRoot);
2062
+ await temporary.writeFile(`${randomBytes(32).toString("base64url")}
2063
+ `, "utf8");
2064
+ await temporary.sync();
2065
+ await temporary.close();
2066
+ temporary = void 0;
2067
+ try {
2068
+ await link(temporaryPath, path);
2069
+ } catch (error) {
2070
+ if (!isFileAlreadyExists(error)) throw error;
2071
+ }
2072
+ await validateApplicationSecret(path, canonicalSourceRoot);
2073
+ await syncDirectory(directoryPath, canonicalSourceRoot);
2074
+ } catch (error) {
2075
+ failure = error;
2076
+ }
2077
+ try {
2078
+ await temporary?.close();
2079
+ } catch (error) {
2080
+ failure ??= error;
2081
+ }
2082
+ try {
2083
+ await unlink(temporaryPath);
2084
+ } catch (error) {
2085
+ if (!isMissingFile(error)) failure ??= error;
2086
+ }
2087
+ if (failure !== void 0) throw failure;
2088
+ await removeStaleSecretTemps(directoryPath);
2089
+ }
2090
+ async function validateApplicationSecret(path, canonicalSourceRoot) {
2091
+ await validateExistingPathChain(path, canonicalSourceRoot, "file");
2092
+ const file = await openSafeFile(path, constants.O_RDONLY, 384, "Application secret", canonicalSourceRoot);
2093
+ try {
2094
+ const details = await file.stat();
2095
+ const currentUid = process.getuid?.();
2096
+ const secret = await file.readFile("utf8");
2097
+ await validateExistingPathChain(path, canonicalSourceRoot, "file");
2098
+ if (currentUid === void 0 || details.uid !== currentUid || (details.mode & 511) !== 384 || details.size !== 44 || !/^[A-Za-z0-9_-]{43}\n$/.test(secret)) throw unsafeFilesystemEntry("Application secret must be a complete current-user-owned mode-0600 value in the expected format.");
2099
+ return secret;
2100
+ } finally {
2101
+ await file.close();
2102
+ }
2103
+ }
2104
+ async function syncDirectory(path, canonicalSourceRoot) {
2105
+ const directory = await openSafeDirectory(path, canonicalSourceRoot);
2106
+ try {
2107
+ await directory.sync();
2108
+ } finally {
2109
+ await directory.close();
2110
+ }
2111
+ }
2112
+ async function removeStaleSecretTemps(directoryPath) {
2113
+ const prefix = `.${SECRET_FILE_NAME}.tmp-`;
2114
+ for (const entry of await readdir(directoryPath)) {
2115
+ if (!entry.startsWith(prefix)) continue;
2116
+ try {
2117
+ await unlink(resolve(directoryPath, entry));
2118
+ } catch (error) {
2119
+ if (!isMissingFile(error)) throw error;
2120
+ }
2121
+ }
2122
+ }
2123
+ async function openSafeDirectory(path, canonicalSourceRoot) {
2124
+ let directory;
2125
+ try {
2126
+ directory = await open(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
2127
+ await validateOpenHandlePath(directory, path, "directory", canonicalSourceRoot);
2128
+ return directory;
2129
+ } catch (error) {
2130
+ await directory?.close();
2131
+ if (error instanceof LocalRuntimeError) throw error;
2132
+ throw unsafeFilesystemEntry(`Could not safely open local runtime directory: ${path}`, error);
2133
+ }
2134
+ }
2135
+ async function openSafeFile(path, flags, mode, label, canonicalSourceRoot) {
2136
+ let file;
2137
+ try {
2138
+ file = await open(path, flags | constants.O_NOFOLLOW, mode);
2139
+ await validateOpenHandlePath(file, path, "file", canonicalSourceRoot);
2140
+ return file;
2141
+ } catch (error) {
2142
+ await file?.close();
2143
+ if (error instanceof LocalRuntimeError || isFileAlreadyExists(error)) throw error;
2144
+ throw unsafeFilesystemEntry(`${label} could not be opened without following symbolic links.`, error);
2145
+ }
2146
+ }
2147
+ async function validateOpenHandlePath(handle, path, expectedType, canonicalSourceRoot) {
2148
+ const opened = await handle.stat();
2149
+ const current = await lstat(path);
2150
+ const expectedDirectory = expectedType === "directory";
2151
+ if (current.isSymbolicLink() || (expectedDirectory ? !opened.isDirectory() : !opened.isFile()) || (expectedDirectory ? !current.isDirectory() : !current.isFile()) || opened.dev !== current.dev || opened.ino !== current.ino) throw unsafeFilesystemEntry(`Local runtime ${expectedType} changed while it was being opened: ${path}`);
2152
+ await validateExistingPathChain(path, canonicalSourceRoot, expectedType);
2153
+ const afterValidation = await lstat(path);
2154
+ if (afterValidation.isSymbolicLink() || opened.dev !== afterValidation.dev || opened.ino !== afterValidation.ino) throw unsafeFilesystemEntry(`Local runtime ${expectedType} changed during validation: ${path}`);
2155
+ }
2156
+ async function validateExistingPathChain(path, canonicalSourceRoot, leafType) {
2157
+ const components = absolutePathComponents(path);
2158
+ for (const [index, component] of components.entries()) {
2159
+ const details = await lstat(component);
2160
+ const expectedFile = index === components.length - 1 && leafType === "file";
2161
+ if (details.isSymbolicLink() || (expectedFile ? !details.isFile() : !details.isDirectory())) throw unsafeFilesystemEntry(`Local runtime path contains an unsafe component: ${component}`);
2162
+ await assertCanonicalEntry(component, canonicalSourceRoot);
2163
+ }
2164
+ }
2165
+ async function assertCanonicalEntry(path, canonicalSourceRoot) {
2166
+ const canonical = await realpath(path);
2167
+ if (!sameFilesystemPath(canonical, path)) throw unsafeFilesystemEntry(`Local runtime path must not be redirected: ${path}`);
2168
+ if (isInside(canonicalSourceRoot, canonical)) throw unsafeFilesystemEntry("Local application data must remain outside the canonical source tree.");
2169
+ }
2170
+ function absolutePathComponents(path) {
2171
+ const absolute = resolve(path);
2172
+ const root = parse(absolute).root;
2173
+ const parts = absolute.slice(root.length).split(sep).filter(Boolean);
2174
+ const components = [root];
2175
+ for (const part of parts) components.push(resolve(components.at(-1), part));
2176
+ return components;
2177
+ }
2178
+ function requireNoFollowSupport() {
2179
+ if (typeof constants.O_NOFOLLOW !== "number" || constants.O_NOFOLLOW === 0 || typeof constants.O_DIRECTORY !== "number" || constants.O_DIRECTORY === 0) throw unsafeFilesystemEntry("This platform cannot safely initialize local storage without no-follow filesystem support.");
2180
+ }
2181
+ function sameFilesystemPath(left, right, runtimePlatform = platform()) {
2182
+ return normalizeFilesystemIdentity(left, runtimePlatform) === normalizeFilesystemIdentity(right, runtimePlatform);
2183
+ }
2184
+ function usesCaseInsensitivePathGuards(runtimePlatform) {
2185
+ return runtimePlatform === "win32" || runtimePlatform === "darwin";
2186
+ }
2187
+ function normalizeFilesystemIdentity(value, runtimePlatform = platform()) {
2188
+ const normalized = resolve(value);
2189
+ return usesCaseInsensitivePathGuards(runtimePlatform) ? normalized.toLowerCase() : normalized;
2190
+ }
2191
+ function unsafeFilesystemEntry(message, cause) {
2192
+ const error = new LocalRuntimeError("invalid_configuration", message);
2193
+ if (cause !== void 0) Object.defineProperty(error, "cause", { value: cause });
2194
+ return error;
2195
+ }
2196
+ function validateBootstrapTtl(value) {
2197
+ if (!Number.isInteger(value) || value < 1 || value > MAX_BOOTSTRAP_TTL_SECONDS) throw new LocalRuntimeError("invalid_configuration", `Owner bootstrap TTL must be an integer from 1 to ${MAX_BOOTSTRAP_TTL_SECONDS} seconds.`);
2198
+ return value;
2199
+ }
2200
+ function validateSessionTtl(value) {
2201
+ if (!Number.isInteger(value) || value < 1) throw new LocalRuntimeError("invalid_configuration", "Session TTL must be a whole number of seconds greater than or equal to 1.");
2202
+ return value;
2203
+ }
2204
+ function validateOwnerInput(input) {
2205
+ if (!input.token || !input.name.trim() || !input.email.trim()) throw new LocalRuntimeError("bootstrap_invalid", "Owner bootstrap requires a token, name, and email address.");
2206
+ }
2207
+ function normalizeLoopbackHost(value) {
2208
+ const host = value.trim().toLowerCase().replace(/^\[|\]$/g, "");
2209
+ if (host === "::1") return host;
2210
+ const octets = host.split(".");
2211
+ if (octets.length === 4 && octets.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255) && Number(octets[0]) === 127) return host;
2212
+ throw new LocalRuntimeError("unsafe_public_exposure", "Owner bootstrap is restricted to a loopback IP literal. Use ::1 or 127.0.0.0/8.");
2213
+ }
2214
+ function validateApplicationId(value) {
2215
+ const appId = value.trim().toLowerCase();
2216
+ if (!/^[a-z0-9][a-z0-9.-]{0,62}$/.test(appId)) throw new LocalRuntimeError("invalid_configuration", "appId must contain only lowercase letters, digits, dots, or hyphens.");
2217
+ return appId;
2218
+ }
2219
+ function encodeApplicationId(value) {
2220
+ const source = value.trim().toLowerCase();
2221
+ if (/^[a-z0-9][a-z0-9.-]{0,62}$/.test(source)) return source;
2222
+ const digest = createHash("sha256").update(value.trim()).digest("hex").slice(0, 10);
2223
+ return validateApplicationId(`${source.replace(/[^a-z0-9.-]+/g, "-").replace(/^[^a-z0-9]+|[^a-z0-9]+$/g, "").slice(0, 52).replace(/[^a-z0-9]+$/g, "") || "app"}-${digest}`);
2224
+ }
2225
+ function defaultApplicationDataRoot(runtimePlatform, runtimeHome, env) {
2226
+ if (runtimePlatform === "darwin") return resolve(runtimeHome, "Library", "Application Support");
2227
+ if (runtimePlatform === "win32") return resolve(env.LOCALAPPDATA ?? env.APPDATA ?? runtimeHome);
2228
+ return resolve(env.XDG_DATA_HOME ?? resolve(runtimeHome, ".local", "share"));
2229
+ }
2230
+ function isInside(parent, child, runtimePlatform = platform()) {
2231
+ const path = relative(normalizeFilesystemIdentity(parent, runtimePlatform), normalizeFilesystemIdentity(child, runtimePlatform));
2232
+ return path === "" || !path.startsWith("..") && !isAbsolute(path);
2233
+ }
2234
+ function parseStoredDate(value) {
2235
+ if (!(typeof value === "string" || value instanceof Date)) return null;
2236
+ const date = value instanceof Date ? value : new Date(value);
2237
+ return Number.isNaN(date.getTime()) ? null : date;
2238
+ }
2239
+ function requireId(value, label) {
2240
+ if (typeof value !== "string" || value.length === 0) throw new Error(`${label} was not persisted with an id.`);
2241
+ return value;
2242
+ }
2243
+ function isMissingFile(error) {
2244
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2245
+ }
2246
+ function hasMissingFileCause(error) {
2247
+ return typeof error === "object" && error !== null && "cause" in error && isMissingFile(error.cause);
2248
+ }
2249
+ function isFileAlreadyExists(error) {
2250
+ return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
2251
+ }
2252
+ function isDirectoryNotEmpty(error) {
2253
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOTEMPTY";
2254
+ }
2255
+ //#endregion
2256
+ export { initializeDeployedApplicationRuntime as A, lockOwnerPid as C, reclaimStaleRecord as D, RECLAIM_MUTEX_FILE as E, resolveApplicationId as M, runtimeConfigurationFingerprint as N, DeployedRuntimeCleanupError as O, isStaleOperationLock as S, withOperationLock as T, projectRuntimeDiagnostics as _, prepareLocalDatabaseStorage as a, findInstalledPackage as b, validateLocalDatabaseStorage as c, prepareApplicationStateRoot as d, resolveApplicationStateRoot as f, RUNTIME_WORKER_FRESHNESS_SECONDS as g, RUNTIME_DIAGNOSTICS_SCHEMA_VERSION as h, initializeLocalApplicationRuntime as i, isSessionTenantAuthorized as j, DeployedRuntimeError as k, acquireWriterLease as l, MAX_RUNTIME_DIAGNOSTIC_TOOLS as m, MIGRATION_FAILED_MESSAGE as n, resolveLocalRuntimePaths as o, MAX_RUNTIME_DIAGNOSTIC_ERRORS as p, encodeApplicationId as r, validateApplicationId as s, LocalRuntimeError as t, readActiveWriterLease as u, PROVIDER_READINESS_SETTINGS as v, processExists as w, resolveReadinessModule as x, createProviderReadinessProbe as y };
2257
+
2258
+ //# sourceMappingURL=src-DZGOVGsU.js.map