@lunora/runtime 1.0.0-alpha.30 → 1.0.0-alpha.31

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 CHANGED
@@ -2,6 +2,7 @@ import { RankDirection, RankPageRow, DatabaseWriterLike } from '@lunora/do';
2
2
  export type { RankDirection as RankPageDirection, RankPageRowKey as RankPageKey, RankPageRow, ShardRankPageResult } from '@lunora/do';
3
3
  import { WorkflowsRestClient } from '@lunora/workflow';
4
4
  import { LunoraError as LunoraError$1, ErrorBody } from '@lunora/errors';
5
+ import { R2SqlClient } from '@lunora/bindings/r2sql';
5
6
  /**
6
7
  * Turn-key incremental-sync source helpers for warehouse connectors
7
8
  * (Fivetran custom functions, Airbyte incremental sources).
@@ -2924,6 +2925,17 @@ interface PipelineLike {
2924
2925
  interface PipelineLogSinkOptions {
2925
2926
  /** The Cloudflare Pipeline binding each log record is durably sent to. */
2926
2927
  pipeline: PipelineLike;
2928
+ /**
2929
+ * When true, `fields` is written as a **JSON string** (`JSON.stringify`)
2930
+ * rather than a nested object. Defaults to `false` for back-compatibility.
2931
+ *
2932
+ * Turn it on when the destination Iceberg table types `fields` as a `string`
2933
+ * column so the archive stays queryable (R2 SQL can `LIKE`/compare a string
2934
+ * column, but not index into an arbitrarily-shaped struct). The reader
2935
+ * (`createPipelineLogReader`) parses such a JSON string back to an object on
2936
+ * read. Leave it off when the table types `fields` as a native struct.
2937
+ */
2938
+ serializeFields?: boolean;
2927
2939
  }
2928
2940
  /**
2929
2941
  * A sink that durably persists each `ctx.log` line to a Cloudflare Pipeline
@@ -2933,6 +2945,20 @@ interface PipelineLogSinkOptions {
2933
2945
  * structured record (message, level, function path, fields, trace ids, shard,
2934
2946
  * user, timestamp) in object storage under the app's own account.
2935
2947
  *
2948
+ * **Written-column contract.** Each record is a flat object; this is the exact
2949
+ * read-side schema `createPipelineLogReader` (`pipeline-log-reader.ts`) mirrors
2950
+ * in its `DEFAULT_LOG_COLUMNS`. Keep the two in lockstep — a column added here
2951
+ * must gain a default there:
2952
+ * - `functionPath` (string) — always present
2953
+ * - `level` (string severity) — always present
2954
+ * - `message` (string) — always present
2955
+ * - `ts` (number, epoch-millis) — always present
2956
+ * - `fields` (nested object, or a JSON string when `serializeFields`) — when set
2957
+ * - `shardKey` (string) — when set
2958
+ * - `userId` (string) — when set
2959
+ * - `traceId` (string) — when set
2960
+ * - `spanId` (string) — when set
2961
+ *
2936
2962
  * Only `onLog` is implemented — RPC-span metrics belong in
2937
2963
  * {@link analyticsEngineSink}. `Pipeline.send` is durable/fire-and-forget on the
2938
2964
  * platform; the call is registered with the request's `context.waitUntil` when
@@ -2943,7 +2969,8 @@ interface PipelineLogSinkOptions {
2943
2969
  * Privacy: the persisted record carries `message` + structured `fields` (not the
2944
2970
  * raw positional args). They may include user input — the R2 bucket is your own,
2945
2971
  * but treat it as a log store and gate PII upstream if that is a concern.
2946
- * @param options Sink options: `pipeline` is the Cloudflare Pipeline binding.
2972
+ * @param options Sink options: `pipeline` is the Cloudflare Pipeline binding;
2973
+ * `serializeFields` stores `fields` as a queryable JSON string.
2947
2974
  */
2948
2975
  declare const pipelineLogSink: (options: PipelineLogSinkOptions) => ObservabilitySink;
2949
2976
  /** Options for {@link otlpSink}. */
@@ -3010,5 +3037,140 @@ declare const otlpSink: (options: OtlpSinkOptions) => ObservabilitySink;
3010
3037
  * @param sinks The sinks to fan out to.
3011
3038
  */
3012
3039
  declare const combineSinks: (...sinks: ObservabilitySink[]) => ObservabilitySink;
3040
+ /**
3041
+ * The written-column contract: every field `pipelineLogSink` emits, mapped to the
3042
+ * column it is stored under by default (the identity mapping). Also the source of
3043
+ * truth for the {@link PipelineLogField} union. Mirrors the record built in
3044
+ * `pipelineLogSink` — the read side of the same contract.
3045
+ */
3046
+ declare const DEFAULT_COLUMNS: {
3047
+ readonly fields: "fields";
3048
+ readonly functionPath: "functionPath";
3049
+ readonly level: "level";
3050
+ readonly message: "message";
3051
+ readonly shardKey: "shardKey";
3052
+ readonly spanId: "spanId";
3053
+ readonly traceId: "traceId";
3054
+ readonly ts: "ts";
3055
+ readonly userId: "userId";
3056
+ };
3057
+ /**
3058
+ * The canonical field names of one persisted log record — the keys
3059
+ * `pipelineLogSink` writes. Used as the {@link PipelineLogColumnMap} keys and the
3060
+ * {@link PipelineLogRow} shape, so the reader stays decoupled from whatever
3061
+ * physical column names the operator's Iceberg table happens to use.
3062
+ */
3063
+ type PipelineLogField = keyof typeof DEFAULT_COLUMNS;
3064
+ /**
3065
+ * Field-to-column-name map. Defaults to the identity mapping (each field stored
3066
+ * under its own name, matching what `pipelineLogSink` writes). Override per-field
3067
+ * when the Iceberg schema renames a column; unspecified fields keep their
3068
+ * default. This is the single knob that lets one reader serve differently shaped
3069
+ * Data Catalog tables.
3070
+ */
3071
+ type PipelineLogColumnMap = Partial<Record<PipelineLogField, string>>;
3072
+ /** An opaque keyset cursor: the `ts` of the row after the last one returned. */
3073
+ interface PipelineLogCursor {
3074
+ /** Epoch-millis boundary; the next page is every row strictly older than this. */
3075
+ ts: number;
3076
+ }
3077
+ /** Filters for one {@link PipelineLogReader} query. Every value is inlined safely (`lit`/`sql`). */
3078
+ interface PipelineLogQuery {
3079
+ /** Continue after a previous page (keyset on `ts DESC`). Combined with the other filters. */
3080
+ cursor?: PipelineLogCursor;
3081
+ /** Match only this exact function path's records. Prefer `functionPathPrefix` for a namespace sweep. */
3082
+ functionPath?: string;
3083
+ /** Match records whose `functionPath` starts with this string (rendered as a `LIKE 'prefix%'`). */
3084
+ functionPathPrefix?: string;
3085
+ /** Match only this exact severity. When set, `minLevel` is ignored for the same field. */
3086
+ level?: ContextLogLevel;
3087
+ /** Max rows to return. Clamped to `[1, 10000]`; defaults to {@link DEFAULT_LOG_LIMIT}. */
3088
+ limit?: number;
3089
+ /** Severity floor: keep every level at or above this in {@link LOG_LEVEL_ORDER} (e.g. `warn` keeps warn, error, fatal). */
3090
+ minLevel?: ContextLogLevel;
3091
+ /** Match only this shard key. */
3092
+ shardKey?: string;
3093
+ /** Lower time bound, inclusive (`ts` at or after this), epoch-millis. */
3094
+ sinceTs?: number;
3095
+ /** Match only this trace id. */
3096
+ traceId?: string;
3097
+ /** Upper time bound, inclusive (`ts` at or before this), epoch-millis. */
3098
+ untilTs?: number;
3099
+ /** Match only this acting user id. */
3100
+ userId?: string;
3101
+ }
3102
+ /**
3103
+ * One decoded log record. Always keyed by the canonical {@link PipelineLogField}
3104
+ * names regardless of the physical columns (the reader remaps via `columnMap`),
3105
+ * so consumers never see the operator's storage names.
3106
+ */
3107
+ interface PipelineLogRow {
3108
+ /**
3109
+ * Structured fields, when the record carried them. A `serializeFields` sink
3110
+ * stores these as a JSON string, which the reader parses back to an object;
3111
+ * a plain string that is not valid JSON is returned verbatim.
3112
+ */
3113
+ fields?: unknown;
3114
+ /** Function path that emitted the line, e.g. `"messages:list"`. */
3115
+ functionPath: string;
3116
+ /** Severity the line was logged at. */
3117
+ level: ContextLogLevel;
3118
+ /** Rendered message. */
3119
+ message: string;
3120
+ /** Shard key for single-shard calls, when present. */
3121
+ shardKey?: string;
3122
+ /** Span id the line was emitted under, when present. */
3123
+ spanId?: string;
3124
+ /** Trace id the line belongs to, when present. */
3125
+ traceId?: string;
3126
+ /** Epoch-millis the line was emitted. */
3127
+ ts: number;
3128
+ /** Acting user id, when present. */
3129
+ userId?: string;
3130
+ }
3131
+ /** One page of {@link PipelineLogRow}s, newest first, plus the cursor for the next page (absent means last page). */
3132
+ interface PipelineLogPage {
3133
+ /** The cursor to pass as {@link PipelineLogQuery} `cursor` for the following page; absent when this is the last page. */
3134
+ nextCursor?: PipelineLogCursor;
3135
+ /** The rows, ordered `ts DESC` (newest first). At most `limit` of them. */
3136
+ rows: PipelineLogRow[];
3137
+ }
3138
+ /** Options for {@link createPipelineLogReader}. */
3139
+ interface PipelineLogReaderOptions {
3140
+ /**
3141
+ * Override any physical column name that diverges from the default (identity)
3142
+ * mapping. Unspecified fields keep their {@link DEFAULT_LOG_COLUMNS} name.
3143
+ */
3144
+ columnMap?: PipelineLogColumnMap;
3145
+ /**
3146
+ * The Iceberg namespace the `table` lives in (R2 Data Catalog database).
3147
+ * Combined as `namespace.table` in the `FROM` clause; omit when `table`
3148
+ * already carries its namespace.
3149
+ */
3150
+ namespace?: string;
3151
+ /** The Iceberg table name the Pipeline writes log records to (e.g. `"logs"`). */
3152
+ table: string;
3153
+ }
3154
+ /** The reader surface: a single keyset-paginated {@link PipelineLogPage} query. */
3155
+ interface PipelineLogReader {
3156
+ /** Run one filtered, keyset-paginated read and return a {@link PipelineLogPage}. */
3157
+ query: (query?: PipelineLogQuery) => Promise<PipelineLogPage>;
3158
+ }
3159
+ /** The written-column contract exposed publicly: canonical field to default physical column name. */
3160
+ declare const DEFAULT_LOG_COLUMNS: Readonly<Record<PipelineLogField, string>>;
3161
+ /** Default page size when a query omits `limit`. */
3162
+ declare const DEFAULT_LOG_LIMIT: number;
3163
+ /**
3164
+ * Build a durable-log reader over one R2 Data Catalog (Iceberg) table.
3165
+ *
3166
+ * The returned {@link PipelineLogReader} compiles each call to a safe
3167
+ * `SELECT ... WHERE ... ORDER BY ts DESC LIMIT n` (over-fetching by one) and
3168
+ * decodes the rows back to the canonical {@link PipelineLogRow} shape. All value
3169
+ * filters are escaped through `@lunora/bindings/r2sql`'s `sql`/`lit`; column
3170
+ * names come from `options.columnMap` (operator config), spliced with `raw`.
3171
+ * @param client An {@link R2SqlClient} (`createR2Sql({ accountId, apiToken, bucket })`).
3172
+ * @param options The target `table` (plus optional `namespace`) and any `columnMap` overrides.
3173
+ */
3174
+ declare const createPipelineLogReader: (client: R2SqlClient, options: PipelineLogReaderOptions) => PipelineLogReader;
3013
3175
  declare const VERSION: string;
3014
- export { type AdminTableResolver, type AirbyteMessage, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, type AnalyticsEngineSinkOptions, type AuthAdmin, type AuthCapabilities, type AuthConfigInfo, type AuthImpersonation, 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 LogFields, type LogLevel, LunoraError, type LunoraErrorBody, type LunoraHandlerOptions, type LunoraWorker, type MergeStrategy, type MetricEvent, type MetricKind, type MigrationFanOutRequest, type MigrationFanOutResult, NOOP_EXECUTION_CONTEXT, type ObservabilityEvent, type ObservabilitySink, type ObservabilitySinkContext, type OtlpSinkOptions, type PipelineLike, type PipelineLogSinkOptions, 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 SpanEvent, 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, pipelineLogSink, resolveLunoraOptions, resolveSecurity, resolveShard, routeIdentityResolvers, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
3176
+ export { type AdminTableResolver, type AirbyteMessage, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, type AnalyticsEngineSinkOptions, type AuthAdmin, type AuthCapabilities, type AuthConfigInfo, type AuthImpersonation, 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_LOG_COLUMNS, DEFAULT_LOG_LIMIT, 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 LogFields, type LogLevel, LunoraError, type LunoraErrorBody, type LunoraHandlerOptions, type LunoraWorker, type MergeStrategy, type MetricEvent, type MetricKind, type MigrationFanOutRequest, type MigrationFanOutResult, NOOP_EXECUTION_CONTEXT, type ObservabilityEvent, type ObservabilitySink, type ObservabilitySinkContext, type OtlpSinkOptions, type PipelineLike, type PipelineLogColumnMap, type PipelineLogCursor, type PipelineLogField, type PipelineLogPage, type PipelineLogQuery, type PipelineLogReader, type PipelineLogReaderOptions, type PipelineLogRow, type PipelineLogSinkOptions, 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 SpanEvent, 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, createPipelineLogReader, createQueryCoordinator, createStaticShardRegistry, createWorker, decorateResponse, defineRpcEnvelope, emitLogEvent, emitRpcEvent, enforceOrigin, handleCorsPreflight, mergeStrategyForAggregate, otlpSink, pipelineLogSink, resolveLunoraOptions, resolveSecurity, resolveShard, routeIdentityResolvers, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { RankDirection, RankPageRow, DatabaseWriterLike } from '@lunora/do';
2
2
  export type { RankDirection as RankPageDirection, RankPageRowKey as RankPageKey, RankPageRow, ShardRankPageResult } from '@lunora/do';
3
3
  import { WorkflowsRestClient } from '@lunora/workflow';
4
4
  import { LunoraError as LunoraError$1, ErrorBody } from '@lunora/errors';
5
+ import { R2SqlClient } from '@lunora/bindings/r2sql';
5
6
  /**
6
7
  * Turn-key incremental-sync source helpers for warehouse connectors
7
8
  * (Fivetran custom functions, Airbyte incremental sources).
@@ -2924,6 +2925,17 @@ interface PipelineLike {
2924
2925
  interface PipelineLogSinkOptions {
2925
2926
  /** The Cloudflare Pipeline binding each log record is durably sent to. */
2926
2927
  pipeline: PipelineLike;
2928
+ /**
2929
+ * When true, `fields` is written as a **JSON string** (`JSON.stringify`)
2930
+ * rather than a nested object. Defaults to `false` for back-compatibility.
2931
+ *
2932
+ * Turn it on when the destination Iceberg table types `fields` as a `string`
2933
+ * column so the archive stays queryable (R2 SQL can `LIKE`/compare a string
2934
+ * column, but not index into an arbitrarily-shaped struct). The reader
2935
+ * (`createPipelineLogReader`) parses such a JSON string back to an object on
2936
+ * read. Leave it off when the table types `fields` as a native struct.
2937
+ */
2938
+ serializeFields?: boolean;
2927
2939
  }
2928
2940
  /**
2929
2941
  * A sink that durably persists each `ctx.log` line to a Cloudflare Pipeline
@@ -2933,6 +2945,20 @@ interface PipelineLogSinkOptions {
2933
2945
  * structured record (message, level, function path, fields, trace ids, shard,
2934
2946
  * user, timestamp) in object storage under the app's own account.
2935
2947
  *
2948
+ * **Written-column contract.** Each record is a flat object; this is the exact
2949
+ * read-side schema `createPipelineLogReader` (`pipeline-log-reader.ts`) mirrors
2950
+ * in its `DEFAULT_LOG_COLUMNS`. Keep the two in lockstep — a column added here
2951
+ * must gain a default there:
2952
+ * - `functionPath` (string) — always present
2953
+ * - `level` (string severity) — always present
2954
+ * - `message` (string) — always present
2955
+ * - `ts` (number, epoch-millis) — always present
2956
+ * - `fields` (nested object, or a JSON string when `serializeFields`) — when set
2957
+ * - `shardKey` (string) — when set
2958
+ * - `userId` (string) — when set
2959
+ * - `traceId` (string) — when set
2960
+ * - `spanId` (string) — when set
2961
+ *
2936
2962
  * Only `onLog` is implemented — RPC-span metrics belong in
2937
2963
  * {@link analyticsEngineSink}. `Pipeline.send` is durable/fire-and-forget on the
2938
2964
  * platform; the call is registered with the request's `context.waitUntil` when
@@ -2943,7 +2969,8 @@ interface PipelineLogSinkOptions {
2943
2969
  * Privacy: the persisted record carries `message` + structured `fields` (not the
2944
2970
  * raw positional args). They may include user input — the R2 bucket is your own,
2945
2971
  * but treat it as a log store and gate PII upstream if that is a concern.
2946
- * @param options Sink options: `pipeline` is the Cloudflare Pipeline binding.
2972
+ * @param options Sink options: `pipeline` is the Cloudflare Pipeline binding;
2973
+ * `serializeFields` stores `fields` as a queryable JSON string.
2947
2974
  */
2948
2975
  declare const pipelineLogSink: (options: PipelineLogSinkOptions) => ObservabilitySink;
2949
2976
  /** Options for {@link otlpSink}. */
@@ -3010,5 +3037,140 @@ declare const otlpSink: (options: OtlpSinkOptions) => ObservabilitySink;
3010
3037
  * @param sinks The sinks to fan out to.
3011
3038
  */
3012
3039
  declare const combineSinks: (...sinks: ObservabilitySink[]) => ObservabilitySink;
3040
+ /**
3041
+ * The written-column contract: every field `pipelineLogSink` emits, mapped to the
3042
+ * column it is stored under by default (the identity mapping). Also the source of
3043
+ * truth for the {@link PipelineLogField} union. Mirrors the record built in
3044
+ * `pipelineLogSink` — the read side of the same contract.
3045
+ */
3046
+ declare const DEFAULT_COLUMNS: {
3047
+ readonly fields: "fields";
3048
+ readonly functionPath: "functionPath";
3049
+ readonly level: "level";
3050
+ readonly message: "message";
3051
+ readonly shardKey: "shardKey";
3052
+ readonly spanId: "spanId";
3053
+ readonly traceId: "traceId";
3054
+ readonly ts: "ts";
3055
+ readonly userId: "userId";
3056
+ };
3057
+ /**
3058
+ * The canonical field names of one persisted log record — the keys
3059
+ * `pipelineLogSink` writes. Used as the {@link PipelineLogColumnMap} keys and the
3060
+ * {@link PipelineLogRow} shape, so the reader stays decoupled from whatever
3061
+ * physical column names the operator's Iceberg table happens to use.
3062
+ */
3063
+ type PipelineLogField = keyof typeof DEFAULT_COLUMNS;
3064
+ /**
3065
+ * Field-to-column-name map. Defaults to the identity mapping (each field stored
3066
+ * under its own name, matching what `pipelineLogSink` writes). Override per-field
3067
+ * when the Iceberg schema renames a column; unspecified fields keep their
3068
+ * default. This is the single knob that lets one reader serve differently shaped
3069
+ * Data Catalog tables.
3070
+ */
3071
+ type PipelineLogColumnMap = Partial<Record<PipelineLogField, string>>;
3072
+ /** An opaque keyset cursor: the `ts` of the row after the last one returned. */
3073
+ interface PipelineLogCursor {
3074
+ /** Epoch-millis boundary; the next page is every row strictly older than this. */
3075
+ ts: number;
3076
+ }
3077
+ /** Filters for one {@link PipelineLogReader} query. Every value is inlined safely (`lit`/`sql`). */
3078
+ interface PipelineLogQuery {
3079
+ /** Continue after a previous page (keyset on `ts DESC`). Combined with the other filters. */
3080
+ cursor?: PipelineLogCursor;
3081
+ /** Match only this exact function path's records. Prefer `functionPathPrefix` for a namespace sweep. */
3082
+ functionPath?: string;
3083
+ /** Match records whose `functionPath` starts with this string (rendered as a `LIKE 'prefix%'`). */
3084
+ functionPathPrefix?: string;
3085
+ /** Match only this exact severity. When set, `minLevel` is ignored for the same field. */
3086
+ level?: ContextLogLevel;
3087
+ /** Max rows to return. Clamped to `[1, 10000]`; defaults to {@link DEFAULT_LOG_LIMIT}. */
3088
+ limit?: number;
3089
+ /** Severity floor: keep every level at or above this in {@link LOG_LEVEL_ORDER} (e.g. `warn` keeps warn, error, fatal). */
3090
+ minLevel?: ContextLogLevel;
3091
+ /** Match only this shard key. */
3092
+ shardKey?: string;
3093
+ /** Lower time bound, inclusive (`ts` at or after this), epoch-millis. */
3094
+ sinceTs?: number;
3095
+ /** Match only this trace id. */
3096
+ traceId?: string;
3097
+ /** Upper time bound, inclusive (`ts` at or before this), epoch-millis. */
3098
+ untilTs?: number;
3099
+ /** Match only this acting user id. */
3100
+ userId?: string;
3101
+ }
3102
+ /**
3103
+ * One decoded log record. Always keyed by the canonical {@link PipelineLogField}
3104
+ * names regardless of the physical columns (the reader remaps via `columnMap`),
3105
+ * so consumers never see the operator's storage names.
3106
+ */
3107
+ interface PipelineLogRow {
3108
+ /**
3109
+ * Structured fields, when the record carried them. A `serializeFields` sink
3110
+ * stores these as a JSON string, which the reader parses back to an object;
3111
+ * a plain string that is not valid JSON is returned verbatim.
3112
+ */
3113
+ fields?: unknown;
3114
+ /** Function path that emitted the line, e.g. `"messages:list"`. */
3115
+ functionPath: string;
3116
+ /** Severity the line was logged at. */
3117
+ level: ContextLogLevel;
3118
+ /** Rendered message. */
3119
+ message: string;
3120
+ /** Shard key for single-shard calls, when present. */
3121
+ shardKey?: string;
3122
+ /** Span id the line was emitted under, when present. */
3123
+ spanId?: string;
3124
+ /** Trace id the line belongs to, when present. */
3125
+ traceId?: string;
3126
+ /** Epoch-millis the line was emitted. */
3127
+ ts: number;
3128
+ /** Acting user id, when present. */
3129
+ userId?: string;
3130
+ }
3131
+ /** One page of {@link PipelineLogRow}s, newest first, plus the cursor for the next page (absent means last page). */
3132
+ interface PipelineLogPage {
3133
+ /** The cursor to pass as {@link PipelineLogQuery} `cursor` for the following page; absent when this is the last page. */
3134
+ nextCursor?: PipelineLogCursor;
3135
+ /** The rows, ordered `ts DESC` (newest first). At most `limit` of them. */
3136
+ rows: PipelineLogRow[];
3137
+ }
3138
+ /** Options for {@link createPipelineLogReader}. */
3139
+ interface PipelineLogReaderOptions {
3140
+ /**
3141
+ * Override any physical column name that diverges from the default (identity)
3142
+ * mapping. Unspecified fields keep their {@link DEFAULT_LOG_COLUMNS} name.
3143
+ */
3144
+ columnMap?: PipelineLogColumnMap;
3145
+ /**
3146
+ * The Iceberg namespace the `table` lives in (R2 Data Catalog database).
3147
+ * Combined as `namespace.table` in the `FROM` clause; omit when `table`
3148
+ * already carries its namespace.
3149
+ */
3150
+ namespace?: string;
3151
+ /** The Iceberg table name the Pipeline writes log records to (e.g. `"logs"`). */
3152
+ table: string;
3153
+ }
3154
+ /** The reader surface: a single keyset-paginated {@link PipelineLogPage} query. */
3155
+ interface PipelineLogReader {
3156
+ /** Run one filtered, keyset-paginated read and return a {@link PipelineLogPage}. */
3157
+ query: (query?: PipelineLogQuery) => Promise<PipelineLogPage>;
3158
+ }
3159
+ /** The written-column contract exposed publicly: canonical field to default physical column name. */
3160
+ declare const DEFAULT_LOG_COLUMNS: Readonly<Record<PipelineLogField, string>>;
3161
+ /** Default page size when a query omits `limit`. */
3162
+ declare const DEFAULT_LOG_LIMIT: number;
3163
+ /**
3164
+ * Build a durable-log reader over one R2 Data Catalog (Iceberg) table.
3165
+ *
3166
+ * The returned {@link PipelineLogReader} compiles each call to a safe
3167
+ * `SELECT ... WHERE ... ORDER BY ts DESC LIMIT n` (over-fetching by one) and
3168
+ * decodes the rows back to the canonical {@link PipelineLogRow} shape. All value
3169
+ * filters are escaped through `@lunora/bindings/r2sql`'s `sql`/`lit`; column
3170
+ * names come from `options.columnMap` (operator config), spliced with `raw`.
3171
+ * @param client An {@link R2SqlClient} (`createR2Sql({ accountId, apiToken, bucket })`).
3172
+ * @param options The target `table` (plus optional `namespace`) and any `columnMap` overrides.
3173
+ */
3174
+ declare const createPipelineLogReader: (client: R2SqlClient, options: PipelineLogReaderOptions) => PipelineLogReader;
3013
3175
  declare const VERSION: string;
3014
- export { type AdminTableResolver, type AirbyteMessage, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, type AnalyticsEngineSinkOptions, type AuthAdmin, type AuthCapabilities, type AuthConfigInfo, type AuthImpersonation, 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 LogFields, type LogLevel, LunoraError, type LunoraErrorBody, type LunoraHandlerOptions, type LunoraWorker, type MergeStrategy, type MetricEvent, type MetricKind, type MigrationFanOutRequest, type MigrationFanOutResult, NOOP_EXECUTION_CONTEXT, type ObservabilityEvent, type ObservabilitySink, type ObservabilitySinkContext, type OtlpSinkOptions, type PipelineLike, type PipelineLogSinkOptions, 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 SpanEvent, 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, pipelineLogSink, resolveLunoraOptions, resolveSecurity, resolveShard, routeIdentityResolvers, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
3176
+ export { type AdminTableResolver, type AirbyteMessage, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, type AnalyticsEngineSinkOptions, type AuthAdmin, type AuthCapabilities, type AuthConfigInfo, type AuthImpersonation, 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_LOG_COLUMNS, DEFAULT_LOG_LIMIT, 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 LogFields, type LogLevel, LunoraError, type LunoraErrorBody, type LunoraHandlerOptions, type LunoraWorker, type MergeStrategy, type MetricEvent, type MetricKind, type MigrationFanOutRequest, type MigrationFanOutResult, NOOP_EXECUTION_CONTEXT, type ObservabilityEvent, type ObservabilitySink, type ObservabilitySinkContext, type OtlpSinkOptions, type PipelineLike, type PipelineLogColumnMap, type PipelineLogCursor, type PipelineLogField, type PipelineLogPage, type PipelineLogQuery, type PipelineLogReader, type PipelineLogReaderOptions, type PipelineLogRow, type PipelineLogSinkOptions, 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 SpanEvent, 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, createPipelineLogReader, createQueryCoordinator, createStaticShardRegistry, createWorker, decorateResponse, defineRpcEnvelope, emitLogEvent, emitRpcEvent, enforceOrigin, handleCorsPreflight, mergeStrategyForAggregate, otlpSink, pipelineLogSink, resolveLunoraOptions, resolveSecurity, resolveShard, routeIdentityResolvers, sentrySink, toAirbyteMessages, toErrorResponse, toFivetranResponse, webhookSink, withFrameworkWorker };
package/dist/index.mjs CHANGED
@@ -4,7 +4,8 @@ export { createCrossShardRelationCapabilities } from './packem_shared/createCros
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, otlpSink, pipelineLogSink, sentrySink, webhookSink } from './packem_shared/analyticsEngineSink-DWUhiYHC.mjs';
7
+ export { analyticsEngineSink, combineSinks, consoleSink, otlpSink, pipelineLogSink, sentrySink, webhookSink } from './packem_shared/analyticsEngineSink-Bn7p0URe.mjs';
8
+ export { DEFAULT_LOG_COLUMNS, DEFAULT_LOG_LIMIT, createPipelineLogReader } from './packem_shared/DEFAULT_LOG_COLUMNS-J94BHDTf.mjs';
8
9
  export { createQueryCoordinator, createStaticShardRegistry, mergeStrategyForAggregate } from './packem_shared/createQueryCoordinator-DNCJzOZE.mjs';
9
10
  export { applyJurisdiction, resolveShard } from './packem_shared/applyJurisdiction-BkZtTkct.mjs';
10
11
  export { decorateResponse, enforceOrigin, handleCorsPreflight, resolveSecurity } from './packem_shared/decorateResponse-DRWQFNhF.mjs';
@@ -0,0 +1,135 @@
1
+ import { raw, sql, desc } from '@lunora/bindings/r2sql';
2
+
3
+ const LOG_LEVEL_ORDER = ["trace", "debug", "log", "info", "warn", "error", "fatal"];
4
+
5
+ const DEFAULT_COLUMNS = {
6
+ fields: "fields",
7
+ functionPath: "functionPath",
8
+ level: "level",
9
+ message: "message",
10
+ shardKey: "shardKey",
11
+ spanId: "spanId",
12
+ traceId: "traceId",
13
+ ts: "ts",
14
+ userId: "userId"
15
+ };
16
+ const DEFAULT_LIMIT = 500;
17
+ const MAX_LIMIT = 1e4;
18
+ const clampLimit = (limit) => {
19
+ if (limit === void 0 || !Number.isFinite(limit)) {
20
+ return DEFAULT_LIMIT;
21
+ }
22
+ const floored = Math.floor(limit);
23
+ if (floored < 1) {
24
+ return 1;
25
+ }
26
+ if (floored > MAX_LIMIT) {
27
+ return MAX_LIMIT;
28
+ }
29
+ return floored;
30
+ };
31
+ const toTs = (value) => typeof value === "number" ? value : Number(value);
32
+ const renderCell = (value) => value === null || value === void 0 ? "" : String(value);
33
+ const decodeFields = (value) => {
34
+ if (typeof value !== "string") {
35
+ return value;
36
+ }
37
+ try {
38
+ return JSON.parse(value);
39
+ } catch {
40
+ return value;
41
+ }
42
+ };
43
+ const DEFAULT_LOG_COLUMNS = DEFAULT_COLUMNS;
44
+ const DEFAULT_LOG_LIMIT = DEFAULT_LIMIT;
45
+ const createPipelineLogReader = (client, options) => {
46
+ const columns = { ...DEFAULT_COLUMNS, ...options.columnMap };
47
+ const tableReference = options.namespace === void 0 ? options.table : `${options.namespace}.${options.table}`;
48
+ return {
49
+ query: async (query = {}) => {
50
+ const limit = clampLimit(query.limit);
51
+ const fetchLimit = Math.min(limit + 1, MAX_LIMIT);
52
+ const builder = client.from(tableReference).select(
53
+ raw(columns.functionPath),
54
+ raw(columns.level),
55
+ raw(columns.message),
56
+ raw(columns.ts),
57
+ raw(columns.fields),
58
+ raw(columns.shardKey),
59
+ raw(columns.userId),
60
+ raw(columns.traceId),
61
+ raw(columns.spanId)
62
+ );
63
+ if (query.sinceTs !== void 0) {
64
+ builder.where(sql`${raw(columns.ts)} >= ${query.sinceTs}`);
65
+ }
66
+ if (query.untilTs !== void 0) {
67
+ builder.where(sql`${raw(columns.ts)} <= ${query.untilTs}`);
68
+ }
69
+ if (query.level !== void 0) {
70
+ builder.where(sql`${raw(columns.level)} = ${query.level}`);
71
+ } else if (query.minLevel !== void 0) {
72
+ const floorIndex = LOG_LEVEL_ORDER.indexOf(query.minLevel);
73
+ const allowed = floorIndex === -1 ? [...LOG_LEVEL_ORDER] : LOG_LEVEL_ORDER.slice(floorIndex);
74
+ builder.where(sql`${raw(columns.level)} IN ${allowed}`);
75
+ }
76
+ if (query.functionPath !== void 0) {
77
+ builder.where(sql`${raw(columns.functionPath)} = ${query.functionPath}`);
78
+ }
79
+ if (query.functionPathPrefix !== void 0) {
80
+ builder.where(sql`${raw(columns.functionPath)} LIKE ${`${query.functionPathPrefix}%`}`);
81
+ }
82
+ if (query.traceId !== void 0) {
83
+ builder.where(sql`${raw(columns.traceId)} = ${query.traceId}`);
84
+ }
85
+ if (query.shardKey !== void 0) {
86
+ builder.where(sql`${raw(columns.shardKey)} = ${query.shardKey}`);
87
+ }
88
+ if (query.userId !== void 0) {
89
+ builder.where(sql`${raw(columns.userId)} = ${query.userId}`);
90
+ }
91
+ if (query.cursor !== void 0) {
92
+ builder.where(sql`${raw(columns.ts)} < ${query.cursor.ts}`);
93
+ }
94
+ builder.orderBy(desc(raw(columns.ts))).limit(fetchLimit);
95
+ const { rows } = await builder.run();
96
+ const hasMore = rows.length > limit;
97
+ const pageRows = hasMore ? rows.slice(0, limit) : rows;
98
+ const overflow = hasMore ? rows[limit] : void 0;
99
+ const decoded = pageRows.map((row) => {
100
+ const out = {
101
+ functionPath: renderCell(row[columns.functionPath]),
102
+ // The stored `level` is one of the canonical severities; the
103
+ // reader trusts the writer's contract here rather than re-validating.
104
+ level: renderCell(row[columns.level]),
105
+ message: renderCell(row[columns.message]),
106
+ ts: toTs(row[columns.ts])
107
+ };
108
+ const fields = row[columns.fields];
109
+ if (fields !== void 0 && fields !== null) {
110
+ out.fields = decodeFields(fields);
111
+ }
112
+ const shardKey = row[columns.shardKey];
113
+ if (shardKey !== void 0 && shardKey !== null) {
114
+ out.shardKey = renderCell(shardKey);
115
+ }
116
+ const userId = row[columns.userId];
117
+ if (userId !== void 0 && userId !== null) {
118
+ out.userId = renderCell(userId);
119
+ }
120
+ const traceId = row[columns.traceId];
121
+ if (traceId !== void 0 && traceId !== null) {
122
+ out.traceId = renderCell(traceId);
123
+ }
124
+ const spanId = row[columns.spanId];
125
+ if (spanId !== void 0 && spanId !== null) {
126
+ out.spanId = renderCell(spanId);
127
+ }
128
+ return out;
129
+ });
130
+ return overflow === void 0 ? { rows: decoded } : { nextCursor: { ts: toTs(overflow[columns.ts]) }, rows: decoded };
131
+ }
132
+ };
133
+ };
134
+
135
+ export { DEFAULT_LOG_COLUMNS, DEFAULT_LOG_LIMIT, createPipelineLogReader };
@@ -270,7 +270,7 @@ const analyticsEngineSink = (options) => {
270
270
  };
271
271
  };
272
272
  const pipelineLogSink = (options) => {
273
- const { pipeline } = options;
273
+ const { pipeline, serializeFields } = options;
274
274
  return {
275
275
  onLog: (event, context) => {
276
276
  try {
@@ -281,7 +281,7 @@ const pipelineLogSink = (options) => {
281
281
  ts: event.ts
282
282
  };
283
283
  if (event.fields) {
284
- record.fields = event.fields;
284
+ record.fields = serializeFields === true ? JSON.stringify(event.fields) : event.fields;
285
285
  }
286
286
  if (event.shardKey !== void 0) {
287
287
  record.shardKey = event.shardKey;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/runtime",
3
- "version": "1.0.0-alpha.30",
3
+ "version": "1.0.0-alpha.31",
4
4
  "description": "Lunora Worker runtime: the RPC router, shard resolver, and query coordinator",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -46,6 +46,7 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
+ "@lunora/bindings": "1.0.0-alpha.9",
49
50
  "@lunora/errors": "1.0.0-alpha.6"
50
51
  },
51
52
  "engines": {