@vibeorm/runtime 2.0.0-alpha.8 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +2 -2
  2. package/dist/adapter.d.ts +102 -4
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/bulk-upsert.d.ts +282 -0
  5. package/dist/bulk-upsert.d.ts.map +1 -0
  6. package/dist/client.d.ts +49 -18
  7. package/dist/client.d.ts.map +1 -1
  8. package/dist/codecs.d.ts.map +1 -1
  9. package/dist/diagnostics/index.d.ts +12 -0
  10. package/dist/diagnostics/index.d.ts.map +1 -0
  11. package/dist/diagnostics/insight.d.ts +63 -0
  12. package/dist/diagnostics/insight.d.ts.map +1 -0
  13. package/dist/diagnostics/plan.d.ts +88 -0
  14. package/dist/diagnostics/plan.d.ts.map +1 -0
  15. package/dist/diagnostics/preview.d.ts +43 -0
  16. package/dist/diagnostics/preview.d.ts.map +1 -0
  17. package/dist/diagnostics/types.d.ts +223 -0
  18. package/dist/diagnostics/types.d.ts.map +1 -0
  19. package/dist/diagnostics/workload.d.ts +32 -0
  20. package/dist/diagnostics/workload.d.ts.map +1 -0
  21. package/dist/index.d.ts +24 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +10637 -5008
  24. package/dist/index.js.map +34 -15
  25. package/dist/keyset-iterator.d.ts +73 -0
  26. package/dist/keyset-iterator.d.ts.map +1 -0
  27. package/dist/keyset.d.ts +121 -0
  28. package/dist/keyset.d.ts.map +1 -0
  29. package/dist/model-meta.d.ts +18 -2
  30. package/dist/model-meta.d.ts.map +1 -1
  31. package/dist/nested-writes.d.ts +0 -33
  32. package/dist/nested-writes.d.ts.map +1 -1
  33. package/dist/policy-operation.d.ts +14 -0
  34. package/dist/policy-operation.d.ts.map +1 -0
  35. package/dist/policy.d.ts +5 -28
  36. package/dist/policy.d.ts.map +1 -1
  37. package/dist/query-builder.d.ts +22 -1
  38. package/dist/query-builder.d.ts.map +1 -1
  39. package/dist/relation-key.d.ts +23 -0
  40. package/dist/relation-key.d.ts.map +1 -0
  41. package/dist/relation-loader.d.ts +0 -29
  42. package/dist/relation-loader.d.ts.map +1 -1
  43. package/dist/relation-plan.d.ts +45 -7
  44. package/dist/relation-plan.d.ts.map +1 -1
  45. package/dist/render-cache.d.ts.map +1 -1
  46. package/dist/rls-context.d.ts +14 -0
  47. package/dist/rls-context.d.ts.map +1 -0
  48. package/dist/rls-readiness.d.ts +114 -0
  49. package/dist/rls-readiness.d.ts.map +1 -0
  50. package/dist/scoped.d.ts +104 -0
  51. package/dist/scoped.d.ts.map +1 -0
  52. package/dist/strict-args.d.ts +47 -0
  53. package/dist/strict-args.d.ts.map +1 -0
  54. package/dist/telemetry/collector.d.ts +53 -0
  55. package/dist/telemetry/collector.d.ts.map +1 -0
  56. package/dist/telemetry/config.d.ts +53 -0
  57. package/dist/telemetry/config.d.ts.map +1 -0
  58. package/dist/telemetry/fingerprint.d.ts +38 -0
  59. package/dist/telemetry/fingerprint.d.ts.map +1 -0
  60. package/dist/telemetry/index.d.ts +18 -0
  61. package/dist/telemetry/index.d.ts.map +1 -0
  62. package/dist/telemetry/recorder.d.ts +93 -0
  63. package/dist/telemetry/recorder.d.ts.map +1 -0
  64. package/dist/telemetry/statement.d.ts +53 -0
  65. package/dist/telemetry/statement.d.ts.map +1 -0
  66. package/dist/telemetry/types.d.ts +265 -0
  67. package/dist/telemetry/types.d.ts.map +1 -0
  68. package/dist/validators.d.ts +5 -3
  69. package/dist/validators.d.ts.map +1 -1
  70. package/dist/views.d.ts.map +1 -1
  71. package/dist/write-scope.d.ts +14 -0
  72. package/dist/write-scope.d.ts.map +1 -0
  73. package/package.json +5 -4
@@ -0,0 +1,14 @@
1
+ import type { SchemaIR } from "@vibeorm/schema";
2
+ import type { SqlDialect } from "@vibeorm/sql";
3
+ /** Private, copied identity and its total transaction-settings transport. */
4
+ export type BoundRlsContext = {
5
+ readonly values: Readonly<Record<string, unknown>>;
6
+ readonly settings: Readonly<Record<string, string | null>>;
7
+ };
8
+ /** Validate before serialization; missing nullable slots are still missing keys. */
9
+ export declare function bindRlsContext(params: {
10
+ schema: SchemaIR;
11
+ dialect: SqlDialect;
12
+ context: unknown;
13
+ }): BoundRlsContext;
14
+ //# sourceMappingURL=rls-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rls-context.d.ts","sourceRoot":"","sources":["../src/rls-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAoB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAElE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C,6EAA6E;AAC7E,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;CAC5D,CAAC;AA0BF,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,eAAe,CA6CnH"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Native-RLS readiness — verify the ACTUAL runtime privileges and the ACTUAL
3
+ * policy state of the live database (plan contracts 9, 11, 12, 16).
4
+ *
5
+ * What this module refuses to do is as important as what it does:
6
+ * - it never trusts a migration record, a policy NAME or a policy COMMENT
7
+ * (lane 3S proved a rewritten comment can make a comment check green while
8
+ * the policy itself is tampered);
9
+ * - it compares policy expressions as PostgreSQL's OWN deparse of a probe
10
+ * policy created on a same-named TEMP clone under an empty `search_path`
11
+ * — never as normalized text. Only desired deparse is cached by the client;
12
+ * live expressions and privileges are read again every transaction;
13
+ * - a database that will not let it verify reports `"unavailable"`, which is
14
+ * NOT ready. There is no path from "could not check" to "ready".
15
+ *
16
+ * Cost and blast radius: no DDL on any live table (only `pg_temp` clones, so
17
+ * the live table sees an AccessShareLock, never AccessExclusiveLock), no role
18
+ * switch, no context binding, no schema or role provisioning of any kind, and
19
+ * `search_path` saved and restored inside the probe's own savepoint. Nothing
20
+ * about live readiness is cached. Reconnect invalidates desired deparse too.
21
+ * Within one backend generation, desired deparse may be stale after privileged
22
+ * dependency changes that leave identical expression text; this is not an
23
+ * instantaneous-revocation guarantee. Standalone doctor checks never cache.
24
+ */
25
+ import type { Provider, SchemaIR } from "@vibeorm/schema";
26
+ import type { DatabaseAdapter } from "./adapter.ts";
27
+ /** Stable identifier of one readiness check; callers branch on this, never on text. */
28
+ export type NativeRlsCheckName = "runtime-role-identity" | "runtime-role-privileges" | "elevated-role-membership" | "schema-control" | "table-ownership" | "table-truncate-privilege" | "row-level-security-enabled" | "row-level-security-forced" | "managed-policy-present" | "managed-policy-shape" | "unexpected-policy" | "managed-policy-expression" | "unsafe-objects";
29
+ /**
30
+ * `"unavailable"` means the database would not let this check run (no TEMP
31
+ * privilege, an unreadable catalog flag). It blocks readiness exactly like
32
+ * `"fail"` — the two differ only in what a human should do about them.
33
+ * `"warn"` is a reported limitation that does not block: the PGlite session
34
+ * role, and the presence of unsafe objects (contract 16 reports them).
35
+ */
36
+ export type NativeRlsCheckStatus = "pass" | "warn" | "fail" | "unavailable";
37
+ /** One verified fact about the live database. */
38
+ export type NativeRlsCheck = {
39
+ readonly name: NativeRlsCheckName;
40
+ readonly status: NativeRlsCheckStatus;
41
+ readonly detail: string;
42
+ /** Offending tables, roles or objects — never a context or identity VALUE. */
43
+ readonly subjects: readonly string[];
44
+ };
45
+ /** An object that can execute with an authority other than the caller's. */
46
+ export type NativeRlsUnsafeObject = {
47
+ readonly kind: "view" | "materializedView" | "securityDefinerRoutine" | "trigger";
48
+ /** `public.order_report`, `public.escalate`, `public.orders.audit_orders`. */
49
+ readonly name: string;
50
+ readonly owner: string;
51
+ readonly detail: string;
52
+ };
53
+ /** The full readiness verdict. Built fresh on every call — never cached. */
54
+ export type NativeRlsReport = {
55
+ /** True only when every check is `"pass"` or `"warn"`. */
56
+ readonly ready: boolean;
57
+ readonly currentRole: string;
58
+ readonly sessionRole: string;
59
+ readonly provider: Provider;
60
+ readonly serverVersionNum: number | null;
61
+ /** Physical tables of every model carrying a rule other than `"unprotected"`. */
62
+ readonly tables: readonly string[];
63
+ readonly checks: readonly NativeRlsCheck[];
64
+ /** The `"fail"` / `"unavailable"` subset — what makes `ready` false. */
65
+ readonly problems: readonly NativeRlsCheck[];
66
+ readonly unsafeObjects: readonly NativeRlsUnsafeObject[];
67
+ };
68
+ export { RLS_READINESS_SQL, NATIVE_RLS_PROBE_POLICY } from "@vibeorm/sql";
69
+ /**
70
+ * Verify readiness inside a transaction that is ALREADY bound — the role is
71
+ * bound (PGlite) or logged in (server), the context settings are set, and the
72
+ * caller's own statements will run in this same transaction.
73
+ *
74
+ * Binds nothing itself: no role switch, no context setting, and the
75
+ * `search_path` it needs is emptied and restored inside its own savepoint, so
76
+ * the caller's statements are unaffected. Safe to call twice in one
77
+ * transaction — the temp clones are dropped before it returns.
78
+ *
79
+ * @param runtimeRole Compared against `current_user` when supplied. NEVER used
80
+ * to switch role: that binding belongs to the caller.
81
+ */
82
+ export declare function inspectNativeRlsTransaction(params: {
83
+ adapter: DatabaseAdapter;
84
+ schema: SchemaIR;
85
+ runtimeRole?: string;
86
+ /** Client identity scopes desired-expression reuse; omit for a fresh doctor probe.
87
+ * Live facts are never cached. Reuse requires a readable backend pid/start time. */
88
+ cacheKey?: object;
89
+ }): Promise<NativeRlsReport>;
90
+ /**
91
+ * Verify readiness on an adapter that is not already inside a bound
92
+ * transaction — `vibeorm doctor`, or a one-off connection check. Opens one
93
+ * transaction and, on PGlite only, binds the configured runtime role as its
94
+ * first statement. A server connection is never switched: its login role IS
95
+ * the runtime role, and a supplied `runtimeRole` is only compared with it.
96
+ */
97
+ export declare function inspectNativeRls(params: {
98
+ adapter: DatabaseAdapter;
99
+ schema: SchemaIR;
100
+ runtimeRole?: string;
101
+ }): Promise<NativeRlsReport>;
102
+ /**
103
+ * Refuse to proceed on a database that is not ready. `meta.checks` names every
104
+ * check that failed or could not be run — callers branch on those stable
105
+ * names, never on this message.
106
+ */
107
+ export declare function assertNativeRlsReady(params: {
108
+ report: NativeRlsReport;
109
+ }): void;
110
+ /** One line per check plus one per unsafe object — `vibeorm doctor`'s output. */
111
+ export declare function formatNativeRlsReport(params: {
112
+ report: NativeRlsReport;
113
+ }): string[];
114
+ //# sourceMappingURL=rls-readiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rls-readiness.d.ts","sourceRoot":"","sources":["../src/rls-readiness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAW,QAAQ,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAIpD,uFAAuF;AACvF,MAAM,MAAM,kBAAkB,GAC1B,uBAAuB,GACvB,yBAAyB,GACzB,0BAA0B,GAC1B,gBAAgB,GAChB,iBAAiB,GACjB,0BAA0B,GAC1B,4BAA4B,GAC5B,2BAA2B,GAC3B,wBAAwB,GACxB,sBAAsB,GACtB,mBAAmB,GACnB,2BAA2B,GAC3B,gBAAgB,CAAC;AAErB;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,aAAa,CAAC;AAE5E,iDAAiD;AACjD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,wBAAwB,GAAG,SAAS,CAAC;IAClF,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,eAAe,GAAG;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,QAAQ,CAAC,aAAa,EAAE,SAAS,qBAAqB,EAAE,CAAC;CAC1D,CAAC;AAGF,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AA4hB1E;;;;;;;;;;;;GAYG;AACH,wBAAsB,2BAA2B,CAAC,MAAM,EAAE;IACxD,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,QAAQ,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;wFACoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,eAAe,CAAC,CA2H3B;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,QAAQ,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,eAAe,CAAC,CAiB3B;AAgBD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,eAAe,CAAA;CAAE,GAAG,IAAI,CAc9E;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,eAAe,CAAA;CAAE,GAAG,MAAM,EAAE,CAanF"}
@@ -0,0 +1,104 @@
1
+ import type { RelationIR } from "@vibeorm/schema";
2
+ import type { FieldMeta, RuntimeMeta } from "./model-meta.ts";
3
+ import type { ClientRow, DynamicClient, ModelDelegate } from "./client.ts";
4
+ /** Scope values a tenant column can carry (matches String / Int / BigInt columns). */
5
+ export type ScopeValue = string | number | bigint;
6
+ /**
7
+ * What happens when a scoped model appears as a CHILD inside another model's
8
+ * nested write: `"verify"` (default) rewrites reference verbs into
9
+ * tenant-verified lookups; `"refuse"` rejects every reference verb (nested
10
+ * `create` stays allowed under both — it is forced into the tenant).
11
+ */
12
+ export type ScopedNestedMode = "verify" | "refuse";
13
+ /** One model's classification in the tenancy map. */
14
+ export type ScopedModelEntry = "none" | {
15
+ readonly column: string;
16
+ readonly nested?: ScopedNestedMode;
17
+ } | {
18
+ readonly through: string;
19
+ readonly nested?: ScopedNestedMode;
20
+ };
21
+ /** The per-model tenancy map — must classify EVERY model (totality). */
22
+ export type ScopedModelsMap = Readonly<Record<string, ScopedModelEntry>>;
23
+ /** The `$scoped` argument. */
24
+ export type ScopedClientConfig = {
25
+ readonly scope: ScopeValue | null;
26
+ readonly models: ScopedModelsMap;
27
+ };
28
+ type ResolvedEntry = {
29
+ readonly kind: "none";
30
+ } | {
31
+ readonly kind: "column";
32
+ readonly field: FieldMeta;
33
+ readonly nested: ScopedNestedMode;
34
+ } | {
35
+ readonly kind: "through";
36
+ readonly relation: RelationIR;
37
+ readonly targetModel: string;
38
+ readonly nested: ScopedNestedMode;
39
+ };
40
+ type ResolvedScopedModels = ReadonlyMap<string, ResolvedEntry>;
41
+ /**
42
+ * Validate the models map against the runtime meta: totality, known
43
+ * columns/relations, scopeable column types, acyclic through-chains ending on
44
+ * a { column } model. Cached by (map, meta) object identity — the framework
45
+ * holds one map constant and calls `$scoped` per request.
46
+ */
47
+ export declare function resolveScopedModels(params: {
48
+ meta: RuntimeMeta;
49
+ models: ScopedModelsMap;
50
+ }): ResolvedScopedModels;
51
+ /** A rewritten call: which base-delegate method to run, with which args. */
52
+ export type RewrittenScopedCall = {
53
+ readonly model: string;
54
+ readonly method: string;
55
+ readonly args: ClientRow | undefined;
56
+ };
57
+ /** A validated, scope-bound rewriter — the pure core a binding's tests drive. */
58
+ export type ScopeRewriter = {
59
+ readonly rewrite: (call: {
60
+ model: string;
61
+ method: string;
62
+ args?: ClientRow;
63
+ }) => RewrittenScopedCall;
64
+ };
65
+ /**
66
+ * Validate a models map + scope against the runtime meta and return the pure
67
+ * per-call rewriter (`ScopedQueryError` on refusals). This is `$scoped`'s
68
+ * engine without the client plumbing — conformance rewrite-vectors run here.
69
+ */
70
+ export declare function createScopeRewriter(params: {
71
+ meta: RuntimeMeta;
72
+ models: ScopedModelsMap;
73
+ scope: ScopeValue | null;
74
+ }): ScopeRewriter;
75
+ /**
76
+ * Rewrite ONE delegate call for a scoped handle — pure: same inputs, same
77
+ * output; refusals throw `ScopedQueryError` before any SQL could run. This is
78
+ * the function the conformance rewrite-vectors pin.
79
+ */
80
+ export declare function rewriteScopedCall(params: {
81
+ meta: RuntimeMeta;
82
+ resolved: ResolvedScopedModels;
83
+ scope: ScopeValue | null;
84
+ preds: ReadonlyMap<string, ClientRow | null>;
85
+ model: string;
86
+ method: string;
87
+ args: ClientRow | undefined;
88
+ }): RewrittenScopedCall;
89
+ /**
90
+ * Attach `$scoped` to a built client. Validation happens per call:
91
+ * `@@policy` schemas refuse (the two scoping layers do not combine), the
92
+ * models map is resolved against the schema (cached by object identity), and
93
+ * the scope value is type-checked against every scoped column.
94
+ */
95
+ export declare function attachScoped(params: {
96
+ client: DynamicClient;
97
+ meta: RuntimeMeta;
98
+ wrapDelegate?: (params: {
99
+ model: string;
100
+ delegate: ModelDelegate;
101
+ }) => ModelDelegate;
102
+ }): void;
103
+ export {};
104
+ //# sourceMappingURL=scoped.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoped.d.ts","sourceRoot":"","sources":["../src/scoped.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Strict argument handling (EPIC 2) — ON BY DEFAULT.
3
+ *
4
+ * Legacy (and Prisma's own default) semantics treat `where: { tenantId: undefined }`
5
+ * exactly like `where: {}`: the member vanishes and a bulk write silently widens
6
+ * to every row. That is the single most expensive footgun in an ORM argument
7
+ * object, and it is invisible in review because the *code* names the filter.
8
+ *
9
+ * So an explicit `undefined` anywhere in ORM argument SYNTAX is refused before a
10
+ * statement is built, and `SKIP` is the deliberate way to say "omit this
11
+ * property". `strictArguments: false` on the client opts out and restores the
12
+ * legacy behaviour; only that explicit `false` does — an options object that
13
+ * never mentions the flag stays strict.
14
+ *
15
+ * What is NOT argument syntax, and is therefore never inspected:
16
+ * - anything under a `Json`-typed field (a JSON document's own keys are data,
17
+ * exactly as `validators.ts` and the query builder already treat them);
18
+ * - non-plain objects (`Date`, `Uint8Array`, `Decimal`, any class instance);
19
+ * - symbol-keyed properties.
20
+ */
21
+ import type { ModelMeta, RuntimeMeta } from "./model-meta.ts";
22
+ /**
23
+ * Omit a property deliberately: `{ where: { tenantId: SKIP } }` compiles exactly
24
+ * like `{ where: {} }`, in strict mode and in legacy mode alike.
25
+ *
26
+ * A globally registered symbol, so a consumer that cannot import the runtime
27
+ * (a generated-client-only app, a serialized argument builder) can name the very
28
+ * same value with `Symbol.for("vibeorm.skip")`.
29
+ *
30
+ * Named `SKIP`, not `skip`: `skip` is already a pagination argument, and
31
+ * `{ take: 10, skip: SKIP }` must not read as two meanings of one word.
32
+ */
33
+ export declare const SKIP: unique symbol;
34
+ /** The type of the {@link SKIP} sentinel. */
35
+ export type Skip = typeof SKIP;
36
+ /**
37
+ * Check one public call's arguments. Runs BEFORE any wrapper can discard an
38
+ * undefined member and before a statement is built, so a refused root operation
39
+ * dispatches nothing at all.
40
+ */
41
+ export declare function checkStrictArguments(params: {
42
+ readonly meta: RuntimeMeta;
43
+ readonly model: ModelMeta;
44
+ readonly method: string;
45
+ readonly args: Record<string, unknown> | undefined;
46
+ }): Record<string, unknown> | undefined;
47
+ //# sourceMappingURL=strict-args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strict-args.d.ts","sourceRoot":"","sources":["../src/strict-args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAI9D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,IAAI,EAAE,OAAO,MAAkD,CAAC;AAE7E,6CAA6C;AAC7C,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC;AAsH/B;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CACpD,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAMtC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * An in-memory collector — the ONLY exporter this repository ships.
3
+ *
4
+ * Its purpose is tests and local inspection: assert the emitted operation tree,
5
+ * the statement records and the database outcome together. It is deliberately
6
+ * not a monitoring integration; a real deployment writes its own exporter (or a
7
+ * future, separately decided OpenTelemetry bridge).
8
+ *
9
+ * `throwOn` makes it hostile on demand — the fixture that proves a throwing
10
+ * exporter cannot change a database outcome.
11
+ */
12
+ import type { TelemetryDropEvent, TelemetryExportPhase, TelemetryExporter, TelemetryOperationEvent, TelemetryStatementEvent } from "./types.ts";
13
+ /** One operation with the operations that ran inside it. */
14
+ export type TelemetryOperationNode = {
15
+ readonly event: TelemetryOperationEvent;
16
+ readonly children: readonly TelemetryOperationNode[];
17
+ };
18
+ /** The collector handle: the exporter to pass to the client, plus what it saw. */
19
+ export type TelemetryCollector = {
20
+ /** Pass this as `telemetry.exporter`. */
21
+ readonly exporter: TelemetryExporter;
22
+ readonly operations: readonly TelemetryOperationEvent[];
23
+ readonly statements: readonly TelemetryStatementEvent[];
24
+ readonly drops: readonly TelemetryDropEvent[];
25
+ readonly flushes: () => number;
26
+ readonly shutdowns: () => number;
27
+ readonly reset: () => void;
28
+ /** Operations as a parent/child tree, roots first, each level in emission order. */
29
+ readonly tree: () => readonly TelemetryOperationNode[];
30
+ /** Every operation whose `parentOperationId` is the given id, in emission order. */
31
+ readonly childrenOf: (params: {
32
+ operationId: string;
33
+ }) => readonly TelemetryOperationEvent[];
34
+ /** The single operation for a model method, or `undefined`; throws VIBE_VALIDATION when several match. */
35
+ readonly operationFor: (params: {
36
+ model?: string;
37
+ method: string;
38
+ }) => TelemetryOperationEvent | undefined;
39
+ };
40
+ /**
41
+ * Build a collector.
42
+ *
43
+ * @example
44
+ * const collector = createTelemetryCollector();
45
+ * const db = VibeClient({ adapter, telemetry: { exporter: collector.exporter } });
46
+ * await db.user.findMany();
47
+ * expect(collector.operations).toHaveLength(1);
48
+ */
49
+ export declare function createTelemetryCollector(params?: {
50
+ /** Callbacks that inject VIBE_INTERNAL, to prove the database outcome does not depend on the exporter. */
51
+ readonly throwOn?: readonly TelemetryExportPhase[];
52
+ }): TelemetryCollector;
53
+ //# sourceMappingURL=collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../src/telemetry/collector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EACV,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,4DAA4D;AAC5D,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,KAAK,EAAE,uBAAuB,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,SAAS,sBAAsB,EAAE,CAAC;CACtD,CAAC;AAEF,kFAAkF;AAClF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,yCAAyC;IACzC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACxD,QAAQ,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,MAAM,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAC3B,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,MAAM,SAAS,sBAAsB,EAAE,CAAC;IACvD,oFAAoF;IACpF,QAAQ,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,SAAS,uBAAuB,EAAE,CAAC;IAC7F,0GAA0G;IAC1G,QAAQ,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,uBAAuB,GAAG,SAAS,CAAC;CAC5G,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,CAAC,EAAE;IAChD,0GAA0G;IAC1G,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACpD,GAAG,kBAAkB,CAyFrB"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * THE telemetry config module (EPIC 1). Every threshold, limit and sampling
3
+ * default lives here and nowhere else — no magic literal for telemetry may
4
+ * appear in the recorder, the client or an adapter.
5
+ *
6
+ * Invalid configuration fails loudly at client construction with
7
+ * `VIBE_CONFIG`, not silently at the first query.
8
+ */
9
+ import type { TelemetryDelivery, TelemetryOptions, TelemetrySamplerInput } from "./types.ts";
10
+ /** Documented defaults. Privacy-conservative: nothing sensitive is captured unless asked for. */
11
+ export type TelemetryDefaults = {
12
+ readonly captureSql: false;
13
+ readonly captureParameterValues: false;
14
+ readonly detailedTimings: false;
15
+ readonly sampleRate: 1;
16
+ readonly delivery: "immediate";
17
+ readonly maxBufferedOperations: 1024;
18
+ readonly maxStatementsPerOperation: 256;
19
+ readonly slowStatementMs: 200;
20
+ readonly slowOperationMs: 500;
21
+ /** Longest fingerprint kept; longer normalized text is truncated with a stable suffix. */
22
+ readonly maxFingerprintLength: 512;
23
+ /** Histogram bucket bounds in milliseconds (upper-inclusive), plus an overflow bucket. */
24
+ readonly latencyBucketsMs: readonly [1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000];
25
+ };
26
+ export declare const TELEMETRY_DEFAULTS: TelemetryDefaults;
27
+ /** Every option resolved to a concrete value — what the recorder reads. */
28
+ export type ResolvedTelemetryConfig = {
29
+ readonly captureSql: boolean;
30
+ readonly captureParameterValues: boolean;
31
+ readonly detailedTimings: boolean;
32
+ readonly sampleRate: number;
33
+ readonly sampler?: (input: TelemetrySamplerInput) => boolean;
34
+ readonly context?: () => unknown;
35
+ readonly delivery: TelemetryDelivery;
36
+ readonly maxBufferedOperations: number;
37
+ readonly maxStatementsPerOperation: number;
38
+ readonly slowStatementMs: number;
39
+ readonly slowOperationMs: number;
40
+ readonly maxFingerprintLength: number;
41
+ readonly latencyBucketsMs: readonly number[];
42
+ };
43
+ /**
44
+ * Validate and resolve `ClientOptions.telemetry`.
45
+ *
46
+ * @throws VibeError VIBE_CONFIG when the exporter is missing, the sample rate is
47
+ * outside 0..1, a limit is not a positive integer, a threshold is negative, or
48
+ * `delivery` is not one of the two documented modes.
49
+ */
50
+ export declare function resolveTelemetryConfig(params: {
51
+ options: TelemetryOptions;
52
+ }): ResolvedTelemetryConfig;
53
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/telemetry/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAI7F,iGAAiG;AACjG,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;IAC3B,QAAQ,CAAC,sBAAsB,EAAE,KAAK,CAAC;IACvC,QAAQ,CAAC,eAAe,EAAE,KAAK,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,qBAAqB,EAAE,IAAI,CAAC;IACrC,QAAQ,CAAC,yBAAyB,EAAE,GAAG,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC;IAC9B,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC;IAC9B,0FAA0F;IAC1F,QAAQ,CAAC,oBAAoB,EAAE,GAAG,CAAC;IACnC,0FAA0F;IAC1F,QAAQ,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;CACzF,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,iBAc/B,CAAC;AAIH,2EAA2E;AAC3E,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,OAAO,CAAC;IAC7D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,yBAAyB,EAAE,MAAM,CAAC;IAC3C,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C,CAAC;AA4BF;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,gBAAgB,CAAA;CAAE,GAAG,uBAAuB,CA4CrG"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Stable, normalized query fingerprints for grouping (EPIC 1).
3
+ *
4
+ * A fingerprint is what a metrics backend may safely use as a label: bounded in
5
+ * length, free of literals, free of identifiers that carry data (request ids,
6
+ * tenant ids, URLs), and identical across executions of the same query shape.
7
+ *
8
+ * ORM-rendered SQL is already literal-free (every value is a placeholder), so
9
+ * normalization exists mainly for raw SQL, where a caller may have inlined
10
+ * literals. Normalization removes them BEFORE hashing, so a fingerprint can
11
+ * never leak a value even when raw text is never captured.
12
+ */
13
+ /**
14
+ * Reduce SQL text to its shape: comments removed, string and numeric literals
15
+ * replaced by `?`, placeholder numbering flattened, whitespace collapsed.
16
+ *
17
+ * @example
18
+ * normalizeSql({ text: `SELECT * FROM "User" WHERE id = 42 AND name = 'bob'` });
19
+ * // 'SELECT * FROM "User" WHERE id = ? AND name = ?'
20
+ */
21
+ export declare function normalizeSql(params: {
22
+ text: string;
23
+ }): string;
24
+ /**
25
+ * Build the grouping key for one statement: `model.method#hash` (raw SQL uses
26
+ * `raw` as the model). Always bounded by `maxLength`.
27
+ *
28
+ * @example
29
+ * queryFingerprint({ model: "User", method: "findMany", text: sql, maxLength: 512 });
30
+ * // 'User.findMany#3d0a91b2'
31
+ */
32
+ export declare function queryFingerprint(params: {
33
+ model: string | null;
34
+ method: string;
35
+ text: string;
36
+ maxLength: number;
37
+ }): string;
38
+ //# sourceMappingURL=fingerprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/telemetry/fingerprint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAqD7D;AAcD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAQT"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `@vibeorm/runtime` telemetry — the vendor-neutral observability seam.
3
+ *
4
+ * This is the MODULE barrel for `src/telemetry/**`, not the package's root
5
+ * barrel. See the lane's hand-off note for the one-line addition that makes
6
+ * these types public from `@vibeorm/runtime` itself.
7
+ */
8
+ export { TELEMETRY_DEFAULTS, resolveTelemetryConfig } from "./config.ts";
9
+ export type { ResolvedTelemetryConfig, TelemetryDefaults } from "./config.ts";
10
+ export { normalizeSql, queryFingerprint } from "./fingerprint.ts";
11
+ export { createTelemetryRecorder } from "./recorder.ts";
12
+ export type { TelemetryBeginInput, TelemetryOperationScope, TelemetryRecorder, TelemetrySettleInput, TelemetryStatementInput, } from "./recorder.ts";
13
+ export { instrumentStatement } from "./statement.ts";
14
+ export type { InstrumentStatementInput, StatementReport } from "./statement.ts";
15
+ export { createTelemetryCollector } from "./collector.ts";
16
+ export type { TelemetryCollector, TelemetryOperationNode } from "./collector.ts";
17
+ export type { TelemetryDelivery, TelemetryDropEvent, TelemetryExportPhase, TelemetryExporter, TelemetryHandle, TelemetryHistogram, TelemetryMetricsSnapshot, TelemetryOperationEvent, TelemetryOperationKind, TelemetryOptions, TelemetryOutcome, TelemetryParentContext, TelemetrySamplerInput, TelemetryStage, TelemetryStatementEvent, TelemetryStatementKind, TelemetryTimingDetail, } from "./types.ts";
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACzE,YAAY,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE9E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEhF,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,qBAAqB,EACrB,cAAc,EACd,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * The recorder: one per client, owning operation identity, sampling, bounded
3
+ * buffering, drop accounting, metrics and every guarded exporter call.
4
+ *
5
+ * Three invariants this module exists to hold:
6
+ *
7
+ * 1. **Exactly one terminal operation outcome.** `settle()` is idempotent —
8
+ * success, refusal before dispatch, validation failure, adapter failure,
9
+ * decode failure and commit/rollback failure all end in one event, and a
10
+ * later duplicate settle is ignored rather than emitting a second span.
11
+ * 2. **The database never depends on the exporter.** Every exporter callback
12
+ * runs inside a guard; a throw is reported to `onExporterError` (itself
13
+ * guarded) and swallowed. No exporter can change a query's arguments, its
14
+ * results or whether it committed.
15
+ * 3. **Nothing unknown is invented.** Absent row counts, absent durations and
16
+ * absent timing subdivisions stay absent.
17
+ */
18
+ import type { Dialect, Provider } from "@vibeorm/schema";
19
+ import type { ResolvedTelemetryConfig } from "./config.ts";
20
+ import type { TelemetryMetricsSnapshot, TelemetryOperationKind, TelemetryOptions, TelemetryOutcome, TelemetryStage, TelemetryStatementKind } from "./types.ts";
21
+ /** What starting an operation needs to know. */
22
+ export type TelemetryBeginInput = {
23
+ readonly kind: TelemetryOperationKind;
24
+ readonly model: string | null;
25
+ readonly method: string;
26
+ readonly dialect: Dialect;
27
+ readonly provider: Provider;
28
+ /** The enclosing transaction/extension operation, when there is one. */
29
+ readonly parentOperationId?: string;
30
+ };
31
+ /** One finished statement, reported by the execution path that ran it. */
32
+ export type TelemetryStatementInput = {
33
+ readonly kind: TelemetryStatementKind;
34
+ readonly model: string | null;
35
+ readonly method: string;
36
+ readonly text: string;
37
+ readonly values: readonly unknown[];
38
+ readonly inTransaction: boolean;
39
+ readonly durationMs: number;
40
+ readonly outcome: TelemetryOutcome;
41
+ /** Absent when this path cannot know it. */
42
+ readonly rowCount?: number;
43
+ readonly error?: unknown;
44
+ /** Time spent rendering this statement, when measured. */
45
+ readonly renderMs?: number;
46
+ };
47
+ /** How an operation ended. */
48
+ export type TelemetrySettleInput = {
49
+ readonly outcome: TelemetryOutcome;
50
+ /** Named only where the caller genuinely knows better than the derivation below. */
51
+ readonly stage?: TelemetryStage;
52
+ readonly error?: unknown;
53
+ /** Rows the operation is known to have produced or affected. */
54
+ readonly rowCount?: number;
55
+ };
56
+ /** The per-operation handle the client threads through its engine. */
57
+ export type TelemetryOperationScope = {
58
+ readonly id: string;
59
+ /** False when sampling declined this operation: statements and the span are skipped, counters are not. */
60
+ readonly sampled: boolean;
61
+ /** Mark ACTUAL dispatch. Idempotent; the first call starts the operation clock. */
62
+ readonly dispatch: () => void;
63
+ /**
64
+ * Re-attach this operation to the parent that actually executes it. An array
65
+ * `$transaction` member is created in one context and executed by another;
66
+ * the executing transaction is the truthful parent.
67
+ */
68
+ readonly reparent: (params: {
69
+ parentOperationId: string;
70
+ }) => void;
71
+ readonly statement: (input: TelemetryStatementInput) => void;
72
+ /** Terminal outcome. Idempotent — only the first call emits. */
73
+ readonly settle: (input: TelemetrySettleInput) => void;
74
+ };
75
+ /** One client's telemetry. */
76
+ export type TelemetryRecorder = {
77
+ readonly config: ResolvedTelemetryConfig;
78
+ readonly begin: (input: TelemetryBeginInput) => TelemetryOperationScope;
79
+ /** Drain buffered operations and ask the exporter to flush. Never shuts anything down. */
80
+ readonly flush: () => Promise<void>;
81
+ /** Host-owned: drains, then calls `exporter.shutdown`. The client never calls this. */
82
+ readonly shutdown: () => Promise<void>;
83
+ readonly metrics: () => TelemetryMetricsSnapshot;
84
+ };
85
+ /**
86
+ * Build a recorder from validated options.
87
+ *
88
+ * @throws VibeError VIBE_CONFIG when the options are invalid (see `resolveTelemetryConfig`).
89
+ */
90
+ export declare function createTelemetryRecorder(params: {
91
+ options: TelemetryOptions;
92
+ }): TelemetryRecorder;
93
+ //# sourceMappingURL=recorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../src/telemetry/recorder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The single statement-instrumentation seam.
3
+ *
4
+ * Every SQL execution in the client goes through here so that the legacy
5
+ * `onQuery` / `$on("query")` contract and the new telemetry path are reported
6
+ * from ONE place with unchanged semantics:
7
+ *
8
+ * - the legacy report still fires only after the adapter SUCCEEDS, before row
9
+ * materialization, with the same payload it always had;
10
+ * - telemetry additionally records FAILED statements, which the legacy path
11
+ * has never seen;
12
+ * - with neither observer present nothing is measured at all — no clock read,
13
+ * no allocation.
14
+ */
15
+ import type { TelemetryOperationScope } from "./recorder.ts";
16
+ import type { TelemetryStatementKind } from "./types.ts";
17
+ /**
18
+ * The legacy per-statement payload. Structurally identical to the client's
19
+ * `QueryEvent`; declared here so the telemetry module never imports the client
20
+ * (which imports this one).
21
+ */
22
+ export type StatementReport = {
23
+ readonly model: string | null;
24
+ readonly method: string;
25
+ readonly text: string;
26
+ readonly values: readonly unknown[];
27
+ readonly durationMs: number;
28
+ };
29
+ /** What `instrumentStatement` needs; `run` is the adapter call itself. */
30
+ export type InstrumentStatementInput<T> = {
31
+ readonly scope: TelemetryOperationScope | undefined;
32
+ /** Legacy `onQuery`/`$on` sink, or `undefined` when nobody is listening. */
33
+ readonly legacy: ((report: StatementReport) => void) | undefined;
34
+ readonly kind: TelemetryStatementKind;
35
+ readonly model: string | null;
36
+ readonly method: string;
37
+ readonly text: string;
38
+ readonly values: readonly unknown[];
39
+ readonly inTransaction: boolean;
40
+ /** Rendering time for this statement, when it was measured. */
41
+ readonly renderMs?: number;
42
+ /** Reads a known row count off the adapter result; return `undefined` when unknowable. */
43
+ readonly rowCount?: (value: T) => number | undefined;
44
+ readonly run: () => Promise<T>;
45
+ };
46
+ /**
47
+ * Run one statement with observation attached.
48
+ *
49
+ * The adapter's own result and its rejection are passed through untouched: an
50
+ * observer can never change what a query returns or whether it fails.
51
+ */
52
+ export declare function instrumentStatement<T>(params: InstrumentStatementInput<T>): Promise<T>;
53
+ //# sourceMappingURL=statement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statement.d.ts","sourceRoot":"","sources":["../../src/telemetry/statement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAA2B,MAAM,eAAe,CAAC;AACtF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,0EAA0E;AAC1E,MAAM,MAAM,wBAAwB,CAAC,CAAC,IAAI;IACxC,QAAQ,CAAC,KAAK,EAAE,uBAAuB,GAAG,SAAS,CAAC;IACpD,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACjE,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,0FAA0F;IAC1F,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;IACrD,QAAQ,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;CAChC,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAmD5F"}