@objectstack/core 16.1.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 +258 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +104 -4
- package/dist/index.d.ts +104 -4
- package/dist/index.js +254 -12
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1751,6 +1751,14 @@ interface ResolvedAuthzContext {
|
|
|
1751
1751
|
tabPermissions?: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'>;
|
|
1752
1752
|
/** Fellow-org user IDs for RLS scoping of identity tables (`id IN (...)`). */
|
|
1753
1753
|
org_user_ids: string[];
|
|
1754
|
+
/**
|
|
1755
|
+
* [ADR-0105 D2] Every organization this principal currently holds a VALID
|
|
1756
|
+
* membership in — the caller's org access set, and the read reach of the
|
|
1757
|
+
* `group` tenancy posture (Layer 0 becomes `organization_id IN (...)`).
|
|
1758
|
+
* Resolved here, once, so no surface re-derives it; empty for an anonymous or
|
|
1759
|
+
* membership-less principal, which fails the group wall closed.
|
|
1760
|
+
*/
|
|
1761
|
+
accessible_org_ids: string[];
|
|
1754
1762
|
/**
|
|
1755
1763
|
* [ADR-0095 D2/D3] The monotonic posture rung this principal resolves to,
|
|
1756
1764
|
* DERIVED once here from held capability grants (never a better-auth role):
|
|
@@ -1788,6 +1796,8 @@ interface UserAuthzGrants {
|
|
|
1788
1796
|
systemPermissions: string[];
|
|
1789
1797
|
/** Fellow-org user IDs for RLS scoping of identity tables (`id IN (...)`). */
|
|
1790
1798
|
org_user_ids: string[];
|
|
1799
|
+
/** [ADR-0105 D2] Organizations this user holds a currently-valid membership in. */
|
|
1800
|
+
accessible_org_ids: string[];
|
|
1791
1801
|
tabPermissions?: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'>;
|
|
1792
1802
|
posture?: AuthzPosture;
|
|
1793
1803
|
/** The user's unique email (`sys_user`), for `current_user.email` owner RLS. */
|
|
@@ -2123,12 +2133,16 @@ type BucketGranularity = 'day' | 'week' | 'month' | 'quarter' | 'year';
|
|
|
2123
2133
|
* `datetime` field in a reference timezone layers that on top (and, per
|
|
2124
2134
|
* ADR-0053, a `date` field compares against these `YYYY-MM-DD` bounds directly).
|
|
2125
2135
|
*
|
|
2126
|
-
* Returns `null` for the
|
|
2127
|
-
*
|
|
2136
|
+
* Returns `null` for the empty bucket, an unparseable key, or a key that is
|
|
2137
|
+
* shape-valid but out of range (e.g. `2026-13`, a `-W53` in a 52-week year,
|
|
2128
2138
|
* `2026-02-30`). Callers drop the range and fall back to an unscoped (superset)
|
|
2129
2139
|
* drill rather than emit a wrong bound.
|
|
2140
|
+
*
|
|
2141
|
+
* `key` admits `null` because that IS the empty bucket's key on both aggregation
|
|
2142
|
+
* paths (#3839); callers pass a grouped row's dimension value straight through
|
|
2143
|
+
* rather than casting a lie.
|
|
2130
2144
|
*/
|
|
2131
|
-
declare function bucketKeyToCalendarRange(key: string, granularity: BucketGranularity): {
|
|
2145
|
+
declare function bucketKeyToCalendarRange(key: string | null | undefined, granularity: BucketGranularity): {
|
|
2132
2146
|
start: string;
|
|
2133
2147
|
end: string;
|
|
2134
2148
|
} | null;
|
|
@@ -2251,6 +2265,92 @@ declare function withTransientRetry<T>(fn: (attempt: number) => Promise<T>, opts
|
|
|
2251
2265
|
*/
|
|
2252
2266
|
declare function bulkWrite<TRow, TRecord = any>(rows: TRow[], opts: BulkWriteOptions<TRow, TRecord>): Promise<BulkWriteRowResult<TRecord>[]>;
|
|
2253
2267
|
|
|
2268
|
+
/**
|
|
2269
|
+
* The slice of an execution context the resolver reads. Structural on purpose —
|
|
2270
|
+
* see {@link filterTokenContextFrom}.
|
|
2271
|
+
*/
|
|
2272
|
+
interface ExecutionContextLike {
|
|
2273
|
+
readonly userId?: string;
|
|
2274
|
+
readonly tenantId?: string;
|
|
2275
|
+
readonly timezone?: string;
|
|
2276
|
+
}
|
|
2277
|
+
/**
|
|
2278
|
+
* The request-scoped values a placeholder can resolve against.
|
|
2279
|
+
*
|
|
2280
|
+
* `now` is captured ONCE per resolve call so every token in one filter shares
|
|
2281
|
+
* an instant — otherwise a `$gte {current_month_start}` / `$lt
|
|
2282
|
+
* {next_month_start}` pair evaluated microseconds apart could straddle a
|
|
2283
|
+
* month boundary and silently drop a row.
|
|
2284
|
+
*/
|
|
2285
|
+
interface FilterTokenResolutionContext {
|
|
2286
|
+
/** Reference instant. Defaults to `new Date()` at call time. */
|
|
2287
|
+
now?: Date;
|
|
2288
|
+
/** IANA reference timezone for calendar boundaries. Defaults to UTC. */
|
|
2289
|
+
timezone?: string;
|
|
2290
|
+
/** Resolves `{current_user_id}`. */
|
|
2291
|
+
userId?: string;
|
|
2292
|
+
/** Resolves `{current_org_id}`. */
|
|
2293
|
+
orgId?: string;
|
|
2294
|
+
}
|
|
2295
|
+
/**
|
|
2296
|
+
* Raised when a filter carries a placeholder outside the vocabulary.
|
|
2297
|
+
*
|
|
2298
|
+
* Carries `status`/`code` so the REST layer's generic 4xx passthrough maps it
|
|
2299
|
+
* to a **400 with a fixable message** rather than a 500: the caller's filter is
|
|
2300
|
+
* malformed, the server is fine. (Same convention plugin-sharing uses for its
|
|
2301
|
+
* record-scope denial — no runtime dependency in either direction.)
|
|
2302
|
+
*/
|
|
2303
|
+
declare class UnknownFilterTokenError extends Error {
|
|
2304
|
+
readonly token: string;
|
|
2305
|
+
readonly suggestion?: string;
|
|
2306
|
+
readonly status = 400;
|
|
2307
|
+
readonly code = "FILTER_TOKEN_UNKNOWN";
|
|
2308
|
+
constructor(token: string, suggestion?: string);
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Raised when a token IS in the vocabulary but the request carries no value
|
|
2312
|
+
* for it — an unauthenticated caller filtering on `{current_user_id}`.
|
|
2313
|
+
*
|
|
2314
|
+
* Distinct from {@link UnknownFilterTokenError} because the fix is different:
|
|
2315
|
+
* the metadata is correct, the context is not. Never silently resolves to
|
|
2316
|
+
* `null`/`undefined`, which on most drivers degrades to `IS NULL` and would
|
|
2317
|
+
* quietly hand back rows the filter was written to exclude.
|
|
2318
|
+
*/
|
|
2319
|
+
declare class UnresolvedFilterTokenError extends Error {
|
|
2320
|
+
readonly token: string;
|
|
2321
|
+
/** 400, not 500 — see {@link UnknownFilterTokenError}. */
|
|
2322
|
+
readonly status = 400;
|
|
2323
|
+
readonly code = "FILTER_TOKEN_UNRESOLVED";
|
|
2324
|
+
constructor(token: string, detail: string);
|
|
2325
|
+
}
|
|
2326
|
+
/**
|
|
2327
|
+
* Resolve one token NAME (the bit inside the braces) to its concrete value.
|
|
2328
|
+
* Throws {@link UnresolvedFilterTokenError} for a vocabulary token the request
|
|
2329
|
+
* carries no value for. Returns `undefined` only when the token is outside the
|
|
2330
|
+
* vocabulary — callers turn that into {@link UnknownFilterTokenError}.
|
|
2331
|
+
*/
|
|
2332
|
+
declare function resolveFilterToken(token: string, ctx?: FilterTokenResolutionContext): unknown;
|
|
2333
|
+
/**
|
|
2334
|
+
* Deep-replace every fully-wrapped placeholder in `filter` with its resolved
|
|
2335
|
+
* value, returning a NEW tree (the caller's metadata is never mutated — a view
|
|
2336
|
+
* or dataset definition is shared across requests, so resolving in place would
|
|
2337
|
+
* bake one request's user id, and one day's dates, into every later render).
|
|
2338
|
+
*
|
|
2339
|
+
* Returns the input unchanged, by reference, when it holds no placeholders.
|
|
2340
|
+
*/
|
|
2341
|
+
declare function resolveFilterTokens<T>(filter: T, ctx?: FilterTokenResolutionContext): T;
|
|
2342
|
+
/**
|
|
2343
|
+
* Convenience bridge from an execution context to the resolver's inputs.
|
|
2344
|
+
* `{current_org_id}` reads `tenantId` — the active organization IS the tenant
|
|
2345
|
+
* on the read path (same value the RLS compiler binds to
|
|
2346
|
+
* `current_user.organization_id`).
|
|
2347
|
+
*
|
|
2348
|
+
* Typed structurally, not as `ExecutionContext`, so both the parsed context
|
|
2349
|
+
* (defaults applied) and the pre-parse `ExecutionContextInput` a caller holds
|
|
2350
|
+
* mid-pipeline satisfy it. The three fields read here are optional in both.
|
|
2351
|
+
*/
|
|
2352
|
+
declare function filterTokenContextFrom(execCtx: ExecutionContextLike | undefined, now?: Date): FilterTokenResolutionContext;
|
|
2353
|
+
|
|
2254
2354
|
/**
|
|
2255
2355
|
* In-memory Map-backed cache fallback.
|
|
2256
2356
|
*
|
|
@@ -2687,4 +2787,4 @@ declare class NamespaceResolver {
|
|
|
2687
2787
|
private suggestAlternative;
|
|
2688
2788
|
}
|
|
2689
2789
|
|
|
2690
|
-
export { ANONYMOUS_DENY_BODY, ANONYMOUS_DENY_CODE, ANONYMOUS_DENY_MESSAGE, ANONYMOUS_DENY_STATUS, API_KEY_PREFIX, type AnonymousDenyInput, type ApiKeyPrincipal, ApiRegistry, type ApiRegistryPluginConfig, type AuthGate, type BucketGranularity, type BulkWriteOptions, type BulkWriteRowResult, CORE_FALLBACK_FACTORIES, type CalendarParts, DependencyResolver, type GeneratedApiKey, type GrantValidityWindow, HotReloadManager, type KernelState, type KeyInput, type LadderPrincipal, type LadderRow, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, POSTURE_INJECTION_RULE, POSTURE_LADDER, POSTURE_RANK, type ParsedSignature, type PermissionCheckResult$1 as PermissionCheckResult, type PermissionGrant, type Plugin, type PluginArtifactVerifyResult, PluginConfigValidator, type PluginContext, PluginHealthMonitor, type PluginHealthStatus, type PluginLoadResult, PluginLoader, type PluginMetadata, type PermissionCheckResult as PluginPermissionCheckResult, PluginPermissionEnforcer, PluginPermissionManager, type PluginPermissions, PluginSandboxRuntime, PluginSecurityScanner, type PluginSignatureConfig, PluginSignatureVerifier, type PluginStartupResult, type PostureEvidence, type PublisherVerifyResult, index as QA, type ResolveAuthzInput, type ResolveLocalizationInput, type ResolveUserAuthzGrantsOptions, type ResolvedAuthzContext, type ResourceUsage, type RetryOptions, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, type UserAuthzGrants, type VersionCompatibility, bucketKeyToCalendarRange, buildPermissionsFromGrants, bulkWrite, calendarPartsInTz, calendarPartsInTzOrUtc, counterSignPayload, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, deepMerge, defaultIsTransientError, derivePosture, evaluateAuthGate, extractApiKey, generateApiKey, generateEd25519KeyPair, getEnv, getMemoryUsage, hashApiKey, isAuthGateAllowlisted, isExpired, isGrantActive, isGrantExpired, isNode, parseScopes, parseSignature, postureVisibleRows, readAuthoredTranslationLayer, resolveApiKeyPrincipal, resolveAuthzContext, resolveLocale, resolveLocalizationContext, resolveUserAuthzGrants, safeExit, shouldDenyAnonymous, signPayload, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature, wireAuthoredTranslationSync, withTransientRetry, zonedDateStartToUtcMs };
|
|
2790
|
+
export { ANONYMOUS_DENY_BODY, ANONYMOUS_DENY_CODE, ANONYMOUS_DENY_MESSAGE, ANONYMOUS_DENY_STATUS, API_KEY_PREFIX, type AnonymousDenyInput, type ApiKeyPrincipal, ApiRegistry, type ApiRegistryPluginConfig, type AuthGate, type BucketGranularity, type BulkWriteOptions, type BulkWriteRowResult, CORE_FALLBACK_FACTORIES, type CalendarParts, DependencyResolver, type ExecutionContextLike, type FilterTokenResolutionContext, type GeneratedApiKey, type GrantValidityWindow, HotReloadManager, type KernelState, type KeyInput, type LadderPrincipal, type LadderRow, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, POSTURE_INJECTION_RULE, POSTURE_LADDER, POSTURE_RANK, type ParsedSignature, type PermissionCheckResult$1 as PermissionCheckResult, type PermissionGrant, type Plugin, type PluginArtifactVerifyResult, PluginConfigValidator, type PluginContext, PluginHealthMonitor, type PluginHealthStatus, type PluginLoadResult, PluginLoader, type PluginMetadata, type PermissionCheckResult as PluginPermissionCheckResult, PluginPermissionEnforcer, PluginPermissionManager, type PluginPermissions, PluginSandboxRuntime, PluginSecurityScanner, type PluginSignatureConfig, PluginSignatureVerifier, type PluginStartupResult, type PostureEvidence, type PublisherVerifyResult, index as QA, type ResolveAuthzInput, type ResolveLocalizationInput, type ResolveUserAuthzGrantsOptions, type ResolvedAuthzContext, type ResourceUsage, type RetryOptions, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, UnknownFilterTokenError, UnresolvedFilterTokenError, type UserAuthzGrants, type VersionCompatibility, bucketKeyToCalendarRange, buildPermissionsFromGrants, bulkWrite, calendarPartsInTz, calendarPartsInTzOrUtc, counterSignPayload, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, deepMerge, defaultIsTransientError, derivePosture, evaluateAuthGate, extractApiKey, filterTokenContextFrom, generateApiKey, generateEd25519KeyPair, getEnv, getMemoryUsage, hashApiKey, isAuthGateAllowlisted, isExpired, isGrantActive, isGrantExpired, isNode, parseScopes, parseSignature, postureVisibleRows, readAuthoredTranslationLayer, resolveApiKeyPrincipal, resolveAuthzContext, resolveFilterToken, resolveFilterTokens, resolveLocale, resolveLocalizationContext, resolveUserAuthzGrants, safeExit, shouldDenyAnonymous, signPayload, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature, wireAuthoredTranslationSync, withTransientRetry, zonedDateStartToUtcMs };
|
package/dist/index.d.ts
CHANGED
|
@@ -1751,6 +1751,14 @@ interface ResolvedAuthzContext {
|
|
|
1751
1751
|
tabPermissions?: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'>;
|
|
1752
1752
|
/** Fellow-org user IDs for RLS scoping of identity tables (`id IN (...)`). */
|
|
1753
1753
|
org_user_ids: string[];
|
|
1754
|
+
/**
|
|
1755
|
+
* [ADR-0105 D2] Every organization this principal currently holds a VALID
|
|
1756
|
+
* membership in — the caller's org access set, and the read reach of the
|
|
1757
|
+
* `group` tenancy posture (Layer 0 becomes `organization_id IN (...)`).
|
|
1758
|
+
* Resolved here, once, so no surface re-derives it; empty for an anonymous or
|
|
1759
|
+
* membership-less principal, which fails the group wall closed.
|
|
1760
|
+
*/
|
|
1761
|
+
accessible_org_ids: string[];
|
|
1754
1762
|
/**
|
|
1755
1763
|
* [ADR-0095 D2/D3] The monotonic posture rung this principal resolves to,
|
|
1756
1764
|
* DERIVED once here from held capability grants (never a better-auth role):
|
|
@@ -1788,6 +1796,8 @@ interface UserAuthzGrants {
|
|
|
1788
1796
|
systemPermissions: string[];
|
|
1789
1797
|
/** Fellow-org user IDs for RLS scoping of identity tables (`id IN (...)`). */
|
|
1790
1798
|
org_user_ids: string[];
|
|
1799
|
+
/** [ADR-0105 D2] Organizations this user holds a currently-valid membership in. */
|
|
1800
|
+
accessible_org_ids: string[];
|
|
1791
1801
|
tabPermissions?: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'>;
|
|
1792
1802
|
posture?: AuthzPosture;
|
|
1793
1803
|
/** The user's unique email (`sys_user`), for `current_user.email` owner RLS. */
|
|
@@ -2123,12 +2133,16 @@ type BucketGranularity = 'day' | 'week' | 'month' | 'quarter' | 'year';
|
|
|
2123
2133
|
* `datetime` field in a reference timezone layers that on top (and, per
|
|
2124
2134
|
* ADR-0053, a `date` field compares against these `YYYY-MM-DD` bounds directly).
|
|
2125
2135
|
*
|
|
2126
|
-
* Returns `null` for the
|
|
2127
|
-
*
|
|
2136
|
+
* Returns `null` for the empty bucket, an unparseable key, or a key that is
|
|
2137
|
+
* shape-valid but out of range (e.g. `2026-13`, a `-W53` in a 52-week year,
|
|
2128
2138
|
* `2026-02-30`). Callers drop the range and fall back to an unscoped (superset)
|
|
2129
2139
|
* drill rather than emit a wrong bound.
|
|
2140
|
+
*
|
|
2141
|
+
* `key` admits `null` because that IS the empty bucket's key on both aggregation
|
|
2142
|
+
* paths (#3839); callers pass a grouped row's dimension value straight through
|
|
2143
|
+
* rather than casting a lie.
|
|
2130
2144
|
*/
|
|
2131
|
-
declare function bucketKeyToCalendarRange(key: string, granularity: BucketGranularity): {
|
|
2145
|
+
declare function bucketKeyToCalendarRange(key: string | null | undefined, granularity: BucketGranularity): {
|
|
2132
2146
|
start: string;
|
|
2133
2147
|
end: string;
|
|
2134
2148
|
} | null;
|
|
@@ -2251,6 +2265,92 @@ declare function withTransientRetry<T>(fn: (attempt: number) => Promise<T>, opts
|
|
|
2251
2265
|
*/
|
|
2252
2266
|
declare function bulkWrite<TRow, TRecord = any>(rows: TRow[], opts: BulkWriteOptions<TRow, TRecord>): Promise<BulkWriteRowResult<TRecord>[]>;
|
|
2253
2267
|
|
|
2268
|
+
/**
|
|
2269
|
+
* The slice of an execution context the resolver reads. Structural on purpose —
|
|
2270
|
+
* see {@link filterTokenContextFrom}.
|
|
2271
|
+
*/
|
|
2272
|
+
interface ExecutionContextLike {
|
|
2273
|
+
readonly userId?: string;
|
|
2274
|
+
readonly tenantId?: string;
|
|
2275
|
+
readonly timezone?: string;
|
|
2276
|
+
}
|
|
2277
|
+
/**
|
|
2278
|
+
* The request-scoped values a placeholder can resolve against.
|
|
2279
|
+
*
|
|
2280
|
+
* `now` is captured ONCE per resolve call so every token in one filter shares
|
|
2281
|
+
* an instant — otherwise a `$gte {current_month_start}` / `$lt
|
|
2282
|
+
* {next_month_start}` pair evaluated microseconds apart could straddle a
|
|
2283
|
+
* month boundary and silently drop a row.
|
|
2284
|
+
*/
|
|
2285
|
+
interface FilterTokenResolutionContext {
|
|
2286
|
+
/** Reference instant. Defaults to `new Date()` at call time. */
|
|
2287
|
+
now?: Date;
|
|
2288
|
+
/** IANA reference timezone for calendar boundaries. Defaults to UTC. */
|
|
2289
|
+
timezone?: string;
|
|
2290
|
+
/** Resolves `{current_user_id}`. */
|
|
2291
|
+
userId?: string;
|
|
2292
|
+
/** Resolves `{current_org_id}`. */
|
|
2293
|
+
orgId?: string;
|
|
2294
|
+
}
|
|
2295
|
+
/**
|
|
2296
|
+
* Raised when a filter carries a placeholder outside the vocabulary.
|
|
2297
|
+
*
|
|
2298
|
+
* Carries `status`/`code` so the REST layer's generic 4xx passthrough maps it
|
|
2299
|
+
* to a **400 with a fixable message** rather than a 500: the caller's filter is
|
|
2300
|
+
* malformed, the server is fine. (Same convention plugin-sharing uses for its
|
|
2301
|
+
* record-scope denial — no runtime dependency in either direction.)
|
|
2302
|
+
*/
|
|
2303
|
+
declare class UnknownFilterTokenError extends Error {
|
|
2304
|
+
readonly token: string;
|
|
2305
|
+
readonly suggestion?: string;
|
|
2306
|
+
readonly status = 400;
|
|
2307
|
+
readonly code = "FILTER_TOKEN_UNKNOWN";
|
|
2308
|
+
constructor(token: string, suggestion?: string);
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Raised when a token IS in the vocabulary but the request carries no value
|
|
2312
|
+
* for it — an unauthenticated caller filtering on `{current_user_id}`.
|
|
2313
|
+
*
|
|
2314
|
+
* Distinct from {@link UnknownFilterTokenError} because the fix is different:
|
|
2315
|
+
* the metadata is correct, the context is not. Never silently resolves to
|
|
2316
|
+
* `null`/`undefined`, which on most drivers degrades to `IS NULL` and would
|
|
2317
|
+
* quietly hand back rows the filter was written to exclude.
|
|
2318
|
+
*/
|
|
2319
|
+
declare class UnresolvedFilterTokenError extends Error {
|
|
2320
|
+
readonly token: string;
|
|
2321
|
+
/** 400, not 500 — see {@link UnknownFilterTokenError}. */
|
|
2322
|
+
readonly status = 400;
|
|
2323
|
+
readonly code = "FILTER_TOKEN_UNRESOLVED";
|
|
2324
|
+
constructor(token: string, detail: string);
|
|
2325
|
+
}
|
|
2326
|
+
/**
|
|
2327
|
+
* Resolve one token NAME (the bit inside the braces) to its concrete value.
|
|
2328
|
+
* Throws {@link UnresolvedFilterTokenError} for a vocabulary token the request
|
|
2329
|
+
* carries no value for. Returns `undefined` only when the token is outside the
|
|
2330
|
+
* vocabulary — callers turn that into {@link UnknownFilterTokenError}.
|
|
2331
|
+
*/
|
|
2332
|
+
declare function resolveFilterToken(token: string, ctx?: FilterTokenResolutionContext): unknown;
|
|
2333
|
+
/**
|
|
2334
|
+
* Deep-replace every fully-wrapped placeholder in `filter` with its resolved
|
|
2335
|
+
* value, returning a NEW tree (the caller's metadata is never mutated — a view
|
|
2336
|
+
* or dataset definition is shared across requests, so resolving in place would
|
|
2337
|
+
* bake one request's user id, and one day's dates, into every later render).
|
|
2338
|
+
*
|
|
2339
|
+
* Returns the input unchanged, by reference, when it holds no placeholders.
|
|
2340
|
+
*/
|
|
2341
|
+
declare function resolveFilterTokens<T>(filter: T, ctx?: FilterTokenResolutionContext): T;
|
|
2342
|
+
/**
|
|
2343
|
+
* Convenience bridge from an execution context to the resolver's inputs.
|
|
2344
|
+
* `{current_org_id}` reads `tenantId` — the active organization IS the tenant
|
|
2345
|
+
* on the read path (same value the RLS compiler binds to
|
|
2346
|
+
* `current_user.organization_id`).
|
|
2347
|
+
*
|
|
2348
|
+
* Typed structurally, not as `ExecutionContext`, so both the parsed context
|
|
2349
|
+
* (defaults applied) and the pre-parse `ExecutionContextInput` a caller holds
|
|
2350
|
+
* mid-pipeline satisfy it. The three fields read here are optional in both.
|
|
2351
|
+
*/
|
|
2352
|
+
declare function filterTokenContextFrom(execCtx: ExecutionContextLike | undefined, now?: Date): FilterTokenResolutionContext;
|
|
2353
|
+
|
|
2254
2354
|
/**
|
|
2255
2355
|
* In-memory Map-backed cache fallback.
|
|
2256
2356
|
*
|
|
@@ -2687,4 +2787,4 @@ declare class NamespaceResolver {
|
|
|
2687
2787
|
private suggestAlternative;
|
|
2688
2788
|
}
|
|
2689
2789
|
|
|
2690
|
-
export { ANONYMOUS_DENY_BODY, ANONYMOUS_DENY_CODE, ANONYMOUS_DENY_MESSAGE, ANONYMOUS_DENY_STATUS, API_KEY_PREFIX, type AnonymousDenyInput, type ApiKeyPrincipal, ApiRegistry, type ApiRegistryPluginConfig, type AuthGate, type BucketGranularity, type BulkWriteOptions, type BulkWriteRowResult, CORE_FALLBACK_FACTORIES, type CalendarParts, DependencyResolver, type GeneratedApiKey, type GrantValidityWindow, HotReloadManager, type KernelState, type KeyInput, type LadderPrincipal, type LadderRow, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, POSTURE_INJECTION_RULE, POSTURE_LADDER, POSTURE_RANK, type ParsedSignature, type PermissionCheckResult$1 as PermissionCheckResult, type PermissionGrant, type Plugin, type PluginArtifactVerifyResult, PluginConfigValidator, type PluginContext, PluginHealthMonitor, type PluginHealthStatus, type PluginLoadResult, PluginLoader, type PluginMetadata, type PermissionCheckResult as PluginPermissionCheckResult, PluginPermissionEnforcer, PluginPermissionManager, type PluginPermissions, PluginSandboxRuntime, PluginSecurityScanner, type PluginSignatureConfig, PluginSignatureVerifier, type PluginStartupResult, type PostureEvidence, type PublisherVerifyResult, index as QA, type ResolveAuthzInput, type ResolveLocalizationInput, type ResolveUserAuthzGrantsOptions, type ResolvedAuthzContext, type ResourceUsage, type RetryOptions, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, type UserAuthzGrants, type VersionCompatibility, bucketKeyToCalendarRange, buildPermissionsFromGrants, bulkWrite, calendarPartsInTz, calendarPartsInTzOrUtc, counterSignPayload, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, deepMerge, defaultIsTransientError, derivePosture, evaluateAuthGate, extractApiKey, generateApiKey, generateEd25519KeyPair, getEnv, getMemoryUsage, hashApiKey, isAuthGateAllowlisted, isExpired, isGrantActive, isGrantExpired, isNode, parseScopes, parseSignature, postureVisibleRows, readAuthoredTranslationLayer, resolveApiKeyPrincipal, resolveAuthzContext, resolveLocale, resolveLocalizationContext, resolveUserAuthzGrants, safeExit, shouldDenyAnonymous, signPayload, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature, wireAuthoredTranslationSync, withTransientRetry, zonedDateStartToUtcMs };
|
|
2790
|
+
export { ANONYMOUS_DENY_BODY, ANONYMOUS_DENY_CODE, ANONYMOUS_DENY_MESSAGE, ANONYMOUS_DENY_STATUS, API_KEY_PREFIX, type AnonymousDenyInput, type ApiKeyPrincipal, ApiRegistry, type ApiRegistryPluginConfig, type AuthGate, type BucketGranularity, type BulkWriteOptions, type BulkWriteRowResult, CORE_FALLBACK_FACTORIES, type CalendarParts, DependencyResolver, type ExecutionContextLike, type FilterTokenResolutionContext, type GeneratedApiKey, type GrantValidityWindow, HotReloadManager, type KernelState, type KeyInput, type LadderPrincipal, type LadderRow, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, POSTURE_INJECTION_RULE, POSTURE_LADDER, POSTURE_RANK, type ParsedSignature, type PermissionCheckResult$1 as PermissionCheckResult, type PermissionGrant, type Plugin, type PluginArtifactVerifyResult, PluginConfigValidator, type PluginContext, PluginHealthMonitor, type PluginHealthStatus, type PluginLoadResult, PluginLoader, type PluginMetadata, type PermissionCheckResult as PluginPermissionCheckResult, PluginPermissionEnforcer, PluginPermissionManager, type PluginPermissions, PluginSandboxRuntime, PluginSecurityScanner, type PluginSignatureConfig, PluginSignatureVerifier, type PluginStartupResult, type PostureEvidence, type PublisherVerifyResult, index as QA, type ResolveAuthzInput, type ResolveLocalizationInput, type ResolveUserAuthzGrantsOptions, type ResolvedAuthzContext, type ResourceUsage, type RetryOptions, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, UnknownFilterTokenError, UnresolvedFilterTokenError, type UserAuthzGrants, type VersionCompatibility, bucketKeyToCalendarRange, buildPermissionsFromGrants, bulkWrite, calendarPartsInTz, calendarPartsInTzOrUtc, counterSignPayload, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, deepMerge, defaultIsTransientError, derivePosture, evaluateAuthGate, extractApiKey, filterTokenContextFrom, generateApiKey, generateEd25519KeyPair, getEnv, getMemoryUsage, hashApiKey, isAuthGateAllowlisted, isExpired, isGrantActive, isGrantExpired, isNode, parseScopes, parseSignature, postureVisibleRows, readAuthoredTranslationLayer, resolveApiKeyPrincipal, resolveAuthzContext, resolveFilterToken, resolveFilterTokens, resolveLocale, resolveLocalizationContext, resolveUserAuthzGrants, safeExit, shouldDenyAnonymous, signPayload, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature, wireAuthoredTranslationSync, withTransientRetry, zonedDateStartToUtcMs };
|
package/dist/index.js
CHANGED
|
@@ -1252,6 +1252,7 @@ function createMemoryMetadata() {
|
|
|
1252
1252
|
}
|
|
1253
1253
|
|
|
1254
1254
|
// src/fallbacks/authored-translation-sync.ts
|
|
1255
|
+
import { LEGACY_OBJECT_FIRST_KEYS } from "@objectstack/spec/system";
|
|
1255
1256
|
var OWNER_PROP = "__authoredTranslationSyncOwner";
|
|
1256
1257
|
var LOCALE_LIKE = /^[a-z]{2,3}([_-]([A-Za-z]{4}|[A-Za-z]{2}|[0-9]{3}))?$/;
|
|
1257
1258
|
async function readAuthoredTranslationLayer(engine, logger) {
|
|
@@ -1281,14 +1282,32 @@ async function readAuthoredTranslationLayer(engine, logger) {
|
|
|
1281
1282
|
continue;
|
|
1282
1283
|
}
|
|
1283
1284
|
if (!data || typeof data !== "object") continue;
|
|
1284
|
-
const
|
|
1285
|
+
const legacyKeys = LEGACY_OBJECT_FIRST_KEYS.filter((key) => data[key] !== void 0);
|
|
1286
|
+
if (legacyKeys.length > 0) {
|
|
1287
|
+
logger?.warn?.(
|
|
1288
|
+
`[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`
|
|
1289
|
+
);
|
|
1290
|
+
continue;
|
|
1291
|
+
}
|
|
1292
|
+
const locale = typeof data?.locale === "string" && data.locale || (typeof row?.name === "string" && LOCALE_LIKE.test(row.name) ? row.name : void 0) || void 0;
|
|
1285
1293
|
if (!locale) {
|
|
1286
1294
|
logger?.warn?.(
|
|
1287
|
-
`[i18n] authored translation '${row?.name}' has no resolvable locale (set
|
|
1295
|
+
`[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
1296
|
);
|
|
1289
1297
|
continue;
|
|
1290
1298
|
}
|
|
1291
|
-
const {
|
|
1299
|
+
const {
|
|
1300
|
+
name: _n,
|
|
1301
|
+
locale: _l,
|
|
1302
|
+
_packageId: _p,
|
|
1303
|
+
_packageVersion: _pv,
|
|
1304
|
+
_provenance: _pr,
|
|
1305
|
+
_lock: _lk,
|
|
1306
|
+
_lockReason: _lr,
|
|
1307
|
+
_lockDocsUrl: _ld,
|
|
1308
|
+
_lockSource: _ls,
|
|
1309
|
+
...payload
|
|
1310
|
+
} = data;
|
|
1292
1311
|
byLocale[locale] = deepMerge(byLocale[locale] ?? {}, payload);
|
|
1293
1312
|
}
|
|
1294
1313
|
return byLocale;
|
|
@@ -4212,7 +4231,7 @@ import {
|
|
|
4212
4231
|
mapMembershipRole,
|
|
4213
4232
|
BUILTIN_IDENTITY_PLATFORM_ADMIN,
|
|
4214
4233
|
ADMIN_FULL_ACCESS,
|
|
4215
|
-
|
|
4234
|
+
ORGANIZATION_ADMIN_GRANTS
|
|
4216
4235
|
} from "@objectstack/spec";
|
|
4217
4236
|
|
|
4218
4237
|
// src/security/grant-validity.ts
|
|
@@ -4320,7 +4339,8 @@ async function resolveAuthzContext(input) {
|
|
|
4320
4339
|
positions: [],
|
|
4321
4340
|
permissions: [],
|
|
4322
4341
|
systemPermissions: [],
|
|
4323
|
-
org_user_ids: []
|
|
4342
|
+
org_user_ids: [],
|
|
4343
|
+
accessible_org_ids: []
|
|
4324
4344
|
};
|
|
4325
4345
|
let userId;
|
|
4326
4346
|
let tenantId;
|
|
@@ -4356,6 +4376,7 @@ async function resolveAuthzContext(input) {
|
|
|
4356
4376
|
ctx.permissions = grants.permissions;
|
|
4357
4377
|
ctx.systemPermissions = grants.systemPermissions;
|
|
4358
4378
|
ctx.org_user_ids = grants.org_user_ids;
|
|
4379
|
+
ctx.accessible_org_ids = grants.accessible_org_ids;
|
|
4359
4380
|
if (grants.tabPermissions) ctx.tabPermissions = grants.tabPermissions;
|
|
4360
4381
|
if (grants.posture) ctx.posture = grants.posture;
|
|
4361
4382
|
if (grants.email && !ctx.email) ctx.email = grants.email;
|
|
@@ -4367,7 +4388,8 @@ async function resolveUserAuthzGrants(ql, userId, opts = {}) {
|
|
|
4367
4388
|
positions: [],
|
|
4368
4389
|
permissions: Array.isArray(opts.seedPermissions) ? [...opts.seedPermissions] : [],
|
|
4369
4390
|
systemPermissions: [],
|
|
4370
|
-
org_user_ids: [userId]
|
|
4391
|
+
org_user_ids: [userId],
|
|
4392
|
+
accessible_org_ids: []
|
|
4371
4393
|
};
|
|
4372
4394
|
if (opts.seedEmail) grants.email = opts.seedEmail;
|
|
4373
4395
|
if (!ql || typeof ql.find !== "function") return grants;
|
|
@@ -4385,9 +4407,17 @@ async function resolveUserAuthzGrants(ql, userId, opts = {}) {
|
|
|
4385
4407
|
const u = await getUserRow();
|
|
4386
4408
|
if (u?.email) grants.email = String(u.email);
|
|
4387
4409
|
}
|
|
4388
|
-
const
|
|
4389
|
-
const members = await tryFind(ql, "sys_member",
|
|
4410
|
+
const nowMs = opts.nowMs ?? Date.now();
|
|
4411
|
+
const members = await tryFind(ql, "sys_member", { user_id: userId }, 200);
|
|
4412
|
+
const accessibleOrgIds = /* @__PURE__ */ new Set();
|
|
4390
4413
|
for (const m of members) {
|
|
4414
|
+
if (!isGrantActive(m, nowMs)) continue;
|
|
4415
|
+
const org = m.organization_id ?? m.organizationId;
|
|
4416
|
+
if (typeof org === "string" && org) accessibleOrgIds.add(org);
|
|
4417
|
+
}
|
|
4418
|
+
grants.accessible_org_ids = Array.from(accessibleOrgIds);
|
|
4419
|
+
const activeMembers = tenantId ? members.filter((m) => (m.organization_id ?? m.organizationId) === tenantId) : members;
|
|
4420
|
+
for (const m of activeMembers) {
|
|
4391
4421
|
if (m.role && typeof m.role === "string") {
|
|
4392
4422
|
for (const raw of m.role.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
4393
4423
|
const r = mapMembershipRole(raw);
|
|
@@ -4395,7 +4425,6 @@ async function resolveUserAuthzGrants(ql, userId, opts = {}) {
|
|
|
4395
4425
|
}
|
|
4396
4426
|
}
|
|
4397
4427
|
}
|
|
4398
|
-
const nowMs = opts.nowMs ?? Date.now();
|
|
4399
4428
|
const userPositionRows = await tryFind(ql, "sys_user_position", { user_id: userId }, 200);
|
|
4400
4429
|
for (const ur of userPositionRows) {
|
|
4401
4430
|
const org = ur.organization_id ?? null;
|
|
@@ -4467,7 +4496,9 @@ async function resolveUserAuthzGrants(ql, userId, opts = {}) {
|
|
|
4467
4496
|
}
|
|
4468
4497
|
grants.posture = derivePosture({
|
|
4469
4498
|
isPlatformAdmin: hasPlatformAdminGrant,
|
|
4470
|
-
|
|
4499
|
+
// [ADR-0105 D4] Either org-admin capability set resolves the rung — the
|
|
4500
|
+
// wall-less variant differs only by withholding the superuser bits.
|
|
4501
|
+
isTenantAdmin: ORGANIZATION_ADMIN_GRANTS.some((n) => grants.permissions.includes(n))
|
|
4471
4502
|
});
|
|
4472
4503
|
if (!grants.permissions.includes("ai_seat")) {
|
|
4473
4504
|
const aiAccess = (await getUserRow())?.ai_access;
|
|
@@ -4598,8 +4629,8 @@ function calendarPartsInTzOrUtc(d, tz) {
|
|
|
4598
4629
|
day: d.getUTCDate()
|
|
4599
4630
|
};
|
|
4600
4631
|
}
|
|
4601
|
-
function zonedDateStartToUtcMs(
|
|
4602
|
-
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(
|
|
4632
|
+
function zonedDateStartToUtcMs(ymd2, tz) {
|
|
4633
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(ymd2);
|
|
4603
4634
|
const wallAsUtc = m ? Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3])) : NaN;
|
|
4604
4635
|
if (!tz || tz === "UTC" || Number.isNaN(wallAsUtc)) return wallAsUtc;
|
|
4605
4636
|
try {
|
|
@@ -4815,6 +4846,212 @@ async function bulkWrite(rows, opts) {
|
|
|
4815
4846
|
return results;
|
|
4816
4847
|
}
|
|
4817
4848
|
|
|
4849
|
+
// src/utils/filter-tokens.ts
|
|
4850
|
+
import {
|
|
4851
|
+
classifyFilterToken,
|
|
4852
|
+
parseDateMacroParam
|
|
4853
|
+
} from "@objectstack/spec/data";
|
|
4854
|
+
var UnknownFilterTokenError = class extends Error {
|
|
4855
|
+
constructor(token, suggestion) {
|
|
4856
|
+
super(
|
|
4857
|
+
`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."
|
|
4858
|
+
);
|
|
4859
|
+
this.status = 400;
|
|
4860
|
+
this.code = "FILTER_TOKEN_UNKNOWN";
|
|
4861
|
+
this.name = "UnknownFilterTokenError";
|
|
4862
|
+
this.token = token;
|
|
4863
|
+
this.suggestion = suggestion;
|
|
4864
|
+
}
|
|
4865
|
+
};
|
|
4866
|
+
var UnresolvedFilterTokenError = class extends Error {
|
|
4867
|
+
constructor(token, detail) {
|
|
4868
|
+
super(`Filter placeholder "{${token}}" cannot be resolved: ${detail}`);
|
|
4869
|
+
/** 400, not 500 — see {@link UnknownFilterTokenError}. */
|
|
4870
|
+
this.status = 400;
|
|
4871
|
+
this.code = "FILTER_TOKEN_UNRESOLVED";
|
|
4872
|
+
this.name = "UnresolvedFilterTokenError";
|
|
4873
|
+
this.token = token;
|
|
4874
|
+
}
|
|
4875
|
+
};
|
|
4876
|
+
function ymd(year, month, day) {
|
|
4877
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
4878
|
+
return `${year}-${p(month)}-${p(day)}`;
|
|
4879
|
+
}
|
|
4880
|
+
function proxyDay(now, timezone) {
|
|
4881
|
+
const { year, month, day } = calendarPartsInTzOrUtc(now, timezone);
|
|
4882
|
+
return new Date(Date.UTC(year, month - 1, day));
|
|
4883
|
+
}
|
|
4884
|
+
var asYmd = (d) => ymd(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
|
|
4885
|
+
function startOfPeriod(kind, d) {
|
|
4886
|
+
const r = new Date(d.getTime());
|
|
4887
|
+
switch (kind) {
|
|
4888
|
+
case "week": {
|
|
4889
|
+
const dow = (r.getUTCDay() + 6) % 7;
|
|
4890
|
+
r.setUTCDate(r.getUTCDate() - dow);
|
|
4891
|
+
return r;
|
|
4892
|
+
}
|
|
4893
|
+
case "month":
|
|
4894
|
+
return new Date(Date.UTC(r.getUTCFullYear(), r.getUTCMonth(), 1));
|
|
4895
|
+
case "quarter":
|
|
4896
|
+
return new Date(Date.UTC(r.getUTCFullYear(), Math.floor(r.getUTCMonth() / 3) * 3, 1));
|
|
4897
|
+
case "year":
|
|
4898
|
+
return new Date(Date.UTC(r.getUTCFullYear(), 0, 1));
|
|
4899
|
+
}
|
|
4900
|
+
}
|
|
4901
|
+
function daysInMonth(year, month) {
|
|
4902
|
+
return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
|
|
4903
|
+
}
|
|
4904
|
+
function addMonthsClamped(d, n) {
|
|
4905
|
+
const year = d.getUTCFullYear();
|
|
4906
|
+
const month = d.getUTCMonth() + n;
|
|
4907
|
+
const targetYear = year + Math.floor(month / 12);
|
|
4908
|
+
const targetMonth = (month % 12 + 12) % 12;
|
|
4909
|
+
const day = Math.min(d.getUTCDate(), daysInMonth(targetYear, targetMonth));
|
|
4910
|
+
return new Date(Date.UTC(
|
|
4911
|
+
targetYear,
|
|
4912
|
+
targetMonth,
|
|
4913
|
+
day,
|
|
4914
|
+
d.getUTCHours(),
|
|
4915
|
+
d.getUTCMinutes(),
|
|
4916
|
+
d.getUTCSeconds(),
|
|
4917
|
+
d.getUTCMilliseconds()
|
|
4918
|
+
));
|
|
4919
|
+
}
|
|
4920
|
+
function addPeriods(kind, d, n) {
|
|
4921
|
+
switch (kind) {
|
|
4922
|
+
case "week": {
|
|
4923
|
+
const r = new Date(d.getTime());
|
|
4924
|
+
r.setUTCDate(r.getUTCDate() + n * 7);
|
|
4925
|
+
return r;
|
|
4926
|
+
}
|
|
4927
|
+
case "month":
|
|
4928
|
+
return addMonthsClamped(d, n);
|
|
4929
|
+
case "quarter":
|
|
4930
|
+
return addMonthsClamped(d, n * 3);
|
|
4931
|
+
case "year":
|
|
4932
|
+
return addMonthsClamped(d, n * 12);
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
function addUnits(unit, d, n) {
|
|
4936
|
+
const r = new Date(d.getTime());
|
|
4937
|
+
switch (unit) {
|
|
4938
|
+
case "minute":
|
|
4939
|
+
r.setUTCMinutes(r.getUTCMinutes() + n);
|
|
4940
|
+
return r;
|
|
4941
|
+
case "hour":
|
|
4942
|
+
r.setUTCHours(r.getUTCHours() + n);
|
|
4943
|
+
return r;
|
|
4944
|
+
case "day":
|
|
4945
|
+
r.setUTCDate(r.getUTCDate() + n);
|
|
4946
|
+
return r;
|
|
4947
|
+
case "week":
|
|
4948
|
+
r.setUTCDate(r.getUTCDate() + n * 7);
|
|
4949
|
+
return r;
|
|
4950
|
+
// Month/year steps clamp rather than overflow — see addMonthsClamped.
|
|
4951
|
+
case "month":
|
|
4952
|
+
return addMonthsClamped(d, n);
|
|
4953
|
+
case "year":
|
|
4954
|
+
return addMonthsClamped(d, n * 12);
|
|
4955
|
+
}
|
|
4956
|
+
}
|
|
4957
|
+
var PERIOD_RE = /^(?:(current|last|next)_)?(week|month|quarter|year)_(start|end)$/;
|
|
4958
|
+
function resolvePeriodToken(token, today) {
|
|
4959
|
+
const m = PERIOD_RE.exec(token);
|
|
4960
|
+
if (!m) return void 0;
|
|
4961
|
+
const rel = m[1] ?? "current";
|
|
4962
|
+
const kind = m[2];
|
|
4963
|
+
const bound = m[3];
|
|
4964
|
+
const offset = rel === "last" ? -1 : rel === "next" ? 1 : 0;
|
|
4965
|
+
const periodStart = startOfPeriod(kind, addPeriods(kind, startOfPeriod(kind, today), offset));
|
|
4966
|
+
if (bound === "start") return asYmd(periodStart);
|
|
4967
|
+
const next = addPeriods(kind, periodStart, 1);
|
|
4968
|
+
next.setUTCDate(next.getUTCDate() - 1);
|
|
4969
|
+
return asYmd(next);
|
|
4970
|
+
}
|
|
4971
|
+
function resolveFilterToken(token, ctx = {}) {
|
|
4972
|
+
const now = ctx.now ?? /* @__PURE__ */ new Date();
|
|
4973
|
+
if (token === "current_user_id") {
|
|
4974
|
+
if (!ctx.userId) {
|
|
4975
|
+
throw new UnresolvedFilterTokenError(
|
|
4976
|
+
token,
|
|
4977
|
+
"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."
|
|
4978
|
+
);
|
|
4979
|
+
}
|
|
4980
|
+
return ctx.userId;
|
|
4981
|
+
}
|
|
4982
|
+
if (token === "current_org_id") {
|
|
4983
|
+
if (!ctx.orgId) {
|
|
4984
|
+
throw new UnresolvedFilterTokenError(
|
|
4985
|
+
token,
|
|
4986
|
+
"the request carries no active organization (ExecutionContext.tenantId is unset). Set the active org on the request, or drop the token from the filter."
|
|
4987
|
+
);
|
|
4988
|
+
}
|
|
4989
|
+
return ctx.orgId;
|
|
4990
|
+
}
|
|
4991
|
+
const today = proxyDay(now, ctx.timezone);
|
|
4992
|
+
switch (token) {
|
|
4993
|
+
case "now":
|
|
4994
|
+
return now.toISOString();
|
|
4995
|
+
case "today":
|
|
4996
|
+
return asYmd(today);
|
|
4997
|
+
case "yesterday":
|
|
4998
|
+
return asYmd(addUnits("day", today, -1));
|
|
4999
|
+
case "tomorrow":
|
|
5000
|
+
return asYmd(addUnits("day", today, 1));
|
|
5001
|
+
}
|
|
5002
|
+
const period = resolvePeriodToken(token, today);
|
|
5003
|
+
if (period !== void 0) return period;
|
|
5004
|
+
const param = parseDateMacroParam(token);
|
|
5005
|
+
if (param) {
|
|
5006
|
+
const sign = param.direction === "ago" ? -1 : 1;
|
|
5007
|
+
if (param.unit === "minute" || param.unit === "hour") {
|
|
5008
|
+
return addUnits(param.unit, now, sign * param.n).toISOString();
|
|
5009
|
+
}
|
|
5010
|
+
return asYmd(addUnits(param.unit, today, sign * param.n));
|
|
5011
|
+
}
|
|
5012
|
+
return void 0;
|
|
5013
|
+
}
|
|
5014
|
+
function hasFilterToken(node) {
|
|
5015
|
+
if (typeof node === "string") return classifyFilterToken(node) !== null;
|
|
5016
|
+
if (Array.isArray(node)) return node.some(hasFilterToken);
|
|
5017
|
+
if (node && typeof node === "object" && !(node instanceof Date)) {
|
|
5018
|
+
return Object.values(node).some(hasFilterToken);
|
|
5019
|
+
}
|
|
5020
|
+
return false;
|
|
5021
|
+
}
|
|
5022
|
+
function resolveFilterTokens(filter, ctx = {}) {
|
|
5023
|
+
if (filter == null) return filter;
|
|
5024
|
+
if (!hasFilterToken(filter)) return filter;
|
|
5025
|
+
const pinned = { ...ctx, now: ctx.now ?? /* @__PURE__ */ new Date() };
|
|
5026
|
+
const walk = (node) => {
|
|
5027
|
+
if (typeof node === "string") {
|
|
5028
|
+
const cls = classifyFilterToken(node);
|
|
5029
|
+
if (!cls) return node;
|
|
5030
|
+
if (cls.kind === "unknown") throw new UnknownFilterTokenError(cls.token, cls.suggestion);
|
|
5031
|
+
const resolved = resolveFilterToken(cls.token, pinned);
|
|
5032
|
+
if (resolved === void 0) throw new UnknownFilterTokenError(cls.token);
|
|
5033
|
+
return resolved;
|
|
5034
|
+
}
|
|
5035
|
+
if (Array.isArray(node)) return node.map(walk);
|
|
5036
|
+
if (node && typeof node === "object") {
|
|
5037
|
+
if (node instanceof Date) return node;
|
|
5038
|
+
const out = {};
|
|
5039
|
+
for (const [k, v] of Object.entries(node)) out[k] = walk(v);
|
|
5040
|
+
return out;
|
|
5041
|
+
}
|
|
5042
|
+
return node;
|
|
5043
|
+
};
|
|
5044
|
+
return walk(filter);
|
|
5045
|
+
}
|
|
5046
|
+
function filterTokenContextFrom(execCtx, now) {
|
|
5047
|
+
return {
|
|
5048
|
+
now,
|
|
5049
|
+
timezone: execCtx?.timezone,
|
|
5050
|
+
userId: execCtx?.userId,
|
|
5051
|
+
orgId: execCtx?.tenantId
|
|
5052
|
+
};
|
|
5053
|
+
}
|
|
5054
|
+
|
|
4818
5055
|
// src/health-monitor.ts
|
|
4819
5056
|
var PluginHealthMonitor = class {
|
|
4820
5057
|
constructor(logger) {
|
|
@@ -5759,6 +5996,8 @@ export {
|
|
|
5759
5996
|
SecurePluginContext,
|
|
5760
5997
|
SemanticVersionManager,
|
|
5761
5998
|
ServiceLifecycle,
|
|
5999
|
+
UnknownFilterTokenError,
|
|
6000
|
+
UnresolvedFilterTokenError,
|
|
5762
6001
|
bucketKeyToCalendarRange,
|
|
5763
6002
|
buildPermissionsFromGrants,
|
|
5764
6003
|
bulkWrite,
|
|
@@ -5779,6 +6018,7 @@ export {
|
|
|
5779
6018
|
derivePosture,
|
|
5780
6019
|
evaluateAuthGate,
|
|
5781
6020
|
extractApiKey,
|
|
6021
|
+
filterTokenContextFrom,
|
|
5782
6022
|
generateApiKey,
|
|
5783
6023
|
generateEd25519KeyPair,
|
|
5784
6024
|
getEnv,
|
|
@@ -5795,6 +6035,8 @@ export {
|
|
|
5795
6035
|
readAuthoredTranslationLayer,
|
|
5796
6036
|
resolveApiKeyPrincipal,
|
|
5797
6037
|
resolveAuthzContext,
|
|
6038
|
+
resolveFilterToken,
|
|
6039
|
+
resolveFilterTokens,
|
|
5798
6040
|
resolveLocale,
|
|
5799
6041
|
resolveLocalizationContext,
|
|
5800
6042
|
resolveUserAuthzGrants,
|