@stacksjs/orm 0.75.320 → 0.75.322
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 +29 -0
- package/dist/native.js +1 -0
- package/package.json +23 -23
package/dist/native.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Database } from 'bun:sqlite';
|
|
2
|
+
import { MODEL_DEFINITION } from './define-model';
|
|
3
|
+
import type { StacksModelDefinition } from './define-model';
|
|
4
|
+
/**
|
|
5
|
+
* Own one SQLite database in a native backend process. Schema comes from the
|
|
6
|
+
* same migration planner as Buddy; additive upgrades and the snapshot commit
|
|
7
|
+
* atomically. Destructive upgrades require an explicit application migration.
|
|
8
|
+
*/
|
|
9
|
+
export declare function openNativeDatabase(options: { path: string, models: Record<string, NativeModel> }): Database;
|
|
10
|
+
/**
|
|
11
|
+
* Model-declared CRUD for an authenticated native transport. Authorization is
|
|
12
|
+
* mandatory and evaluated before queries; web middleware is never bypassed.
|
|
13
|
+
* Use only behind stx's authenticated native backend, or supply an equivalent
|
|
14
|
+
* per-request authorization implementation. Native models explicitly declare
|
|
15
|
+
* middleware: [] because their session is enforced by this transport.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createNativeModelApi(options: {
|
|
18
|
+
models: Record<string, NativeModel>
|
|
19
|
+
authorize: (request: Request, model: string, ability: string) => boolean | Promise<boolean>
|
|
20
|
+
}): (request: Request) => Promise<Response | null>;
|
|
21
|
+
/** Explicit model registry: the compiled app never discovers source files at runtime. */
|
|
22
|
+
export declare interface NativeModel {
|
|
23
|
+
[MODEL_DEFINITION]: StacksModelDefinition
|
|
24
|
+
find: (id: number) => Promise<unknown>
|
|
25
|
+
create: (data: Record<string, unknown>) => Promise<unknown>
|
|
26
|
+
update: (id: number, data: Record<string, unknown>) => Promise<unknown>
|
|
27
|
+
destroy: (id: number) => Promise<unknown>
|
|
28
|
+
limit: (count: number) => { offset: (offset: number) => { get: () => Promise<unknown> } }
|
|
29
|
+
}
|
package/dist/native.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Database}from"bun:sqlite";import{chmodSync,mkdirSync}from"node:fs";import{dirname,isAbsolute}from"node:path";import{buildMigrationPlan,configureOrm,generateDiffOperations}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{if(options.path!==":memory:")chmodSync(options.path,384);db.exec("PRAGMA foreign_keys = ON; 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);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);db.query("INSERT INTO _stacks_native_schema (id, snapshot) VALUES (1, ?) ON CONFLICT (id) DO UPDATE SET snapshot = excluded.snapshot").run(JSON.stringify(next))}).immediate();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.322",
|
|
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.322",
|
|
67
|
+
"@stacksjs/cli": "0.75.322",
|
|
68
|
+
"@stacksjs/config": "0.75.322",
|
|
69
|
+
"@stacksjs/database": "0.75.322",
|
|
70
|
+
"@stacksjs/env": "0.75.322",
|
|
71
|
+
"@stacksjs/error-handling": "0.75.322",
|
|
72
|
+
"@stacksjs/events": "0.75.322",
|
|
73
|
+
"@stacksjs/logging": "0.75.322",
|
|
74
|
+
"@stacksjs/model-meta": "0.75.322",
|
|
75
|
+
"@stacksjs/pagination": "0.75.322",
|
|
76
|
+
"@stacksjs/path": "0.75.322",
|
|
77
|
+
"@stacksjs/query-builder": "0.75.322",
|
|
78
|
+
"@stacksjs/realtime": "0.75.322",
|
|
79
|
+
"@stacksjs/router": "0.75.322",
|
|
80
|
+
"@stacksjs/storage": "0.75.322",
|
|
81
|
+
"@stacksjs/strings": "0.75.322",
|
|
82
82
|
"@stacksjs/ts-validation": "^0.5.6",
|
|
83
|
-
"@stacksjs/validation": "0.75.
|
|
83
|
+
"@stacksjs/validation": "0.75.322",
|
|
84
84
|
"bun-query-builder": "^0.3.22"
|
|
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.322",
|
|
88
|
+
"@stacksjs/payments": "0.75.322",
|
|
89
|
+
"@stacksjs/queue": "0.75.322",
|
|
90
|
+
"@stacksjs/search-engine": "0.75.322",
|
|
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.322",
|
|
112
112
|
"better-dx": "^0.2.24",
|
|
113
113
|
"stripe": "23.0.0"
|
|
114
114
|
}
|