nucleus-core-ts 0.9.813 → 0.9.815

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nucleus-core-ts",
3
- "version": "0.9.813",
3
+ "version": "0.9.815",
4
4
  "description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
5
5
  "author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -107,3 +107,31 @@ describe('numeric defaults are quoted', () => {
107
107
  expect(code).toContain(".default('0')")
108
108
  })
109
109
  })
110
+
111
+ /**
112
+ * An empty array default written as `{}` is an empty OBJECT in Postgres.
113
+ *
114
+ * Every row that never had a value then reads back as `{}` — and the first
115
+ * `for…of` over it throws "object is not iterable". Which is every existing row,
116
+ * the moment a new jsonb column is added to a table already in use: the panel
117
+ * that reads it dies on the next click, about a column nobody has filled in yet.
118
+ */
119
+ describe('jsonb defaults keep their own shape', () => {
120
+ const emit = (col: Record<string, unknown>) => generateColumnCode(col as never, [], 'widgets')
121
+
122
+ it('an empty array default stays an array', () => {
123
+ expect(emit({ name: 'reference_checks', type: 'jsonb', default: [] })).toContain("'[]'")
124
+ })
125
+
126
+ it('an empty object default stays an object', () => {
127
+ expect(emit({ name: 'settings', type: 'jsonb', default: {} })).toContain("'{}'")
128
+ })
129
+
130
+ it('carries a default that actually holds something', () => {
131
+ expect(emit({ name: 'flags', type: 'jsonb', default: { a: 1 } })).toContain('\'{"a":1}\'')
132
+ })
133
+
134
+ it('escapes a quote rather than ending the literal early', () => {
135
+ expect(emit({ name: 'labels', type: 'jsonb', default: ["it's"] })).toContain("''s")
136
+ })
137
+ })
@@ -212,7 +212,11 @@ export function selectEmittedTables<T extends { table_name: string }>(
212
212
  )
213
213
  }
214
214
 
215
- export function generateColumnCode(col: Column, allTables: string[], currentTable?: string): string {
215
+ export function generateColumnCode(
216
+ col: Column,
217
+ allTables: string[],
218
+ currentTable?: string
219
+ ): string {
216
220
  const drizzleType = TYPE_MAP[col.type] || 'text'
217
221
  let code = ''
218
222
 
@@ -273,10 +277,13 @@ export function generateColumnCode(col: Column, allTables: string[], currentTabl
273
277
  // that ran fine and failed `tsc`, which is how it survived: the file is
274
278
  // generated, so nobody reads it, and only a typecheck ever objected.
275
279
  code += isNumericType(col.type) ? `.default('${col.default}')` : `.default(${col.default})`
276
- } else if (Array.isArray(col.default)) {
277
- code += `.default(sql\`'{}'\`)`
278
- } else if (typeof col.default === 'object') {
279
- code += `.default(sql\`'{}'\`)`
280
+ } else if (typeof col.default === 'object' && col.default !== null) {
281
+ // Serialised properly, because `[]` written as `{}` is an empty OBJECT in
282
+ // Postgres. Every reader that iterates the column then throws "object is
283
+ // not iterable" the first time a row that never had a value is read —
284
+ // which is every existing row, the moment the column is added.
285
+ const literal = JSON.stringify(col.default).replace(/'/g, "''")
286
+ code += `.default(sql\`'${literal}'\`)`
280
287
  }
281
288
  }
282
289