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