@lunora/server 1.0.0-alpha.32 → 1.0.0-alpha.34

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/types.d.mts CHANGED
@@ -1381,6 +1381,28 @@ interface LunoraLogMethod {
1381
1381
  interface LunoraLogger {
1382
1382
  readonly debug: LunoraLogMethod;
1383
1383
  readonly error: LunoraLogMethod;
1384
+ /**
1385
+ * Emit a **structured event** instead of a log line — OpenTelemetry's Events
1386
+ * API, on the wire as `LogRecord.eventName` (plus an `event.name` attribute
1387
+ * for collectors predating that field).
1388
+ *
1389
+ * ```ts
1390
+ * ctx.log.event("checkout.completed", { plan: user.plan, total, currency });
1391
+ * ```
1392
+ *
1393
+ * The difference from `ctx.log.info("checkout completed", { … })` is what a
1394
+ * backend can do with it. A log line's payload is its message: prose, written
1395
+ * for a human, free to be reworded next sprint — so "how many checkouts
1396
+ * completed, by plan, this hour" degrades into a substring search over
1397
+ * English. An event's payload is its `fields` under a **stable name**, which a
1398
+ * collector can index, group, and alert on directly.
1399
+ *
1400
+ * Rule of thumb: `log.*` for narration you'd read while debugging, `event` for
1401
+ * anything you'd ever put on a dashboard. And for facts about the request as a
1402
+ * whole, prefer `ctx.span` — one wide event beats a dozen
1403
+ * events, however well named.
1404
+ */
1405
+ readonly event: (name: string, fields?: LogFields) => void;
1384
1406
  readonly fatal: LunoraLogMethod;
1385
1407
  readonly info: LunoraLogMethod;
1386
1408
  readonly log: LunoraLogMethod;
@@ -1404,10 +1426,71 @@ interface LunoraLogger {
1404
1426
  * handle writes are merged over them at record time, post-hoc winning on a clash.
1405
1427
  */
1406
1428
  interface SpanHandle {
1429
+ /**
1430
+ * Record a timestamped event on the enclosing span — a retry, a cache miss, a
1431
+ * state transition. Prefer this over an extra `ctx.log` line for anything that
1432
+ * only makes sense *relative to this span*: it rides the span's own export, so
1433
+ * it costs no additional record and can never be separated from its context.
1434
+ */
1435
+ addEvent: (name: string, attributes?: LogFields) => void;
1436
+ /**
1437
+ * Link this span to one in another trace — how a queue consumer points back at
1438
+ * the request that enqueued its message without collapsing every producer into
1439
+ * one giant trace.
1440
+ */
1441
+ addLink: (link: SpanLink) => void;
1442
+ /**
1443
+ * Record a **handled** exception as the OTel-conventional `exception` span
1444
+ * event (`exception.type` / `exception.message` / `exception.stacktrace`),
1445
+ * without marking the span failed.
1446
+ *
1447
+ * For an error you swallowed — a retried request, a fallback that worked. An
1448
+ * error that escapes the span body is recorded automatically and *does* set
1449
+ * the error status, so don't call this for one you're re-throwing.
1450
+ */
1451
+ recordException: (error: unknown) => void;
1407
1452
  /** Set one attribute on the enclosing span (merged at record time; post-hoc wins on key clash). */
1408
1453
  setAttribute: (key: string, value: LogFields[string]) => void;
1409
1454
  /** Merge attributes onto the enclosing span (post-hoc wins on key clash). */
1410
1455
  setAttributes: (fields: LogFields) => void;
1456
+ /**
1457
+ * The W3C ids of the span this handle refers to (32-hex trace, 16-hex span).
1458
+ *
1459
+ * On `ctx.span` these are the DISPATCH's ids — the trace the whole request
1460
+ * belongs to. Use it to echo a trace id back to a caller so a user can quote
1461
+ * it in a bug report, to build a `traceparent` for a hand-rolled outbound
1462
+ * call, or to parent a third-party library's spans onto this request.
1463
+ */
1464
+ spanContext: () => {
1465
+ spanId: string;
1466
+ traceId: string;
1467
+ };
1468
+ }
1469
+ /**
1470
+ * A causal reference to a span in another trace (OTel `Span.links`). Ids are
1471
+ * lowercase hex — 32 chars for `traceId`, 16 for `spanId`.
1472
+ */
1473
+ interface SpanLink {
1474
+ /** Attributes describing the relationship, e.g. `{ "link.kind": "enqueued_by" }`. */
1475
+ attributes?: LogFields;
1476
+ spanId: string;
1477
+ traceId: string;
1478
+ }
1479
+ /** OTel `SpanKind`. Drives a collector's service map — see {@link SpanOptions.kind}. */
1480
+ type SpanKind = "client" | "consumer" | "internal" | "producer" | "server";
1481
+ /** Options accepted by `ctx.trace(name, fn, options)` beyond a plain attribute bag. */
1482
+ interface SpanOptions {
1483
+ /** Start attributes, snapshotted before the body runs. */
1484
+ attributes?: LogFields;
1485
+ /**
1486
+ * OTel `SpanKind`, default `"internal"`. Set `"client"` for a call OUT to
1487
+ * another service and `"producer"`/`"consumer"` for queue hops: a collector
1488
+ * builds its service map from this, so leaving everything `"internal"` yields
1489
+ * a trace with no topology.
1490
+ */
1491
+ kind?: SpanKind;
1492
+ /** Links to spans in other traces, known at span start. */
1493
+ links?: SpanLink[];
1411
1494
  }
1412
1495
  /**
1413
1496
  * Span factory on every function `ctx`. Wraps a sub-operation so it becomes its
@@ -1458,10 +1541,52 @@ interface SpanHandle {
1458
1541
  * @param fn The body to time, receiving a tracer bound to this span for any
1459
1542
  * nested spans and the enclosing span's {@link SpanHandle} for post-hoc
1460
1543
  * attributes. May be sync or async; the result is awaited.
1461
- * @param attributes Structured attributes to stamp on the span at start,
1462
- * normalized like a log line's `fields`.
1544
+ * @param attributes Either a plain attribute bag to stamp on the span at start
1545
+ * (normalized like a log line's `fields`), or a {@link SpanOptions} object when
1546
+ * you need `kind` or `links`. It is read as options only when *every* key is one
1547
+ * of `attributes`/`kind`/`links`; `{ attributes: { kind: "premium" } }` is the
1548
+ * explicit form if your own attributes happen to be named that.
1549
+ */
1550
+ type LunoraTracer = <T>(name: string, function_: (trace: LunoraTracer, span: SpanHandle) => Promise<T> | T, attributes?: LogFields | SpanOptions) => Promise<T>;
1551
+ /**
1552
+ * `ctx.span` — a handle onto **this request's own span**, and with it the
1553
+ * wide-event API.
1554
+ *
1555
+ * ```ts
1556
+ * export const checkout = mutation({ handler: async (ctx, args) => {
1557
+ * ctx.span.setAttributes({ "user.plan": user.plan, "cart.items": cart.length });
1558
+ * const payment = await charge(cart);
1559
+ * ctx.span.setAttributes({ "payment.provider": payment.provider, "payment.total": payment.total });
1560
+ * if (payment.retried) ctx.span.addEvent("payment.retried", { attempts: payment.attempts });
1561
+ * return payment;
1562
+ * }});
1563
+ * ```
1564
+ *
1565
+ * **Why this instead of more log lines.** The usual way to make a handler
1566
+ * observable is to sprinkle `ctx.log.info` through it, which costs one record per
1567
+ * call, scatters one request's facts across a dozen rows, and forces every
1568
+ * question to be answered by correlating them back together. The wide-event
1569
+ * pattern inverts that: accumulate the facts as you learn them, and emit **one**
1570
+ * richly-attributed record per unit of work. Cost is flat — one span per request
1571
+ * no matter how much you attach — and every question ("p99 checkout latency for
1572
+ * pro-plan users with >10 items") becomes a single filter over one table instead
1573
+ * of a join across log lines.
1574
+ *
1575
+ * **This is plain OpenTelemetry, not a Lunora convention.** The attributes land
1576
+ * on the span the dispatch already emits, and are additionally exported as an
1577
+ * OTel Event record named `lunora.dispatch`, correlated by `trace_id`/`span_id`.
1578
+ * Any OTLP backend groups and aggregates them with no special configuration.
1579
+ *
1580
+ * **`span` vs `trace`.** `ctx.trace(name, fn)` creates a NEW child span to time a
1581
+ * sub-operation; `ctx.span` attaches to the one that already exists for the
1582
+ * request. Use `trace` for "how long did this part take", `span` for "what was
1583
+ * true about this request". Inside a `ctx.trace` body, the handle passed as the
1584
+ * body's second argument is that child span's equivalent of this.
1585
+ *
1586
+ * Attributes are normalized exactly like `ctx.log` fields, and recording is
1587
+ * best-effort — a telemetry failure never breaks the handler.
1463
1588
  */
1464
- type LunoraTracer = <T>(name: string, function_: (trace: LunoraTracer, span: SpanHandle) => Promise<T> | T, attributes?: LogFields) => Promise<T>;
1589
+ type LunoraWideEvent = SpanHandle;
1465
1590
  /**
1466
1591
  * Application metrics on every function `ctx` — the third signal alongside
1467
1592
  * `ctx.log` and `ctx.trace`. Each call records one measurement that flows to an
@@ -1546,6 +1671,8 @@ interface QueryCtx {
1546
1671
  readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
1547
1672
  /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1548
1673
  readonly secrets: Secrets;
1674
+ /** Attach facts to THIS request's span — the wide event; see {@link LunoraWideEvent}. */
1675
+ readonly span: LunoraWideEvent;
1549
1676
  readonly storage: ReadOnlyStorage;
1550
1677
  /** Wrap a sub-operation in its own nested span; see {@link LunoraTracer}. */
1551
1678
  readonly trace: LunoraTracer;
@@ -1600,6 +1727,8 @@ interface MutationCtx {
1600
1727
  readonly scheduler: Scheduler;
1601
1728
  /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1602
1729
  readonly secrets: Secrets;
1730
+ /** Attach facts to THIS request's span — the wide event; see {@link LunoraWideEvent}. */
1731
+ readonly span: LunoraWideEvent;
1603
1732
  readonly storage: ReadOnlyStorage;
1604
1733
  /** Wrap a sub-operation in its own nested span; see {@link LunoraTracer}. */
1605
1734
  readonly trace: LunoraTracer;
@@ -1649,6 +1778,8 @@ interface ActionCtx {
1649
1778
  readonly scheduler: Scheduler;
1650
1779
  /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1651
1780
  readonly secrets: Secrets;
1781
+ /** Attach facts to THIS request's span — the wide event; see {@link LunoraWideEvent}. */
1782
+ readonly span: LunoraWideEvent;
1652
1783
  readonly storage: Storage;
1653
1784
  /** Wrap a sub-operation in its own nested span; see {@link LunoraTracer}. */
1654
1785
  readonly trace: LunoraTracer;
@@ -1662,4 +1793,4 @@ interface ActionCtx {
1662
1793
  */
1663
1794
  type AnyApi = Record<string, Record<string, RegisteredFunction<ArgsValidator, unknown, FunctionKind>>>;
1664
1795
  declare const anyApi: AnyApi;
1665
- export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, type ExposeConfig, type ExternalSourceCursor, type ExternalSourceDefinition, type ExternalSourceMode, type ExternalSourceRefresh, type FunctionKind, type FunctionVisibility, type GeoBoundingBox, type GeoFilterBuilder, type GeoIndexDefinition, type GeoPointInput, type GlobalBackend, type IndexDefinition, type IndexRangeBuilder, type InferArgs, type LifecycleEvent, type LifecycleEventKind, type LogFields, type LunoraLogMethod, type LunoraLogger, type LunoraMetrics, type LunoraTracer, type MutationCtx, type OnDeleteAction, type PaginationOptions, type PaginationResult, type QueryCtx, type RankIndexDefinition, type RankSortKey, type ReadOnlyStorage, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMutation, type RegisteredQuery, type RegisteredStream, type RelationDefinition, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type Schema, type SearchFilterBuilder, type SearchIndexDefinition, type Secrets, type SecretsStoreSecretLike, type ShardMode, type SpanHandle, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableDefinition, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerBuilder, type TriggerCtx, type TriggerDatabase, type TriggerDefinition, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type TtlDefinition, type VectorEmbedder, type VectorIndexDefinition, type VectorMatch, type VectorMatches, type VectorMetric, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, type X402ProcedureConfig, anyApi };
1796
+ export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, type ExposeConfig, type ExternalSourceCursor, type ExternalSourceDefinition, type ExternalSourceMode, type ExternalSourceRefresh, type FunctionKind, type FunctionVisibility, type GeoBoundingBox, type GeoFilterBuilder, type GeoIndexDefinition, type GeoPointInput, type GlobalBackend, type IndexDefinition, type IndexRangeBuilder, type InferArgs, type LifecycleEvent, type LifecycleEventKind, type LogFields, type LunoraLogMethod, type LunoraLogger, type LunoraMetrics, type LunoraTracer, type LunoraWideEvent, type MutationCtx, type OnDeleteAction, type PaginationOptions, type PaginationResult, type QueryCtx, type RankIndexDefinition, type RankSortKey, type ReadOnlyStorage, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMutation, type RegisteredQuery, type RegisteredStream, type RelationDefinition, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type Schema, type SearchFilterBuilder, type SearchIndexDefinition, type Secrets, type SecretsStoreSecretLike, type ShardMode, type SpanHandle, type SpanKind, type SpanLink, type SpanOptions, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableDefinition, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerBuilder, type TriggerCtx, type TriggerDatabase, type TriggerDefinition, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type TtlDefinition, type VectorEmbedder, type VectorIndexDefinition, type VectorMatch, type VectorMatches, type VectorMetric, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, type X402ProcedureConfig, anyApi };
package/dist/types.d.ts CHANGED
@@ -1381,6 +1381,28 @@ interface LunoraLogMethod {
1381
1381
  interface LunoraLogger {
1382
1382
  readonly debug: LunoraLogMethod;
1383
1383
  readonly error: LunoraLogMethod;
1384
+ /**
1385
+ * Emit a **structured event** instead of a log line — OpenTelemetry's Events
1386
+ * API, on the wire as `LogRecord.eventName` (plus an `event.name` attribute
1387
+ * for collectors predating that field).
1388
+ *
1389
+ * ```ts
1390
+ * ctx.log.event("checkout.completed", { plan: user.plan, total, currency });
1391
+ * ```
1392
+ *
1393
+ * The difference from `ctx.log.info("checkout completed", { … })` is what a
1394
+ * backend can do with it. A log line's payload is its message: prose, written
1395
+ * for a human, free to be reworded next sprint — so "how many checkouts
1396
+ * completed, by plan, this hour" degrades into a substring search over
1397
+ * English. An event's payload is its `fields` under a **stable name**, which a
1398
+ * collector can index, group, and alert on directly.
1399
+ *
1400
+ * Rule of thumb: `log.*` for narration you'd read while debugging, `event` for
1401
+ * anything you'd ever put on a dashboard. And for facts about the request as a
1402
+ * whole, prefer `ctx.span` — one wide event beats a dozen
1403
+ * events, however well named.
1404
+ */
1405
+ readonly event: (name: string, fields?: LogFields) => void;
1384
1406
  readonly fatal: LunoraLogMethod;
1385
1407
  readonly info: LunoraLogMethod;
1386
1408
  readonly log: LunoraLogMethod;
@@ -1404,10 +1426,71 @@ interface LunoraLogger {
1404
1426
  * handle writes are merged over them at record time, post-hoc winning on a clash.
1405
1427
  */
1406
1428
  interface SpanHandle {
1429
+ /**
1430
+ * Record a timestamped event on the enclosing span — a retry, a cache miss, a
1431
+ * state transition. Prefer this over an extra `ctx.log` line for anything that
1432
+ * only makes sense *relative to this span*: it rides the span's own export, so
1433
+ * it costs no additional record and can never be separated from its context.
1434
+ */
1435
+ addEvent: (name: string, attributes?: LogFields) => void;
1436
+ /**
1437
+ * Link this span to one in another trace — how a queue consumer points back at
1438
+ * the request that enqueued its message without collapsing every producer into
1439
+ * one giant trace.
1440
+ */
1441
+ addLink: (link: SpanLink) => void;
1442
+ /**
1443
+ * Record a **handled** exception as the OTel-conventional `exception` span
1444
+ * event (`exception.type` / `exception.message` / `exception.stacktrace`),
1445
+ * without marking the span failed.
1446
+ *
1447
+ * For an error you swallowed — a retried request, a fallback that worked. An
1448
+ * error that escapes the span body is recorded automatically and *does* set
1449
+ * the error status, so don't call this for one you're re-throwing.
1450
+ */
1451
+ recordException: (error: unknown) => void;
1407
1452
  /** Set one attribute on the enclosing span (merged at record time; post-hoc wins on key clash). */
1408
1453
  setAttribute: (key: string, value: LogFields[string]) => void;
1409
1454
  /** Merge attributes onto the enclosing span (post-hoc wins on key clash). */
1410
1455
  setAttributes: (fields: LogFields) => void;
1456
+ /**
1457
+ * The W3C ids of the span this handle refers to (32-hex trace, 16-hex span).
1458
+ *
1459
+ * On `ctx.span` these are the DISPATCH's ids — the trace the whole request
1460
+ * belongs to. Use it to echo a trace id back to a caller so a user can quote
1461
+ * it in a bug report, to build a `traceparent` for a hand-rolled outbound
1462
+ * call, or to parent a third-party library's spans onto this request.
1463
+ */
1464
+ spanContext: () => {
1465
+ spanId: string;
1466
+ traceId: string;
1467
+ };
1468
+ }
1469
+ /**
1470
+ * A causal reference to a span in another trace (OTel `Span.links`). Ids are
1471
+ * lowercase hex — 32 chars for `traceId`, 16 for `spanId`.
1472
+ */
1473
+ interface SpanLink {
1474
+ /** Attributes describing the relationship, e.g. `{ "link.kind": "enqueued_by" }`. */
1475
+ attributes?: LogFields;
1476
+ spanId: string;
1477
+ traceId: string;
1478
+ }
1479
+ /** OTel `SpanKind`. Drives a collector's service map — see {@link SpanOptions.kind}. */
1480
+ type SpanKind = "client" | "consumer" | "internal" | "producer" | "server";
1481
+ /** Options accepted by `ctx.trace(name, fn, options)` beyond a plain attribute bag. */
1482
+ interface SpanOptions {
1483
+ /** Start attributes, snapshotted before the body runs. */
1484
+ attributes?: LogFields;
1485
+ /**
1486
+ * OTel `SpanKind`, default `"internal"`. Set `"client"` for a call OUT to
1487
+ * another service and `"producer"`/`"consumer"` for queue hops: a collector
1488
+ * builds its service map from this, so leaving everything `"internal"` yields
1489
+ * a trace with no topology.
1490
+ */
1491
+ kind?: SpanKind;
1492
+ /** Links to spans in other traces, known at span start. */
1493
+ links?: SpanLink[];
1411
1494
  }
1412
1495
  /**
1413
1496
  * Span factory on every function `ctx`. Wraps a sub-operation so it becomes its
@@ -1458,10 +1541,52 @@ interface SpanHandle {
1458
1541
  * @param fn The body to time, receiving a tracer bound to this span for any
1459
1542
  * nested spans and the enclosing span's {@link SpanHandle} for post-hoc
1460
1543
  * attributes. May be sync or async; the result is awaited.
1461
- * @param attributes Structured attributes to stamp on the span at start,
1462
- * normalized like a log line's `fields`.
1544
+ * @param attributes Either a plain attribute bag to stamp on the span at start
1545
+ * (normalized like a log line's `fields`), or a {@link SpanOptions} object when
1546
+ * you need `kind` or `links`. It is read as options only when *every* key is one
1547
+ * of `attributes`/`kind`/`links`; `{ attributes: { kind: "premium" } }` is the
1548
+ * explicit form if your own attributes happen to be named that.
1549
+ */
1550
+ type LunoraTracer = <T>(name: string, function_: (trace: LunoraTracer, span: SpanHandle) => Promise<T> | T, attributes?: LogFields | SpanOptions) => Promise<T>;
1551
+ /**
1552
+ * `ctx.span` — a handle onto **this request's own span**, and with it the
1553
+ * wide-event API.
1554
+ *
1555
+ * ```ts
1556
+ * export const checkout = mutation({ handler: async (ctx, args) => {
1557
+ * ctx.span.setAttributes({ "user.plan": user.plan, "cart.items": cart.length });
1558
+ * const payment = await charge(cart);
1559
+ * ctx.span.setAttributes({ "payment.provider": payment.provider, "payment.total": payment.total });
1560
+ * if (payment.retried) ctx.span.addEvent("payment.retried", { attempts: payment.attempts });
1561
+ * return payment;
1562
+ * }});
1563
+ * ```
1564
+ *
1565
+ * **Why this instead of more log lines.** The usual way to make a handler
1566
+ * observable is to sprinkle `ctx.log.info` through it, which costs one record per
1567
+ * call, scatters one request's facts across a dozen rows, and forces every
1568
+ * question to be answered by correlating them back together. The wide-event
1569
+ * pattern inverts that: accumulate the facts as you learn them, and emit **one**
1570
+ * richly-attributed record per unit of work. Cost is flat — one span per request
1571
+ * no matter how much you attach — and every question ("p99 checkout latency for
1572
+ * pro-plan users with >10 items") becomes a single filter over one table instead
1573
+ * of a join across log lines.
1574
+ *
1575
+ * **This is plain OpenTelemetry, not a Lunora convention.** The attributes land
1576
+ * on the span the dispatch already emits, and are additionally exported as an
1577
+ * OTel Event record named `lunora.dispatch`, correlated by `trace_id`/`span_id`.
1578
+ * Any OTLP backend groups and aggregates them with no special configuration.
1579
+ *
1580
+ * **`span` vs `trace`.** `ctx.trace(name, fn)` creates a NEW child span to time a
1581
+ * sub-operation; `ctx.span` attaches to the one that already exists for the
1582
+ * request. Use `trace` for "how long did this part take", `span` for "what was
1583
+ * true about this request". Inside a `ctx.trace` body, the handle passed as the
1584
+ * body's second argument is that child span's equivalent of this.
1585
+ *
1586
+ * Attributes are normalized exactly like `ctx.log` fields, and recording is
1587
+ * best-effort — a telemetry failure never breaks the handler.
1463
1588
  */
1464
- type LunoraTracer = <T>(name: string, function_: (trace: LunoraTracer, span: SpanHandle) => Promise<T> | T, attributes?: LogFields) => Promise<T>;
1589
+ type LunoraWideEvent = SpanHandle;
1465
1590
  /**
1466
1591
  * Application metrics on every function `ctx` — the third signal alongside
1467
1592
  * `ctx.log` and `ctx.trace`. Each call records one measurement that flows to an
@@ -1546,6 +1671,8 @@ interface QueryCtx {
1546
1671
  readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
1547
1672
  /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1548
1673
  readonly secrets: Secrets;
1674
+ /** Attach facts to THIS request's span — the wide event; see {@link LunoraWideEvent}. */
1675
+ readonly span: LunoraWideEvent;
1549
1676
  readonly storage: ReadOnlyStorage;
1550
1677
  /** Wrap a sub-operation in its own nested span; see {@link LunoraTracer}. */
1551
1678
  readonly trace: LunoraTracer;
@@ -1600,6 +1727,8 @@ interface MutationCtx {
1600
1727
  readonly scheduler: Scheduler;
1601
1728
  /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1602
1729
  readonly secrets: Secrets;
1730
+ /** Attach facts to THIS request's span — the wide event; see {@link LunoraWideEvent}. */
1731
+ readonly span: LunoraWideEvent;
1603
1732
  readonly storage: ReadOnlyStorage;
1604
1733
  /** Wrap a sub-operation in its own nested span; see {@link LunoraTracer}. */
1605
1734
  readonly trace: LunoraTracer;
@@ -1649,6 +1778,8 @@ interface ActionCtx {
1649
1778
  readonly scheduler: Scheduler;
1650
1779
  /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1651
1780
  readonly secrets: Secrets;
1781
+ /** Attach facts to THIS request's span — the wide event; see {@link LunoraWideEvent}. */
1782
+ readonly span: LunoraWideEvent;
1652
1783
  readonly storage: Storage;
1653
1784
  /** Wrap a sub-operation in its own nested span; see {@link LunoraTracer}. */
1654
1785
  readonly trace: LunoraTracer;
@@ -1662,4 +1793,4 @@ interface ActionCtx {
1662
1793
  */
1663
1794
  type AnyApi = Record<string, Record<string, RegisteredFunction<ArgsValidator, unknown, FunctionKind>>>;
1664
1795
  declare const anyApi: AnyApi;
1665
- export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, type ExposeConfig, type ExternalSourceCursor, type ExternalSourceDefinition, type ExternalSourceMode, type ExternalSourceRefresh, type FunctionKind, type FunctionVisibility, type GeoBoundingBox, type GeoFilterBuilder, type GeoIndexDefinition, type GeoPointInput, type GlobalBackend, type IndexDefinition, type IndexRangeBuilder, type InferArgs, type LifecycleEvent, type LifecycleEventKind, type LogFields, type LunoraLogMethod, type LunoraLogger, type LunoraMetrics, type LunoraTracer, type MutationCtx, type OnDeleteAction, type PaginationOptions, type PaginationResult, type QueryCtx, type RankIndexDefinition, type RankSortKey, type ReadOnlyStorage, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMutation, type RegisteredQuery, type RegisteredStream, type RelationDefinition, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type Schema, type SearchFilterBuilder, type SearchIndexDefinition, type Secrets, type SecretsStoreSecretLike, type ShardMode, type SpanHandle, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableDefinition, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerBuilder, type TriggerCtx, type TriggerDatabase, type TriggerDefinition, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type TtlDefinition, type VectorEmbedder, type VectorIndexDefinition, type VectorMatch, type VectorMatches, type VectorMetric, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, type X402ProcedureConfig, anyApi };
1796
+ export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, type ExposeConfig, type ExternalSourceCursor, type ExternalSourceDefinition, type ExternalSourceMode, type ExternalSourceRefresh, type FunctionKind, type FunctionVisibility, type GeoBoundingBox, type GeoFilterBuilder, type GeoIndexDefinition, type GeoPointInput, type GlobalBackend, type IndexDefinition, type IndexRangeBuilder, type InferArgs, type LifecycleEvent, type LifecycleEventKind, type LogFields, type LunoraLogMethod, type LunoraLogger, type LunoraMetrics, type LunoraTracer, type LunoraWideEvent, type MutationCtx, type OnDeleteAction, type PaginationOptions, type PaginationResult, type QueryCtx, type RankIndexDefinition, type RankSortKey, type ReadOnlyStorage, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMutation, type RegisteredQuery, type RegisteredStream, type RelationDefinition, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type Schema, type SearchFilterBuilder, type SearchIndexDefinition, type Secrets, type SecretsStoreSecretLike, type ShardMode, type SpanHandle, type SpanKind, type SpanLink, type SpanOptions, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableDefinition, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerBuilder, type TriggerCtx, type TriggerDatabase, type TriggerDefinition, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type TtlDefinition, type VectorEmbedder, type VectorIndexDefinition, type VectorMatch, type VectorMatches, type VectorMetric, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, type X402ProcedureConfig, anyApi };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/server",
3
- "version": "1.0.0-alpha.32",
3
+ "version": "1.0.0-alpha.34",
4
4
  "description": "Server primitives for Lunora: defineSchema, defineTable, query, mutation, and action",
5
5
  "keywords": [
6
6
  "backend",
@@ -56,6 +56,10 @@
56
56
  "types": "./dist/rls/testing.d.ts",
57
57
  "import": "./dist/rls/testing.mjs"
58
58
  },
59
+ "./otel": {
60
+ "types": "./dist/otel.d.ts",
61
+ "import": "./dist/otel.mjs"
62
+ },
59
63
  "./package.json": "./package.json"
60
64
  },
61
65
  "publishConfig": {
@@ -66,7 +70,15 @@
66
70
  "@lunora/scheduler": "1.0.0-alpha.12",
67
71
  "@lunora/values": "1.0.0-alpha.11",
68
72
  "drizzle-orm": "^0.45.2",
69
- "hono": "^4.12.30"
73
+ "hono": "^4.12.32"
74
+ },
75
+ "peerDependencies": {
76
+ "@opentelemetry/api": "^1.9.1"
77
+ },
78
+ "peerDependenciesMeta": {
79
+ "@opentelemetry/api": {
80
+ "optional": true
81
+ }
70
82
  },
71
83
  "engines": {
72
84
  "node": "^22.15.0 || >=24.11.0"