@vibeorm/runtime 2.0.0-alpha.1 → 2.0.0-alpha.10
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/adapter.d.ts +132 -6
- package/dist/adapter.d.ts.map +1 -1
- package/dist/bulk-upsert.d.ts +282 -0
- package/dist/bulk-upsert.d.ts.map +1 -0
- package/dist/client.d.ts +70 -19
- package/dist/client.d.ts.map +1 -1
- package/dist/codecs.d.ts.map +1 -1
- package/dist/computed.d.ts +43 -0
- package/dist/computed.d.ts.map +1 -0
- package/dist/db-now.d.ts +41 -0
- package/dist/db-now.d.ts.map +1 -0
- package/dist/diagnostics/index.d.ts +12 -0
- package/dist/diagnostics/index.d.ts.map +1 -0
- package/dist/diagnostics/insight.d.ts +63 -0
- package/dist/diagnostics/insight.d.ts.map +1 -0
- package/dist/diagnostics/plan.d.ts +88 -0
- package/dist/diagnostics/plan.d.ts.map +1 -0
- package/dist/diagnostics/preview.d.ts +43 -0
- package/dist/diagnostics/preview.d.ts.map +1 -0
- package/dist/diagnostics/types.d.ts +223 -0
- package/dist/diagnostics/types.d.ts.map +1 -0
- package/dist/diagnostics/workload.d.ts +32 -0
- package/dist/diagnostics/workload.d.ts.map +1 -0
- package/dist/index.d.ts +33 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10958 -4325
- package/dist/index.js.map +36 -13
- package/dist/keyset-iterator.d.ts +73 -0
- package/dist/keyset-iterator.d.ts.map +1 -0
- package/dist/keyset.d.ts +121 -0
- package/dist/keyset.d.ts.map +1 -0
- package/dist/model-meta.d.ts +62 -6
- package/dist/model-meta.d.ts.map +1 -1
- package/dist/nested-writes.d.ts +0 -33
- package/dist/nested-writes.d.ts.map +1 -1
- package/dist/policy-operation.d.ts +14 -0
- package/dist/policy-operation.d.ts.map +1 -0
- package/dist/policy.d.ts +17 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/query-builder.d.ts +30 -1
- package/dist/query-builder.d.ts.map +1 -1
- package/dist/relation-key.d.ts +23 -0
- package/dist/relation-key.d.ts.map +1 -0
- package/dist/relation-loader.d.ts +0 -29
- package/dist/relation-loader.d.ts.map +1 -1
- package/dist/relation-plan.d.ts +47 -7
- package/dist/relation-plan.d.ts.map +1 -1
- package/dist/render-cache.d.ts.map +1 -1
- package/dist/rls-context.d.ts +14 -0
- package/dist/rls-context.d.ts.map +1 -0
- package/dist/rls-readiness.d.ts +114 -0
- package/dist/rls-readiness.d.ts.map +1 -0
- package/dist/scoped.d.ts +104 -0
- package/dist/scoped.d.ts.map +1 -0
- package/dist/strict-args.d.ts +47 -0
- package/dist/strict-args.d.ts.map +1 -0
- package/dist/telemetry/collector.d.ts +53 -0
- package/dist/telemetry/collector.d.ts.map +1 -0
- package/dist/telemetry/config.d.ts +53 -0
- package/dist/telemetry/config.d.ts.map +1 -0
- package/dist/telemetry/fingerprint.d.ts +38 -0
- package/dist/telemetry/fingerprint.d.ts.map +1 -0
- package/dist/telemetry/index.d.ts +18 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/recorder.d.ts +93 -0
- package/dist/telemetry/recorder.d.ts.map +1 -0
- package/dist/telemetry/statement.d.ts +53 -0
- package/dist/telemetry/statement.d.ts.map +1 -0
- package/dist/telemetry/types.d.ts +265 -0
- package/dist/telemetry/types.d.ts.map +1 -0
- package/dist/validators.d.ts +61 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/views.d.ts.map +1 -1
- package/dist/write-scope.d.ts +14 -0
- package/dist/write-scope.d.ts.map +1 -0
- package/package.json +6 -4
package/dist/relation-plan.d.ts
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* - `select: { rel: … }` and `include: { rel: … }` both load; `include` wins
|
|
11
11
|
* when both name the same relation.
|
|
12
12
|
* - `_count` uses the v1 shape: `include._count: true | { select: { rel: true } }`
|
|
13
|
-
* (or under `select`)
|
|
13
|
+
* (or under `select`) and counts LIST relations only. A count entry may also
|
|
14
|
+
* be `{ where: … }` (EPIC 4): the filter is carried on the resolved node and
|
|
15
|
+
* compiled by the loader through the ordinary where compiler.
|
|
14
16
|
* - Implicit many-to-many resolves through the ONE shared join-table rule
|
|
15
17
|
* (`implicitJoinTable` in @vibeorm/schema) — never re-derived here.
|
|
16
18
|
*/
|
|
@@ -31,10 +33,15 @@ export type RelationLink = {
|
|
|
31
33
|
readonly kind: RelationKind;
|
|
32
34
|
readonly relation: RelationIR;
|
|
33
35
|
readonly target: ModelMeta;
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
/**
|
|
37
|
+
* ORDERED parent-side correlation fields — index `i` pairs with
|
|
38
|
+
* `childKeys[i]`. A composite foreign key carries every column here; there is
|
|
39
|
+
* deliberately no singular accessor, so no call site can truncate a tuple to
|
|
40
|
+
* its first member.
|
|
41
|
+
*/
|
|
42
|
+
readonly parentKeys: readonly FieldMeta[];
|
|
43
|
+
/** ORDERED child-side correlation fields, positionally paired with {@link parentKeys}. */
|
|
44
|
+
readonly childKeys: readonly FieldMeta[];
|
|
38
45
|
/** Join-table wiring; `null` unless kind is `manyToMany`. */
|
|
39
46
|
readonly join: {
|
|
40
47
|
readonly tableName: string;
|
|
@@ -54,6 +61,8 @@ export type RelationArgs = {
|
|
|
54
61
|
readonly projected: readonly string[] | null;
|
|
55
62
|
/** Fields in `projected` the caller did NOT ask for — deleted after stitching. */
|
|
56
63
|
readonly strip: readonly string[];
|
|
64
|
+
/** Computed fields to attach to loaded children, or `null` — resolved like the top level. */
|
|
65
|
+
readonly computed: readonly string[] | null;
|
|
57
66
|
/** Deeper relation levels under this one. */
|
|
58
67
|
readonly nested: RelationSelection | null;
|
|
59
68
|
};
|
|
@@ -63,11 +72,42 @@ export type RelationTreeNode = {
|
|
|
63
72
|
readonly link: RelationLink;
|
|
64
73
|
readonly args: RelationArgs;
|
|
65
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* One relation counted into `_count`, with the filter that narrows it.
|
|
77
|
+
*
|
|
78
|
+
* The link is resolved HERE, once, so no later consumer has to re-derive a
|
|
79
|
+
* relation's ordered key tuples just to count through them.
|
|
80
|
+
*/
|
|
81
|
+
export type RelationCountNode = {
|
|
82
|
+
/** List-relation field name on the parent (`posts`). */
|
|
83
|
+
readonly name: string;
|
|
84
|
+
/** Structural link the count correlates and groups on. */
|
|
85
|
+
readonly link: RelationLink;
|
|
86
|
+
/**
|
|
87
|
+
* Validated `where` narrowing the counted children, or `undefined` for
|
|
88
|
+
* "count every child". The loader compiles it with `compileWhere` — the same
|
|
89
|
+
* centralized compiler and codecs an ordinary query where goes through.
|
|
90
|
+
*/
|
|
91
|
+
readonly where: Record<string, unknown> | undefined;
|
|
92
|
+
};
|
|
66
93
|
/** Everything relation-shaped one query level asked for. */
|
|
67
94
|
export type RelationSelection = {
|
|
68
95
|
readonly relations: readonly RelationTreeNode[];
|
|
69
|
-
/** List
|
|
70
|
-
readonly counts: readonly
|
|
96
|
+
/** List relations to count into `_count`, each with its own filter. */
|
|
97
|
+
readonly counts: readonly RelationCountNode[];
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* The ONE correlation pair of an arity-1 link, for the paths that are single-key
|
|
101
|
+
* by construction (implicit many-to-many, whose join table has one column per
|
|
102
|
+
* side). Refuses a composite link with a typed capability error BEFORE any
|
|
103
|
+
* statement runs — never truncates.
|
|
104
|
+
*/
|
|
105
|
+
export declare function singleKeyPair(params: {
|
|
106
|
+
link: RelationLink;
|
|
107
|
+
what: string;
|
|
108
|
+
}): {
|
|
109
|
+
readonly parent: FieldMeta;
|
|
110
|
+
readonly child: FieldMeta;
|
|
71
111
|
};
|
|
72
112
|
/** Resolve how one relation is physically wired. */
|
|
73
113
|
export declare function resolveRelationLink(params: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relation-plan.d.ts","sourceRoot":"","sources":["../src/relation-plan.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"relation-plan.d.ts","sourceRoot":"","sources":["../src/relation-plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-cache.d.ts","sourceRoot":"","sources":["../src/render-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;
|
|
1
|
+
{"version":3,"file":"render-cache.d.ts","sourceRoot":"","sources":["../src/render-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAWH,OAAO,KAAK,EAMV,aAAa,EAEb,UAAU,EAEV,YAAY,EAGb,MAAM,cAAc,CAAC;AAItB,KAAK,UAAU,GAAG;IAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;CACnD,CAAC;AAEF,2EAA2E;AAC3E,KAAK,SAAS,GAAG,UAAU,GAAG,IAAI,CAAC;AAInC,8EAA8E;AAC9E,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAE7D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAEtC;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAKtC;CACF;AA0fD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,OAAO,EAAE,UAAU,CAAC;IACpB,SAAS,EAAE,YAAY,CAAC;IACxB,KAAK,EAAE,oBAAoB,CAAC;CAC7B,GAAG,aAAa,CAoBhB"}
|
|
@@ -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"}
|
package/dist/scoped.d.ts
ADDED
|
@@ -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"}
|