@objectstack/runtime 5.1.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 -30
- 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 -30
- package/dist/index.js.map +1 -1
- package/package.json +17 -16
package/dist/index.cjs
CHANGED
|
@@ -175,6 +175,10 @@ var init_driver_plugin = __esm({
|
|
|
175
175
|
});
|
|
176
176
|
|
|
177
177
|
// src/seed-loader.ts
|
|
178
|
+
var seed_loader_exports = {};
|
|
179
|
+
__export(seed_loader_exports, {
|
|
180
|
+
SeedLoaderService: () => SeedLoaderService
|
|
181
|
+
});
|
|
178
182
|
var import_data, import_formula, DEFAULT_EXTERNAL_ID_FIELD, _SeedLoaderService, SeedLoaderService;
|
|
179
183
|
var init_seed_loader = __esm({
|
|
180
184
|
"src/seed-loader.ts"() {
|
|
@@ -910,7 +914,7 @@ var init_quickjs_runner = __esm({
|
|
|
910
914
|
evalRes.value.dispose();
|
|
911
915
|
let pumps = 0;
|
|
912
916
|
while (pumps < 1e3) {
|
|
913
|
-
await new Promise((
|
|
917
|
+
await new Promise((resolve2) => setImmediate(resolve2));
|
|
914
918
|
const pending = runtime.executePendingJobs();
|
|
915
919
|
if (pending.error) {
|
|
916
920
|
const err = vm.dump(pending.error);
|
|
@@ -9194,7 +9198,7 @@ var require_utils = __commonJS({
|
|
|
9194
9198
|
}
|
|
9195
9199
|
}
|
|
9196
9200
|
function get(url, options = {}) {
|
|
9197
|
-
return new Promise((
|
|
9201
|
+
return new Promise((resolve2, reject) => {
|
|
9198
9202
|
let timeoutId;
|
|
9199
9203
|
const request = http.get(url, options, (response) => {
|
|
9200
9204
|
response.setEncoding("utf8");
|
|
@@ -9202,7 +9206,7 @@ var require_utils = __commonJS({
|
|
|
9202
9206
|
response.on("data", (chunk) => body += chunk);
|
|
9203
9207
|
response.on("end", () => {
|
|
9204
9208
|
(0, timers_1.clearTimeout)(timeoutId);
|
|
9205
|
-
|
|
9209
|
+
resolve2({ status: response.statusCode, body });
|
|
9206
9210
|
});
|
|
9207
9211
|
}).on("error", (error2) => {
|
|
9208
9212
|
(0, timers_1.clearTimeout)(timeoutId);
|
|
@@ -9221,13 +9225,13 @@ var require_utils = __commonJS({
|
|
|
9221
9225
|
return host && match.test(host.toLowerCase()) ? true : false;
|
|
9222
9226
|
}
|
|
9223
9227
|
function promiseWithResolvers() {
|
|
9224
|
-
let
|
|
9228
|
+
let resolve2;
|
|
9225
9229
|
let reject;
|
|
9226
9230
|
const promise = new Promise(function withResolversExecutor(promiseResolve, promiseReject) {
|
|
9227
|
-
|
|
9231
|
+
resolve2 = promiseResolve;
|
|
9228
9232
|
reject = promiseReject;
|
|
9229
9233
|
});
|
|
9230
|
-
return { promise, resolve, reject };
|
|
9234
|
+
return { promise, resolve: resolve2, reject };
|
|
9231
9235
|
}
|
|
9232
9236
|
function squashError(_error) {
|
|
9233
9237
|
return;
|
|
@@ -9238,8 +9242,8 @@ var require_utils = __commonJS({
|
|
|
9238
9242
|
exports2.randomBytes = randomBytes;
|
|
9239
9243
|
async function once(ee, name, options) {
|
|
9240
9244
|
options?.signal?.throwIfAborted();
|
|
9241
|
-
const { promise, resolve, reject } = promiseWithResolvers();
|
|
9242
|
-
const onEvent = (data) =>
|
|
9245
|
+
const { promise, resolve: resolve2, reject } = promiseWithResolvers();
|
|
9246
|
+
const onEvent = (data) => resolve2(data);
|
|
9243
9247
|
const onError = (error2) => reject(error2);
|
|
9244
9248
|
const abortListener = addAbortListener(options?.signal, function() {
|
|
9245
9249
|
reject(this.reason);
|
|
@@ -11840,13 +11844,13 @@ var require_mongo_logger = __commonJS({
|
|
|
11840
11844
|
function createStdioLogger(stream) {
|
|
11841
11845
|
return {
|
|
11842
11846
|
write: (log) => {
|
|
11843
|
-
return new Promise((
|
|
11847
|
+
return new Promise((resolve2, reject) => {
|
|
11844
11848
|
const logLine = (0, util_1.inspect)(log, { compact: true, breakLength: Infinity });
|
|
11845
11849
|
stream.write(`${logLine}
|
|
11846
11850
|
`, "utf-8", (error2) => {
|
|
11847
11851
|
if (error2)
|
|
11848
11852
|
return reject(error2);
|
|
11849
|
-
|
|
11853
|
+
resolve2(true);
|
|
11850
11854
|
});
|
|
11851
11855
|
});
|
|
11852
11856
|
}
|
|
@@ -22621,20 +22625,20 @@ var require_compression = __commonJS({
|
|
|
22621
22625
|
]);
|
|
22622
22626
|
var ZSTD_COMPRESSION_LEVEL = 3;
|
|
22623
22627
|
var zlibInflate = (buf) => {
|
|
22624
|
-
return new Promise((
|
|
22628
|
+
return new Promise((resolve2, reject) => {
|
|
22625
22629
|
zlib.inflate(buf, (error2, result) => {
|
|
22626
22630
|
if (error2)
|
|
22627
22631
|
return reject(error2);
|
|
22628
|
-
|
|
22632
|
+
resolve2(result);
|
|
22629
22633
|
});
|
|
22630
22634
|
});
|
|
22631
22635
|
};
|
|
22632
22636
|
var zlibDeflate = (buf, options) => {
|
|
22633
|
-
return new Promise((
|
|
22637
|
+
return new Promise((resolve2, reject) => {
|
|
22634
22638
|
zlib.deflate(buf, options, (error2, result) => {
|
|
22635
22639
|
if (error2)
|
|
22636
22640
|
return reject(error2);
|
|
22637
|
-
|
|
22641
|
+
resolve2(result);
|
|
22638
22642
|
});
|
|
22639
22643
|
});
|
|
22640
22644
|
};
|
|
@@ -23355,7 +23359,7 @@ var require_state_machine = __commonJS({
|
|
|
23355
23359
|
socket = tls.connect(socketOptions, () => {
|
|
23356
23360
|
socket.write(message);
|
|
23357
23361
|
});
|
|
23358
|
-
const { promise: willResolveKmsRequest, reject: rejectOnTlsSocketError, resolve } = (0, utils_1.promiseWithResolvers)();
|
|
23362
|
+
const { promise: willResolveKmsRequest, reject: rejectOnTlsSocketError, resolve: resolve2 } = (0, utils_1.promiseWithResolvers)();
|
|
23359
23363
|
abortListener = (0, utils_1.addAbortListener)(options?.signal, function() {
|
|
23360
23364
|
destroySockets();
|
|
23361
23365
|
rejectOnTlsSocketError(this.reason);
|
|
@@ -23367,7 +23371,7 @@ var require_state_machine = __commonJS({
|
|
|
23367
23371
|
request.addResponse(buffer.read(bytesNeeded));
|
|
23368
23372
|
}
|
|
23369
23373
|
if (request.bytesNeeded <= 0) {
|
|
23370
|
-
|
|
23374
|
+
resolve2();
|
|
23371
23375
|
}
|
|
23372
23376
|
});
|
|
23373
23377
|
await (options?.timeoutContext?.csotEnabled() ? Promise.all([
|
|
@@ -24948,8 +24952,8 @@ var require_on_data = __commonJS({
|
|
|
24948
24952
|
}
|
|
24949
24953
|
if (finished)
|
|
24950
24954
|
return closeHandler();
|
|
24951
|
-
const { promise, resolve, reject } = (0, utils_1.promiseWithResolvers)();
|
|
24952
|
-
unconsumedPromises.push({ resolve, reject });
|
|
24955
|
+
const { promise, resolve: resolve2, reject } = (0, utils_1.promiseWithResolvers)();
|
|
24956
|
+
unconsumedPromises.push({ resolve: resolve2, reject });
|
|
24953
24957
|
return promise;
|
|
24954
24958
|
},
|
|
24955
24959
|
return() {
|
|
@@ -26116,13 +26120,13 @@ var require_connect = __commonJS({
|
|
|
26116
26120
|
socket.setNoDelay(noDelay);
|
|
26117
26121
|
socket.setTimeout(connectTimeoutMS);
|
|
26118
26122
|
let cancellationHandler = null;
|
|
26119
|
-
const { promise: connectedSocket, resolve, reject } = (0, utils_1.promiseWithResolvers)();
|
|
26123
|
+
const { promise: connectedSocket, resolve: resolve2, reject } = (0, utils_1.promiseWithResolvers)();
|
|
26120
26124
|
if (existingSocket) {
|
|
26121
|
-
|
|
26125
|
+
resolve2(socket);
|
|
26122
26126
|
} else {
|
|
26123
26127
|
const start = performance.now();
|
|
26124
26128
|
const connectEvent = useTLS ? "secureConnect" : "connect";
|
|
26125
|
-
socket.once(connectEvent, () =>
|
|
26129
|
+
socket.once(connectEvent, () => resolve2(socket)).once("error", (cause) => reject(new error_1.MongoNetworkError(error_1.MongoError.buildErrorMessage(cause), { cause }))).once("timeout", () => {
|
|
26126
26130
|
reject(new error_1.MongoNetworkTimeoutError(`Socket '${connectEvent}' timed out after ${performance.now() - start | 0}ms (connectTimeoutMS: ${connectTimeoutMS})`));
|
|
26127
26131
|
}).once("close", () => reject(new error_1.MongoNetworkError(`Socket closed after ${performance.now() - start | 0} during connection establishment`)));
|
|
26128
26132
|
if (options.cancellationToken != null) {
|
|
@@ -26646,10 +26650,10 @@ var require_connection_pool = __commonJS({
|
|
|
26646
26650
|
async checkOut(options) {
|
|
26647
26651
|
const checkoutTime = (0, utils_1.processTimeMS)();
|
|
26648
26652
|
this.emitAndLog(_ConnectionPool.CONNECTION_CHECK_OUT_STARTED, new connection_pool_events_1.ConnectionCheckOutStartedEvent(this));
|
|
26649
|
-
const { promise, resolve, reject } = (0, utils_1.promiseWithResolvers)();
|
|
26653
|
+
const { promise, resolve: resolve2, reject } = (0, utils_1.promiseWithResolvers)();
|
|
26650
26654
|
const timeout = options.timeoutContext.connectionCheckoutTimeout;
|
|
26651
26655
|
const waitQueueMember = {
|
|
26652
|
-
resolve,
|
|
26656
|
+
resolve: resolve2,
|
|
26653
26657
|
reject,
|
|
26654
26658
|
cancelled: false,
|
|
26655
26659
|
checkoutTime
|
|
@@ -27884,13 +27888,13 @@ var require_connection_string = __commonJS({
|
|
|
27884
27888
|
var LB_REPLICA_SET_ERROR = "loadBalanced option not supported with a replicaSet option";
|
|
27885
27889
|
var LB_DIRECT_CONNECTION_ERROR = "loadBalanced option not supported when directConnection is provided";
|
|
27886
27890
|
function retryDNSTimeoutFor(rrtype) {
|
|
27887
|
-
const
|
|
27891
|
+
const resolve2 = rrtype === "SRV" ? (address) => dns.promises.resolve(address, "SRV") : (address) => dns.promises.resolve(address, "TXT");
|
|
27888
27892
|
return async function dnsReqRetryTimeout(lookupAddress) {
|
|
27889
27893
|
try {
|
|
27890
|
-
return await
|
|
27894
|
+
return await resolve2(lookupAddress);
|
|
27891
27895
|
} catch (firstDNSError) {
|
|
27892
27896
|
if (firstDNSError.code === dns.TIMEOUT) {
|
|
27893
|
-
return await
|
|
27897
|
+
return await resolve2(lookupAddress);
|
|
27894
27898
|
} else {
|
|
27895
27899
|
throw firstDNSError;
|
|
27896
27900
|
}
|
|
@@ -31529,13 +31533,13 @@ var require_topology = __commonJS({
|
|
|
31529
31533
|
}
|
|
31530
31534
|
return transaction.server;
|
|
31531
31535
|
}
|
|
31532
|
-
const { promise: serverPromise, resolve, reject } = (0, utils_1.promiseWithResolvers)();
|
|
31536
|
+
const { promise: serverPromise, resolve: resolve2, reject } = (0, utils_1.promiseWithResolvers)();
|
|
31533
31537
|
const waitQueueMember = {
|
|
31534
31538
|
serverSelector,
|
|
31535
31539
|
topologyDescription: this.description,
|
|
31536
31540
|
mongoLogger: this.client.mongoLogger,
|
|
31537
31541
|
transaction,
|
|
31538
|
-
resolve,
|
|
31542
|
+
resolve: resolve2,
|
|
31539
31543
|
reject,
|
|
31540
31544
|
cancelled: false,
|
|
31541
31545
|
startTime: (0, utils_1.processTimeMS)(),
|
|
@@ -35157,6 +35161,7 @@ __export(index_exports, {
|
|
|
35157
35161
|
ArtifactEnvironmentRegistry: () => ArtifactEnvironmentRegistry,
|
|
35158
35162
|
ArtifactKernelFactory: () => ArtifactKernelFactory,
|
|
35159
35163
|
AuthProxyPlugin: () => AuthProxyPlugin,
|
|
35164
|
+
DEFAULT_CLOUD_URL: () => DEFAULT_CLOUD_URL,
|
|
35160
35165
|
DEFAULT_RATE_LIMITS: () => DEFAULT_RATE_LIMITS,
|
|
35161
35166
|
DriverPlugin: () => DriverPlugin,
|
|
35162
35167
|
FileArtifactApiClient: () => FileArtifactApiClient,
|
|
@@ -35165,10 +35170,15 @@ __export(index_exports, {
|
|
|
35165
35170
|
InMemoryErrorReporter: () => import_observability2.InMemoryErrorReporter,
|
|
35166
35171
|
InMemoryMetricsRegistry: () => import_observability.InMemoryMetricsRegistry,
|
|
35167
35172
|
KernelManager: () => KernelManager,
|
|
35173
|
+
MarketplaceInstallLocalPlugin: () => MarketplaceInstallLocalPlugin,
|
|
35174
|
+
MarketplaceProxyPlugin: () => MarketplaceProxyPlugin,
|
|
35168
35175
|
MiddlewareManager: () => MiddlewareManager,
|
|
35169
35176
|
NoopErrorReporter: () => import_observability2.NoopErrorReporter,
|
|
35170
35177
|
NoopMetricsRegistry: () => import_observability.NoopMetricsRegistry,
|
|
35178
|
+
OBSERVABILITY_ERRORS_SERVICE: () => import_observability3.OBSERVABILITY_ERRORS_SERVICE,
|
|
35179
|
+
OBSERVABILITY_METRICS_SERVICE: () => import_observability3.OBSERVABILITY_METRICS_SERVICE,
|
|
35171
35180
|
ObjectKernel: () => import_core4.ObjectKernel,
|
|
35181
|
+
ObservabilityServicePlugin: () => ObservabilityServicePlugin,
|
|
35172
35182
|
PLATFORM_SSO_PROVIDER_ID: () => PLATFORM_SSO_PROVIDER_ID,
|
|
35173
35183
|
QuickJSScriptRunner: () => QuickJSScriptRunner,
|
|
35174
35184
|
RUNTIME_METRICS: () => import_observability.RUNTIME_METRICS,
|
|
@@ -35205,7 +35215,10 @@ __export(index_exports, {
|
|
|
35205
35215
|
mergeRuntimeModule: () => mergeRuntimeModule,
|
|
35206
35216
|
parseTraceparent: () => parseTraceparent,
|
|
35207
35217
|
readArtifactSource: () => readArtifactSource,
|
|
35218
|
+
resolveCloudUrl: () => resolveCloudUrl,
|
|
35208
35219
|
resolveDefaultArtifactPath: () => resolveDefaultArtifactPath,
|
|
35220
|
+
resolveErrorReporter: () => resolveErrorReporter,
|
|
35221
|
+
resolveMetrics: () => resolveMetrics,
|
|
35209
35222
|
resolveRequestId: () => resolveRequestId,
|
|
35210
35223
|
seedPlatformSsoClient: () => seedPlatformSsoClient
|
|
35211
35224
|
});
|
|
@@ -35214,12 +35227,25 @@ var import_core4 = require("@objectstack/core");
|
|
|
35214
35227
|
|
|
35215
35228
|
// src/runtime.ts
|
|
35216
35229
|
var import_core = require("@objectstack/core");
|
|
35230
|
+
var import_service_cluster = require("@objectstack/service-cluster");
|
|
35217
35231
|
var Runtime = class {
|
|
35218
35232
|
constructor(config = {}) {
|
|
35219
35233
|
this.kernel = new import_core.ObjectKernel(config.kernel);
|
|
35220
35234
|
if (config.server) {
|
|
35221
35235
|
this.kernel.registerService("http.server", config.server);
|
|
35222
35236
|
}
|
|
35237
|
+
if (config.cluster !== false) {
|
|
35238
|
+
const opts = this.normalizeClusterOptions(config.cluster);
|
|
35239
|
+
this.kernel.use(new import_service_cluster.ClusterServicePlugin(opts));
|
|
35240
|
+
this.kernel.use(new import_service_cluster.MetadataClusterBridgePlugin());
|
|
35241
|
+
}
|
|
35242
|
+
}
|
|
35243
|
+
normalizeClusterOptions(raw) {
|
|
35244
|
+
if (!raw) return {};
|
|
35245
|
+
if (typeof raw === "object" && ("cluster" in raw || "config" in raw) && !("driver" in raw)) {
|
|
35246
|
+
return raw;
|
|
35247
|
+
}
|
|
35248
|
+
return { config: raw };
|
|
35223
35249
|
}
|
|
35224
35250
|
/**
|
|
35225
35251
|
* Register a plugin
|
|
@@ -35521,6 +35547,23 @@ async function resolveExecutionContext(opts) {
|
|
|
35521
35547
|
}
|
|
35522
35548
|
}
|
|
35523
35549
|
}
|
|
35550
|
+
if (tenantId) {
|
|
35551
|
+
const orgMembers = await tryFind(
|
|
35552
|
+
ql,
|
|
35553
|
+
"sys_member",
|
|
35554
|
+
{ organization_id: tenantId },
|
|
35555
|
+
1e3
|
|
35556
|
+
);
|
|
35557
|
+
const orgUserIds = Array.from(
|
|
35558
|
+
new Set(
|
|
35559
|
+
orgMembers.map((m) => m.user_id ?? m.userId).filter((v) => typeof v === "string" && v.length > 0)
|
|
35560
|
+
)
|
|
35561
|
+
);
|
|
35562
|
+
if (!orgUserIds.includes(userId)) orgUserIds.push(userId);
|
|
35563
|
+
ctx.org_user_ids = orgUserIds;
|
|
35564
|
+
} else {
|
|
35565
|
+
ctx.org_user_ids = [userId];
|
|
35566
|
+
}
|
|
35524
35567
|
const upsRows = await tryFind(
|
|
35525
35568
|
ql,
|
|
35526
35569
|
"sys_user_permission_set",
|
|
@@ -38788,6 +38831,44 @@ function safeReport(reporter, err, ctx) {
|
|
|
38788
38831
|
}
|
|
38789
38832
|
}
|
|
38790
38833
|
|
|
38834
|
+
// src/observability/observability-service-plugin.ts
|
|
38835
|
+
var import_observability3 = require("@objectstack/observability");
|
|
38836
|
+
var ObservabilityServicePlugin = class {
|
|
38837
|
+
constructor(options = {}) {
|
|
38838
|
+
this.name = "com.objectstack.observability.service";
|
|
38839
|
+
this.version = "1.0.0";
|
|
38840
|
+
this.type = "standard";
|
|
38841
|
+
this.options = options;
|
|
38842
|
+
}
|
|
38843
|
+
async init(ctx) {
|
|
38844
|
+
const metrics = this.options.metrics ?? new import_observability.NoopMetricsRegistry();
|
|
38845
|
+
const errors2 = this.options.errors ?? new import_observability2.NoopErrorReporter();
|
|
38846
|
+
ctx.registerService(import_observability3.OBSERVABILITY_METRICS_SERVICE, metrics);
|
|
38847
|
+
ctx.registerService(import_observability3.OBSERVABILITY_ERRORS_SERVICE, errors2);
|
|
38848
|
+
ctx.logger.info(
|
|
38849
|
+
`ObservabilityServicePlugin: registered metrics=${metrics.constructor?.name ?? "unknown"} errors=${errors2.constructor?.name ?? "unknown"}`
|
|
38850
|
+
);
|
|
38851
|
+
}
|
|
38852
|
+
};
|
|
38853
|
+
function resolveMetrics(ctx, override) {
|
|
38854
|
+
if (override) return override;
|
|
38855
|
+
try {
|
|
38856
|
+
const m = ctx.getService(import_observability3.OBSERVABILITY_METRICS_SERVICE);
|
|
38857
|
+
if (m) return m;
|
|
38858
|
+
} catch {
|
|
38859
|
+
}
|
|
38860
|
+
return new import_observability.NoopMetricsRegistry();
|
|
38861
|
+
}
|
|
38862
|
+
function resolveErrorReporter(ctx, override) {
|
|
38863
|
+
if (override) return override;
|
|
38864
|
+
try {
|
|
38865
|
+
const e = ctx.getService(import_observability3.OBSERVABILITY_ERRORS_SERVICE);
|
|
38866
|
+
if (e) return e;
|
|
38867
|
+
} catch {
|
|
38868
|
+
}
|
|
38869
|
+
return new import_observability2.NoopErrorReporter();
|
|
38870
|
+
}
|
|
38871
|
+
|
|
38791
38872
|
// src/dispatcher-plugin.ts
|
|
38792
38873
|
function mountRouteOnServer(route, server, routePath, securityHeaders) {
|
|
38793
38874
|
const handler = async (req, res) => {
|
|
@@ -40511,7 +40592,39 @@ var ArtifactKernelFactory = class {
|
|
|
40511
40592
|
// intentionally do NOT pass crossSubDomainCookies here
|
|
40512
40593
|
// so cookies stay isolated per project subdomain.
|
|
40513
40594
|
trustedOrigins: trustedOriginsList.length ? trustedOriginsList : void 0,
|
|
40514
|
-
...oidcProviders ? { oidcProviders } : {}
|
|
40595
|
+
...oidcProviders ? { oidcProviders } : {},
|
|
40596
|
+
// Auto-provision a personal organization for every new
|
|
40597
|
+
// user. SecurityPlugin's ObjectQL middleware does this
|
|
40598
|
+
// for direct `ql.insert` calls, but better-auth's
|
|
40599
|
+
// adapter writes through `dataEngine` directly,
|
|
40600
|
+
// bypassing that middleware — so JIT-created SSO users
|
|
40601
|
+
// would otherwise land on the empty "create
|
|
40602
|
+
// organization" screen on first login.
|
|
40603
|
+
databaseHooks: {
|
|
40604
|
+
user: {
|
|
40605
|
+
create: {
|
|
40606
|
+
after: async (user) => {
|
|
40607
|
+
try {
|
|
40608
|
+
const ql = kernel.getService("objectql");
|
|
40609
|
+
if (!ql) return;
|
|
40610
|
+
const [{ ensureUserHasOrganization, cloneTenantSeedData }] = await Promise.all([
|
|
40611
|
+
import("@objectstack/plugin-security")
|
|
40612
|
+
]);
|
|
40613
|
+
await ensureUserHasOrganization(ql, user, {
|
|
40614
|
+
logger: this.logger,
|
|
40615
|
+
cloneSeedData: cloneTenantSeedData
|
|
40616
|
+
});
|
|
40617
|
+
} catch (e) {
|
|
40618
|
+
this.logger.warn?.("[ArtifactKernelFactory] auto-org provisioning hook failed", {
|
|
40619
|
+
projectId,
|
|
40620
|
+
userId: user?.id,
|
|
40621
|
+
error: e?.message
|
|
40622
|
+
});
|
|
40623
|
+
}
|
|
40624
|
+
}
|
|
40625
|
+
}
|
|
40626
|
+
}
|
|
40627
|
+
}
|
|
40515
40628
|
}));
|
|
40516
40629
|
if (oidcProviders) {
|
|
40517
40630
|
this.logger.info?.("[ArtifactKernelFactory] platform SSO wired", {
|
|
@@ -40904,6 +41017,114 @@ var AuthProxyPlugin = class {
|
|
|
40904
41017
|
}
|
|
40905
41018
|
};
|
|
40906
41019
|
|
|
41020
|
+
// src/cloud/cloud-url.ts
|
|
41021
|
+
var DEFAULT_CLOUD_URL = "https://cloud.objectos.app";
|
|
41022
|
+
function resolveCloudUrl(explicit) {
|
|
41023
|
+
const raw = (explicit ?? process.env.OS_CLOUD_URL ?? "").trim();
|
|
41024
|
+
const lower = raw.toLowerCase();
|
|
41025
|
+
if (lower === "off" || lower === "none" || lower === "local" || lower === "disabled") {
|
|
41026
|
+
return "";
|
|
41027
|
+
}
|
|
41028
|
+
const picked = raw || DEFAULT_CLOUD_URL;
|
|
41029
|
+
return picked.replace(/\/+$/, "");
|
|
41030
|
+
}
|
|
41031
|
+
|
|
41032
|
+
// src/cloud/marketplace-proxy-plugin.ts
|
|
41033
|
+
var MARKETPLACE_PREFIX = "/api/v1/marketplace";
|
|
41034
|
+
var MarketplaceProxyPlugin = class _MarketplaceProxyPlugin {
|
|
41035
|
+
constructor(config = {}) {
|
|
41036
|
+
this.name = "com.objectstack.runtime.marketplace-proxy";
|
|
41037
|
+
this.version = "1.0.0";
|
|
41038
|
+
this.init = async (_ctx) => {
|
|
41039
|
+
};
|
|
41040
|
+
this.start = async (ctx) => {
|
|
41041
|
+
ctx.hook("kernel:ready", async () => {
|
|
41042
|
+
let httpServer;
|
|
41043
|
+
try {
|
|
41044
|
+
httpServer = ctx.getService("http-server");
|
|
41045
|
+
} catch {
|
|
41046
|
+
ctx.logger?.warn?.("[MarketplaceProxyPlugin] http-server not available \u2014 marketplace routes not mounted");
|
|
41047
|
+
return;
|
|
41048
|
+
}
|
|
41049
|
+
if (!httpServer || typeof httpServer.getRawApp !== "function") {
|
|
41050
|
+
ctx.logger?.warn?.("[MarketplaceProxyPlugin] http-server missing getRawApp() \u2014 marketplace routes not mounted");
|
|
41051
|
+
return;
|
|
41052
|
+
}
|
|
41053
|
+
const rawApp = httpServer.getRawApp();
|
|
41054
|
+
const cloudUrl = this.cloudUrl;
|
|
41055
|
+
const handler = async (c, next) => {
|
|
41056
|
+
if (!cloudUrl) {
|
|
41057
|
+
return c.json({
|
|
41058
|
+
success: false,
|
|
41059
|
+
error: {
|
|
41060
|
+
code: "marketplace_unavailable",
|
|
41061
|
+
message: "No control-plane URL configured for this runtime (OS_CLOUD_URL)."
|
|
41062
|
+
}
|
|
41063
|
+
}, 503);
|
|
41064
|
+
}
|
|
41065
|
+
try {
|
|
41066
|
+
const incomingUrl = new URL(c.req.url);
|
|
41067
|
+
if (incomingUrl.pathname.startsWith(`${MARKETPLACE_PREFIX}/install-local`)) {
|
|
41068
|
+
return next();
|
|
41069
|
+
}
|
|
41070
|
+
const target = `${cloudUrl}${incomingUrl.pathname}${incomingUrl.search}`;
|
|
41071
|
+
const method = String(c.req.method ?? "GET").toUpperCase();
|
|
41072
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
41073
|
+
return c.json({
|
|
41074
|
+
success: false,
|
|
41075
|
+
error: {
|
|
41076
|
+
code: "marketplace_method_not_allowed",
|
|
41077
|
+
message: `Marketplace proxy only forwards GET/HEAD; install via cloud.`
|
|
41078
|
+
}
|
|
41079
|
+
}, 405);
|
|
41080
|
+
}
|
|
41081
|
+
const resp = await fetch(target, {
|
|
41082
|
+
method,
|
|
41083
|
+
headers: {
|
|
41084
|
+
// Strip the inbound Host header — fetch will set
|
|
41085
|
+
// it to the cloud host. Forward only the
|
|
41086
|
+
// identifying headers cloud might log.
|
|
41087
|
+
"Accept": c.req.header("accept") ?? "application/json",
|
|
41088
|
+
"User-Agent": `objectos-marketplace-proxy/${_MarketplaceProxyPlugin.prototype.version ?? "1.0.0"}`
|
|
41089
|
+
}
|
|
41090
|
+
});
|
|
41091
|
+
const headers = new Headers();
|
|
41092
|
+
const passthroughHeaders = ["content-type", "cache-control", "etag", "last-modified"];
|
|
41093
|
+
for (const h of passthroughHeaders) {
|
|
41094
|
+
const v = resp.headers.get(h);
|
|
41095
|
+
if (v) headers.set(h, v);
|
|
41096
|
+
}
|
|
41097
|
+
const body = await resp.arrayBuffer();
|
|
41098
|
+
return new Response(body, { status: resp.status, headers });
|
|
41099
|
+
} catch (err) {
|
|
41100
|
+
const errObj = err instanceof Error ? err : new Error(err?.message ?? String(err));
|
|
41101
|
+
ctx.logger?.error?.("[MarketplaceProxyPlugin] proxy failed", errObj);
|
|
41102
|
+
return c.json({
|
|
41103
|
+
success: false,
|
|
41104
|
+
error: {
|
|
41105
|
+
code: "marketplace_proxy_failed",
|
|
41106
|
+
message: err?.message ?? String(err)
|
|
41107
|
+
}
|
|
41108
|
+
}, 502);
|
|
41109
|
+
}
|
|
41110
|
+
};
|
|
41111
|
+
if (typeof rawApp.all === "function") {
|
|
41112
|
+
rawApp.all(`${MARKETPLACE_PREFIX}/*`, handler);
|
|
41113
|
+
} else {
|
|
41114
|
+
for (const m of ["get", "head"]) {
|
|
41115
|
+
try {
|
|
41116
|
+
rawApp[m]?.(`${MARKETPLACE_PREFIX}/*`, handler);
|
|
41117
|
+
} catch {
|
|
41118
|
+
}
|
|
41119
|
+
}
|
|
41120
|
+
}
|
|
41121
|
+
ctx.logger?.info?.(`[MarketplaceProxyPlugin] mounted at ${MARKETPLACE_PREFIX}/* \u2192 ${cloudUrl || "(unconfigured)"}`);
|
|
41122
|
+
});
|
|
41123
|
+
};
|
|
41124
|
+
this.cloudUrl = resolveCloudUrl(config.controlPlaneUrl);
|
|
41125
|
+
}
|
|
41126
|
+
};
|
|
41127
|
+
|
|
40907
41128
|
// src/cloud/file-artifact-api-client.ts
|
|
40908
41129
|
var import_promises2 = require("fs/promises");
|
|
40909
41130
|
var import_node_path5 = require("path");
|
|
@@ -41130,7 +41351,7 @@ async function createObjectOSStack(config) {
|
|
|
41130
41351
|
};
|
|
41131
41352
|
const enginePlugins = await createHostEnginePlugins();
|
|
41132
41353
|
return {
|
|
41133
|
-
plugins: [...enginePlugins, new ObjectOSProjectPlugin(merged), new AuthProxyPlugin()],
|
|
41354
|
+
plugins: [...enginePlugins, new ObjectOSProjectPlugin(merged), new AuthProxyPlugin(), new MarketplaceProxyPlugin({ controlPlaneUrl: merged.controlPlaneUrl === "file" ? void 0 : merged.controlPlaneUrl })],
|
|
41134
41355
|
api: {
|
|
41135
41356
|
enableProjectScoping: true,
|
|
41136
41357
|
projectResolution: "auto",
|
|
@@ -41144,6 +41365,462 @@ async function createObjectOSStack(config) {
|
|
|
41144
41365
|
};
|
|
41145
41366
|
}
|
|
41146
41367
|
|
|
41368
|
+
// src/cloud/marketplace-install-local-plugin.ts
|
|
41369
|
+
var import_node_fs3 = require("fs");
|
|
41370
|
+
var import_node_path6 = require("path");
|
|
41371
|
+
var ROUTE_BASE = "/api/v1/marketplace/install-local";
|
|
41372
|
+
var DEFAULT_DIR = ".objectstack/installed-packages";
|
|
41373
|
+
function safeFilename(manifestId) {
|
|
41374
|
+
return manifestId.replace(/[^a-zA-Z0-9._-]/g, "_") + ".json";
|
|
41375
|
+
}
|
|
41376
|
+
var MarketplaceInstallLocalPlugin = class {
|
|
41377
|
+
constructor(config = {}) {
|
|
41378
|
+
this.name = "com.objectstack.runtime.marketplace-install-local";
|
|
41379
|
+
this.version = "1.0.0";
|
|
41380
|
+
this.init = async (_ctx) => {
|
|
41381
|
+
};
|
|
41382
|
+
this.start = async (ctx) => {
|
|
41383
|
+
ctx.hook("kernel:ready", async () => {
|
|
41384
|
+
await this.rehydrate(ctx);
|
|
41385
|
+
let httpServer;
|
|
41386
|
+
try {
|
|
41387
|
+
httpServer = ctx.getService("http-server");
|
|
41388
|
+
} catch {
|
|
41389
|
+
ctx.logger?.warn?.("[MarketplaceInstallLocal] http-server not available \u2014 install endpoints not mounted");
|
|
41390
|
+
return;
|
|
41391
|
+
}
|
|
41392
|
+
if (!httpServer || typeof httpServer.getRawApp !== "function") {
|
|
41393
|
+
ctx.logger?.warn?.("[MarketplaceInstallLocal] http-server missing getRawApp() \u2014 install endpoints not mounted");
|
|
41394
|
+
return;
|
|
41395
|
+
}
|
|
41396
|
+
const rawApp = httpServer.getRawApp();
|
|
41397
|
+
const postHandler = async (c) => this.handleInstall(c, ctx);
|
|
41398
|
+
const getHandler = async (c) => this.handleList(c);
|
|
41399
|
+
const deleteHandler = async (c) => this.handleUninstall(c, ctx);
|
|
41400
|
+
if (typeof rawApp.post === "function") rawApp.post(ROUTE_BASE, postHandler);
|
|
41401
|
+
if (typeof rawApp.get === "function") rawApp.get(ROUTE_BASE, getHandler);
|
|
41402
|
+
if (typeof rawApp.delete === "function") rawApp.delete(`${ROUTE_BASE}/:manifestId`, deleteHandler);
|
|
41403
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] mounted at ${ROUTE_BASE} (storage: ${this.storageDir})`);
|
|
41404
|
+
});
|
|
41405
|
+
};
|
|
41406
|
+
/**
|
|
41407
|
+
* Re-register every cached manifest with the kernel's manifest service.
|
|
41408
|
+
* Safe to call on a kernel that already has the same manifest_id (the
|
|
41409
|
+
* underlying ObjectQL registry overwrites by id, but we still warn so
|
|
41410
|
+
* a developer can spot the dev-time clash between their config.ts and
|
|
41411
|
+
* a marketplace package).
|
|
41412
|
+
*/
|
|
41413
|
+
this.rehydrate = async (ctx) => {
|
|
41414
|
+
const entries = this.readAll();
|
|
41415
|
+
if (entries.length === 0) return;
|
|
41416
|
+
let manifestService = null;
|
|
41417
|
+
try {
|
|
41418
|
+
manifestService = ctx.getService("manifest");
|
|
41419
|
+
} catch {
|
|
41420
|
+
ctx.logger?.warn?.("[MarketplaceInstallLocal] no `manifest` service \u2014 rehydrate skipped");
|
|
41421
|
+
return;
|
|
41422
|
+
}
|
|
41423
|
+
for (const entry of entries) {
|
|
41424
|
+
try {
|
|
41425
|
+
manifestService.register(entry.manifest);
|
|
41426
|
+
try {
|
|
41427
|
+
const ql = ctx.getService("objectql");
|
|
41428
|
+
if (ql && typeof ql.syncSchemas === "function") await ql.syncSchemas();
|
|
41429
|
+
} catch {
|
|
41430
|
+
}
|
|
41431
|
+
await this.applySideEffects(ctx, entry.manifest, { seedNow: false });
|
|
41432
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] rehydrated ${entry.manifestId}@${entry.version}`);
|
|
41433
|
+
} catch (err) {
|
|
41434
|
+
ctx.logger?.error?.(`[MarketplaceInstallLocal] rehydrate failed for ${entry.manifestId}`, err instanceof Error ? err : new Error(String(err)));
|
|
41435
|
+
}
|
|
41436
|
+
}
|
|
41437
|
+
};
|
|
41438
|
+
this.handleInstall = async (c, ctx) => {
|
|
41439
|
+
if (!this.cloudUrl) {
|
|
41440
|
+
return c.json({ success: false, error: { code: "marketplace_unavailable", message: "OS_CLOUD_URL not configured." } }, 503);
|
|
41441
|
+
}
|
|
41442
|
+
const userId = await this.requireAuthenticatedUser(c, ctx);
|
|
41443
|
+
if (!userId) {
|
|
41444
|
+
return c.json({ success: false, error: { code: "unauthorized", message: "Authentication required to install packages." } }, 401);
|
|
41445
|
+
}
|
|
41446
|
+
let body = {};
|
|
41447
|
+
try {
|
|
41448
|
+
body = await c.req.json();
|
|
41449
|
+
} catch {
|
|
41450
|
+
}
|
|
41451
|
+
const packageId = String(body?.packageId ?? "").trim();
|
|
41452
|
+
const versionId = String(body?.versionId ?? "latest").trim() || "latest";
|
|
41453
|
+
if (!packageId) {
|
|
41454
|
+
return c.json({ success: false, error: { code: "bad_request", message: "packageId is required." } }, 400);
|
|
41455
|
+
}
|
|
41456
|
+
let payload;
|
|
41457
|
+
try {
|
|
41458
|
+
const url = `${this.cloudUrl}/api/v1/marketplace/packages/${encodeURIComponent(packageId)}/versions/${encodeURIComponent(versionId)}/manifest`;
|
|
41459
|
+
const resp = await fetch(url, { headers: { "Accept": "application/json" } });
|
|
41460
|
+
if (!resp.ok) {
|
|
41461
|
+
const text = await resp.text().catch(() => "");
|
|
41462
|
+
return c.json({
|
|
41463
|
+
success: false,
|
|
41464
|
+
error: { code: "cloud_fetch_failed", message: `Cloud returned ${resp.status}: ${text.slice(0, 200)}` }
|
|
41465
|
+
}, resp.status === 404 ? 404 : 502);
|
|
41466
|
+
}
|
|
41467
|
+
payload = await resp.json();
|
|
41468
|
+
} catch (err) {
|
|
41469
|
+
return c.json({
|
|
41470
|
+
success: false,
|
|
41471
|
+
error: { code: "cloud_fetch_failed", message: err?.message ?? String(err) }
|
|
41472
|
+
}, 502);
|
|
41473
|
+
}
|
|
41474
|
+
const data = payload?.data ?? payload;
|
|
41475
|
+
const manifest = data?.manifest;
|
|
41476
|
+
const resolvedVersionId = String(data?.version_id ?? versionId);
|
|
41477
|
+
const version = String(data?.version ?? "unknown");
|
|
41478
|
+
const manifestId = String(manifest?.id ?? manifest?.name ?? "");
|
|
41479
|
+
if (!manifest || !manifestId) {
|
|
41480
|
+
return c.json({ success: false, error: { code: "invalid_manifest", message: "Cloud returned an invalid manifest payload." } }, 502);
|
|
41481
|
+
}
|
|
41482
|
+
const conflict = this.findConflict(ctx, manifestId);
|
|
41483
|
+
if (conflict === "user-code") {
|
|
41484
|
+
return c.json({
|
|
41485
|
+
success: false,
|
|
41486
|
+
error: {
|
|
41487
|
+
code: "manifest_conflict",
|
|
41488
|
+
message: `manifest_id "${manifestId}" is already defined by this runtime's local code. Refusing to overwrite. Uninstall the local definition first.`
|
|
41489
|
+
}
|
|
41490
|
+
}, 409);
|
|
41491
|
+
}
|
|
41492
|
+
const entry = {
|
|
41493
|
+
packageId,
|
|
41494
|
+
versionId: resolvedVersionId,
|
|
41495
|
+
manifestId,
|
|
41496
|
+
version,
|
|
41497
|
+
manifest,
|
|
41498
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
41499
|
+
installedBy: userId
|
|
41500
|
+
};
|
|
41501
|
+
try {
|
|
41502
|
+
(0, import_node_fs3.mkdirSync)(this.storageDir, { recursive: true });
|
|
41503
|
+
(0, import_node_fs3.writeFileSync)((0, import_node_path6.join)(this.storageDir, safeFilename(manifestId)), JSON.stringify(entry, null, 2), "utf8");
|
|
41504
|
+
} catch (err) {
|
|
41505
|
+
return c.json({
|
|
41506
|
+
success: false,
|
|
41507
|
+
error: { code: "storage_failed", message: `Failed to persist manifest: ${err?.message ?? err}` }
|
|
41508
|
+
}, 500);
|
|
41509
|
+
}
|
|
41510
|
+
try {
|
|
41511
|
+
const manifestService = ctx.getService("manifest");
|
|
41512
|
+
manifestService.register(manifest);
|
|
41513
|
+
} catch (err) {
|
|
41514
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] hot-register failed for ${manifestId} (will load on next restart): ${err?.message ?? err}`);
|
|
41515
|
+
}
|
|
41516
|
+
try {
|
|
41517
|
+
const ql = ctx.getService("objectql");
|
|
41518
|
+
if (ql && typeof ql.syncSchemas === "function") {
|
|
41519
|
+
await ql.syncSchemas();
|
|
41520
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] syncSchemas() ran after registering ${manifestId}`);
|
|
41521
|
+
}
|
|
41522
|
+
} catch (err) {
|
|
41523
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] syncSchemas failed for ${manifestId}: ${err?.message ?? err}`);
|
|
41524
|
+
}
|
|
41525
|
+
const seededSummary = await this.applySideEffects(ctx, manifest, { seedNow: true, c });
|
|
41526
|
+
return c.json({
|
|
41527
|
+
success: true,
|
|
41528
|
+
data: {
|
|
41529
|
+
manifestId,
|
|
41530
|
+
version,
|
|
41531
|
+
versionId: resolvedVersionId,
|
|
41532
|
+
installedAt: entry.installedAt,
|
|
41533
|
+
hotLoaded: true,
|
|
41534
|
+
upgradedFrom: conflict === "marketplace" ? "previous-marketplace-version" : null,
|
|
41535
|
+
translationsLoaded: seededSummary.translationsLoaded,
|
|
41536
|
+
seeded: seededSummary.seeded,
|
|
41537
|
+
note: "App is now available in this runtime. Refresh the console to see it in the app switcher."
|
|
41538
|
+
}
|
|
41539
|
+
}, 200);
|
|
41540
|
+
};
|
|
41541
|
+
this.handleList = async (c) => {
|
|
41542
|
+
const entries = this.readAll();
|
|
41543
|
+
return c.json({
|
|
41544
|
+
success: true,
|
|
41545
|
+
data: {
|
|
41546
|
+
items: entries.map((e) => ({
|
|
41547
|
+
packageId: e.packageId,
|
|
41548
|
+
versionId: e.versionId,
|
|
41549
|
+
manifestId: e.manifestId,
|
|
41550
|
+
version: e.version,
|
|
41551
|
+
installedAt: e.installedAt,
|
|
41552
|
+
installedBy: e.installedBy
|
|
41553
|
+
})),
|
|
41554
|
+
total: entries.length,
|
|
41555
|
+
storageDir: this.storageDir
|
|
41556
|
+
}
|
|
41557
|
+
}, 200);
|
|
41558
|
+
};
|
|
41559
|
+
this.handleUninstall = async (c, ctx) => {
|
|
41560
|
+
const userId = await this.requireAuthenticatedUser(c, ctx);
|
|
41561
|
+
if (!userId) {
|
|
41562
|
+
return c.json({ success: false, error: { code: "unauthorized", message: "Authentication required." } }, 401);
|
|
41563
|
+
}
|
|
41564
|
+
const manifestId = String(c.req.param?.("manifestId") ?? c.req.params?.manifestId ?? "").trim();
|
|
41565
|
+
if (!manifestId) {
|
|
41566
|
+
return c.json({ success: false, error: { code: "bad_request", message: "manifestId path param required." } }, 400);
|
|
41567
|
+
}
|
|
41568
|
+
const file = (0, import_node_path6.join)(this.storageDir, safeFilename(manifestId));
|
|
41569
|
+
if (!(0, import_node_fs3.existsSync)(file)) {
|
|
41570
|
+
return c.json({ success: false, error: { code: "not_found", message: `No marketplace install for ${manifestId}.` } }, 404);
|
|
41571
|
+
}
|
|
41572
|
+
try {
|
|
41573
|
+
(0, import_node_fs3.unlinkSync)(file);
|
|
41574
|
+
} catch (err) {
|
|
41575
|
+
return c.json({ success: false, error: { code: "storage_failed", message: err?.message ?? String(err) } }, 500);
|
|
41576
|
+
}
|
|
41577
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] uninstalled ${manifestId} (cached manifest removed; restart runtime to unload from running kernel)`);
|
|
41578
|
+
return c.json({
|
|
41579
|
+
success: true,
|
|
41580
|
+
data: {
|
|
41581
|
+
manifestId,
|
|
41582
|
+
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)."
|
|
41583
|
+
}
|
|
41584
|
+
}, 200);
|
|
41585
|
+
};
|
|
41586
|
+
/**
|
|
41587
|
+
* Detect whether `manifestId` is already known to the kernel and classify
|
|
41588
|
+
* the source so we can refuse vs upgrade gracefully.
|
|
41589
|
+
*
|
|
41590
|
+
* 'none' — fresh install
|
|
41591
|
+
* 'marketplace' — previously installed by this plugin (allow upgrade)
|
|
41592
|
+
* 'user-code' — defined by AppPlugin from objectstack.config.ts
|
|
41593
|
+
* (refuse to avoid silently overwriting authored code)
|
|
41594
|
+
*/
|
|
41595
|
+
this.findConflict = (ctx, manifestId) => {
|
|
41596
|
+
if ((0, import_node_fs3.existsSync)((0, import_node_path6.join)(this.storageDir, safeFilename(manifestId)))) {
|
|
41597
|
+
return "marketplace";
|
|
41598
|
+
}
|
|
41599
|
+
try {
|
|
41600
|
+
const ql = ctx.getService("objectql");
|
|
41601
|
+
const packages = ql?.registry?.getAllPackages?.() ?? [];
|
|
41602
|
+
const hit = packages.find(
|
|
41603
|
+
(p) => (p?.manifest?.id ?? p?.id ?? p?.manifest?.name) === manifestId
|
|
41604
|
+
);
|
|
41605
|
+
if (hit) return "user-code";
|
|
41606
|
+
} catch {
|
|
41607
|
+
}
|
|
41608
|
+
return "none";
|
|
41609
|
+
};
|
|
41610
|
+
/**
|
|
41611
|
+
* Pull a userId out of the request's better-auth session, if any.
|
|
41612
|
+
* Returns null when there is no signed-in user. v1 does not check
|
|
41613
|
+
* admin role — UI gating + the auth requirement is sufficient for
|
|
41614
|
+
* dev / single-tenant runtimes. Stricter checks can be layered on
|
|
41615
|
+
* via a middleware in cloud-hosted multi-tenant deployments.
|
|
41616
|
+
*/
|
|
41617
|
+
/**
|
|
41618
|
+
* Replicate the start-time side-effects that AppPlugin runs for
|
|
41619
|
+
* statically-declared apps but the `manifest` service does NOT:
|
|
41620
|
+
*
|
|
41621
|
+
* 1. Load `manifest.translations` (array of `Record<locale, data>`)
|
|
41622
|
+
* into the i18n service — auto-creating an in-memory fallback if
|
|
41623
|
+
* none is registered, matching AppPlugin's behaviour.
|
|
41624
|
+
*
|
|
41625
|
+
* 2. Merge `manifest.data` (an array of seed datasets) into the
|
|
41626
|
+
* kernel's `seed-datasets` service so SecurityPlugin's per-org
|
|
41627
|
+
* replay middleware picks them up on every future
|
|
41628
|
+
* sys_organization insert.
|
|
41629
|
+
*
|
|
41630
|
+
* 3. When `seedNow=true`, also run the seed immediately so the user
|
|
41631
|
+
* sees demo data without having to create a new org:
|
|
41632
|
+
* • single-tenant: run SeedLoaderService inline (mirrors
|
|
41633
|
+
* AppPlugin single-tenant branch)
|
|
41634
|
+
* • multi-tenant: invoke `seed-replayer` for the caller's
|
|
41635
|
+
* active org (resolved from the request session)
|
|
41636
|
+
*
|
|
41637
|
+
* Errors are logged but never thrown — install succeeds even if
|
|
41638
|
+
* post-register side-effects partially fail (the manifest itself is
|
|
41639
|
+
* already registered + cached). Returns a small summary for the
|
|
41640
|
+
* response envelope.
|
|
41641
|
+
*/
|
|
41642
|
+
this.applySideEffects = async (ctx, manifest, opts) => {
|
|
41643
|
+
const appId = String(manifest?.id ?? "unknown");
|
|
41644
|
+
let translationsLoaded = 0;
|
|
41645
|
+
let seedSummary = { mode: "skipped", reason: "no-datasets" };
|
|
41646
|
+
try {
|
|
41647
|
+
const bundles = [];
|
|
41648
|
+
if (Array.isArray(manifest?.translations)) bundles.push(...manifest.translations);
|
|
41649
|
+
if (Array.isArray(manifest?.i18n)) bundles.push(...manifest.i18n);
|
|
41650
|
+
if (bundles.length > 0) {
|
|
41651
|
+
let i18nService;
|
|
41652
|
+
try {
|
|
41653
|
+
i18nService = ctx.getService("i18n");
|
|
41654
|
+
} catch {
|
|
41655
|
+
}
|
|
41656
|
+
if (!i18nService) {
|
|
41657
|
+
try {
|
|
41658
|
+
const mod = await import("@objectstack/core");
|
|
41659
|
+
const createMemoryI18n = mod.createMemoryI18n;
|
|
41660
|
+
if (typeof createMemoryI18n === "function") {
|
|
41661
|
+
i18nService = createMemoryI18n();
|
|
41662
|
+
ctx.registerService?.("i18n", i18nService);
|
|
41663
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] auto-registered in-memory i18n fallback for "${appId}"`);
|
|
41664
|
+
}
|
|
41665
|
+
} catch {
|
|
41666
|
+
}
|
|
41667
|
+
}
|
|
41668
|
+
if (i18nService?.loadTranslations) {
|
|
41669
|
+
for (const bundle of bundles) {
|
|
41670
|
+
for (const [locale, data] of Object.entries(bundle)) {
|
|
41671
|
+
if (data && typeof data === "object") {
|
|
41672
|
+
try {
|
|
41673
|
+
i18nService.loadTranslations(locale, data);
|
|
41674
|
+
translationsLoaded++;
|
|
41675
|
+
} catch (err) {
|
|
41676
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] failed to load ${appId} translations for ${locale}: ${err?.message ?? err}`);
|
|
41677
|
+
}
|
|
41678
|
+
}
|
|
41679
|
+
}
|
|
41680
|
+
}
|
|
41681
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] loaded ${translationsLoaded} locale bundle(s) for ${appId}`);
|
|
41682
|
+
}
|
|
41683
|
+
}
|
|
41684
|
+
} catch (err) {
|
|
41685
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] i18n side-effect failed for ${appId}: ${err?.message ?? err}`);
|
|
41686
|
+
}
|
|
41687
|
+
const datasets = Array.isArray(manifest?.data) ? manifest.data.filter((d) => d && d.object && Array.isArray(d.records)) : [];
|
|
41688
|
+
if (datasets.length > 0) {
|
|
41689
|
+
try {
|
|
41690
|
+
const kernel = ctx.kernel;
|
|
41691
|
+
let existing = [];
|
|
41692
|
+
try {
|
|
41693
|
+
const v = kernel?.getService?.("seed-datasets");
|
|
41694
|
+
if (Array.isArray(v)) existing = v;
|
|
41695
|
+
} catch {
|
|
41696
|
+
}
|
|
41697
|
+
const merged = [...existing, ...datasets];
|
|
41698
|
+
if (kernel?.registerService) kernel.registerService("seed-datasets", merged);
|
|
41699
|
+
else ctx.registerService?.("seed-datasets", merged);
|
|
41700
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] merged ${datasets.length} seed dataset(s) into kernel (total: ${merged.length})`);
|
|
41701
|
+
} catch (err) {
|
|
41702
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] failed to merge seed-datasets: ${err?.message ?? err}`);
|
|
41703
|
+
}
|
|
41704
|
+
}
|
|
41705
|
+
if (opts.seedNow && datasets.length > 0) {
|
|
41706
|
+
const multiTenant = String(process.env.OS_MULTI_TENANT ?? "true").toLowerCase() !== "false";
|
|
41707
|
+
try {
|
|
41708
|
+
const ql = ctx.getService("objectql");
|
|
41709
|
+
let metadata;
|
|
41710
|
+
try {
|
|
41711
|
+
metadata = ctx.getService("metadata");
|
|
41712
|
+
} catch {
|
|
41713
|
+
}
|
|
41714
|
+
if (!ql || !metadata) {
|
|
41715
|
+
seedSummary = { mode: "skipped", reason: "objectql-or-metadata-missing" };
|
|
41716
|
+
} else {
|
|
41717
|
+
let organizationId;
|
|
41718
|
+
if (multiTenant) {
|
|
41719
|
+
const resolved = await this.resolveActiveOrgId(opts.c, ctx);
|
|
41720
|
+
if (resolved) organizationId = resolved;
|
|
41721
|
+
else {
|
|
41722
|
+
seedSummary = { mode: "skipped", reason: "multi-tenant-no-active-org" };
|
|
41723
|
+
ctx.logger?.warn?.("[MarketplaceInstallLocal] multi-tenant: no active org on request \u2014 data not seeded");
|
|
41724
|
+
}
|
|
41725
|
+
}
|
|
41726
|
+
if (!multiTenant || organizationId) {
|
|
41727
|
+
const [{ SeedLoaderService: SeedLoaderService2 }, { SeedLoaderRequestSchema }] = await Promise.all([
|
|
41728
|
+
Promise.resolve().then(() => (init_seed_loader(), seed_loader_exports)),
|
|
41729
|
+
import("@objectstack/spec/data")
|
|
41730
|
+
]);
|
|
41731
|
+
const seedLoader = new SeedLoaderService2(ql, metadata, ctx.logger);
|
|
41732
|
+
const request = SeedLoaderRequestSchema.parse({
|
|
41733
|
+
datasets,
|
|
41734
|
+
config: {
|
|
41735
|
+
defaultMode: "upsert",
|
|
41736
|
+
multiPass: true,
|
|
41737
|
+
...organizationId ? { organizationId } : {}
|
|
41738
|
+
}
|
|
41739
|
+
});
|
|
41740
|
+
const result = await seedLoader.load(request);
|
|
41741
|
+
seedSummary = {
|
|
41742
|
+
mode: "inline",
|
|
41743
|
+
inserted: result.summary.totalInserted,
|
|
41744
|
+
updated: result.summary.totalUpdated,
|
|
41745
|
+
errors: result.errors.length
|
|
41746
|
+
};
|
|
41747
|
+
ctx.logger?.info?.(`[MarketplaceInstallLocal] inline seed for ${appId}${organizationId ? ` (org=${organizationId})` : ""}: inserted=${seedSummary.inserted} updated=${seedSummary.updated} errors=${seedSummary.errors}`);
|
|
41748
|
+
}
|
|
41749
|
+
}
|
|
41750
|
+
} catch (err) {
|
|
41751
|
+
seedSummary = { mode: "skipped", reason: `seed-error: ${err?.message ?? err}` };
|
|
41752
|
+
ctx.logger?.warn?.(`[MarketplaceInstallLocal] seed run failed for ${appId}: ${err?.message ?? err}`);
|
|
41753
|
+
}
|
|
41754
|
+
}
|
|
41755
|
+
return { translationsLoaded, seeded: seedSummary };
|
|
41756
|
+
};
|
|
41757
|
+
/**
|
|
41758
|
+
* Best-effort active-org resolution. Reads the better-auth session
|
|
41759
|
+
* (same path as requireAuthenticatedUser) and returns
|
|
41760
|
+
* `session.activeOrganizationId`, falling back to the user's first
|
|
41761
|
+
* org membership.
|
|
41762
|
+
*/
|
|
41763
|
+
this.resolveActiveOrgId = async (c, ctx) => {
|
|
41764
|
+
if (!c?.req?.raw?.headers) return null;
|
|
41765
|
+
try {
|
|
41766
|
+
const authService = ctx.getService("auth");
|
|
41767
|
+
let api = authService?.api;
|
|
41768
|
+
if (!api && typeof authService?.getApi === "function") api = await authService.getApi();
|
|
41769
|
+
if (!api?.getSession) return null;
|
|
41770
|
+
const session = await api.getSession({ headers: c.req.raw.headers });
|
|
41771
|
+
const direct = session?.session?.activeOrganizationId ?? session?.activeOrganizationId ?? null;
|
|
41772
|
+
if (direct) return String(direct);
|
|
41773
|
+
const userId = session?.user?.id;
|
|
41774
|
+
if (!userId) return null;
|
|
41775
|
+
try {
|
|
41776
|
+
const ql = ctx.getService("objectql");
|
|
41777
|
+
if (ql?.find) {
|
|
41778
|
+
const rows = await ql.find("sys_organization_member", { where: { user_id: userId }, limit: 1, context: { isSystem: true } });
|
|
41779
|
+
const row = Array.isArray(rows) ? rows[0] : rows?.items?.[0] ?? null;
|
|
41780
|
+
return row?.organization_id ? String(row.organization_id) : null;
|
|
41781
|
+
}
|
|
41782
|
+
} catch {
|
|
41783
|
+
}
|
|
41784
|
+
} catch {
|
|
41785
|
+
}
|
|
41786
|
+
return null;
|
|
41787
|
+
};
|
|
41788
|
+
this.requireAuthenticatedUser = async (c, ctx) => {
|
|
41789
|
+
try {
|
|
41790
|
+
const authService = ctx.getService("auth");
|
|
41791
|
+
let api = authService?.api;
|
|
41792
|
+
if (!api && typeof authService?.getApi === "function") {
|
|
41793
|
+
api = await authService.getApi();
|
|
41794
|
+
}
|
|
41795
|
+
if (api?.getSession && c?.req?.raw?.headers) {
|
|
41796
|
+
const session = await api.getSession({ headers: c.req.raw.headers });
|
|
41797
|
+
const userId = session?.user?.id ?? null;
|
|
41798
|
+
if (userId) return String(userId);
|
|
41799
|
+
}
|
|
41800
|
+
} catch {
|
|
41801
|
+
}
|
|
41802
|
+
const xUserId = c?.req?.header?.("x-user-id");
|
|
41803
|
+
if (xUserId) return String(xUserId);
|
|
41804
|
+
return null;
|
|
41805
|
+
};
|
|
41806
|
+
this.readAll = () => {
|
|
41807
|
+
if (!(0, import_node_fs3.existsSync)(this.storageDir)) return [];
|
|
41808
|
+
const out = [];
|
|
41809
|
+
for (const name of (0, import_node_fs3.readdirSync)(this.storageDir)) {
|
|
41810
|
+
if (!name.endsWith(".json")) continue;
|
|
41811
|
+
try {
|
|
41812
|
+
const raw = (0, import_node_fs3.readFileSync)((0, import_node_path6.join)(this.storageDir, name), "utf8");
|
|
41813
|
+
out.push(JSON.parse(raw));
|
|
41814
|
+
} catch {
|
|
41815
|
+
}
|
|
41816
|
+
}
|
|
41817
|
+
return out;
|
|
41818
|
+
};
|
|
41819
|
+
this.cloudUrl = resolveCloudUrl(config.controlPlaneUrl);
|
|
41820
|
+
this.storageDir = config.storageDir ? (0, import_node_path6.resolve)(config.storageDir) : (0, import_node_path6.resolve)(process.cwd(), DEFAULT_DIR);
|
|
41821
|
+
}
|
|
41822
|
+
};
|
|
41823
|
+
|
|
41147
41824
|
// src/index.ts
|
|
41148
41825
|
init_platform_sso();
|
|
41149
41826
|
|
|
@@ -41179,6 +41856,7 @@ __reExport(index_exports, require("@objectstack/core"), module.exports);
|
|
|
41179
41856
|
ArtifactEnvironmentRegistry,
|
|
41180
41857
|
ArtifactKernelFactory,
|
|
41181
41858
|
AuthProxyPlugin,
|
|
41859
|
+
DEFAULT_CLOUD_URL,
|
|
41182
41860
|
DEFAULT_RATE_LIMITS,
|
|
41183
41861
|
DriverPlugin,
|
|
41184
41862
|
FileArtifactApiClient,
|
|
@@ -41187,10 +41865,15 @@ __reExport(index_exports, require("@objectstack/core"), module.exports);
|
|
|
41187
41865
|
InMemoryErrorReporter,
|
|
41188
41866
|
InMemoryMetricsRegistry,
|
|
41189
41867
|
KernelManager,
|
|
41868
|
+
MarketplaceInstallLocalPlugin,
|
|
41869
|
+
MarketplaceProxyPlugin,
|
|
41190
41870
|
MiddlewareManager,
|
|
41191
41871
|
NoopErrorReporter,
|
|
41192
41872
|
NoopMetricsRegistry,
|
|
41873
|
+
OBSERVABILITY_ERRORS_SERVICE,
|
|
41874
|
+
OBSERVABILITY_METRICS_SERVICE,
|
|
41193
41875
|
ObjectKernel,
|
|
41876
|
+
ObservabilityServicePlugin,
|
|
41194
41877
|
PLATFORM_SSO_PROVIDER_ID,
|
|
41195
41878
|
QuickJSScriptRunner,
|
|
41196
41879
|
RUNTIME_METRICS,
|
|
@@ -41227,7 +41910,10 @@ __reExport(index_exports, require("@objectstack/core"), module.exports);
|
|
|
41227
41910
|
mergeRuntimeModule,
|
|
41228
41911
|
parseTraceparent,
|
|
41229
41912
|
readArtifactSource,
|
|
41913
|
+
resolveCloudUrl,
|
|
41230
41914
|
resolveDefaultArtifactPath,
|
|
41915
|
+
resolveErrorReporter,
|
|
41916
|
+
resolveMetrics,
|
|
41231
41917
|
resolveRequestId,
|
|
41232
41918
|
seedPlatformSsoClient,
|
|
41233
41919
|
...require("@objectstack/core")
|