@objectstack/core 16.0.0 → 17.0.0-rc.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 +301 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +154 -4
- package/dist/index.d.ts +154 -4
- package/dist/index.js +296 -30
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -60,6 +60,8 @@ __export(index_exports, {
|
|
|
60
60
|
SecurePluginContext: () => SecurePluginContext,
|
|
61
61
|
SemanticVersionManager: () => SemanticVersionManager,
|
|
62
62
|
ServiceLifecycle: () => ServiceLifecycle,
|
|
63
|
+
UnknownFilterTokenError: () => UnknownFilterTokenError,
|
|
64
|
+
UnresolvedFilterTokenError: () => UnresolvedFilterTokenError,
|
|
63
65
|
bucketKeyToCalendarRange: () => bucketKeyToCalendarRange,
|
|
64
66
|
buildPermissionsFromGrants: () => buildPermissionsFromGrants,
|
|
65
67
|
bulkWrite: () => bulkWrite,
|
|
@@ -80,6 +82,7 @@ __export(index_exports, {
|
|
|
80
82
|
derivePosture: () => derivePosture,
|
|
81
83
|
evaluateAuthGate: () => evaluateAuthGate,
|
|
82
84
|
extractApiKey: () => extractApiKey,
|
|
85
|
+
filterTokenContextFrom: () => filterTokenContextFrom,
|
|
83
86
|
generateApiKey: () => generateApiKey,
|
|
84
87
|
generateEd25519KeyPair: () => generateEd25519KeyPair,
|
|
85
88
|
getEnv: () => getEnv,
|
|
@@ -96,8 +99,11 @@ __export(index_exports, {
|
|
|
96
99
|
readAuthoredTranslationLayer: () => readAuthoredTranslationLayer,
|
|
97
100
|
resolveApiKeyPrincipal: () => resolveApiKeyPrincipal,
|
|
98
101
|
resolveAuthzContext: () => resolveAuthzContext,
|
|
102
|
+
resolveFilterToken: () => resolveFilterToken,
|
|
103
|
+
resolveFilterTokens: () => resolveFilterTokens,
|
|
99
104
|
resolveLocale: () => resolveLocale,
|
|
100
105
|
resolveLocalizationContext: () => resolveLocalizationContext,
|
|
106
|
+
resolveUserAuthzGrants: () => resolveUserAuthzGrants,
|
|
101
107
|
safeExit: () => safeExit,
|
|
102
108
|
shouldDenyAnonymous: () => shouldDenyAnonymous,
|
|
103
109
|
signPayload: () => signPayload,
|
|
@@ -534,7 +540,7 @@ function createLogger(config) {
|
|
|
534
540
|
}
|
|
535
541
|
|
|
536
542
|
// src/kernel.ts
|
|
537
|
-
var
|
|
543
|
+
var import_system2 = require("@objectstack/spec/system");
|
|
538
544
|
|
|
539
545
|
// src/security/plugin-config-validator.ts
|
|
540
546
|
var import_zod = require("zod");
|
|
@@ -1347,6 +1353,7 @@ function createMemoryMetadata() {
|
|
|
1347
1353
|
}
|
|
1348
1354
|
|
|
1349
1355
|
// src/fallbacks/authored-translation-sync.ts
|
|
1356
|
+
var import_system = require("@objectstack/spec/system");
|
|
1350
1357
|
var OWNER_PROP = "__authoredTranslationSyncOwner";
|
|
1351
1358
|
var LOCALE_LIKE = /^[a-z]{2,3}([_-]([A-Za-z]{4}|[A-Za-z]{2}|[0-9]{3}))?$/;
|
|
1352
1359
|
async function readAuthoredTranslationLayer(engine, logger) {
|
|
@@ -1376,14 +1383,32 @@ async function readAuthoredTranslationLayer(engine, logger) {
|
|
|
1376
1383
|
continue;
|
|
1377
1384
|
}
|
|
1378
1385
|
if (!data || typeof data !== "object") continue;
|
|
1379
|
-
const
|
|
1386
|
+
const legacyKeys = import_system.LEGACY_OBJECT_FIRST_KEYS.filter((key) => data[key] !== void 0);
|
|
1387
|
+
if (legacyKeys.length > 0) {
|
|
1388
|
+
logger?.warn?.(
|
|
1389
|
+
`[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`
|
|
1390
|
+
);
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
const locale = typeof data?.locale === "string" && data.locale || (typeof row?.name === "string" && LOCALE_LIKE.test(row.name) ? row.name : void 0) || void 0;
|
|
1380
1394
|
if (!locale) {
|
|
1381
1395
|
logger?.warn?.(
|
|
1382
|
-
`[i18n] authored translation '${row?.name}' has no resolvable locale (set
|
|
1396
|
+
`[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`
|
|
1383
1397
|
);
|
|
1384
1398
|
continue;
|
|
1385
1399
|
}
|
|
1386
|
-
const {
|
|
1400
|
+
const {
|
|
1401
|
+
name: _n,
|
|
1402
|
+
locale: _l,
|
|
1403
|
+
_packageId: _p,
|
|
1404
|
+
_packageVersion: _pv,
|
|
1405
|
+
_provenance: _pr,
|
|
1406
|
+
_lock: _lk,
|
|
1407
|
+
_lockReason: _lr,
|
|
1408
|
+
_lockDocsUrl: _ld,
|
|
1409
|
+
_lockSource: _ls,
|
|
1410
|
+
...payload
|
|
1411
|
+
} = data;
|
|
1387
1412
|
byLocale[locale] = deepMerge(byLocale[locale] ?? {}, payload);
|
|
1388
1413
|
}
|
|
1389
1414
|
return byLocale;
|
|
@@ -1607,7 +1632,7 @@ var ObjectKernel = class {
|
|
|
1607
1632
|
*/
|
|
1608
1633
|
preInjectCoreFallbacks() {
|
|
1609
1634
|
if (this.config.skipSystemValidation) return;
|
|
1610
|
-
for (const [serviceName, criticality] of Object.entries(
|
|
1635
|
+
for (const [serviceName, criticality] of Object.entries(import_system2.ServiceRequirementDef)) {
|
|
1611
1636
|
if (criticality !== "core") continue;
|
|
1612
1637
|
const hasService = this.services.has(serviceName) || this.pluginLoader.hasService(serviceName);
|
|
1613
1638
|
if (!hasService) {
|
|
@@ -1631,7 +1656,7 @@ var ObjectKernel = class {
|
|
|
1631
1656
|
this.logger.debug("Validating system service requirements...");
|
|
1632
1657
|
const missingServices = [];
|
|
1633
1658
|
const missingCoreServices = [];
|
|
1634
|
-
for (const [serviceName, criticality] of Object.entries(
|
|
1659
|
+
for (const [serviceName, criticality] of Object.entries(import_system2.ServiceRequirementDef)) {
|
|
1635
1660
|
const hasService = this.services.has(serviceName) || this.pluginLoader.hasService(serviceName);
|
|
1636
1661
|
if (!hasService) {
|
|
1637
1662
|
if (criticality === "required") {
|
|
@@ -4410,7 +4435,8 @@ async function resolveAuthzContext(input) {
|
|
|
4410
4435
|
positions: [],
|
|
4411
4436
|
permissions: [],
|
|
4412
4437
|
systemPermissions: [],
|
|
4413
|
-
org_user_ids: []
|
|
4438
|
+
org_user_ids: [],
|
|
4439
|
+
accessible_org_ids: []
|
|
4414
4440
|
};
|
|
4415
4441
|
let userId;
|
|
4416
4442
|
let tenantId;
|
|
@@ -4436,6 +4462,33 @@ async function resolveAuthzContext(input) {
|
|
|
4436
4462
|
ctx.userId = userId;
|
|
4437
4463
|
if (tenantId) ctx.tenantId = tenantId;
|
|
4438
4464
|
if (!ql || typeof ql.find !== "function") return ctx;
|
|
4465
|
+
const grants = await resolveUserAuthzGrants(ql, userId, {
|
|
4466
|
+
tenantId,
|
|
4467
|
+
nowMs: input.nowMs,
|
|
4468
|
+
seedPermissions: ctx.permissions,
|
|
4469
|
+
seedEmail: ctx.email
|
|
4470
|
+
});
|
|
4471
|
+
ctx.positions = grants.positions;
|
|
4472
|
+
ctx.permissions = grants.permissions;
|
|
4473
|
+
ctx.systemPermissions = grants.systemPermissions;
|
|
4474
|
+
ctx.org_user_ids = grants.org_user_ids;
|
|
4475
|
+
ctx.accessible_org_ids = grants.accessible_org_ids;
|
|
4476
|
+
if (grants.tabPermissions) ctx.tabPermissions = grants.tabPermissions;
|
|
4477
|
+
if (grants.posture) ctx.posture = grants.posture;
|
|
4478
|
+
if (grants.email && !ctx.email) ctx.email = grants.email;
|
|
4479
|
+
return ctx;
|
|
4480
|
+
}
|
|
4481
|
+
async function resolveUserAuthzGrants(ql, userId, opts = {}) {
|
|
4482
|
+
const { tenantId } = opts;
|
|
4483
|
+
const grants = {
|
|
4484
|
+
positions: [],
|
|
4485
|
+
permissions: Array.isArray(opts.seedPermissions) ? [...opts.seedPermissions] : [],
|
|
4486
|
+
systemPermissions: [],
|
|
4487
|
+
org_user_ids: [userId],
|
|
4488
|
+
accessible_org_ids: []
|
|
4489
|
+
};
|
|
4490
|
+
if (opts.seedEmail) grants.email = opts.seedEmail;
|
|
4491
|
+
if (!ql || typeof ql.find !== "function") return grants;
|
|
4439
4492
|
let userRowLoaded = false;
|
|
4440
4493
|
let userRow;
|
|
4441
4494
|
const getUserRow = async () => {
|
|
@@ -4446,28 +4499,35 @@ async function resolveAuthzContext(input) {
|
|
|
4446
4499
|
}
|
|
4447
4500
|
return userRow;
|
|
4448
4501
|
};
|
|
4449
|
-
if (!
|
|
4502
|
+
if (!grants.email) {
|
|
4450
4503
|
const u = await getUserRow();
|
|
4451
|
-
if (u?.email)
|
|
4504
|
+
if (u?.email) grants.email = String(u.email);
|
|
4452
4505
|
}
|
|
4453
|
-
const
|
|
4454
|
-
const members = await tryFind(ql, "sys_member",
|
|
4506
|
+
const nowMs = opts.nowMs ?? Date.now();
|
|
4507
|
+
const members = await tryFind(ql, "sys_member", { user_id: userId }, 200);
|
|
4508
|
+
const accessibleOrgIds = /* @__PURE__ */ new Set();
|
|
4455
4509
|
for (const m of members) {
|
|
4510
|
+
if (!isGrantActive(m, nowMs)) continue;
|
|
4511
|
+
const org = m.organization_id ?? m.organizationId;
|
|
4512
|
+
if (typeof org === "string" && org) accessibleOrgIds.add(org);
|
|
4513
|
+
}
|
|
4514
|
+
grants.accessible_org_ids = Array.from(accessibleOrgIds);
|
|
4515
|
+
const activeMembers = tenantId ? members.filter((m) => (m.organization_id ?? m.organizationId) === tenantId) : members;
|
|
4516
|
+
for (const m of activeMembers) {
|
|
4456
4517
|
if (m.role && typeof m.role === "string") {
|
|
4457
4518
|
for (const raw of m.role.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
4458
4519
|
const r = (0, import_spec.mapMembershipRole)(raw);
|
|
4459
|
-
if (!
|
|
4520
|
+
if (!grants.positions.includes(r)) grants.positions.push(r);
|
|
4460
4521
|
}
|
|
4461
4522
|
}
|
|
4462
4523
|
}
|
|
4463
|
-
const nowMs = input.nowMs ?? Date.now();
|
|
4464
4524
|
const userPositionRows = await tryFind(ql, "sys_user_position", { user_id: userId }, 200);
|
|
4465
4525
|
for (const ur of userPositionRows) {
|
|
4466
4526
|
const org = ur.organization_id ?? null;
|
|
4467
4527
|
if (org && tenantId && org !== tenantId) continue;
|
|
4468
4528
|
if (!isGrantActive(ur, nowMs)) continue;
|
|
4469
4529
|
const r = ur.position;
|
|
4470
|
-
if (typeof r === "string" && r && !
|
|
4530
|
+
if (typeof r === "string" && r && !grants.positions.includes(r)) grants.positions.push(r);
|
|
4471
4531
|
}
|
|
4472
4532
|
if (tenantId) {
|
|
4473
4533
|
const orgMembers = await tryFind(ql, "sys_member", { organization_id: tenantId }, 1e3);
|
|
@@ -4475,9 +4535,7 @@ async function resolveAuthzContext(input) {
|
|
|
4475
4535
|
orgMembers.map((m) => m.user_id ?? m.userId).filter((v) => typeof v === "string" && v.length > 0)
|
|
4476
4536
|
);
|
|
4477
4537
|
ids.add(userId);
|
|
4478
|
-
|
|
4479
|
-
} else {
|
|
4480
|
-
ctx.org_user_ids = [userId];
|
|
4538
|
+
grants.org_user_ids = Array.from(ids);
|
|
4481
4539
|
}
|
|
4482
4540
|
const upsRowsAll = await tryFind(ql, "sys_user_permission_set", { user_id: userId }, 100);
|
|
4483
4541
|
const upsRows = upsRowsAll.filter((r) => isGrantActive(r, nowMs));
|
|
@@ -4491,9 +4549,9 @@ async function resolveAuthzContext(input) {
|
|
|
4491
4549
|
upsRows.filter((r) => (r.organization_id ?? r.organizationId ?? null) === null).map((r) => r.permission_set_id ?? r.permissionSetId).filter(Boolean)
|
|
4492
4550
|
);
|
|
4493
4551
|
let hasPlatformAdminGrant = false;
|
|
4494
|
-
if (!
|
|
4495
|
-
if (
|
|
4496
|
-
const positionRows = await tryFind(ql, "sys_position", { name: { $in:
|
|
4552
|
+
if (!grants.positions.includes("everyone")) grants.positions.push("everyone");
|
|
4553
|
+
if (grants.positions.length > 0) {
|
|
4554
|
+
const positionRows = await tryFind(ql, "sys_position", { name: { $in: grants.positions } }, 100);
|
|
4497
4555
|
const positionIds = positionRows.map((r) => r.id).filter(Boolean);
|
|
4498
4556
|
if (positionIds.length > 0) {
|
|
4499
4557
|
const rpsRows = await tryFind(ql, "sys_position_permission_set", { position_id: { $in: positionIds } }, 500);
|
|
@@ -4508,12 +4566,12 @@ async function resolveAuthzContext(input) {
|
|
|
4508
4566
|
const tabRank = { hidden: 0, default_off: 1, default_on: 2, visible: 3 };
|
|
4509
4567
|
const mergedTabs = {};
|
|
4510
4568
|
for (const ps of psRows) {
|
|
4511
|
-
if (ps.name && !
|
|
4569
|
+
if (ps.name && !grants.permissions.includes(ps.name)) grants.permissions.push(ps.name);
|
|
4512
4570
|
if (ps.name === import_spec.ADMIN_FULL_ACCESS && unscopedUserPsIds.has(ps.id)) hasPlatformAdminGrant = true;
|
|
4513
4571
|
const sysPerms = typeof ps.system_permissions === "string" ? safeJsonParse2(ps.system_permissions, []) : ps.system_permissions ?? ps.systemPermissions;
|
|
4514
4572
|
if (Array.isArray(sysPerms)) {
|
|
4515
4573
|
for (const p of sysPerms) {
|
|
4516
|
-
if (typeof p === "string" && !
|
|
4574
|
+
if (typeof p === "string" && !grants.systemPermissions.includes(p)) grants.systemPermissions.push(p);
|
|
4517
4575
|
}
|
|
4518
4576
|
}
|
|
4519
4577
|
const tabs = typeof ps.tab_permissions === "string" ? safeJsonParse2(ps.tab_permissions, {}) : ps.tab_permissions ?? ps.tabPermissions;
|
|
@@ -4527,20 +4585,22 @@ async function resolveAuthzContext(input) {
|
|
|
4527
4585
|
}
|
|
4528
4586
|
}
|
|
4529
4587
|
}
|
|
4530
|
-
if (Object.keys(mergedTabs).length > 0)
|
|
4588
|
+
if (Object.keys(mergedTabs).length > 0) grants.tabPermissions = mergedTabs;
|
|
4531
4589
|
}
|
|
4532
|
-
if (hasPlatformAdminGrant && !
|
|
4533
|
-
|
|
4590
|
+
if (hasPlatformAdminGrant && !grants.positions.includes(import_spec.BUILTIN_IDENTITY_PLATFORM_ADMIN)) {
|
|
4591
|
+
grants.positions.unshift(import_spec.BUILTIN_IDENTITY_PLATFORM_ADMIN);
|
|
4534
4592
|
}
|
|
4535
|
-
|
|
4593
|
+
grants.posture = derivePosture({
|
|
4536
4594
|
isPlatformAdmin: hasPlatformAdminGrant,
|
|
4537
|
-
|
|
4595
|
+
// [ADR-0105 D4] Either org-admin capability set resolves the rung — the
|
|
4596
|
+
// wall-less variant differs only by withholding the superuser bits.
|
|
4597
|
+
isTenantAdmin: import_spec.ORGANIZATION_ADMIN_GRANTS.some((n) => grants.permissions.includes(n))
|
|
4538
4598
|
});
|
|
4539
|
-
if (!
|
|
4599
|
+
if (!grants.permissions.includes("ai_seat")) {
|
|
4540
4600
|
const aiAccess = (await getUserRow())?.ai_access;
|
|
4541
|
-
if (aiAccess === true || aiAccess === 1 || aiAccess === "1")
|
|
4601
|
+
if (aiAccess === true || aiAccess === 1 || aiAccess === "1") grants.permissions.push("ai_seat");
|
|
4542
4602
|
}
|
|
4543
|
-
return
|
|
4603
|
+
return grants;
|
|
4544
4604
|
}
|
|
4545
4605
|
function isValidTimeZone(tz) {
|
|
4546
4606
|
try {
|
|
@@ -4665,8 +4725,8 @@ function calendarPartsInTzOrUtc(d, tz) {
|
|
|
4665
4725
|
day: d.getUTCDate()
|
|
4666
4726
|
};
|
|
4667
4727
|
}
|
|
4668
|
-
function zonedDateStartToUtcMs(
|
|
4669
|
-
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(
|
|
4728
|
+
function zonedDateStartToUtcMs(ymd2, tz) {
|
|
4729
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(ymd2);
|
|
4670
4730
|
const wallAsUtc = m ? Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3])) : NaN;
|
|
4671
4731
|
if (!tz || tz === "UTC" || Number.isNaN(wallAsUtc)) return wallAsUtc;
|
|
4672
4732
|
try {
|
|
@@ -4882,6 +4942,209 @@ async function bulkWrite(rows, opts) {
|
|
|
4882
4942
|
return results;
|
|
4883
4943
|
}
|
|
4884
4944
|
|
|
4945
|
+
// src/utils/filter-tokens.ts
|
|
4946
|
+
var import_data = require("@objectstack/spec/data");
|
|
4947
|
+
var UnknownFilterTokenError = class extends Error {
|
|
4948
|
+
constructor(token, suggestion) {
|
|
4949
|
+
super(
|
|
4950
|
+
`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."
|
|
4951
|
+
);
|
|
4952
|
+
this.status = 400;
|
|
4953
|
+
this.code = "FILTER_TOKEN_UNKNOWN";
|
|
4954
|
+
this.name = "UnknownFilterTokenError";
|
|
4955
|
+
this.token = token;
|
|
4956
|
+
this.suggestion = suggestion;
|
|
4957
|
+
}
|
|
4958
|
+
};
|
|
4959
|
+
var UnresolvedFilterTokenError = class extends Error {
|
|
4960
|
+
constructor(token, detail) {
|
|
4961
|
+
super(`Filter placeholder "{${token}}" cannot be resolved: ${detail}`);
|
|
4962
|
+
/** 400, not 500 — see {@link UnknownFilterTokenError}. */
|
|
4963
|
+
this.status = 400;
|
|
4964
|
+
this.code = "FILTER_TOKEN_UNRESOLVED";
|
|
4965
|
+
this.name = "UnresolvedFilterTokenError";
|
|
4966
|
+
this.token = token;
|
|
4967
|
+
}
|
|
4968
|
+
};
|
|
4969
|
+
function ymd(year, month, day) {
|
|
4970
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
4971
|
+
return `${year}-${p(month)}-${p(day)}`;
|
|
4972
|
+
}
|
|
4973
|
+
function proxyDay(now, timezone) {
|
|
4974
|
+
const { year, month, day } = calendarPartsInTzOrUtc(now, timezone);
|
|
4975
|
+
return new Date(Date.UTC(year, month - 1, day));
|
|
4976
|
+
}
|
|
4977
|
+
var asYmd = (d) => ymd(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
|
|
4978
|
+
function startOfPeriod(kind, d) {
|
|
4979
|
+
const r = new Date(d.getTime());
|
|
4980
|
+
switch (kind) {
|
|
4981
|
+
case "week": {
|
|
4982
|
+
const dow = (r.getUTCDay() + 6) % 7;
|
|
4983
|
+
r.setUTCDate(r.getUTCDate() - dow);
|
|
4984
|
+
return r;
|
|
4985
|
+
}
|
|
4986
|
+
case "month":
|
|
4987
|
+
return new Date(Date.UTC(r.getUTCFullYear(), r.getUTCMonth(), 1));
|
|
4988
|
+
case "quarter":
|
|
4989
|
+
return new Date(Date.UTC(r.getUTCFullYear(), Math.floor(r.getUTCMonth() / 3) * 3, 1));
|
|
4990
|
+
case "year":
|
|
4991
|
+
return new Date(Date.UTC(r.getUTCFullYear(), 0, 1));
|
|
4992
|
+
}
|
|
4993
|
+
}
|
|
4994
|
+
function daysInMonth(year, month) {
|
|
4995
|
+
return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
|
|
4996
|
+
}
|
|
4997
|
+
function addMonthsClamped(d, n) {
|
|
4998
|
+
const year = d.getUTCFullYear();
|
|
4999
|
+
const month = d.getUTCMonth() + n;
|
|
5000
|
+
const targetYear = year + Math.floor(month / 12);
|
|
5001
|
+
const targetMonth = (month % 12 + 12) % 12;
|
|
5002
|
+
const day = Math.min(d.getUTCDate(), daysInMonth(targetYear, targetMonth));
|
|
5003
|
+
return new Date(Date.UTC(
|
|
5004
|
+
targetYear,
|
|
5005
|
+
targetMonth,
|
|
5006
|
+
day,
|
|
5007
|
+
d.getUTCHours(),
|
|
5008
|
+
d.getUTCMinutes(),
|
|
5009
|
+
d.getUTCSeconds(),
|
|
5010
|
+
d.getUTCMilliseconds()
|
|
5011
|
+
));
|
|
5012
|
+
}
|
|
5013
|
+
function addPeriods(kind, d, n) {
|
|
5014
|
+
switch (kind) {
|
|
5015
|
+
case "week": {
|
|
5016
|
+
const r = new Date(d.getTime());
|
|
5017
|
+
r.setUTCDate(r.getUTCDate() + n * 7);
|
|
5018
|
+
return r;
|
|
5019
|
+
}
|
|
5020
|
+
case "month":
|
|
5021
|
+
return addMonthsClamped(d, n);
|
|
5022
|
+
case "quarter":
|
|
5023
|
+
return addMonthsClamped(d, n * 3);
|
|
5024
|
+
case "year":
|
|
5025
|
+
return addMonthsClamped(d, n * 12);
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
function addUnits(unit, d, n) {
|
|
5029
|
+
const r = new Date(d.getTime());
|
|
5030
|
+
switch (unit) {
|
|
5031
|
+
case "minute":
|
|
5032
|
+
r.setUTCMinutes(r.getUTCMinutes() + n);
|
|
5033
|
+
return r;
|
|
5034
|
+
case "hour":
|
|
5035
|
+
r.setUTCHours(r.getUTCHours() + n);
|
|
5036
|
+
return r;
|
|
5037
|
+
case "day":
|
|
5038
|
+
r.setUTCDate(r.getUTCDate() + n);
|
|
5039
|
+
return r;
|
|
5040
|
+
case "week":
|
|
5041
|
+
r.setUTCDate(r.getUTCDate() + n * 7);
|
|
5042
|
+
return r;
|
|
5043
|
+
// Month/year steps clamp rather than overflow — see addMonthsClamped.
|
|
5044
|
+
case "month":
|
|
5045
|
+
return addMonthsClamped(d, n);
|
|
5046
|
+
case "year":
|
|
5047
|
+
return addMonthsClamped(d, n * 12);
|
|
5048
|
+
}
|
|
5049
|
+
}
|
|
5050
|
+
var PERIOD_RE = /^(?:(current|last|next)_)?(week|month|quarter|year)_(start|end)$/;
|
|
5051
|
+
function resolvePeriodToken(token, today) {
|
|
5052
|
+
const m = PERIOD_RE.exec(token);
|
|
5053
|
+
if (!m) return void 0;
|
|
5054
|
+
const rel = m[1] ?? "current";
|
|
5055
|
+
const kind = m[2];
|
|
5056
|
+
const bound = m[3];
|
|
5057
|
+
const offset = rel === "last" ? -1 : rel === "next" ? 1 : 0;
|
|
5058
|
+
const periodStart = startOfPeriod(kind, addPeriods(kind, startOfPeriod(kind, today), offset));
|
|
5059
|
+
if (bound === "start") return asYmd(periodStart);
|
|
5060
|
+
const next = addPeriods(kind, periodStart, 1);
|
|
5061
|
+
next.setUTCDate(next.getUTCDate() - 1);
|
|
5062
|
+
return asYmd(next);
|
|
5063
|
+
}
|
|
5064
|
+
function resolveFilterToken(token, ctx = {}) {
|
|
5065
|
+
const now = ctx.now ?? /* @__PURE__ */ new Date();
|
|
5066
|
+
if (token === "current_user_id") {
|
|
5067
|
+
if (!ctx.userId) {
|
|
5068
|
+
throw new UnresolvedFilterTokenError(
|
|
5069
|
+
token,
|
|
5070
|
+
"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."
|
|
5071
|
+
);
|
|
5072
|
+
}
|
|
5073
|
+
return ctx.userId;
|
|
5074
|
+
}
|
|
5075
|
+
if (token === "current_org_id") {
|
|
5076
|
+
if (!ctx.orgId) {
|
|
5077
|
+
throw new UnresolvedFilterTokenError(
|
|
5078
|
+
token,
|
|
5079
|
+
"the request carries no active organization (ExecutionContext.tenantId is unset). Set the active org on the request, or drop the token from the filter."
|
|
5080
|
+
);
|
|
5081
|
+
}
|
|
5082
|
+
return ctx.orgId;
|
|
5083
|
+
}
|
|
5084
|
+
const today = proxyDay(now, ctx.timezone);
|
|
5085
|
+
switch (token) {
|
|
5086
|
+
case "now":
|
|
5087
|
+
return now.toISOString();
|
|
5088
|
+
case "today":
|
|
5089
|
+
return asYmd(today);
|
|
5090
|
+
case "yesterday":
|
|
5091
|
+
return asYmd(addUnits("day", today, -1));
|
|
5092
|
+
case "tomorrow":
|
|
5093
|
+
return asYmd(addUnits("day", today, 1));
|
|
5094
|
+
}
|
|
5095
|
+
const period = resolvePeriodToken(token, today);
|
|
5096
|
+
if (period !== void 0) return period;
|
|
5097
|
+
const param = (0, import_data.parseDateMacroParam)(token);
|
|
5098
|
+
if (param) {
|
|
5099
|
+
const sign = param.direction === "ago" ? -1 : 1;
|
|
5100
|
+
if (param.unit === "minute" || param.unit === "hour") {
|
|
5101
|
+
return addUnits(param.unit, now, sign * param.n).toISOString();
|
|
5102
|
+
}
|
|
5103
|
+
return asYmd(addUnits(param.unit, today, sign * param.n));
|
|
5104
|
+
}
|
|
5105
|
+
return void 0;
|
|
5106
|
+
}
|
|
5107
|
+
function hasFilterToken(node) {
|
|
5108
|
+
if (typeof node === "string") return (0, import_data.classifyFilterToken)(node) !== null;
|
|
5109
|
+
if (Array.isArray(node)) return node.some(hasFilterToken);
|
|
5110
|
+
if (node && typeof node === "object" && !(node instanceof Date)) {
|
|
5111
|
+
return Object.values(node).some(hasFilterToken);
|
|
5112
|
+
}
|
|
5113
|
+
return false;
|
|
5114
|
+
}
|
|
5115
|
+
function resolveFilterTokens(filter, ctx = {}) {
|
|
5116
|
+
if (filter == null) return filter;
|
|
5117
|
+
if (!hasFilterToken(filter)) return filter;
|
|
5118
|
+
const pinned = { ...ctx, now: ctx.now ?? /* @__PURE__ */ new Date() };
|
|
5119
|
+
const walk = (node) => {
|
|
5120
|
+
if (typeof node === "string") {
|
|
5121
|
+
const cls = (0, import_data.classifyFilterToken)(node);
|
|
5122
|
+
if (!cls) return node;
|
|
5123
|
+
if (cls.kind === "unknown") throw new UnknownFilterTokenError(cls.token, cls.suggestion);
|
|
5124
|
+
const resolved = resolveFilterToken(cls.token, pinned);
|
|
5125
|
+
if (resolved === void 0) throw new UnknownFilterTokenError(cls.token);
|
|
5126
|
+
return resolved;
|
|
5127
|
+
}
|
|
5128
|
+
if (Array.isArray(node)) return node.map(walk);
|
|
5129
|
+
if (node && typeof node === "object") {
|
|
5130
|
+
if (node instanceof Date) return node;
|
|
5131
|
+
const out = {};
|
|
5132
|
+
for (const [k, v] of Object.entries(node)) out[k] = walk(v);
|
|
5133
|
+
return out;
|
|
5134
|
+
}
|
|
5135
|
+
return node;
|
|
5136
|
+
};
|
|
5137
|
+
return walk(filter);
|
|
5138
|
+
}
|
|
5139
|
+
function filterTokenContextFrom(execCtx, now) {
|
|
5140
|
+
return {
|
|
5141
|
+
now,
|
|
5142
|
+
timezone: execCtx?.timezone,
|
|
5143
|
+
userId: execCtx?.userId,
|
|
5144
|
+
orgId: execCtx?.tenantId
|
|
5145
|
+
};
|
|
5146
|
+
}
|
|
5147
|
+
|
|
4885
5148
|
// src/health-monitor.ts
|
|
4886
5149
|
var PluginHealthMonitor = class {
|
|
4887
5150
|
constructor(logger) {
|
|
@@ -5827,6 +6090,8 @@ var NamespaceResolver = class {
|
|
|
5827
6090
|
SecurePluginContext,
|
|
5828
6091
|
SemanticVersionManager,
|
|
5829
6092
|
ServiceLifecycle,
|
|
6093
|
+
UnknownFilterTokenError,
|
|
6094
|
+
UnresolvedFilterTokenError,
|
|
5830
6095
|
bucketKeyToCalendarRange,
|
|
5831
6096
|
buildPermissionsFromGrants,
|
|
5832
6097
|
bulkWrite,
|
|
@@ -5847,6 +6112,7 @@ var NamespaceResolver = class {
|
|
|
5847
6112
|
derivePosture,
|
|
5848
6113
|
evaluateAuthGate,
|
|
5849
6114
|
extractApiKey,
|
|
6115
|
+
filterTokenContextFrom,
|
|
5850
6116
|
generateApiKey,
|
|
5851
6117
|
generateEd25519KeyPair,
|
|
5852
6118
|
getEnv,
|
|
@@ -5863,8 +6129,11 @@ var NamespaceResolver = class {
|
|
|
5863
6129
|
readAuthoredTranslationLayer,
|
|
5864
6130
|
resolveApiKeyPrincipal,
|
|
5865
6131
|
resolveAuthzContext,
|
|
6132
|
+
resolveFilterToken,
|
|
6133
|
+
resolveFilterTokens,
|
|
5866
6134
|
resolveLocale,
|
|
5867
6135
|
resolveLocalizationContext,
|
|
6136
|
+
resolveUserAuthzGrants,
|
|
5868
6137
|
safeExit,
|
|
5869
6138
|
shouldDenyAnonymous,
|
|
5870
6139
|
signPayload,
|