@objectstack/core 16.1.0 → 17.0.0-rc.1
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/CHANGELOG.md +3047 -0
- package/dist/index.cjs +470 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +325 -21
- package/dist/index.d.ts +325 -21
- package/dist/index.js +460 -103
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -10,6 +10,89 @@ var __export = (target, all) => {
|
|
|
10
10
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
// src/plugin-order.ts
|
|
14
|
+
function resolvePluginOrder(plugins) {
|
|
15
|
+
const resolved = [];
|
|
16
|
+
const visited = /* @__PURE__ */ new Set();
|
|
17
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
18
|
+
const visit = (pluginName) => {
|
|
19
|
+
if (visited.has(pluginName)) return;
|
|
20
|
+
if (visiting.has(pluginName)) {
|
|
21
|
+
throw new Error(`[Kernel] Circular dependency detected: ${pluginName}`);
|
|
22
|
+
}
|
|
23
|
+
const plugin = plugins.get(pluginName);
|
|
24
|
+
if (!plugin) {
|
|
25
|
+
throw new Error(`[Kernel] Plugin '${pluginName}' not found`);
|
|
26
|
+
}
|
|
27
|
+
visiting.add(pluginName);
|
|
28
|
+
for (const dep of plugin.dependencies ?? []) {
|
|
29
|
+
if (!plugins.has(dep)) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`[Kernel] Dependency '${dep}' not found for plugin '${pluginName}'`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
visit(dep);
|
|
35
|
+
}
|
|
36
|
+
for (const dep of plugin.optionalDependencies ?? []) {
|
|
37
|
+
if (plugins.has(dep)) visit(dep);
|
|
38
|
+
}
|
|
39
|
+
visiting.delete(pluginName);
|
|
40
|
+
visited.add(pluginName);
|
|
41
|
+
resolved.push(plugin);
|
|
42
|
+
};
|
|
43
|
+
for (const pluginName of plugins.keys()) {
|
|
44
|
+
visit(pluginName);
|
|
45
|
+
}
|
|
46
|
+
return resolved;
|
|
47
|
+
}
|
|
48
|
+
function validateInitServiceContract(ordered, isServiceRegistered) {
|
|
49
|
+
const providerSlot = /* @__PURE__ */ new Map();
|
|
50
|
+
ordered.forEach((plugin, slot) => {
|
|
51
|
+
for (const service of plugin.providesServices ?? []) {
|
|
52
|
+
if (!providerSlot.has(service)) {
|
|
53
|
+
providerSlot.set(service, { plugin: plugin.name, slot });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const violations = [];
|
|
58
|
+
ordered.forEach((plugin, slot) => {
|
|
59
|
+
for (const service of plugin.requiresServices ?? []) {
|
|
60
|
+
if (isServiceRegistered(service)) continue;
|
|
61
|
+
const provider = providerSlot.get(service);
|
|
62
|
+
if (provider && provider.slot > slot) {
|
|
63
|
+
violations.push(
|
|
64
|
+
`'${plugin.name}' requires service '${service}' during init, but '${service}' is provided by '${provider.plugin}', which initializes later (slot ${provider.slot} vs ${slot}). Registration order is not a contract \u2014 declare '${provider.plugin}' in '${plugin.name}'.dependencies (hard) or .optionalDependencies (order-if-present) so the kernel hoists it.`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
if (violations.length > 0) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`[Kernel] Plugin ordering contract violated (#4131):
|
|
72
|
+
- ${violations.join("\n - ")}`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function describeInitOrderFault(currentlyInitializing, plugins, serviceName) {
|
|
77
|
+
if (!currentlyInitializing) return "";
|
|
78
|
+
let providerHint = "";
|
|
79
|
+
for (const plugin of plugins) {
|
|
80
|
+
if (plugin.providesServices?.includes(serviceName)) {
|
|
81
|
+
providerHint = ` '${serviceName}' is provided by composed plugin '${plugin.name}', which has not initialized yet \u2014 declare it in the requiring plugin's dependencies/optionalDependencies.`;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return ` (while plugin '${currentlyInitializing}' was initializing \u2014 a composition/ordering fault, #4131.${providerHint})`;
|
|
86
|
+
}
|
|
87
|
+
function assertInitServiceRequirements(plugin, isServiceRegistered) {
|
|
88
|
+
for (const service of plugin.requiresServices ?? []) {
|
|
89
|
+
if (isServiceRegistered(service)) continue;
|
|
90
|
+
throw new Error(
|
|
91
|
+
`[Kernel] Plugin '${plugin.name}' requires service '${service}' at init, but no such service is registered at this point of the boot. No composed plugin that initializes earlier provides it \u2014 compose a provider (and, if it initializes later without declaring '${service}' in providesServices, order it ahead via this plugin's dependencies/optionalDependencies) (#4131).`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
13
96
|
// src/kernel-base.ts
|
|
14
97
|
var ObjectKernelBase = class {
|
|
15
98
|
constructor(logger) {
|
|
@@ -60,7 +143,9 @@ var ObjectKernelBase = class {
|
|
|
60
143
|
if (this.services instanceof Map) {
|
|
61
144
|
const service = this.services.get(name);
|
|
62
145
|
if (!service) {
|
|
63
|
-
throw new Error(
|
|
146
|
+
throw new Error(
|
|
147
|
+
`[Kernel] Service '${name}' not found${this.describeInitOrderFault(name)}`
|
|
148
|
+
);
|
|
64
149
|
}
|
|
65
150
|
return service;
|
|
66
151
|
} else {
|
|
@@ -111,40 +196,37 @@ var ObjectKernelBase = class {
|
|
|
111
196
|
};
|
|
112
197
|
}
|
|
113
198
|
/**
|
|
114
|
-
* Resolve plugin dependencies using topological sort
|
|
199
|
+
* Resolve plugin dependencies using topological sort — `dependencies`
|
|
200
|
+
* hard, `optionalDependencies` order-if-present (ADR-0116, #4131). One
|
|
201
|
+
* implementation shared with ObjectKernel via `plugin-order.ts`.
|
|
115
202
|
* @returns Ordered list of plugins (dependencies first)
|
|
116
203
|
*/
|
|
117
204
|
resolveDependencies() {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
};
|
|
144
|
-
for (const pluginName of this.plugins.keys()) {
|
|
145
|
-
visit(pluginName);
|
|
146
|
-
}
|
|
147
|
-
return resolved;
|
|
205
|
+
return resolvePluginOrder(this.plugins);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Whether a service is registered on this kernel right now. Backs the
|
|
209
|
+
* init-service contract checks (#4131).
|
|
210
|
+
*/
|
|
211
|
+
hasRegisteredService(name) {
|
|
212
|
+
return this.services.has(name);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Pre-Phase-1 ordering validation (ADR-0116, #4131): a plugin whose
|
|
216
|
+
* `requiresServices` names a service provided only by a LATER plugin is
|
|
217
|
+
* a named boot error before any init side effects.
|
|
218
|
+
*/
|
|
219
|
+
validateInitServices(ordered) {
|
|
220
|
+
validateInitServiceContract(ordered, (name) => this.hasRegisteredService(name));
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* When a getService miss happens while a plugin's init() is running,
|
|
224
|
+
* append the structural diagnosis (#4131): which plugin was initializing,
|
|
225
|
+
* and — when a composed plugin declares the service — who provides it.
|
|
226
|
+
* Empty string outside Phase 1, so non-boot messages stay unchanged.
|
|
227
|
+
*/
|
|
228
|
+
describeInitOrderFault(serviceName) {
|
|
229
|
+
return describeInitOrderFault(this.currentlyInitializing, this.plugins.values(), serviceName);
|
|
148
230
|
}
|
|
149
231
|
/**
|
|
150
232
|
* Run plugin init phase
|
|
@@ -153,12 +235,16 @@ var ObjectKernelBase = class {
|
|
|
153
235
|
async runPluginInit(plugin) {
|
|
154
236
|
const pluginName = plugin.name;
|
|
155
237
|
this.logger.info(`Initializing plugin: ${pluginName}`);
|
|
238
|
+
assertInitServiceRequirements(plugin, (name) => this.hasRegisteredService(name));
|
|
239
|
+
this.currentlyInitializing = pluginName;
|
|
156
240
|
try {
|
|
157
241
|
await plugin.init(this.context);
|
|
158
242
|
this.logger.info(`Plugin initialized: ${pluginName}`);
|
|
159
243
|
} catch (error) {
|
|
160
244
|
this.logger.error(`Plugin init failed: ${pluginName}`, error);
|
|
161
245
|
throw error;
|
|
246
|
+
} finally {
|
|
247
|
+
this.currentlyInitializing = void 0;
|
|
162
248
|
}
|
|
163
249
|
}
|
|
164
250
|
/**
|
|
@@ -1011,7 +1097,11 @@ function createMemoryCache() {
|
|
|
1011
1097
|
let hits = 0;
|
|
1012
1098
|
let misses = 0;
|
|
1013
1099
|
return {
|
|
1014
|
-
|
|
1100
|
+
__serviceInfo: {
|
|
1101
|
+
status: "degraded",
|
|
1102
|
+
handlerReady: false,
|
|
1103
|
+
message: "In-process Map cache \u2014 not shared across instances, lost on restart. Register a cache plugin (e.g. Redis) for a real one."
|
|
1104
|
+
},
|
|
1015
1105
|
_serviceName: "cache",
|
|
1016
1106
|
async get(key) {
|
|
1017
1107
|
const entry = store.get(key);
|
|
@@ -1046,7 +1136,11 @@ function createMemoryQueue() {
|
|
|
1046
1136
|
const handlers = /* @__PURE__ */ new Map();
|
|
1047
1137
|
let msgId = 0;
|
|
1048
1138
|
return {
|
|
1049
|
-
|
|
1139
|
+
__serviceInfo: {
|
|
1140
|
+
status: "degraded",
|
|
1141
|
+
handlerReady: false,
|
|
1142
|
+
message: "Synchronous in-process delivery \u2014 no durability, retry, or cross-instance fan-out. Register a queue plugin (e.g. BullMQ) for a real one."
|
|
1143
|
+
},
|
|
1050
1144
|
_serviceName: "queue",
|
|
1051
1145
|
async publish(queue, data) {
|
|
1052
1146
|
const id = `fallback-msg-${++msgId}`;
|
|
@@ -1073,7 +1167,11 @@ function createMemoryQueue() {
|
|
|
1073
1167
|
function createMemoryJob() {
|
|
1074
1168
|
const jobs = /* @__PURE__ */ new Map();
|
|
1075
1169
|
return {
|
|
1076
|
-
|
|
1170
|
+
__serviceInfo: {
|
|
1171
|
+
status: "degraded",
|
|
1172
|
+
handlerReady: false,
|
|
1173
|
+
message: "In-process job registry \u2014 trigger() runs handlers, but scheduled jobs never fire on their own (no timer). Register a job plugin (e.g. Agenda) for real scheduling."
|
|
1174
|
+
},
|
|
1077
1175
|
_serviceName: "job",
|
|
1078
1176
|
async schedule(name, schedule, handler) {
|
|
1079
1177
|
jobs.set(name, { schedule, handler });
|
|
@@ -1152,7 +1250,15 @@ function createMemoryI18n() {
|
|
|
1152
1250
|
return void 0;
|
|
1153
1251
|
}
|
|
1154
1252
|
return {
|
|
1155
|
-
|
|
1253
|
+
// [#4058] `degraded` (ADR-0076 D12): translations, locale fallback and
|
|
1254
|
+
// interpolation are all real — what is missing is persistence and the
|
|
1255
|
+
// authoring surface service-i18n adds. `handlerReady` left at the
|
|
1256
|
+
// `degraded` default (true): the dispatcher's `/i18n` domain does serve
|
|
1257
|
+
// this implementation.
|
|
1258
|
+
__serviceInfo: {
|
|
1259
|
+
status: "degraded",
|
|
1260
|
+
message: "In-memory translations \u2014 real lookup and locale fallback, but nothing is persisted. Register I18nServicePlugin from @objectstack/service-i18n for the full implementation."
|
|
1261
|
+
},
|
|
1156
1262
|
_serviceName: "i18n",
|
|
1157
1263
|
t(key, locale, params) {
|
|
1158
1264
|
const data = resolveTranslations(locale) ?? mergedLocale(defaultLocale);
|
|
@@ -1209,7 +1315,14 @@ function createMemoryMetadata() {
|
|
|
1209
1315
|
return map;
|
|
1210
1316
|
}
|
|
1211
1317
|
return {
|
|
1212
|
-
|
|
1318
|
+
// [#4058] `degraded` (ADR-0076 D12): the registry is real — everything
|
|
1319
|
+
// registered is listable and readable back — it simply never reaches disk
|
|
1320
|
+
// or a database. `handlerReady` keeps the `degraded` default (true): the
|
|
1321
|
+
// dispatcher's `/meta` domain serves this implementation.
|
|
1322
|
+
__serviceInfo: {
|
|
1323
|
+
status: "degraded",
|
|
1324
|
+
message: "In-memory metadata registry \u2014 real reads and writes, no persistence (lost on restart). Register MetadataPlugin for a persisted registry."
|
|
1325
|
+
},
|
|
1213
1326
|
_serviceName: "metadata",
|
|
1214
1327
|
async register(type, name, data) {
|
|
1215
1328
|
getTypeMap(type).set(name, data);
|
|
@@ -1252,6 +1365,7 @@ function createMemoryMetadata() {
|
|
|
1252
1365
|
}
|
|
1253
1366
|
|
|
1254
1367
|
// src/fallbacks/authored-translation-sync.ts
|
|
1368
|
+
import { LEGACY_OBJECT_FIRST_KEYS } from "@objectstack/spec/system";
|
|
1255
1369
|
var OWNER_PROP = "__authoredTranslationSyncOwner";
|
|
1256
1370
|
var LOCALE_LIKE = /^[a-z]{2,3}([_-]([A-Za-z]{4}|[A-Za-z]{2}|[0-9]{3}))?$/;
|
|
1257
1371
|
async function readAuthoredTranslationLayer(engine, logger) {
|
|
@@ -1281,14 +1395,32 @@ async function readAuthoredTranslationLayer(engine, logger) {
|
|
|
1281
1395
|
continue;
|
|
1282
1396
|
}
|
|
1283
1397
|
if (!data || typeof data !== "object") continue;
|
|
1284
|
-
const
|
|
1398
|
+
const legacyKeys = LEGACY_OBJECT_FIRST_KEYS.filter((key) => data[key] !== void 0);
|
|
1399
|
+
if (legacyKeys.length > 0) {
|
|
1400
|
+
logger?.warn?.(
|
|
1401
|
+
`[i18n] authored translation '${row?.name}' uses the retired object-first shape (${legacyKeys.join(", ")}) \u2014 nothing resolves from it; re-author it under 'objects.<object_name>' with a top-level 'locale' \u2014 skipped`
|
|
1402
|
+
);
|
|
1403
|
+
continue;
|
|
1404
|
+
}
|
|
1405
|
+
const locale = typeof data?.locale === "string" && data.locale || (typeof row?.name === "string" && LOCALE_LIKE.test(row.name) ? row.name : void 0) || void 0;
|
|
1285
1406
|
if (!locale) {
|
|
1286
1407
|
logger?.warn?.(
|
|
1287
|
-
`[i18n] authored translation '${row?.name}' has no resolvable locale (set
|
|
1408
|
+
`[i18n] authored translation '${row?.name}' has no resolvable locale (set the top-level 'locale', or name the item after its BCP-47 locale) \u2014 skipped`
|
|
1288
1409
|
);
|
|
1289
1410
|
continue;
|
|
1290
1411
|
}
|
|
1291
|
-
const {
|
|
1412
|
+
const {
|
|
1413
|
+
name: _n,
|
|
1414
|
+
locale: _l,
|
|
1415
|
+
_packageId: _p,
|
|
1416
|
+
_packageVersion: _pv,
|
|
1417
|
+
_provenance: _pr,
|
|
1418
|
+
_lock: _lk,
|
|
1419
|
+
_lockReason: _lr,
|
|
1420
|
+
_lockDocsUrl: _ld,
|
|
1421
|
+
_lockSource: _ls,
|
|
1422
|
+
...payload
|
|
1423
|
+
} = data;
|
|
1292
1424
|
byLocale[locale] = deepMerge(byLocale[locale] ?? {}, payload);
|
|
1293
1425
|
}
|
|
1294
1426
|
return byLocale;
|
|
@@ -1406,24 +1538,12 @@ var ObjectKernel = class {
|
|
|
1406
1538
|
this.services.set(name, loaderService);
|
|
1407
1539
|
return loaderService;
|
|
1408
1540
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
});
|
|
1414
|
-
throw new Error(`Service '${name}' is async - use await`);
|
|
1415
|
-
}
|
|
1416
|
-
return service2;
|
|
1417
|
-
} catch (error) {
|
|
1418
|
-
if (error.message?.includes("is async")) {
|
|
1419
|
-
throw error;
|
|
1420
|
-
}
|
|
1421
|
-
const isNotFoundError = error.message === `Service '${name}' not found`;
|
|
1422
|
-
if (!isNotFoundError) {
|
|
1423
|
-
throw error;
|
|
1424
|
-
}
|
|
1425
|
-
throw new Error(`[Kernel] Service '${name}' not found`);
|
|
1541
|
+
if (!this.pluginLoader.hasService(name)) {
|
|
1542
|
+
throw new Error(
|
|
1543
|
+
`[Kernel] Service '${name}' not found${this.describeInitOrderFault(name)}`
|
|
1544
|
+
);
|
|
1426
1545
|
}
|
|
1546
|
+
throw new Error(`Service '${name}' is async - use await`);
|
|
1427
1547
|
},
|
|
1428
1548
|
replaceService: (name, implementation) => {
|
|
1429
1549
|
const hasService = this.services.has(name) || this.pluginLoader.hasService(name);
|
|
@@ -1582,6 +1702,7 @@ var ObjectKernel = class {
|
|
|
1582
1702
|
this.logger.warn("Circular service dependencies detected:", { cycles });
|
|
1583
1703
|
}
|
|
1584
1704
|
const orderedPlugins = this.resolveDependencies();
|
|
1705
|
+
validateInitServiceContract(orderedPlugins, (name) => this.hasAnyService(name));
|
|
1585
1706
|
this.logger.info("Phase 1: Init plugins");
|
|
1586
1707
|
for (const plugin of orderedPlugins) {
|
|
1587
1708
|
await this.initPluginWithTimeout(plugin);
|
|
@@ -1727,13 +1848,36 @@ var ObjectKernel = class {
|
|
|
1727
1848
|
async initPluginWithTimeout(plugin) {
|
|
1728
1849
|
const timeout = plugin.startupTimeout || this.config.defaultStartupTimeout;
|
|
1729
1850
|
this.logger.debug(`Init: ${plugin.name}`, { plugin: plugin.name });
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1851
|
+
assertInitServiceRequirements(plugin, (name) => this.hasAnyService(name));
|
|
1852
|
+
this.currentlyInitializing = plugin.name;
|
|
1853
|
+
try {
|
|
1854
|
+
const initPromise = plugin.init(this.context);
|
|
1855
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1856
|
+
setTimeout(() => {
|
|
1857
|
+
reject(new Error(`Plugin ${plugin.name} init timeout after ${timeout}ms`));
|
|
1858
|
+
}, timeout);
|
|
1859
|
+
});
|
|
1860
|
+
await Promise.race([initPromise, timeoutPromise]);
|
|
1861
|
+
} finally {
|
|
1862
|
+
this.currentlyInitializing = void 0;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* Whether a service is resolvable on this kernel right now — direct
|
|
1867
|
+
* registration or a loader-registered factory. Backs the init-service
|
|
1868
|
+
* contract checks (#4131).
|
|
1869
|
+
*/
|
|
1870
|
+
hasAnyService(name) {
|
|
1871
|
+
return this.services.has(name) || this.pluginLoader.hasService(name);
|
|
1872
|
+
}
|
|
1873
|
+
/**
|
|
1874
|
+
* When a getService miss happens while a plugin's init() is running,
|
|
1875
|
+
* append the structural diagnosis (#4131): which plugin was initializing,
|
|
1876
|
+
* and — when a composed plugin declares the service — who provides it.
|
|
1877
|
+
* Empty string outside Phase 1, so non-boot messages stay unchanged.
|
|
1878
|
+
*/
|
|
1879
|
+
describeInitOrderFault(serviceName) {
|
|
1880
|
+
return describeInitOrderFault(this.currentlyInitializing, this.plugins.values(), serviceName);
|
|
1737
1881
|
}
|
|
1738
1882
|
async startPluginWithTimeout(plugin) {
|
|
1739
1883
|
if (!plugin.start) {
|
|
@@ -1807,35 +1951,13 @@ var ObjectKernel = class {
|
|
|
1807
1951
|
}
|
|
1808
1952
|
}
|
|
1809
1953
|
}
|
|
1954
|
+
/**
|
|
1955
|
+
* Topological order over `dependencies` (hard) + `optionalDependencies`
|
|
1956
|
+
* (order-if-present) — ADR-0116, #4131. One implementation shared with
|
|
1957
|
+
* LiteKernel via `plugin-order.ts`.
|
|
1958
|
+
*/
|
|
1810
1959
|
resolveDependencies() {
|
|
1811
|
-
|
|
1812
|
-
const visited = /* @__PURE__ */ new Set();
|
|
1813
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
1814
|
-
const visit = (pluginName) => {
|
|
1815
|
-
if (visited.has(pluginName)) return;
|
|
1816
|
-
if (visiting.has(pluginName)) {
|
|
1817
|
-
throw new Error(`[Kernel] Circular dependency detected: ${pluginName}`);
|
|
1818
|
-
}
|
|
1819
|
-
const plugin = this.plugins.get(pluginName);
|
|
1820
|
-
if (!plugin) {
|
|
1821
|
-
throw new Error(`[Kernel] Plugin '${pluginName}' not found`);
|
|
1822
|
-
}
|
|
1823
|
-
visiting.add(pluginName);
|
|
1824
|
-
const deps = plugin.dependencies || [];
|
|
1825
|
-
for (const dep of deps) {
|
|
1826
|
-
if (!this.plugins.has(dep)) {
|
|
1827
|
-
throw new Error(`[Kernel] Dependency '${dep}' not found for plugin '${pluginName}'`);
|
|
1828
|
-
}
|
|
1829
|
-
visit(dep);
|
|
1830
|
-
}
|
|
1831
|
-
visiting.delete(pluginName);
|
|
1832
|
-
visited.add(pluginName);
|
|
1833
|
-
resolved.push(plugin);
|
|
1834
|
-
};
|
|
1835
|
-
for (const pluginName of this.plugins.keys()) {
|
|
1836
|
-
visit(pluginName);
|
|
1837
|
-
}
|
|
1838
|
-
return resolved;
|
|
1960
|
+
return resolvePluginOrder(this.plugins);
|
|
1839
1961
|
}
|
|
1840
1962
|
registerShutdownSignals() {
|
|
1841
1963
|
const signals = ["SIGINT", "SIGTERM", "SIGQUIT"];
|
|
@@ -1901,6 +2023,7 @@ var LiteKernel = class extends ObjectKernelBase {
|
|
|
1901
2023
|
this.state = "initializing";
|
|
1902
2024
|
this.logger.info("Bootstrap started");
|
|
1903
2025
|
const orderedPlugins = this.resolveDependencies();
|
|
2026
|
+
this.validateInitServices(orderedPlugins);
|
|
1904
2027
|
this.logger.info("Phase 1: Init plugins");
|
|
1905
2028
|
for (const plugin of orderedPlugins) {
|
|
1906
2029
|
await this.runPluginInit(plugin);
|
|
@@ -2501,6 +2624,11 @@ function createApiRegistryPlugin(config = {}) {
|
|
|
2501
2624
|
} = config;
|
|
2502
2625
|
return {
|
|
2503
2626
|
name: "com.objectstack.core.api-registry",
|
|
2627
|
+
/**
|
|
2628
|
+
* Services init() registers on every path (ADR-0116, #4131) — lets the
|
|
2629
|
+
* kernel name this plugin when a consumer requires one before it inits.
|
|
2630
|
+
*/
|
|
2631
|
+
providesServices: ["api-registry"],
|
|
2504
2632
|
type: "standard",
|
|
2505
2633
|
version: "1.0.0",
|
|
2506
2634
|
init: async (ctx) => {
|
|
@@ -4212,7 +4340,7 @@ import {
|
|
|
4212
4340
|
mapMembershipRole,
|
|
4213
4341
|
BUILTIN_IDENTITY_PLATFORM_ADMIN,
|
|
4214
4342
|
ADMIN_FULL_ACCESS,
|
|
4215
|
-
|
|
4343
|
+
ORGANIZATION_ADMIN_GRANTS
|
|
4216
4344
|
} from "@objectstack/spec";
|
|
4217
4345
|
|
|
4218
4346
|
// src/security/grant-validity.ts
|
|
@@ -4320,7 +4448,8 @@ async function resolveAuthzContext(input) {
|
|
|
4320
4448
|
positions: [],
|
|
4321
4449
|
permissions: [],
|
|
4322
4450
|
systemPermissions: [],
|
|
4323
|
-
org_user_ids: []
|
|
4451
|
+
org_user_ids: [],
|
|
4452
|
+
accessible_org_ids: []
|
|
4324
4453
|
};
|
|
4325
4454
|
let userId;
|
|
4326
4455
|
let tenantId;
|
|
@@ -4356,6 +4485,7 @@ async function resolveAuthzContext(input) {
|
|
|
4356
4485
|
ctx.permissions = grants.permissions;
|
|
4357
4486
|
ctx.systemPermissions = grants.systemPermissions;
|
|
4358
4487
|
ctx.org_user_ids = grants.org_user_ids;
|
|
4488
|
+
ctx.accessible_org_ids = grants.accessible_org_ids;
|
|
4359
4489
|
if (grants.tabPermissions) ctx.tabPermissions = grants.tabPermissions;
|
|
4360
4490
|
if (grants.posture) ctx.posture = grants.posture;
|
|
4361
4491
|
if (grants.email && !ctx.email) ctx.email = grants.email;
|
|
@@ -4367,7 +4497,8 @@ async function resolveUserAuthzGrants(ql, userId, opts = {}) {
|
|
|
4367
4497
|
positions: [],
|
|
4368
4498
|
permissions: Array.isArray(opts.seedPermissions) ? [...opts.seedPermissions] : [],
|
|
4369
4499
|
systemPermissions: [],
|
|
4370
|
-
org_user_ids: [userId]
|
|
4500
|
+
org_user_ids: [userId],
|
|
4501
|
+
accessible_org_ids: []
|
|
4371
4502
|
};
|
|
4372
4503
|
if (opts.seedEmail) grants.email = opts.seedEmail;
|
|
4373
4504
|
if (!ql || typeof ql.find !== "function") return grants;
|
|
@@ -4385,9 +4516,17 @@ async function resolveUserAuthzGrants(ql, userId, opts = {}) {
|
|
|
4385
4516
|
const u = await getUserRow();
|
|
4386
4517
|
if (u?.email) grants.email = String(u.email);
|
|
4387
4518
|
}
|
|
4388
|
-
const
|
|
4389
|
-
const members = await tryFind(ql, "sys_member",
|
|
4519
|
+
const nowMs = opts.nowMs ?? Date.now();
|
|
4520
|
+
const members = await tryFind(ql, "sys_member", { user_id: userId }, 200);
|
|
4521
|
+
const accessibleOrgIds = /* @__PURE__ */ new Set();
|
|
4390
4522
|
for (const m of members) {
|
|
4523
|
+
if (!isGrantActive(m, nowMs)) continue;
|
|
4524
|
+
const org = m.organization_id ?? m.organizationId;
|
|
4525
|
+
if (typeof org === "string" && org) accessibleOrgIds.add(org);
|
|
4526
|
+
}
|
|
4527
|
+
grants.accessible_org_ids = Array.from(accessibleOrgIds);
|
|
4528
|
+
const activeMembers = tenantId ? members.filter((m) => (m.organization_id ?? m.organizationId) === tenantId) : members;
|
|
4529
|
+
for (const m of activeMembers) {
|
|
4391
4530
|
if (m.role && typeof m.role === "string") {
|
|
4392
4531
|
for (const raw of m.role.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
4393
4532
|
const r = mapMembershipRole(raw);
|
|
@@ -4395,7 +4534,6 @@ async function resolveUserAuthzGrants(ql, userId, opts = {}) {
|
|
|
4395
4534
|
}
|
|
4396
4535
|
}
|
|
4397
4536
|
}
|
|
4398
|
-
const nowMs = opts.nowMs ?? Date.now();
|
|
4399
4537
|
const userPositionRows = await tryFind(ql, "sys_user_position", { user_id: userId }, 200);
|
|
4400
4538
|
for (const ur of userPositionRows) {
|
|
4401
4539
|
const org = ur.organization_id ?? null;
|
|
@@ -4467,7 +4605,9 @@ async function resolveUserAuthzGrants(ql, userId, opts = {}) {
|
|
|
4467
4605
|
}
|
|
4468
4606
|
grants.posture = derivePosture({
|
|
4469
4607
|
isPlatformAdmin: hasPlatformAdminGrant,
|
|
4470
|
-
|
|
4608
|
+
// [ADR-0105 D4] Either org-admin capability set resolves the rung — the
|
|
4609
|
+
// wall-less variant differs only by withholding the superuser bits.
|
|
4610
|
+
isTenantAdmin: ORGANIZATION_ADMIN_GRANTS.some((n) => grants.permissions.includes(n))
|
|
4471
4611
|
});
|
|
4472
4612
|
if (!grants.permissions.includes("ai_seat")) {
|
|
4473
4613
|
const aiAccess = (await getUserRow())?.ai_access;
|
|
@@ -4556,14 +4696,13 @@ function evaluateAuthGate(sessionUser, path) {
|
|
|
4556
4696
|
|
|
4557
4697
|
// src/security/anonymous-deny.ts
|
|
4558
4698
|
var ANONYMOUS_DENY_STATUS = 401;
|
|
4559
|
-
var ANONYMOUS_DENY_CODE = "
|
|
4699
|
+
var ANONYMOUS_DENY_CODE = "UNAUTHENTICATED";
|
|
4560
4700
|
var ANONYMOUS_DENY_MESSAGE = "Authentication is required to access this endpoint.";
|
|
4561
4701
|
var ANONYMOUS_DENY_BODY = {
|
|
4562
4702
|
error: ANONYMOUS_DENY_CODE,
|
|
4563
4703
|
message: ANONYMOUS_DENY_MESSAGE
|
|
4564
4704
|
};
|
|
4565
4705
|
function shouldDenyAnonymous(input) {
|
|
4566
|
-
if (!input.requireAuth) return false;
|
|
4567
4706
|
if (typeof input.method === "string" && input.method.toUpperCase() === "OPTIONS") {
|
|
4568
4707
|
return false;
|
|
4569
4708
|
}
|
|
@@ -4575,6 +4714,7 @@ function shouldDenyAnonymous(input) {
|
|
|
4575
4714
|
}
|
|
4576
4715
|
|
|
4577
4716
|
// src/utils/datetime.ts
|
|
4717
|
+
import { nextUtcCalendarDay, utcInstantMs } from "@objectstack/spec/data";
|
|
4578
4718
|
function calendarPartsInTz(d, tz) {
|
|
4579
4719
|
const parts = new Intl.DateTimeFormat("en-US", {
|
|
4580
4720
|
timeZone: tz,
|
|
@@ -4598,8 +4738,8 @@ function calendarPartsInTzOrUtc(d, tz) {
|
|
|
4598
4738
|
day: d.getUTCDate()
|
|
4599
4739
|
};
|
|
4600
4740
|
}
|
|
4601
|
-
function zonedDateStartToUtcMs(
|
|
4602
|
-
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(
|
|
4741
|
+
function zonedDateStartToUtcMs(ymd2, tz) {
|
|
4742
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(ymd2);
|
|
4603
4743
|
const wallAsUtc = m ? Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3])) : NaN;
|
|
4604
4744
|
if (!tz || tz === "UTC" || Number.isNaN(wallAsUtc)) return wallAsUtc;
|
|
4605
4745
|
try {
|
|
@@ -4815,6 +4955,212 @@ async function bulkWrite(rows, opts) {
|
|
|
4815
4955
|
return results;
|
|
4816
4956
|
}
|
|
4817
4957
|
|
|
4958
|
+
// src/utils/filter-tokens.ts
|
|
4959
|
+
import {
|
|
4960
|
+
classifyFilterToken,
|
|
4961
|
+
parseDateMacroParam
|
|
4962
|
+
} from "@objectstack/spec/data";
|
|
4963
|
+
var UnknownFilterTokenError = class extends Error {
|
|
4964
|
+
constructor(token, suggestion) {
|
|
4965
|
+
super(
|
|
4966
|
+
`Unresolvable filter placeholder "{${token}}". ` + (suggestion ? `Did you mean "{${suggestion}}"? ` : "Resolvable placeholders are the context tokens ({current_user_id}, {current_org_id}) and the date macros ({today}, {current_quarter_start}, {30_days_ago}, \u2026). ") + "Sending it to the data engine verbatim would compare it as a literal string and match nothing, which is indistinguishable from an empty result."
|
|
4967
|
+
);
|
|
4968
|
+
this.status = 400;
|
|
4969
|
+
this.code = "FILTER_TOKEN_UNKNOWN";
|
|
4970
|
+
this.name = "UnknownFilterTokenError";
|
|
4971
|
+
this.token = token;
|
|
4972
|
+
this.suggestion = suggestion;
|
|
4973
|
+
}
|
|
4974
|
+
};
|
|
4975
|
+
var UnresolvedFilterTokenError = class extends Error {
|
|
4976
|
+
constructor(token, detail) {
|
|
4977
|
+
super(`Filter placeholder "{${token}}" cannot be resolved: ${detail}`);
|
|
4978
|
+
/** 400, not 500 — see {@link UnknownFilterTokenError}. */
|
|
4979
|
+
this.status = 400;
|
|
4980
|
+
this.code = "FILTER_TOKEN_UNRESOLVED";
|
|
4981
|
+
this.name = "UnresolvedFilterTokenError";
|
|
4982
|
+
this.token = token;
|
|
4983
|
+
}
|
|
4984
|
+
};
|
|
4985
|
+
function ymd(year, month, day) {
|
|
4986
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
4987
|
+
return `${year}-${p(month)}-${p(day)}`;
|
|
4988
|
+
}
|
|
4989
|
+
function proxyDay(now, timezone) {
|
|
4990
|
+
const { year, month, day } = calendarPartsInTzOrUtc(now, timezone);
|
|
4991
|
+
return new Date(Date.UTC(year, month - 1, day));
|
|
4992
|
+
}
|
|
4993
|
+
var asYmd = (d) => ymd(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
|
|
4994
|
+
function startOfPeriod(kind, d) {
|
|
4995
|
+
const r = new Date(d.getTime());
|
|
4996
|
+
switch (kind) {
|
|
4997
|
+
case "week": {
|
|
4998
|
+
const dow = (r.getUTCDay() + 6) % 7;
|
|
4999
|
+
r.setUTCDate(r.getUTCDate() - dow);
|
|
5000
|
+
return r;
|
|
5001
|
+
}
|
|
5002
|
+
case "month":
|
|
5003
|
+
return new Date(Date.UTC(r.getUTCFullYear(), r.getUTCMonth(), 1));
|
|
5004
|
+
case "quarter":
|
|
5005
|
+
return new Date(Date.UTC(r.getUTCFullYear(), Math.floor(r.getUTCMonth() / 3) * 3, 1));
|
|
5006
|
+
case "year":
|
|
5007
|
+
return new Date(Date.UTC(r.getUTCFullYear(), 0, 1));
|
|
5008
|
+
}
|
|
5009
|
+
}
|
|
5010
|
+
function daysInMonth(year, month) {
|
|
5011
|
+
return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
|
|
5012
|
+
}
|
|
5013
|
+
function addMonthsClamped(d, n) {
|
|
5014
|
+
const year = d.getUTCFullYear();
|
|
5015
|
+
const month = d.getUTCMonth() + n;
|
|
5016
|
+
const targetYear = year + Math.floor(month / 12);
|
|
5017
|
+
const targetMonth = (month % 12 + 12) % 12;
|
|
5018
|
+
const day = Math.min(d.getUTCDate(), daysInMonth(targetYear, targetMonth));
|
|
5019
|
+
return new Date(Date.UTC(
|
|
5020
|
+
targetYear,
|
|
5021
|
+
targetMonth,
|
|
5022
|
+
day,
|
|
5023
|
+
d.getUTCHours(),
|
|
5024
|
+
d.getUTCMinutes(),
|
|
5025
|
+
d.getUTCSeconds(),
|
|
5026
|
+
d.getUTCMilliseconds()
|
|
5027
|
+
));
|
|
5028
|
+
}
|
|
5029
|
+
function addPeriods(kind, d, n) {
|
|
5030
|
+
switch (kind) {
|
|
5031
|
+
case "week": {
|
|
5032
|
+
const r = new Date(d.getTime());
|
|
5033
|
+
r.setUTCDate(r.getUTCDate() + n * 7);
|
|
5034
|
+
return r;
|
|
5035
|
+
}
|
|
5036
|
+
case "month":
|
|
5037
|
+
return addMonthsClamped(d, n);
|
|
5038
|
+
case "quarter":
|
|
5039
|
+
return addMonthsClamped(d, n * 3);
|
|
5040
|
+
case "year":
|
|
5041
|
+
return addMonthsClamped(d, n * 12);
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
function addUnits(unit, d, n) {
|
|
5045
|
+
const r = new Date(d.getTime());
|
|
5046
|
+
switch (unit) {
|
|
5047
|
+
case "minute":
|
|
5048
|
+
r.setUTCMinutes(r.getUTCMinutes() + n);
|
|
5049
|
+
return r;
|
|
5050
|
+
case "hour":
|
|
5051
|
+
r.setUTCHours(r.getUTCHours() + n);
|
|
5052
|
+
return r;
|
|
5053
|
+
case "day":
|
|
5054
|
+
r.setUTCDate(r.getUTCDate() + n);
|
|
5055
|
+
return r;
|
|
5056
|
+
case "week":
|
|
5057
|
+
r.setUTCDate(r.getUTCDate() + n * 7);
|
|
5058
|
+
return r;
|
|
5059
|
+
// Month/year steps clamp rather than overflow — see addMonthsClamped.
|
|
5060
|
+
case "month":
|
|
5061
|
+
return addMonthsClamped(d, n);
|
|
5062
|
+
case "year":
|
|
5063
|
+
return addMonthsClamped(d, n * 12);
|
|
5064
|
+
}
|
|
5065
|
+
}
|
|
5066
|
+
var PERIOD_RE = /^(?:(current|last|next)_)?(week|month|quarter|year)_(start|end)$/;
|
|
5067
|
+
function resolvePeriodToken(token, today) {
|
|
5068
|
+
const m = PERIOD_RE.exec(token);
|
|
5069
|
+
if (!m) return void 0;
|
|
5070
|
+
const rel = m[1] ?? "current";
|
|
5071
|
+
const kind = m[2];
|
|
5072
|
+
const bound = m[3];
|
|
5073
|
+
const offset = rel === "last" ? -1 : rel === "next" ? 1 : 0;
|
|
5074
|
+
const periodStart = startOfPeriod(kind, addPeriods(kind, startOfPeriod(kind, today), offset));
|
|
5075
|
+
if (bound === "start") return asYmd(periodStart);
|
|
5076
|
+
const next = addPeriods(kind, periodStart, 1);
|
|
5077
|
+
next.setUTCDate(next.getUTCDate() - 1);
|
|
5078
|
+
return asYmd(next);
|
|
5079
|
+
}
|
|
5080
|
+
function resolveFilterToken(token, ctx = {}) {
|
|
5081
|
+
const now = ctx.now ?? /* @__PURE__ */ new Date();
|
|
5082
|
+
if (token === "current_user_id") {
|
|
5083
|
+
if (!ctx.userId) {
|
|
5084
|
+
throw new UnresolvedFilterTokenError(
|
|
5085
|
+
token,
|
|
5086
|
+
"the request has no authenticated user. A filter scoped to the signed-in user cannot run for an anonymous or system caller \u2014 gate the surface on authentication, or drop the token from the filter."
|
|
5087
|
+
);
|
|
5088
|
+
}
|
|
5089
|
+
return ctx.userId;
|
|
5090
|
+
}
|
|
5091
|
+
if (token === "current_org_id") {
|
|
5092
|
+
if (!ctx.orgId) {
|
|
5093
|
+
throw new UnresolvedFilterTokenError(
|
|
5094
|
+
token,
|
|
5095
|
+
"the request carries no active organization (ExecutionContext.tenantId is unset). Set the active org on the request, or drop the token from the filter."
|
|
5096
|
+
);
|
|
5097
|
+
}
|
|
5098
|
+
return ctx.orgId;
|
|
5099
|
+
}
|
|
5100
|
+
const today = proxyDay(now, ctx.timezone);
|
|
5101
|
+
switch (token) {
|
|
5102
|
+
case "now":
|
|
5103
|
+
return now.toISOString();
|
|
5104
|
+
case "today":
|
|
5105
|
+
return asYmd(today);
|
|
5106
|
+
case "yesterday":
|
|
5107
|
+
return asYmd(addUnits("day", today, -1));
|
|
5108
|
+
case "tomorrow":
|
|
5109
|
+
return asYmd(addUnits("day", today, 1));
|
|
5110
|
+
}
|
|
5111
|
+
const period = resolvePeriodToken(token, today);
|
|
5112
|
+
if (period !== void 0) return period;
|
|
5113
|
+
const param = parseDateMacroParam(token);
|
|
5114
|
+
if (param) {
|
|
5115
|
+
const sign = param.direction === "ago" ? -1 : 1;
|
|
5116
|
+
if (param.unit === "minute" || param.unit === "hour") {
|
|
5117
|
+
return addUnits(param.unit, now, sign * param.n).toISOString();
|
|
5118
|
+
}
|
|
5119
|
+
return asYmd(addUnits(param.unit, today, sign * param.n));
|
|
5120
|
+
}
|
|
5121
|
+
return void 0;
|
|
5122
|
+
}
|
|
5123
|
+
function hasFilterToken(node) {
|
|
5124
|
+
if (typeof node === "string") return classifyFilterToken(node) !== null;
|
|
5125
|
+
if (Array.isArray(node)) return node.some(hasFilterToken);
|
|
5126
|
+
if (node && typeof node === "object" && !(node instanceof Date)) {
|
|
5127
|
+
return Object.values(node).some(hasFilterToken);
|
|
5128
|
+
}
|
|
5129
|
+
return false;
|
|
5130
|
+
}
|
|
5131
|
+
function resolveFilterTokens(filter, ctx = {}) {
|
|
5132
|
+
if (filter == null) return filter;
|
|
5133
|
+
if (!hasFilterToken(filter)) return filter;
|
|
5134
|
+
const pinned = { ...ctx, now: ctx.now ?? /* @__PURE__ */ new Date() };
|
|
5135
|
+
const walk = (node) => {
|
|
5136
|
+
if (typeof node === "string") {
|
|
5137
|
+
const cls = classifyFilterToken(node);
|
|
5138
|
+
if (!cls) return node;
|
|
5139
|
+
if (cls.kind === "unknown") throw new UnknownFilterTokenError(cls.token, cls.suggestion);
|
|
5140
|
+
const resolved = resolveFilterToken(cls.token, pinned);
|
|
5141
|
+
if (resolved === void 0) throw new UnknownFilterTokenError(cls.token);
|
|
5142
|
+
return resolved;
|
|
5143
|
+
}
|
|
5144
|
+
if (Array.isArray(node)) return node.map(walk);
|
|
5145
|
+
if (node && typeof node === "object") {
|
|
5146
|
+
if (node instanceof Date) return node;
|
|
5147
|
+
const out = {};
|
|
5148
|
+
for (const [k, v] of Object.entries(node)) out[k] = walk(v);
|
|
5149
|
+
return out;
|
|
5150
|
+
}
|
|
5151
|
+
return node;
|
|
5152
|
+
};
|
|
5153
|
+
return walk(filter);
|
|
5154
|
+
}
|
|
5155
|
+
function filterTokenContextFrom(execCtx, now) {
|
|
5156
|
+
return {
|
|
5157
|
+
now,
|
|
5158
|
+
timezone: execCtx?.timezone,
|
|
5159
|
+
userId: execCtx?.userId,
|
|
5160
|
+
orgId: execCtx?.tenantId
|
|
5161
|
+
};
|
|
5162
|
+
}
|
|
5163
|
+
|
|
4818
5164
|
// src/health-monitor.ts
|
|
4819
5165
|
var PluginHealthMonitor = class {
|
|
4820
5166
|
constructor(logger) {
|
|
@@ -5759,6 +6105,9 @@ export {
|
|
|
5759
6105
|
SecurePluginContext,
|
|
5760
6106
|
SemanticVersionManager,
|
|
5761
6107
|
ServiceLifecycle,
|
|
6108
|
+
UnknownFilterTokenError,
|
|
6109
|
+
UnresolvedFilterTokenError,
|
|
6110
|
+
assertInitServiceRequirements,
|
|
5762
6111
|
bucketKeyToCalendarRange,
|
|
5763
6112
|
buildPermissionsFromGrants,
|
|
5764
6113
|
bulkWrite,
|
|
@@ -5777,8 +6126,10 @@ export {
|
|
|
5777
6126
|
deepMerge,
|
|
5778
6127
|
defaultIsTransientError,
|
|
5779
6128
|
derivePosture,
|
|
6129
|
+
describeInitOrderFault,
|
|
5780
6130
|
evaluateAuthGate,
|
|
5781
6131
|
extractApiKey,
|
|
6132
|
+
filterTokenContextFrom,
|
|
5782
6133
|
generateApiKey,
|
|
5783
6134
|
generateEd25519KeyPair,
|
|
5784
6135
|
getEnv,
|
|
@@ -5789,18 +6140,24 @@ export {
|
|
|
5789
6140
|
isGrantActive,
|
|
5790
6141
|
isGrantExpired,
|
|
5791
6142
|
isNode,
|
|
6143
|
+
nextUtcCalendarDay,
|
|
5792
6144
|
parseScopes,
|
|
5793
6145
|
parseSignature,
|
|
5794
6146
|
postureVisibleRows,
|
|
5795
6147
|
readAuthoredTranslationLayer,
|
|
5796
6148
|
resolveApiKeyPrincipal,
|
|
5797
6149
|
resolveAuthzContext,
|
|
6150
|
+
resolveFilterToken,
|
|
6151
|
+
resolveFilterTokens,
|
|
5798
6152
|
resolveLocale,
|
|
5799
6153
|
resolveLocalizationContext,
|
|
6154
|
+
resolvePluginOrder,
|
|
5800
6155
|
resolveUserAuthzGrants,
|
|
5801
6156
|
safeExit,
|
|
5802
6157
|
shouldDenyAnonymous,
|
|
5803
6158
|
signPayload,
|
|
6159
|
+
utcInstantMs,
|
|
6160
|
+
validateInitServiceContract,
|
|
5804
6161
|
verifyPayload,
|
|
5805
6162
|
verifyPlatformSignature,
|
|
5806
6163
|
verifyPluginArtifact,
|