@objectstack/runtime 7.6.0 → 7.8.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 +136 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +136 -2
- package/dist/index.js.map +1 -1
- package/package.json +18 -18
package/dist/index.d.cts
CHANGED
|
@@ -1959,6 +1959,26 @@ declare class FileArtifactApiClient {
|
|
|
1959
1959
|
private defaultLocalSqliteRuntime;
|
|
1960
1960
|
}
|
|
1961
1961
|
|
|
1962
|
+
/**
|
|
1963
|
+
* createObjectOSStack
|
|
1964
|
+
*
|
|
1965
|
+
* ObjectOS pure-runtime stack — no control-plane database, no auth /
|
|
1966
|
+
* security / audit / tenant plugins. The host kernel registers:
|
|
1967
|
+
*
|
|
1968
|
+
* - A minimal engine triplet (ObjectQL + in-memory DriverPlugin +
|
|
1969
|
+
* MetadataPlugin) so CLI auto-injected plugins (Setup, Studio,
|
|
1970
|
+
* Dispatcher, REST) and the runtime can boot. The host kernel itself
|
|
1971
|
+
* never reads or writes business data — every record query is routed
|
|
1972
|
+
* to a per-project kernel built from a remote artifact.
|
|
1973
|
+
* - The `env-registry` and `kernel-manager` services, so the runtime's
|
|
1974
|
+
* HTTP dispatcher can resolve hostnames and dispatch every request
|
|
1975
|
+
* to the matching project kernel.
|
|
1976
|
+
*
|
|
1977
|
+
* Invoked by `createRuntimeStack()` whenever `OS_CLOUD_URL`
|
|
1978
|
+
* (or `config.controlPlaneUrl`) is set. The same plugin shape is returned
|
|
1979
|
+
* as `createCloudStack()` so host configs can swap stacks transparently.
|
|
1980
|
+
*/
|
|
1981
|
+
|
|
1962
1982
|
interface ObjectOSStackConfig {
|
|
1963
1983
|
/**
|
|
1964
1984
|
* Control-plane base URL (HTTP) or a sentinel of `'file'` for the
|
|
@@ -1992,6 +2012,21 @@ interface ObjectOSStackConfig {
|
|
|
1992
2012
|
artifactCacheTtlMs?: number;
|
|
1993
2013
|
/** API prefix (carried for parity with cloud-stack). Default: /api/v1. */
|
|
1994
2014
|
apiPrefix?: string;
|
|
2015
|
+
/**
|
|
2016
|
+
* Host-supplied runtime plugins appended to the stack's default plugin
|
|
2017
|
+
* list. This is the official seam for a host (e.g. the ObjectStack Cloud
|
|
2018
|
+
* repo) to add **product/policy** plugins — marketplace install, cloud-
|
|
2019
|
+
* account binding, set-initial-password — to the otherwise-neutral
|
|
2020
|
+
* framework runtime, WITHOUT a framework release and without reaching into
|
|
2021
|
+
* the returned array by hand.
|
|
2022
|
+
*
|
|
2023
|
+
* They are appended last, so they mount their routes after the framework
|
|
2024
|
+
* plugins and can override/augment behaviour (e.g. supply a credentialled
|
|
2025
|
+
* install path that the browse-only MarketplaceProxyPlugin deliberately
|
|
2026
|
+
* does not). See docs/design/cloud-account-binding-marketplace-install.md
|
|
2027
|
+
* (ADR §5.2 — "framework exposes seams; cloud supplies metadata + policy").
|
|
2028
|
+
*/
|
|
2029
|
+
extraPlugins?: Plugin[];
|
|
1995
2030
|
}
|
|
1996
2031
|
interface ObjectOSStackResult {
|
|
1997
2032
|
plugins: any[];
|
package/dist/index.d.ts
CHANGED
|
@@ -1959,6 +1959,26 @@ declare class FileArtifactApiClient {
|
|
|
1959
1959
|
private defaultLocalSqliteRuntime;
|
|
1960
1960
|
}
|
|
1961
1961
|
|
|
1962
|
+
/**
|
|
1963
|
+
* createObjectOSStack
|
|
1964
|
+
*
|
|
1965
|
+
* ObjectOS pure-runtime stack — no control-plane database, no auth /
|
|
1966
|
+
* security / audit / tenant plugins. The host kernel registers:
|
|
1967
|
+
*
|
|
1968
|
+
* - A minimal engine triplet (ObjectQL + in-memory DriverPlugin +
|
|
1969
|
+
* MetadataPlugin) so CLI auto-injected plugins (Setup, Studio,
|
|
1970
|
+
* Dispatcher, REST) and the runtime can boot. The host kernel itself
|
|
1971
|
+
* never reads or writes business data — every record query is routed
|
|
1972
|
+
* to a per-project kernel built from a remote artifact.
|
|
1973
|
+
* - The `env-registry` and `kernel-manager` services, so the runtime's
|
|
1974
|
+
* HTTP dispatcher can resolve hostnames and dispatch every request
|
|
1975
|
+
* to the matching project kernel.
|
|
1976
|
+
*
|
|
1977
|
+
* Invoked by `createRuntimeStack()` whenever `OS_CLOUD_URL`
|
|
1978
|
+
* (or `config.controlPlaneUrl`) is set. The same plugin shape is returned
|
|
1979
|
+
* as `createCloudStack()` so host configs can swap stacks transparently.
|
|
1980
|
+
*/
|
|
1981
|
+
|
|
1962
1982
|
interface ObjectOSStackConfig {
|
|
1963
1983
|
/**
|
|
1964
1984
|
* Control-plane base URL (HTTP) or a sentinel of `'file'` for the
|
|
@@ -1992,6 +2012,21 @@ interface ObjectOSStackConfig {
|
|
|
1992
2012
|
artifactCacheTtlMs?: number;
|
|
1993
2013
|
/** API prefix (carried for parity with cloud-stack). Default: /api/v1. */
|
|
1994
2014
|
apiPrefix?: string;
|
|
2015
|
+
/**
|
|
2016
|
+
* Host-supplied runtime plugins appended to the stack's default plugin
|
|
2017
|
+
* list. This is the official seam for a host (e.g. the ObjectStack Cloud
|
|
2018
|
+
* repo) to add **product/policy** plugins — marketplace install, cloud-
|
|
2019
|
+
* account binding, set-initial-password — to the otherwise-neutral
|
|
2020
|
+
* framework runtime, WITHOUT a framework release and without reaching into
|
|
2021
|
+
* the returned array by hand.
|
|
2022
|
+
*
|
|
2023
|
+
* They are appended last, so they mount their routes after the framework
|
|
2024
|
+
* plugins and can override/augment behaviour (e.g. supply a credentialled
|
|
2025
|
+
* install path that the browse-only MarketplaceProxyPlugin deliberately
|
|
2026
|
+
* does not). See docs/design/cloud-account-binding-marketplace-install.md
|
|
2027
|
+
* (ADR §5.2 — "framework exposes seams; cloud supplies metadata + policy").
|
|
2028
|
+
*/
|
|
2029
|
+
extraPlugins?: Plugin[];
|
|
1995
2030
|
}
|
|
1996
2031
|
interface ObjectOSStackResult {
|
|
1997
2032
|
plugins: any[];
|
package/dist/index.js
CHANGED
|
@@ -300,6 +300,24 @@ var init_seed_loader = __esm({
|
|
|
300
300
|
for (const ref of objectRefs) {
|
|
301
301
|
const fieldValue = record[ref.field];
|
|
302
302
|
if (fieldValue === void 0 || fieldValue === null) continue;
|
|
303
|
+
if (typeof fieldValue === "object") {
|
|
304
|
+
const wrapped = fieldValue.externalId;
|
|
305
|
+
const hint = wrapped !== void 0 ? ` Pass the natural key directly: ${ref.field}: ${JSON.stringify(wrapped)}.` : ` Pass the target's ${ref.targetField} value as a plain string.`;
|
|
306
|
+
const error = {
|
|
307
|
+
sourceObject: objectName,
|
|
308
|
+
field: ref.field,
|
|
309
|
+
targetObject: ref.targetObject,
|
|
310
|
+
targetField: ref.targetField,
|
|
311
|
+
attemptedValue: fieldValue,
|
|
312
|
+
recordIndex: i,
|
|
313
|
+
message: `Invalid reference for ${objectName}.${ref.field}: expected a ${ref.targetObject}.${ref.targetField} natural-key string but got an object.${hint}`
|
|
314
|
+
};
|
|
315
|
+
errors.push(error);
|
|
316
|
+
allErrors.push(error);
|
|
317
|
+
this.logger.warn(`[SeedLoader] ${error.message}`, { recordIndex: i });
|
|
318
|
+
record[ref.field] = null;
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
303
321
|
if (typeof fieldValue !== "string" || this.looksLikeInternalId(fieldValue)) continue;
|
|
304
322
|
const targetMap = insertedRecords.get(ref.targetObject);
|
|
305
323
|
const resolvedId = targetMap?.get(String(fieldValue));
|
|
@@ -3514,6 +3532,23 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3514
3532
|
return { handled: true, response: this.error(e.message, 404) };
|
|
3515
3533
|
}
|
|
3516
3534
|
}
|
|
3535
|
+
if (parts.length === 1 && parts[0] === "_drafts" && (!method || method.toUpperCase() === "GET")) {
|
|
3536
|
+
const protocol = await this.resolveService("protocol");
|
|
3537
|
+
if (protocol && typeof protocol.listDrafts === "function") {
|
|
3538
|
+
try {
|
|
3539
|
+
const organizationId = await this.resolveActiveOrganizationId(_context);
|
|
3540
|
+
const data = await protocol.listDrafts({
|
|
3541
|
+
packageId: query?.packageId || void 0,
|
|
3542
|
+
type: query?.type || void 0,
|
|
3543
|
+
organizationId
|
|
3544
|
+
});
|
|
3545
|
+
return { handled: true, response: this.success(data) };
|
|
3546
|
+
} catch (e) {
|
|
3547
|
+
return { handled: true, response: this.error(e.message, 500) };
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
return { handled: true, response: this.error("Draft listing not supported", 501) };
|
|
3551
|
+
}
|
|
3517
3552
|
if (parts.length === 1) {
|
|
3518
3553
|
const typeOrName = parts[0];
|
|
3519
3554
|
const packageId = query?.package || void 0;
|
|
@@ -3813,7 +3848,15 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3813
3848
|
return { handled: true, response: this.success({ packages, total: packages.length }) };
|
|
3814
3849
|
}
|
|
3815
3850
|
if (parts.length === 0 && m === "POST") {
|
|
3816
|
-
const
|
|
3851
|
+
const manifest = body.manifest || body;
|
|
3852
|
+
let pkg;
|
|
3853
|
+
const protocolSvc = await this.resolveService("protocol").catch(() => null);
|
|
3854
|
+
if (protocolSvc && typeof protocolSvc.installPackage === "function") {
|
|
3855
|
+
const out = await protocolSvc.installPackage({ manifest, settings: body.settings });
|
|
3856
|
+
pkg = out?.package ?? out;
|
|
3857
|
+
} else {
|
|
3858
|
+
pkg = registry.installPackage(manifest, body.settings);
|
|
3859
|
+
}
|
|
3817
3860
|
const res = this.success(pkg);
|
|
3818
3861
|
res.status = 201;
|
|
3819
3862
|
return { handled: true, response: res };
|
|
@@ -3849,6 +3892,24 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3849
3892
|
}
|
|
3850
3893
|
return { handled: true, response: this.error("Metadata service not available", 503) };
|
|
3851
3894
|
}
|
|
3895
|
+
if (parts.length === 2 && parts[1] === "publish-drafts" && m === "POST") {
|
|
3896
|
+
const id = decodeURIComponent(parts[0]);
|
|
3897
|
+
const protocol = await this.resolveService("protocol");
|
|
3898
|
+
if (protocol && typeof protocol.publishPackageDrafts === "function") {
|
|
3899
|
+
try {
|
|
3900
|
+
const organizationId = await this.resolveActiveOrganizationId(_context);
|
|
3901
|
+
const result = await protocol.publishPackageDrafts({
|
|
3902
|
+
packageId: id,
|
|
3903
|
+
...organizationId ? { organizationId } : {},
|
|
3904
|
+
...body?.actor ? { actor: body.actor } : {}
|
|
3905
|
+
});
|
|
3906
|
+
return { handled: true, response: this.success(result) };
|
|
3907
|
+
} catch (e) {
|
|
3908
|
+
return { handled: true, response: this.error(e.message, e.statusCode || 500) };
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3911
|
+
return { handled: true, response: this.error("Draft publishing not supported", 501) };
|
|
3912
|
+
}
|
|
3852
3913
|
if (parts.length === 2 && parts[1] === "revert" && m === "POST") {
|
|
3853
3914
|
const id = decodeURIComponent(parts[0]);
|
|
3854
3915
|
const metadataService = await this.getService(CoreServiceName.enum.metadata);
|
|
@@ -5358,6 +5419,14 @@ function createDispatcherPlugin(config = {}) {
|
|
|
5358
5419
|
errorResponse(err, res);
|
|
5359
5420
|
}
|
|
5360
5421
|
});
|
|
5422
|
+
server.post(`${prefix}/packages/:id/publish-drafts`, async (req, res) => {
|
|
5423
|
+
try {
|
|
5424
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/publish-drafts`, "POST", req.body, {}, { request: req });
|
|
5425
|
+
sendResult(result, res);
|
|
5426
|
+
} catch (err) {
|
|
5427
|
+
errorResponse(err, res);
|
|
5428
|
+
}
|
|
5429
|
+
});
|
|
5361
5430
|
server.post(`${prefix}/packages/:id/revert`, async (req, res) => {
|
|
5362
5431
|
try {
|
|
5363
5432
|
const result = await dispatcher.handlePackages(`/${req.params.id}/revert`, "POST", req.body, {}, { request: req });
|
|
@@ -7370,6 +7439,61 @@ var AuthProxyPlugin = class {
|
|
|
7370
7439
|
return c.text(`sso-exchange failed: ${err?.message ?? String(err)}`, 500);
|
|
7371
7440
|
}
|
|
7372
7441
|
}
|
|
7442
|
+
if (c.req.method === "POST" && subPath === "set-initial-password") {
|
|
7443
|
+
try {
|
|
7444
|
+
let body = {};
|
|
7445
|
+
try {
|
|
7446
|
+
body = await c.req.json();
|
|
7447
|
+
} catch {
|
|
7448
|
+
body = {};
|
|
7449
|
+
}
|
|
7450
|
+
const newPassword = body?.newPassword;
|
|
7451
|
+
if (typeof newPassword !== "string" || newPassword.length === 0) {
|
|
7452
|
+
return c.json({ success: false, error: { code: "invalid_request", message: "newPassword is required" } }, 400);
|
|
7453
|
+
}
|
|
7454
|
+
if (typeof authSvc?.getAuthContext !== "function") {
|
|
7455
|
+
return c.json({ success: false, error: { code: "unavailable", message: "Auth context unavailable" } }, 503);
|
|
7456
|
+
}
|
|
7457
|
+
let userId;
|
|
7458
|
+
try {
|
|
7459
|
+
const api = typeof authSvc.getApi === "function" ? await authSvc.getApi() : null;
|
|
7460
|
+
const session = await api?.getSession?.({ headers: c.req.raw.headers });
|
|
7461
|
+
userId = session?.user?.id ? String(session.user.id) : void 0;
|
|
7462
|
+
} catch {
|
|
7463
|
+
}
|
|
7464
|
+
if (!userId) {
|
|
7465
|
+
return c.json({ success: false, error: { code: "unauthorized", message: "Sign in first" } }, 401);
|
|
7466
|
+
}
|
|
7467
|
+
const setPwCtx = await authSvc.getAuthContext();
|
|
7468
|
+
if (!setPwCtx?.internalAdapter || !setPwCtx?.password) {
|
|
7469
|
+
return c.json({ success: false, error: { code: "unavailable", message: "Auth context unavailable" } }, 503);
|
|
7470
|
+
}
|
|
7471
|
+
const minLen = setPwCtx.password?.config?.minPasswordLength ?? 8;
|
|
7472
|
+
const maxLen = setPwCtx.password?.config?.maxPasswordLength ?? 128;
|
|
7473
|
+
if (newPassword.length < minLen) {
|
|
7474
|
+
return c.json({ success: false, error: { code: "password_too_short", message: `Password must be at least ${minLen} characters` } }, 400);
|
|
7475
|
+
}
|
|
7476
|
+
if (newPassword.length > maxLen) {
|
|
7477
|
+
return c.json({ success: false, error: { code: "password_too_long", message: `Password must be at most ${maxLen} characters` } }, 400);
|
|
7478
|
+
}
|
|
7479
|
+
const accounts = await setPwCtx.internalAdapter.findAccounts(userId);
|
|
7480
|
+
const existingCredential = accounts?.find?.((a) => a.providerId === "credential" && a.password);
|
|
7481
|
+
if (existingCredential) {
|
|
7482
|
+
return c.json({ success: false, error: { code: "credential_account_exists", message: "A local password is already set for this account. Use change-password instead." } }, 409);
|
|
7483
|
+
}
|
|
7484
|
+
const passwordHash = await setPwCtx.password.hash(newPassword);
|
|
7485
|
+
await setPwCtx.internalAdapter.createAccount({
|
|
7486
|
+
userId,
|
|
7487
|
+
providerId: "credential",
|
|
7488
|
+
accountId: userId,
|
|
7489
|
+
password: passwordHash
|
|
7490
|
+
});
|
|
7491
|
+
return c.json({ success: true });
|
|
7492
|
+
} catch (err) {
|
|
7493
|
+
ctx.logger?.error?.("[AuthProxyPlugin] set-initial-password failed", err instanceof Error ? err : new Error(String(err)));
|
|
7494
|
+
return c.json({ success: false, error: { code: "set_password_failed", message: String(err?.message ?? err) } }, 500);
|
|
7495
|
+
}
|
|
7496
|
+
}
|
|
7373
7497
|
const fn = await resolveAuthHandler(authSvc);
|
|
7374
7498
|
if (!fn) {
|
|
7375
7499
|
return c.json({ error: "auth_service_unavailable", environmentId }, 503);
|
|
@@ -8084,7 +8208,17 @@ async function createObjectOSStack(config) {
|
|
|
8084
8208
|
};
|
|
8085
8209
|
const enginePlugins = await createHostEnginePlugins();
|
|
8086
8210
|
return {
|
|
8087
|
-
plugins: [
|
|
8211
|
+
plugins: [
|
|
8212
|
+
...enginePlugins,
|
|
8213
|
+
new ObjectOSEnvironmentPlugin(merged),
|
|
8214
|
+
new AuthProxyPlugin(),
|
|
8215
|
+
new MarketplaceProxyPlugin({ controlPlaneUrl: merged.controlPlaneUrl === "file" ? void 0 : merged.controlPlaneUrl }),
|
|
8216
|
+
new RuntimeConfigPlugin({ controlPlaneUrl: merged.controlPlaneUrl === "file" ? void 0 : merged.controlPlaneUrl, installLocal: false }),
|
|
8217
|
+
// Host-supplied product/policy plugins (the official seam — see
|
|
8218
|
+
// ObjectOSStackConfig.extraPlugins). Appended last so they mount
|
|
8219
|
+
// after the framework defaults.
|
|
8220
|
+
...config.extraPlugins ?? []
|
|
8221
|
+
],
|
|
8088
8222
|
api: {
|
|
8089
8223
|
enableProjectScoping: true,
|
|
8090
8224
|
projectResolution: "auto",
|