@everystack/model 0.3.3 → 0.3.5

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/README.md CHANGED
@@ -74,6 +74,8 @@ export const models = [Account, Note];
74
74
 
75
75
  Modifiers: `.primaryKey()`, `.notNull()` / `.nullable()`, `.unique()`, `.default(value)` / `.defaultNow()` / `.defaultRandom()`, `.references(() => Other, { onDelete, onUpdate })`, `.private()` (never serialized), `.readonly()` (never accepted on write), `.validate(zodSchema)`, and `.renamedFrom('oldField')` (turns a drop+add into a data-preserving `RENAME COLUMN`).
76
76
 
77
+ Renaming a whole table works the same way, at the model level: `defineModel('articles', { renamedFrom: 'posts', ... })` compiles to one `ALTER TABLE … RENAME TO …` (data preserved, policies ride along) instead of CREATE plus a held DROP. Both markers are one-shot — remove them once every environment is past the state; a satisfied rename is an inert notice.
78
+
77
79
  `.validate(...)` does double duty: the SQL-expressible refinements (`min`/`max`/`length`/range/enum) become a Postgres `CHECK` — the backstop for the SSR-direct write path that never runs app-layer Zod — while richer refinements stay app-layer only.
78
80
 
79
81
  ## Constraints
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@everystack/model",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Declare a table's schema, validation, and authorization once — compiled to migrations, RLS, and handler config",
5
5
  "license": "AGPL-3.0-only",
6
6
  "publishConfig": {
package/src/drizzle.ts CHANGED
@@ -22,7 +22,10 @@ import {
22
22
  uuid,
23
23
  integer,
24
24
  bigint,
25
+ serial,
25
26
  bigserial,
27
+ real,
28
+ doublePrecision,
26
29
  boolean,
27
30
  timestamp,
28
31
  date,
@@ -74,8 +77,14 @@ function baseColumn(columnName: string, spec: FieldSpec): PgColumnBuilderBase {
74
77
  return integer(columnName);
75
78
  case 'bigint':
76
79
  return bigint(columnName, { mode: 'number' });
80
+ case 'serial':
81
+ return serial(columnName);
77
82
  case 'bigserial':
78
83
  return bigserial(columnName, { mode: 'number' });
84
+ case 'real':
85
+ return real(columnName);
86
+ case 'doublePrecision':
87
+ return doublePrecision(columnName);
79
88
  case 'boolean':
80
89
  return boolean(columnName);
81
90
  case 'timestamptz':
package/src/field.ts CHANGED
@@ -15,8 +15,11 @@ export type FieldType =
15
15
  | 'text'
16
16
  | 'uuid'
17
17
  | 'bigint'
18
+ | 'serial'
18
19
  | 'bigserial'
19
20
  | 'integer'
21
+ | 'real'
22
+ | 'doublePrecision'
20
23
  | 'boolean'
21
24
  | 'timestamptz'
22
25
  | 'timestamp'
@@ -186,8 +189,14 @@ export const field = {
186
189
  text: (): FieldBuilder => new FieldBuilder('text'),
187
190
  uuid: (): FieldBuilder => new FieldBuilder('uuid'),
188
191
  bigint: (): FieldBuilder => new FieldBuilder('bigint'),
189
- bigserial: (): FieldBuilder => new FieldBuilder('bigserial'),
192
+ // A serial column is NOT NULL by Postgres definition, so the builder starts that way.
193
+ serial: (): FieldBuilder => new FieldBuilder('serial').notNull(),
194
+ bigserial: (): FieldBuilder => new FieldBuilder('bigserial').notNull(),
190
195
  integer: (): FieldBuilder => new FieldBuilder('integer'),
196
+ /** `real` — single-precision float (float4). */
197
+ real: (): FieldBuilder => new FieldBuilder('real'),
198
+ /** `double precision` — double-precision float (float8). */
199
+ doublePrecision: (): FieldBuilder => new FieldBuilder('doublePrecision'),
191
200
  boolean: (): FieldBuilder => new FieldBuilder('boolean'),
192
201
  timestamptz: (): FieldBuilder => new FieldBuilder('timestamptz'),
193
202
  timestamp: (): FieldBuilder => new FieldBuilder('timestamp'),
package/src/model.ts CHANGED
@@ -63,6 +63,15 @@ export interface ModelDefinition {
63
63
  * can span schemas — the model's schema always wins over a generate call's default.
64
64
  */
65
65
  schema?: string;
66
+ /**
67
+ * `renamedFrom: 'old_table'` — a one-shot migration hint, the table-level
68
+ * analog of `field.renamedFrom()`. A diff cannot distinguish RENAME from
69
+ * DROP+ADD; the marker carries the intent, so the diff emits one
70
+ * `ALTER TABLE … RENAME TO …` (data preserved) instead of CREATE plus a
71
+ * held DROP. Same schema as the model. Remove it once every environment
72
+ * is past the state — a satisfied rename is an inert notice.
73
+ */
74
+ renamedFrom?: string;
66
75
  /**
67
76
  * `true` removes the table from the generic data API — `deriveHandlerConfig`
68
77
  * skips it, so it never enters `exposedTables`. Default `false`. Package-owned
@@ -90,6 +99,8 @@ export interface ModelDescriptor {
90
99
  schema: string;
91
100
  /** `true` removes the table from the generic data API (not in `exposedTables`). Default `false`. */
92
101
  private: boolean;
102
+ /** One-shot rename hint: this table used to be named `renamedFrom` (same schema). */
103
+ renamedFrom?: string;
93
104
  }
94
105
 
95
106
  export function defineModel(table: string, def: ModelDefinition): ModelDescriptor {
@@ -108,5 +119,6 @@ export function defineModel(table: string, def: ModelDefinition): ModelDescripto
108
119
  writtenBy: def.writtenBy ?? 'app',
109
120
  schema: def.schema ?? 'public',
110
121
  private: def.private ?? false,
122
+ ...(def.renamedFrom ? { renamedFrom: def.renamedFrom } : {}),
111
123
  };
112
124
  }