@objectstack/runtime 5.0.0 → 5.2.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 +716 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +237 -5
- package/dist/index.d.ts +237 -5
- package/dist/index.js +713 -41
- package/dist/index.js.map +1 -1
- package/package.json +17 -16
package/dist/index.js
CHANGED
|
@@ -178,6 +178,10 @@ var init_driver_plugin = __esm({
|
|
|
178
178
|
});
|
|
179
179
|
|
|
180
180
|
// src/seed-loader.ts
|
|
181
|
+
var seed_loader_exports = {};
|
|
182
|
+
__export(seed_loader_exports, {
|
|
183
|
+
SeedLoaderService: () => SeedLoaderService
|
|
184
|
+
});
|
|
181
185
|
import { SeedLoaderConfigSchema } from "@objectstack/spec/data";
|
|
182
186
|
import { resolveSeedRecord } from "@objectstack/formula";
|
|
183
187
|
var DEFAULT_EXTERNAL_ID_FIELD, _SeedLoaderService, SeedLoaderService;
|
|
@@ -915,7 +919,7 @@ var init_quickjs_runner = __esm({
|
|
|
915
919
|
evalRes.value.dispose();
|
|
916
920
|
let pumps = 0;
|
|
917
921
|
while (pumps < 1e3) {
|
|
918
|
-
await new Promise((
|
|
922
|
+
await new Promise((resolve2) => setImmediate(resolve2));
|
|
919
923
|
const pending = runtime.executePendingJobs();
|
|
920
924
|
if (pending.error) {
|
|
921
925
|
const err = vm.dump(pending.error);
|
|
@@ -9198,7 +9202,7 @@ var require_utils = __commonJS({
|
|
|
9198
9202
|
}
|
|
9199
9203
|
}
|
|
9200
9204
|
function get(url, options = {}) {
|
|
9201
|
-
return new Promise((
|
|
9205
|
+
return new Promise((resolve2, reject) => {
|
|
9202
9206
|
let timeoutId;
|
|
9203
9207
|
const request = http.get(url, options, (response) => {
|
|
9204
9208
|
response.setEncoding("utf8");
|
|
@@ -9206,7 +9210,7 @@ var require_utils = __commonJS({
|
|
|
9206
9210
|
response.on("data", (chunk) => body += chunk);
|
|
9207
9211
|
response.on("end", () => {
|
|
9208
9212
|
(0, timers_1.clearTimeout)(timeoutId);
|
|
9209
|
-
|
|
9213
|
+
resolve2({ status: response.statusCode, body });
|
|
9210
9214
|
});
|
|
9211
9215
|
}).on("error", (error2) => {
|
|
9212
9216
|
(0, timers_1.clearTimeout)(timeoutId);
|
|
@@ -9225,13 +9229,13 @@ var require_utils = __commonJS({
|
|
|
9225
9229
|
return host && match.test(host.toLowerCase()) ? true : false;
|
|
9226
9230
|
}
|
|
9227
9231
|
function promiseWithResolvers() {
|
|
9228
|
-
let
|
|
9232
|
+
let resolve2;
|
|
9229
9233
|
let reject;
|
|
9230
9234
|
const promise = new Promise(function withResolversExecutor(promiseResolve, promiseReject) {
|
|
9231
|
-
|
|
9235
|
+
resolve2 = promiseResolve;
|
|
9232
9236
|
reject = promiseReject;
|
|
9233
9237
|
});
|
|
9234
|
-
return { promise, resolve, reject };
|
|
9238
|
+
return { promise, resolve: resolve2, reject };
|
|
9235
9239
|
}
|
|
9236
9240
|
function squashError(_error) {
|
|
9237
9241
|
return;
|
|
@@ -9242,8 +9246,8 @@ var require_utils = __commonJS({
|
|
|
9242
9246
|
exports.randomBytes = randomBytes;
|
|
9243
9247
|
async function once(ee, name, options) {
|
|
9244
9248
|
options?.signal?.throwIfAborted();
|
|
9245
|
-
const { promise, resolve, reject } = promiseWithResolvers();
|
|
9246
|
-
const onEvent = (data) =>
|
|
9249
|
+
const { promise, resolve: resolve2, reject } = promiseWithResolvers();
|
|
9250
|
+
const onEvent = (data) => resolve2(data);
|
|
9247
9251
|
const onError = (error2) => reject(error2);
|
|
9248
9252
|
const abortListener = addAbortListener(options?.signal, function() {
|
|
9249
9253
|
reject(this.reason);
|
|
@@ -11844,13 +11848,13 @@ var require_mongo_logger = __commonJS({
|
|
|
11844
11848
|
function createStdioLogger(stream) {
|
|
11845
11849
|
return {
|
|
11846
11850
|
write: (log) => {
|
|
11847
|
-
return new Promise((
|
|
11851
|
+
return new Promise((resolve2, reject) => {
|
|
11848
11852
|
const logLine = (0, util_1.inspect)(log, { compact: true, breakLength: Infinity });
|
|
11849
11853
|
stream.write(`${logLine}
|
|
11850
11854
|
`, "utf-8", (error2) => {
|
|
11851
11855
|
if (error2)
|
|
11852
11856
|
return reject(error2);
|
|
11853
|
-
|
|
11857
|
+
resolve2(true);
|
|
11854
11858
|
});
|
|
11855
11859
|
});
|
|
11856
11860
|
}
|
|
@@ -22625,20 +22629,20 @@ var require_compression = __commonJS({
|
|
|
22625
22629
|
]);
|
|
22626
22630
|
var ZSTD_COMPRESSION_LEVEL = 3;
|
|
22627
22631
|
var zlibInflate = (buf) => {
|
|
22628
|
-
return new Promise((
|
|
22632
|
+
return new Promise((resolve2, reject) => {
|
|
22629
22633
|
zlib.inflate(buf, (error2, result) => {
|
|
22630
22634
|
if (error2)
|
|
22631
22635
|
return reject(error2);
|
|
22632
|
-
|
|
22636
|
+
resolve2(result);
|
|
22633
22637
|
});
|
|
22634
22638
|
});
|
|
22635
22639
|
};
|
|
22636
22640
|
var zlibDeflate = (buf, options) => {
|
|
22637
|
-
return new Promise((
|
|
22641
|
+
return new Promise((resolve2, reject) => {
|
|
22638
22642
|
zlib.deflate(buf, options, (error2, result) => {
|
|
22639
22643
|
if (error2)
|
|
22640
22644
|
return reject(error2);
|
|
22641
|
-
|
|
22645
|
+
resolve2(result);
|
|
22642
22646
|
});
|
|
22643
22647
|
});
|
|
22644
22648
|
};
|
|
@@ -23359,7 +23363,7 @@ var require_state_machine = __commonJS({
|
|
|
23359
23363
|
socket = tls.connect(socketOptions, () => {
|
|
23360
23364
|
socket.write(message);
|
|
23361
23365
|
});
|
|
23362
|
-
const { promise: willResolveKmsRequest, reject: rejectOnTlsSocketError, resolve } = (0, utils_1.promiseWithResolvers)();
|
|
23366
|
+
const { promise: willResolveKmsRequest, reject: rejectOnTlsSocketError, resolve: resolve2 } = (0, utils_1.promiseWithResolvers)();
|
|
23363
23367
|
abortListener = (0, utils_1.addAbortListener)(options?.signal, function() {
|
|
23364
23368
|
destroySockets();
|
|
23365
23369
|
rejectOnTlsSocketError(this.reason);
|
|
@@ -23371,7 +23375,7 @@ var require_state_machine = __commonJS({
|
|
|
23371
23375
|
request.addResponse(buffer.read(bytesNeeded));
|
|
23372
23376
|
}
|
|
23373
23377
|
if (request.bytesNeeded <= 0) {
|
|
23374
|
-
|
|
23378
|
+
resolve2();
|
|
23375
23379
|
}
|
|
23376
23380
|
});
|
|
23377
23381
|
await (options?.timeoutContext?.csotEnabled() ? Promise.all([
|
|
@@ -24952,8 +24956,8 @@ var require_on_data = __commonJS({
|
|
|
24952
24956
|
}
|
|
24953
24957
|
if (finished)
|
|
24954
24958
|
return closeHandler();
|
|
24955
|
-
const { promise, resolve, reject } = (0, utils_1.promiseWithResolvers)();
|
|
24956
|
-
unconsumedPromises.push({ resolve, reject });
|
|
24959
|
+
const { promise, resolve: resolve2, reject } = (0, utils_1.promiseWithResolvers)();
|
|
24960
|
+
unconsumedPromises.push({ resolve: resolve2, reject });
|
|
24957
24961
|
return promise;
|
|
24958
24962
|
},
|
|
24959
24963
|
return() {
|
|
@@ -26120,13 +26124,13 @@ var require_connect = __commonJS({
|
|
|
26120
26124
|
socket.setNoDelay(noDelay);
|
|
26121
26125
|
socket.setTimeout(connectTimeoutMS);
|
|
26122
26126
|
let cancellationHandler = null;
|
|
26123
|
-
const { promise: connectedSocket, resolve, reject } = (0, utils_1.promiseWithResolvers)();
|
|
26127
|
+
const { promise: connectedSocket, resolve: resolve2, reject } = (0, utils_1.promiseWithResolvers)();
|
|
26124
26128
|
if (existingSocket) {
|
|
26125
|
-
|
|
26129
|
+
resolve2(socket);
|
|
26126
26130
|
} else {
|
|
26127
26131
|
const start = performance.now();
|
|
26128
26132
|
const connectEvent = useTLS ? "secureConnect" : "connect";
|
|
26129
|
-
socket.once(connectEvent, () =>
|
|
26133
|
+
socket.once(connectEvent, () => resolve2(socket)).once("error", (cause) => reject(new error_1.MongoNetworkError(error_1.MongoError.buildErrorMessage(cause), { cause }))).once("timeout", () => {
|
|
26130
26134
|
reject(new error_1.MongoNetworkTimeoutError(`Socket '${connectEvent}' timed out after ${performance.now() - start | 0}ms (connectTimeoutMS: ${connectTimeoutMS})`));
|
|
26131
26135
|
}).once("close", () => reject(new error_1.MongoNetworkError(`Socket closed after ${performance.now() - start | 0} during connection establishment`)));
|
|
26132
26136
|
if (options.cancellationToken != null) {
|
|
@@ -26650,10 +26654,10 @@ var require_connection_pool = __commonJS({
|
|
|
26650
26654
|
async checkOut(options) {
|
|
26651
26655
|
const checkoutTime = (0, utils_1.processTimeMS)();
|
|
26652
26656
|
this.emitAndLog(_ConnectionPool.CONNECTION_CHECK_OUT_STARTED, new connection_pool_events_1.ConnectionCheckOutStartedEvent(this));
|
|
26653
|
-
const { promise, resolve, reject } = (0, utils_1.promiseWithResolvers)();
|
|
26657
|
+
const { promise, resolve: resolve2, reject } = (0, utils_1.promiseWithResolvers)();
|
|
26654
26658
|
const timeout = options.timeoutContext.connectionCheckoutTimeout;
|
|
26655
26659
|
const waitQueueMember = {
|
|
26656
|
-
resolve,
|
|
26660
|
+
resolve: resolve2,
|
|
26657
26661
|
reject,
|
|
26658
26662
|
cancelled: false,
|
|
26659
26663
|
checkoutTime
|
|
@@ -27888,13 +27892,13 @@ var require_connection_string = __commonJS({
|
|
|
27888
27892
|
var LB_REPLICA_SET_ERROR = "loadBalanced option not supported with a replicaSet option";
|
|
27889
27893
|
var LB_DIRECT_CONNECTION_ERROR = "loadBalanced option not supported when directConnection is provided";
|
|
27890
27894
|
function retryDNSTimeoutFor(rrtype) {
|
|
27891
|
-
const
|
|
27895
|
+
const resolve2 = rrtype === "SRV" ? (address) => dns.promises.resolve(address, "SRV") : (address) => dns.promises.resolve(address, "TXT");
|
|
27892
27896
|
return async function dnsReqRetryTimeout(lookupAddress) {
|
|
27893
27897
|
try {
|
|
27894
|
-
return await
|
|
27898
|
+
return await resolve2(lookupAddress);
|
|
27895
27899
|
} catch (firstDNSError) {
|
|
27896
27900
|
if (firstDNSError.code === dns.TIMEOUT) {
|
|
27897
|
-
return await
|
|
27901
|
+
return await resolve2(lookupAddress);
|
|
27898
27902
|
} else {
|
|
27899
27903
|
throw firstDNSError;
|
|
27900
27904
|
}
|
|
@@ -31533,13 +31537,13 @@ var require_topology = __commonJS({
|
|
|
31533
31537
|
}
|
|
31534
31538
|
return transaction.server;
|
|
31535
31539
|
}
|
|
31536
|
-
const { promise: serverPromise, resolve, reject } = (0, utils_1.promiseWithResolvers)();
|
|
31540
|
+
const { promise: serverPromise, resolve: resolve2, reject } = (0, utils_1.promiseWithResolvers)();
|
|
31537
31541
|
const waitQueueMember = {
|
|
31538
31542
|
serverSelector,
|
|
31539
31543
|
topologyDescription: this.description,
|
|
31540
31544
|
mongoLogger: this.client.mongoLogger,
|
|
31541
31545
|
transaction,
|
|
31542
|
-
resolve,
|
|
31546
|
+
resolve: resolve2,
|
|
31543
31547
|
reject,
|
|
31544
31548
|
cancelled: false,
|
|
31545
31549
|
startTime: (0, utils_1.processTimeMS)(),
|
|
@@ -35158,12 +35162,28 @@ import { ObjectKernel as ObjectKernel4 } from "@objectstack/core";
|
|
|
35158
35162
|
|
|
35159
35163
|
// src/runtime.ts
|
|
35160
35164
|
import { ObjectKernel } from "@objectstack/core";
|
|
35165
|
+
import {
|
|
35166
|
+
ClusterServicePlugin,
|
|
35167
|
+
MetadataClusterBridgePlugin
|
|
35168
|
+
} from "@objectstack/service-cluster";
|
|
35161
35169
|
var Runtime = class {
|
|
35162
35170
|
constructor(config = {}) {
|
|
35163
35171
|
this.kernel = new ObjectKernel(config.kernel);
|
|
35164
35172
|
if (config.server) {
|
|
35165
35173
|
this.kernel.registerService("http.server", config.server);
|
|
35166
35174
|
}
|
|
35175
|
+
if (config.cluster !== false) {
|
|
35176
|
+
const opts = this.normalizeClusterOptions(config.cluster);
|
|
35177
|
+
this.kernel.use(new ClusterServicePlugin(opts));
|
|
35178
|
+
this.kernel.use(new MetadataClusterBridgePlugin());
|
|
35179
|
+
}
|
|
35180
|
+
}
|
|
35181
|
+
normalizeClusterOptions(raw) {
|
|
35182
|
+
if (!raw) return {};
|
|
35183
|
+
if (typeof raw === "object" && ("cluster" in raw || "config" in raw) && !("driver" in raw)) {
|
|
35184
|
+
return raw;
|
|
35185
|
+
}
|
|
35186
|
+
return { config: raw };
|
|
35167
35187
|
}
|
|
35168
35188
|
/**
|
|
35169
35189
|
* Register a plugin
|
|
@@ -35465,6 +35485,23 @@ async function resolveExecutionContext(opts) {
|
|
|
35465
35485
|
}
|
|
35466
35486
|
}
|
|
35467
35487
|
}
|
|
35488
|
+
if (tenantId) {
|
|
35489
|
+
const orgMembers = await tryFind(
|
|
35490
|
+
ql,
|
|
35491
|
+
"sys_member",
|
|
35492
|
+
{ organization_id: tenantId },
|
|
35493
|
+
1e3
|
|
35494
|
+
);
|
|
35495
|
+
const orgUserIds = Array.from(
|
|
35496
|
+
new Set(
|
|
35497
|
+
orgMembers.map((m) => m.user_id ?? m.userId).filter((v) => typeof v === "string" && v.length > 0)
|
|
35498
|
+
)
|
|
35499
|
+
);
|
|
35500
|
+
if (!orgUserIds.includes(userId)) orgUserIds.push(userId);
|
|
35501
|
+
ctx.org_user_ids = orgUserIds;
|
|
35502
|
+
} else {
|
|
35503
|
+
ctx.org_user_ids = [userId];
|
|
35504
|
+
}
|
|
35468
35505
|
const upsRows = await tryFind(
|
|
35469
35506
|
ql,
|
|
35470
35507
|
"sys_user_permission_set",
|
|
@@ -38739,6 +38776,47 @@ function safeReport(reporter, err, ctx) {
|
|
|
38739
38776
|
}
|
|
38740
38777
|
}
|
|
38741
38778
|
|
|
38779
|
+
// src/observability/observability-service-plugin.ts
|
|
38780
|
+
import {
|
|
38781
|
+
OBSERVABILITY_METRICS_SERVICE,
|
|
38782
|
+
OBSERVABILITY_ERRORS_SERVICE
|
|
38783
|
+
} from "@objectstack/observability";
|
|
38784
|
+
var ObservabilityServicePlugin = class {
|
|
38785
|
+
constructor(options = {}) {
|
|
38786
|
+
this.name = "com.objectstack.observability.service";
|
|
38787
|
+
this.version = "1.0.0";
|
|
38788
|
+
this.type = "standard";
|
|
38789
|
+
this.options = options;
|
|
38790
|
+
}
|
|
38791
|
+
async init(ctx) {
|
|
38792
|
+
const metrics = this.options.metrics ?? new NoopMetricsRegistry();
|
|
38793
|
+
const errors2 = this.options.errors ?? new NoopErrorReporter();
|
|
38794
|
+
ctx.registerService(OBSERVABILITY_METRICS_SERVICE, metrics);
|
|
38795
|
+
ctx.registerService(OBSERVABILITY_ERRORS_SERVICE, errors2);
|
|
38796
|
+
ctx.logger.info(
|
|
38797
|
+
`ObservabilityServicePlugin: registered metrics=${metrics.constructor?.name ?? "unknown"} errors=${errors2.constructor?.name ?? "unknown"}`
|
|
38798
|
+
);
|
|
38799
|
+
}
|
|
38800
|
+
};
|
|
38801
|
+
function resolveMetrics(ctx, override) {
|
|
38802
|
+
if (override) return override;
|
|
38803
|
+
try {
|
|
38804
|
+
const m = ctx.getService(OBSERVABILITY_METRICS_SERVICE);
|
|
38805
|
+
if (m) return m;
|
|
38806
|
+
} catch {
|
|
38807
|
+
}
|
|
38808
|
+
return new NoopMetricsRegistry();
|
|
38809
|
+
}
|
|
38810
|
+
function resolveErrorReporter(ctx, override) {
|
|
38811
|
+
if (override) return override;
|
|
38812
|
+
try {
|
|
38813
|
+
const e = ctx.getService(OBSERVABILITY_ERRORS_SERVICE);
|
|
38814
|
+
if (e) return e;
|
|
38815
|
+
} catch {
|
|
38816
|
+
}
|
|
38817
|
+
return new NoopErrorReporter();
|
|
38818
|
+
}
|
|
38819
|
+
|
|
38742
38820
|
// src/dispatcher-plugin.ts
|
|
38743
38821
|
function mountRouteOnServer(route, server, routePath, securityHeaders) {
|
|
38744
38822
|
const handler = async (req, res) => {
|
|
@@ -40462,7 +40540,39 @@ var ArtifactKernelFactory = class {
|
|
|
40462
40540
|
// intentionally do NOT pass crossSubDomainCookies here
|
|
40463
40541
|
// so cookies stay isolated per project subdomain.
|
|
40464
40542
|
trustedOrigins: trustedOriginsList.length ? trustedOriginsList : void 0,
|
|
40465
|
-
...oidcProviders ? { oidcProviders } : {}
|
|
40543
|
+
...oidcProviders ? { oidcProviders } : {},
|
|
40544
|
+
// Auto-provision a personal organization for every new
|
|
40545
|
+
// user. SecurityPlugin's ObjectQL middleware does this
|
|
40546
|
+
// for direct `ql.insert` calls, but better-auth's
|
|
40547
|
+
// adapter writes through `dataEngine` directly,
|
|
40548
|
+
// bypassing that middleware — so JIT-created SSO users
|
|
40549
|
+
// would otherwise land on the empty "create
|
|
40550
|
+
// organization" screen on first login.
|
|
40551
|
+
databaseHooks: {
|
|
40552
|
+
user: {
|
|
40553
|
+
create: {
|
|
40554
|
+
after: async (user) => {
|
|
40555
|
+
try {
|
|
40556
|
+
const ql = kernel.getService("objectql");
|
|
40557
|
+
if (!ql) return;
|
|
40558
|
+
const [{ ensureUserHasOrganization, cloneTenantSeedData }] = await Promise.all([
|
|
40559
|
+
import("@objectstack/plugin-security")
|
|
40560
|
+
]);
|
|
40561
|
+
await ensureUserHasOrganization(ql, user, {
|
|
40562
|
+
logger: this.logger,
|
|
40563
|
+
cloneSeedData: cloneTenantSeedData
|
|
40564
|
+
});
|
|
40565
|
+
} catch (e) {
|
|
40566
|
+
this.logger.warn?.("[ArtifactKernelFactory] auto-org provisioning hook failed", {
|
|
40567
|
+
projectId,
|
|
40568
|
+
userId: user?.id,
|
|
40569
|
+
error: e?.message
|
|
40570
|
+
});
|
|
40571
|
+
}
|
|
40572
|
+
}
|
|
40573
|
+
}
|
|
40574
|
+
}
|
|
40575
|
+
}
|
|
40466
40576
|
}));
|
|
40467
40577
|
if (oidcProviders) {
|
|
40468
40578
|
this.logger.info?.("[ArtifactKernelFactory] platform SSO wired", {
|
|
@@ -40792,17 +40902,6 @@ var AuthProxyPlugin = class {
|
|
|
40792
40902
|
}
|
|
40793
40903
|
}
|
|
40794
40904
|
try {
|
|
40795
|
-
try {
|
|
40796
|
-
const pubCfg = typeof authSvc?.getPublicConfig === "function" ? authSvc.getPublicConfig() : null;
|
|
40797
|
-
const ssoProviders = Array.isArray(pubCfg?.socialProviders) ? pubCfg.socialProviders : [];
|
|
40798
|
-
const ssoWired = ssoProviders.some(
|
|
40799
|
-
(p) => p?.enabled !== false && p?.id === "objectstack-cloud"
|
|
40800
|
-
);
|
|
40801
|
-
if (ssoWired) {
|
|
40802
|
-
return c.json({ hasOwner: true });
|
|
40803
|
-
}
|
|
40804
|
-
} catch {
|
|
40805
|
-
}
|
|
40806
40905
|
const dataEngine = typeof authSvc?.getDataEngine === "function" ? authSvc.getDataEngine() : null;
|
|
40807
40906
|
if (!dataEngine || typeof dataEngine.count !== "function") {
|
|
40808
40907
|
return c.json({ hasOwner: true });
|
|
@@ -40866,6 +40965,114 @@ var AuthProxyPlugin = class {
|
|
|
40866
40965
|
}
|
|
40867
40966
|
};
|
|
40868
40967
|
|
|
40968
|
+
// src/cloud/cloud-url.ts
|
|
40969
|
+
var DEFAULT_CLOUD_URL = "https://cloud.objectos.app";
|
|
40970
|
+
function resolveCloudUrl(explicit) {
|
|
40971
|
+
const raw = (explicit ?? process.env.OS_CLOUD_URL ?? "").trim();
|
|
40972
|
+
const lower = raw.toLowerCase();
|
|
40973
|
+
if (lower === "off" || lower === "none" || lower === "local" || lower === "disabled") {
|
|
40974
|
+
return "";
|
|
40975
|
+
}
|
|
40976
|
+
const picked = raw || DEFAULT_CLOUD_URL;
|
|
40977
|
+
return picked.replace(/\/+$/, "");
|
|
40978
|
+
}
|
|
40979
|
+
|
|
40980
|
+
// src/cloud/marketplace-proxy-plugin.ts
|
|
40981
|
+
var MARKETPLACE_PREFIX = "/api/v1/marketplace";
|
|
40982
|
+
var MarketplaceProxyPlugin = class _MarketplaceProxyPlugin {
|
|
40983
|
+
constructor(config = {}) {
|
|
40984
|
+
this.name = "com.objectstack.runtime.marketplace-proxy";
|
|
40985
|
+
this.version = "1.0.0";
|
|
40986
|
+
this.init = async (_ctx) => {
|
|
40987
|
+
};
|
|
40988
|
+
this.start = async (ctx) => {
|
|
40989
|
+
ctx.hook("kernel:ready", async () => {
|
|
40990
|
+
let httpServer;
|
|
40991
|
+
try {
|
|
40992
|
+
httpServer = ctx.getService("http-server");
|
|
40993
|
+
} catch {
|
|
40994
|
+
ctx.logger?.warn?.("[MarketplaceProxyPlugin] http-server not available \u2014 marketplace routes not mounted");
|
|
40995
|
+
return;
|
|
40996
|
+
}
|
|
40997
|
+
if (!httpServer || typeof httpServer.getRawApp !== "function") {
|
|
40998
|
+
ctx.logger?.warn?.("[MarketplaceProxyPlugin] http-server missing getRawApp() \u2014 marketplace routes not mounted");
|
|
40999
|
+
return;
|
|
41000
|
+
}
|
|
41001
|
+
const rawApp = httpServer.getRawApp();
|
|
41002
|
+
const cloudUrl = this.cloudUrl;
|
|
41003
|
+
const handler = async (c, next) => {
|
|
41004
|
+
if (!cloudUrl) {
|
|
41005
|
+
return c.json({
|
|
41006
|
+
success: false,
|
|
41007
|
+
error: {
|
|
41008
|
+
code: "marketplace_unavailable",
|
|
41009
|
+
message: "No control-plane URL configured for this runtime (OS_CLOUD_URL)."
|
|
41010
|
+
}
|
|
41011
|
+
}, 503);
|
|
41012
|
+
}
|
|
41013
|
+
try {
|
|
41014
|
+
const incomingUrl = new URL(c.req.url);
|
|
41015
|
+
if (incomingUrl.pathname.startsWith(`${MARKETPLACE_PREFIX}/install-local`)) {
|
|
41016
|
+
return next();
|
|
41017
|
+
}
|
|
41018
|
+
const target = `${cloudUrl}${incomingUrl.pathname}${incomingUrl.search}`;
|
|
41019
|
+
const method = String(c.req.method ?? "GET").toUpperCase();
|
|
41020
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
41021
|
+
return c.json({
|
|
41022
|
+
success: false,
|
|
41023
|
+
error: {
|
|
41024
|
+
code: "marketplace_method_not_allowed",
|
|
41025
|
+
message: `Marketplace proxy only forwards GET/HEAD; install via cloud.`
|
|
41026
|
+
}
|
|
41027
|
+
}, 405);
|
|
41028
|
+
}
|
|
41029
|
+
const resp = await fetch(target, {
|
|
41030
|
+
method,
|
|
41031
|
+
headers: {
|
|
41032
|
+
// Strip the inbound Host header — fetch will set
|
|
41033
|
+
// it to the cloud host. Forward only the
|
|
41034
|
+
// identifying headers cloud might log.
|
|
41035
|
+
"Accept": c.req.header("accept") ?? "application/json",
|
|
41036
|
+
"User-Agent": `objectos-marketplace-proxy/${_MarketplaceProxyPlugin.prototype.version ?? "1.0.0"}`
|
|
41037
|
+
}
|
|
41038
|
+
});
|
|
41039
|
+
const headers = new Headers();
|
|
41040
|
+
const passthroughHeaders = ["content-type", "cache-control", "etag", "last-modified"];
|
|
41041
|
+
for (const h of passthroughHeaders) {
|
|
41042
|
+
const v = resp.headers.get(h);
|
|
41043
|
+
if (v) headers.set(h, v);
|
|
41044
|
+
}
|
|
41045
|
+
const body = await resp.arrayBuffer();
|
|
41046
|
+
return new Response(body, { status: resp.status, headers });
|
|
41047
|
+
} catch (err) {
|
|
41048
|
+
const errObj = err instanceof Error ? err : new Error(err?.message ?? String(err));
|
|
41049
|
+
ctx.logger?.error?.("[MarketplaceProxyPlugin] proxy failed", errObj);
|
|
41050
|
+
return c.json({
|
|
41051
|
+
success: false,
|
|
41052
|
+
error: {
|
|
41053
|
+
code: "marketplace_proxy_failed",
|
|
41054
|
+
message: err?.message ?? String(err)
|
|
41055
|
+
}
|
|
41056
|
+
}, 502);
|
|
41057
|
+
}
|
|
41058
|
+
};
|
|
41059
|
+
if (typeof rawApp.all === "function") {
|
|
41060
|
+
rawApp.all(`${MARKETPLACE_PREFIX}/*`, handler);
|
|
41061
|
+
} else {
|
|
41062
|
+
for (const m of ["get", "head"]) {
|
|
41063
|
+
try {
|
|
41064
|
+
rawApp[m]?.(`${MARKETPLACE_PREFIX}/*`, handler);
|
|
41065
|
+
} catch {
|
|
41066
|
+
}
|
|
41067
|
+
}
|
|
41068
|
+
}
|
|
41069
|
+
ctx.logger?.info?.(`[MarketplaceProxyPlugin] mounted at ${MARKETPLACE_PREFIX}/* \u2192 ${cloudUrl || "(unconfigured)"}`);
|
|
41070
|
+
});
|
|
41071
|
+
};
|
|
41072
|
+
this.cloudUrl = resolveCloudUrl(config.controlPlaneUrl);
|
|
41073
|
+
}
|
|
41074
|
+
};
|
|
41075
|
+
|
|
40869
41076
|
// src/cloud/file-artifact-api-client.ts
|
|
40870
41077
|
import { readFile as readFile2, stat } from "fs/promises";
|
|
40871
41078
|
import { resolve as resolvePath4 } from "path";
|
|
@@ -41092,7 +41299,7 @@ async function createObjectOSStack(config) {
|
|
|
41092
41299
|
};
|
|
41093
41300
|
const enginePlugins = await createHostEnginePlugins();
|
|
41094
41301
|
return {
|
|
41095
|
-
plugins: [...enginePlugins, new ObjectOSProjectPlugin(merged), new AuthProxyPlugin()],
|
|
41302
|
+
plugins: [...enginePlugins, new ObjectOSProjectPlugin(merged), new AuthProxyPlugin(), new MarketplaceProxyPlugin({ controlPlaneUrl: merged.controlPlaneUrl === "file" ? void 0 : merged.controlPlaneUrl })],
|
|
41096
41303
|
api: {
|
|
41097
41304
|
enableProjectScoping: true,
|
|
41098
41305
|
projectResolution: "auto",
|
|
@@ -41106,6 +41313,462 @@ async function createObjectOSStack(config) {
|
|
|
41106
41313
|
};
|
|
41107
41314
|
}
|
|
41108
41315
|
|
|
41316
|
+
// src/cloud/marketplace-install-local-plugin.ts
|
|
41317
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, readdirSync, unlinkSync, writeFileSync } from "fs";
|
|
41318
|
+
import { join, resolve } from "path";
|
|
41319
|
+
var ROUTE_BASE = "/api/v1/marketplace/install-local";
|
|
41320
|
+
var DEFAULT_DIR = ".objectstack/installed-packages";
|
|
41321
|
+
function safeFilename(manifestId) {
|
|
41322
|
+
return manifestId.replace(/[^a-zA-Z0-9._-]/g, "_") + ".json";
|
|
41323
|
+
}
|
|
41324
|
+
var MarketplaceInstallLocalPlugin = class {
|
|
41325
|
+
constructor(config = {}) {
|
|
41326
|
+
this.name = "com.objectstack.runtime.marketplace-install-local";
|
|
41327
|
+
this.version = "1.0.0";
|
|
41328
|
+
this.init = async (_ctx) => {
|
|
41329
|
+
};
|
|
41330
|
+
this.start = async (ctx) => {
|
|
41331
|
+
ctx.hook("kernel:ready", async () => {
|
|
41332
|
+
await this.rehydrate(ctx);
|
|
41333
|
+
let httpServer;
|
|
41334
|
+
try {
|
|
41335
|
+
httpServer = ctx.getService("http-server");
|
|
41336
|
+
} catch {
|
|
41337
|
+
ctx.logger?.warn?.("[MarketplaceInstallLocal] http-server not available \u2014 install endpoints not mounted");
|
|
41338
|
+
return;
|
|
41339
|
+
}
|
|
41340
|
+
if (!httpServer || typeof httpServer.getRawApp !== "function") {
|
|
41341
|
+
ctx.logger?.warn?.("[MarketplaceInstallLocal] http-server missing getRawApp() \u2014 install endpoints not mounted");
|
|
41342
|
+
return;
|
|
41343
|
+
}
|
|
41344
|
+
const rawApp = httpServer.getRawApp();
|
|
41345
|
+
const postHandler = async (c) => this.handleInstall(c, ctx);
|
|
41346
|
+
const getHandler = async (c) => this.handleList(c);
|
|
41347
|
+
const deleteHandler = async (c) => this.handleUninstall(c, ctx);
|
|
41348
|
+
if (typeof rawApp.post === "function") rawApp.post(ROUTE_BASE, postHandler);
|
|
41349
|
+
if (typeof rawApp.get === "function") rawApp.get(ROUTE_BASE, getHandler);
|
|
41350
|
+
if (typeof rawApp.delete === "function") rawApp.delete(`${ROUTE_BASE}/:manifestId`, deleteHandler);
|
|
41351
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] mounted at ${ROUTE_BASE} (storage: ${this.storageDir})`);
|
|
41352
|
+
});
|
|
41353
|
+
};
|
|
41354
|
+
/**
|
|
41355
|
+
* Re-register every cached manifest with the kernel's manifest service.
|
|
41356
|
+
* Safe to call on a kernel that already has the same manifest_id (the
|
|
41357
|
+
* underlying ObjectQL registry overwrites by id, but we still warn so
|
|
41358
|
+
* a developer can spot the dev-time clash between their config.ts and
|
|
41359
|
+
* a marketplace package).
|
|
41360
|
+
*/
|
|
41361
|
+
this.rehydrate = async (ctx) => {
|
|
41362
|
+
const entries = this.readAll();
|
|
41363
|
+
if (entries.length === 0) return;
|
|
41364
|
+
let manifestService = null;
|
|
41365
|
+
try {
|
|
41366
|
+
manifestService = ctx.getService("manifest");
|
|
41367
|
+
} catch {
|
|
41368
|
+
ctx.logger?.warn?.("[MarketplaceInstallLocal] no `manifest` service \u2014 rehydrate skipped");
|
|
41369
|
+
return;
|
|
41370
|
+
}
|
|
41371
|
+
for (const entry of entries) {
|
|
41372
|
+
try {
|
|
41373
|
+
manifestService.register(entry.manifest);
|
|
41374
|
+
try {
|
|
41375
|
+
const ql = ctx.getService("objectql");
|
|
41376
|
+
if (ql && typeof ql.syncSchemas === "function") await ql.syncSchemas();
|
|
41377
|
+
} catch {
|
|
41378
|
+
}
|
|
41379
|
+
await this.applySideEffects(ctx, entry.manifest, { seedNow: false });
|
|
41380
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] rehydrated ${entry.manifestId}@${entry.version}`);
|
|
41381
|
+
} catch (err) {
|
|
41382
|
+
ctx.logger?.error?.(`[MarketplaceInstallLocal] rehydrate failed for ${entry.manifestId}`, err instanceof Error ? err : new Error(String(err)));
|
|
41383
|
+
}
|
|
41384
|
+
}
|
|
41385
|
+
};
|
|
41386
|
+
this.handleInstall = async (c, ctx) => {
|
|
41387
|
+
if (!this.cloudUrl) {
|
|
41388
|
+
return c.json({ success: false, error: { code: "marketplace_unavailable", message: "OS_CLOUD_URL not configured." } }, 503);
|
|
41389
|
+
}
|
|
41390
|
+
const userId = await this.requireAuthenticatedUser(c, ctx);
|
|
41391
|
+
if (!userId) {
|
|
41392
|
+
return c.json({ success: false, error: { code: "unauthorized", message: "Authentication required to install packages." } }, 401);
|
|
41393
|
+
}
|
|
41394
|
+
let body = {};
|
|
41395
|
+
try {
|
|
41396
|
+
body = await c.req.json();
|
|
41397
|
+
} catch {
|
|
41398
|
+
}
|
|
41399
|
+
const packageId = String(body?.packageId ?? "").trim();
|
|
41400
|
+
const versionId = String(body?.versionId ?? "latest").trim() || "latest";
|
|
41401
|
+
if (!packageId) {
|
|
41402
|
+
return c.json({ success: false, error: { code: "bad_request", message: "packageId is required." } }, 400);
|
|
41403
|
+
}
|
|
41404
|
+
let payload;
|
|
41405
|
+
try {
|
|
41406
|
+
const url = `${this.cloudUrl}/api/v1/marketplace/packages/${encodeURIComponent(packageId)}/versions/${encodeURIComponent(versionId)}/manifest`;
|
|
41407
|
+
const resp = await fetch(url, { headers: { "Accept": "application/json" } });
|
|
41408
|
+
if (!resp.ok) {
|
|
41409
|
+
const text = await resp.text().catch(() => "");
|
|
41410
|
+
return c.json({
|
|
41411
|
+
success: false,
|
|
41412
|
+
error: { code: "cloud_fetch_failed", message: `Cloud returned ${resp.status}: ${text.slice(0, 200)}` }
|
|
41413
|
+
}, resp.status === 404 ? 404 : 502);
|
|
41414
|
+
}
|
|
41415
|
+
payload = await resp.json();
|
|
41416
|
+
} catch (err) {
|
|
41417
|
+
return c.json({
|
|
41418
|
+
success: false,
|
|
41419
|
+
error: { code: "cloud_fetch_failed", message: err?.message ?? String(err) }
|
|
41420
|
+
}, 502);
|
|
41421
|
+
}
|
|
41422
|
+
const data = payload?.data ?? payload;
|
|
41423
|
+
const manifest = data?.manifest;
|
|
41424
|
+
const resolvedVersionId = String(data?.version_id ?? versionId);
|
|
41425
|
+
const version = String(data?.version ?? "unknown");
|
|
41426
|
+
const manifestId = String(manifest?.id ?? manifest?.name ?? "");
|
|
41427
|
+
if (!manifest || !manifestId) {
|
|
41428
|
+
return c.json({ success: false, error: { code: "invalid_manifest", message: "Cloud returned an invalid manifest payload." } }, 502);
|
|
41429
|
+
}
|
|
41430
|
+
const conflict = this.findConflict(ctx, manifestId);
|
|
41431
|
+
if (conflict === "user-code") {
|
|
41432
|
+
return c.json({
|
|
41433
|
+
success: false,
|
|
41434
|
+
error: {
|
|
41435
|
+
code: "manifest_conflict",
|
|
41436
|
+
message: `manifest_id "${manifestId}" is already defined by this runtime's local code. Refusing to overwrite. Uninstall the local definition first.`
|
|
41437
|
+
}
|
|
41438
|
+
}, 409);
|
|
41439
|
+
}
|
|
41440
|
+
const entry = {
|
|
41441
|
+
packageId,
|
|
41442
|
+
versionId: resolvedVersionId,
|
|
41443
|
+
manifestId,
|
|
41444
|
+
version,
|
|
41445
|
+
manifest,
|
|
41446
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
41447
|
+
installedBy: userId
|
|
41448
|
+
};
|
|
41449
|
+
try {
|
|
41450
|
+
mkdirSync2(this.storageDir, { recursive: true });
|
|
41451
|
+
writeFileSync(join(this.storageDir, safeFilename(manifestId)), JSON.stringify(entry, null, 2), "utf8");
|
|
41452
|
+
} catch (err) {
|
|
41453
|
+
return c.json({
|
|
41454
|
+
success: false,
|
|
41455
|
+
error: { code: "storage_failed", message: `Failed to persist manifest: ${err?.message ?? err}` }
|
|
41456
|
+
}, 500);
|
|
41457
|
+
}
|
|
41458
|
+
try {
|
|
41459
|
+
const manifestService = ctx.getService("manifest");
|
|
41460
|
+
manifestService.register(manifest);
|
|
41461
|
+
} catch (err) {
|
|
41462
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] hot-register failed for ${manifestId} (will load on next restart): ${err?.message ?? err}`);
|
|
41463
|
+
}
|
|
41464
|
+
try {
|
|
41465
|
+
const ql = ctx.getService("objectql");
|
|
41466
|
+
if (ql && typeof ql.syncSchemas === "function") {
|
|
41467
|
+
await ql.syncSchemas();
|
|
41468
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] syncSchemas() ran after registering ${manifestId}`);
|
|
41469
|
+
}
|
|
41470
|
+
} catch (err) {
|
|
41471
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] syncSchemas failed for ${manifestId}: ${err?.message ?? err}`);
|
|
41472
|
+
}
|
|
41473
|
+
const seededSummary = await this.applySideEffects(ctx, manifest, { seedNow: true, c });
|
|
41474
|
+
return c.json({
|
|
41475
|
+
success: true,
|
|
41476
|
+
data: {
|
|
41477
|
+
manifestId,
|
|
41478
|
+
version,
|
|
41479
|
+
versionId: resolvedVersionId,
|
|
41480
|
+
installedAt: entry.installedAt,
|
|
41481
|
+
hotLoaded: true,
|
|
41482
|
+
upgradedFrom: conflict === "marketplace" ? "previous-marketplace-version" : null,
|
|
41483
|
+
translationsLoaded: seededSummary.translationsLoaded,
|
|
41484
|
+
seeded: seededSummary.seeded,
|
|
41485
|
+
note: "App is now available in this runtime. Refresh the console to see it in the app switcher."
|
|
41486
|
+
}
|
|
41487
|
+
}, 200);
|
|
41488
|
+
};
|
|
41489
|
+
this.handleList = async (c) => {
|
|
41490
|
+
const entries = this.readAll();
|
|
41491
|
+
return c.json({
|
|
41492
|
+
success: true,
|
|
41493
|
+
data: {
|
|
41494
|
+
items: entries.map((e) => ({
|
|
41495
|
+
packageId: e.packageId,
|
|
41496
|
+
versionId: e.versionId,
|
|
41497
|
+
manifestId: e.manifestId,
|
|
41498
|
+
version: e.version,
|
|
41499
|
+
installedAt: e.installedAt,
|
|
41500
|
+
installedBy: e.installedBy
|
|
41501
|
+
})),
|
|
41502
|
+
total: entries.length,
|
|
41503
|
+
storageDir: this.storageDir
|
|
41504
|
+
}
|
|
41505
|
+
}, 200);
|
|
41506
|
+
};
|
|
41507
|
+
this.handleUninstall = async (c, ctx) => {
|
|
41508
|
+
const userId = await this.requireAuthenticatedUser(c, ctx);
|
|
41509
|
+
if (!userId) {
|
|
41510
|
+
return c.json({ success: false, error: { code: "unauthorized", message: "Authentication required." } }, 401);
|
|
41511
|
+
}
|
|
41512
|
+
const manifestId = String(c.req.param?.("manifestId") ?? c.req.params?.manifestId ?? "").trim();
|
|
41513
|
+
if (!manifestId) {
|
|
41514
|
+
return c.json({ success: false, error: { code: "bad_request", message: "manifestId path param required." } }, 400);
|
|
41515
|
+
}
|
|
41516
|
+
const file = join(this.storageDir, safeFilename(manifestId));
|
|
41517
|
+
if (!existsSync2(file)) {
|
|
41518
|
+
return c.json({ success: false, error: { code: "not_found", message: `No marketplace install for ${manifestId}.` } }, 404);
|
|
41519
|
+
}
|
|
41520
|
+
try {
|
|
41521
|
+
unlinkSync(file);
|
|
41522
|
+
} catch (err) {
|
|
41523
|
+
return c.json({ success: false, error: { code: "storage_failed", message: err?.message ?? String(err) } }, 500);
|
|
41524
|
+
}
|
|
41525
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] uninstalled ${manifestId} (cached manifest removed; restart runtime to unload from running kernel)`);
|
|
41526
|
+
return c.json({
|
|
41527
|
+
success: true,
|
|
41528
|
+
data: {
|
|
41529
|
+
manifestId,
|
|
41530
|
+
note: "Cached manifest removed. The app remains loaded in the running kernel until the next restart (the kernel API does not support unregistering apps in-place)."
|
|
41531
|
+
}
|
|
41532
|
+
}, 200);
|
|
41533
|
+
};
|
|
41534
|
+
/**
|
|
41535
|
+
* Detect whether `manifestId` is already known to the kernel and classify
|
|
41536
|
+
* the source so we can refuse vs upgrade gracefully.
|
|
41537
|
+
*
|
|
41538
|
+
* 'none' — fresh install
|
|
41539
|
+
* 'marketplace' — previously installed by this plugin (allow upgrade)
|
|
41540
|
+
* 'user-code' — defined by AppPlugin from objectstack.config.ts
|
|
41541
|
+
* (refuse to avoid silently overwriting authored code)
|
|
41542
|
+
*/
|
|
41543
|
+
this.findConflict = (ctx, manifestId) => {
|
|
41544
|
+
if (existsSync2(join(this.storageDir, safeFilename(manifestId)))) {
|
|
41545
|
+
return "marketplace";
|
|
41546
|
+
}
|
|
41547
|
+
try {
|
|
41548
|
+
const ql = ctx.getService("objectql");
|
|
41549
|
+
const packages = ql?.registry?.getAllPackages?.() ?? [];
|
|
41550
|
+
const hit = packages.find(
|
|
41551
|
+
(p) => (p?.manifest?.id ?? p?.id ?? p?.manifest?.name) === manifestId
|
|
41552
|
+
);
|
|
41553
|
+
if (hit) return "user-code";
|
|
41554
|
+
} catch {
|
|
41555
|
+
}
|
|
41556
|
+
return "none";
|
|
41557
|
+
};
|
|
41558
|
+
/**
|
|
41559
|
+
* Pull a userId out of the request's better-auth session, if any.
|
|
41560
|
+
* Returns null when there is no signed-in user. v1 does not check
|
|
41561
|
+
* admin role — UI gating + the auth requirement is sufficient for
|
|
41562
|
+
* dev / single-tenant runtimes. Stricter checks can be layered on
|
|
41563
|
+
* via a middleware in cloud-hosted multi-tenant deployments.
|
|
41564
|
+
*/
|
|
41565
|
+
/**
|
|
41566
|
+
* Replicate the start-time side-effects that AppPlugin runs for
|
|
41567
|
+
* statically-declared apps but the `manifest` service does NOT:
|
|
41568
|
+
*
|
|
41569
|
+
* 1. Load `manifest.translations` (array of `Record<locale, data>`)
|
|
41570
|
+
* into the i18n service — auto-creating an in-memory fallback if
|
|
41571
|
+
* none is registered, matching AppPlugin's behaviour.
|
|
41572
|
+
*
|
|
41573
|
+
* 2. Merge `manifest.data` (an array of seed datasets) into the
|
|
41574
|
+
* kernel's `seed-datasets` service so SecurityPlugin's per-org
|
|
41575
|
+
* replay middleware picks them up on every future
|
|
41576
|
+
* sys_organization insert.
|
|
41577
|
+
*
|
|
41578
|
+
* 3. When `seedNow=true`, also run the seed immediately so the user
|
|
41579
|
+
* sees demo data without having to create a new org:
|
|
41580
|
+
* • single-tenant: run SeedLoaderService inline (mirrors
|
|
41581
|
+
* AppPlugin single-tenant branch)
|
|
41582
|
+
* • multi-tenant: invoke `seed-replayer` for the caller's
|
|
41583
|
+
* active org (resolved from the request session)
|
|
41584
|
+
*
|
|
41585
|
+
* Errors are logged but never thrown — install succeeds even if
|
|
41586
|
+
* post-register side-effects partially fail (the manifest itself is
|
|
41587
|
+
* already registered + cached). Returns a small summary for the
|
|
41588
|
+
* response envelope.
|
|
41589
|
+
*/
|
|
41590
|
+
this.applySideEffects = async (ctx, manifest, opts) => {
|
|
41591
|
+
const appId = String(manifest?.id ?? "unknown");
|
|
41592
|
+
let translationsLoaded = 0;
|
|
41593
|
+
let seedSummary = { mode: "skipped", reason: "no-datasets" };
|
|
41594
|
+
try {
|
|
41595
|
+
const bundles = [];
|
|
41596
|
+
if (Array.isArray(manifest?.translations)) bundles.push(...manifest.translations);
|
|
41597
|
+
if (Array.isArray(manifest?.i18n)) bundles.push(...manifest.i18n);
|
|
41598
|
+
if (bundles.length > 0) {
|
|
41599
|
+
let i18nService;
|
|
41600
|
+
try {
|
|
41601
|
+
i18nService = ctx.getService("i18n");
|
|
41602
|
+
} catch {
|
|
41603
|
+
}
|
|
41604
|
+
if (!i18nService) {
|
|
41605
|
+
try {
|
|
41606
|
+
const mod = await import("@objectstack/core");
|
|
41607
|
+
const createMemoryI18n = mod.createMemoryI18n;
|
|
41608
|
+
if (typeof createMemoryI18n === "function") {
|
|
41609
|
+
i18nService = createMemoryI18n();
|
|
41610
|
+
ctx.registerService?.("i18n", i18nService);
|
|
41611
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] auto-registered in-memory i18n fallback for "${appId}"`);
|
|
41612
|
+
}
|
|
41613
|
+
} catch {
|
|
41614
|
+
}
|
|
41615
|
+
}
|
|
41616
|
+
if (i18nService?.loadTranslations) {
|
|
41617
|
+
for (const bundle of bundles) {
|
|
41618
|
+
for (const [locale, data] of Object.entries(bundle)) {
|
|
41619
|
+
if (data && typeof data === "object") {
|
|
41620
|
+
try {
|
|
41621
|
+
i18nService.loadTranslations(locale, data);
|
|
41622
|
+
translationsLoaded++;
|
|
41623
|
+
} catch (err) {
|
|
41624
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] failed to load ${appId} translations for ${locale}: ${err?.message ?? err}`);
|
|
41625
|
+
}
|
|
41626
|
+
}
|
|
41627
|
+
}
|
|
41628
|
+
}
|
|
41629
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] loaded ${translationsLoaded} locale bundle(s) for ${appId}`);
|
|
41630
|
+
}
|
|
41631
|
+
}
|
|
41632
|
+
} catch (err) {
|
|
41633
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] i18n side-effect failed for ${appId}: ${err?.message ?? err}`);
|
|
41634
|
+
}
|
|
41635
|
+
const datasets = Array.isArray(manifest?.data) ? manifest.data.filter((d) => d && d.object && Array.isArray(d.records)) : [];
|
|
41636
|
+
if (datasets.length > 0) {
|
|
41637
|
+
try {
|
|
41638
|
+
const kernel = ctx.kernel;
|
|
41639
|
+
let existing = [];
|
|
41640
|
+
try {
|
|
41641
|
+
const v = kernel?.getService?.("seed-datasets");
|
|
41642
|
+
if (Array.isArray(v)) existing = v;
|
|
41643
|
+
} catch {
|
|
41644
|
+
}
|
|
41645
|
+
const merged = [...existing, ...datasets];
|
|
41646
|
+
if (kernel?.registerService) kernel.registerService("seed-datasets", merged);
|
|
41647
|
+
else ctx.registerService?.("seed-datasets", merged);
|
|
41648
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] merged ${datasets.length} seed dataset(s) into kernel (total: ${merged.length})`);
|
|
41649
|
+
} catch (err) {
|
|
41650
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] failed to merge seed-datasets: ${err?.message ?? err}`);
|
|
41651
|
+
}
|
|
41652
|
+
}
|
|
41653
|
+
if (opts.seedNow && datasets.length > 0) {
|
|
41654
|
+
const multiTenant = String(process.env.OS_MULTI_TENANT ?? "true").toLowerCase() !== "false";
|
|
41655
|
+
try {
|
|
41656
|
+
const ql = ctx.getService("objectql");
|
|
41657
|
+
let metadata;
|
|
41658
|
+
try {
|
|
41659
|
+
metadata = ctx.getService("metadata");
|
|
41660
|
+
} catch {
|
|
41661
|
+
}
|
|
41662
|
+
if (!ql || !metadata) {
|
|
41663
|
+
seedSummary = { mode: "skipped", reason: "objectql-or-metadata-missing" };
|
|
41664
|
+
} else {
|
|
41665
|
+
let organizationId;
|
|
41666
|
+
if (multiTenant) {
|
|
41667
|
+
const resolved = await this.resolveActiveOrgId(opts.c, ctx);
|
|
41668
|
+
if (resolved) organizationId = resolved;
|
|
41669
|
+
else {
|
|
41670
|
+
seedSummary = { mode: "skipped", reason: "multi-tenant-no-active-org" };
|
|
41671
|
+
ctx.logger?.warn?.("[MarketplaceInstallLocal] multi-tenant: no active org on request \u2014 data not seeded");
|
|
41672
|
+
}
|
|
41673
|
+
}
|
|
41674
|
+
if (!multiTenant || organizationId) {
|
|
41675
|
+
const [{ SeedLoaderService: SeedLoaderService2 }, { SeedLoaderRequestSchema }] = await Promise.all([
|
|
41676
|
+
Promise.resolve().then(() => (init_seed_loader(), seed_loader_exports)),
|
|
41677
|
+
import("@objectstack/spec/data")
|
|
41678
|
+
]);
|
|
41679
|
+
const seedLoader = new SeedLoaderService2(ql, metadata, ctx.logger);
|
|
41680
|
+
const request = SeedLoaderRequestSchema.parse({
|
|
41681
|
+
datasets,
|
|
41682
|
+
config: {
|
|
41683
|
+
defaultMode: "upsert",
|
|
41684
|
+
multiPass: true,
|
|
41685
|
+
...organizationId ? { organizationId } : {}
|
|
41686
|
+
}
|
|
41687
|
+
});
|
|
41688
|
+
const result = await seedLoader.load(request);
|
|
41689
|
+
seedSummary = {
|
|
41690
|
+
mode: "inline",
|
|
41691
|
+
inserted: result.summary.totalInserted,
|
|
41692
|
+
updated: result.summary.totalUpdated,
|
|
41693
|
+
errors: result.errors.length
|
|
41694
|
+
};
|
|
41695
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] inline seed for ${appId}${organizationId ? ` (org=${organizationId})` : ""}: inserted=${seedSummary.inserted} updated=${seedSummary.updated} errors=${seedSummary.errors}`);
|
|
41696
|
+
}
|
|
41697
|
+
}
|
|
41698
|
+
} catch (err) {
|
|
41699
|
+
seedSummary = { mode: "skipped", reason: `seed-error: ${err?.message ?? err}` };
|
|
41700
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] seed run failed for ${appId}: ${err?.message ?? err}`);
|
|
41701
|
+
}
|
|
41702
|
+
}
|
|
41703
|
+
return { translationsLoaded, seeded: seedSummary };
|
|
41704
|
+
};
|
|
41705
|
+
/**
|
|
41706
|
+
* Best-effort active-org resolution. Reads the better-auth session
|
|
41707
|
+
* (same path as requireAuthenticatedUser) and returns
|
|
41708
|
+
* `session.activeOrganizationId`, falling back to the user's first
|
|
41709
|
+
* org membership.
|
|
41710
|
+
*/
|
|
41711
|
+
this.resolveActiveOrgId = async (c, ctx) => {
|
|
41712
|
+
if (!c?.req?.raw?.headers) return null;
|
|
41713
|
+
try {
|
|
41714
|
+
const authService = ctx.getService("auth");
|
|
41715
|
+
let api = authService?.api;
|
|
41716
|
+
if (!api && typeof authService?.getApi === "function") api = await authService.getApi();
|
|
41717
|
+
if (!api?.getSession) return null;
|
|
41718
|
+
const session = await api.getSession({ headers: c.req.raw.headers });
|
|
41719
|
+
const direct = session?.session?.activeOrganizationId ?? session?.activeOrganizationId ?? null;
|
|
41720
|
+
if (direct) return String(direct);
|
|
41721
|
+
const userId = session?.user?.id;
|
|
41722
|
+
if (!userId) return null;
|
|
41723
|
+
try {
|
|
41724
|
+
const ql = ctx.getService("objectql");
|
|
41725
|
+
if (ql?.find) {
|
|
41726
|
+
const rows = await ql.find("sys_organization_member", { where: { user_id: userId }, limit: 1, context: { isSystem: true } });
|
|
41727
|
+
const row = Array.isArray(rows) ? rows[0] : rows?.items?.[0] ?? null;
|
|
41728
|
+
return row?.organization_id ? String(row.organization_id) : null;
|
|
41729
|
+
}
|
|
41730
|
+
} catch {
|
|
41731
|
+
}
|
|
41732
|
+
} catch {
|
|
41733
|
+
}
|
|
41734
|
+
return null;
|
|
41735
|
+
};
|
|
41736
|
+
this.requireAuthenticatedUser = async (c, ctx) => {
|
|
41737
|
+
try {
|
|
41738
|
+
const authService = ctx.getService("auth");
|
|
41739
|
+
let api = authService?.api;
|
|
41740
|
+
if (!api && typeof authService?.getApi === "function") {
|
|
41741
|
+
api = await authService.getApi();
|
|
41742
|
+
}
|
|
41743
|
+
if (api?.getSession && c?.req?.raw?.headers) {
|
|
41744
|
+
const session = await api.getSession({ headers: c.req.raw.headers });
|
|
41745
|
+
const userId = session?.user?.id ?? null;
|
|
41746
|
+
if (userId) return String(userId);
|
|
41747
|
+
}
|
|
41748
|
+
} catch {
|
|
41749
|
+
}
|
|
41750
|
+
const xUserId = c?.req?.header?.("x-user-id");
|
|
41751
|
+
if (xUserId) return String(xUserId);
|
|
41752
|
+
return null;
|
|
41753
|
+
};
|
|
41754
|
+
this.readAll = () => {
|
|
41755
|
+
if (!existsSync2(this.storageDir)) return [];
|
|
41756
|
+
const out = [];
|
|
41757
|
+
for (const name of readdirSync(this.storageDir)) {
|
|
41758
|
+
if (!name.endsWith(".json")) continue;
|
|
41759
|
+
try {
|
|
41760
|
+
const raw = readFileSync(join(this.storageDir, name), "utf8");
|
|
41761
|
+
out.push(JSON.parse(raw));
|
|
41762
|
+
} catch {
|
|
41763
|
+
}
|
|
41764
|
+
}
|
|
41765
|
+
return out;
|
|
41766
|
+
};
|
|
41767
|
+
this.cloudUrl = resolveCloudUrl(config.controlPlaneUrl);
|
|
41768
|
+
this.storageDir = config.storageDir ? resolve(config.storageDir) : resolve(process.cwd(), DEFAULT_DIR);
|
|
41769
|
+
}
|
|
41770
|
+
};
|
|
41771
|
+
|
|
41109
41772
|
// src/index.ts
|
|
41110
41773
|
init_platform_sso();
|
|
41111
41774
|
|
|
@@ -41145,6 +41808,7 @@ export {
|
|
|
41145
41808
|
ArtifactEnvironmentRegistry,
|
|
41146
41809
|
ArtifactKernelFactory,
|
|
41147
41810
|
AuthProxyPlugin,
|
|
41811
|
+
DEFAULT_CLOUD_URL,
|
|
41148
41812
|
DEFAULT_RATE_LIMITS,
|
|
41149
41813
|
DriverPlugin,
|
|
41150
41814
|
FileArtifactApiClient,
|
|
@@ -41153,10 +41817,15 @@ export {
|
|
|
41153
41817
|
InMemoryErrorReporter,
|
|
41154
41818
|
InMemoryMetricsRegistry,
|
|
41155
41819
|
KernelManager,
|
|
41820
|
+
MarketplaceInstallLocalPlugin,
|
|
41821
|
+
MarketplaceProxyPlugin,
|
|
41156
41822
|
MiddlewareManager,
|
|
41157
41823
|
NoopErrorReporter,
|
|
41158
41824
|
NoopMetricsRegistry,
|
|
41825
|
+
OBSERVABILITY_ERRORS_SERVICE,
|
|
41826
|
+
OBSERVABILITY_METRICS_SERVICE,
|
|
41159
41827
|
ObjectKernel4 as ObjectKernel,
|
|
41828
|
+
ObservabilityServicePlugin,
|
|
41160
41829
|
PLATFORM_SSO_PROVIDER_ID,
|
|
41161
41830
|
QuickJSScriptRunner,
|
|
41162
41831
|
RUNTIME_METRICS,
|
|
@@ -41193,7 +41862,10 @@ export {
|
|
|
41193
41862
|
mergeRuntimeModule,
|
|
41194
41863
|
parseTraceparent,
|
|
41195
41864
|
readArtifactSource,
|
|
41865
|
+
resolveCloudUrl,
|
|
41196
41866
|
resolveDefaultArtifactPath,
|
|
41867
|
+
resolveErrorReporter,
|
|
41868
|
+
resolveMetrics,
|
|
41197
41869
|
resolveRequestId,
|
|
41198
41870
|
seedPlatformSsoClient
|
|
41199
41871
|
};
|