@vibeorm/runtime 2.0.0-alpha.7 → 2.0.0-alpha.8

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/client.d.ts CHANGED
@@ -22,6 +22,8 @@ import type { RelationStrategy } from "./query-builder.ts";
22
22
  import type { ClientComputedOptions } from "./computed.ts";
23
23
  import type { ClientExtensionsOptions } from "./extensions.ts";
24
24
  import type { DefineViewFn } from "./views.ts";
25
+ import type { WriteValidators } from "./validators.ts";
26
+ import type { PolicyContextValue } from "./policy.ts";
25
27
  /** One executed statement, reported to `ClientOptions.onQuery`. */
26
28
  export type QueryEvent = {
27
29
  /** Model name, or `null` for raw queries. */
@@ -52,6 +54,13 @@ export type ClientOptions = {
52
54
  * v1 parity.
53
55
  */
54
56
  readonly distinctOrderByPk?: boolean;
57
+ /**
58
+ * Opt-in write-boundary validators (Standard Schema, per model × field):
59
+ * `{ User: { settings: zodSchema } }`. Run on every write path (nested
60
+ * creates included) before encoding; failure throws VIBE_VALIDATION with
61
+ * the validator's issues in `meta`. Reads are never taxed.
62
+ */
63
+ readonly validate?: WriteValidators;
55
64
  };
56
65
  /** Rows are plain objects keyed by field name; the generated client narrows them. */
57
66
  export type ClientRow = Record<string, unknown>;
@@ -128,6 +137,14 @@ export type DynamicClient = {
128
137
  * it is always present on a built client, transaction clients included.
129
138
  */
130
139
  readonly $defineView?: DefineViewFn;
140
+ /**
141
+ * Row scoping (@@policy): bind a per-request context — the returned client
142
+ * AND-s the policy predicate into every query on policied models and forces
143
+ * it into creates. Attached only when the schema declares @@policy.
144
+ */
145
+ readonly $withPolicy?: (context: Readonly<Record<string, PolicyContextValue>>) => DynamicClient;
146
+ /** Deliberate, greppable full-access escape from policy scoping. */
147
+ readonly $bypassPolicy?: () => DynamicClient;
131
148
  };
132
149
  /**
133
150
  * Build a client for a schema over an adapter.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -25,6 +25,10 @@ export type { ClientComputedOptions } from "./computed.ts";
25
25
  export { assertExtensionManifest, buildConnectHooks, buildExtensionOperatorMap, } from "./extensions.ts";
26
26
  export type { ClientExtensionMount, ClientExtensionsOptions, ConnectHook, ExtensionMountContext, } from "./extensions.ts";
27
27
  export { ViewResult, attachDefineView, createView } from "./views.ts";
28
+ export { applyWriteValidators, validateWriteData } from "./validators.ts";
29
+ export type { StandardIssue, StandardSchemaV1, WriteValidators } from "./validators.ts";
30
+ export { attachPolicy } from "./policy.ts";
31
+ export type { PolicyContextValue } from "./policy.ts";
28
32
  export type { DefineViewFn, ViewClientShape, ViewDefinition, ViewModelDelegate } from "./views.ts";
29
33
  export { createClient } from "./client.ts";
30
34
  export type { BatchResult, ClientOptions, ClientRow, DynamicClient, ModelDelegate, QueryEvent, } from "./client.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EACV,eAAe,EACf,cAAc,EACd,WAAW,EACX,WAAW,EACX,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAItB,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,GACb,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,mBAAmB,EACnB,SAAS,EACT,SAAS,EACT,WAAW,GACZ,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,SAAS,EACT,UAAU,EACV,WAAW,EACX,WAAW,EACX,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIzE,OAAO,EACL,WAAW,EACX,UAAU,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AACnF,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC/E,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAI7D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACzF,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIxE,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC5E,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAI3D,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,WAAW,EACX,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAInG,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EACV,WAAW,EACX,aAAa,EACb,SAAS,EACT,aAAa,EACb,aAAa,EACb,UAAU,GACX,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EACV,eAAe,EACf,cAAc,EACd,WAAW,EACX,WAAW,EACX,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAItB,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,GACb,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,mBAAmB,EACnB,SAAS,EACT,SAAS,EACT,WAAW,GACZ,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,SAAS,EACT,UAAU,EACV,WAAW,EACX,WAAW,EACX,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIzE,OAAO,EACL,WAAW,EACX,UAAU,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AACnF,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC/E,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAI7D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACzF,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIxE,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC5E,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAI3D,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,WAAW,EACX,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAItE,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAInG,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EACV,WAAW,EACX,aAAa,EACb,SAAS,EACT,aAAa,EACb,aAAa,EACb,UAAU,GACX,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -133,6 +133,7 @@ function buildFieldMeta(params) {
133
133
  return {
134
134
  name: field.name,
135
135
  columnName: field.dbName ?? field.name,
136
+ ...field.collation === undefined ? {} : { collation: field.collation },
136
137
  type: field.type,
137
138
  scalarType: enumRef ? null : field.type,
138
139
  enumName: enumRef ? field.type.enum : null,
@@ -231,6 +232,7 @@ function buildModelMeta(params) {
231
232
  singleIndexUniques,
232
233
  relations: model.relations,
233
234
  relationByName: new Map(model.relations.map((relation) => [relation.name, relation])),
235
+ ...model.policy === undefined ? {} : { policy: model.policy },
234
236
  isView: model.isView,
235
237
  computed
236
238
  };
@@ -1440,6 +1442,7 @@ function compileOperatorFilter(params) {
1440
1442
  }
1441
1443
  const insensitive = mode === "insensitive";
1442
1444
  const strict = mode === "strict";
1445
+ const ciCollated = (params2) => params2.dialect.name === "mysql" && params2.field.collation?.toLowerCase().endsWith("_ci") === true;
1443
1446
  const requireStringOperand = (operator, operand) => {
1444
1447
  if (typeof operand !== "string") {
1445
1448
  throw validationError2({
@@ -1467,7 +1470,8 @@ function compileOperatorFilter(params) {
1467
1470
  kind: "like",
1468
1471
  column,
1469
1472
  pattern: escapeLikePattern({ raw: requireStringOperand("equals", operand) }),
1470
- insensitive: true
1473
+ insensitive: true,
1474
+ ...ciCollated({ dialect, field }) ? { ciCollation: true } : {}
1471
1475
  });
1472
1476
  break;
1473
1477
  }
@@ -1564,7 +1568,8 @@ function compileOperatorFilter(params) {
1564
1568
  kind: "like",
1565
1569
  column,
1566
1570
  pattern: compileStringPattern({ operator, operand, field, model }),
1567
- insensitive
1571
+ insensitive,
1572
+ ...insensitive && ciCollated({ dialect, field }) ? { ciCollation: true } : {}
1568
1573
  });
1569
1574
  break;
1570
1575
  }
@@ -5505,8 +5510,341 @@ function attachDefineView(params) {
5505
5510
  const value = (options) => createView({ meta, models, name: options.name, definition: options.definition });
5506
5511
  Object.defineProperty(client, "$defineView", { value, enumerable: true });
5507
5512
  }
5513
+ // packages/runtime/src/validators.ts
5514
+ import { VibeError as VibeError11 } from "@vibeorm/schema";
5515
+ function validationFailed(params) {
5516
+ return new VibeError11({
5517
+ code: "VIBE_VALIDATION",
5518
+ message: `validation failed for ${params.model}.${params.field}: ${params.issues[0]?.message ?? "invalid value"}`,
5519
+ meta: { model: params.model, field: params.field, issues: params.issues }
5520
+ });
5521
+ }
5522
+ function validateValue(params) {
5523
+ const result = params.schema["~standard"].validate(params.value);
5524
+ if (result instanceof Promise) {
5525
+ throw new VibeError11({
5526
+ code: "VIBE_VALIDATION",
5527
+ message: `async validator on ${params.model}.${params.field} \u2014 write validators must be synchronous (validate before calling the client for async rules)`,
5528
+ meta: { model: params.model, field: params.field }
5529
+ });
5530
+ }
5531
+ if (result.issues !== undefined) {
5532
+ throw validationFailed({ model: params.model, field: params.field, issues: result.issues });
5533
+ }
5534
+ return result.value;
5535
+ }
5536
+ function validateWriteData(params) {
5537
+ const { meta, model, data, validators } = params;
5538
+ const fieldSchemas = validators[model.name];
5539
+ const out = { ...data };
5540
+ for (const [key, value] of Object.entries(data)) {
5541
+ if (value === undefined)
5542
+ continue;
5543
+ const schema = fieldSchemas?.[key];
5544
+ if (schema !== undefined) {
5545
+ const isOperator = typeof value === "object" && value !== null && !Array.isArray(value) && "set" in value;
5546
+ if (isOperator) {
5547
+ const inner = value.set;
5548
+ out[key] = { set: validateValue({ model: model.name, field: key, schema, value: inner }) };
5549
+ } else {
5550
+ out[key] = validateValue({ model: model.name, field: key, schema, value });
5551
+ }
5552
+ continue;
5553
+ }
5554
+ const relation = model.relationByName.get(key);
5555
+ if (relation === undefined || typeof value !== "object" || value === null)
5556
+ continue;
5557
+ const target = meta.modelByName.get(relation.targetModel);
5558
+ if (target === undefined || validators[target.name] === undefined)
5559
+ continue;
5560
+ const verbs = value;
5561
+ const recurse = (row) => typeof row === "object" && row !== null ? validateWriteData({ meta, model: target, data: row, validators }) : row;
5562
+ const next = { ...verbs };
5563
+ if (verbs.create !== undefined) {
5564
+ next.create = Array.isArray(verbs.create) ? verbs.create.map(recurse) : recurse(verbs.create);
5565
+ }
5566
+ if (verbs.connectOrCreate !== undefined) {
5567
+ const entries = Array.isArray(verbs.connectOrCreate) ? verbs.connectOrCreate : [verbs.connectOrCreate];
5568
+ const mapped = entries.map((entry) => {
5569
+ const pair = entry;
5570
+ return { ...pair, create: recurse(pair.create) };
5571
+ });
5572
+ next.connectOrCreate = Array.isArray(verbs.connectOrCreate) ? mapped : mapped[0];
5573
+ }
5574
+ if (verbs.update !== undefined && !Array.isArray(verbs.update)) {
5575
+ const body = verbs.update;
5576
+ next.update = body.data !== undefined ? { ...body, data: recurse(body.data) } : recurse(body);
5577
+ }
5578
+ if (verbs.upsert !== undefined && !Array.isArray(verbs.upsert)) {
5579
+ const body = verbs.upsert;
5580
+ next.upsert = { ...body, create: recurse(body.create), update: recurse(body.update) };
5581
+ }
5582
+ out[key] = next;
5583
+ }
5584
+ return out;
5585
+ }
5586
+ function applyWriteValidators(params) {
5587
+ const { meta, model, method, args, validators } = params;
5588
+ const validate = (row) => typeof row === "object" && row !== null ? validateWriteData({ meta, model, data: row, validators }) : row;
5589
+ if (method === "create" || method === "update" || method === "updateMany") {
5590
+ return args.data === undefined ? args : { ...args, data: validate(args.data) };
5591
+ }
5592
+ if (method === "createMany" || method === "createManyAndReturn") {
5593
+ return Array.isArray(args.data) ? { ...args, data: args.data.map(validate) } : args;
5594
+ }
5595
+ if (method === "upsert") {
5596
+ return { ...args, create: validate(args.create), update: validate(args.update) };
5597
+ }
5598
+ return args;
5599
+ }
5600
+ // packages/runtime/src/policy.ts
5601
+ import { VibeError as VibeError12 } from "@vibeorm/schema";
5602
+ function policyError(message, meta) {
5603
+ return new VibeError12({ code: "VIBE_VALIDATION", message, meta: meta ?? {} });
5604
+ }
5605
+ function policiedModels(meta) {
5606
+ return meta.models.filter((model) => model.policy !== undefined);
5607
+ }
5608
+ function mergeWhere(params) {
5609
+ const { where, field, value } = params;
5610
+ const predicate = { [field]: value };
5611
+ return where === undefined ? predicate : { AND: [where, predicate] };
5612
+ }
5613
+ function uniqueToFilter(params) {
5614
+ const out = {};
5615
+ for (const [key, value] of Object.entries(params.where)) {
5616
+ const isCompound = typeof value === "object" && value !== null && !Array.isArray(value) && params.model.compoundUniques.has(key);
5617
+ if (isCompound)
5618
+ Object.assign(out, value);
5619
+ else
5620
+ out[key] = value;
5621
+ }
5622
+ return out;
5623
+ }
5624
+ function scopeSelection(params) {
5625
+ const { meta, model, args, context } = params;
5626
+ const out = { ...args };
5627
+ for (const key of ["include", "select"]) {
5628
+ const block = args[key];
5629
+ if (typeof block !== "object" || block === null)
5630
+ continue;
5631
+ const next = { ...block };
5632
+ for (const [name, spec] of Object.entries(block)) {
5633
+ const relation = model.relationByName.get(name);
5634
+ if (relation === undefined)
5635
+ continue;
5636
+ const target = meta.modelByName.get(relation.targetModel);
5637
+ if (target?.policy === undefined)
5638
+ continue;
5639
+ const value = context[target.policy.context];
5640
+ const spec2 = spec === true ? {} : { ...spec };
5641
+ spec2.where = mergeWhere({ where: spec2.where, field: target.policy.field, value });
5642
+ next[name] = scopeSelection({ meta, model: target, args: spec2, context });
5643
+ }
5644
+ out[key] = next;
5645
+ }
5646
+ return out;
5647
+ }
5648
+ var REFERENCE_VERBS = ["connect", "connectOrCreate", "set", "disconnect", "update", "updateMany", "delete", "deleteMany", "upsert"];
5649
+ function forceCreateData(params) {
5650
+ const { meta, model, data, context } = params;
5651
+ const out = { ...data };
5652
+ if (model.policy !== undefined) {
5653
+ const value = context[model.policy.context];
5654
+ const given = data[model.policy.field];
5655
+ if (given !== undefined && given !== value) {
5656
+ throw policyError(`create on ${model.name}: data.${model.policy.field} conflicts with the bound policy context`, { model: model.name, field: model.policy.field });
5657
+ }
5658
+ out[model.policy.field] = value;
5659
+ }
5660
+ for (const [name, spec] of Object.entries(data)) {
5661
+ const relation = model.relationByName.get(name);
5662
+ if (relation === undefined || typeof spec !== "object" || spec === null)
5663
+ continue;
5664
+ const target = meta.modelByName.get(relation.targetModel);
5665
+ if (target?.policy === undefined)
5666
+ continue;
5667
+ const verbs = spec;
5668
+ for (const verb of REFERENCE_VERBS) {
5669
+ if (verbs[verb] !== undefined) {
5670
+ throw policyError(`nested ${verb} on policied model ${target.name} is not policy-safe from a scoped client \u2014 perform it in an explicit $transaction on the scoped delegate`, { model: model.name, relation: name, verb });
5671
+ }
5672
+ }
5673
+ if (verbs.create !== undefined) {
5674
+ const recurse = (row) => forceCreateData({ meta, model: target, data: row, context });
5675
+ out[name] = { ...verbs, create: Array.isArray(verbs.create) ? verbs.create.map(recurse) : recurse(verbs.create) };
5676
+ }
5677
+ }
5678
+ return out;
5679
+ }
5680
+ function scopedDelegate(params) {
5681
+ const { meta, model, base, context, transaction } = params;
5682
+ const policy = model.policy;
5683
+ const value = context[policy.context];
5684
+ const pk = model.primaryKey.map((field) => field.name);
5685
+ const scopeFind = (args) => {
5686
+ const scoped = scopeSelection({ meta, model, args: args ?? {}, context });
5687
+ return { ...scoped, where: mergeWhere({ where: scoped.where, field: policy.field, value }) };
5688
+ };
5689
+ const scopeUnique = (args) => {
5690
+ const scoped = scopeSelection({ meta, model, args, context });
5691
+ const flat = uniqueToFilter({ model, where: args.where ?? {} });
5692
+ return { ...scoped, where: mergeWhere({ where: flat, field: policy.field, value }) };
5693
+ };
5694
+ const pkClientKey = () => {
5695
+ for (const [key, group] of model.compoundUniques) {
5696
+ const names = group.fields.map((field) => field.name);
5697
+ if (names.length === pk.length && names.every((name) => pk.includes(name)))
5698
+ return key;
5699
+ }
5700
+ return pk.join("_");
5701
+ };
5702
+ const lockThen = (args, write) => transaction(async (tx) => {
5703
+ const raw = tx.$bypassPolicy === undefined ? tx : tx.$bypassPolicy();
5704
+ const target = await raw.$models[model.name].findFirst({
5705
+ where: scopeUnique(args).where,
5706
+ select: Object.fromEntries(pk.map((name) => [name, true])),
5707
+ lock: "update"
5708
+ });
5709
+ if (target === null)
5710
+ return null;
5711
+ const pkWhere = pk.length === 1 ? { [pk[0]]: target[pk[0]] } : { [pkClientKey()]: Object.fromEntries(pk.map((name) => [name, target[name]])) };
5712
+ return write(raw, pkWhere);
5713
+ });
5714
+ const notFound2 = (method) => new VibeError12({ code: "VIBE_NOT_FOUND", message: `${method} on ${model.name}: no row matches the unique where inside the bound policy context`, meta: { model: model.name, method } });
5715
+ return {
5716
+ findMany: (args) => base.findMany(scopeFind(args)),
5717
+ findFirst: (args) => base.findFirst(scopeFind(args)),
5718
+ findFirstOrThrow: (args) => base.findFirstOrThrow(scopeFind(args)),
5719
+ findUnique: (args) => base.findFirst(scopeUnique(args)),
5720
+ findUniqueOrThrow: (args) => base.findFirstOrThrow(scopeUnique(args)),
5721
+ create: async (args) => base.create({ ...args, data: forceCreateData({ meta, model, data: args.data ?? {}, context }) }),
5722
+ createMany: async (args) => base.createMany({
5723
+ ...args,
5724
+ data: (args.data ?? []).map((row) => forceCreateData({ meta, model, data: row, context }))
5725
+ }),
5726
+ createManyAndReturn: async (args) => base.createManyAndReturn({
5727
+ ...args,
5728
+ data: (args.data ?? []).map((row) => forceCreateData({ meta, model, data: row, context }))
5729
+ }),
5730
+ update: async (args) => {
5731
+ const row = await lockThen(args, (raw, pkWhere) => raw.$models[model.name].update({ ...args, where: pkWhere }));
5732
+ if (row === null)
5733
+ throw notFound2("update");
5734
+ return row;
5735
+ },
5736
+ updateMany: (args) => base.updateMany({ ...args, where: mergeWhere({ where: args.where, field: policy.field, value }) }),
5737
+ upsert: async (args) => {
5738
+ const row = await lockThen(args, (raw, pkWhere) => raw.$models[model.name].update({ where: pkWhere, data: args.update, ...args.select === undefined ? {} : { select: args.select } }));
5739
+ if (row !== null)
5740
+ return row;
5741
+ return await base.create({
5742
+ data: forceCreateData({ meta, model, data: args.create ?? {}, context }),
5743
+ ...args.select === undefined ? {} : { select: args.select }
5744
+ });
5745
+ },
5746
+ delete: async (args) => {
5747
+ const row = await lockThen(args, (raw, pkWhere) => raw.$models[model.name].delete({ ...args, where: pkWhere }));
5748
+ if (row === null)
5749
+ throw notFound2("delete");
5750
+ return row;
5751
+ },
5752
+ deleteMany: (args) => base.deleteMany({ ...args ?? {}, where: mergeWhere({ where: args?.where, field: policy.field, value }) }),
5753
+ count: (args) => base.count(scopeFind(args)),
5754
+ aggregate: (args) => base.aggregate(scopeFind(args)),
5755
+ groupBy: (args) => base.groupBy(scopeFind(args))
5756
+ };
5757
+ }
5758
+ function scopeExtensionProps(params) {
5759
+ const out = {};
5760
+ for (const [key, prop] of Object.entries(params.base)) {
5761
+ if (key in params.delegate)
5762
+ continue;
5763
+ if (typeof prop === "function") {
5764
+ out[key] = (args) => prop({ ...args ?? {}, where: mergeWhere({ where: args?.where, field: params.field, value: params.value }) });
5765
+ } else if (typeof prop === "object" && prop !== null) {
5766
+ out[key] = scopeExtensionProps({ base: prop, delegate: {}, field: params.field, value: params.value });
5767
+ }
5768
+ }
5769
+ return out;
5770
+ }
5771
+ function attachPolicy(params) {
5772
+ const { client, meta } = params;
5773
+ const policied = policiedModels(meta);
5774
+ if (policied.length === 0)
5775
+ return;
5776
+ const originals = new Map;
5777
+ for (const model of policied) {
5778
+ const original = client.$models[model.name];
5779
+ originals.set(model.name, { ...original });
5780
+ const guard = (method) => () => Promise.reject(policyError(`model ${model.name} declares @@policy \u2014 bind a context with $withPolicy({ ${model.policy?.context}: ... }) or bypass deliberately with $bypassPolicy()`, { model: model.name, method }));
5781
+ const guarded = {};
5782
+ for (const key of Object.keys(original)) {
5783
+ guarded[key] = typeof original[key] === "function" ? guard(key) : original[key];
5784
+ }
5785
+ Object.assign(original, guarded);
5786
+ }
5787
+ const buildScoped = (context) => {
5788
+ for (const model of policied) {
5789
+ const key = model.policy?.context;
5790
+ if (context[key] === undefined || context[key] === null) {
5791
+ throw policyError(`$withPolicy: missing context value "${key}" (required by ${model.name})`, { context: key });
5792
+ }
5793
+ }
5794
+ const scoped = Object.create(client);
5795
+ const scopedModels = { ...client.$models };
5796
+ for (const model of policied) {
5797
+ const base = originals.get(model.name);
5798
+ const value = context[model.policy?.context];
5799
+ const delegate = scopedDelegate({ meta, model, base, context, transaction: client.$transaction });
5800
+ const full = { ...delegate, ...scopeExtensionProps({ base, delegate, field: model.policy?.field, value }) };
5801
+ scopedModels[model.name] = full;
5802
+ scopedModels[model.clientName] = full;
5803
+ Object.defineProperty(scoped, model.clientName, { value: full, enumerable: true });
5804
+ }
5805
+ Object.defineProperty(scoped, "$models", { value: scopedModels, enumerable: true });
5806
+ attachDefineView({ client: scoped, meta, models: scopedModels });
5807
+ Object.defineProperty(scoped, "$transaction", {
5808
+ value: (arg, options) => typeof arg === "function" ? client.$transaction((tx) => arg(buildScopedOver(tx, context)), options) : client.$transaction(arg, options),
5809
+ enumerable: true
5810
+ });
5811
+ return scoped;
5812
+ };
5813
+ const buildScopedOver = (tx, context) => {
5814
+ const rawTx = tx.$bypassPolicy === undefined ? tx : tx.$bypassPolicy();
5815
+ const inner = Object.create(tx);
5816
+ const scopedModels = { ...tx.$models };
5817
+ for (const model of policied) {
5818
+ const base = rawTx.$models[model.name];
5819
+ const delegate = scopedDelegate({ meta, model, base, context, transaction: tx.$transaction });
5820
+ scopedModels[model.name] = delegate;
5821
+ scopedModels[model.clientName] = delegate;
5822
+ Object.defineProperty(inner, model.clientName, { value: delegate, enumerable: true });
5823
+ }
5824
+ Object.defineProperty(inner, "$models", { value: scopedModels, enumerable: true });
5825
+ attachDefineView({ client: inner, meta, models: scopedModels });
5826
+ return inner;
5827
+ };
5828
+ Object.defineProperty(client, "$withPolicy", { value: (context) => buildScoped(context), enumerable: true });
5829
+ Object.defineProperty(client, "$bypassPolicy", {
5830
+ value: () => {
5831
+ const bypass = Object.create(client);
5832
+ const models = { ...client.$models };
5833
+ for (const model of policied) {
5834
+ const original = originals.get(model.name);
5835
+ models[model.name] = original;
5836
+ models[model.clientName] = original;
5837
+ Object.defineProperty(bypass, model.clientName, { value: original, enumerable: true });
5838
+ }
5839
+ Object.defineProperty(bypass, "$models", { value: models, enumerable: true });
5840
+ attachDefineView({ client: bypass, meta, models });
5841
+ return bypass;
5842
+ },
5843
+ enumerable: true
5844
+ });
5845
+ }
5508
5846
  // packages/runtime/src/client.ts
5509
- import { VibeError as VibeError11, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
5847
+ import { VibeError as VibeError13, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
5510
5848
  import { ParamCollector, getSqlDialect } from "@vibeorm/sql";
5511
5849
 
5512
5850
  // packages/runtime/src/render-cache.ts
@@ -5956,7 +6294,7 @@ function renderStatementCached(params) {
5956
6294
  // packages/runtime/src/client.ts
5957
6295
  function notFound2(params) {
5958
6296
  const { model, method } = params;
5959
- return new VibeError11({
6297
+ return new VibeError13({
5960
6298
  code: "VIBE_NOT_FOUND",
5961
6299
  message: `no ${model} record matched the where clause of ${method}()`,
5962
6300
  meta: { model, method }
@@ -5964,7 +6302,7 @@ function notFound2(params) {
5964
6302
  }
5965
6303
  function viewIsReadOnly(params) {
5966
6304
  const { model, method } = params;
5967
- return new VibeError11({
6305
+ return new VibeError13({
5968
6306
  code: "VIBE_VALIDATION",
5969
6307
  message: `${model} is a view \u2014 ${method}() is not available on views (they are read-only); write to the underlying table instead`,
5970
6308
  meta: { model, method }
@@ -6093,7 +6431,7 @@ class QueryPromise extends Promise {
6093
6431
  }
6094
6432
  claim() {
6095
6433
  if (this.started) {
6096
- throw new VibeError11({
6434
+ throw new VibeError13({
6097
6435
  code: "VIBE_VALIDATION",
6098
6436
  message: "$transaction([...]) needs operations that have not dispatched \u2014 pass delegate calls in the same tick they are created (this one was awaited or created earlier and already executed outside the transaction)"
6099
6437
  });
@@ -6171,7 +6509,7 @@ async function runAffected(params) {
6171
6509
  function plan(params) {
6172
6510
  const { engine, model, method, args } = params;
6173
6511
  if (args !== undefined && args["lock"] !== undefined && engine.inTransaction !== true) {
6174
- throw new VibeError11({
6512
+ throw new VibeError13({
6175
6513
  code: "VIBE_VALIDATION",
6176
6514
  message: `lock on ${model}.${method}() requires a transaction \u2014 call it on the client $transaction hands its callback (outside one, the lock would release at statement end)`,
6177
6515
  meta: { model, method }
@@ -6244,7 +6582,7 @@ async function insertedKeyWhere(params) {
6244
6582
  });
6245
6583
  const id = result.rows[0]?.id;
6246
6584
  if (id === undefined || id === null) {
6247
- throw new VibeError11({
6585
+ throw new VibeError13({
6248
6586
  code: "VIBE_INTERNAL",
6249
6587
  message: `could not read back the generated key for ${source.model.name}`,
6250
6588
  meta: { model: source.model.name }
@@ -6546,7 +6884,7 @@ function createDelegate(params) {
6546
6884
  const skipping = args.onConflict !== undefined;
6547
6885
  if (hasNestedWrites({ model: modelMeta, data: args.data })) {
6548
6886
  if (skipping) {
6549
- throw new VibeError11({
6887
+ throw new VibeError13({
6550
6888
  code: "VIBE_VALIDATION",
6551
6889
  message: `onConflict cannot combine with nested writes on ${modelMeta.name}.create() \u2014 insert the parent row alone, then connect relations`,
6552
6890
  meta: { model: modelMeta.name }
@@ -6630,7 +6968,7 @@ function createDelegate(params) {
6630
6968
  const upsert = async (target, args) => {
6631
6969
  const modelMeta = getModelMeta({ meta: target.meta, model });
6632
6970
  if (hasNestedWrites({ model: modelMeta, data: args.create }) || hasNestedWrites({ model: modelMeta, data: args.update })) {
6633
- throw new VibeError11({
6971
+ throw new VibeError13({
6634
6972
  code: "VIBE_VALIDATION",
6635
6973
  message: `nested writes are not supported inside upsert arms on ${modelMeta.name} \u2014 use create()/update() with nested data`,
6636
6974
  meta: { model: modelMeta.name }
@@ -6638,7 +6976,7 @@ function createDelegate(params) {
6638
6976
  }
6639
6977
  if (!upsertUsesNativeStatement({ model: modelMeta, args })) {
6640
6978
  if (args.onConflict !== undefined) {
6641
- throw new VibeError11({
6979
+ throw new VibeError13({
6642
6980
  code: "VIBE_VALIDATION",
6643
6981
  message: `onConflict on ${modelMeta.name}.upsert() needs the native single-statement path \u2014 this shape (a guard field beyond the conflict target, or create data not binding the where's key) runs the find-then-write fallback, which cannot honor an explicit conflict target`,
6644
6982
  meta: { model: modelMeta.name }
@@ -6765,6 +7103,17 @@ function createDelegate(params) {
6765
7103
  const meta = getModelMeta({ meta: engine.meta, model });
6766
7104
  return meta.isView ? readOnlyDelegate({ delegate, model: meta.name }) : delegate;
6767
7105
  }
7106
+ var WRITE_METHODS = ["create", "createMany", "createManyAndReturn", "update", "updateMany", "upsert"];
7107
+ function withWriteValidation(params) {
7108
+ const { engine, delegate, model, validators } = params;
7109
+ const meta = getModelMeta({ meta: engine.meta, model });
7110
+ const wrapped = { ...delegate };
7111
+ for (const method of WRITE_METHODS) {
7112
+ const original = delegate[method];
7113
+ wrapped[method] = (args) => original(applyWriteValidators({ meta: engine.meta, model: meta, method, args, validators }));
7114
+ }
7115
+ return wrapped;
7116
+ }
6768
7117
  function buildRawStatement(params) {
6769
7118
  const { dialect, strings, values } = params;
6770
7119
  const collector = new ParamCollector(dialect);
@@ -6778,7 +7127,11 @@ function buildClient(params) {
6778
7127
  const { engine } = params;
6779
7128
  const models = {};
6780
7129
  for (const model of engine.meta.models) {
6781
- const delegate = createDelegate({ engine, model: model.name });
7130
+ let delegate = createDelegate({ engine, model: model.name });
7131
+ const validators = engine.options?.validate;
7132
+ if (validators?.[model.name] !== undefined) {
7133
+ delegate = withWriteValidation({ engine, delegate, model: model.name, validators });
7134
+ }
6782
7135
  models[model.name] = delegate;
6783
7136
  models[model.clientName] = delegate;
6784
7137
  }
@@ -6800,7 +7153,7 @@ function buildClient(params) {
6800
7153
  let claimError = null;
6801
7154
  for (const operation of operations) {
6802
7155
  if (!(operation instanceof QueryPromise)) {
6803
- claimError = new VibeError11({
7156
+ claimError = new VibeError13({
6804
7157
  code: "VIBE_VALIDATION",
6805
7158
  message: "$transaction([...]) takes query promises from model delegate calls \u2014 a foreign promise is already running and cannot join the transaction"
6806
7159
  });
@@ -6839,7 +7192,7 @@ function buildClient(params) {
6839
7192
  $models: models,
6840
7193
  $on: (event, listener) => {
6841
7194
  if (event !== "query") {
6842
- throw new VibeError11({
7195
+ throw new VibeError13({
6843
7196
  code: "VIBE_VALIDATION",
6844
7197
  message: `unknown client event "${String(event)}" \u2014 supported: "query"`,
6845
7198
  meta: { event }
@@ -6873,6 +7226,7 @@ function buildClient(params) {
6873
7226
  }
6874
7227
  attachExtensionMounts({ engine, client, models });
6875
7228
  attachDefineView({ client, meta: engine.meta, models });
7229
+ attachPolicy({ client, meta: engine.meta });
6876
7230
  return client;
6877
7231
  }
6878
7232
  function makeMountContext(params) {
@@ -6902,7 +7256,7 @@ function makeMountContext(params) {
6902
7256
  compileWhere: ({ where, model: modelName, qualify }) => {
6903
7257
  const target = modelName === undefined ? model : getModelMeta({ meta: engine.meta, model: modelName });
6904
7258
  if (target === null) {
6905
- throw new VibeError11({
7259
+ throw new VibeError13({
6906
7260
  code: "VIBE_INTERNAL",
6907
7261
  message: `extension "${extension}" compiled a where with no model in scope`,
6908
7262
  meta: { extension }
@@ -6928,7 +7282,7 @@ function attachExtensionMounts(params) {
6928
7282
  const model = getModelMeta({ meta: engine.meta, model: mount.model ?? "" });
6929
7283
  const delegate = models[model.name];
6930
7284
  if (delegate === undefined || mount.property in delegate) {
6931
- throw new VibeError11({
7285
+ throw new VibeError13({
6932
7286
  code: "VIBE_CONFIG",
6933
7287
  message: `extension "${mount.extension}" cannot mount "${mount.property}" on ${model.name} \u2014 the name is taken`,
6934
7288
  meta: { extension: mount.extension, model: model.name, property: mount.property }
@@ -6939,7 +7293,7 @@ function attachExtensionMounts(params) {
6939
7293
  continue;
6940
7294
  }
6941
7295
  if (!mount.property.startsWith("$") || mount.property in client) {
6942
- throw new VibeError11({
7296
+ throw new VibeError13({
6943
7297
  code: "VIBE_CONFIG",
6944
7298
  message: `extension "${mount.extension}" cannot mount client property "${mount.property}" \u2014 root mounts are $-prefixed and must not collide`,
6945
7299
  meta: { extension: mount.extension, property: mount.property }
@@ -6959,7 +7313,7 @@ function createClient(params) {
6959
7313
  assertComputedManifest({ manifest: computed.manifest, specs: computed.specs });
6960
7314
  }
6961
7315
  if (computed === undefined && options !== undefined && options["computed"] !== undefined) {
6962
- throw new VibeError11({
7316
+ throw new VibeError13({
6963
7317
  code: "VIBE_CONFIG",
6964
7318
  message: "this client was generated without computed fields \u2014 declare them in vibeorm.config.ts and re-run vibeorm generate",
6965
7319
  meta: {}
@@ -6987,6 +7341,7 @@ function createClient(params) {
6987
7341
  });
6988
7342
  }
6989
7343
  export {
7344
+ validateWriteData,
6990
7345
  updateWithNested,
6991
7346
  toSqlExecutor,
6992
7347
  toClientName,
@@ -7022,10 +7377,12 @@ export {
7022
7377
  buildExtensionOperatorMap,
7023
7378
  buildConnectHooks,
7024
7379
  attachRelations,
7380
+ attachPolicy,
7025
7381
  attachLateralRelations,
7026
7382
  attachDefineView,
7027
7383
  assertExtensionManifest,
7028
7384
  assertComputedManifest,
7385
+ applyWriteValidators,
7029
7386
  applyComputedFields,
7030
7387
  ViewResult,
7031
7388
  SQLITE_CODECS,
@@ -7035,4 +7392,4 @@ export {
7035
7392
  COUNT_ALIAS
7036
7393
  };
7037
7394
 
7038
- //# debugId=96AA73705B84E72364756E2164756E21
7395
+ //# debugId=EC61C00B9601D9AD64756E2164756E21