@vibeorm/schema 5.0.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.
@@ -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
@@ -130,8 +130,11 @@ export declare function internalError(params: {
130
130
  * Stable lifecycle categories; metadata never includes identity values.
131
131
  * `"null-claim"`: a key listed in `nativeRls.refuseNull` is bound to `null` and
132
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.
133
136
  */
134
- export declare const RLS_CONTEXT_REASONS: readonly ["missing", "invalid", "expired", "conflicting", "null-claim"];
137
+ export declare const RLS_CONTEXT_REASONS: readonly ["missing", "invalid", "expired", "conflicting", "null-claim", "context-switched"];
135
138
  export type RlsContextReason = (typeof RLS_CONTEXT_REASONS)[number];
136
139
  /** Native RLS binding/lifecycle failure, distinct from database permissions. */
137
140
  export declare class RlsContextError extends VibeError {
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
@@ -71,7 +71,8 @@ var RLS_CONTEXT_REASONS = Object.freeze([
71
71
  "invalid",
72
72
  "expired",
73
73
  "conflicting",
74
- "null-claim"
74
+ "null-claim",
75
+ "context-switched"
75
76
  ]);
76
77
 
77
78
  class RlsContextError extends VibeError {
@@ -447,6 +448,7 @@ var POSTGRES_CAPABILITIES = Object.freeze({
447
448
  distinctOn: true,
448
449
  partialIndexes: true,
449
450
  conflictTargetConstraint: true,
451
+ conflictUpdateWhere: true,
450
452
  transactionalDdl: true,
451
453
  createManyAndReturn: true,
452
454
  jsonColumnType: true,
@@ -457,7 +459,8 @@ var POSTGRES_CAPABILITIES = Object.freeze({
457
459
  advisoryLocks: true,
458
460
  timeOfDayTypes: Object.freeze(["time", "timetz"]),
459
461
  uuidColumns: "native",
460
- localDateColumns: true
462
+ localDateColumns: true,
463
+ dateTruncation: true
461
464
  });
462
465
  var SQLITE_CAPABILITIES = Object.freeze({
463
466
  rowRuleAppendOnly: true,
@@ -485,6 +488,7 @@ var SQLITE_CAPABILITIES = Object.freeze({
485
488
  distinctOn: false,
486
489
  partialIndexes: true,
487
490
  conflictTargetConstraint: false,
491
+ conflictUpdateWhere: true,
488
492
  transactionalDdl: true,
489
493
  createManyAndReturn: true,
490
494
  jsonColumnType: false,
@@ -495,7 +499,8 @@ var SQLITE_CAPABILITIES = Object.freeze({
495
499
  advisoryLocks: false,
496
500
  timeOfDayTypes: Object.freeze([]),
497
501
  uuidColumns: "text",
498
- localDateColumns: true
502
+ localDateColumns: true,
503
+ dateTruncation: false
499
504
  });
500
505
  var MYSQL_CAPABILITIES = Object.freeze({
501
506
  rowRuleAppendOnly: true,
@@ -523,6 +528,7 @@ var MYSQL_CAPABILITIES = Object.freeze({
523
528
  distinctOn: false,
524
529
  partialIndexes: false,
525
530
  conflictTargetConstraint: false,
531
+ conflictUpdateWhere: false,
526
532
  transactionalDdl: false,
527
533
  createManyAndReturn: false,
528
534
  jsonColumnType: true,
@@ -533,7 +539,8 @@ var MYSQL_CAPABILITIES = Object.freeze({
533
539
  advisoryLocks: false,
534
540
  timeOfDayTypes: Object.freeze(["time"]),
535
541
  uuidColumns: "unsupported",
536
- localDateColumns: true
542
+ localDateColumns: true,
543
+ dateTruncation: false
537
544
  });
538
545
  var DIALECT_CAPABILITIES = Object.freeze({
539
546
  postgres: POSTGRES_CAPABILITIES,
@@ -2414,6 +2421,23 @@ function timeOfDayType(params) {
2414
2421
  return "timetz";
2415
2422
  return "time";
2416
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
+ }
2417
2441
  // packages/schema/src/computed.ts
2418
2442
  function buildComputedManifest(params) {
2419
2443
  const { computed } = params;
@@ -2778,6 +2802,8 @@ var RESERVED_ENUM_NAMES = [
2778
2802
  "KeysetPage",
2779
2803
  "KeysetOptions",
2780
2804
  "RowLockRequest",
2805
+ "GroupByTruncateUnit",
2806
+ "GroupByKeyName",
2781
2807
  "JsonValue",
2782
2808
  "InputJsonValue",
2783
2809
  "InputJsonArray",
@@ -2810,6 +2836,7 @@ var RESERVED_ENUM_NAMES = [
2810
2836
  "Skip",
2811
2837
  "TransactionOptions",
2812
2838
  "CallbackTransactionOptions",
2839
+ "RawStatement",
2813
2840
  "AdvisoryLockOptions",
2814
2841
  "StatementDiagnosticsOptions",
2815
2842
  "SchemaIR",
@@ -2872,6 +2899,7 @@ var GENERATED_TYPE_NAMES = {
2872
2899
  "MaxAggregateOrderByInput",
2873
2900
  "HavingInput",
2874
2901
  "GroupByOrderByInput",
2902
+ "GroupByTruncatedKey",
2875
2903
  "FindManyArgs",
2876
2904
  "FindPageArgs",
2877
2905
  "FindFirstArgs",
@@ -3004,7 +3032,10 @@ function validateGeneratedNames(params) {
3004
3032
  owners.set("BoundVibeClientInstance", "native RLS client");
3005
3033
  owners.set("BoundVibeTransactionClientInstance", "native RLS client");
3006
3034
  owners.set("OperatorVibeClientInstance", "native RLS client");
3035
+ owners.set("OperatorVibeTransactionClientInstance", "native RLS client");
3007
3036
  }
3037
+ if (schema.models.some((model) => model.policy !== undefined))
3038
+ owners.set("PolicyClientInstance", "policy client");
3008
3039
  if (schema.models.some((model) => model.fields.some((field) => isLocalDateField({ field })))) {
3009
3040
  for (const name of ["LocalDate", "LocalDateFilter", "LocalDateNullableFilter"])
3010
3041
  owners.set(name, "local-date filters");
@@ -4368,6 +4399,7 @@ function validRlsForeignKey(params) {
4368
4399
  }
4369
4400
  export {
4370
4401
  CHECK_RULE_GROUP_MAX,
4402
+ DATE_TRUNCATION_UNITS,
4371
4403
  DIALECTS,
4372
4404
  DIALECT_CAPABILITIES,
4373
4405
  EMPTY_EXTENSION_REGISTRY,
@@ -4412,6 +4444,7 @@ export {
4412
4444
  buildComputedManifest,
4413
4445
  buildExtensionRegistry,
4414
4446
  checkRulePredicate,
4447
+ dateTruncationStorage,
4415
4448
  decodePrismaString,
4416
4449
  defaultExpressionIndexName,
4417
4450
  defaultForeignKeyName,
@@ -4502,4 +4535,4 @@ export {
4502
4535
  validatorRefIssue
4503
4536
  };
4504
4537
 
4505
- //# debugId=BE4A4B85E399756C64756E2164756E21
4538
+ //# debugId=92D7E56C6E6AF7A964756E2164756E21