@vibeorm/schema 4.3.0 → 5.1.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.
@@ -82,7 +82,7 @@ export type DialectCapabilities = {
82
82
  readonly nativeEnums: boolean;
83
83
  /** Native ILIKE operator. False ⇒ LOWER() LIKE LOWER() strategy. */
84
84
  readonly ilike: boolean;
85
- /** LATERAL JOIN relation strategy ("join") availability. */
85
+ /** LATERAL JOIN relation strategy ("join") availability (mysql: 8.0.14 or later, not MariaDB). */
86
86
  readonly lateralJoin: boolean;
87
87
  /** Native DISTINCT ON. */
88
88
  readonly distinctOn: boolean;
@@ -90,6 +90,14 @@ export type DialectCapabilities = {
90
90
  readonly partialIndexes: boolean;
91
91
  /** Named-constraint conflict targets (`ON CONFLICT ON CONSTRAINT …`). */
92
92
  readonly conflictTargetConstraint: boolean;
93
+ /**
94
+ * A guard on the conflict's update arm: `ON CONFLICT (…) DO UPDATE SET …
95
+ * WHERE <guard>` — the existing row is updated (and returned) only when the
96
+ * guard admits it, in the same atomic statement. Tenant-scoped (`$scoped`)
97
+ * and `$withPolicy` upserts use it; MySQL's `ON DUPLICATE KEY UPDATE` has no
98
+ * WHERE, so there they keep the transactional find-then-write path.
99
+ */
100
+ readonly conflictUpdateWhere: boolean;
93
101
  /** DDL participates in transactions (mysql auto-commits DDL). */
94
102
  readonly transactionalDdl: boolean;
95
103
  /** createManyAndReturn support (requires multi-row RETURNING). */
@@ -134,6 +142,13 @@ export type DialectCapabilities = {
134
142
  readonly uuidColumns: "native" | "text" | "unsupported";
135
143
  /** String-backed Gregorian calendar days: native DATE, or TEXT with an owned CHECK on SQLite. */
136
144
  readonly localDateColumns: boolean;
145
+ /**
146
+ * Calendar truncation of a timestamp in a named time zone — the `groupBy`
147
+ * key `{ field, truncate, timeZone }` (PostgreSQL `date_trunc` with a zone,
148
+ * PostgreSQL 12 or later). Without it the key is refused when the query is
149
+ * built.
150
+ */
151
+ readonly dateTruncation: boolean;
137
152
  };
138
153
  export declare const POSTGRES_CAPABILITIES: DialectCapabilities;
139
154
  export declare const SQLITE_CAPABILITIES: DialectCapabilities;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Calendar truncation of a timestamp — the `groupBy` key form
3
+ * `{ field, truncate, timeZone }` (one group per hour, day, ISO week, month
4
+ * or year of a `DateTime` field, in a named time zone).
5
+ *
6
+ * ONE rule decides which fields can take it: the runtime's refusal, the
7
+ * renderer's expression and the generator's `by` type all call
8
+ * {@link dateTruncationStorage}, so they can never disagree.
9
+ */
10
+ import type { FieldType } from "./ir.ts";
11
+ /** A calendar period a timestamp can be truncated to (PostgreSQL `date_trunc` units; `week` is the ISO week, starting on Monday). */
12
+ export type DateTruncationUnit = "hour" | "day" | "week" | "month" | "year";
13
+ /** Every {@link DateTruncationUnit}, in increasing length. */
14
+ export declare const DATE_TRUNCATION_UNITS: readonly DateTruncationUnit[];
15
+ /**
16
+ * How a truncatable field stores its value on PostgreSQL. `"timestamptz"`: an
17
+ * instant (the canonical `DateTime` mapping, `@db.Timestamptz(n)`).
18
+ * `"timestamp"`: a zone-less column (`@db.Timestamp(n)`), which by VibeORM's
19
+ * contract holds the UTC wall-clock of the instant on every postgres driver.
20
+ */
21
+ export type DateTruncationStorage = "timestamptz" | "timestamp";
22
+ /**
23
+ * The storage form of a field a timestamp truncation can read, or `undefined`
24
+ * when the field cannot be truncated: not a `DateTime`, a list, an
25
+ * unsupported type, or a native type that is not a timestamp (`@db.Date`,
26
+ * `@db.Time`, `@db.Timetz`). A `DateTime` without a native type takes the
27
+ * canonical mapping, `timestamptz`.
28
+ */
29
+ export declare function dateTruncationStorage(params: {
30
+ readonly field: {
31
+ readonly type: FieldType;
32
+ readonly nativeType?: string;
33
+ readonly isList: boolean;
34
+ readonly isUnsupported?: boolean;
35
+ };
36
+ }): DateTruncationStorage | undefined;
37
+ //# sourceMappingURL=date-truncation.d.ts.map
package/dist/errors.d.ts CHANGED
@@ -126,8 +126,15 @@ export declare function internalError(params: {
126
126
  message: string;
127
127
  meta?: Record<string, unknown>;
128
128
  }): VibeError;
129
- /** Stable lifecycle categories; metadata never includes identity values. */
130
- export declare const RLS_CONTEXT_REASONS: readonly ["missing", "invalid", "expired", "conflicting"];
129
+ /**
130
+ * Stable lifecycle categories; metadata never includes identity values.
131
+ * `"null-claim"`: a key listed in `nativeRls.refuseNull` is bound to `null` and
132
+ * the operation reaches a model whose rule reads it (or runs raw SQL).
133
+ * `"context-switched"`: the handle's transaction is running a `tx.$inContext`
134
+ * switch under another identity; the handle (and every handle derived from it)
135
+ * is refused until the switch has restored its claims.
136
+ */
137
+ export declare const RLS_CONTEXT_REASONS: readonly ["missing", "invalid", "expired", "conflicting", "null-claim", "context-switched"];
131
138
  export type RlsContextReason = (typeof RLS_CONTEXT_REASONS)[number];
132
139
  /** Native RLS binding/lifecycle failure, distinct from database permissions. */
133
140
  export declare class RlsContextError extends VibeError {
@@ -135,6 +142,7 @@ export declare class RlsContextError extends VibeError {
135
142
  constructor(params: {
136
143
  reason: RlsContextReason;
137
144
  key?: string;
145
+ model?: string;
138
146
  });
139
147
  }
140
148
  /**
package/dist/index.d.ts CHANGED
@@ -26,6 +26,8 @@ export { assertMigrationObjects, isSqlObjectDefinition, isSqlObjectIdentity, sql
26
26
  export type { MigrationObjectIR, QualifiedSqlName, SqlObjectDefinition, SqlObjectDefinitionFormat, SqlObjectFacts, SqlObjectIdentity } from "./migration-objects.ts";
27
27
  export type { AdvisoryLockMode, DialectCapabilities, FullTextEngine, RowLockStrength, RowLockWaitPolicy, TimeOfDayType, } from "./capabilities.ts";
28
28
  export { timeOfDayType } from "./time-of-day.ts";
29
+ export { DATE_TRUNCATION_UNITS, dateTruncationStorage } from "./date-truncation.ts";
30
+ export type { DateTruncationStorage, DateTruncationUnit } from "./date-truncation.ts";
29
31
  export { isLocalDateField, isCanonicalLocalDate, localDateCheckName } from "./local-date.ts";
30
32
  export { READ_ONLY_REFUSAL_REASONS, ReadOnlyClientError, SCOPED_REFUSAL_REASONS, ScopedQueryError, VIBE_ERROR_CODES, VibeError, internalError, invalidStoredData, unsupportedCapability, } from "./errors.ts";
31
33
  export type { ReadOnlyRefusalReason, ScopedRefusalReason, VibeErrorCode } from "./errors.ts";
package/dist/index.js CHANGED
@@ -70,7 +70,9 @@ var RLS_CONTEXT_REASONS = Object.freeze([
70
70
  "missing",
71
71
  "invalid",
72
72
  "expired",
73
- "conflicting"
73
+ "conflicting",
74
+ "null-claim",
75
+ "context-switched"
74
76
  ]);
75
77
 
76
78
  class RlsContextError extends VibeError {
@@ -78,8 +80,8 @@ class RlsContextError extends VibeError {
78
80
  constructor(params) {
79
81
  super({
80
82
  code: "VIBE_RLS_CONTEXT",
81
- message: `Native RLS context is ${params.reason}${params.key === undefined ? "" : ` at key ${JSON.stringify(params.key)}`}`,
82
- meta: { reason: params.reason, ...params.key === undefined ? {} : { key: params.key } }
83
+ message: params.reason === "null-claim" ? `Native RLS context key ${JSON.stringify(params.key)} is null and refused${params.model === undefined ? " for raw SQL" : ` for model ${JSON.stringify(params.model)}`}` : `Native RLS context is ${params.reason}${params.key === undefined ? "" : ` at key ${JSON.stringify(params.key)}`}`,
84
+ meta: { reason: params.reason, ...params.key === undefined ? {} : { key: params.key }, ...params.model === undefined ? {} : { model: params.model } }
83
85
  });
84
86
  this.name = "RlsContextError";
85
87
  this.reason = params.reason;
@@ -230,6 +232,7 @@ var RESERVED_CLIENT_NAMES = [
230
232
  "$executeRaw",
231
233
  "$executeRawUnsafe",
232
234
  "$on",
235
+ "$observe",
233
236
  "$extends",
234
237
  "$defineView",
235
238
  "$withPolicy",
@@ -445,6 +448,7 @@ var POSTGRES_CAPABILITIES = Object.freeze({
445
448
  distinctOn: true,
446
449
  partialIndexes: true,
447
450
  conflictTargetConstraint: true,
451
+ conflictUpdateWhere: true,
448
452
  transactionalDdl: true,
449
453
  createManyAndReturn: true,
450
454
  jsonColumnType: true,
@@ -455,7 +459,8 @@ var POSTGRES_CAPABILITIES = Object.freeze({
455
459
  advisoryLocks: true,
456
460
  timeOfDayTypes: Object.freeze(["time", "timetz"]),
457
461
  uuidColumns: "native",
458
- localDateColumns: true
462
+ localDateColumns: true,
463
+ dateTruncation: true
459
464
  });
460
465
  var SQLITE_CAPABILITIES = Object.freeze({
461
466
  rowRuleAppendOnly: true,
@@ -483,6 +488,7 @@ var SQLITE_CAPABILITIES = Object.freeze({
483
488
  distinctOn: false,
484
489
  partialIndexes: true,
485
490
  conflictTargetConstraint: false,
491
+ conflictUpdateWhere: true,
486
492
  transactionalDdl: true,
487
493
  createManyAndReturn: true,
488
494
  jsonColumnType: false,
@@ -493,7 +499,8 @@ var SQLITE_CAPABILITIES = Object.freeze({
493
499
  advisoryLocks: false,
494
500
  timeOfDayTypes: Object.freeze([]),
495
501
  uuidColumns: "text",
496
- localDateColumns: true
502
+ localDateColumns: true,
503
+ dateTruncation: false
497
504
  });
498
505
  var MYSQL_CAPABILITIES = Object.freeze({
499
506
  rowRuleAppendOnly: true,
@@ -517,10 +524,11 @@ var MYSQL_CAPABILITIES = Object.freeze({
517
524
  scalarArrays: false,
518
525
  nativeEnums: true,
519
526
  ilike: false,
520
- lateralJoin: false,
527
+ lateralJoin: true,
521
528
  distinctOn: false,
522
529
  partialIndexes: false,
523
530
  conflictTargetConstraint: false,
531
+ conflictUpdateWhere: false,
524
532
  transactionalDdl: false,
525
533
  createManyAndReturn: false,
526
534
  jsonColumnType: true,
@@ -531,7 +539,8 @@ var MYSQL_CAPABILITIES = Object.freeze({
531
539
  advisoryLocks: false,
532
540
  timeOfDayTypes: Object.freeze(["time"]),
533
541
  uuidColumns: "unsupported",
534
- localDateColumns: true
542
+ localDateColumns: true,
543
+ dateTruncation: false
535
544
  });
536
545
  var DIALECT_CAPABILITIES = Object.freeze({
537
546
  postgres: POSTGRES_CAPABILITIES,
@@ -2412,6 +2421,23 @@ function timeOfDayType(params) {
2412
2421
  return "timetz";
2413
2422
  return "time";
2414
2423
  }
2424
+ // packages/schema/src/date-truncation.ts
2425
+ var DATE_TRUNCATION_UNITS = Object.freeze(["hour", "day", "week", "month", "year"]);
2426
+ var ZONED_NATIVE_TYPE = /^timestamptz(?:\(\d+\))?$|^timestamp(?:\(\d+\))?withtimezone$/;
2427
+ var ZONE_LESS_NATIVE_TYPE = /^timestamp(?:\(\d+\))?(?:withouttimezone)?$/;
2428
+ function dateTruncationStorage(params) {
2429
+ const { field } = params;
2430
+ if (field.type !== "DateTime" || field.isList || field.isUnsupported === true)
2431
+ return;
2432
+ if (field.nativeType === undefined)
2433
+ return "timestamptz";
2434
+ const native = field.nativeType.toLowerCase().replace(/\s+/g, "");
2435
+ if (ZONED_NATIVE_TYPE.test(native))
2436
+ return "timestamptz";
2437
+ if (ZONE_LESS_NATIVE_TYPE.test(native))
2438
+ return "timestamp";
2439
+ return;
2440
+ }
2415
2441
  // packages/schema/src/computed.ts
2416
2442
  function buildComputedManifest(params) {
2417
2443
  const { computed } = params;
@@ -2776,6 +2802,8 @@ var RESERVED_ENUM_NAMES = [
2776
2802
  "KeysetPage",
2777
2803
  "KeysetOptions",
2778
2804
  "RowLockRequest",
2805
+ "GroupByTruncateUnit",
2806
+ "GroupByKeyName",
2779
2807
  "JsonValue",
2780
2808
  "InputJsonValue",
2781
2809
  "InputJsonArray",
@@ -2803,8 +2831,12 @@ var RESERVED_ENUM_NAMES = [
2803
2831
  "ClientContext",
2804
2832
  "QueryOperation",
2805
2833
  "QueryEvent",
2834
+ "QueryObserver",
2835
+ "QueryObserverFailure",
2806
2836
  "Skip",
2807
2837
  "TransactionOptions",
2838
+ "CallbackTransactionOptions",
2839
+ "RawStatement",
2808
2840
  "AdvisoryLockOptions",
2809
2841
  "StatementDiagnosticsOptions",
2810
2842
  "SchemaIR",
@@ -2867,6 +2899,7 @@ var GENERATED_TYPE_NAMES = {
2867
2899
  "MaxAggregateOrderByInput",
2868
2900
  "HavingInput",
2869
2901
  "GroupByOrderByInput",
2902
+ "GroupByTruncatedKey",
2870
2903
  "FindManyArgs",
2871
2904
  "FindPageArgs",
2872
2905
  "FindFirstArgs",
@@ -2998,7 +3031,11 @@ function validateGeneratedNames(params) {
2998
3031
  owners.set("RlsContext", "native RLS client");
2999
3032
  owners.set("BoundVibeClientInstance", "native RLS client");
3000
3033
  owners.set("BoundVibeTransactionClientInstance", "native RLS client");
3034
+ owners.set("OperatorVibeClientInstance", "native RLS client");
3035
+ owners.set("OperatorVibeTransactionClientInstance", "native RLS client");
3001
3036
  }
3037
+ if (schema.models.some((model) => model.policy !== undefined))
3038
+ owners.set("PolicyClientInstance", "policy client");
3002
3039
  if (schema.models.some((model) => model.fields.some((field) => isLocalDateField({ field })))) {
3003
3040
  for (const name of ["LocalDate", "LocalDateFilter", "LocalDateNullableFilter"])
3004
3041
  owners.set(name, "local-date filters");
@@ -4362,6 +4399,7 @@ function validRlsForeignKey(params) {
4362
4399
  }
4363
4400
  export {
4364
4401
  CHECK_RULE_GROUP_MAX,
4402
+ DATE_TRUNCATION_UNITS,
4365
4403
  DIALECTS,
4366
4404
  DIALECT_CAPABILITIES,
4367
4405
  EMPTY_EXTENSION_REGISTRY,
@@ -4406,6 +4444,7 @@ export {
4406
4444
  buildComputedManifest,
4407
4445
  buildExtensionRegistry,
4408
4446
  checkRulePredicate,
4447
+ dateTruncationStorage,
4409
4448
  decodePrismaString,
4410
4449
  defaultExpressionIndexName,
4411
4450
  defaultForeignKeyName,
@@ -4496,4 +4535,4 @@ export {
4496
4535
  validatorRefIssue
4497
4536
  };
4498
4537
 
4499
- //# debugId=2B926380FFCA323B64756E2164756E21
4538
+ //# debugId=92D7E56C6E6AF7A964756E2164756E21