@stacksjs/orm 0.75.482 → 0.75.483
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/dist/native.d.ts +7 -0
- package/dist/native.js +1 -1
- package/package.json +23 -23
package/dist/native.d.ts
CHANGED
|
@@ -5,6 +5,13 @@ import type { StacksModelDefinition } from './define-model';
|
|
|
5
5
|
* Own one SQLite database in a native backend process. Schema comes from the
|
|
6
6
|
* same migration planner as Buddy; additive upgrades and the snapshot commit
|
|
7
7
|
* atomically. Destructive upgrades require an explicit application migration.
|
|
8
|
+
*
|
|
9
|
+
* One destructive change is tolerated rather than refused: a column that is in
|
|
10
|
+
* the store but not in this build's models. That is what an installed app sees
|
|
11
|
+
* after a rollback, and refusing left it unable to open at all — in a launcher
|
|
12
|
+
* with no dock icon, with nothing on screen to say why. The column is kept
|
|
13
|
+
* instead. This build cannot read it, so keeping it costs only space, and the
|
|
14
|
+
* build that wrote it still can.
|
|
8
15
|
*/
|
|
9
16
|
export declare function openNativeDatabase(options: { path: string, models: Record<string, NativeModel> }): Database;
|
|
10
17
|
/**
|
package/dist/native.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Database}from"bun:sqlite";import{chmodSync,mkdirSync}from"node:fs";import{dirname,isAbsolute}from"node:path";import{buildMigrationPlan,configureOrm,generateDiffOperations,setConfig}from"@stacksjs/query-builder";import{apiBasePath,filterFillable,getWritableFields,mapWriteError,resolveIndexPageArgs}from"./auto-crud";import{MODEL_DEFINITION,toAttrs}from"./define-model";export function openNativeDatabase(options){if(options.path!==":memory:"&&!isAbsolute(options.path))throw Error("A native database requires an absolute Application Support path");if(options.path!==":memory:")mkdirSync(dirname(options.path),{recursive:!0,mode:448});const db=new Database(options.path,{create:!0});try{setConfig({verbose:!1});if(options.path!==":memory:")chmodSync(options.path,384);db.exec("PRAGMA foreign_keys = OFF; PRAGMA busy_timeout = 5000;");const models=Object.fromEntries(Object.entries(options.models).map(([name,model])=>[name,model[MODEL_DEFINITION]])),next=buildMigrationPlan(models,{dialect:"sqlite"});db.transaction(()=>{db.exec("CREATE TABLE IF NOT EXISTS _stacks_native_schema (id INTEGER PRIMARY KEY CHECK (id = 1), snapshot TEXT NOT NULL)");const saved=db.query("SELECT snapshot FROM _stacks_native_schema WHERE id = 1").get(),previous=saved?JSON.parse(saved.snapshot):void 0,diff=generateDiffOperations(previous,next,{externalTransaction:!0});if(diff.operations.some((operation)=>operation.destructive))throw Error("Native model upgrade contains destructive changes; provide an explicit data migration");for(const statement of diff.statements)if(statement.replace(/--[^\n]*/g,"").trim())db.exec(statement)
|
|
1
|
+
import{Database}from"bun:sqlite";import{chmodSync,mkdirSync}from"node:fs";import{dirname,isAbsolute}from"node:path";import{buildMigrationPlan,configureOrm,generateDiffOperations,setConfig}from"@stacksjs/query-builder";import{apiBasePath,filterFillable,getWritableFields,mapWriteError,resolveIndexPageArgs}from"./auto-crud";import{MODEL_DEFINITION,toAttrs}from"./define-model";const sameSql=(sql)=>(sql??"").replace(/\s+/g," ").trim();function retainingColumns(next,previous,retained){const wanted=new Map;for(const operation of retained){if(!operation.table||!operation.column)continue;const columns=wanted.get(operation.table)??new Set;columns.add(operation.column);wanted.set(operation.table,columns)}if(!wanted.size)return next;const plan=next,before=previous,tables=(plan.tables??[]).map((table)=>{const names=wanted.get(table.table??"");if(!names?.size)return table;const carried=((before?.tables??[]).find((entry)=>entry.table===table.table)?.columns??[]).filter((column)=>column.name&&names.has(column.name));return carried.length?{...table,columns:[...table.columns??[],...carried]}:table});return{...plan,tables}}export function openNativeDatabase(options){if(options.path!==":memory:"&&!isAbsolute(options.path))throw Error("A native database requires an absolute Application Support path");if(options.path!==":memory:")mkdirSync(dirname(options.path),{recursive:!0,mode:448});const db=new Database(options.path,{create:!0});try{setConfig({verbose:!1});if(options.path!==":memory:")chmodSync(options.path,384);db.exec("PRAGMA foreign_keys = OFF; PRAGMA busy_timeout = 5000;");const models=Object.fromEntries(Object.entries(options.models).map(([name,model])=>[name,model[MODEL_DEFINITION]])),next=buildMigrationPlan(models,{dialect:"sqlite"});db.transaction(()=>{db.exec("CREATE TABLE IF NOT EXISTS _stacks_native_schema (id INTEGER PRIMARY KEY CHECK (id = 1), snapshot TEXT NOT NULL)");const saved=db.query("SELECT snapshot FROM _stacks_native_schema WHERE id = 1").get(),previous=saved?JSON.parse(saved.snapshot):void 0,diff=generateDiffOperations(previous,next,{externalTransaction:!0}),retained=diff.operations.filter((operation)=>operation.destructive&&operation.kind==="drop_column");if(diff.operations.some((operation)=>operation.destructive&&operation.kind!=="drop_column"))throw Error("Native model upgrade contains destructive changes; provide an explicit data migration");const kept=new Set(diff.operations.filter((operation)=>!retained.includes(operation)).map((operation)=>sameSql(operation.sql))),skip=new Set(retained.map((operation)=>sameSql(operation.sql)).filter((sql)=>sql&&!kept.has(sql)));for(const statement of diff.statements){if(skip.has(sameSql(statement)))continue;if(statement.replace(/--[^\n]*/g,"").trim())db.exec(statement)}if(db.query("PRAGMA foreign_key_check").all().length)throw Error("Native model upgrade violates foreign-key constraints");db.query("INSERT INTO _stacks_native_schema (id, snapshot) VALUES (1, ?) ON CONFLICT (id) DO UPDATE SET snapshot = excluded.snapshot").run(JSON.stringify(retainingColumns(next,previous,retained)))}).immediate();db.exec("PRAGMA foreign_keys = ON;");configureOrm({database:db});return db}catch(error){db.close();throw error}}export function createNativeModelApi(options){const models=Object.entries(options.models).flatMap(([name,model])=>{const definition=model[MODEL_DEFINITION],api=definition.traits?.useApi;if(!api||typeof api!=="object")return[];if(!Array.isArray(api.middleware)||api.middleware.length)throw Error(`${name}: native CRUD requires explicit middleware: [] and transport authorization`);if(!api.uri)throw Error(`${name}: native CRUD requires a useApi uri`);return[{name,model,definition,api,base:apiBasePath(api.uri,api.prefix)}]}),json=(data,status=200)=>Response.json(data,{status,headers:{"cache-control":"private, no-store"}});return async(request)=>{const url=new URL(request.url);for(const entry of models){const{name,model,definition,api,base}=entry,suffix=url.pathname.slice(base.length);if(!url.pathname.startsWith(base)||suffix&&!/^\/\d+$/.test(suffix))continue;const id=suffix?Number(suffix.slice(1)):void 0,ability=request.method==="GET"?id===void 0?"index":"show":request.method==="POST"&&id===void 0?"store":["PATCH","PUT"].includes(request.method)&&id!==void 0?"update":request.method==="DELETE"&&id!==void 0?"destroy":void 0;if(!ability||!api.routes?.includes(ability))return json({error:"Method not allowed"},405);if(!await options.authorize(request,name,ability))return json({error:"Forbidden"},403);try{if(ability==="index"){const{perPage,offset}=resolveIndexPageArgs(url.searchParams);return json({data:toAttrs(await model.limit(perPage).offset(offset).get())})}if(id!==void 0&&!await model.find(id))return json({error:"Not found"},404);if(ability==="show")return json({data:toAttrs(await model.find(id))});if(ability==="destroy"){await model.destroy(id);return new Response(null,{status:204})}let body;try{body=await request.json()}catch{return json({error:"Invalid JSON"},400)}if(!body||typeof body!=="object"||Array.isArray(body))return json({error:"Expected an object"},422);const fields=getWritableFields({attributes:definition.attributes,belongsTo:Object.values(definition.belongsTo??{})}),data=filterFillable(body,fields),row=ability==="store"?await model.create(data):await model.update(id,data);return json({data:toAttrs(row)},ability==="store"?201:200)}catch(error){const result=mapWriteError(error,name,ability==="store"?"create":"update"),body={...result.body};delete body.detail;return json(body,result.status)}}return null}}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/orm",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.483",
|
|
6
6
|
"description": "The Stacks ORM integration",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -63,31 +63,31 @@
|
|
|
63
63
|
"prepublishOnly": "bun run build"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@stacksjs/cache": "0.75.
|
|
67
|
-
"@stacksjs/cli": "0.75.
|
|
68
|
-
"@stacksjs/config": "0.75.
|
|
69
|
-
"@stacksjs/database": "0.75.
|
|
70
|
-
"@stacksjs/env": "0.75.
|
|
71
|
-
"@stacksjs/error-handling": "0.75.
|
|
72
|
-
"@stacksjs/events": "0.75.
|
|
73
|
-
"@stacksjs/logging": "0.75.
|
|
74
|
-
"@stacksjs/model-meta": "0.75.
|
|
75
|
-
"@stacksjs/pagination": "0.75.
|
|
76
|
-
"@stacksjs/path": "0.75.
|
|
77
|
-
"@stacksjs/query-builder": "0.75.
|
|
78
|
-
"@stacksjs/realtime": "0.75.
|
|
79
|
-
"@stacksjs/router": "0.75.
|
|
80
|
-
"@stacksjs/storage": "0.75.
|
|
81
|
-
"@stacksjs/strings": "0.75.
|
|
66
|
+
"@stacksjs/cache": "0.75.483",
|
|
67
|
+
"@stacksjs/cli": "0.75.483",
|
|
68
|
+
"@stacksjs/config": "0.75.483",
|
|
69
|
+
"@stacksjs/database": "0.75.483",
|
|
70
|
+
"@stacksjs/env": "0.75.483",
|
|
71
|
+
"@stacksjs/error-handling": "0.75.483",
|
|
72
|
+
"@stacksjs/events": "0.75.483",
|
|
73
|
+
"@stacksjs/logging": "0.75.483",
|
|
74
|
+
"@stacksjs/model-meta": "0.75.483",
|
|
75
|
+
"@stacksjs/pagination": "0.75.483",
|
|
76
|
+
"@stacksjs/path": "0.75.483",
|
|
77
|
+
"@stacksjs/query-builder": "0.75.483",
|
|
78
|
+
"@stacksjs/realtime": "0.75.483",
|
|
79
|
+
"@stacksjs/router": "0.75.483",
|
|
80
|
+
"@stacksjs/storage": "0.75.483",
|
|
81
|
+
"@stacksjs/strings": "0.75.483",
|
|
82
82
|
"@stacksjs/ts-validation": "^0.5.7",
|
|
83
|
-
"@stacksjs/validation": "0.75.
|
|
83
|
+
"@stacksjs/validation": "0.75.483",
|
|
84
84
|
"bun-query-builder": "^0.3.38"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
|
-
"@stacksjs/auth": "0.75.
|
|
88
|
-
"@stacksjs/payments": "0.75.
|
|
89
|
-
"@stacksjs/queue": "0.75.
|
|
90
|
-
"@stacksjs/search-engine": "0.75.
|
|
87
|
+
"@stacksjs/auth": "0.75.483",
|
|
88
|
+
"@stacksjs/payments": "0.75.483",
|
|
89
|
+
"@stacksjs/queue": "0.75.483",
|
|
90
|
+
"@stacksjs/search-engine": "0.75.483",
|
|
91
91
|
"stripe": "23.0.0"
|
|
92
92
|
},
|
|
93
93
|
"peerDependenciesMeta": {
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
110
|
"devDependencies": {
|
|
111
|
-
"@stacksjs/types": "0.75.
|
|
111
|
+
"@stacksjs/types": "0.75.483",
|
|
112
112
|
"better-dx": "^0.2.24",
|
|
113
113
|
"stripe": "23.0.0"
|
|
114
114
|
}
|