@lunora/runtime 1.0.0-alpha.23 → 1.0.0-alpha.25
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.d.mts +61 -1
- package/dist/index.d.ts +61 -1
- package/dist/index.mjs +3 -3
- package/dist/packem_shared/analyticsEngineSink-BpJlA7e9.mjs +275 -0
- package/dist/packem_shared/{composeWorker-CB3pLcCP.mjs → composeWorker-CxwkPZUl.mjs} +39 -20
- package/dist/packem_shared/{decorateResponse-CsZc49QC.mjs → decorateResponse-DRWQFNhF.mjs} +8 -1
- package/package.json +2 -2
- package/dist/packem_shared/analyticsEngineSink-DqEvrQs0.mjs +0 -141
package/dist/index.d.mts
CHANGED
|
@@ -2365,6 +2365,12 @@ interface RpcContext {
|
|
|
2365
2365
|
shardKey: string;
|
|
2366
2366
|
}
|
|
2367
2367
|
/**
|
|
2368
|
+
* Ask the owner how many relays to spread new connections across for `shardKey`
|
|
2369
|
+
* (plan 075 Phase 2), cached per isolate so a promoted shard doesn't add a
|
|
2370
|
+
* round-trip to every WS upgrade. Fails closed to `0` (owner-served) on any error,
|
|
2371
|
+
* so a relay-probe hiccup can never break a connection.
|
|
2372
|
+
*/
|
|
2373
|
+
/**
|
|
2368
2374
|
* The composed Lunora worker. `fetch` / `scheduled` are the standard Cloudflare
|
|
2369
2375
|
* module-worker entrypoints (so the object can be re-exported directly as
|
|
2370
2376
|
* `export default createWorker(...)`). `serverQuery` is the in-process fast-path
|
|
@@ -2772,6 +2778,60 @@ interface AnalyticsEngineSinkOptions extends OnlyErrorsOption {
|
|
|
2772
2778
|
* only error events (defaults to all events).
|
|
2773
2779
|
*/
|
|
2774
2780
|
declare const analyticsEngineSink: (options: AnalyticsEngineSinkOptions) => ObservabilitySink;
|
|
2781
|
+
/** Options for {@link otlpSink}. */
|
|
2782
|
+
interface OtlpSinkOptions extends OnlyErrorsOption {
|
|
2783
|
+
/**
|
|
2784
|
+
* The OTLP-over-HTTP collector base endpoint (e.g.
|
|
2785
|
+
* `https://collector.example.com`). Following the OTel base-endpoint
|
|
2786
|
+
* convention, the sink POSTs spans to `${endpoint}/v1/traces` and log
|
|
2787
|
+
* records to `${endpoint}/v1/logs`; a trailing slash is tolerated.
|
|
2788
|
+
*/
|
|
2789
|
+
endpoint: string;
|
|
2790
|
+
/**
|
|
2791
|
+
* Extra headers merged onto every OTLP POST — typically an `Authorization`
|
|
2792
|
+
* bearer plus the `x-lunora-deployment` / `x-lunora-org` correlation headers
|
|
2793
|
+
* the platform injects at deploy. `Content-Type: application/json` is set by
|
|
2794
|
+
* default and may be overridden here.
|
|
2795
|
+
*/
|
|
2796
|
+
headers?: Record<string, string>;
|
|
2797
|
+
/**
|
|
2798
|
+
* Value of the `service.name` resource attribute on every exported span and
|
|
2799
|
+
* log — the logical service the telemetry belongs to. Defaults to `lunora`.
|
|
2800
|
+
*/
|
|
2801
|
+
serviceName?: string;
|
|
2802
|
+
/**
|
|
2803
|
+
* Convenience bearer token: when set, an `Authorization: Bearer` header
|
|
2804
|
+
* carrying it is added to every POST (overriding any authorization in
|
|
2805
|
+
* `headers`). Mirrors the container exporter so the platform can inject the
|
|
2806
|
+
* same `LUNORA_OTLP_TOKEN` into both. Leave unset for an unauthenticated collector.
|
|
2807
|
+
*/
|
|
2808
|
+
token?: string;
|
|
2809
|
+
}
|
|
2810
|
+
/**
|
|
2811
|
+
* A fire-and-forget sink that exports telemetry over OTLP-over-HTTP (JSON).
|
|
2812
|
+
*
|
|
2813
|
+
* This is the single, standard wire contract both the worker and (via the
|
|
2814
|
+
* container exporter helper) container processes use, so telemetry from either
|
|
2815
|
+
* side lands in the same collector. Each RPC dispatch becomes one OTLP **span**
|
|
2816
|
+
* (`${endpoint}/v1/traces`) named after its `functionPath`, with start/end
|
|
2817
|
+
* derived from `durationMs` and status OK/ERROR; each `ctx.log.*` line becomes
|
|
2818
|
+
* one OTLP **log record** (`${endpoint}/v1/logs`). Trace/span ids are random
|
|
2819
|
+
* per span — real trace correlation (worker→container `traceparent`) is a later
|
|
2820
|
+
* phase.
|
|
2821
|
+
*
|
|
2822
|
+
* Like {@link webhookSink}, each export is its own `fetch`, registered with the
|
|
2823
|
+
* request's `context.waitUntil` when present so it survives isolate teardown,
|
|
2824
|
+
* and every rejection is swallowed so a flaky collector never surfaces to the
|
|
2825
|
+
* caller.
|
|
2826
|
+
*
|
|
2827
|
+
* Privacy: spans carry `error.type`/`error.message` and logs carry the rendered
|
|
2828
|
+
* `message`, which may include user input. Point `endpoint` only at a collector
|
|
2829
|
+
* you trust, and gate PII upstream if that is a concern.
|
|
2830
|
+
* @param options Sink options: `endpoint` is the collector base URL, `headers`
|
|
2831
|
+
* are merged onto every POST (auth + correlation), `serviceName` sets the
|
|
2832
|
+
* resource `service.name`, and `onlyErrors` exports error spans only.
|
|
2833
|
+
*/
|
|
2834
|
+
declare const otlpSink: (options: OtlpSinkOptions) => ObservabilitySink;
|
|
2775
2835
|
/**
|
|
2776
2836
|
* Combine several sinks into one that fans each event out to all of them.
|
|
2777
2837
|
*
|
|
@@ -2781,4 +2841,4 @@ declare const analyticsEngineSink: (options: AnalyticsEngineSinkOptions) => Obse
|
|
|
2781
2841
|
*/
|
|
2782
2842
|
declare const combineSinks: (...sinks: ObservabilitySink[]) => ObservabilitySink;
|
|
2783
2843
|
declare const VERSION: string;
|
|
2784
|
-
export { type AdminTableResolver, type AirbyteMessage, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, type AnalyticsEngineSinkOptions, type AuthAdmin, type AuthCapabilities, type AuthConfigInfo, type AuthImpersonation, type AuthIntrospector, type AuthPage, type AuthSession, type AuthUser, type AuthUserFieldSpec, type BackupManifest, type BackupStore, type ComposeIdentityResolversErrorMode, type ComposeIdentityResolversOptions, type ConnectorChange, type ConnectorSyncPage, type CorsOptions, type CronHandler, type CronJobDispatch, type CronJobInfo, type CrossShardCounter, type CrossShardReader, type CrossShardRelationCapabilities, type CrossShardRelationOptions, type CsrfOptions, DEFAULT_REGISTRY_CACHE_TTL_MS, type DurableObjectJurisdiction, type DynamicShardRegistry, type DynamicShardRegistryOptions, type ExecutionContextLike, type ExportFanOutRequest, type ExportFanOutResult, type FanOutRequest, type FanOutResult, type FanOutSpec, type FivetranResponse, type FrameworkHostHandler, type FrameworkWorkerOptions, type FrameworkWorkerOptionsInput, type FunctionDescriptor, type FunctionRegistryEntry, type FunctionRegistryLike, type GlobalExportFunction as GlobalExportFn, type GlobalImportFunction as GlobalImportFn, type GlobalIntrospector, type GlobalTableInfo as GlobalTableInfoMeta, type GlobalTablePage as GlobalTablePageMeta, type HttpActionContext, type HttpActionLike, type HttpRouterLike, type IdentityContractLike, type IdentityResolver, type IdentityValidation, type ImportFanOutRequest, type ImportFanOutResult, type KvIntrospector, type KvKeyEntry, type KvKeyListResult, type KvNamespaceSummary, type KvValueResult, type ListAuthUsersOptions, type LogEvent, type LogLevel, LunoraError, type LunoraErrorBody, type LunoraHandlerOptions, type LunoraWorker, type MergeStrategy, type MigrationFanOutRequest, type MigrationFanOutResult, NOOP_EXECUTION_CONTEXT, type ObservabilityEvent, type ObservabilitySink, type ObservabilitySinkContext, type QueryCoordinator, type QueryCoordinatorOptions, type RankFanOutRequest, type RankFanOutResult, type RankPageFanOutRequest, type RankPageFanOutResult, type ResolvedSecurity, type ResolvedShard, type Route, type RpcContext, type RpcEnvelope, SHARD_REGISTRY_DO_NAME, type ScheduledControllerLike, type SecurityHeadersOptions, type SecurityOptions, type SentrySinkOptions, type ShardError, type ShardExportOutcome, type ShardImportOutcome, type ShardMigrationOutcome, type ShardNamespaceLike, type ShardRankOutcome, type ShardRankPageOutcome, type ShardRegistry, type ShardTrafficEntry, type ShardTrafficFanOutRequest, type ShardTrafficFanOutResult, type ShardingInfo, type StorageListFunction as StorageListFn, type StorageObject, VERSION, type VectorIndexSummary, type VectorIntrospector, type VectorQueryMatch, type WebhookSinkOptions, type WorkerOptions, analyticsEngineSink, applyJurisdiction, combineSinks, composeIdentityResolvers, composeWorker, consoleSink, createCrossShardRelationCapabilities, createDynamicShardRegistry, createLunoraHandler, createQueryCoordinator, createStaticShardRegistry, createWorker, decorateResponse, defineRpcEnvelope, emitLogEvent, emitRpcEvent, enforceOrigin, handleCorsPreflight, mergeStrategyForAggregate, resolveLunoraOptions, resolveSecurity, resolveShard, routeIdentityResolvers, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
|
|
2844
|
+
export { type AdminTableResolver, type AirbyteMessage, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, type AnalyticsEngineSinkOptions, type AuthAdmin, type AuthCapabilities, type AuthConfigInfo, type AuthImpersonation, type AuthIntrospector, type AuthPage, type AuthSession, type AuthUser, type AuthUserFieldSpec, type BackupManifest, type BackupStore, type ComposeIdentityResolversErrorMode, type ComposeIdentityResolversOptions, type ConnectorChange, type ConnectorSyncPage, type CorsOptions, type CronHandler, type CronJobDispatch, type CronJobInfo, type CrossShardCounter, type CrossShardReader, type CrossShardRelationCapabilities, type CrossShardRelationOptions, type CsrfOptions, DEFAULT_REGISTRY_CACHE_TTL_MS, type DurableObjectJurisdiction, type DynamicShardRegistry, type DynamicShardRegistryOptions, type ExecutionContextLike, type ExportFanOutRequest, type ExportFanOutResult, type FanOutRequest, type FanOutResult, type FanOutSpec, type FivetranResponse, type FrameworkHostHandler, type FrameworkWorkerOptions, type FrameworkWorkerOptionsInput, type FunctionDescriptor, type FunctionRegistryEntry, type FunctionRegistryLike, type GlobalExportFunction as GlobalExportFn, type GlobalImportFunction as GlobalImportFn, type GlobalIntrospector, type GlobalTableInfo as GlobalTableInfoMeta, type GlobalTablePage as GlobalTablePageMeta, type HttpActionContext, type HttpActionLike, type HttpRouterLike, type IdentityContractLike, type IdentityResolver, type IdentityValidation, type ImportFanOutRequest, type ImportFanOutResult, type KvIntrospector, type KvKeyEntry, type KvKeyListResult, type KvNamespaceSummary, type KvValueResult, type ListAuthUsersOptions, type LogEvent, type LogLevel, LunoraError, type LunoraErrorBody, type LunoraHandlerOptions, type LunoraWorker, type MergeStrategy, type MigrationFanOutRequest, type MigrationFanOutResult, NOOP_EXECUTION_CONTEXT, type ObservabilityEvent, type ObservabilitySink, type ObservabilitySinkContext, type OtlpSinkOptions, type QueryCoordinator, type QueryCoordinatorOptions, type RankFanOutRequest, type RankFanOutResult, type RankPageFanOutRequest, type RankPageFanOutResult, type ResolvedSecurity, type ResolvedShard, type Route, type RpcContext, type RpcEnvelope, SHARD_REGISTRY_DO_NAME, type ScheduledControllerLike, type SecurityHeadersOptions, type SecurityOptions, type SentrySinkOptions, type ShardError, type ShardExportOutcome, type ShardImportOutcome, type ShardMigrationOutcome, type ShardNamespaceLike, type ShardRankOutcome, type ShardRankPageOutcome, type ShardRegistry, type ShardTrafficEntry, type ShardTrafficFanOutRequest, type ShardTrafficFanOutResult, type ShardingInfo, type StorageListFunction as StorageListFn, type StorageObject, VERSION, type VectorIndexSummary, type VectorIntrospector, type VectorQueryMatch, type WebhookSinkOptions, type WorkerOptions, analyticsEngineSink, applyJurisdiction, combineSinks, composeIdentityResolvers, composeWorker, consoleSink, createCrossShardRelationCapabilities, createDynamicShardRegistry, createLunoraHandler, createQueryCoordinator, createStaticShardRegistry, createWorker, decorateResponse, defineRpcEnvelope, emitLogEvent, emitRpcEvent, enforceOrigin, handleCorsPreflight, mergeStrategyForAggregate, otlpSink, resolveLunoraOptions, resolveSecurity, resolveShard, routeIdentityResolvers, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
|
package/dist/index.d.ts
CHANGED
|
@@ -2365,6 +2365,12 @@ interface RpcContext {
|
|
|
2365
2365
|
shardKey: string;
|
|
2366
2366
|
}
|
|
2367
2367
|
/**
|
|
2368
|
+
* Ask the owner how many relays to spread new connections across for `shardKey`
|
|
2369
|
+
* (plan 075 Phase 2), cached per isolate so a promoted shard doesn't add a
|
|
2370
|
+
* round-trip to every WS upgrade. Fails closed to `0` (owner-served) on any error,
|
|
2371
|
+
* so a relay-probe hiccup can never break a connection.
|
|
2372
|
+
*/
|
|
2373
|
+
/**
|
|
2368
2374
|
* The composed Lunora worker. `fetch` / `scheduled` are the standard Cloudflare
|
|
2369
2375
|
* module-worker entrypoints (so the object can be re-exported directly as
|
|
2370
2376
|
* `export default createWorker(...)`). `serverQuery` is the in-process fast-path
|
|
@@ -2772,6 +2778,60 @@ interface AnalyticsEngineSinkOptions extends OnlyErrorsOption {
|
|
|
2772
2778
|
* only error events (defaults to all events).
|
|
2773
2779
|
*/
|
|
2774
2780
|
declare const analyticsEngineSink: (options: AnalyticsEngineSinkOptions) => ObservabilitySink;
|
|
2781
|
+
/** Options for {@link otlpSink}. */
|
|
2782
|
+
interface OtlpSinkOptions extends OnlyErrorsOption {
|
|
2783
|
+
/**
|
|
2784
|
+
* The OTLP-over-HTTP collector base endpoint (e.g.
|
|
2785
|
+
* `https://collector.example.com`). Following the OTel base-endpoint
|
|
2786
|
+
* convention, the sink POSTs spans to `${endpoint}/v1/traces` and log
|
|
2787
|
+
* records to `${endpoint}/v1/logs`; a trailing slash is tolerated.
|
|
2788
|
+
*/
|
|
2789
|
+
endpoint: string;
|
|
2790
|
+
/**
|
|
2791
|
+
* Extra headers merged onto every OTLP POST — typically an `Authorization`
|
|
2792
|
+
* bearer plus the `x-lunora-deployment` / `x-lunora-org` correlation headers
|
|
2793
|
+
* the platform injects at deploy. `Content-Type: application/json` is set by
|
|
2794
|
+
* default and may be overridden here.
|
|
2795
|
+
*/
|
|
2796
|
+
headers?: Record<string, string>;
|
|
2797
|
+
/**
|
|
2798
|
+
* Value of the `service.name` resource attribute on every exported span and
|
|
2799
|
+
* log — the logical service the telemetry belongs to. Defaults to `lunora`.
|
|
2800
|
+
*/
|
|
2801
|
+
serviceName?: string;
|
|
2802
|
+
/**
|
|
2803
|
+
* Convenience bearer token: when set, an `Authorization: Bearer` header
|
|
2804
|
+
* carrying it is added to every POST (overriding any authorization in
|
|
2805
|
+
* `headers`). Mirrors the container exporter so the platform can inject the
|
|
2806
|
+
* same `LUNORA_OTLP_TOKEN` into both. Leave unset for an unauthenticated collector.
|
|
2807
|
+
*/
|
|
2808
|
+
token?: string;
|
|
2809
|
+
}
|
|
2810
|
+
/**
|
|
2811
|
+
* A fire-and-forget sink that exports telemetry over OTLP-over-HTTP (JSON).
|
|
2812
|
+
*
|
|
2813
|
+
* This is the single, standard wire contract both the worker and (via the
|
|
2814
|
+
* container exporter helper) container processes use, so telemetry from either
|
|
2815
|
+
* side lands in the same collector. Each RPC dispatch becomes one OTLP **span**
|
|
2816
|
+
* (`${endpoint}/v1/traces`) named after its `functionPath`, with start/end
|
|
2817
|
+
* derived from `durationMs` and status OK/ERROR; each `ctx.log.*` line becomes
|
|
2818
|
+
* one OTLP **log record** (`${endpoint}/v1/logs`). Trace/span ids are random
|
|
2819
|
+
* per span — real trace correlation (worker→container `traceparent`) is a later
|
|
2820
|
+
* phase.
|
|
2821
|
+
*
|
|
2822
|
+
* Like {@link webhookSink}, each export is its own `fetch`, registered with the
|
|
2823
|
+
* request's `context.waitUntil` when present so it survives isolate teardown,
|
|
2824
|
+
* and every rejection is swallowed so a flaky collector never surfaces to the
|
|
2825
|
+
* caller.
|
|
2826
|
+
*
|
|
2827
|
+
* Privacy: spans carry `error.type`/`error.message` and logs carry the rendered
|
|
2828
|
+
* `message`, which may include user input. Point `endpoint` only at a collector
|
|
2829
|
+
* you trust, and gate PII upstream if that is a concern.
|
|
2830
|
+
* @param options Sink options: `endpoint` is the collector base URL, `headers`
|
|
2831
|
+
* are merged onto every POST (auth + correlation), `serviceName` sets the
|
|
2832
|
+
* resource `service.name`, and `onlyErrors` exports error spans only.
|
|
2833
|
+
*/
|
|
2834
|
+
declare const otlpSink: (options: OtlpSinkOptions) => ObservabilitySink;
|
|
2775
2835
|
/**
|
|
2776
2836
|
* Combine several sinks into one that fans each event out to all of them.
|
|
2777
2837
|
*
|
|
@@ -2781,4 +2841,4 @@ declare const analyticsEngineSink: (options: AnalyticsEngineSinkOptions) => Obse
|
|
|
2781
2841
|
*/
|
|
2782
2842
|
declare const combineSinks: (...sinks: ObservabilitySink[]) => ObservabilitySink;
|
|
2783
2843
|
declare const VERSION: string;
|
|
2784
|
-
export { type AdminTableResolver, type AirbyteMessage, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, type AnalyticsEngineSinkOptions, type AuthAdmin, type AuthCapabilities, type AuthConfigInfo, type AuthImpersonation, type AuthIntrospector, type AuthPage, type AuthSession, type AuthUser, type AuthUserFieldSpec, type BackupManifest, type BackupStore, type ComposeIdentityResolversErrorMode, type ComposeIdentityResolversOptions, type ConnectorChange, type ConnectorSyncPage, type CorsOptions, type CronHandler, type CronJobDispatch, type CronJobInfo, type CrossShardCounter, type CrossShardReader, type CrossShardRelationCapabilities, type CrossShardRelationOptions, type CsrfOptions, DEFAULT_REGISTRY_CACHE_TTL_MS, type DurableObjectJurisdiction, type DynamicShardRegistry, type DynamicShardRegistryOptions, type ExecutionContextLike, type ExportFanOutRequest, type ExportFanOutResult, type FanOutRequest, type FanOutResult, type FanOutSpec, type FivetranResponse, type FrameworkHostHandler, type FrameworkWorkerOptions, type FrameworkWorkerOptionsInput, type FunctionDescriptor, type FunctionRegistryEntry, type FunctionRegistryLike, type GlobalExportFunction as GlobalExportFn, type GlobalImportFunction as GlobalImportFn, type GlobalIntrospector, type GlobalTableInfo as GlobalTableInfoMeta, type GlobalTablePage as GlobalTablePageMeta, type HttpActionContext, type HttpActionLike, type HttpRouterLike, type IdentityContractLike, type IdentityResolver, type IdentityValidation, type ImportFanOutRequest, type ImportFanOutResult, type KvIntrospector, type KvKeyEntry, type KvKeyListResult, type KvNamespaceSummary, type KvValueResult, type ListAuthUsersOptions, type LogEvent, type LogLevel, LunoraError, type LunoraErrorBody, type LunoraHandlerOptions, type LunoraWorker, type MergeStrategy, type MigrationFanOutRequest, type MigrationFanOutResult, NOOP_EXECUTION_CONTEXT, type ObservabilityEvent, type ObservabilitySink, type ObservabilitySinkContext, type QueryCoordinator, type QueryCoordinatorOptions, type RankFanOutRequest, type RankFanOutResult, type RankPageFanOutRequest, type RankPageFanOutResult, type ResolvedSecurity, type ResolvedShard, type Route, type RpcContext, type RpcEnvelope, SHARD_REGISTRY_DO_NAME, type ScheduledControllerLike, type SecurityHeadersOptions, type SecurityOptions, type SentrySinkOptions, type ShardError, type ShardExportOutcome, type ShardImportOutcome, type ShardMigrationOutcome, type ShardNamespaceLike, type ShardRankOutcome, type ShardRankPageOutcome, type ShardRegistry, type ShardTrafficEntry, type ShardTrafficFanOutRequest, type ShardTrafficFanOutResult, type ShardingInfo, type StorageListFunction as StorageListFn, type StorageObject, VERSION, type VectorIndexSummary, type VectorIntrospector, type VectorQueryMatch, type WebhookSinkOptions, type WorkerOptions, analyticsEngineSink, applyJurisdiction, combineSinks, composeIdentityResolvers, composeWorker, consoleSink, createCrossShardRelationCapabilities, createDynamicShardRegistry, createLunoraHandler, createQueryCoordinator, createStaticShardRegistry, createWorker, decorateResponse, defineRpcEnvelope, emitLogEvent, emitRpcEvent, enforceOrigin, handleCorsPreflight, mergeStrategyForAggregate, resolveLunoraOptions, resolveSecurity, resolveShard, routeIdentityResolvers, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
|
|
2844
|
+
export { type AdminTableResolver, type AirbyteMessage, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, type AnalyticsEngineSinkOptions, type AuthAdmin, type AuthCapabilities, type AuthConfigInfo, type AuthImpersonation, type AuthIntrospector, type AuthPage, type AuthSession, type AuthUser, type AuthUserFieldSpec, type BackupManifest, type BackupStore, type ComposeIdentityResolversErrorMode, type ComposeIdentityResolversOptions, type ConnectorChange, type ConnectorSyncPage, type CorsOptions, type CronHandler, type CronJobDispatch, type CronJobInfo, type CrossShardCounter, type CrossShardReader, type CrossShardRelationCapabilities, type CrossShardRelationOptions, type CsrfOptions, DEFAULT_REGISTRY_CACHE_TTL_MS, type DurableObjectJurisdiction, type DynamicShardRegistry, type DynamicShardRegistryOptions, type ExecutionContextLike, type ExportFanOutRequest, type ExportFanOutResult, type FanOutRequest, type FanOutResult, type FanOutSpec, type FivetranResponse, type FrameworkHostHandler, type FrameworkWorkerOptions, type FrameworkWorkerOptionsInput, type FunctionDescriptor, type FunctionRegistryEntry, type FunctionRegistryLike, type GlobalExportFunction as GlobalExportFn, type GlobalImportFunction as GlobalImportFn, type GlobalIntrospector, type GlobalTableInfo as GlobalTableInfoMeta, type GlobalTablePage as GlobalTablePageMeta, type HttpActionContext, type HttpActionLike, type HttpRouterLike, type IdentityContractLike, type IdentityResolver, type IdentityValidation, type ImportFanOutRequest, type ImportFanOutResult, type KvIntrospector, type KvKeyEntry, type KvKeyListResult, type KvNamespaceSummary, type KvValueResult, type ListAuthUsersOptions, type LogEvent, type LogLevel, LunoraError, type LunoraErrorBody, type LunoraHandlerOptions, type LunoraWorker, type MergeStrategy, type MigrationFanOutRequest, type MigrationFanOutResult, NOOP_EXECUTION_CONTEXT, type ObservabilityEvent, type ObservabilitySink, type ObservabilitySinkContext, type OtlpSinkOptions, type QueryCoordinator, type QueryCoordinatorOptions, type RankFanOutRequest, type RankFanOutResult, type RankPageFanOutRequest, type RankPageFanOutResult, type ResolvedSecurity, type ResolvedShard, type Route, type RpcContext, type RpcEnvelope, SHARD_REGISTRY_DO_NAME, type ScheduledControllerLike, type SecurityHeadersOptions, type SecurityOptions, type SentrySinkOptions, type ShardError, type ShardExportOutcome, type ShardImportOutcome, type ShardMigrationOutcome, type ShardNamespaceLike, type ShardRankOutcome, type ShardRankPageOutcome, type ShardRegistry, type ShardTrafficEntry, type ShardTrafficFanOutRequest, type ShardTrafficFanOutResult, type ShardingInfo, type StorageListFunction as StorageListFn, type StorageObject, VERSION, type VectorIndexSummary, type VectorIntrospector, type VectorQueryMatch, type WebhookSinkOptions, type WorkerOptions, analyticsEngineSink, applyJurisdiction, combineSinks, composeIdentityResolvers, composeWorker, consoleSink, createCrossShardRelationCapabilities, createDynamicShardRegistry, createLunoraHandler, createQueryCoordinator, createStaticShardRegistry, createWorker, decorateResponse, defineRpcEnvelope, emitLogEvent, emitRpcEvent, enforceOrigin, handleCorsPreflight, mergeStrategyForAggregate, otlpSink, resolveLunoraOptions, resolveSecurity, resolveShard, routeIdentityResolvers, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
export { toAirbyteMessages, toFivetranResponse } from './packem_shared/toAirbyteMessages-DrHdplb4.mjs';
|
|
2
|
-
export { composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, resolveLunoraOptions, withFrameworkWorker } from './packem_shared/composeWorker-
|
|
2
|
+
export { composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, resolveLunoraOptions, withFrameworkWorker } from './packem_shared/composeWorker-CxwkPZUl.mjs';
|
|
3
3
|
export { createCrossShardRelationCapabilities } from './packem_shared/createCrossShardRelationCapabilities-CbcWjkAn.mjs';
|
|
4
4
|
export { DEFAULT_REGISTRY_CACHE_TTL_MS, SHARD_REGISTRY_DO_NAME, createDynamicShardRegistry } from './packem_shared/DEFAULT_REGISTRY_CACHE_TTL_MS-B3pA7aXp.mjs';
|
|
5
5
|
export { LunoraError, toErrorResponse } from './packem_shared/LunoraError-Bpb9EFJ3.mjs';
|
|
6
6
|
export { emitLogEvent, emitRpcEvent } from './packem_shared/emitLogEvent-pEdtqAK8.mjs';
|
|
7
|
-
export { analyticsEngineSink, combineSinks, consoleSink, sentrySink, webhookSink } from './packem_shared/analyticsEngineSink-
|
|
7
|
+
export { analyticsEngineSink, combineSinks, consoleSink, otlpSink, sentrySink, webhookSink } from './packem_shared/analyticsEngineSink-BpJlA7e9.mjs';
|
|
8
8
|
export { createQueryCoordinator, createStaticShardRegistry, mergeStrategyForAggregate } from './packem_shared/createQueryCoordinator-DNCJzOZE.mjs';
|
|
9
9
|
export { applyJurisdiction, resolveShard } from './packem_shared/applyJurisdiction-BkZtTkct.mjs';
|
|
10
|
-
export { decorateResponse, enforceOrigin, handleCorsPreflight, resolveSecurity } from './packem_shared/decorateResponse-
|
|
10
|
+
export { decorateResponse, enforceOrigin, handleCorsPreflight, resolveSecurity } from './packem_shared/decorateResponse-DRWQFNhF.mjs';
|
|
11
11
|
export { NOOP_EXECUTION_CONTEXT } from './packem_shared/NOOP_EXECUTION_CONTEXT-CCTu0Bf1.mjs';
|
|
12
12
|
export { composeIdentityResolvers, routeIdentityResolvers } from './packem_shared/composeIdentityResolvers-XGjO7V1J.mjs';
|
|
13
13
|
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
const OTLP_SEVERITY = {
|
|
2
|
+
debug: 5,
|
|
3
|
+
// DEBUG
|
|
4
|
+
error: 17,
|
|
5
|
+
// ERROR
|
|
6
|
+
info: 9,
|
|
7
|
+
// INFO
|
|
8
|
+
log: 9,
|
|
9
|
+
// INFO
|
|
10
|
+
warn: 13
|
|
11
|
+
// WARN
|
|
12
|
+
};
|
|
13
|
+
const otlpUnixNano = (ms) => `${String(Math.round(ms))}000000`;
|
|
14
|
+
const otlpRandomHex = (bytes) => {
|
|
15
|
+
const buffer = new Uint8Array(bytes);
|
|
16
|
+
crypto.getRandomValues(buffer);
|
|
17
|
+
let hex = "";
|
|
18
|
+
for (const byte of buffer) {
|
|
19
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
20
|
+
}
|
|
21
|
+
return hex;
|
|
22
|
+
};
|
|
23
|
+
const encodeAttribute = (key, value) => {
|
|
24
|
+
if (typeof value === "boolean") {
|
|
25
|
+
return { key, value: { boolValue: value } };
|
|
26
|
+
}
|
|
27
|
+
if (typeof value === "number") {
|
|
28
|
+
if (!Number.isFinite(value)) {
|
|
29
|
+
return { key, value: { stringValue: String(value) } };
|
|
30
|
+
}
|
|
31
|
+
return Number.isSafeInteger(value) ? { key, value: { intValue: String(value) } } : { key, value: { doubleValue: value } };
|
|
32
|
+
}
|
|
33
|
+
return { key, value: { stringValue: value } };
|
|
34
|
+
};
|
|
35
|
+
const mergeHeaders = (defaults, overrides, token) => {
|
|
36
|
+
const merged = {};
|
|
37
|
+
const seen = /* @__PURE__ */ new Map();
|
|
38
|
+
const put = (name, value) => {
|
|
39
|
+
const lower = name.toLowerCase();
|
|
40
|
+
const existing = seen.get(lower);
|
|
41
|
+
if (existing === void 0) {
|
|
42
|
+
seen.set(lower, name);
|
|
43
|
+
merged[name] = value;
|
|
44
|
+
} else {
|
|
45
|
+
merged[existing] = value;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
for (const [name, value] of Object.entries(defaults)) {
|
|
49
|
+
put(name, value);
|
|
50
|
+
}
|
|
51
|
+
for (const [name, value] of Object.entries(overrides ?? {})) {
|
|
52
|
+
put(name, value);
|
|
53
|
+
}
|
|
54
|
+
if (token !== void 0 && token.length > 0) {
|
|
55
|
+
put("authorization", `Bearer ${token}`);
|
|
56
|
+
}
|
|
57
|
+
return merged;
|
|
58
|
+
};
|
|
59
|
+
const wrapResourceSpans = (span, scopeName, serviceName) => {
|
|
60
|
+
return {
|
|
61
|
+
resourceSpans: [
|
|
62
|
+
{
|
|
63
|
+
resource: { attributes: [encodeAttribute("service.name", serviceName)] },
|
|
64
|
+
scopeSpans: [{ scope: { name: scopeName }, spans: [span] }]
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const wrapResourceLogs = (logRecord, scopeName, serviceName) => {
|
|
70
|
+
return {
|
|
71
|
+
resourceLogs: [
|
|
72
|
+
{
|
|
73
|
+
resource: { attributes: [encodeAttribute("service.name", serviceName)] },
|
|
74
|
+
scopeLogs: [{ logRecords: [logRecord], scope: { name: scopeName } }]
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const shouldSkip = (event, onlyErrors) => onlyErrors === true && event.ok;
|
|
81
|
+
const otlpTraceBody = (event, serviceName, endMs) => {
|
|
82
|
+
const attributes = [encodeAttribute("lunora.function_path", event.functionPath), encodeAttribute("lunora.ok", event.ok)];
|
|
83
|
+
if (event.shardKey !== void 0) {
|
|
84
|
+
attributes.push(encodeAttribute("lunora.shard_key", event.shardKey));
|
|
85
|
+
}
|
|
86
|
+
if (event.error) {
|
|
87
|
+
attributes.push(encodeAttribute("error.type", event.error.code), encodeAttribute("lunora.error_status", event.error.status));
|
|
88
|
+
}
|
|
89
|
+
if (event.fanOut) {
|
|
90
|
+
attributes.push(
|
|
91
|
+
encodeAttribute("lunora.fanout.table", event.fanOut.table),
|
|
92
|
+
encodeAttribute("lunora.fanout.shards", event.fanOut.shards),
|
|
93
|
+
encodeAttribute("lunora.fanout.failed", event.fanOut.failed)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const span = {
|
|
97
|
+
attributes,
|
|
98
|
+
endTimeUnixNano: otlpUnixNano(endMs),
|
|
99
|
+
// SPAN_KIND_SERVER — a dispatched RPC is server-side request handling.
|
|
100
|
+
kind: 2,
|
|
101
|
+
name: event.functionPath,
|
|
102
|
+
spanId: otlpRandomHex(8),
|
|
103
|
+
startTimeUnixNano: otlpUnixNano(endMs - event.durationMs),
|
|
104
|
+
// STATUS_CODE_OK (1) / STATUS_CODE_ERROR (2).
|
|
105
|
+
status: event.ok ? { code: 1 } : { code: 2, message: event.error?.message ?? "" },
|
|
106
|
+
traceId: otlpRandomHex(16)
|
|
107
|
+
};
|
|
108
|
+
return wrapResourceSpans(span, "@lunora/runtime", serviceName);
|
|
109
|
+
};
|
|
110
|
+
const otlpLogBody = (event, serviceName) => {
|
|
111
|
+
const attributes = [encodeAttribute("lunora.function_path", event.functionPath)];
|
|
112
|
+
if (event.shardKey !== void 0) {
|
|
113
|
+
attributes.push(encodeAttribute("lunora.shard_key", event.shardKey));
|
|
114
|
+
}
|
|
115
|
+
if (event.userId !== void 0) {
|
|
116
|
+
attributes.push(encodeAttribute("lunora.user_id", event.userId));
|
|
117
|
+
}
|
|
118
|
+
const logRecord = {
|
|
119
|
+
attributes,
|
|
120
|
+
body: { stringValue: event.message },
|
|
121
|
+
severityNumber: OTLP_SEVERITY[event.level],
|
|
122
|
+
severityText: event.level.toUpperCase(),
|
|
123
|
+
timeUnixNano: otlpUnixNano(event.ts)
|
|
124
|
+
};
|
|
125
|
+
return wrapResourceLogs(logRecord, "@lunora/runtime", serviceName);
|
|
126
|
+
};
|
|
127
|
+
const otlpPost = (url, body, headers, context) => {
|
|
128
|
+
try {
|
|
129
|
+
const sent = fetch(url, { body: JSON.stringify(body), headers, method: "POST" }).catch(() => {
|
|
130
|
+
});
|
|
131
|
+
if (context?.waitUntil) {
|
|
132
|
+
context.waitUntil(sent);
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const consoleSink = (options = {}) => {
|
|
138
|
+
const { onlyErrors } = options;
|
|
139
|
+
return {
|
|
140
|
+
onLog: (event) => {
|
|
141
|
+
if (event.level === "error") {
|
|
142
|
+
console.error("[lunora:log]", event.functionPath, event.message);
|
|
143
|
+
} else {
|
|
144
|
+
console.log("[lunora:log]", event.functionPath, event.message);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
onRpc: (event) => {
|
|
148
|
+
if (shouldSkip(event, onlyErrors)) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (event.ok) {
|
|
152
|
+
console.log("[lunora:rpc]", event);
|
|
153
|
+
} else {
|
|
154
|
+
console.error("[lunora:rpc]", event);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
const webhookSink = (options) => {
|
|
160
|
+
const { headers, onlyErrors, transform, url } = options;
|
|
161
|
+
const mergedHeaders = mergeHeaders({ "content-type": "application/json" }, headers);
|
|
162
|
+
return {
|
|
163
|
+
onRpc: (event, context) => {
|
|
164
|
+
if (shouldSkip(event, onlyErrors)) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
let payload = event;
|
|
169
|
+
if (transform) {
|
|
170
|
+
try {
|
|
171
|
+
payload = transform(event);
|
|
172
|
+
} catch {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (payload === null || payload === void 0) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const sent = fetch(url, {
|
|
180
|
+
body: JSON.stringify(payload),
|
|
181
|
+
headers: mergedHeaders,
|
|
182
|
+
method: "POST"
|
|
183
|
+
}).catch(() => {
|
|
184
|
+
});
|
|
185
|
+
if (context?.waitUntil) {
|
|
186
|
+
context.waitUntil(sent);
|
|
187
|
+
}
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
const sentrySink = (options) => {
|
|
194
|
+
const { capture } = options;
|
|
195
|
+
const onlyErrors = options.onlyErrors ?? true;
|
|
196
|
+
return {
|
|
197
|
+
onRpc: (event) => {
|
|
198
|
+
if (shouldSkip(event, onlyErrors)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
capture(event);
|
|
203
|
+
} catch {
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
const analyticsEngineSink = (options) => {
|
|
209
|
+
const { dataset, onlyErrors } = options;
|
|
210
|
+
return {
|
|
211
|
+
onRpc: (event) => {
|
|
212
|
+
if (shouldSkip(event, onlyErrors)) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
dataset.writeDataPoint({
|
|
217
|
+
blobs: [event.functionPath, event.ok ? "ok" : "error", event.shardKey ?? "", event.error?.code ?? "", event.fanOut?.table ?? ""],
|
|
218
|
+
doubles: [event.durationMs, event.ok ? 0 : 1, event.fanOut?.shards ?? 0, event.fanOut?.failed ?? 0],
|
|
219
|
+
indexes: [event.functionPath]
|
|
220
|
+
});
|
|
221
|
+
} catch {
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
const otlpSink = (options) => {
|
|
227
|
+
const { endpoint, headers, onlyErrors, token } = options;
|
|
228
|
+
const serviceName = options.serviceName ?? "lunora";
|
|
229
|
+
let base = endpoint;
|
|
230
|
+
while (base.endsWith("/")) {
|
|
231
|
+
base = base.slice(0, -1);
|
|
232
|
+
}
|
|
233
|
+
const tracesUrl = `${base}/v1/traces`;
|
|
234
|
+
const logsUrl = `${base}/v1/logs`;
|
|
235
|
+
const mergedHeaders = mergeHeaders({ "content-type": "application/json" }, headers, token);
|
|
236
|
+
return {
|
|
237
|
+
onLog: (event, context) => {
|
|
238
|
+
otlpPost(logsUrl, otlpLogBody(event, serviceName), mergedHeaders, context);
|
|
239
|
+
},
|
|
240
|
+
onRpc: (event, context) => {
|
|
241
|
+
if (shouldSkip(event, onlyErrors)) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
otlpPost(tracesUrl, otlpTraceBody(event, serviceName, Date.now()), mergedHeaders, context);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
const combineSinks = (...sinks) => {
|
|
249
|
+
return {
|
|
250
|
+
onLog: (event, context) => {
|
|
251
|
+
for (const sink of sinks) {
|
|
252
|
+
if (!sink.onLog) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
sink.onLog(event, context);
|
|
257
|
+
} catch {
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
onRpc: (event, context) => {
|
|
262
|
+
for (const sink of sinks) {
|
|
263
|
+
if (!sink.onRpc) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
sink.onRpc(event, context);
|
|
268
|
+
} catch {
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
export { analyticsEngineSink, combineSinks, consoleSink, otlpSink, sentrySink, webhookSink };
|
|
@@ -5,7 +5,17 @@ import { wrapResolverWithContract } from './composeIdentityResolvers-XGjO7V1J.mj
|
|
|
5
5
|
export { composeIdentityResolvers, routeIdentityResolvers } from './composeIdentityResolvers-XGjO7V1J.mjs';
|
|
6
6
|
import { emitRpcEvent } from './emitLogEvent-pEdtqAK8.mjs';
|
|
7
7
|
import { resolveShard, applyJurisdiction } from './applyJurisdiction-BkZtTkct.mjs';
|
|
8
|
-
import { resolveSecurity, handleCorsPreflight, enforceOrigin, decorateResponse, enforceWebSocketOrigin } from './decorateResponse-
|
|
8
|
+
import { resolveSecurity, handleCorsPreflight, enforceOrigin, decorateResponse, enforceWebSocketOrigin } from './decorateResponse-DRWQFNhF.mjs';
|
|
9
|
+
|
|
10
|
+
const evictOldestEntry = (map, capacity) => {
|
|
11
|
+
if (map.size < capacity) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const oldest = map.keys().next().value;
|
|
15
|
+
if (oldest !== void 0) {
|
|
16
|
+
map.delete(oldest);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
9
19
|
|
|
10
20
|
const RELAY_NAME_INFIX = "::relay::";
|
|
11
21
|
const relayName = (ownerKey, index) => `${ownerKey}${RELAY_NAME_INFIX}${String(index)}`;
|
|
@@ -477,6 +487,9 @@ const normalizeBatchCall = (raw, index, defaultShard) => {
|
|
|
477
487
|
if (call.functionPath.startsWith("__lunora_relation__:") || call.functionPath.startsWith("__lunora_admin__")) {
|
|
478
488
|
throw new LunoraError("reserved function path cannot be batched", { code: "FORBIDDEN", status: 403 });
|
|
479
489
|
}
|
|
490
|
+
if (call.args !== void 0 && (typeof call.args !== "object" || call.args === null || Array.isArray(call.args))) {
|
|
491
|
+
throw new LunoraError("each batch call `args` must be an object", { code: "BAD_REQUEST", status: 400 });
|
|
492
|
+
}
|
|
480
493
|
return {
|
|
481
494
|
entry: {
|
|
482
495
|
args: call.args === void 0 ? {} : call.args,
|
|
@@ -2085,12 +2098,16 @@ const forwardToShard = async (namespace, shardKey, request) => {
|
|
|
2085
2098
|
};
|
|
2086
2099
|
const relayProbeCache = /* @__PURE__ */ new Map();
|
|
2087
2100
|
const RELAY_PROBE_TTL_MS = 5e3;
|
|
2101
|
+
const RELAY_PROBE_MAX_ENTRIES = 4096;
|
|
2088
2102
|
const probeRelayCount = async (namespace, shardKey) => {
|
|
2089
2103
|
const now = Date.now();
|
|
2090
2104
|
const cached = relayProbeCache.get(shardKey);
|
|
2091
2105
|
if (cached !== void 0 && cached.expiresMs > now) {
|
|
2092
2106
|
return cached.relayCount;
|
|
2093
2107
|
}
|
|
2108
|
+
if (cached !== void 0) {
|
|
2109
|
+
relayProbeCache.delete(shardKey);
|
|
2110
|
+
}
|
|
2094
2111
|
let relayCount = 0;
|
|
2095
2112
|
try {
|
|
2096
2113
|
const response = await resolveShard(namespace, shardKey).fetch(new Request("https://shard.internal/_lunora/route"));
|
|
@@ -2104,6 +2121,7 @@ const probeRelayCount = async (namespace, shardKey) => {
|
|
|
2104
2121
|
} catch {
|
|
2105
2122
|
relayCount = 0;
|
|
2106
2123
|
}
|
|
2124
|
+
evictOldestEntry(relayProbeCache, RELAY_PROBE_MAX_ENTRIES);
|
|
2107
2125
|
relayProbeCache.set(shardKey, { expiresMs: now + RELAY_PROBE_TTL_MS, relayCount });
|
|
2108
2126
|
return relayCount;
|
|
2109
2127
|
};
|
|
@@ -2515,9 +2533,12 @@ const createWorker = (options) => {
|
|
|
2515
2533
|
guardUnauthenticatedShardAccess("shard");
|
|
2516
2534
|
}
|
|
2517
2535
|
const upgradeHeaders = new Headers(request.headers);
|
|
2518
|
-
upgradeHeaders.
|
|
2519
|
-
|
|
2520
|
-
|
|
2536
|
+
const clientHeaderNames = [...upgradeHeaders.keys()];
|
|
2537
|
+
for (const name of clientHeaderNames) {
|
|
2538
|
+
if (name.startsWith("x-lunora-")) {
|
|
2539
|
+
upgradeHeaders.delete(name);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2521
2542
|
const forwardedUserId = forwardedHeaders["x-lunora-userid"];
|
|
2522
2543
|
const forwardedIdentity = forwardedHeaders["x-lunora-identity"];
|
|
2523
2544
|
const forwardedExp = forwardedHeaders["x-lunora-identity-exp"];
|
|
@@ -2602,12 +2623,6 @@ const createWorker = (options) => {
|
|
|
2602
2623
|
},
|
|
2603
2624
|
sinkContext
|
|
2604
2625
|
);
|
|
2605
|
-
const responseBookmark = response.headers.get("x-d1-bookmark");
|
|
2606
|
-
if (responseBookmark) {
|
|
2607
|
-
const headers = new Headers(response.headers);
|
|
2608
|
-
headers.set("x-d1-bookmark", responseBookmark);
|
|
2609
|
-
return new Response(response.body, { headers, status: response.status });
|
|
2610
|
-
}
|
|
2611
2626
|
return response;
|
|
2612
2627
|
} catch (error) {
|
|
2613
2628
|
emitRpcEvent(observability, buildErrorEvent(functionPath, Date.now() - rpcStartedAt, error, { shardKey }), sinkContext);
|
|
@@ -2714,7 +2729,7 @@ const createWorker = (options) => {
|
|
|
2714
2729
|
if (!Array.isArray(calls)) {
|
|
2715
2730
|
throw new LunoraError("RPC batch `calls` must be an array", { code: "BAD_REQUEST", status: 400 });
|
|
2716
2731
|
}
|
|
2717
|
-
const { headers: forwardedHeaders, identity } = await resolveForwardContext(request, env,
|
|
2732
|
+
const { headers: forwardedHeaders, identity } = await resolveForwardContext(request, env, publicResolveIdentity);
|
|
2718
2733
|
const groups = groupBatchCallsByShard(calls, defaultShard);
|
|
2719
2734
|
for (const entries of groups.values()) {
|
|
2720
2735
|
for (const entry of entries) {
|
|
@@ -2741,7 +2756,7 @@ const createWorker = (options) => {
|
|
|
2741
2756
|
}
|
|
2742
2757
|
} : void 0;
|
|
2743
2758
|
const results = [];
|
|
2744
|
-
|
|
2759
|
+
const bookmarks = [];
|
|
2745
2760
|
const slotError = (entry, status, code, message) => {
|
|
2746
2761
|
return { body: { error: { code, message } }, id: entry.id, status };
|
|
2747
2762
|
};
|
|
@@ -2792,7 +2807,7 @@ const createWorker = (options) => {
|
|
|
2792
2807
|
const durationMs = Date.now() - subStartedAt;
|
|
2793
2808
|
const bookmark = response.headers.get("x-d1-bookmark");
|
|
2794
2809
|
if (bookmark) {
|
|
2795
|
-
|
|
2810
|
+
bookmarks.push(bookmark);
|
|
2796
2811
|
}
|
|
2797
2812
|
let parsed;
|
|
2798
2813
|
try {
|
|
@@ -2823,8 +2838,9 @@ const createWorker = (options) => {
|
|
|
2823
2838
|
})
|
|
2824
2839
|
);
|
|
2825
2840
|
const responseHeaders = { "content-type": "application/json" };
|
|
2826
|
-
|
|
2827
|
-
|
|
2841
|
+
const [onlyBookmark] = bookmarks;
|
|
2842
|
+
if (bookmarks.length === 1 && onlyBookmark !== void 0) {
|
|
2843
|
+
responseHeaders["x-d1-bookmark"] = onlyBookmark;
|
|
2828
2844
|
}
|
|
2829
2845
|
return Response.json({ results }, { headers: responseHeaders, status: 200 });
|
|
2830
2846
|
};
|
|
@@ -2879,13 +2895,14 @@ const createWorker = (options) => {
|
|
|
2879
2895
|
if (!coordinator) {
|
|
2880
2896
|
throw new LunoraError("scheduled backup requires a `queryCoordinator` on the worker", { code: "BACKUP_NOT_CONFIGURED", status: 500 });
|
|
2881
2897
|
}
|
|
2882
|
-
|
|
2883
|
-
|
|
2898
|
+
const adminToken = effectiveAdminToken();
|
|
2899
|
+
if (!adminToken || adminToken.length === 0) {
|
|
2900
|
+
throw new LunoraError("scheduled backup requires an `adminToken` (or `env.LUNORA_ADMIN_TOKEN`) to authenticate the per-shard export gate", {
|
|
2884
2901
|
code: "BACKUP_NOT_CONFIGURED",
|
|
2885
2902
|
status: 500
|
|
2886
2903
|
});
|
|
2887
2904
|
}
|
|
2888
|
-
const forwardedHeaders = { authorization: `Bearer ${
|
|
2905
|
+
const forwardedHeaders = { authorization: `Bearer ${adminToken}`, "content-type": "application/json" };
|
|
2889
2906
|
const tables = options.backupTables;
|
|
2890
2907
|
let rows = 0;
|
|
2891
2908
|
let bytes = 0;
|
|
@@ -2931,6 +2948,7 @@ const createWorker = (options) => {
|
|
|
2931
2948
|
await pruneBackups(store, prefix);
|
|
2932
2949
|
};
|
|
2933
2950
|
const handleScheduled = async (controller, env, context) => {
|
|
2951
|
+
resolveAdminTokenFromEnv(env);
|
|
2934
2952
|
const errors = [];
|
|
2935
2953
|
const toError = (error) => error instanceof Error ? error : new Error(String(error));
|
|
2936
2954
|
const userHandler = options.crons?.[controller.cron];
|
|
@@ -3047,7 +3065,8 @@ const createWorker = (options) => {
|
|
|
3047
3065
|
const url = new URL(request.url);
|
|
3048
3066
|
if (request.method === "POST" || request.method === "PUT") {
|
|
3049
3067
|
const contentLength = Number(request.headers.get("content-length") ?? "");
|
|
3050
|
-
|
|
3068
|
+
const maxBodyBytes = url.pathname === KV_VALUE_PATH ? KV_VALUE_MAX_BODY_BYTES : MAX_BODY_BYTES;
|
|
3069
|
+
if (Number.isFinite(contentLength) && contentLength > maxBodyBytes) {
|
|
3051
3070
|
throw new LunoraError("Body too large", { code: "PAYLOAD_TOO_LARGE", status: 413 });
|
|
3052
3071
|
}
|
|
3053
3072
|
}
|
|
@@ -3148,4 +3167,4 @@ const resolveLunoraOptions = (options, env) => {
|
|
|
3148
3167
|
const createLunoraHandler = (options = {}) => (request, env, context) => createWorker(resolveLunoraOptions(options, env)).fetch(request, env, context ?? NOOP_EXECUTION_CONTEXT);
|
|
3149
3168
|
const defineRpcEnvelope = (envelope) => envelope;
|
|
3150
3169
|
|
|
3151
|
-
export { NOOP_EXECUTION_CONTEXT, composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, resolveLunoraOptions, withFrameworkWorker };
|
|
3170
|
+
export { NOOP_EXECUTION_CONTEXT, composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, probeRelayCount, resolveLunoraOptions, withFrameworkWorker };
|
|
@@ -202,7 +202,14 @@ const handleCorsPreflight = (request, resolved) => {
|
|
|
202
202
|
const headers = corsResponseHeaders(origin, resolved.cors);
|
|
203
203
|
const requested = request.headers.get("access-control-request-headers");
|
|
204
204
|
headers.set("access-control-allow-methods", resolved.cors.allowedMethods.join(", "));
|
|
205
|
-
|
|
205
|
+
let allowedHeaders;
|
|
206
|
+
if (requested === null) {
|
|
207
|
+
allowedHeaders = resolved.cors.allowedHeaders.join(", ");
|
|
208
|
+
} else {
|
|
209
|
+
const permitted = new Set(resolved.cors.allowedHeaders.map((name) => name.toLowerCase()));
|
|
210
|
+
allowedHeaders = requested.split(",").map((name) => name.trim()).filter((name) => name.length > 0 && permitted.has(name.toLowerCase())).join(", ");
|
|
211
|
+
}
|
|
212
|
+
headers.set("access-control-allow-headers", allowedHeaders);
|
|
206
213
|
headers.set("access-control-max-age", String(resolved.cors.maxAge));
|
|
207
214
|
return new Response(null, { headers, status: 204 });
|
|
208
215
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/runtime",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.25",
|
|
4
4
|
"description": "Lunora Worker runtime: the RPC router, shard resolver, and query coordinator",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@lunora/errors": "1.0.0-alpha.
|
|
49
|
+
"@lunora/errors": "1.0.0-alpha.4"
|
|
50
50
|
},
|
|
51
51
|
"engines": {
|
|
52
52
|
"node": "^22.15.0 || >=24.11.0"
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
const shouldSkip = (event, onlyErrors) => onlyErrors === true && event.ok;
|
|
2
|
-
const mergeHeaders = (defaults, overrides) => {
|
|
3
|
-
if (!overrides) {
|
|
4
|
-
return { ...defaults };
|
|
5
|
-
}
|
|
6
|
-
const merged = {};
|
|
7
|
-
const seen = /* @__PURE__ */ new Map();
|
|
8
|
-
for (const [name, value] of Object.entries(defaults)) {
|
|
9
|
-
const lower = name.toLowerCase();
|
|
10
|
-
seen.set(lower, name);
|
|
11
|
-
merged[name] = value;
|
|
12
|
-
}
|
|
13
|
-
for (const [name, value] of Object.entries(overrides)) {
|
|
14
|
-
const lower = name.toLowerCase();
|
|
15
|
-
const existing = seen.get(lower);
|
|
16
|
-
if (existing === void 0) {
|
|
17
|
-
seen.set(lower, name);
|
|
18
|
-
merged[name] = value;
|
|
19
|
-
} else {
|
|
20
|
-
merged[existing] = value;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return merged;
|
|
24
|
-
};
|
|
25
|
-
const consoleSink = (options = {}) => {
|
|
26
|
-
const { onlyErrors } = options;
|
|
27
|
-
return {
|
|
28
|
-
onLog: (event) => {
|
|
29
|
-
if (event.level === "error") {
|
|
30
|
-
console.error("[lunora:log]", event.functionPath, event.message);
|
|
31
|
-
} else {
|
|
32
|
-
console.log("[lunora:log]", event.functionPath, event.message);
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
onRpc: (event) => {
|
|
36
|
-
if (shouldSkip(event, onlyErrors)) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
if (event.ok) {
|
|
40
|
-
console.log("[lunora:rpc]", event);
|
|
41
|
-
} else {
|
|
42
|
-
console.error("[lunora:rpc]", event);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
const webhookSink = (options) => {
|
|
48
|
-
const { headers, onlyErrors, transform, url } = options;
|
|
49
|
-
const mergedHeaders = mergeHeaders({ "content-type": "application/json" }, headers);
|
|
50
|
-
return {
|
|
51
|
-
onRpc: (event, context) => {
|
|
52
|
-
if (shouldSkip(event, onlyErrors)) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
try {
|
|
56
|
-
let payload = event;
|
|
57
|
-
if (transform) {
|
|
58
|
-
try {
|
|
59
|
-
payload = transform(event);
|
|
60
|
-
} catch {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
if (payload === null || payload === void 0) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
const sent = fetch(url, {
|
|
68
|
-
body: JSON.stringify(payload),
|
|
69
|
-
headers: mergedHeaders,
|
|
70
|
-
method: "POST"
|
|
71
|
-
}).catch(() => {
|
|
72
|
-
});
|
|
73
|
-
if (context?.waitUntil) {
|
|
74
|
-
context.waitUntil(sent);
|
|
75
|
-
}
|
|
76
|
-
} catch {
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
};
|
|
81
|
-
const sentrySink = (options) => {
|
|
82
|
-
const { capture } = options;
|
|
83
|
-
const onlyErrors = options.onlyErrors ?? true;
|
|
84
|
-
return {
|
|
85
|
-
onRpc: (event) => {
|
|
86
|
-
if (shouldSkip(event, onlyErrors)) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
try {
|
|
90
|
-
capture(event);
|
|
91
|
-
} catch {
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
};
|
|
96
|
-
const analyticsEngineSink = (options) => {
|
|
97
|
-
const { dataset, onlyErrors } = options;
|
|
98
|
-
return {
|
|
99
|
-
onRpc: (event) => {
|
|
100
|
-
if (shouldSkip(event, onlyErrors)) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
try {
|
|
104
|
-
dataset.writeDataPoint({
|
|
105
|
-
blobs: [event.functionPath, event.ok ? "ok" : "error", event.shardKey ?? "", event.error?.code ?? "", event.fanOut?.table ?? ""],
|
|
106
|
-
doubles: [event.durationMs, event.ok ? 0 : 1, event.fanOut?.shards ?? 0, event.fanOut?.failed ?? 0],
|
|
107
|
-
indexes: [event.functionPath]
|
|
108
|
-
});
|
|
109
|
-
} catch {
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
};
|
|
114
|
-
const combineSinks = (...sinks) => {
|
|
115
|
-
return {
|
|
116
|
-
onLog: (event, context) => {
|
|
117
|
-
for (const sink of sinks) {
|
|
118
|
-
if (!sink.onLog) {
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
try {
|
|
122
|
-
sink.onLog(event, context);
|
|
123
|
-
} catch {
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
onRpc: (event, context) => {
|
|
128
|
-
for (const sink of sinks) {
|
|
129
|
-
if (!sink.onRpc) {
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
try {
|
|
133
|
-
sink.onRpc(event, context);
|
|
134
|
-
} catch {
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
export { analyticsEngineSink, combineSinks, consoleSink, sentrySink, webhookSink };
|