@objectstack/runtime 6.8.0 → 6.9.0
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/dist/index.cjs +201 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +201 -51
- package/dist/index.js.map +1 -1
- package/package.json +18 -18
package/dist/index.d.cts
CHANGED
|
@@ -98,6 +98,7 @@ declare const StandaloneStackConfigSchema: z.ZodObject<{
|
|
|
98
98
|
}>>;
|
|
99
99
|
environmentId: z.ZodOptional<z.ZodString>;
|
|
100
100
|
artifactPath: z.ZodOptional<z.ZodString>;
|
|
101
|
+
projectRoot: z.ZodOptional<z.ZodString>;
|
|
101
102
|
}, z.core.$strip>;
|
|
102
103
|
type StandaloneStackConfig = z.input<typeof StandaloneStackConfigSchema>;
|
|
103
104
|
interface StandaloneStackResult {
|
|
@@ -1485,7 +1486,7 @@ declare class HttpDispatcher {
|
|
|
1485
1486
|
* Handle AI service routes (/ai/chat, /ai/models, /ai/conversations, etc.)
|
|
1486
1487
|
* Resolves the AI service and its built-in route handlers, then dispatches.
|
|
1487
1488
|
*/
|
|
1488
|
-
handleAI(subPath: string, method: string, body: any, query: any,
|
|
1489
|
+
handleAI(subPath: string, method: string, body: any, query: any, context: HttpProtocolContext): Promise<HttpDispatcherResult>;
|
|
1489
1490
|
/**
|
|
1490
1491
|
* Main Dispatcher Entry Point
|
|
1491
1492
|
* Routes the request to the appropriate handler based on path and precedence
|
package/dist/index.d.ts
CHANGED
|
@@ -98,6 +98,7 @@ declare const StandaloneStackConfigSchema: z.ZodObject<{
|
|
|
98
98
|
}>>;
|
|
99
99
|
environmentId: z.ZodOptional<z.ZodString>;
|
|
100
100
|
artifactPath: z.ZodOptional<z.ZodString>;
|
|
101
|
+
projectRoot: z.ZodOptional<z.ZodString>;
|
|
101
102
|
}, z.core.$strip>;
|
|
102
103
|
type StandaloneStackConfig = z.input<typeof StandaloneStackConfigSchema>;
|
|
103
104
|
interface StandaloneStackResult {
|
|
@@ -1485,7 +1486,7 @@ declare class HttpDispatcher {
|
|
|
1485
1486
|
* Handle AI service routes (/ai/chat, /ai/models, /ai/conversations, etc.)
|
|
1486
1487
|
* Resolves the AI service and its built-in route handlers, then dispatches.
|
|
1487
1488
|
*/
|
|
1488
|
-
handleAI(subPath: string, method: string, body: any, query: any,
|
|
1489
|
+
handleAI(subPath: string, method: string, body: any, query: any, context: HttpProtocolContext): Promise<HttpDispatcherResult>;
|
|
1489
1490
|
/**
|
|
1490
1491
|
* Main Dispatcher Entry Point
|
|
1491
1492
|
* Routes the request to the appropriate handler based on path and precedence
|
package/dist/index.js
CHANGED
|
@@ -1445,6 +1445,66 @@ var init_app_plugin = __esm({
|
|
|
1445
1445
|
} catch (err) {
|
|
1446
1446
|
ctx.logger.error("[AppPlugin] Failed to schedule approval-process registration", err, { appId });
|
|
1447
1447
|
}
|
|
1448
|
+
try {
|
|
1449
|
+
const jobs = Array.isArray(this.bundle.jobs) ? this.bundle.jobs : Array.isArray((this.bundle.manifest || {}).jobs) ? this.bundle.manifest.jobs : [];
|
|
1450
|
+
if (jobs.length > 0) {
|
|
1451
|
+
ctx.hook("kernel:ready", async () => {
|
|
1452
|
+
let svc;
|
|
1453
|
+
try {
|
|
1454
|
+
svc = ctx.getService("job");
|
|
1455
|
+
} catch {
|
|
1456
|
+
}
|
|
1457
|
+
if (!svc || typeof svc.schedule !== "function") {
|
|
1458
|
+
ctx.logger.warn("[AppPlugin] job service not registered \u2014 skipping declarative jobs", {
|
|
1459
|
+
appId,
|
|
1460
|
+
jobCount: jobs.length
|
|
1461
|
+
});
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
const fnMap = collectBundleFunctions(this.bundle);
|
|
1465
|
+
let ok = 0;
|
|
1466
|
+
for (const job of jobs) {
|
|
1467
|
+
const jobName = job?.name;
|
|
1468
|
+
if (!jobName) {
|
|
1469
|
+
ctx.logger.warn("[AppPlugin] skipping job without name", { appId, job });
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1472
|
+
if (job.enabled === false) {
|
|
1473
|
+
ctx.logger.debug("[AppPlugin] job disabled \u2014 skipping", { appId, job: jobName });
|
|
1474
|
+
continue;
|
|
1475
|
+
}
|
|
1476
|
+
const handler = fnMap[job.handler];
|
|
1477
|
+
if (typeof handler !== "function") {
|
|
1478
|
+
ctx.logger.warn("[AppPlugin] job handler not found in bundle.functions \u2014 skipping", {
|
|
1479
|
+
appId,
|
|
1480
|
+
job: jobName,
|
|
1481
|
+
handler: job.handler
|
|
1482
|
+
});
|
|
1483
|
+
continue;
|
|
1484
|
+
}
|
|
1485
|
+
try {
|
|
1486
|
+
await svc.schedule(
|
|
1487
|
+
jobName,
|
|
1488
|
+
job.schedule,
|
|
1489
|
+
async (jobCtx) => {
|
|
1490
|
+
await handler({ ...jobCtx, jobId: jobName, bundle: this.bundle });
|
|
1491
|
+
}
|
|
1492
|
+
);
|
|
1493
|
+
ok++;
|
|
1494
|
+
} catch (err) {
|
|
1495
|
+
ctx.logger.warn("[AppPlugin] Failed to schedule job", {
|
|
1496
|
+
appId,
|
|
1497
|
+
job: jobName,
|
|
1498
|
+
error: err?.message ?? String(err)
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
ctx.logger.info("[AppPlugin] Scheduled background jobs", { appId, count: ok });
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
} catch (err) {
|
|
1506
|
+
ctx.logger.error("[AppPlugin] Failed to schedule background-job registration", err, { appId });
|
|
1507
|
+
}
|
|
1448
1508
|
this.emitCatalogEvent(ctx, "app:registered", sys);
|
|
1449
1509
|
await this.loadTranslations(ctx, appId);
|
|
1450
1510
|
const seedDatasets = [];
|
|
@@ -1517,7 +1577,7 @@ var init_app_plugin = __esm({
|
|
|
1517
1577
|
} catch (e) {
|
|
1518
1578
|
ctx.logger.warn("[Seeder] Failed to register seed-datasets/seed-replayer service", { error: e?.message });
|
|
1519
1579
|
}
|
|
1520
|
-
const multiTenant = String(process.env.OS_MULTI_TENANT ?? "
|
|
1580
|
+
const multiTenant = String(process.env.OS_MULTI_TENANT ?? "false").toLowerCase() !== "false";
|
|
1521
1581
|
if (multiTenant) {
|
|
1522
1582
|
ctx.logger.info("[Seeder] multi-tenant mode \u2014 skipping inline seed; per-org replay will run on sys_organization insert");
|
|
1523
1583
|
} else {
|
|
@@ -1572,10 +1632,22 @@ var init_app_plugin = __esm({
|
|
|
1572
1632
|
};
|
|
1573
1633
|
this.bundle = bundle;
|
|
1574
1634
|
this.projectContext = projectContext;
|
|
1575
|
-
const sys = bundle
|
|
1576
|
-
const appId = sys
|
|
1635
|
+
const sys = bundle?.manifest || bundle;
|
|
1636
|
+
const appId = sys?.id || sys?.name;
|
|
1637
|
+
if (!appId) {
|
|
1638
|
+
const bundleKeys = bundle && typeof bundle === "object" ? Object.keys(bundle).slice(0, 20).join(",") : typeof bundle;
|
|
1639
|
+
const sysKeys = sys && typeof sys === "object" ? Object.keys(sys).slice(0, 20).join(",") : typeof sys;
|
|
1640
|
+
const ctxHint = projectContext ? ` projectContext=${JSON.stringify({
|
|
1641
|
+
environmentId: projectContext.environmentId,
|
|
1642
|
+
packageId: projectContext.packageId,
|
|
1643
|
+
source: projectContext.source
|
|
1644
|
+
})}` : "";
|
|
1645
|
+
throw new Error(
|
|
1646
|
+
`[AppPlugin] bundle is missing manifest.id and manifest.name \u2014 cannot register as a plugin. bundleKeys=[${bundleKeys}] sysKeys=[${sysKeys}]${ctxHint}`
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1577
1649
|
this.name = `plugin.app.${appId}`;
|
|
1578
|
-
this.version = sys
|
|
1650
|
+
this.version = sys?.version;
|
|
1579
1651
|
}
|
|
1580
1652
|
/**
|
|
1581
1653
|
* Emit a kernel hook so the control-plane `AppCatalogService` can
|
|
@@ -2122,7 +2194,19 @@ var StandaloneStackConfigSchema = z.object({
|
|
|
2122
2194
|
databaseAuthToken: z.string().optional(),
|
|
2123
2195
|
databaseDriver: z.enum(["sqlite", "sqlite-wasm", "turso", "memory", "postgres", "mongodb"]).optional(),
|
|
2124
2196
|
environmentId: z.string().optional(),
|
|
2125
|
-
artifactPath: z.string().optional()
|
|
2197
|
+
artifactPath: z.string().optional(),
|
|
2198
|
+
/**
|
|
2199
|
+
* Project root directory. When set (typically by the CLI after locating
|
|
2200
|
+
* `objectstack.config.ts`), the default sqlite database is placed under
|
|
2201
|
+
* `<projectRoot>/.objectstack/data/standalone.db` instead of the global
|
|
2202
|
+
* `~/.objectstack/data/standalone.db`. This keeps per-project data
|
|
2203
|
+
* scoped to the project folder so different examples / apps don't
|
|
2204
|
+
* share a single database by accident.
|
|
2205
|
+
*
|
|
2206
|
+
* Explicit `databaseUrl` / `OS_DATABASE_URL` / `OS_HOME` still take
|
|
2207
|
+
* precedence over this default.
|
|
2208
|
+
*/
|
|
2209
|
+
projectRoot: z.string().optional()
|
|
2126
2210
|
});
|
|
2127
2211
|
function detectDriverFromUrl(dbUrl) {
|
|
2128
2212
|
if (/^memory:\/\//i.test(dbUrl)) return "memory";
|
|
@@ -2147,7 +2231,7 @@ async function createStandaloneStack(config) {
|
|
|
2147
2231
|
const environmentId = cfg.environmentId ?? process.env.OS_ENVIRONMENT_ID ?? "proj_local";
|
|
2148
2232
|
const artifactPathInput = cfg.artifactPath ?? process.env.OS_ARTIFACT_PATH ?? resolvePath2(cwd, "dist/objectstack.json");
|
|
2149
2233
|
const artifactPath = isHttpUrl(artifactPathInput) ? artifactPathInput : artifactPathInput.startsWith("/") ? artifactPathInput : resolvePath2(cwd, artifactPathInput);
|
|
2150
|
-
const dbUrl = cfg.databaseUrl ?? process.env.OS_DATABASE_URL?.trim() ?? process.env.TURSO_DATABASE_URL?.trim() ?? `file:${resolvePath2(resolveObjectStackHome(), "data/standalone.db")}
|
|
2234
|
+
const dbUrl = cfg.databaseUrl ?? process.env.OS_DATABASE_URL?.trim() ?? process.env.TURSO_DATABASE_URL?.trim() ?? (process.env.OS_HOME?.trim() ? `file:${resolvePath2(resolveObjectStackHome(), "data/standalone.db")}` : cfg.projectRoot ? `file:${resolvePath2(cfg.projectRoot, ".objectstack/data/standalone.db")}` : `file:${resolvePath2(resolveObjectStackHome(), "data/standalone.db")}`);
|
|
2151
2235
|
const dbAuthToken = cfg.databaseAuthToken ?? process.env.OS_DATABASE_AUTH_TOKEN?.trim() ?? process.env.TURSO_AUTH_TOKEN?.trim();
|
|
2152
2236
|
const explicitDriver = cfg.databaseDriver ?? process.env.OS_DATABASE_DRIVER?.trim();
|
|
2153
2237
|
const dbDriver = explicitDriver ?? detectDriverFromUrl(dbUrl);
|
|
@@ -2365,6 +2449,13 @@ function toHeaders(input) {
|
|
|
2365
2449
|
}
|
|
2366
2450
|
return h;
|
|
2367
2451
|
}
|
|
2452
|
+
function safeJsonParse2(s, fallback) {
|
|
2453
|
+
try {
|
|
2454
|
+
return JSON.parse(s);
|
|
2455
|
+
} catch {
|
|
2456
|
+
return fallback;
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2368
2459
|
async function tryFind(ql, object, where, limit = 100) {
|
|
2369
2460
|
if (!ql || typeof ql.find !== "function") return [];
|
|
2370
2461
|
try {
|
|
@@ -2380,6 +2471,7 @@ async function resolveExecutionContext(opts) {
|
|
|
2380
2471
|
const ctx = {
|
|
2381
2472
|
roles: [],
|
|
2382
2473
|
permissions: [],
|
|
2474
|
+
systemPermissions: [],
|
|
2383
2475
|
isSystem: false
|
|
2384
2476
|
};
|
|
2385
2477
|
let userId;
|
|
@@ -2417,8 +2509,12 @@ async function resolveExecutionContext(opts) {
|
|
|
2417
2509
|
if (!userId) {
|
|
2418
2510
|
try {
|
|
2419
2511
|
const authService = await opts.getService("auth");
|
|
2512
|
+
let api = authService?.api;
|
|
2513
|
+
if (!api && typeof authService?.getApi === "function") {
|
|
2514
|
+
api = await authService.getApi();
|
|
2515
|
+
}
|
|
2420
2516
|
const headersInstance = toHeaders(headers);
|
|
2421
|
-
const sessionData = await
|
|
2517
|
+
const sessionData = await api?.getSession?.({ headers: headersInstance });
|
|
2422
2518
|
userId = sessionData?.user?.id ?? sessionData?.session?.userId;
|
|
2423
2519
|
tenantId = tenantId ?? sessionData?.session?.activeOrganizationId;
|
|
2424
2520
|
ctx.accessToken = sessionData?.session?.token ?? ctx.accessToken;
|
|
@@ -2488,10 +2584,38 @@ async function resolveExecutionContext(opts) {
|
|
|
2488
2584
|
{ id: { $in: Array.from(psIds) } },
|
|
2489
2585
|
500
|
|
2490
2586
|
);
|
|
2587
|
+
const tabRank = {
|
|
2588
|
+
hidden: 0,
|
|
2589
|
+
default_off: 1,
|
|
2590
|
+
default_on: 2,
|
|
2591
|
+
visible: 3
|
|
2592
|
+
};
|
|
2593
|
+
const mergedTabs = {};
|
|
2491
2594
|
for (const ps of psRows) {
|
|
2492
2595
|
if (ps.name && !ctx.permissions.includes(ps.name)) {
|
|
2493
2596
|
ctx.permissions.push(ps.name);
|
|
2494
2597
|
}
|
|
2598
|
+
const sysPerms = typeof ps.system_permissions === "string" ? safeJsonParse2(ps.system_permissions, []) : ps.system_permissions ?? ps.systemPermissions;
|
|
2599
|
+
if (Array.isArray(sysPerms)) {
|
|
2600
|
+
for (const p of sysPerms) {
|
|
2601
|
+
if (typeof p === "string" && !ctx.systemPermissions.includes(p)) {
|
|
2602
|
+
ctx.systemPermissions.push(p);
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
const tabs = typeof ps.tab_permissions === "string" ? safeJsonParse2(ps.tab_permissions, {}) : ps.tab_permissions ?? ps.tabPermissions;
|
|
2607
|
+
if (tabs && typeof tabs === "object") {
|
|
2608
|
+
for (const [app, val] of Object.entries(tabs)) {
|
|
2609
|
+
if (typeof val !== "string" || !(val in tabRank)) continue;
|
|
2610
|
+
const cur = mergedTabs[app];
|
|
2611
|
+
if (!cur || tabRank[val] > tabRank[cur]) {
|
|
2612
|
+
mergedTabs[app] = val;
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
if (Object.keys(mergedTabs).length > 0) {
|
|
2618
|
+
ctx.tabPermissions = mergedTabs;
|
|
2495
2619
|
}
|
|
2496
2620
|
}
|
|
2497
2621
|
return ctx;
|
|
@@ -5235,7 +5359,7 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
5235
5359
|
* Handle AI service routes (/ai/chat, /ai/models, /ai/conversations, etc.)
|
|
5236
5360
|
* Resolves the AI service and its built-in route handlers, then dispatches.
|
|
5237
5361
|
*/
|
|
5238
|
-
async handleAI(subPath, method, body, query,
|
|
5362
|
+
async handleAI(subPath, method, body, query, context) {
|
|
5239
5363
|
let aiService;
|
|
5240
5364
|
try {
|
|
5241
5365
|
aiService = await this.resolveService("ai");
|
|
@@ -5279,7 +5403,23 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
5279
5403
|
if (route.method !== method) continue;
|
|
5280
5404
|
const params = matchRoute(route.path, fullPath);
|
|
5281
5405
|
if (params === null) continue;
|
|
5282
|
-
const
|
|
5406
|
+
const ec = context.executionContext;
|
|
5407
|
+
const user = ec?.userId ? {
|
|
5408
|
+
userId: ec.userId,
|
|
5409
|
+
id: ec.userId,
|
|
5410
|
+
displayName: ec.userDisplayName ?? ec.userName ?? ec.userId,
|
|
5411
|
+
email: ec.userEmail,
|
|
5412
|
+
roles: Array.isArray(ec.roles) ? ec.roles : [],
|
|
5413
|
+
permissions: Array.isArray(ec.permissions) ? ec.permissions : [],
|
|
5414
|
+
organizationId: ec.tenantId
|
|
5415
|
+
} : void 0;
|
|
5416
|
+
const result = await route.handler({
|
|
5417
|
+
body,
|
|
5418
|
+
params,
|
|
5419
|
+
query,
|
|
5420
|
+
headers: context.request?.headers,
|
|
5421
|
+
user
|
|
5422
|
+
});
|
|
5283
5423
|
if (result.stream && result.events) {
|
|
5284
5424
|
return {
|
|
5285
5425
|
handled: true,
|
|
@@ -5784,13 +5924,22 @@ function resolveErrorReporter(ctx, override) {
|
|
|
5784
5924
|
}
|
|
5785
5925
|
|
|
5786
5926
|
// src/dispatcher-plugin.ts
|
|
5787
|
-
function mountRouteOnServer(route, server, routePath, securityHeaders) {
|
|
5927
|
+
function mountRouteOnServer(route, server, routePath, securityHeaders, resolveUser) {
|
|
5788
5928
|
const handler = async (req, res) => {
|
|
5789
5929
|
try {
|
|
5930
|
+
let user;
|
|
5931
|
+
if (resolveUser) {
|
|
5932
|
+
try {
|
|
5933
|
+
user = await resolveUser(req.headers ?? {});
|
|
5934
|
+
} catch {
|
|
5935
|
+
}
|
|
5936
|
+
}
|
|
5790
5937
|
const result = await route.handler({
|
|
5791
5938
|
body: req.body,
|
|
5792
5939
|
params: req.params,
|
|
5793
|
-
query: req.query
|
|
5940
|
+
query: req.query,
|
|
5941
|
+
headers: req.headers,
|
|
5942
|
+
user
|
|
5794
5943
|
});
|
|
5795
5944
|
if (result.stream && result.events) {
|
|
5796
5945
|
res.status(result.status);
|
|
@@ -6527,6 +6676,32 @@ function createDispatcherPlugin(config = {}) {
|
|
|
6527
6676
|
}
|
|
6528
6677
|
}
|
|
6529
6678
|
ctx.logger.info("Dispatcher bridge routes registered", { prefix, enableProjectScoping, projectResolution });
|
|
6679
|
+
const resolveRequestUser = async (headers) => {
|
|
6680
|
+
try {
|
|
6681
|
+
const authService = ctx.getService("auth");
|
|
6682
|
+
if (!authService) return void 0;
|
|
6683
|
+
let api = authService.api;
|
|
6684
|
+
if (!api && typeof authService.getApi === "function") {
|
|
6685
|
+
api = await authService.getApi();
|
|
6686
|
+
}
|
|
6687
|
+
if (!api?.getSession) return void 0;
|
|
6688
|
+
const headersInstance = headers instanceof Headers ? headers : new Headers(headers);
|
|
6689
|
+
const sessionData = await api.getSession({ headers: headersInstance });
|
|
6690
|
+
const userId = sessionData?.user?.id ?? sessionData?.session?.userId;
|
|
6691
|
+
if (!userId) return void 0;
|
|
6692
|
+
return {
|
|
6693
|
+
userId,
|
|
6694
|
+
id: userId,
|
|
6695
|
+
displayName: sessionData?.user?.name ?? sessionData?.user?.email ?? userId,
|
|
6696
|
+
email: sessionData?.user?.email,
|
|
6697
|
+
roles: [],
|
|
6698
|
+
permissions: [],
|
|
6699
|
+
organizationId: sessionData?.session?.activeOrganizationId
|
|
6700
|
+
};
|
|
6701
|
+
} catch {
|
|
6702
|
+
return void 0;
|
|
6703
|
+
}
|
|
6704
|
+
};
|
|
6530
6705
|
const toScopedPath = (routePath) => {
|
|
6531
6706
|
if (routePath.startsWith(prefix)) {
|
|
6532
6707
|
const tail = routePath.slice(prefix.length);
|
|
@@ -6539,11 +6714,11 @@ function createDispatcherPlugin(config = {}) {
|
|
|
6539
6714
|
const routePath = route.path.startsWith("/api/v1") ? route.path : `${prefix}${route.path}`;
|
|
6540
6715
|
let count = 0;
|
|
6541
6716
|
if (enableProjectScoping && projectResolution === "required") {
|
|
6542
|
-
if (mountRouteOnServer(route, server, toScopedPath(routePath), securityHeaders)) count++;
|
|
6717
|
+
if (mountRouteOnServer(route, server, toScopedPath(routePath), securityHeaders, resolveRequestUser)) count++;
|
|
6543
6718
|
} else {
|
|
6544
|
-
if (mountRouteOnServer(route, server, routePath, securityHeaders)) count++;
|
|
6719
|
+
if (mountRouteOnServer(route, server, routePath, securityHeaders, resolveRequestUser)) count++;
|
|
6545
6720
|
if (enableProjectScoping) {
|
|
6546
|
-
if (mountRouteOnServer(route, server, toScopedPath(routePath), securityHeaders)) count++;
|
|
6721
|
+
if (mountRouteOnServer(route, server, toScopedPath(routePath), securityHeaders, resolveRequestUser)) count++;
|
|
6547
6722
|
}
|
|
6548
6723
|
}
|
|
6549
6724
|
return count;
|
|
@@ -7605,39 +7780,7 @@ var ArtifactKernelFactory = class {
|
|
|
7605
7780
|
// intentionally do NOT pass crossSubDomainCookies here
|
|
7606
7781
|
// so cookies stay isolated per project subdomain.
|
|
7607
7782
|
trustedOrigins: trustedOriginsList.length ? trustedOriginsList : void 0,
|
|
7608
|
-
...oidcProviders ? { oidcProviders } : {}
|
|
7609
|
-
// Auto-provision a personal organization for every new
|
|
7610
|
-
// user. SecurityPlugin's ObjectQL middleware does this
|
|
7611
|
-
// for direct `ql.insert` calls, but better-auth's
|
|
7612
|
-
// adapter writes through `dataEngine` directly,
|
|
7613
|
-
// bypassing that middleware — so JIT-created SSO users
|
|
7614
|
-
// would otherwise land on the empty "create
|
|
7615
|
-
// organization" screen on first login.
|
|
7616
|
-
databaseHooks: {
|
|
7617
|
-
user: {
|
|
7618
|
-
create: {
|
|
7619
|
-
after: async (user) => {
|
|
7620
|
-
try {
|
|
7621
|
-
const ql = kernel.getService("objectql");
|
|
7622
|
-
if (!ql) return;
|
|
7623
|
-
const [{ ensureUserHasOrganization, cloneTenantSeedData }] = await Promise.all([
|
|
7624
|
-
import("@objectstack/plugin-security")
|
|
7625
|
-
]);
|
|
7626
|
-
await ensureUserHasOrganization(ql, user, {
|
|
7627
|
-
logger: this.logger,
|
|
7628
|
-
cloneSeedData: cloneTenantSeedData
|
|
7629
|
-
});
|
|
7630
|
-
} catch (e) {
|
|
7631
|
-
this.logger.warn?.("[ArtifactKernelFactory] auto-org provisioning hook failed", {
|
|
7632
|
-
environmentId,
|
|
7633
|
-
userId: user?.id,
|
|
7634
|
-
error: e?.message
|
|
7635
|
-
});
|
|
7636
|
-
}
|
|
7637
|
-
}
|
|
7638
|
-
}
|
|
7639
|
-
}
|
|
7640
|
-
}
|
|
7783
|
+
...oidcProviders ? { oidcProviders } : {}
|
|
7641
7784
|
}));
|
|
7642
7785
|
if (oidcProviders) {
|
|
7643
7786
|
this.logger.info?.("[ArtifactKernelFactory] platform SSO wired", {
|
|
@@ -7656,7 +7799,7 @@ var ArtifactKernelFactory = class {
|
|
|
7656
7799
|
}
|
|
7657
7800
|
try {
|
|
7658
7801
|
const { SecurityPlugin } = await import("@objectstack/plugin-security");
|
|
7659
|
-
const multiTenant = String(process.env.OS_MULTI_TENANT ?? "
|
|
7802
|
+
const multiTenant = String(process.env.OS_MULTI_TENANT ?? "false").toLowerCase() !== "false";
|
|
7660
7803
|
await kernel.use(new SecurityPlugin({ multiTenant }));
|
|
7661
7804
|
} catch (err) {
|
|
7662
7805
|
this.logger.warn?.("[ArtifactKernelFactory] SecurityPlugin not registered", {
|
|
@@ -7665,8 +7808,15 @@ var ArtifactKernelFactory = class {
|
|
|
7665
7808
|
});
|
|
7666
7809
|
}
|
|
7667
7810
|
const projectName = project.hostname ?? environmentId;
|
|
7668
|
-
const
|
|
7669
|
-
const
|
|
7811
|
+
const artifactAny = artifact;
|
|
7812
|
+
const topLevelManifest = artifactAny?.manifest && typeof artifactAny.manifest === "object" ? artifactAny.manifest : null;
|
|
7813
|
+
const topLevelFunctions = Array.isArray(artifactAny?.functions) ? artifactAny.functions : [];
|
|
7814
|
+
const bundle = {
|
|
7815
|
+
...artifact.metadata ?? {},
|
|
7816
|
+
...topLevelManifest ? { manifest: topLevelManifest } : {},
|
|
7817
|
+
functions: topLevelFunctions
|
|
7818
|
+
};
|
|
7819
|
+
const sys = bundle.manifest ?? bundle;
|
|
7670
7820
|
const packageId = sys?.packageId ?? sys?.package_id ?? bundle?.packageId;
|
|
7671
7821
|
const i18nCfg = bundle?.i18n ?? sys?.i18n ?? {};
|
|
7672
7822
|
const trArr = Array.isArray(bundle?.translations) ? bundle.translations : Array.isArray(sys?.translations) ? sys.translations : [];
|
|
@@ -8934,7 +9084,7 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
8934
9084
|
}
|
|
8935
9085
|
}
|
|
8936
9086
|
if (opts.seedNow && datasets.length > 0) {
|
|
8937
|
-
const multiTenant = String(process.env.OS_MULTI_TENANT ?? "
|
|
9087
|
+
const multiTenant = String(process.env.OS_MULTI_TENANT ?? "false").toLowerCase() !== "false";
|
|
8938
9088
|
try {
|
|
8939
9089
|
const ql = ctx.getService("objectql");
|
|
8940
9090
|
let metadata;
|