@objectstack/runtime 7.2.1 → 7.3.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 +453 -253
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -4
- package/dist/index.d.ts +24 -4
- package/dist/index.js +444 -244
- package/dist/index.js.map +1 -1
- package/package.json +18 -18
package/dist/index.cjs
CHANGED
|
@@ -726,6 +726,52 @@ var init_seed_loader = __esm({
|
|
|
726
726
|
}
|
|
727
727
|
});
|
|
728
728
|
|
|
729
|
+
// src/package-state-store.ts
|
|
730
|
+
function sanitizeEnvironmentId(environmentId) {
|
|
731
|
+
const raw = (environmentId ?? process.env.OS_ENVIRONMENT_ID ?? DEFAULT_ENVIRONMENT_ID).trim();
|
|
732
|
+
const safe = raw.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
733
|
+
return safe.length > 0 ? safe : DEFAULT_ENVIRONMENT_ID;
|
|
734
|
+
}
|
|
735
|
+
function stateFilePath(environmentId) {
|
|
736
|
+
return (0, import_node_path2.join)(resolveObjectStackHome(), "package-state", `${sanitizeEnvironmentId(environmentId)}.json`);
|
|
737
|
+
}
|
|
738
|
+
function readState(environmentId) {
|
|
739
|
+
const file = stateFilePath(environmentId);
|
|
740
|
+
if (!(0, import_node_fs.existsSync)(file)) return {};
|
|
741
|
+
try {
|
|
742
|
+
const parsed = JSON.parse((0, import_node_fs.readFileSync)(file, "utf8"));
|
|
743
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
744
|
+
} catch {
|
|
745
|
+
return {};
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
function writeState(environmentId, state) {
|
|
749
|
+
const file = stateFilePath(environmentId);
|
|
750
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path2.dirname)(file), { recursive: true });
|
|
751
|
+
(0, import_node_fs.writeFileSync)(file, `${JSON.stringify(state, null, 2)}
|
|
752
|
+
`, "utf8");
|
|
753
|
+
}
|
|
754
|
+
function loadDisabledPackageIds(environmentId) {
|
|
755
|
+
const disabled = readState(environmentId).disabled;
|
|
756
|
+
return new Set(Array.isArray(disabled) ? disabled.filter((id) => typeof id === "string") : []);
|
|
757
|
+
}
|
|
758
|
+
function setPackageDisabled(environmentId, packageId, disabled) {
|
|
759
|
+
const ids = loadDisabledPackageIds(environmentId);
|
|
760
|
+
if (disabled) ids.add(packageId);
|
|
761
|
+
else ids.delete(packageId);
|
|
762
|
+
writeState(environmentId, { disabled: Array.from(ids).sort() });
|
|
763
|
+
}
|
|
764
|
+
var import_node_fs, import_node_path2, DEFAULT_ENVIRONMENT_ID;
|
|
765
|
+
var init_package_state_store = __esm({
|
|
766
|
+
"src/package-state-store.ts"() {
|
|
767
|
+
"use strict";
|
|
768
|
+
import_node_fs = require("fs");
|
|
769
|
+
import_node_path2 = require("path");
|
|
770
|
+
init_standalone_stack();
|
|
771
|
+
DEFAULT_ENVIRONMENT_ID = "default";
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
|
|
729
775
|
// src/sandbox/quickjs-runner.ts
|
|
730
776
|
function installApiMethod(vm, parent, method, objectName, ctx, caps, required, origin) {
|
|
731
777
|
const fn = vm.newAsyncifiedFunction(method, async (...argHandles) => {
|
|
@@ -1290,6 +1336,7 @@ var init_app_plugin = __esm({
|
|
|
1290
1336
|
"use strict";
|
|
1291
1337
|
import_types = require("@objectstack/types");
|
|
1292
1338
|
init_seed_loader();
|
|
1339
|
+
init_package_state_store();
|
|
1293
1340
|
init_quickjs_runner();
|
|
1294
1341
|
init_body_runner();
|
|
1295
1342
|
AppPlugin = class {
|
|
@@ -1315,6 +1362,24 @@ var init_app_plugin = __esm({
|
|
|
1315
1362
|
console.warn(
|
|
1316
1363
|
`[AppPlugin:init] appId=${appId} keys=${Object.keys(servicePayload).join(",")} flows=${Array.isArray(servicePayload.flows) ? servicePayload.flows.length : "n/a"}`
|
|
1317
1364
|
);
|
|
1365
|
+
try {
|
|
1366
|
+
const ql = ctx.getService("objectql");
|
|
1367
|
+
const setter = ql?.registry?.setInitialDisabledPackageIds;
|
|
1368
|
+
if (typeof setter === "function") {
|
|
1369
|
+
const disabled = loadDisabledPackageIds(this.projectContext?.environmentId);
|
|
1370
|
+
if (disabled.size > 0) {
|
|
1371
|
+
setter.call(ql.registry, disabled);
|
|
1372
|
+
ctx.logger.info("[AppPlugin] seeded persisted disabled packages", {
|
|
1373
|
+
environmentId: this.projectContext?.environmentId,
|
|
1374
|
+
disabled: Array.from(disabled)
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
} catch (err) {
|
|
1379
|
+
ctx.logger.warn("[AppPlugin] failed to seed persisted package state", {
|
|
1380
|
+
error: err?.message ?? String(err)
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1318
1383
|
ctx.getService("manifest").register(servicePayload);
|
|
1319
1384
|
};
|
|
1320
1385
|
this.start = async (ctx) => {
|
|
@@ -1859,6 +1924,170 @@ var init_app_plugin = __esm({
|
|
|
1859
1924
|
}
|
|
1860
1925
|
});
|
|
1861
1926
|
|
|
1927
|
+
// src/standalone-stack.ts
|
|
1928
|
+
function resolveObjectStackHome() {
|
|
1929
|
+
const raw = process.env.OS_HOME?.trim();
|
|
1930
|
+
if (raw && raw.length > 0) {
|
|
1931
|
+
if (raw.startsWith("~")) return (0, import_node_path3.resolve)((0, import_node_os.homedir)(), raw.slice(1).replace(/^[/\\]/, ""));
|
|
1932
|
+
return (0, import_node_path3.resolve)(raw);
|
|
1933
|
+
}
|
|
1934
|
+
return (0, import_node_path3.resolve)((0, import_node_os.homedir)(), ".objectstack");
|
|
1935
|
+
}
|
|
1936
|
+
function detectDriverFromUrl(dbUrl) {
|
|
1937
|
+
if (/^memory:\/\//i.test(dbUrl)) return "memory";
|
|
1938
|
+
if (/^(postgres(ql)?|pg):\/\//i.test(dbUrl)) return "postgres";
|
|
1939
|
+
if (/^mongodb(\+srv)?:\/\//i.test(dbUrl)) return "mongodb";
|
|
1940
|
+
if (/^wasm-sqlite:\/\//i.test(dbUrl)) return "sqlite-wasm";
|
|
1941
|
+
if (/\.wasm\.db$/i.test(dbUrl)) return "sqlite-wasm";
|
|
1942
|
+
if (/^file:/i.test(dbUrl)) return "sqlite";
|
|
1943
|
+
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(dbUrl)) return "sqlite";
|
|
1944
|
+
throw new Error(
|
|
1945
|
+
`[StandaloneStack] Unsupported database URL scheme: ${dbUrl}. Supported schemes: memory://, postgres://, pg://, mongodb://, mongodb+srv://, file:`
|
|
1946
|
+
);
|
|
1947
|
+
}
|
|
1948
|
+
async function createStandaloneStack(config) {
|
|
1949
|
+
const cfg = StandaloneStackConfigSchema.parse(config ?? {});
|
|
1950
|
+
const { ObjectQLPlugin } = await import("@objectstack/objectql");
|
|
1951
|
+
const { MetadataPlugin } = await import("@objectstack/metadata");
|
|
1952
|
+
const { DriverPlugin: DriverPlugin2 } = await Promise.resolve().then(() => (init_driver_plugin(), driver_plugin_exports));
|
|
1953
|
+
const { AppPlugin: AppPlugin2 } = await Promise.resolve().then(() => (init_app_plugin(), app_plugin_exports));
|
|
1954
|
+
const cwd = process.cwd();
|
|
1955
|
+
const environmentId = cfg.environmentId ?? process.env.OS_ENVIRONMENT_ID ?? "proj_local";
|
|
1956
|
+
const artifactPathInput = cfg.artifactPath ?? process.env.OS_ARTIFACT_PATH ?? (0, import_node_path3.resolve)(cwd, "dist/objectstack.json");
|
|
1957
|
+
const artifactPath = isHttpUrl(artifactPathInput) ? artifactPathInput : artifactPathInput.startsWith("/") ? artifactPathInput : (0, import_node_path3.resolve)(cwd, artifactPathInput);
|
|
1958
|
+
const dbUrl = cfg.databaseUrl ?? (0, import_types2.readEnvWithDeprecation)("OS_DATABASE_URL", "DATABASE_URL")?.trim() ?? process.env.TURSO_DATABASE_URL?.trim() ?? (process.env.OS_HOME?.trim() ? `file:${(0, import_node_path3.resolve)(resolveObjectStackHome(), "data/standalone.db")}` : cfg.projectRoot ? `file:${(0, import_node_path3.resolve)(cfg.projectRoot, ".objectstack/data/standalone.db")}` : `file:${(0, import_node_path3.resolve)(resolveObjectStackHome(), "data/standalone.db")}`);
|
|
1959
|
+
const explicitDriver = cfg.databaseDriver ?? process.env.OS_DATABASE_DRIVER?.trim();
|
|
1960
|
+
const dbDriver = explicitDriver ?? detectDriverFromUrl(dbUrl);
|
|
1961
|
+
let driverPlugin;
|
|
1962
|
+
if (dbDriver === "memory") {
|
|
1963
|
+
const { InMemoryDriver } = await import("@objectstack/driver-memory");
|
|
1964
|
+
driverPlugin = new DriverPlugin2(new InMemoryDriver());
|
|
1965
|
+
} else if (dbDriver === "postgres") {
|
|
1966
|
+
const { SqlDriver } = await import("@objectstack/driver-sql");
|
|
1967
|
+
driverPlugin = new DriverPlugin2(
|
|
1968
|
+
new SqlDriver({
|
|
1969
|
+
client: "pg",
|
|
1970
|
+
connection: dbUrl,
|
|
1971
|
+
pool: { min: 0, max: 5 }
|
|
1972
|
+
})
|
|
1973
|
+
);
|
|
1974
|
+
} else if (dbDriver === "mongodb") {
|
|
1975
|
+
let MongoDBDriver;
|
|
1976
|
+
try {
|
|
1977
|
+
({ MongoDBDriver } = await import("@objectstack/driver-mongodb"));
|
|
1978
|
+
} catch (err) {
|
|
1979
|
+
throw new Error(
|
|
1980
|
+
`[StandaloneStack] mongodb URL detected but @objectstack/driver-mongodb is not installed. Add it as a dependency or pass an explicit driverPlugin. (${err?.message ?? err})`
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1983
|
+
driverPlugin = new DriverPlugin2(new MongoDBDriver({ url: dbUrl }));
|
|
1984
|
+
} else if (dbDriver === "sqlite-wasm") {
|
|
1985
|
+
const { SqliteWasmDriver } = await import("@objectstack/driver-sqlite-wasm");
|
|
1986
|
+
const filename = dbUrl.replace(/^wasm-sqlite:(\/\/)?/i, "").replace(/^file:(\/\/)?/i, "");
|
|
1987
|
+
if (filename && filename !== ":memory:") {
|
|
1988
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path3.resolve)(filename, ".."), { recursive: true });
|
|
1989
|
+
}
|
|
1990
|
+
driverPlugin = new DriverPlugin2(
|
|
1991
|
+
new SqliteWasmDriver({
|
|
1992
|
+
filename: filename || ":memory:",
|
|
1993
|
+
persist: filename && filename !== ":memory:" ? "on-write" : void 0
|
|
1994
|
+
})
|
|
1995
|
+
);
|
|
1996
|
+
} else {
|
|
1997
|
+
const { SqlDriver } = await import("@objectstack/driver-sql");
|
|
1998
|
+
const filename = dbUrl.replace(/^file:(\/\/)?/, "");
|
|
1999
|
+
if (!filename || /^[a-z][a-z0-9+.-]*:\/\//i.test(filename)) {
|
|
2000
|
+
throw new Error(
|
|
2001
|
+
`[StandaloneStack] sqlite driver was selected but the URL does not look like a file path: "${dbUrl}". Use file:/path/to/db.sqlite, or set OS_DATABASE_DRIVER explicitly.`
|
|
2002
|
+
);
|
|
2003
|
+
}
|
|
2004
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path3.resolve)(filename, ".."), { recursive: true });
|
|
2005
|
+
driverPlugin = new DriverPlugin2(
|
|
2006
|
+
new SqlDriver({
|
|
2007
|
+
client: "better-sqlite3",
|
|
2008
|
+
connection: { filename },
|
|
2009
|
+
useNullAsDefault: true
|
|
2010
|
+
})
|
|
2011
|
+
);
|
|
2012
|
+
}
|
|
2013
|
+
const artifactBundle = await loadArtifactBundle(artifactPath, {
|
|
2014
|
+
tag: "[StandaloneStack]",
|
|
2015
|
+
unwrapEnvelope: true
|
|
2016
|
+
});
|
|
2017
|
+
if (artifactBundle) {
|
|
2018
|
+
const flowsCount = Array.isArray(artifactBundle?.flows) ? artifactBundle.flows.length : "n/a";
|
|
2019
|
+
console.warn(
|
|
2020
|
+
`[StandaloneStack] artifact loaded: path=${artifactPath} keys=${Object.keys(artifactBundle).join(",")} flows=${flowsCount}`
|
|
2021
|
+
);
|
|
2022
|
+
}
|
|
2023
|
+
const plugins = [
|
|
2024
|
+
driverPlugin,
|
|
2025
|
+
new MetadataPlugin({
|
|
2026
|
+
// Source-file scanner OFF — declarative metadata is loaded
|
|
2027
|
+
// from the compiled artifact, not from yaml/json files on
|
|
2028
|
+
// disk. Scanning would also recursively watch the project
|
|
2029
|
+
// root (incl. node_modules), which is expensive and prone
|
|
2030
|
+
// to EMFILE.
|
|
2031
|
+
watch: false,
|
|
2032
|
+
// Artifact-file HMR ON in non-production so edits to
|
|
2033
|
+
// `*.view.ts` / `*.flow.ts` (which the CLI dev-mode watcher
|
|
2034
|
+
// recompiles into `dist/objectstack.json`) are picked up by
|
|
2035
|
+
// the running server WITHOUT requiring a manual restart.
|
|
2036
|
+
// Uses polling under the hood (see plugin.ts) to avoid
|
|
2037
|
+
// `fs.watch` EMFILE on macOS / busy dev hosts.
|
|
2038
|
+
artifactWatch: process.env.NODE_ENV !== "production",
|
|
2039
|
+
environmentId,
|
|
2040
|
+
artifactSource: { mode: "local-file", path: artifactPath }
|
|
2041
|
+
}),
|
|
2042
|
+
new ObjectQLPlugin({ environmentId })
|
|
2043
|
+
];
|
|
2044
|
+
if (artifactBundle) plugins.push(new AppPlugin2(artifactBundle));
|
|
2045
|
+
const requires = Array.isArray(artifactBundle?.requires) ? artifactBundle.requires.filter((c) => typeof c === "string") : void 0;
|
|
2046
|
+
const objects = Array.isArray(artifactBundle?.objects) ? artifactBundle.objects : void 0;
|
|
2047
|
+
const manifest = artifactBundle?.manifest;
|
|
2048
|
+
return {
|
|
2049
|
+
plugins,
|
|
2050
|
+
api: {
|
|
2051
|
+
enableProjectScoping: false,
|
|
2052
|
+
projectResolution: "none"
|
|
2053
|
+
},
|
|
2054
|
+
...requires ? { requires } : {},
|
|
2055
|
+
...objects ? { objects } : {},
|
|
2056
|
+
...manifest ? { manifest } : {}
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
var import_node_path3, import_node_fs2, import_node_os, import_zod, import_types2, StandaloneStackConfigSchema;
|
|
2060
|
+
var init_standalone_stack = __esm({
|
|
2061
|
+
"src/standalone-stack.ts"() {
|
|
2062
|
+
"use strict";
|
|
2063
|
+
import_node_path3 = require("path");
|
|
2064
|
+
import_node_fs2 = require("fs");
|
|
2065
|
+
import_node_os = require("os");
|
|
2066
|
+
import_zod = require("zod");
|
|
2067
|
+
import_types2 = require("@objectstack/types");
|
|
2068
|
+
init_load_artifact_bundle();
|
|
2069
|
+
StandaloneStackConfigSchema = import_zod.z.object({
|
|
2070
|
+
databaseUrl: import_zod.z.string().optional(),
|
|
2071
|
+
databaseAuthToken: import_zod.z.string().optional(),
|
|
2072
|
+
databaseDriver: import_zod.z.enum(["sqlite", "sqlite-wasm", "memory", "postgres", "mongodb"]).optional(),
|
|
2073
|
+
environmentId: import_zod.z.string().optional(),
|
|
2074
|
+
artifactPath: import_zod.z.string().optional(),
|
|
2075
|
+
/**
|
|
2076
|
+
* Project root directory. When set (typically by the CLI after locating
|
|
2077
|
+
* `objectstack.config.ts`), the default sqlite database is placed under
|
|
2078
|
+
* `<projectRoot>/.objectstack/data/standalone.db` instead of the global
|
|
2079
|
+
* `~/.objectstack/data/standalone.db`. This keeps per-project data
|
|
2080
|
+
* scoped to the project folder so different examples / apps don't
|
|
2081
|
+
* share a single database by accident.
|
|
2082
|
+
*
|
|
2083
|
+
* Explicit `databaseUrl` / `OS_DATABASE_URL` / `OS_HOME` still take
|
|
2084
|
+
* precedence over this default.
|
|
2085
|
+
*/
|
|
2086
|
+
projectRoot: import_zod.z.string().optional()
|
|
2087
|
+
});
|
|
2088
|
+
}
|
|
2089
|
+
});
|
|
2090
|
+
|
|
1862
2091
|
// src/cloud/platform-sso.ts
|
|
1863
2092
|
var platform_sso_exports = {};
|
|
1864
2093
|
__export(platform_sso_exports, {
|
|
@@ -2333,173 +2562,19 @@ var Runtime = class {
|
|
|
2333
2562
|
}
|
|
2334
2563
|
};
|
|
2335
2564
|
|
|
2336
|
-
// src/
|
|
2337
|
-
|
|
2338
|
-
var import_node_fs = require("fs");
|
|
2339
|
-
var import_node_os = require("os");
|
|
2340
|
-
var import_zod = require("zod");
|
|
2341
|
-
var import_types2 = require("@objectstack/types");
|
|
2342
|
-
init_load_artifact_bundle();
|
|
2343
|
-
function resolveObjectStackHome() {
|
|
2344
|
-
const raw = process.env.OS_HOME?.trim();
|
|
2345
|
-
if (raw && raw.length > 0) {
|
|
2346
|
-
if (raw.startsWith("~")) return (0, import_node_path2.resolve)((0, import_node_os.homedir)(), raw.slice(1).replace(/^[/\\]/, ""));
|
|
2347
|
-
return (0, import_node_path2.resolve)(raw);
|
|
2348
|
-
}
|
|
2349
|
-
return (0, import_node_path2.resolve)((0, import_node_os.homedir)(), ".objectstack");
|
|
2350
|
-
}
|
|
2351
|
-
var StandaloneStackConfigSchema = import_zod.z.object({
|
|
2352
|
-
databaseUrl: import_zod.z.string().optional(),
|
|
2353
|
-
databaseAuthToken: import_zod.z.string().optional(),
|
|
2354
|
-
databaseDriver: import_zod.z.enum(["sqlite", "sqlite-wasm", "memory", "postgres", "mongodb"]).optional(),
|
|
2355
|
-
environmentId: import_zod.z.string().optional(),
|
|
2356
|
-
artifactPath: import_zod.z.string().optional(),
|
|
2357
|
-
/**
|
|
2358
|
-
* Project root directory. When set (typically by the CLI after locating
|
|
2359
|
-
* `objectstack.config.ts`), the default sqlite database is placed under
|
|
2360
|
-
* `<projectRoot>/.objectstack/data/standalone.db` instead of the global
|
|
2361
|
-
* `~/.objectstack/data/standalone.db`. This keeps per-project data
|
|
2362
|
-
* scoped to the project folder so different examples / apps don't
|
|
2363
|
-
* share a single database by accident.
|
|
2364
|
-
*
|
|
2365
|
-
* Explicit `databaseUrl` / `OS_DATABASE_URL` / `OS_HOME` still take
|
|
2366
|
-
* precedence over this default.
|
|
2367
|
-
*/
|
|
2368
|
-
projectRoot: import_zod.z.string().optional()
|
|
2369
|
-
});
|
|
2370
|
-
function detectDriverFromUrl(dbUrl) {
|
|
2371
|
-
if (/^memory:\/\//i.test(dbUrl)) return "memory";
|
|
2372
|
-
if (/^(postgres(ql)?|pg):\/\//i.test(dbUrl)) return "postgres";
|
|
2373
|
-
if (/^mongodb(\+srv)?:\/\//i.test(dbUrl)) return "mongodb";
|
|
2374
|
-
if (/^wasm-sqlite:\/\//i.test(dbUrl)) return "sqlite-wasm";
|
|
2375
|
-
if (/\.wasm\.db$/i.test(dbUrl)) return "sqlite-wasm";
|
|
2376
|
-
if (/^file:/i.test(dbUrl)) return "sqlite";
|
|
2377
|
-
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(dbUrl)) return "sqlite";
|
|
2378
|
-
throw new Error(
|
|
2379
|
-
`[StandaloneStack] Unsupported database URL scheme: ${dbUrl}. Supported schemes: memory://, postgres://, pg://, mongodb://, mongodb+srv://, file:`
|
|
2380
|
-
);
|
|
2381
|
-
}
|
|
2382
|
-
async function createStandaloneStack(config) {
|
|
2383
|
-
const cfg = StandaloneStackConfigSchema.parse(config ?? {});
|
|
2384
|
-
const { ObjectQLPlugin } = await import("@objectstack/objectql");
|
|
2385
|
-
const { MetadataPlugin } = await import("@objectstack/metadata");
|
|
2386
|
-
const { DriverPlugin: DriverPlugin2 } = await Promise.resolve().then(() => (init_driver_plugin(), driver_plugin_exports));
|
|
2387
|
-
const { AppPlugin: AppPlugin2 } = await Promise.resolve().then(() => (init_app_plugin(), app_plugin_exports));
|
|
2388
|
-
const cwd = process.cwd();
|
|
2389
|
-
const environmentId = cfg.environmentId ?? process.env.OS_ENVIRONMENT_ID ?? "proj_local";
|
|
2390
|
-
const artifactPathInput = cfg.artifactPath ?? process.env.OS_ARTIFACT_PATH ?? (0, import_node_path2.resolve)(cwd, "dist/objectstack.json");
|
|
2391
|
-
const artifactPath = isHttpUrl(artifactPathInput) ? artifactPathInput : artifactPathInput.startsWith("/") ? artifactPathInput : (0, import_node_path2.resolve)(cwd, artifactPathInput);
|
|
2392
|
-
const dbUrl = cfg.databaseUrl ?? (0, import_types2.readEnvWithDeprecation)("OS_DATABASE_URL", "DATABASE_URL")?.trim() ?? process.env.TURSO_DATABASE_URL?.trim() ?? (process.env.OS_HOME?.trim() ? `file:${(0, import_node_path2.resolve)(resolveObjectStackHome(), "data/standalone.db")}` : cfg.projectRoot ? `file:${(0, import_node_path2.resolve)(cfg.projectRoot, ".objectstack/data/standalone.db")}` : `file:${(0, import_node_path2.resolve)(resolveObjectStackHome(), "data/standalone.db")}`);
|
|
2393
|
-
const explicitDriver = cfg.databaseDriver ?? process.env.OS_DATABASE_DRIVER?.trim();
|
|
2394
|
-
const dbDriver = explicitDriver ?? detectDriverFromUrl(dbUrl);
|
|
2395
|
-
let driverPlugin;
|
|
2396
|
-
if (dbDriver === "memory") {
|
|
2397
|
-
const { InMemoryDriver } = await import("@objectstack/driver-memory");
|
|
2398
|
-
driverPlugin = new DriverPlugin2(new InMemoryDriver());
|
|
2399
|
-
} else if (dbDriver === "postgres") {
|
|
2400
|
-
const { SqlDriver } = await import("@objectstack/driver-sql");
|
|
2401
|
-
driverPlugin = new DriverPlugin2(
|
|
2402
|
-
new SqlDriver({
|
|
2403
|
-
client: "pg",
|
|
2404
|
-
connection: dbUrl,
|
|
2405
|
-
pool: { min: 0, max: 5 }
|
|
2406
|
-
})
|
|
2407
|
-
);
|
|
2408
|
-
} else if (dbDriver === "mongodb") {
|
|
2409
|
-
let MongoDBDriver;
|
|
2410
|
-
try {
|
|
2411
|
-
({ MongoDBDriver } = await import("@objectstack/driver-mongodb"));
|
|
2412
|
-
} catch (err) {
|
|
2413
|
-
throw new Error(
|
|
2414
|
-
`[StandaloneStack] mongodb URL detected but @objectstack/driver-mongodb is not installed. Add it as a dependency or pass an explicit driverPlugin. (${err?.message ?? err})`
|
|
2415
|
-
);
|
|
2416
|
-
}
|
|
2417
|
-
driverPlugin = new DriverPlugin2(new MongoDBDriver({ url: dbUrl }));
|
|
2418
|
-
} else if (dbDriver === "sqlite-wasm") {
|
|
2419
|
-
const { SqliteWasmDriver } = await import("@objectstack/driver-sqlite-wasm");
|
|
2420
|
-
const filename = dbUrl.replace(/^wasm-sqlite:(\/\/)?/i, "").replace(/^file:(\/\/)?/i, "");
|
|
2421
|
-
if (filename && filename !== ":memory:") {
|
|
2422
|
-
(0, import_node_fs.mkdirSync)((0, import_node_path2.resolve)(filename, ".."), { recursive: true });
|
|
2423
|
-
}
|
|
2424
|
-
driverPlugin = new DriverPlugin2(
|
|
2425
|
-
new SqliteWasmDriver({
|
|
2426
|
-
filename: filename || ":memory:",
|
|
2427
|
-
persist: filename && filename !== ":memory:" ? "on-write" : void 0
|
|
2428
|
-
})
|
|
2429
|
-
);
|
|
2430
|
-
} else {
|
|
2431
|
-
const { SqlDriver } = await import("@objectstack/driver-sql");
|
|
2432
|
-
const filename = dbUrl.replace(/^file:(\/\/)?/, "");
|
|
2433
|
-
if (!filename || /^[a-z][a-z0-9+.-]*:\/\//i.test(filename)) {
|
|
2434
|
-
throw new Error(
|
|
2435
|
-
`[StandaloneStack] sqlite driver was selected but the URL does not look like a file path: "${dbUrl}". Use file:/path/to/db.sqlite, or set OS_DATABASE_DRIVER explicitly.`
|
|
2436
|
-
);
|
|
2437
|
-
}
|
|
2438
|
-
(0, import_node_fs.mkdirSync)((0, import_node_path2.resolve)(filename, ".."), { recursive: true });
|
|
2439
|
-
driverPlugin = new DriverPlugin2(
|
|
2440
|
-
new SqlDriver({
|
|
2441
|
-
client: "better-sqlite3",
|
|
2442
|
-
connection: { filename },
|
|
2443
|
-
useNullAsDefault: true
|
|
2444
|
-
})
|
|
2445
|
-
);
|
|
2446
|
-
}
|
|
2447
|
-
const artifactBundle = await loadArtifactBundle(artifactPath, {
|
|
2448
|
-
tag: "[StandaloneStack]",
|
|
2449
|
-
unwrapEnvelope: true
|
|
2450
|
-
});
|
|
2451
|
-
if (artifactBundle) {
|
|
2452
|
-
const flowsCount = Array.isArray(artifactBundle?.flows) ? artifactBundle.flows.length : "n/a";
|
|
2453
|
-
console.warn(
|
|
2454
|
-
`[StandaloneStack] artifact loaded: path=${artifactPath} keys=${Object.keys(artifactBundle).join(",")} flows=${flowsCount}`
|
|
2455
|
-
);
|
|
2456
|
-
}
|
|
2457
|
-
const plugins = [
|
|
2458
|
-
driverPlugin,
|
|
2459
|
-
new MetadataPlugin({
|
|
2460
|
-
// Source-file scanner OFF — declarative metadata is loaded
|
|
2461
|
-
// from the compiled artifact, not from yaml/json files on
|
|
2462
|
-
// disk. Scanning would also recursively watch the project
|
|
2463
|
-
// root (incl. node_modules), which is expensive and prone
|
|
2464
|
-
// to EMFILE.
|
|
2465
|
-
watch: false,
|
|
2466
|
-
// Artifact-file HMR ON in non-production so edits to
|
|
2467
|
-
// `*.view.ts` / `*.flow.ts` (which the CLI dev-mode watcher
|
|
2468
|
-
// recompiles into `dist/objectstack.json`) are picked up by
|
|
2469
|
-
// the running server WITHOUT requiring a manual restart.
|
|
2470
|
-
// Uses polling under the hood (see plugin.ts) to avoid
|
|
2471
|
-
// `fs.watch` EMFILE on macOS / busy dev hosts.
|
|
2472
|
-
artifactWatch: process.env.NODE_ENV !== "production",
|
|
2473
|
-
environmentId,
|
|
2474
|
-
artifactSource: { mode: "local-file", path: artifactPath }
|
|
2475
|
-
}),
|
|
2476
|
-
new ObjectQLPlugin({ environmentId })
|
|
2477
|
-
];
|
|
2478
|
-
if (artifactBundle) plugins.push(new AppPlugin2(artifactBundle));
|
|
2479
|
-
const requires = Array.isArray(artifactBundle?.requires) ? artifactBundle.requires.filter((c) => typeof c === "string") : void 0;
|
|
2480
|
-
const objects = Array.isArray(artifactBundle?.objects) ? artifactBundle.objects : void 0;
|
|
2481
|
-
const manifest = artifactBundle?.manifest;
|
|
2482
|
-
return {
|
|
2483
|
-
plugins,
|
|
2484
|
-
api: {
|
|
2485
|
-
enableProjectScoping: false,
|
|
2486
|
-
projectResolution: "none"
|
|
2487
|
-
},
|
|
2488
|
-
...requires ? { requires } : {},
|
|
2489
|
-
...objects ? { objects } : {},
|
|
2490
|
-
...manifest ? { manifest } : {}
|
|
2491
|
-
};
|
|
2492
|
-
}
|
|
2565
|
+
// src/index.ts
|
|
2566
|
+
init_standalone_stack();
|
|
2493
2567
|
|
|
2494
2568
|
// src/default-host.ts
|
|
2495
|
-
var
|
|
2496
|
-
var
|
|
2569
|
+
var import_node_path4 = require("path");
|
|
2570
|
+
var import_node_fs3 = require("fs");
|
|
2571
|
+
init_standalone_stack();
|
|
2497
2572
|
init_load_artifact_bundle();
|
|
2498
2573
|
function resolveDefaultArtifactPath(explicitPath, cwd = process.cwd()) {
|
|
2499
|
-
const candidate = explicitPath ?? process.env.OS_ARTIFACT_PATH ?? (0,
|
|
2574
|
+
const candidate = explicitPath ?? process.env.OS_ARTIFACT_PATH ?? (0, import_node_path4.resolve)(cwd, "dist/objectstack.json");
|
|
2500
2575
|
if (isHttpUrl(candidate)) return candidate;
|
|
2501
2576
|
if (explicitPath || process.env.OS_ARTIFACT_PATH) return candidate;
|
|
2502
|
-
return (0,
|
|
2577
|
+
return (0, import_node_fs3.existsSync)(candidate) ? candidate : void 0;
|
|
2503
2578
|
}
|
|
2504
2579
|
async function createDefaultHostConfig(options = {}) {
|
|
2505
2580
|
const { requireArtifact = true, ...standaloneOpts } = options;
|
|
@@ -2511,10 +2586,10 @@ async function createDefaultHostConfig(options = {}) {
|
|
|
2511
2586
|
}
|
|
2512
2587
|
if (!resolvedArtifact && !requireArtifact) {
|
|
2513
2588
|
const home = resolveObjectStackHome();
|
|
2514
|
-
const stubPath = (0,
|
|
2515
|
-
if (!(0,
|
|
2516
|
-
(0,
|
|
2517
|
-
(0,
|
|
2589
|
+
const stubPath = (0, import_node_path4.resolve)(home, "dist/objectstack.json");
|
|
2590
|
+
if (!(0, import_node_fs3.existsSync)(stubPath)) {
|
|
2591
|
+
(0, import_node_fs3.mkdirSync)((0, import_node_path4.resolve)(stubPath, ".."), { recursive: true });
|
|
2592
|
+
(0, import_node_fs3.writeFileSync)(
|
|
2518
2593
|
stubPath,
|
|
2519
2594
|
JSON.stringify(
|
|
2520
2595
|
{
|
|
@@ -2555,6 +2630,7 @@ var import_core2 = require("@objectstack/core");
|
|
|
2555
2630
|
var import_types3 = require("@objectstack/types");
|
|
2556
2631
|
var import_system = require("@objectstack/spec/system");
|
|
2557
2632
|
var import_shared = require("@objectstack/spec/shared");
|
|
2633
|
+
init_package_state_store();
|
|
2558
2634
|
|
|
2559
2635
|
// src/security/resolve-execution-context.ts
|
|
2560
2636
|
function readHeader(headers, name) {
|
|
@@ -3409,7 +3485,7 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3409
3485
|
if (protocol && typeof protocol.saveMetaItem === "function") {
|
|
3410
3486
|
try {
|
|
3411
3487
|
const organizationId = await this.resolveActiveOrganizationId(_context);
|
|
3412
|
-
const result = await protocol.saveMetaItem({ type, name, item: body, organizationId });
|
|
3488
|
+
const result = await protocol.saveMetaItem({ type, name, item: body, organizationId, ...packageId ? { packageId } : {} });
|
|
3413
3489
|
return { handled: true, response: this.success(result) };
|
|
3414
3490
|
} catch (e) {
|
|
3415
3491
|
return { handled: true, response: this.error(e.message, 400) };
|
|
@@ -3749,12 +3825,22 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3749
3825
|
const id = decodeURIComponent(parts[0]);
|
|
3750
3826
|
const pkg = registry.enablePackage(id);
|
|
3751
3827
|
if (!pkg) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
|
|
3828
|
+
try {
|
|
3829
|
+
setPackageDisabled(_context?.environmentId, id, false);
|
|
3830
|
+
} catch (err) {
|
|
3831
|
+
console.warn("[handlePackages] failed to persist enable state", { id, error: err?.message });
|
|
3832
|
+
}
|
|
3752
3833
|
return { handled: true, response: this.success(pkg) };
|
|
3753
3834
|
}
|
|
3754
3835
|
if (parts.length === 2 && parts[1] === "disable" && m === "PATCH") {
|
|
3755
3836
|
const id = decodeURIComponent(parts[0]);
|
|
3756
3837
|
const pkg = registry.disablePackage(id);
|
|
3757
3838
|
if (!pkg) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
|
|
3839
|
+
try {
|
|
3840
|
+
setPackageDisabled(_context?.environmentId, id, true);
|
|
3841
|
+
} catch (err) {
|
|
3842
|
+
console.warn("[handlePackages] failed to persist disable state", { id, error: err?.message });
|
|
3843
|
+
}
|
|
3758
3844
|
return { handled: true, response: this.success(pkg) };
|
|
3759
3845
|
}
|
|
3760
3846
|
if (parts.length === 2 && parts[1] === "publish" && m === "POST") {
|
|
@@ -3775,6 +3861,14 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3775
3861
|
}
|
|
3776
3862
|
return { handled: true, response: this.error("Metadata service not available", 503) };
|
|
3777
3863
|
}
|
|
3864
|
+
if (parts.length === 2 && parts[1] === "export" && m === "GET") {
|
|
3865
|
+
const id = decodeURIComponent(parts[0]);
|
|
3866
|
+
const manifest = await this.assemblePackageManifest(id, registry, _context);
|
|
3867
|
+
if (!manifest) {
|
|
3868
|
+
return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
|
|
3869
|
+
}
|
|
3870
|
+
return { handled: true, response: this.success(manifest) };
|
|
3871
|
+
}
|
|
3778
3872
|
if (parts.length === 1 && m === "GET") {
|
|
3779
3873
|
const id = decodeURIComponent(parts[0]);
|
|
3780
3874
|
const pkg = registry.getPackage(id);
|
|
@@ -3792,6 +3886,83 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3792
3886
|
}
|
|
3793
3887
|
return { handled: false };
|
|
3794
3888
|
}
|
|
3889
|
+
/**
|
|
3890
|
+
* Assemble a portable, offline-installable package manifest from the
|
|
3891
|
+
* `sys_metadata` overlay rows bound to `packageId`.
|
|
3892
|
+
*
|
|
3893
|
+
* The resulting shape mirrors what `marketplace-install-local` →
|
|
3894
|
+
* `manifestService.register()` → `engine.registerApp()` consumes:
|
|
3895
|
+
* `{ id, name, version, objects:[…], views:[…], flows:[…], … }`
|
|
3896
|
+
* where each category key is the PLURAL manifest name and its value is
|
|
3897
|
+
* an array of clean metadata bodies (provenance decorations stripped).
|
|
3898
|
+
*
|
|
3899
|
+
* Only the metadata categories that `registerApp` can actually consume
|
|
3900
|
+
* are exported. `datasources` and `emailTemplates` are intentionally
|
|
3901
|
+
* excluded (not registered by the import path). `tools` / `skills` ARE
|
|
3902
|
+
* round-tripped: they are registered by `registerApp` on import and
|
|
3903
|
+
* surfaced by `getMetaItems('tool' | 'skill')` on export.
|
|
3904
|
+
*
|
|
3905
|
+
* @returns the manifest object, or `null` if the package id is unknown
|
|
3906
|
+
* AND has no overlay-authored metadata.
|
|
3907
|
+
*/
|
|
3908
|
+
async assemblePackageManifest(packageId, registry, context) {
|
|
3909
|
+
const protocol = await this.resolveService("protocol");
|
|
3910
|
+
if (!protocol || typeof protocol.getMetaItems !== "function") return null;
|
|
3911
|
+
const organizationId = await this.resolveActiveOrganizationId(context);
|
|
3912
|
+
const PROVENANCE_KEYS = /* @__PURE__ */ new Set([
|
|
3913
|
+
"_packageId",
|
|
3914
|
+
"_packageVersionId",
|
|
3915
|
+
"_provenance",
|
|
3916
|
+
"_state",
|
|
3917
|
+
"_version",
|
|
3918
|
+
"_organizationId",
|
|
3919
|
+
"_source",
|
|
3920
|
+
"_id",
|
|
3921
|
+
"_rowId"
|
|
3922
|
+
]);
|
|
3923
|
+
const clean = (item) => {
|
|
3924
|
+
if (!item || typeof item !== "object") return item;
|
|
3925
|
+
const out = {};
|
|
3926
|
+
for (const [k, v] of Object.entries(item)) {
|
|
3927
|
+
if (k.startsWith("_") || PROVENANCE_KEYS.has(k)) continue;
|
|
3928
|
+
out[k] = v;
|
|
3929
|
+
}
|
|
3930
|
+
return out;
|
|
3931
|
+
};
|
|
3932
|
+
const exportPluralKeys = Object.keys(import_shared.PLURAL_TO_SINGULAR).filter(
|
|
3933
|
+
(k) => k !== "datasources" && k !== "emailTemplates"
|
|
3934
|
+
);
|
|
3935
|
+
const manifest = {};
|
|
3936
|
+
let total = 0;
|
|
3937
|
+
for (const plural of exportPluralKeys) {
|
|
3938
|
+
const singular = import_shared.PLURAL_TO_SINGULAR[plural];
|
|
3939
|
+
let items = [];
|
|
3940
|
+
try {
|
|
3941
|
+
const res = await protocol.getMetaItems({ type: singular, packageId, organizationId });
|
|
3942
|
+
items = Array.isArray(res?.items) ? res.items : [];
|
|
3943
|
+
} catch {
|
|
3944
|
+
continue;
|
|
3945
|
+
}
|
|
3946
|
+
if (items.length === 0) continue;
|
|
3947
|
+
manifest[plural] = items.map(clean);
|
|
3948
|
+
total += items.length;
|
|
3949
|
+
}
|
|
3950
|
+
const pkg = (() => {
|
|
3951
|
+
try {
|
|
3952
|
+
return registry?.getPackage?.(packageId);
|
|
3953
|
+
} catch {
|
|
3954
|
+
return void 0;
|
|
3955
|
+
}
|
|
3956
|
+
})();
|
|
3957
|
+
if (total === 0 && !pkg) return null;
|
|
3958
|
+
manifest.id = packageId;
|
|
3959
|
+
manifest.name = pkg?.manifest?.name ?? pkg?.name ?? packageId;
|
|
3960
|
+
manifest.version = pkg?.manifest?.version ?? pkg?.version ?? "1.0.0";
|
|
3961
|
+
if (pkg?.manifest?.label ?? pkg?.label) {
|
|
3962
|
+
manifest.label = pkg?.manifest?.label ?? pkg?.label;
|
|
3963
|
+
}
|
|
3964
|
+
return manifest;
|
|
3965
|
+
}
|
|
3795
3966
|
/**
|
|
3796
3967
|
* Cloud / Environment Control-Plane routes.
|
|
3797
3968
|
*
|
|
@@ -6374,6 +6545,14 @@ function createDispatcherPlugin(config = {}) {
|
|
|
6374
6545
|
errorResponse(err, res);
|
|
6375
6546
|
}
|
|
6376
6547
|
});
|
|
6548
|
+
server.get(`${prefix}/packages/:id/export`, async (req, res) => {
|
|
6549
|
+
try {
|
|
6550
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/export`, "GET", {}, req.query, { request: req });
|
|
6551
|
+
sendResult(result, res);
|
|
6552
|
+
} catch (err) {
|
|
6553
|
+
errorResponse(err, res);
|
|
6554
|
+
}
|
|
6555
|
+
});
|
|
6377
6556
|
server.get(`${prefix}/packages/:id`, async (req, res) => {
|
|
6378
6557
|
try {
|
|
6379
6558
|
const result = await dispatcher.handlePackages(`/${req.params.id}`, "GET", {}, req.query, { request: req });
|
|
@@ -7481,7 +7660,7 @@ var ArtifactApiClient = class {
|
|
|
7481
7660
|
};
|
|
7482
7661
|
|
|
7483
7662
|
// src/cloud/artifact-environment-registry.ts
|
|
7484
|
-
var
|
|
7663
|
+
var import_node_path5 = require("path");
|
|
7485
7664
|
var ArtifactEnvironmentRegistry = class {
|
|
7486
7665
|
constructor(config) {
|
|
7487
7666
|
this.hostnameCache = /* @__PURE__ */ new Map();
|
|
@@ -7653,7 +7832,7 @@ async function createDriver(driverType, databaseUrl, authToken) {
|
|
|
7653
7832
|
case "memory": {
|
|
7654
7833
|
const { InMemoryDriver } = await import("@objectstack/driver-memory");
|
|
7655
7834
|
const dbName = databaseUrl.replace(/^memory:\/\//, "").trim();
|
|
7656
|
-
const filePath = dbName ? (0,
|
|
7835
|
+
const filePath = dbName ? (0, import_node_path5.resolve)(process.cwd(), ".objectstack/data/projects", `${dbName}.json`) : void 0;
|
|
7657
7836
|
return new InMemoryDriver({
|
|
7658
7837
|
persistence: filePath ? { type: "file", path: filePath } : "file"
|
|
7659
7838
|
});
|
|
@@ -8470,7 +8649,7 @@ var AuthProxyPlugin = class {
|
|
|
8470
8649
|
};
|
|
8471
8650
|
|
|
8472
8651
|
// src/cloud/cloud-url.ts
|
|
8473
|
-
var DEFAULT_CLOUD_URL = "https://cloud.objectos.
|
|
8652
|
+
var DEFAULT_CLOUD_URL = "https://cloud.objectos.ai";
|
|
8474
8653
|
function resolveCloudUrl(explicit) {
|
|
8475
8654
|
const raw = (explicit ?? process.env.OS_CLOUD_URL ?? "").trim();
|
|
8476
8655
|
const lower = raw.toLowerCase();
|
|
@@ -8892,11 +9071,11 @@ var RuntimeConfigPlugin = class {
|
|
|
8892
9071
|
|
|
8893
9072
|
// src/cloud/file-artifact-api-client.ts
|
|
8894
9073
|
var import_promises2 = require("fs/promises");
|
|
8895
|
-
var
|
|
9074
|
+
var import_node_path6 = require("path");
|
|
8896
9075
|
var FileArtifactApiClient = class {
|
|
8897
9076
|
constructor(config = {}) {
|
|
8898
9077
|
const cwd = process.cwd();
|
|
8899
|
-
this.artifactPath = (0,
|
|
9078
|
+
this.artifactPath = (0, import_node_path6.resolve)(
|
|
8900
9079
|
cwd,
|
|
8901
9080
|
config.artifactPath ?? process.env.OS_ARTIFACT_PATH ?? "dist/objectstack.json"
|
|
8902
9081
|
);
|
|
@@ -8992,7 +9171,7 @@ var FileArtifactApiClient = class {
|
|
|
8992
9171
|
}
|
|
8993
9172
|
defaultLocalSqliteRuntime() {
|
|
8994
9173
|
const cwd = process.cwd();
|
|
8995
|
-
const dbPath = (0,
|
|
9174
|
+
const dbPath = (0, import_node_path6.resolve)(cwd, ".objectstack/data", `${this.environmentId}.db`);
|
|
8996
9175
|
return {
|
|
8997
9176
|
databaseDriver: "sqlite",
|
|
8998
9177
|
databaseUrl: `file:${dbPath}`
|
|
@@ -9145,8 +9324,8 @@ async function createObjectOSStack(config) {
|
|
|
9145
9324
|
}
|
|
9146
9325
|
|
|
9147
9326
|
// src/cloud/marketplace-install-local-plugin.ts
|
|
9148
|
-
var
|
|
9149
|
-
var
|
|
9327
|
+
var import_node_fs4 = require("fs");
|
|
9328
|
+
var import_node_path7 = require("path");
|
|
9150
9329
|
var import_types5 = require("@objectstack/types");
|
|
9151
9330
|
var ROUTE_BASE = "/api/v1/marketplace/install-local";
|
|
9152
9331
|
var DEFAULT_DIR = ".objectstack/installed-packages";
|
|
@@ -9222,9 +9401,6 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9222
9401
|
}
|
|
9223
9402
|
};
|
|
9224
9403
|
this.handleInstall = async (c, ctx) => {
|
|
9225
|
-
if (!this.cloudUrl) {
|
|
9226
|
-
return c.json({ success: false, error: { code: "marketplace_unavailable", message: "OS_CLOUD_URL not configured." } }, 503);
|
|
9227
|
-
}
|
|
9228
9404
|
const userId = await this.requireAuthenticatedUser(c, ctx);
|
|
9229
9405
|
if (!userId) {
|
|
9230
9406
|
return c.json({ success: false, error: { code: "unauthorized", message: "Authentication required to install packages." } }, 401);
|
|
@@ -9234,69 +9410,87 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9234
9410
|
body = await c.req.json();
|
|
9235
9411
|
} catch {
|
|
9236
9412
|
}
|
|
9237
|
-
const
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9413
|
+
const inlineManifest = body?.manifest && typeof body.manifest === "object" ? body.manifest : null;
|
|
9414
|
+
let manifest;
|
|
9415
|
+
let resolvedVersionId;
|
|
9416
|
+
let version;
|
|
9417
|
+
let packageId;
|
|
9418
|
+
if (inlineManifest) {
|
|
9419
|
+
manifest = inlineManifest;
|
|
9420
|
+
packageId = String(manifest.id ?? manifest.name ?? "").trim();
|
|
9421
|
+
version = String(manifest.version ?? "unknown");
|
|
9422
|
+
resolvedVersionId = String(body?.versionId ?? version);
|
|
9423
|
+
if (!packageId) {
|
|
9424
|
+
return c.json({ success: false, error: { code: "invalid_manifest", message: 'Inline manifest must have an "id" or "name".' } }, 400);
|
|
9425
|
+
}
|
|
9426
|
+
} else {
|
|
9427
|
+
if (!this.cloudUrl) {
|
|
9428
|
+
return c.json({ success: false, error: { code: "marketplace_unavailable", message: "OS_CLOUD_URL not configured." } }, 503);
|
|
9429
|
+
}
|
|
9430
|
+
packageId = String(body?.packageId ?? "").trim();
|
|
9431
|
+
const versionId = String(body?.versionId ?? "latest").trim() || "latest";
|
|
9432
|
+
if (!packageId) {
|
|
9433
|
+
return c.json({ success: false, error: { code: "bad_request", message: "packageId is required." } }, 400);
|
|
9434
|
+
}
|
|
9435
|
+
let payload;
|
|
9436
|
+
const publicBase = resolveMarketplacePublicBaseUrl();
|
|
9437
|
+
const fetchAttempts = [];
|
|
9438
|
+
if (publicBase) {
|
|
9439
|
+
fetchAttempts.push({
|
|
9440
|
+
label: "public-r2",
|
|
9441
|
+
url: `${publicBase}/packages/${encodeURIComponent(packageId)}/versions/${encodeURIComponent(versionId)}/manifest.json`
|
|
9442
|
+
});
|
|
9443
|
+
}
|
|
9246
9444
|
fetchAttempts.push({
|
|
9247
|
-
label: "
|
|
9248
|
-
url: `${
|
|
9445
|
+
label: "cloud",
|
|
9446
|
+
url: `${this.cloudUrl}/api/v1/marketplace/packages/${encodeURIComponent(packageId)}/versions/${encodeURIComponent(versionId)}/manifest`
|
|
9249
9447
|
});
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9448
|
+
let lastErrStatus = 0;
|
|
9449
|
+
let lastErrText = "";
|
|
9450
|
+
for (const attempt of fetchAttempts) {
|
|
9451
|
+
try {
|
|
9452
|
+
const resp = await fetch(attempt.url, { headers: { "Accept": "application/json" } });
|
|
9453
|
+
if (!resp.ok) {
|
|
9454
|
+
lastErrStatus = resp.status;
|
|
9455
|
+
lastErrText = (await resp.text().catch(() => "")).slice(0, 200);
|
|
9456
|
+
if (attempt.label === "public-r2" && resp.status === 404) {
|
|
9457
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] public-r2 miss for ${packageId}@${versionId}, falling back to cloud`);
|
|
9458
|
+
continue;
|
|
9459
|
+
}
|
|
9460
|
+
if (attempt.label === "public-r2" && resp.status >= 500) {
|
|
9461
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] public-r2 ${resp.status}, falling back to cloud`);
|
|
9462
|
+
continue;
|
|
9463
|
+
}
|
|
9464
|
+
break;
|
|
9266
9465
|
}
|
|
9267
|
-
|
|
9268
|
-
|
|
9466
|
+
payload = await resp.json();
|
|
9467
|
+
lastErrStatus = 0;
|
|
9468
|
+
break;
|
|
9469
|
+
} catch (err) {
|
|
9470
|
+
if (attempt.label === "public-r2") {
|
|
9471
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] public-r2 fetch error: ${err?.message ?? err}, falling back to cloud`);
|
|
9269
9472
|
continue;
|
|
9270
9473
|
}
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
break;
|
|
9276
|
-
} catch (err) {
|
|
9277
|
-
if (attempt.label === "public-r2") {
|
|
9278
|
-
ctx.logger?.warn?.(`[MarketplaceInstallLocal] public-r2 fetch error: ${err?.message ?? err}, falling back to cloud`);
|
|
9279
|
-
continue;
|
|
9474
|
+
return c.json({
|
|
9475
|
+
success: false,
|
|
9476
|
+
error: { code: "cloud_fetch_failed", message: err?.message ?? String(err) }
|
|
9477
|
+
}, 502);
|
|
9280
9478
|
}
|
|
9479
|
+
}
|
|
9480
|
+
if (!payload) {
|
|
9281
9481
|
return c.json({
|
|
9282
9482
|
success: false,
|
|
9283
|
-
error: { code: "cloud_fetch_failed", message:
|
|
9284
|
-
}, 502);
|
|
9483
|
+
error: { code: "cloud_fetch_failed", message: `Cloud returned ${lastErrStatus}: ${lastErrText}` }
|
|
9484
|
+
}, lastErrStatus === 404 ? 404 : 502);
|
|
9285
9485
|
}
|
|
9486
|
+
const data = payload?.data ?? payload;
|
|
9487
|
+
manifest = data?.manifest;
|
|
9488
|
+
resolvedVersionId = String(data?.version_id ?? versionId);
|
|
9489
|
+
version = String(data?.version ?? "unknown");
|
|
9286
9490
|
}
|
|
9287
|
-
if (!payload) {
|
|
9288
|
-
return c.json({
|
|
9289
|
-
success: false,
|
|
9290
|
-
error: { code: "cloud_fetch_failed", message: `Cloud returned ${lastErrStatus}: ${lastErrText}` }
|
|
9291
|
-
}, lastErrStatus === 404 ? 404 : 502);
|
|
9292
|
-
}
|
|
9293
|
-
const data = payload?.data ?? payload;
|
|
9294
|
-
const manifest = data?.manifest;
|
|
9295
|
-
const resolvedVersionId = String(data?.version_id ?? versionId);
|
|
9296
|
-
const version = String(data?.version ?? "unknown");
|
|
9297
9491
|
const manifestId = String(manifest?.id ?? manifest?.name ?? "");
|
|
9298
9492
|
if (!manifest || !manifestId) {
|
|
9299
|
-
return c.json({ success: false, error: { code: "invalid_manifest", message: "
|
|
9493
|
+
return c.json({ success: false, error: { code: "invalid_manifest", message: "Invalid manifest payload." } }, inlineManifest ? 400 : 502);
|
|
9300
9494
|
}
|
|
9301
9495
|
const conflict = this.findConflict(ctx, manifestId);
|
|
9302
9496
|
if (conflict === "user-code") {
|
|
@@ -9308,6 +9502,18 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9308
9502
|
}
|
|
9309
9503
|
}, 409);
|
|
9310
9504
|
}
|
|
9505
|
+
try {
|
|
9506
|
+
const manifestService = ctx.getService("manifest");
|
|
9507
|
+
manifestService.register(manifest);
|
|
9508
|
+
} catch (err) {
|
|
9509
|
+
if (inlineManifest) {
|
|
9510
|
+
return c.json({
|
|
9511
|
+
success: false,
|
|
9512
|
+
error: { code: "register_failed", message: `Failed to register imported manifest: ${err?.message ?? err}` }
|
|
9513
|
+
}, 422);
|
|
9514
|
+
}
|
|
9515
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] hot-register failed for ${manifestId} (will load on next restart): ${err?.message ?? err}`);
|
|
9516
|
+
}
|
|
9311
9517
|
const entry = {
|
|
9312
9518
|
packageId,
|
|
9313
9519
|
versionId: resolvedVersionId,
|
|
@@ -9319,20 +9525,14 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9319
9525
|
withSampleData: false
|
|
9320
9526
|
};
|
|
9321
9527
|
try {
|
|
9322
|
-
(0,
|
|
9323
|
-
(0,
|
|
9528
|
+
(0, import_node_fs4.mkdirSync)(this.storageDir, { recursive: true });
|
|
9529
|
+
(0, import_node_fs4.writeFileSync)((0, import_node_path7.join)(this.storageDir, safeFilename(manifestId)), JSON.stringify(entry, null, 2), "utf8");
|
|
9324
9530
|
} catch (err) {
|
|
9325
9531
|
return c.json({
|
|
9326
9532
|
success: false,
|
|
9327
9533
|
error: { code: "storage_failed", message: `Failed to persist manifest: ${err?.message ?? err}` }
|
|
9328
9534
|
}, 500);
|
|
9329
9535
|
}
|
|
9330
|
-
try {
|
|
9331
|
-
const manifestService = ctx.getService("manifest");
|
|
9332
|
-
manifestService.register(manifest);
|
|
9333
|
-
} catch (err) {
|
|
9334
|
-
ctx.logger?.warn?.(`[MarketplaceInstallLocal] hot-register failed for ${manifestId} (will load on next restart): ${err?.message ?? err}`);
|
|
9335
|
-
}
|
|
9336
9536
|
try {
|
|
9337
9537
|
const ql = ctx.getService("objectql");
|
|
9338
9538
|
if (ql && typeof ql.syncSchemas === "function") {
|
|
@@ -9346,7 +9546,7 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9346
9546
|
if (seededSummary.seeded.mode === "inline" && (seededSummary.seeded.inserted ?? 0) + (seededSummary.seeded.updated ?? 0) > 0) {
|
|
9347
9547
|
entry.withSampleData = true;
|
|
9348
9548
|
try {
|
|
9349
|
-
(0,
|
|
9549
|
+
(0, import_node_fs4.writeFileSync)((0, import_node_path7.join)(this.storageDir, safeFilename(manifestId)), JSON.stringify(entry, null, 2), "utf8");
|
|
9350
9550
|
} catch {
|
|
9351
9551
|
}
|
|
9352
9552
|
}
|
|
@@ -9393,12 +9593,12 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9393
9593
|
if (!manifestId) {
|
|
9394
9594
|
return c.json({ success: false, error: { code: "bad_request", message: "manifestId path param required." } }, 400);
|
|
9395
9595
|
}
|
|
9396
|
-
const file = (0,
|
|
9397
|
-
if (!(0,
|
|
9596
|
+
const file = (0, import_node_path7.join)(this.storageDir, safeFilename(manifestId));
|
|
9597
|
+
if (!(0, import_node_fs4.existsSync)(file)) {
|
|
9398
9598
|
return c.json({ success: false, error: { code: "not_found", message: `No marketplace install for ${manifestId}.` } }, 404);
|
|
9399
9599
|
}
|
|
9400
9600
|
try {
|
|
9401
|
-
(0,
|
|
9601
|
+
(0, import_node_fs4.unlinkSync)(file);
|
|
9402
9602
|
} catch (err) {
|
|
9403
9603
|
return c.json({ success: false, error: { code: "storage_failed", message: err?.message ?? String(err) } }, 500);
|
|
9404
9604
|
}
|
|
@@ -9421,7 +9621,7 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9421
9621
|
* (refuse to avoid silently overwriting authored code)
|
|
9422
9622
|
*/
|
|
9423
9623
|
this.findConflict = (ctx, manifestId) => {
|
|
9424
|
-
if ((0,
|
|
9624
|
+
if ((0, import_node_fs4.existsSync)((0, import_node_path7.join)(this.storageDir, safeFilename(manifestId)))) {
|
|
9425
9625
|
return "marketplace";
|
|
9426
9626
|
}
|
|
9427
9627
|
try {
|
|
@@ -9463,13 +9663,13 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9463
9663
|
if (!manifestId) {
|
|
9464
9664
|
return c.json({ success: false, error: { code: "bad_request", message: "manifestId path param required." } }, 400);
|
|
9465
9665
|
}
|
|
9466
|
-
const file = (0,
|
|
9467
|
-
if (!(0,
|
|
9666
|
+
const file = (0, import_node_path7.join)(this.storageDir, safeFilename(manifestId));
|
|
9667
|
+
if (!(0, import_node_fs4.existsSync)(file)) {
|
|
9468
9668
|
return c.json({ success: false, error: { code: "not_found", message: `No marketplace install for ${manifestId}.` } }, 404);
|
|
9469
9669
|
}
|
|
9470
9670
|
let entry;
|
|
9471
9671
|
try {
|
|
9472
|
-
entry = JSON.parse((0,
|
|
9672
|
+
entry = JSON.parse((0, import_node_fs4.readFileSync)(file, "utf8"));
|
|
9473
9673
|
} catch (err) {
|
|
9474
9674
|
return c.json({ success: false, error: { code: "storage_failed", message: `Failed to read manifest cache: ${err?.message ?? err}` } }, 500);
|
|
9475
9675
|
}
|
|
@@ -9485,7 +9685,7 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9485
9685
|
}
|
|
9486
9686
|
try {
|
|
9487
9687
|
entry.withSampleData = true;
|
|
9488
|
-
(0,
|
|
9688
|
+
(0, import_node_fs4.writeFileSync)(file, JSON.stringify(entry, null, 2), "utf8");
|
|
9489
9689
|
} catch {
|
|
9490
9690
|
}
|
|
9491
9691
|
return c.json({
|
|
@@ -9517,13 +9717,13 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9517
9717
|
if (!manifestId) {
|
|
9518
9718
|
return c.json({ success: false, error: { code: "bad_request", message: "manifestId path param required." } }, 400);
|
|
9519
9719
|
}
|
|
9520
|
-
const file = (0,
|
|
9521
|
-
if (!(0,
|
|
9720
|
+
const file = (0, import_node_path7.join)(this.storageDir, safeFilename(manifestId));
|
|
9721
|
+
if (!(0, import_node_fs4.existsSync)(file)) {
|
|
9522
9722
|
return c.json({ success: false, error: { code: "not_found", message: `No marketplace install for ${manifestId}.` } }, 404);
|
|
9523
9723
|
}
|
|
9524
9724
|
let entry;
|
|
9525
9725
|
try {
|
|
9526
|
-
entry = JSON.parse((0,
|
|
9726
|
+
entry = JSON.parse((0, import_node_fs4.readFileSync)(file, "utf8"));
|
|
9527
9727
|
} catch (err) {
|
|
9528
9728
|
return c.json({ success: false, error: { code: "storage_failed", message: `Failed to read manifest cache: ${err?.message ?? err}` } }, 500);
|
|
9529
9729
|
}
|
|
@@ -9572,7 +9772,7 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9572
9772
|
}
|
|
9573
9773
|
try {
|
|
9574
9774
|
entry.withSampleData = false;
|
|
9575
|
-
(0,
|
|
9775
|
+
(0, import_node_fs4.writeFileSync)(file, JSON.stringify(entry, null, 2), "utf8");
|
|
9576
9776
|
} catch {
|
|
9577
9777
|
}
|
|
9578
9778
|
ctx.logger?.info?.(`[MarketplaceInstallLocal] purged ${manifestId}: deleted=${deleted} skipped=${skipped} errors=${errors}`);
|
|
@@ -9771,12 +9971,12 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9771
9971
|
return null;
|
|
9772
9972
|
};
|
|
9773
9973
|
this.readAll = () => {
|
|
9774
|
-
if (!(0,
|
|
9974
|
+
if (!(0, import_node_fs4.existsSync)(this.storageDir)) return [];
|
|
9775
9975
|
const out = [];
|
|
9776
|
-
for (const name of (0,
|
|
9976
|
+
for (const name of (0, import_node_fs4.readdirSync)(this.storageDir)) {
|
|
9777
9977
|
if (!name.endsWith(".json")) continue;
|
|
9778
9978
|
try {
|
|
9779
|
-
const raw = (0,
|
|
9979
|
+
const raw = (0, import_node_fs4.readFileSync)((0, import_node_path7.join)(this.storageDir, name), "utf8");
|
|
9780
9980
|
out.push(JSON.parse(raw));
|
|
9781
9981
|
} catch {
|
|
9782
9982
|
}
|
|
@@ -9784,7 +9984,7 @@ var MarketplaceInstallLocalPlugin = class {
|
|
|
9784
9984
|
return out;
|
|
9785
9985
|
};
|
|
9786
9986
|
this.cloudUrl = resolveCloudUrl(config.controlPlaneUrl);
|
|
9787
|
-
this.storageDir = config.storageDir ? (0,
|
|
9987
|
+
this.storageDir = config.storageDir ? (0, import_node_path7.resolve)(config.storageDir) : (0, import_node_path7.resolve)(process.cwd(), DEFAULT_DIR);
|
|
9788
9988
|
}
|
|
9789
9989
|
};
|
|
9790
9990
|
|