@nuxodin/item 0.6.7 → 0.6.9
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
|
@@ -13,7 +13,12 @@ const capacity = {
|
|
|
13
13
|
maxLength: (need, have) => have >= need,
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const structural = ['
|
|
16
|
+
const structural = ['contentEncoding', 'x-index', 'default']
|
|
17
|
+
|
|
18
|
+
// Only these three formats become a column type in any dialect. `email`, `uri` and the rest are
|
|
19
|
+
// the application's business — a column cannot carry them, so they can never need DDL.
|
|
20
|
+
const temporal = new Set(['date', 'time', 'date-time'])
|
|
21
|
+
const columnFormat = (prop) => temporal.has(prop.format) ? prop.format : undefined
|
|
17
22
|
|
|
18
23
|
/** @param dialectStructural properties this dialect renders too, e.g. 'multipleOf' on MySQL. */
|
|
19
24
|
export function fieldNeedsDdl(next, curr, dialectStructural = []) {
|
|
@@ -23,6 +28,7 @@ export function fieldNeedsDdl(next, curr, dialectStructural = []) {
|
|
|
23
28
|
if (need !== undefined && have !== undefined && !holds(need, have)) return true
|
|
24
29
|
}
|
|
25
30
|
if (schemaType(next) !== schemaType(curr)) return true
|
|
31
|
+
if (columnFormat(next) !== columnFormat(curr)) return true
|
|
26
32
|
if (!!next['x-autoincrement'] !== !!curr['x-autoincrement']) return true
|
|
27
33
|
return [...structural, ...dialectStructural].some(prop => next[prop] !== curr[prop])
|
|
28
34
|
}
|
|
@@ -15,19 +15,32 @@ const indexName = (table, name) => `idx_${table}_${name}`
|
|
|
15
15
|
// SQLite: a single integer primary key must be declared inline as
|
|
16
16
|
// `INTEGER PRIMARY KEY [AUTOINCREMENT]`; AUTOINCREMENT is invalid elsewhere.
|
|
17
17
|
// Composite or non-integer keys use a table-level PRIMARY KEY constraint.
|
|
18
|
+
function soloIntPrimary(fields, primaries) {
|
|
19
|
+
if (primaries.length !== 1) return
|
|
20
|
+
const prop = fields.find(([n]) => n === primaries[0])?.[1]
|
|
21
|
+
return prop && ['integer', 'boolean'].includes(schemaType(prop)) ? primaries[0] : undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// What SQLite never emits cannot be a difference either: a fulltext index needs an FTS virtual
|
|
25
|
+
// table, and AUTOINCREMENT exists only on the rowid alias.
|
|
26
|
+
function emittable(prop, isSoloPk) {
|
|
27
|
+
const out = { ...prop }
|
|
28
|
+
if (out['x-index'] === 'fulltext') delete out['x-index']
|
|
29
|
+
if (!isSoloPk) delete out['x-autoincrement']
|
|
30
|
+
return out
|
|
31
|
+
}
|
|
32
|
+
|
|
18
33
|
function createBody(fields, primaries) {
|
|
19
|
-
const
|
|
20
|
-
const soloInt = primaries.length === 1 && pkProp &&
|
|
21
|
-
['integer', 'boolean'].includes(schemaType(pkProp))
|
|
34
|
+
const solo = soloIntPrimary(fields, primaries)
|
|
22
35
|
const cols = fields.map(([n, f]) => {
|
|
23
|
-
const isPk =
|
|
36
|
+
const isPk = n === solo
|
|
24
37
|
// Only `INTEGER PRIMARY KEY` is the rowid alias, and AUTOINCREMENT is legal nowhere else —
|
|
25
38
|
// so a boolean key keeps the integer spelling, which SQLite would otherwise reject.
|
|
26
39
|
let def = toFieldDef(n, isPk ? { ...f, type: 'integer' } : f)
|
|
27
40
|
if (isPk) def += f['x-autoincrement'] ? ' PRIMARY KEY AUTOINCREMENT' : ' PRIMARY KEY'
|
|
28
41
|
return ' ' + def
|
|
29
42
|
}).join(',\n')
|
|
30
|
-
const pk = (!
|
|
43
|
+
const pk = (!solo && primaries.length) ? `,\n PRIMARY KEY (${primaries.map(quoteId).join(', ')})` : ''
|
|
31
44
|
return `${cols}${pk}`
|
|
32
45
|
}
|
|
33
46
|
|
|
@@ -92,8 +105,9 @@ export async function schemaToDb(schema, query, { force = false, patch = false }
|
|
|
92
105
|
const currFields = new Map(tableFields(current.properties[table]))
|
|
93
106
|
const nextFieldNames = new Set(fields.map(([n]) => n))
|
|
94
107
|
// A new plain column is an ADD COLUMN; only a new key column needs the rebuild.
|
|
108
|
+
const solo = soloIntPrimary(fields, primaries)
|
|
95
109
|
const hasChanges = fields.some(([name, prop]) => currFields.has(name)
|
|
96
|
-
? fieldNeedsDdl(prop, currFields.get(name))
|
|
110
|
+
? fieldNeedsDdl(emittable(prop, name === solo), currFields.get(name))
|
|
97
111
|
: prop['x-index'] === 'primary' || prop['x-autoincrement'])
|
|
98
112
|
const hasDrops = !patch && [...currFields.keys()].some(n => !nextFieldNames.has(n))
|
|
99
113
|
|
|
@@ -17,9 +17,11 @@ export function toFieldDef(name, prop) {
|
|
|
17
17
|
else if (t === 'number') type = 'REAL'
|
|
18
18
|
else if (t === 'object' || t === 'array') type = 'TEXT' // JSON as text
|
|
19
19
|
else {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
// Same reasoning as BOOLEAN: the declared name is the only carrier of the format, and
|
|
21
|
+
// schemaFromField reads these back. TEXT would round-trip to a plain string.
|
|
22
|
+
if (prop.format === 'date') type = 'DATE'
|
|
23
|
+
else if (prop.format === 'time') type = 'TIME'
|
|
24
|
+
else if (prop.format === 'date-time') type = 'DATETIME'
|
|
23
25
|
else if (prop.contentEncoding === 'base64') type = 'BLOB'
|
|
24
26
|
else if (prop.maxLength) type = `VARCHAR(${prop.maxLength})`
|
|
25
27
|
else type = 'TEXT'
|