@objectstack/core 12.5.0 → 13.0.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 +103 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -3
- package/dist/index.d.ts +80 -3
- package/dist/index.js +100 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1733,7 +1733,7 @@ interface ResolvedAuthzContext {
|
|
|
1733
1733
|
tenantId?: string;
|
|
1734
1734
|
email?: string;
|
|
1735
1735
|
accessToken?: string;
|
|
1736
|
-
|
|
1736
|
+
positions: string[];
|
|
1737
1737
|
permissions: string[];
|
|
1738
1738
|
systemPermissions: string[];
|
|
1739
1739
|
tabPermissions?: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'>;
|
|
@@ -1756,7 +1756,7 @@ interface ResolveAuthzInput {
|
|
|
1756
1756
|
}
|
|
1757
1757
|
/**
|
|
1758
1758
|
* Resolve the authorization context for an inbound request. Always resolves —
|
|
1759
|
-
* never throws. Anonymous requests yield `{
|
|
1759
|
+
* never throws. Anonymous requests yield `{ positions: [], permissions: [], ... }`.
|
|
1760
1760
|
*/
|
|
1761
1761
|
declare function resolveAuthzContext(input: ResolveAuthzInput): Promise<ResolvedAuthzContext>;
|
|
1762
1762
|
interface ResolveLocalizationInput {
|
|
@@ -1862,6 +1862,83 @@ declare function calendarPartsInTz(d: Date, tz: string): CalendarParts;
|
|
|
1862
1862
|
*/
|
|
1863
1863
|
declare function calendarPartsInTzOrUtc(d: Date, tz?: string): CalendarParts;
|
|
1864
1864
|
|
|
1865
|
+
/**
|
|
1866
|
+
* `bulkWrite` — the shared batched-write helper used by BOTH the seed loader
|
|
1867
|
+
* (`@objectstack/metadata-protocol`) and the data-import runner
|
|
1868
|
+
* (`@objectstack/rest`), so neither reimplements batching, transient-error
|
|
1869
|
+
* retry, or per-row degradation. See framework#2678.
|
|
1870
|
+
*
|
|
1871
|
+
* ObjectQL's engine already does the efficient thing when handed an ARRAY —
|
|
1872
|
+
* one `driver.bulkCreate` round-trip plus parent-deduplicated summary
|
|
1873
|
+
* recompute (`engine.insert(object, rows[])`) — but seed/import fed it one
|
|
1874
|
+
* record at a time, so neither got the benefit. This module re-chunks rows
|
|
1875
|
+
* into batches and drives them through a caller-supplied batch-write
|
|
1876
|
+
* function, adding:
|
|
1877
|
+
*
|
|
1878
|
+
* - transient-error retry (network blip / timeout) with exponential
|
|
1879
|
+
* backoff, so a dropped connection doesn't silently drop the row (the
|
|
1880
|
+
* 2026-07-06 HotCRM incident: a turso `fetch failed` mid-seed dropped rows
|
|
1881
|
+
* silently because nothing retried);
|
|
1882
|
+
* - per-row degradation when a batch fails for a non-transient (logical /
|
|
1883
|
+
* validation) reason, so one bad row can't fail the other N-1 — needed
|
|
1884
|
+
* because `driver.bulkCreate` is a single multi-row statement/`Promise.all`
|
|
1885
|
+
* on every driver in this repo (sql, memory, mongodb): one bad row fails
|
|
1886
|
+
* the whole call;
|
|
1887
|
+
* - a stable per-row result keyed by the row's original index, so callers
|
|
1888
|
+
* can reassemble output in input order even though rows are processed in
|
|
1889
|
+
* batches (and a batch's flush may be interleaved with other, immediate,
|
|
1890
|
+
* per-row work such as updates).
|
|
1891
|
+
*/
|
|
1892
|
+
interface BulkWriteRowResult<TRecord = any> {
|
|
1893
|
+
/** Index into the original `rows` array passed to {@link bulkWrite}. */
|
|
1894
|
+
index: number;
|
|
1895
|
+
ok: boolean;
|
|
1896
|
+
record?: TRecord;
|
|
1897
|
+
error?: unknown;
|
|
1898
|
+
}
|
|
1899
|
+
interface RetryOptions {
|
|
1900
|
+
/** Max attempts for one write (batch or single-row), including the first. Default 3. */
|
|
1901
|
+
maxRetries?: number;
|
|
1902
|
+
/** Base backoff in ms; doubled each retry, plus jitter. Default 200. */
|
|
1903
|
+
backoffBaseMs?: number;
|
|
1904
|
+
/** Classifies an error as transient (worth retrying) vs logical (the row/batch is just bad). */
|
|
1905
|
+
isTransientError?: (err: unknown) => boolean;
|
|
1906
|
+
/** Injectable sleep, for deterministic tests. */
|
|
1907
|
+
sleep?: (ms: number) => Promise<void>;
|
|
1908
|
+
}
|
|
1909
|
+
interface BulkWriteOptions<TRow, TRecord = any> extends RetryOptions {
|
|
1910
|
+
/** Rows per batch. Default 200 (framework#2678 suggests 100-500). */
|
|
1911
|
+
batchSize?: number;
|
|
1912
|
+
/**
|
|
1913
|
+
* Write one batch. MUST resolve to one record per input row, in the SAME
|
|
1914
|
+
* order as `batch` — {@link bulkWrite} correlates `records[i]` back to
|
|
1915
|
+
* `batch[i]` positionally (this is how every `bulkCreate` implementation in
|
|
1916
|
+
* this repo already behaves: sql's single `INSERT ... VALUES (...), (...)
|
|
1917
|
+
* RETURNING *`, memory's `Promise.all`, mongodb's ordered `insertMany`).
|
|
1918
|
+
*/
|
|
1919
|
+
writeBatch: (batch: TRow[]) => Promise<TRecord[]>;
|
|
1920
|
+
/** Write a single row — used only to degrade a failed batch. */
|
|
1921
|
+
writeOne: (row: TRow) => Promise<TRecord>;
|
|
1922
|
+
}
|
|
1923
|
+
declare function defaultIsTransientError(err: unknown): boolean;
|
|
1924
|
+
/**
|
|
1925
|
+
* Retry a single write (e.g. an `engine.update()` call the seed loader or
|
|
1926
|
+
* import runner makes outside the batched-insert path) with the same
|
|
1927
|
+
* transient-error backoff {@link bulkWrite} applies to batches — so a
|
|
1928
|
+
* network blip doesn't drop an update the way it used to drop an insert.
|
|
1929
|
+
*/
|
|
1930
|
+
declare function withTransientRetry<T>(fn: () => Promise<T>, opts?: RetryOptions): Promise<T>;
|
|
1931
|
+
/**
|
|
1932
|
+
* Write `rows` through `opts.writeBatch` in chunks of `opts.batchSize`,
|
|
1933
|
+
* retrying a whole-batch transient failure with backoff, and degrading to
|
|
1934
|
+
* per-row `opts.writeOne` calls (each itself retried) when a batch fails for
|
|
1935
|
+
* a non-transient reason — so one bad row can't drop the rest of the batch.
|
|
1936
|
+
*
|
|
1937
|
+
* Returns one {@link BulkWriteRowResult} per input row, indexed to match
|
|
1938
|
+
* `rows`' original order.
|
|
1939
|
+
*/
|
|
1940
|
+
declare function bulkWrite<TRow, TRecord = any>(rows: TRow[], opts: BulkWriteOptions<TRow, TRecord>): Promise<BulkWriteRowResult<TRecord>[]>;
|
|
1941
|
+
|
|
1865
1942
|
/**
|
|
1866
1943
|
* In-memory Map-backed cache fallback.
|
|
1867
1944
|
*
|
|
@@ -2298,4 +2375,4 @@ declare class NamespaceResolver {
|
|
|
2298
2375
|
private suggestAlternative;
|
|
2299
2376
|
}
|
|
2300
2377
|
|
|
2301
|
-
export { API_KEY_PREFIX, type ApiKeyPrincipal, ApiRegistry, type ApiRegistryPluginConfig, type AuthGate, CORE_FALLBACK_FACTORIES, type CalendarParts, DependencyResolver, type GeneratedApiKey, HotReloadManager, type KernelState, type KeyInput, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, 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 PublisherVerifyResult, index as QA, type ResolveAuthzInput, type ResolveLocalizationInput, type ResolvedAuthzContext, type ResourceUsage, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, type VersionCompatibility, buildPermissionsFromGrants, calendarPartsInTz, calendarPartsInTzOrUtc, counterSignPayload, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, deepMerge, evaluateAuthGate, extractApiKey, generateApiKey, generateEd25519KeyPair, getEnv, getMemoryUsage, hashApiKey, isAuthGateAllowlisted, isExpired, isNode, parseScopes, parseSignature, readAuthoredTranslationLayer, resolveApiKeyPrincipal, resolveAuthzContext, resolveLocale, resolveLocalizationContext, safeExit, signPayload, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature, wireAuthoredTranslationSync };
|
|
2378
|
+
export { API_KEY_PREFIX, type ApiKeyPrincipal, ApiRegistry, type ApiRegistryPluginConfig, type AuthGate, type BulkWriteOptions, type BulkWriteRowResult, CORE_FALLBACK_FACTORIES, type CalendarParts, DependencyResolver, type GeneratedApiKey, HotReloadManager, type KernelState, type KeyInput, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, 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 PublisherVerifyResult, index as QA, type ResolveAuthzInput, type ResolveLocalizationInput, type ResolvedAuthzContext, type ResourceUsage, type RetryOptions, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, type VersionCompatibility, buildPermissionsFromGrants, bulkWrite, calendarPartsInTz, calendarPartsInTzOrUtc, counterSignPayload, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, deepMerge, defaultIsTransientError, evaluateAuthGate, extractApiKey, generateApiKey, generateEd25519KeyPair, getEnv, getMemoryUsage, hashApiKey, isAuthGateAllowlisted, isExpired, isNode, parseScopes, parseSignature, readAuthoredTranslationLayer, resolveApiKeyPrincipal, resolveAuthzContext, resolveLocale, resolveLocalizationContext, safeExit, signPayload, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature, wireAuthoredTranslationSync, withTransientRetry };
|
package/dist/index.d.ts
CHANGED
|
@@ -1733,7 +1733,7 @@ interface ResolvedAuthzContext {
|
|
|
1733
1733
|
tenantId?: string;
|
|
1734
1734
|
email?: string;
|
|
1735
1735
|
accessToken?: string;
|
|
1736
|
-
|
|
1736
|
+
positions: string[];
|
|
1737
1737
|
permissions: string[];
|
|
1738
1738
|
systemPermissions: string[];
|
|
1739
1739
|
tabPermissions?: Record<string, 'visible' | 'hidden' | 'default_on' | 'default_off'>;
|
|
@@ -1756,7 +1756,7 @@ interface ResolveAuthzInput {
|
|
|
1756
1756
|
}
|
|
1757
1757
|
/**
|
|
1758
1758
|
* Resolve the authorization context for an inbound request. Always resolves —
|
|
1759
|
-
* never throws. Anonymous requests yield `{
|
|
1759
|
+
* never throws. Anonymous requests yield `{ positions: [], permissions: [], ... }`.
|
|
1760
1760
|
*/
|
|
1761
1761
|
declare function resolveAuthzContext(input: ResolveAuthzInput): Promise<ResolvedAuthzContext>;
|
|
1762
1762
|
interface ResolveLocalizationInput {
|
|
@@ -1862,6 +1862,83 @@ declare function calendarPartsInTz(d: Date, tz: string): CalendarParts;
|
|
|
1862
1862
|
*/
|
|
1863
1863
|
declare function calendarPartsInTzOrUtc(d: Date, tz?: string): CalendarParts;
|
|
1864
1864
|
|
|
1865
|
+
/**
|
|
1866
|
+
* `bulkWrite` — the shared batched-write helper used by BOTH the seed loader
|
|
1867
|
+
* (`@objectstack/metadata-protocol`) and the data-import runner
|
|
1868
|
+
* (`@objectstack/rest`), so neither reimplements batching, transient-error
|
|
1869
|
+
* retry, or per-row degradation. See framework#2678.
|
|
1870
|
+
*
|
|
1871
|
+
* ObjectQL's engine already does the efficient thing when handed an ARRAY —
|
|
1872
|
+
* one `driver.bulkCreate` round-trip plus parent-deduplicated summary
|
|
1873
|
+
* recompute (`engine.insert(object, rows[])`) — but seed/import fed it one
|
|
1874
|
+
* record at a time, so neither got the benefit. This module re-chunks rows
|
|
1875
|
+
* into batches and drives them through a caller-supplied batch-write
|
|
1876
|
+
* function, adding:
|
|
1877
|
+
*
|
|
1878
|
+
* - transient-error retry (network blip / timeout) with exponential
|
|
1879
|
+
* backoff, so a dropped connection doesn't silently drop the row (the
|
|
1880
|
+
* 2026-07-06 HotCRM incident: a turso `fetch failed` mid-seed dropped rows
|
|
1881
|
+
* silently because nothing retried);
|
|
1882
|
+
* - per-row degradation when a batch fails for a non-transient (logical /
|
|
1883
|
+
* validation) reason, so one bad row can't fail the other N-1 — needed
|
|
1884
|
+
* because `driver.bulkCreate` is a single multi-row statement/`Promise.all`
|
|
1885
|
+
* on every driver in this repo (sql, memory, mongodb): one bad row fails
|
|
1886
|
+
* the whole call;
|
|
1887
|
+
* - a stable per-row result keyed by the row's original index, so callers
|
|
1888
|
+
* can reassemble output in input order even though rows are processed in
|
|
1889
|
+
* batches (and a batch's flush may be interleaved with other, immediate,
|
|
1890
|
+
* per-row work such as updates).
|
|
1891
|
+
*/
|
|
1892
|
+
interface BulkWriteRowResult<TRecord = any> {
|
|
1893
|
+
/** Index into the original `rows` array passed to {@link bulkWrite}. */
|
|
1894
|
+
index: number;
|
|
1895
|
+
ok: boolean;
|
|
1896
|
+
record?: TRecord;
|
|
1897
|
+
error?: unknown;
|
|
1898
|
+
}
|
|
1899
|
+
interface RetryOptions {
|
|
1900
|
+
/** Max attempts for one write (batch or single-row), including the first. Default 3. */
|
|
1901
|
+
maxRetries?: number;
|
|
1902
|
+
/** Base backoff in ms; doubled each retry, plus jitter. Default 200. */
|
|
1903
|
+
backoffBaseMs?: number;
|
|
1904
|
+
/** Classifies an error as transient (worth retrying) vs logical (the row/batch is just bad). */
|
|
1905
|
+
isTransientError?: (err: unknown) => boolean;
|
|
1906
|
+
/** Injectable sleep, for deterministic tests. */
|
|
1907
|
+
sleep?: (ms: number) => Promise<void>;
|
|
1908
|
+
}
|
|
1909
|
+
interface BulkWriteOptions<TRow, TRecord = any> extends RetryOptions {
|
|
1910
|
+
/** Rows per batch. Default 200 (framework#2678 suggests 100-500). */
|
|
1911
|
+
batchSize?: number;
|
|
1912
|
+
/**
|
|
1913
|
+
* Write one batch. MUST resolve to one record per input row, in the SAME
|
|
1914
|
+
* order as `batch` — {@link bulkWrite} correlates `records[i]` back to
|
|
1915
|
+
* `batch[i]` positionally (this is how every `bulkCreate` implementation in
|
|
1916
|
+
* this repo already behaves: sql's single `INSERT ... VALUES (...), (...)
|
|
1917
|
+
* RETURNING *`, memory's `Promise.all`, mongodb's ordered `insertMany`).
|
|
1918
|
+
*/
|
|
1919
|
+
writeBatch: (batch: TRow[]) => Promise<TRecord[]>;
|
|
1920
|
+
/** Write a single row — used only to degrade a failed batch. */
|
|
1921
|
+
writeOne: (row: TRow) => Promise<TRecord>;
|
|
1922
|
+
}
|
|
1923
|
+
declare function defaultIsTransientError(err: unknown): boolean;
|
|
1924
|
+
/**
|
|
1925
|
+
* Retry a single write (e.g. an `engine.update()` call the seed loader or
|
|
1926
|
+
* import runner makes outside the batched-insert path) with the same
|
|
1927
|
+
* transient-error backoff {@link bulkWrite} applies to batches — so a
|
|
1928
|
+
* network blip doesn't drop an update the way it used to drop an insert.
|
|
1929
|
+
*/
|
|
1930
|
+
declare function withTransientRetry<T>(fn: () => Promise<T>, opts?: RetryOptions): Promise<T>;
|
|
1931
|
+
/**
|
|
1932
|
+
* Write `rows` through `opts.writeBatch` in chunks of `opts.batchSize`,
|
|
1933
|
+
* retrying a whole-batch transient failure with backoff, and degrading to
|
|
1934
|
+
* per-row `opts.writeOne` calls (each itself retried) when a batch fails for
|
|
1935
|
+
* a non-transient reason — so one bad row can't drop the rest of the batch.
|
|
1936
|
+
*
|
|
1937
|
+
* Returns one {@link BulkWriteRowResult} per input row, indexed to match
|
|
1938
|
+
* `rows`' original order.
|
|
1939
|
+
*/
|
|
1940
|
+
declare function bulkWrite<TRow, TRecord = any>(rows: TRow[], opts: BulkWriteOptions<TRow, TRecord>): Promise<BulkWriteRowResult<TRecord>[]>;
|
|
1941
|
+
|
|
1865
1942
|
/**
|
|
1866
1943
|
* In-memory Map-backed cache fallback.
|
|
1867
1944
|
*
|
|
@@ -2298,4 +2375,4 @@ declare class NamespaceResolver {
|
|
|
2298
2375
|
private suggestAlternative;
|
|
2299
2376
|
}
|
|
2300
2377
|
|
|
2301
|
-
export { API_KEY_PREFIX, type ApiKeyPrincipal, ApiRegistry, type ApiRegistryPluginConfig, type AuthGate, CORE_FALLBACK_FACTORIES, type CalendarParts, DependencyResolver, type GeneratedApiKey, HotReloadManager, type KernelState, type KeyInput, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, 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 PublisherVerifyResult, index as QA, type ResolveAuthzInput, type ResolveLocalizationInput, type ResolvedAuthzContext, type ResourceUsage, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, type VersionCompatibility, buildPermissionsFromGrants, calendarPartsInTz, calendarPartsInTzOrUtc, counterSignPayload, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, deepMerge, evaluateAuthGate, extractApiKey, generateApiKey, generateEd25519KeyPair, getEnv, getMemoryUsage, hashApiKey, isAuthGateAllowlisted, isExpired, isNode, parseScopes, parseSignature, readAuthoredTranslationLayer, resolveApiKeyPrincipal, resolveAuthzContext, resolveLocale, resolveLocalizationContext, safeExit, signPayload, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature, wireAuthoredTranslationSync };
|
|
2378
|
+
export { API_KEY_PREFIX, type ApiKeyPrincipal, ApiRegistry, type ApiRegistryPluginConfig, type AuthGate, type BulkWriteOptions, type BulkWriteRowResult, CORE_FALLBACK_FACTORIES, type CalendarParts, DependencyResolver, type GeneratedApiKey, HotReloadManager, type KernelState, type KeyInput, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, 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 PublisherVerifyResult, index as QA, type ResolveAuthzInput, type ResolveLocalizationInput, type ResolvedAuthzContext, type ResourceUsage, type RetryOptions, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, type VersionCompatibility, buildPermissionsFromGrants, bulkWrite, calendarPartsInTz, calendarPartsInTzOrUtc, counterSignPayload, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, deepMerge, defaultIsTransientError, evaluateAuthGate, extractApiKey, generateApiKey, generateEd25519KeyPair, getEnv, getMemoryUsage, hashApiKey, isAuthGateAllowlisted, isExpired, isNode, parseScopes, parseSignature, readAuthoredTranslationLayer, resolveApiKeyPrincipal, resolveAuthzContext, resolveLocale, resolveLocalizationContext, safeExit, signPayload, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature, wireAuthoredTranslationSync, withTransientRetry };
|
package/dist/index.js
CHANGED
|
@@ -4135,7 +4135,7 @@ function safeJsonParse(s, fallback) {
|
|
|
4135
4135
|
// src/security/resolve-authz-context.ts
|
|
4136
4136
|
import {
|
|
4137
4137
|
mapMembershipRole,
|
|
4138
|
-
|
|
4138
|
+
BUILTIN_IDENTITY_PLATFORM_ADMIN,
|
|
4139
4139
|
ADMIN_FULL_ACCESS
|
|
4140
4140
|
} from "@objectstack/spec";
|
|
4141
4141
|
function safeJsonParse2(s, fallback) {
|
|
@@ -4158,7 +4158,7 @@ async function tryFind(ql, object, where, limit = 100) {
|
|
|
4158
4158
|
async function resolveAuthzContext(input) {
|
|
4159
4159
|
const { ql, headers } = input;
|
|
4160
4160
|
const ctx = {
|
|
4161
|
-
|
|
4161
|
+
positions: [],
|
|
4162
4162
|
permissions: [],
|
|
4163
4163
|
systemPermissions: [],
|
|
4164
4164
|
org_user_ids: []
|
|
@@ -4207,16 +4207,16 @@ async function resolveAuthzContext(input) {
|
|
|
4207
4207
|
if (m.role && typeof m.role === "string") {
|
|
4208
4208
|
for (const raw of m.role.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
4209
4209
|
const r = mapMembershipRole(raw);
|
|
4210
|
-
if (!ctx.
|
|
4210
|
+
if (!ctx.positions.includes(r)) ctx.positions.push(r);
|
|
4211
4211
|
}
|
|
4212
4212
|
}
|
|
4213
4213
|
}
|
|
4214
|
-
const
|
|
4215
|
-
for (const ur of
|
|
4214
|
+
const userPositionRows = await tryFind(ql, "sys_user_position", { user_id: userId }, 200);
|
|
4215
|
+
for (const ur of userPositionRows) {
|
|
4216
4216
|
const org = ur.organization_id ?? null;
|
|
4217
4217
|
if (org && tenantId && org !== tenantId) continue;
|
|
4218
|
-
const r = ur.
|
|
4219
|
-
if (typeof r === "string" && r && !ctx.
|
|
4218
|
+
const r = ur.position;
|
|
4219
|
+
if (typeof r === "string" && r && !ctx.positions.includes(r)) ctx.positions.push(r);
|
|
4220
4220
|
}
|
|
4221
4221
|
if (tenantId) {
|
|
4222
4222
|
const orgMembers = await tryFind(ql, "sys_member", { organization_id: tenantId }, 1e3);
|
|
@@ -4239,11 +4239,12 @@ async function resolveAuthzContext(input) {
|
|
|
4239
4239
|
upsRows.filter((r) => (r.organization_id ?? r.organizationId ?? null) === null).map((r) => r.permission_set_id ?? r.permissionSetId).filter(Boolean)
|
|
4240
4240
|
);
|
|
4241
4241
|
let hasPlatformAdminGrant = false;
|
|
4242
|
-
if (ctx.
|
|
4243
|
-
|
|
4244
|
-
const
|
|
4245
|
-
|
|
4246
|
-
|
|
4242
|
+
if (!ctx.positions.includes("everyone")) ctx.positions.push("everyone");
|
|
4243
|
+
if (ctx.positions.length > 0) {
|
|
4244
|
+
const positionRows = await tryFind(ql, "sys_position", { name: { $in: ctx.positions } }, 100);
|
|
4245
|
+
const positionIds = positionRows.map((r) => r.id).filter(Boolean);
|
|
4246
|
+
if (positionIds.length > 0) {
|
|
4247
|
+
const rpsRows = await tryFind(ql, "sys_position_permission_set", { position_id: { $in: positionIds } }, 500);
|
|
4247
4248
|
for (const r of rpsRows) {
|
|
4248
4249
|
const id = r.permission_set_id ?? r.permissionSetId;
|
|
4249
4250
|
if (id) psIds.add(id);
|
|
@@ -4276,8 +4277,8 @@ async function resolveAuthzContext(input) {
|
|
|
4276
4277
|
}
|
|
4277
4278
|
if (Object.keys(mergedTabs).length > 0) ctx.tabPermissions = mergedTabs;
|
|
4278
4279
|
}
|
|
4279
|
-
if (hasPlatformAdminGrant && !ctx.
|
|
4280
|
-
ctx.
|
|
4280
|
+
if (hasPlatformAdminGrant && !ctx.positions.includes(BUILTIN_IDENTITY_PLATFORM_ADMIN)) {
|
|
4281
|
+
ctx.positions.unshift(BUILTIN_IDENTITY_PLATFORM_ADMIN);
|
|
4281
4282
|
}
|
|
4282
4283
|
if (!ctx.permissions.includes("ai_seat")) {
|
|
4283
4284
|
const aiAccess = (await getUserRow())?.ai_access;
|
|
@@ -4389,6 +4390,87 @@ function calendarPartsInTzOrUtc(d, tz) {
|
|
|
4389
4390
|
};
|
|
4390
4391
|
}
|
|
4391
4392
|
|
|
4393
|
+
// src/utils/bulk-write.ts
|
|
4394
|
+
var DEFAULT_BATCH_SIZE = 200;
|
|
4395
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
4396
|
+
var DEFAULT_BACKOFF_BASE_MS = 200;
|
|
4397
|
+
var TRANSIENT_PATTERNS = [
|
|
4398
|
+
/fetch failed/i,
|
|
4399
|
+
/network/i,
|
|
4400
|
+
/timed?\s*out/i,
|
|
4401
|
+
/timeout/i,
|
|
4402
|
+
/socket hang ?up/i,
|
|
4403
|
+
/connection.*(closed|reset|refused|terminated|aborted)/i,
|
|
4404
|
+
/\b(502|503|504)\b/,
|
|
4405
|
+
/server.*unavailable/i,
|
|
4406
|
+
/too many connections/i
|
|
4407
|
+
];
|
|
4408
|
+
var TRANSIENT_CODES = /^(ECONNRESET|ECONNREFUSED|ECONNABORTED|EPIPE|EAI_AGAIN|ETIMEDOUT|EHOSTUNREACH|ENETUNREACH|ENOTFOUND)$/i;
|
|
4409
|
+
function defaultIsTransientError(err) {
|
|
4410
|
+
const code = err?.code;
|
|
4411
|
+
if (typeof code === "string" && TRANSIENT_CODES.test(code)) return true;
|
|
4412
|
+
const message = err?.message;
|
|
4413
|
+
const text = typeof message === "string" ? message : String(err ?? "");
|
|
4414
|
+
return TRANSIENT_PATTERNS.some((re) => re.test(text));
|
|
4415
|
+
}
|
|
4416
|
+
var defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4417
|
+
async function withRetry(fn, opts) {
|
|
4418
|
+
let lastError;
|
|
4419
|
+
for (let attempt = 1; attempt <= opts.maxRetries; attempt++) {
|
|
4420
|
+
try {
|
|
4421
|
+
return await fn();
|
|
4422
|
+
} catch (err) {
|
|
4423
|
+
lastError = err;
|
|
4424
|
+
if (attempt >= opts.maxRetries || !opts.isTransientError(err)) throw err;
|
|
4425
|
+
const jitter = Math.floor(Math.random() * 50);
|
|
4426
|
+
await opts.sleep(opts.backoffBaseMs * 2 ** (attempt - 1) + jitter);
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
throw lastError;
|
|
4430
|
+
}
|
|
4431
|
+
async function withTransientRetry(fn, opts = {}) {
|
|
4432
|
+
return withRetry(fn, {
|
|
4433
|
+
maxRetries: Math.max(1, opts.maxRetries ?? DEFAULT_MAX_RETRIES),
|
|
4434
|
+
backoffBaseMs: opts.backoffBaseMs ?? DEFAULT_BACKOFF_BASE_MS,
|
|
4435
|
+
isTransientError: opts.isTransientError ?? defaultIsTransientError,
|
|
4436
|
+
sleep: opts.sleep ?? defaultSleep
|
|
4437
|
+
});
|
|
4438
|
+
}
|
|
4439
|
+
async function bulkWrite(rows, opts) {
|
|
4440
|
+
const batchSize = Math.max(1, opts.batchSize ?? DEFAULT_BATCH_SIZE);
|
|
4441
|
+
const retryOpts = {
|
|
4442
|
+
maxRetries: Math.max(1, opts.maxRetries ?? DEFAULT_MAX_RETRIES),
|
|
4443
|
+
backoffBaseMs: opts.backoffBaseMs ?? DEFAULT_BACKOFF_BASE_MS,
|
|
4444
|
+
isTransientError: opts.isTransientError ?? defaultIsTransientError,
|
|
4445
|
+
sleep: opts.sleep ?? defaultSleep
|
|
4446
|
+
};
|
|
4447
|
+
const results = new Array(rows.length);
|
|
4448
|
+
for (let start = 0; start < rows.length; start += batchSize) {
|
|
4449
|
+
const batch = rows.slice(start, start + batchSize);
|
|
4450
|
+
try {
|
|
4451
|
+
const records = await withRetry(() => opts.writeBatch(batch), retryOpts);
|
|
4452
|
+
for (let i = 0; i < batch.length; i++) {
|
|
4453
|
+
results[start + i] = { index: start + i, ok: true, record: records[i] };
|
|
4454
|
+
}
|
|
4455
|
+
} catch (batchErr) {
|
|
4456
|
+
if (batch.length === 1) {
|
|
4457
|
+
results[start] = { index: start, ok: false, error: batchErr };
|
|
4458
|
+
continue;
|
|
4459
|
+
}
|
|
4460
|
+
for (let i = 0; i < batch.length; i++) {
|
|
4461
|
+
const idx = start + i;
|
|
4462
|
+
try {
|
|
4463
|
+
const record = await withRetry(() => opts.writeOne(batch[i]), retryOpts);
|
|
4464
|
+
results[idx] = { index: idx, ok: true, record };
|
|
4465
|
+
} catch (err) {
|
|
4466
|
+
results[idx] = { index: idx, ok: false, error: err };
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
return results;
|
|
4472
|
+
}
|
|
4473
|
+
|
|
4392
4474
|
// src/health-monitor.ts
|
|
4393
4475
|
var PluginHealthMonitor = class {
|
|
4394
4476
|
constructor(logger) {
|
|
@@ -5327,6 +5409,7 @@ export {
|
|
|
5327
5409
|
SemanticVersionManager,
|
|
5328
5410
|
ServiceLifecycle,
|
|
5329
5411
|
buildPermissionsFromGrants,
|
|
5412
|
+
bulkWrite,
|
|
5330
5413
|
calendarPartsInTz,
|
|
5331
5414
|
calendarPartsInTzOrUtc,
|
|
5332
5415
|
counterSignPayload,
|
|
@@ -5340,6 +5423,7 @@ export {
|
|
|
5340
5423
|
createPluginConfigValidator,
|
|
5341
5424
|
createPluginPermissionEnforcer,
|
|
5342
5425
|
deepMerge,
|
|
5426
|
+
defaultIsTransientError,
|
|
5343
5427
|
evaluateAuthGate,
|
|
5344
5428
|
extractApiKey,
|
|
5345
5429
|
generateApiKey,
|
|
@@ -5363,6 +5447,7 @@ export {
|
|
|
5363
5447
|
verifyPlatformSignature,
|
|
5364
5448
|
verifyPluginArtifact,
|
|
5365
5449
|
verifyPublisherSignature,
|
|
5366
|
-
wireAuthoredTranslationSync
|
|
5450
|
+
wireAuthoredTranslationSync,
|
|
5451
|
+
withTransientRetry
|
|
5367
5452
|
};
|
|
5368
5453
|
//# sourceMappingURL=index.js.map
|