@objectstack/runtime 6.8.1 → 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 +151 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +151 -49
- package/dist/index.js.map +1 -1
- package/package.json +18 -18
package/dist/index.d.cts
CHANGED
|
@@ -1486,7 +1486,7 @@ declare class HttpDispatcher {
|
|
|
1486
1486
|
* Handle AI service routes (/ai/chat, /ai/models, /ai/conversations, etc.)
|
|
1487
1487
|
* Resolves the AI service and its built-in route handlers, then dispatches.
|
|
1488
1488
|
*/
|
|
1489
|
-
handleAI(subPath: string, method: string, body: any, query: any,
|
|
1489
|
+
handleAI(subPath: string, method: string, body: any, query: any, context: HttpProtocolContext): Promise<HttpDispatcherResult>;
|
|
1490
1490
|
/**
|
|
1491
1491
|
* Main Dispatcher Entry Point
|
|
1492
1492
|
* Routes the request to the appropriate handler based on path and precedence
|
package/dist/index.d.ts
CHANGED
|
@@ -1486,7 +1486,7 @@ declare class HttpDispatcher {
|
|
|
1486
1486
|
* Handle AI service routes (/ai/chat, /ai/models, /ai/conversations, etc.)
|
|
1487
1487
|
* Resolves the AI service and its built-in route handlers, then dispatches.
|
|
1488
1488
|
*/
|
|
1489
|
-
handleAI(subPath: string, method: string, body: any, query: any,
|
|
1489
|
+
handleAI(subPath: string, method: string, body: any, query: any, context: HttpProtocolContext): Promise<HttpDispatcherResult>;
|
|
1490
1490
|
/**
|
|
1491
1491
|
* Main Dispatcher Entry Point
|
|
1492
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
|
|
@@ -2437,8 +2509,12 @@ async function resolveExecutionContext(opts) {
|
|
|
2437
2509
|
if (!userId) {
|
|
2438
2510
|
try {
|
|
2439
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
|
+
}
|
|
2440
2516
|
const headersInstance = toHeaders(headers);
|
|
2441
|
-
const sessionData = await
|
|
2517
|
+
const sessionData = await api?.getSession?.({ headers: headersInstance });
|
|
2442
2518
|
userId = sessionData?.user?.id ?? sessionData?.session?.userId;
|
|
2443
2519
|
tenantId = tenantId ?? sessionData?.session?.activeOrganizationId;
|
|
2444
2520
|
ctx.accessToken = sessionData?.session?.token ?? ctx.accessToken;
|
|
@@ -5283,7 +5359,7 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
5283
5359
|
* Handle AI service routes (/ai/chat, /ai/models, /ai/conversations, etc.)
|
|
5284
5360
|
* Resolves the AI service and its built-in route handlers, then dispatches.
|
|
5285
5361
|
*/
|
|
5286
|
-
async handleAI(subPath, method, body, query,
|
|
5362
|
+
async handleAI(subPath, method, body, query, context) {
|
|
5287
5363
|
let aiService;
|
|
5288
5364
|
try {
|
|
5289
5365
|
aiService = await this.resolveService("ai");
|
|
@@ -5327,7 +5403,23 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
5327
5403
|
if (route.method !== method) continue;
|
|
5328
5404
|
const params = matchRoute(route.path, fullPath);
|
|
5329
5405
|
if (params === null) continue;
|
|
5330
|
-
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
|
+
});
|
|
5331
5423
|
if (result.stream && result.events) {
|
|
5332
5424
|
return {
|
|
5333
5425
|
handled: true,
|
|
@@ -5832,13 +5924,22 @@ function resolveErrorReporter(ctx, override) {
|
|
|
5832
5924
|
}
|
|
5833
5925
|
|
|
5834
5926
|
// src/dispatcher-plugin.ts
|
|
5835
|
-
function mountRouteOnServer(route, server, routePath, securityHeaders) {
|
|
5927
|
+
function mountRouteOnServer(route, server, routePath, securityHeaders, resolveUser) {
|
|
5836
5928
|
const handler = async (req, res) => {
|
|
5837
5929
|
try {
|
|
5930
|
+
let user;
|
|
5931
|
+
if (resolveUser) {
|
|
5932
|
+
try {
|
|
5933
|
+
user = await resolveUser(req.headers ?? {});
|
|
5934
|
+
} catch {
|
|
5935
|
+
}
|
|
5936
|
+
}
|
|
5838
5937
|
const result = await route.handler({
|
|
5839
5938
|
body: req.body,
|
|
5840
5939
|
params: req.params,
|
|
5841
|
-
query: req.query
|
|
5940
|
+
query: req.query,
|
|
5941
|
+
headers: req.headers,
|
|
5942
|
+
user
|
|
5842
5943
|
});
|
|
5843
5944
|
if (result.stream && result.events) {
|
|
5844
5945
|
res.status(result.status);
|
|
@@ -6575,6 +6676,32 @@ function createDispatcherPlugin(config = {}) {
|
|
|
6575
6676
|
}
|
|
6576
6677
|
}
|
|
6577
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
|
+
};
|
|
6578
6705
|
const toScopedPath = (routePath) => {
|
|
6579
6706
|
if (routePath.startsWith(prefix)) {
|
|
6580
6707
|
const tail = routePath.slice(prefix.length);
|
|
@@ -6587,11 +6714,11 @@ function createDispatcherPlugin(config = {}) {
|
|
|
6587
6714
|
const routePath = route.path.startsWith("/api/v1") ? route.path : `${prefix}${route.path}`;
|
|
6588
6715
|
let count = 0;
|
|
6589
6716
|
if (enableProjectScoping && projectResolution === "required") {
|
|
6590
|
-
if (mountRouteOnServer(route, server, toScopedPath(routePath), securityHeaders)) count++;
|
|
6717
|
+
if (mountRouteOnServer(route, server, toScopedPath(routePath), securityHeaders, resolveRequestUser)) count++;
|
|
6591
6718
|
} else {
|
|
6592
|
-
if (mountRouteOnServer(route, server, routePath, securityHeaders)) count++;
|
|
6719
|
+
if (mountRouteOnServer(route, server, routePath, securityHeaders, resolveRequestUser)) count++;
|
|
6593
6720
|
if (enableProjectScoping) {
|
|
6594
|
-
if (mountRouteOnServer(route, server, toScopedPath(routePath), securityHeaders)) count++;
|
|
6721
|
+
if (mountRouteOnServer(route, server, toScopedPath(routePath), securityHeaders, resolveRequestUser)) count++;
|
|
6595
6722
|
}
|
|
6596
6723
|
}
|
|
6597
6724
|
return count;
|
|
@@ -7653,39 +7780,7 @@ var ArtifactKernelFactory = class {
|
|
|
7653
7780
|
// intentionally do NOT pass crossSubDomainCookies here
|
|
7654
7781
|
// so cookies stay isolated per project subdomain.
|
|
7655
7782
|
trustedOrigins: trustedOriginsList.length ? trustedOriginsList : void 0,
|
|
7656
|
-
...oidcProviders ? { oidcProviders } : {}
|
|
7657
|
-
// Auto-provision a personal organization for every new
|
|
7658
|
-
// user. SecurityPlugin's ObjectQL middleware does this
|
|
7659
|
-
// for direct `ql.insert` calls, but better-auth's
|
|
7660
|
-
// adapter writes through `dataEngine` directly,
|
|
7661
|
-
// bypassing that middleware — so JIT-created SSO users
|
|
7662
|
-
// would otherwise land on the empty "create
|
|
7663
|
-
// organization" screen on first login.
|
|
7664
|
-
databaseHooks: {
|
|
7665
|
-
user: {
|
|
7666
|
-
create: {
|
|
7667
|
-
after: async (user) => {
|
|
7668
|
-
try {
|
|
7669
|
-
const ql = kernel.getService("objectql");
|
|
7670
|
-
if (!ql) return;
|
|
7671
|
-
const [{ ensureUserHasOrganization, cloneTenantSeedData }] = await Promise.all([
|
|
7672
|
-
import("@objectstack/plugin-security")
|
|
7673
|
-
]);
|
|
7674
|
-
await ensureUserHasOrganization(ql, user, {
|
|
7675
|
-
logger: this.logger,
|
|
7676
|
-
cloneSeedData: cloneTenantSeedData
|
|
7677
|
-
});
|
|
7678
|
-
} catch (e) {
|
|
7679
|
-
this.logger.warn?.("[ArtifactKernelFactory] auto-org provisioning hook failed", {
|
|
7680
|
-
environmentId,
|
|
7681
|
-
userId: user?.id,
|
|
7682
|
-
error: e?.message
|
|
7683
|
-
});
|
|
7684
|
-
}
|
|
7685
|
-
}
|
|
7686
|
-
}
|
|
7687
|
-
}
|
|
7688
|
-
}
|
|
7783
|
+
...oidcProviders ? { oidcProviders } : {}
|
|
7689
7784
|
}));
|
|
7690
7785
|
if (oidcProviders) {
|
|
7691
7786
|
this.logger.info?.("[ArtifactKernelFactory] platform SSO wired", {
|
|
@@ -7704,7 +7799,7 @@ var ArtifactKernelFactory = class {
|
|
|
7704
7799
|
}
|
|
7705
7800
|
try {
|
|
7706
7801
|
const { SecurityPlugin } = await import("@objectstack/plugin-security");
|
|
7707
|
-
const multiTenant = String(process.env.OS_MULTI_TENANT ?? "
|
|
7802
|
+
const multiTenant = String(process.env.OS_MULTI_TENANT ?? "false").toLowerCase() !== "false";
|
|
7708
7803
|
await kernel.use(new SecurityPlugin({ multiTenant }));
|
|
7709
7804
|
} catch (err) {
|
|
7710
7805
|
this.logger.warn?.("[ArtifactKernelFactory] SecurityPlugin not registered", {
|
|
@@ -7713,8 +7808,15 @@ var ArtifactKernelFactory = class {
|
|
|
7713
7808
|
});
|
|
7714
7809
|
}
|
|
7715
7810
|
const projectName = project.hostname ?? environmentId;
|
|
7716
|
-
const
|
|
7717
|
-
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;
|
|
7718
7820
|
const packageId = sys?.packageId ?? sys?.package_id ?? bundle?.packageId;
|
|
7719
7821
|
const i18nCfg = bundle?.i18n ?? sys?.i18n ?? {};
|
|
7720
7822
|
const trArr = Array.isArray(bundle?.translations) ? bundle.translations : Array.isArray(sys?.translations) ? sys.translations : [];
|
|
@@ -8982,7 +9084,7 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
8982
9084
|
}
|
|
8983
9085
|
}
|
|
8984
9086
|
if (opts.seedNow && datasets.length > 0) {
|
|
8985
|
-
const multiTenant = String(process.env.OS_MULTI_TENANT ?? "
|
|
9087
|
+
const multiTenant = String(process.env.OS_MULTI_TENANT ?? "false").toLowerCase() !== "false";
|
|
8986
9088
|
try {
|
|
8987
9089
|
const ql = ctx.getService("objectql");
|
|
8988
9090
|
let metadata;
|