@happyvertical/smrt-app-runtime 0.53.3 → 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,704 @@
1
+ import { A as initializeDeployedApplicationRuntime, M as resolveApplicationId, N as runtimeConfigurationFingerprint, _ as projectRuntimeDiagnostics, i as initializeLocalApplicationRuntime, j as isSessionTenantAuthorized, o as resolveLocalRuntimePaths, s as validateApplicationId } from "./chunks/src-DZGOVGsU.js";
2
+ import { loadConfig, resolveApplicationRuntime, resolveConfiguredApplicationRuntime } from "@happyvertical/smrt-config";
3
+ import { enableTenancy, getCurrentTenant, isTenancyEnabled } from "@happyvertical/smrt-tenancy";
4
+ import { PermissionResolver, RoleCollection, SessionService, TenantCollection, TenantStatus, getCurrentSessionPermissionContext, getRequestScopedDatabase, withPrincipalPermissionContext, withSessionPermissionContext } from "@happyvertical/smrt-users";
5
+ import { getDatabase } from "@happyvertical/sql";
6
+ import { fail, json, redirect } from "@sveltejs/kit";
7
+ //#region src/sveltekit/handlers.ts
8
+ var RUNTIME_DIAGNOSTICS_READ_PERMISSION = "runtime_diagnostics.read";
9
+ function createRuntimeHealthHandler(runtime) {
10
+ return async () => json(await runtime.health());
11
+ }
12
+ function resolveInputValue(value) {
13
+ return typeof value === "function" ? value() : value;
14
+ }
15
+ function createRuntimeDiagnosticsHandler(options) {
16
+ const runtime = options.runtime;
17
+ const readDiagnostics = options.readDiagnostics ?? (runtime ? () => runtime.readDiagnostics({
18
+ toolNames: resolveInputValue(options.toolNames) ?? [],
19
+ observedAt: options.now?.() ?? /* @__PURE__ */ new Date(),
20
+ schemaStatus: resolveInputValue(options.schemaStatus) ?? "unknown",
21
+ migrationStatus: resolveInputValue(options.migrationStatus) ?? "unknown",
22
+ workerHeartbeatAt: options.workerHeartbeatAt?.() ?? null,
23
+ recentErrors: options.recentErrors?.() ?? []
24
+ }) : void 0);
25
+ const resolveRoleSlug = options.resolveRoleSlug ?? (runtime ? async (roleId) => {
26
+ const config = runtime.classOptions("Role");
27
+ const requestDb = getRequestScopedDatabase();
28
+ return (await (await RoleCollection.create(requestDb ? {
29
+ ...config,
30
+ db: requestDb
31
+ } : config)).findById(roleId))?.slug ?? null;
32
+ } : void 0);
33
+ if (!readDiagnostics || !resolveRoleSlug) throw new TypeError("createRuntimeDiagnosticsHandler requires `runtime`, or both `readDiagnostics` and `resolveRoleSlug`.");
34
+ const resolveLocals = options.resolveLocals ?? ((event) => event.locals);
35
+ return async (event) => {
36
+ const locals = resolveLocals(event);
37
+ const principal = authenticatedPrincipal(locals);
38
+ if (!principal) return stableError(401, "authentication_required");
39
+ let authorized = locals.permissions?.includes(RUNTIME_DIAGNOSTICS_READ_PERMISSION) === true;
40
+ if (!authorized) try {
41
+ authorized = await resolveRoleSlug(principal.roleId) === "owner";
42
+ } catch {
43
+ authorized = false;
44
+ }
45
+ if (!authorized) return stableError(403, "authorization_denied");
46
+ try {
47
+ return json(await readDiagnostics());
48
+ } catch {
49
+ return stableError(503, "diagnostics_unavailable");
50
+ }
51
+ };
52
+ }
53
+ function authenticatedPrincipal(locals) {
54
+ const userId = locals?.user?.id;
55
+ const tenantId = locals?.tenantId;
56
+ const sessionId = locals?.sessionId;
57
+ const membership = locals?.membership;
58
+ if (typeof userId !== "string" || !userId || typeof tenantId !== "string" || !tenantId || typeof sessionId !== "string" || !sessionId || !membership || membership.userId !== userId || membership.tenantId !== tenantId || typeof membership.roleId !== "string" || !membership.roleId) return null;
59
+ try {
60
+ if (membership.isActive?.() !== true) return null;
61
+ } catch {
62
+ return null;
63
+ }
64
+ return { roleId: membership.roleId };
65
+ }
66
+ function stableError(status, code) {
67
+ return json({
68
+ schemaVersion: 1,
69
+ error: { code }
70
+ }, { status });
71
+ }
72
+ function createSessionLayoutLoad() {
73
+ return ({ locals }) => {
74
+ const value = locals;
75
+ return { session: {
76
+ authenticated: Boolean(value.user),
77
+ activeTenantId: value.tenantId ?? null,
78
+ selectedTenantSlug: value.selectedTenantSlug ?? null
79
+ } };
80
+ };
81
+ }
82
+ //#endregion
83
+ //#region src/sveltekit/owner-setup.ts
84
+ var OWNER_SETUP_FIELDS = Object.freeze({
85
+ token: "token",
86
+ name: "name",
87
+ email: "email",
88
+ tenantName: "tenantName"
89
+ });
90
+ var DEFAULT_OWNER_SETUP_MESSAGES = Object.freeze({
91
+ setup_disabled: "Local owner setup is disabled.",
92
+ setup_unavailable: "Local owner setup is available only from this device.",
93
+ setup_invalid_input: "Name, email, and a valid setup token are required.",
94
+ setup_invalid: "The setup invitation is invalid, expired, or already used. Run pnpm app:stop, pnpm app:recover, pnpm app:start, then pnpm app:open."
95
+ });
96
+ function createOwnerSetupPage(runtime, options = {}) {
97
+ const redirectTo = options.redirectTo ?? "/";
98
+ const messages = {
99
+ ...DEFAULT_OWNER_SETUP_MESSAGES,
100
+ ...options.messages
101
+ };
102
+ const failure = (status, code) => fail(status, {
103
+ code,
104
+ message: messages[code]
105
+ });
106
+ const resolveUser = options.resolveUser ?? ((event) => event.locals.user);
107
+ const load = async (event) => {
108
+ if (resolveUser(event)) redirect(303, redirectTo);
109
+ if ((await runtime.resolvedRuntime()).profile !== "local" || !isLoopbackRequest(event)) return {
110
+ available: false,
111
+ token: ""
112
+ };
113
+ const available = (await (await runtime.localRuntime()).diagnostics()).bootstrap.status !== "claimed";
114
+ return {
115
+ available,
116
+ token: available ? event.url.searchParams.get(OWNER_SETUP_FIELDS.token) || "" : ""
117
+ };
118
+ };
119
+ const claim = async (event) => {
120
+ if ((await runtime.resolvedRuntime()).profile !== "local") return failure(404, "setup_disabled");
121
+ if (!isLoopbackRequest(event)) return failure(403, "setup_unavailable");
122
+ let form;
123
+ try {
124
+ form = await event.request.formData();
125
+ } catch {
126
+ return failure(400, "setup_invalid_input");
127
+ }
128
+ const field = (name2) => {
129
+ const value = form.get(name2);
130
+ return typeof value === "string" ? value : "";
131
+ };
132
+ const token = field(OWNER_SETUP_FIELDS.token);
133
+ const name = field(OWNER_SETUP_FIELDS.name);
134
+ const email = field(OWNER_SETUP_FIELDS.email);
135
+ const tenantName = field(OWNER_SETUP_FIELDS.tenantName).trim();
136
+ if (!token || !name.trim() || !email.includes("@")) return failure(400, "setup_invalid_input");
137
+ const local = await runtime.localRuntime();
138
+ let result;
139
+ try {
140
+ result = await local.claimOwner({
141
+ token,
142
+ name,
143
+ email,
144
+ ...tenantName ? { tenantName } : {},
145
+ userAgent: event.request.headers.get("user-agent") || void 0,
146
+ ipAddress: event.getClientAddress()
147
+ });
148
+ } catch {
149
+ return failure(400, "setup_invalid");
150
+ }
151
+ const cookie = runtime.sessionCookie;
152
+ event.cookies.set(cookie.name, result.sessionId, {
153
+ path: cookie.path,
154
+ ...cookie.domain ? { domain: cookie.domain } : {},
155
+ httpOnly: true,
156
+ secure: cookie.secure ?? event.url.protocol === "https:",
157
+ sameSite: cookie.sameSite,
158
+ maxAge: cookie.maxAgeSeconds
159
+ });
160
+ try {
161
+ await options.onOwnerClaimed?.(result, event);
162
+ } catch {}
163
+ redirect(303, redirectTo);
164
+ };
165
+ return Object.freeze({
166
+ load,
167
+ actions: Object.freeze({ default: claim })
168
+ });
169
+ }
170
+ function isLoopbackRequest(event) {
171
+ let address;
172
+ try {
173
+ address = event.getClientAddress();
174
+ } catch {
175
+ return false;
176
+ }
177
+ return isLoopbackAddress(address) && isLoopbackHostname(event.url.hostname);
178
+ }
179
+ function isLoopbackAddress(value) {
180
+ if (typeof value !== "string") return false;
181
+ let host = value.trim().toLowerCase().replace(/^\[|\]$/g, "");
182
+ if (host === "::1" || host === "0:0:0:0:0:0:0:1") return true;
183
+ if (host.startsWith("::ffff:")) host = host.slice(7);
184
+ const octets = host.split(".");
185
+ return octets.length === 4 && octets.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255) && Number(octets[0]) === 127;
186
+ }
187
+ function isLoopbackHostname(hostname) {
188
+ const host = hostname.trim().toLowerCase().replace(/\.$/, "");
189
+ return host === "localhost" || isLoopbackAddress(host);
190
+ }
191
+ //#endregion
192
+ //#region src/sveltekit/tenancy.ts
193
+ var ROOT_LIKE_HOSTS = /* @__PURE__ */ new Set([
194
+ "localhost",
195
+ "127.0.0.1",
196
+ "::1",
197
+ "[::1]"
198
+ ]);
199
+ var RESERVED_SUBDOMAINS = /* @__PURE__ */ new Set([
200
+ "www",
201
+ "api",
202
+ "app",
203
+ "admin"
204
+ ]);
205
+ var NO_SELECTION = Object.freeze({
206
+ tenantId: null,
207
+ tenantSlug: null
208
+ });
209
+ function selectTenantSlug(url, baseDomain = process.env.TENANT_BASE_DOMAIN) {
210
+ const hostname = url.hostname.toLowerCase();
211
+ if (ROOT_LIKE_HOSTS.has(hostname) || hostname.startsWith("[") || /^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) return null;
212
+ const normalizedBase = baseDomain?.trim().toLowerCase().replace(/^\.+|\.+$/g, "");
213
+ let candidate;
214
+ if (normalizedBase) {
215
+ if (hostname === normalizedBase) return null;
216
+ const suffix = `.${normalizedBase}`;
217
+ if (!hostname.endsWith(suffix)) return null;
218
+ candidate = hostname.slice(0, -suffix.length).split(".")[0];
219
+ } else {
220
+ const labels = hostname.split(".");
221
+ if (labels.length < 3) return null;
222
+ candidate = labels[0];
223
+ }
224
+ return candidate && !RESERVED_SUBDOMAINS.has(candidate) ? candidate : null;
225
+ }
226
+ function isMissingTenantTable(error) {
227
+ if (!(error instanceof Error)) return false;
228
+ return error.code === "42P01" || error.message.includes("relation \"tenants\" does not exist") || error.message.includes("no such table: tenants");
229
+ }
230
+ function createSubdomainTenantSelector(options) {
231
+ return async (event) => {
232
+ const tenantSlug = selectTenantSlug(event.url, options.baseDomain);
233
+ if (!tenantSlug) return NO_SELECTION;
234
+ let tenantId;
235
+ try {
236
+ const tenant = await (await TenantCollection.create(options.classOptions())).findBySlug(tenantSlug);
237
+ tenantId = tenant?.status === TenantStatus.ACTIVE ? tenant.id ?? null : null;
238
+ } catch (error) {
239
+ if (!isMissingTenantTable(error)) throw error;
240
+ tenantId = null;
241
+ }
242
+ return {
243
+ tenantId,
244
+ tenantSlug
245
+ };
246
+ };
247
+ }
248
+ function normalizeTenantSelection(selection) {
249
+ return {
250
+ tenantId: typeof selection?.tenantId === "string" && selection.tenantId ? selection.tenantId : null,
251
+ tenantSlug: typeof selection?.tenantSlug === "string" && selection.tenantSlug ? selection.tenantSlug : null
252
+ };
253
+ }
254
+ //#endregion
255
+ //#region src/sveltekit/runtime.ts
256
+ var DEFAULT_SESSION_TTL_SECONDS = 10080 * 60;
257
+ function createSmrtSvelteKitRuntime(options = {}) {
258
+ return composeSmrtSvelteKitRuntime(options, {});
259
+ }
260
+ function composeSmrtSvelteKitRuntime(options, seams) {
261
+ const env = options.env ?? process.env;
262
+ const sourceRoot = options.sourceRoot ?? process.cwd();
263
+ const dataDirectory = options.dataDirectory ?? env.SMRT_DATA_DIR;
264
+ const initializeLocal = seams.initializeLocal ?? initializeLocalApplicationRuntime;
265
+ const initializeDeployed = seams.initializeDeployed ?? initializeDeployedApplicationRuntime;
266
+ if (options.enableTenancy !== false && !isTenancyEnabled()) enableTenancy();
267
+ let appId;
268
+ const applicationId = () => {
269
+ appId ??= options.appId ? validateApplicationId(options.appId) : resolveApplicationId({
270
+ sourceRoot,
271
+ explicitId: env.SMRT_APP_ID
272
+ });
273
+ return appId;
274
+ };
275
+ let resolved;
276
+ let resolvedPromise;
277
+ const resolvedRuntime = () => {
278
+ resolvedPromise ??= (async () => {
279
+ const value = typeof options.runtime === "function" ? await options.runtime() : options.runtime ?? await (seams.loadRuntime ?? loadConfiguredRuntime)();
280
+ resolved = value;
281
+ return value;
282
+ })().catch((error) => {
283
+ resolvedPromise = void 0;
284
+ throw error;
285
+ });
286
+ return resolvedPromise;
287
+ };
288
+ const requireResolved = () => {
289
+ if (!resolved) throw new Error("The SMRT application runtime is not resolved yet; await init() first.");
290
+ return resolved;
291
+ };
292
+ const baseDatabaseConfig = () => {
293
+ const runtime = requireResolved();
294
+ if (runtime.profile === "local") return {
295
+ type: "sqlite",
296
+ url: resolveLocalRuntimePaths({
297
+ appId: applicationId(),
298
+ dataDirectory,
299
+ sourceRoot,
300
+ env
301
+ }).database
302
+ };
303
+ const databaseUrl = env.DATABASE_URL;
304
+ if (!databaseUrl) throw new Error(`${runtime.profile} requires DATABASE_URL.`);
305
+ return {
306
+ type: "postgres",
307
+ url: databaseUrl
308
+ };
309
+ };
310
+ const baseClassOptions = (className) => {
311
+ const defaults = { db: baseDatabaseConfig() };
312
+ const override = options.classOverrides?.[className];
313
+ return override ? {
314
+ ...defaults,
315
+ ...override
316
+ } : defaults;
317
+ };
318
+ const rlsRequestDatabase = () => {
319
+ if (getCurrentSessionPermissionContext()?.postgresRls !== true) return void 0;
320
+ return getRequestScopedDatabase() ?? void 0;
321
+ };
322
+ const databaseConfig = () => rlsRequestDatabase() ?? baseDatabaseConfig();
323
+ const classOptions = (className) => {
324
+ const base = baseClassOptions(className);
325
+ if (options.classOverrides?.[className]?.db !== void 0) return base;
326
+ const requestDb = rlsRequestDatabase();
327
+ return requestDb ? {
328
+ ...base,
329
+ db: requestDb
330
+ } : base;
331
+ };
332
+ let localPromise;
333
+ let localLease;
334
+ const localRuntime = async () => {
335
+ const runtime = await resolvedRuntime();
336
+ if (runtime.profile !== "local") throw new Error("Owner bootstrap is available only in the local profile.");
337
+ if (localPromise) return localPromise;
338
+ const bindHost = options.bindHost || env.HOST || (env.NODE_ENV === "development" ? "127.0.0.1" : null);
339
+ if (!bindHost) throw new Error("Local production startup requires an explicit loopback HOST; use pnpm app:start.");
340
+ const id = applicationId();
341
+ localLease ??= options.acquireWriterLease?.({
342
+ appId: id,
343
+ dataDirectory,
344
+ sourceRoot
345
+ });
346
+ localPromise = initializeLocal({
347
+ appId: id,
348
+ dataDirectory,
349
+ sourceRoot,
350
+ env,
351
+ bindHost,
352
+ providers: {
353
+ database: runtime.providers.database,
354
+ authentication: runtime.providers.authentication,
355
+ tenancy: runtime.providers.tenancy,
356
+ assets: runtime.providers.assets,
357
+ secrets: runtime.providers.secrets,
358
+ jobs: runtime.providers.jobs,
359
+ network: runtime.providers.network
360
+ },
361
+ backgroundJobs: options.backgroundJobs ?? env.SMRT_BACKGROUND_JOBS === "true",
362
+ paidCapabilities: options.paidCapabilities,
363
+ bootstrapTtlSeconds: options.bootstrapTtlSeconds,
364
+ sessionTtlSeconds: options.sessionTtlSeconds,
365
+ prepareDatabase: options.prepareDatabase,
366
+ now: options.now
367
+ }).then(({ runtime: initialized, bootstrap }) => {
368
+ if (bootstrap) try {
369
+ options.onBootstrapInvitation?.(bootstrap, { bindHost });
370
+ } catch {}
371
+ return initialized;
372
+ }).catch((error) => {
373
+ localLease?.release();
374
+ localLease = void 0;
375
+ localPromise = void 0;
376
+ throw error;
377
+ });
378
+ return localPromise;
379
+ };
380
+ let deployedPromise;
381
+ const deployedRuntime = async () => {
382
+ const runtime = await resolvedRuntime();
383
+ if (runtime.profile === "local") throw new Error("deployed_runtime_unavailable");
384
+ const profile = runtime.profile;
385
+ const databaseUrl = env.DATABASE_URL;
386
+ if (!databaseUrl) throw new Error(`${profile} requires DATABASE_URL.`);
387
+ const authenticationProvider = runtime.providers.authentication.provider;
388
+ if (authenticationProvider === "owner-bootstrap") throw new Error("Deployed profiles require public authentication.");
389
+ const readiness = options.providerReadiness;
390
+ if (!readiness) throw new Error(`${profile} requires providerReadiness probes for authentication, assets, and secrets.`);
391
+ deployedPromise ??= initializeDeployed({
392
+ profile,
393
+ providers: {
394
+ database: runtime.providers.database,
395
+ authentication: runtime.providers.authentication,
396
+ tenancy: runtime.providers.tenancy,
397
+ assets: runtime.providers.assets,
398
+ secrets: runtime.providers.secrets,
399
+ jobs: runtime.providers.jobs,
400
+ network: runtime.providers.network
401
+ },
402
+ database: {
403
+ engine: "postgres",
404
+ connect: () => getDatabase({
405
+ type: "postgres",
406
+ url: databaseUrl
407
+ }),
408
+ close: async (db) => {
409
+ await db.close?.();
410
+ }
411
+ },
412
+ authentication: {
413
+ provider: authenticationProvider,
414
+ readiness: readiness("authentication", {
415
+ profile,
416
+ provider: authenticationProvider
417
+ })
418
+ },
419
+ assets: {
420
+ provider: runtime.providers.assets.provider,
421
+ readiness: readiness("assets", {
422
+ profile,
423
+ provider: runtime.providers.assets.provider
424
+ })
425
+ },
426
+ secrets: {
427
+ provider: runtime.providers.secrets.provider,
428
+ readiness: readiness("secrets", {
429
+ profile,
430
+ provider: runtime.providers.secrets.provider
431
+ })
432
+ },
433
+ prepareDatabase: options.prepareDatabase
434
+ });
435
+ return deployedPromise;
436
+ };
437
+ const ready = async () => {
438
+ if ((await resolvedRuntime()).profile === "local") {
439
+ await localRuntime();
440
+ return;
441
+ }
442
+ await deployedRuntime();
443
+ };
444
+ const configurationFingerprint = () => runtimeConfigurationFingerprint(requireResolved(), env);
445
+ const health = async () => {
446
+ await ready();
447
+ const runtime = requireResolved();
448
+ const publicHealth = {
449
+ schemaVersion: 1,
450
+ status: "ready",
451
+ profile: runtime.profile
452
+ };
453
+ return runtime.profile === "local" ? {
454
+ ...publicHealth,
455
+ application: applicationId(),
456
+ instance: env.SMRT_PROCESS_INSTANCE || null,
457
+ configuration: configurationFingerprint()
458
+ } : publicHealth;
459
+ };
460
+ const readDiagnostics = async (diagnosticsOptions) => {
461
+ await ready();
462
+ const runtime = requireResolved();
463
+ const schema = {
464
+ status: diagnosticsOptions.schemaStatus ?? "unknown",
465
+ migrations: diagnosticsOptions.migrationStatus ?? "unknown"
466
+ };
467
+ if (runtime.profile === "local") {
468
+ const diagnostics = await (await localRuntime()).diagnostics();
469
+ return projectRuntimeDiagnostics({
470
+ profile: "local",
471
+ health: "healthy",
472
+ schema,
473
+ capabilities: {
474
+ "asset-storage": "available",
475
+ authentication: "available",
476
+ "background-jobs": diagnostics.jobs.backgroundEnabled ? "available" : "disabled",
477
+ database: "available",
478
+ "paid-capabilities": diagnostics.paidCapabilitiesEnabled ? "available" : "disabled",
479
+ "secret-storage": "available"
480
+ },
481
+ toolNames: diagnosticsOptions.toolNames,
482
+ worker: {
483
+ topology: diagnostics.jobs.topology,
484
+ required: diagnostics.jobs.backgroundEnabled,
485
+ heartbeatAt: diagnosticsOptions.workerHeartbeatAt
486
+ },
487
+ recentErrors: diagnosticsOptions.recentErrors,
488
+ observedAt: diagnosticsOptions.observedAt
489
+ });
490
+ }
491
+ const deployed = await deployedPromise;
492
+ if (!deployed) throw new Error("deployed_runtime_unavailable");
493
+ const readiness = await deployed.readiness();
494
+ const componentStatus = (component) => readiness.components[component].status === "ready" ? "available" : "unavailable";
495
+ return projectRuntimeDiagnostics({
496
+ profile: runtime.profile,
497
+ health: deployed.health().status === "healthy" ? readiness.status === "ready" ? "healthy" : "degraded" : "stopped",
498
+ schema,
499
+ capabilities: {
500
+ "asset-storage": componentStatus("assets"),
501
+ authentication: componentStatus("authentication"),
502
+ "background-jobs": "available",
503
+ database: componentStatus("database"),
504
+ "paid-capabilities": "unknown",
505
+ "secret-storage": componentStatus("secrets")
506
+ },
507
+ toolNames: diagnosticsOptions.toolNames,
508
+ worker: {
509
+ topology: deployed.resolvedRuntime.providers.jobs.topology,
510
+ required: true,
511
+ heartbeatAt: diagnosticsOptions.workerHeartbeatAt
512
+ },
513
+ recentErrors: diagnosticsOptions.recentErrors,
514
+ observedAt: diagnosticsOptions.observedAt
515
+ });
516
+ };
517
+ const selectTenant = options.selectTenant ?? createSubdomainTenantSelector({
518
+ baseDomain: options.tenantBaseDomain ?? env.TENANT_BASE_DOMAIN,
519
+ classOptions: () => baseClassOptions("Tenant")
520
+ });
521
+ const readinessHandle = async ({ event, resolve }) => {
522
+ await ready();
523
+ return resolve(event);
524
+ };
525
+ const tenantSelectionHandle = async ({ event, resolve }) => {
526
+ const locals = runtimeLocals(event.locals);
527
+ const selection = normalizeTenantSelection(await selectTenant({
528
+ url: event.url,
529
+ request: event.request
530
+ }));
531
+ locals.selectedTenantId = selection.tenantId;
532
+ locals.selectedTenantSlug = selection.tenantSlug;
533
+ return resolve(event);
534
+ };
535
+ const sessionCookieName = options.session?.cookieName ?? "sid";
536
+ const sessionSkipPaths = options.session?.skipPaths ?? [];
537
+ const sessionServiceOptions = () => ({
538
+ ...baseClassOptions("Session"),
539
+ ...options.session,
540
+ defaultTTL: options.session?.ttl ?? DEFAULT_SESSION_TTL_SECONDS,
541
+ autoExtend: options.session?.autoExtend ?? false
542
+ });
543
+ const postgresRls = () => requireResolved().providers.tenancy.isolation === "database-rls" ? true : options.session?.postgresRls;
544
+ let permissionResolverPromise;
545
+ const permissionResolver = () => {
546
+ permissionResolverPromise ??= PermissionResolver.create(baseClassOptions("Permission")).catch((error) => {
547
+ permissionResolverPromise = void 0;
548
+ throw error;
549
+ });
550
+ return permissionResolverPromise;
551
+ };
552
+ const runAsPrincipal = async (principal, fn) => {
553
+ const userId = principal?.id;
554
+ const tenantId = principal?.tenantId;
555
+ if (typeof userId !== "string" || userId.length === 0) throw new Error("A bound principal requires a user id.");
556
+ if (typeof tenantId !== "string" || tenantId.length === 0) throw new Error("A bound principal requires a tenant.");
557
+ const resolved2 = await (await permissionResolver()).resolvePermissions(userId, tenantId);
558
+ if (!resolved2.membershipId) throw new Error("The bound principal has no membership in its tenant.");
559
+ const cap = principal.scopes ? new Set(principal.scopes) : void 0;
560
+ const permissions = [...resolved2.permissions].filter((permission) => !cap || cap.has(permission));
561
+ return withPrincipalPermissionContext({
562
+ ...baseClassOptions("Session"),
563
+ userId,
564
+ tenantId,
565
+ permissions,
566
+ enterTenantContext: true,
567
+ postgresRls: postgresRls()
568
+ }, () => fn({
569
+ ...principal,
570
+ scopes: [...permissions]
571
+ }));
572
+ };
573
+ let sessionServicePromise;
574
+ const sessionService = () => {
575
+ sessionServicePromise ??= (async () => {
576
+ const service = new TenantAuthorizingSessionService(sessionServiceOptions(), requireResolved().providers.tenancy.context === "required");
577
+ await service.initialize();
578
+ return service;
579
+ })().catch((error) => {
580
+ sessionServicePromise = void 0;
581
+ throw error;
582
+ });
583
+ return sessionServicePromise;
584
+ };
585
+ const sessionStep = async ({ event, resolve }) => {
586
+ const locals = runtimeLocals(event.locals);
587
+ clearSessionLocals(locals);
588
+ const rls = postgresRls();
589
+ const skipped = sessionSkipPaths.some((path) => event.url.pathname.startsWith(path));
590
+ if (skipped && !rls) return resolve(event);
591
+ const sessionId = skipped ? void 0 : event.cookies.get(sessionCookieName);
592
+ if (!sessionId && !rls) return resolve(event);
593
+ let resolveEntered = false;
594
+ let downstream;
595
+ try {
596
+ const service = await sessionService();
597
+ return await withSessionPermissionContext({
598
+ ...sessionServiceOptions(),
599
+ enterTenantContext: true,
600
+ postgresRls: rls,
601
+ sessionId: sessionId ?? null,
602
+ sessionService: service
603
+ }, async (context) => {
604
+ if (context.session) {
605
+ locals.user = context.user;
606
+ locals.membership = context.membership ?? null;
607
+ locals.permissions = context.permissions;
608
+ locals.tenantId = context.tenantId;
609
+ locals.sessionId = context.sessionId;
610
+ if (context.tenantId) verifiedSessionTenants.set(locals, context.tenantId);
611
+ }
612
+ resolveEntered = true;
613
+ try {
614
+ return await resolve(event);
615
+ } catch (error) {
616
+ downstream = { error };
617
+ throw error;
618
+ }
619
+ });
620
+ } catch (error) {
621
+ if (downstream) throw downstream.error;
622
+ if (resolveEntered) throw error;
623
+ clearSessionLocals(locals);
624
+ return new Response("Internal Server Error", { status: 500 });
625
+ }
626
+ };
627
+ const sessionCookie = Object.freeze({
628
+ name: options.session?.cookieName ?? "sid",
629
+ path: options.session?.cookiePath ?? "/",
630
+ ...options.session?.cookieDomain ? { domain: options.session.cookieDomain } : {},
631
+ sameSite: options.session?.cookieSameSite ?? "lax",
632
+ ...options.session?.cookieSecure !== void 0 ? { secure: options.session.cookieSecure } : {},
633
+ maxAgeSeconds: options.sessionTtlSeconds ?? DEFAULT_SESSION_TTL_SECONDS
634
+ });
635
+ const init = () => ready();
636
+ return Object.freeze({
637
+ handle: composeHandles(readinessHandle, tenantSelectionHandle, sessionStep, authorizedTenantLocalsHandle),
638
+ init,
639
+ sessionCookie,
640
+ resolvedRuntime,
641
+ ready,
642
+ applicationId,
643
+ configurationFingerprint,
644
+ databaseConfig,
645
+ classOptions,
646
+ runAsPrincipal,
647
+ localRuntime,
648
+ deployedRuntime,
649
+ health,
650
+ readDiagnostics
651
+ });
652
+ }
653
+ var verifiedSessionTenants = /* @__PURE__ */ new WeakMap();
654
+ var TenantAuthorizingSessionService = class extends SessionService {
655
+ tenantRequired;
656
+ constructor(options, tenantRequired) {
657
+ super(options);
658
+ this.tenantRequired = tenantRequired;
659
+ }
660
+ async loadSessionContext(sessionId) {
661
+ const context = await super.loadSessionContext(sessionId);
662
+ if (!context) return null;
663
+ if (typeof context.tenantId === "string" && context.tenantId.length > 0) return isSessionTenantAuthorized(context) ? context : null;
664
+ return this.tenantRequired ? null : context;
665
+ }
666
+ };
667
+ function clearSessionLocals(locals) {
668
+ locals.user = null;
669
+ locals.membership = null;
670
+ locals.permissions = [];
671
+ locals.tenantId = null;
672
+ locals.sessionId = null;
673
+ locals.tenantContext = void 0;
674
+ verifiedSessionTenants.delete(locals);
675
+ }
676
+ var authorizedTenantLocalsHandle = async ({ event, resolve }) => {
677
+ const locals = runtimeLocals(event.locals);
678
+ const activeContext = getCurrentTenant();
679
+ if (locals.user && locals.tenantId && verifiedSessionTenants.get(locals) === locals.tenantId && activeContext?.tenantId === locals.tenantId) locals.tenantContext = activeContext;
680
+ return resolve(event);
681
+ };
682
+ function composeHandles(...handles) {
683
+ return ({ event, resolve }) => {
684
+ const run = (index, current, resolveOptions) => {
685
+ const next = handles[index];
686
+ if (!next) return resolve(current, resolveOptions);
687
+ return next({
688
+ event: current,
689
+ resolve: (nextEvent, nextOptions) => run(index + 1, nextEvent, nextOptions ?? resolveOptions)
690
+ });
691
+ };
692
+ return run(0, event);
693
+ };
694
+ }
695
+ function runtimeLocals(locals) {
696
+ return locals;
697
+ }
698
+ async function loadConfiguredRuntime() {
699
+ return (await loadConfig()).runtime ? resolveConfiguredApplicationRuntime() : resolveApplicationRuntime({ profile: "local" });
700
+ }
701
+ //#endregion
702
+ export { DEFAULT_OWNER_SETUP_MESSAGES, OWNER_SETUP_FIELDS, RUNTIME_DIAGNOSTICS_READ_PERMISSION, authorizedTenantLocalsHandle, composeHandles, createOwnerSetupPage, createRuntimeDiagnosticsHandler, createRuntimeHealthHandler, createSessionLayoutLoad, createSmrtSvelteKitRuntime, createSubdomainTenantSelector, isLoopbackAddress, isLoopbackHostname, isLoopbackRequest, normalizeTenantSelection, selectTenantSlug };
703
+
704
+ //# sourceMappingURL=sveltekit.js.map