@mushinjs/plugin-database 0.2.0-next.20261112165327 → 0.2.0-next.20261113103537

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Setobox
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Setobox
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { mkdir, stat } from 'node:fs/promises'\r\nimport { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'\r\nimport { pathToFileURL } from 'node:url'\r\nimport { type Context, field, MushinError, z } from '@mushinjs/core'\r\nimport MemoryDriver from '@minatojs/driver-memory'\r\nimport SQLiteDriver from '@minatojs/driver-sqlite'\r\nimport { $, Database, type Field, type Model } from 'minato'\r\n\r\nexport type { Database, Driver, Field, Model, Query, Selection, Tables, Types } from 'minato'\r\n\r\nexport const name = 'database'\r\nexport const inject = ['actions']\r\n\r\nexport const Config = z.object({\r\n driver: z.enum(['sqlite', 'memory']).default('sqlite').meta(field('Driver', 'sqlite keeps everything in one file; memory loses the data when Mushin.js stops (for trying things out)', {\r\n 'zh-CN': { title: '驱动', description: 'sqlite 把数据存在一个文件里;memory 在 Mushin.js 停止时丢失数据(用于试用)' },\r\n })),\r\n path: z.string().trim().min(1).default('data/mushin.db').meta(field('Database file', 'For SQLite; relative to the instance directory', {\r\n 'zh-CN': { title: '数据库文件', description: 'SQLite 使用;相对于实例目录' },\r\n })),\r\n})\r\n\r\nexport type Config = z.infer<typeof Config>\r\n\r\n// The drivers inject a `logger` service that Cordis 4 keeps as a built-in property instead, so\r\n// they would wait for it forever. They only call `ctx.logger?.info()` and the like, which the\r\n// built-in logger answers, so only the model is injected here.\r\nclass SQLite extends (SQLiteDriver as any) {\r\n static inject = ['model']\r\n}\r\n\r\nclass Memory extends (MemoryDriver as any) {\r\n static inject = ['model']\r\n}\r\n\r\nconst PAGE_LIMIT = 500\r\nconst EXPORT_LIMIT = 100_000\r\n\r\nexport interface ColumnInfo {\r\n name: string\r\n type: string\r\n primary: boolean\r\n nullable: boolean\r\n}\r\n\r\nexport interface TableInfo {\r\n name: string\r\n /** The part of the name before the first underscore, which by convention names the plugin. */\r\n group: string\r\n primary: string[]\r\n columns: ColumnInfo[]\r\n count: number\r\n}\r\n\r\n/** Rows as JSON can carry them: dates as ISO text, binary as a note, big integers as text. */\r\nfunction plain(value: unknown): unknown {\r\n if (value instanceof Date) return value.toISOString()\r\n if (typeof value === 'bigint') return value.toString()\r\n if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) return `<binary ${(value as ArrayBuffer).byteLength} bytes>`\r\n if (Array.isArray(value)) return value.map(plain)\r\n if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, plain(item)]))\r\n return value\r\n}\r\n\r\nfunction csvCell(value: unknown) {\r\n if (value === undefined || value === null) return ''\r\n const text = typeof value === 'object' ? JSON.stringify(value) : String(value)\r\n return /[\",\\r\\n]/.test(text) ? `\"${text.replace(/\"/g, '\"\"')}\"` : text\r\n}\r\n\r\nexport function toCsv(columns: string[], rows: Record<string, unknown>[]) {\r\n return [columns.map(csvCell).join(','), ...rows.map((row) => columns.map((column) => csvCell(row[column])).join(','))].join('\\r\\n') + '\\r\\n'\r\n}\r\n\r\nconst numeric = new Set(['integer', 'unsigned', 'float', 'double', 'decimal', 'bigint'])\r\nconst textual = new Set(['char', 'string', 'text'])\r\n\r\nconst escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\r\n\r\nconst Filter = z.record(z.string(), z.string()).optional().describe('Column → text. Text columns match when they contain the text; numbers and booleans must be equal.')\r\n\r\n/**\r\n * The database service (`ctx.database`, from minato) and the actions behind the Database page.\r\n * Plugins declare tables with `ctx.model.extend()` in a context that injects both `model` and\r\n * `database`; by convention a table's name starts with the plugin's name and an underscore.\r\n */\r\nexport function apply(ctx: Context, config: Config) {\r\n const baseDir = ctx.baseDir ?? process.cwd()\r\n const file = config.driver === 'sqlite' ? (isAbsolute(config.path) ? config.path : resolve(baseDir, config.path)) : undefined\r\n\r\n ctx.plugin(Database)\r\n if (file) {\r\n ctx.inject(['model'], async (ctx) => {\r\n await mkdir(dirname(file), { recursive: true })\r\n // The driver resolves the path against `ctx.baseUrl`; a file URL resolves to itself.\r\n ctx.plugin(SQLite as any, { path: pathToFileURL(file).href })\r\n })\r\n } else {\r\n ctx.plugin(Memory as any, {})\r\n }\r\n\r\n ctx.inject(['model', 'database'], (ctx) => {\r\n const tableOf = (name: string): Model => {\r\n const model = ctx.model.tables[name]\r\n if (!model) throw new MushinError('not_found', `No table named \"${name}\"`)\r\n return model\r\n }\r\n const primaryOf = (model: Model) => (Array.isArray(model.primary) ? model.primary : [model.primary]) as string[]\r\n const columnsOf = (model: Model): ColumnInfo[] => {\r\n const primary = primaryOf(model)\r\n return Object.entries(model.fields as Record<string, Field | undefined>)\r\n .filter(([, item]) => item && !item.deprecated && !item.relation && !item.expr)\r\n .map(([name, item]) => ({ name, type: String(item!.deftype ?? item!.type?.type ?? ''), primary: primary.includes(name), nullable: !!item!.nullable }))\r\n }\r\n const queryOf = (model: Model, filter: Record<string, string> | undefined) => {\r\n const query: Record<string, unknown> = {}\r\n const columns = new Map(columnsOf(model).map((column) => [column.name, column]))\r\n for (const [name, text] of Object.entries(filter ?? {})) {\r\n if (text === '') continue\r\n const column = columns.get(name)\r\n if (!column) throw new MushinError('invalid_input', `Table \"${model.name}\" has no column \"${name}\"`)\r\n if (numeric.has(column.type)) {\r\n const value = Number(text)\r\n if (!Number.isFinite(value)) throw new MushinError('invalid_input', `\"${text}\" is not a number`)\r\n query[name] = value\r\n } else if (column.type === 'boolean') {\r\n query[name] = text === 'true' || text === '1'\r\n } else if (textual.has(column.type)) {\r\n query[name] = { $regex: new RegExp(escapeRegExp(text), 'i') }\r\n } else {\r\n throw new MushinError('invalid_input', `Column \"${name}\" (${column.type}) cannot be filtered`)\r\n }\r\n }\r\n return query\r\n }\r\n const keyQuery = (model: Model, key: Record<string, unknown>) => {\r\n const query: Record<string, unknown> = {}\r\n for (const name of primaryOf(model)) {\r\n if (!(name in key)) throw new MushinError('invalid_input', `The key needs \"${name}\"`)\r\n query[name] = key[name]\r\n }\r\n return query\r\n }\r\n\r\n const status = async () => {\r\n const size = file ? (await stat(file).catch(() => undefined))?.size ?? 0 : undefined\r\n return {\r\n driver: config.driver,\r\n ...(file ? { path: relative(baseDir, file).split(sep).join('/'), size } : {}),\r\n tables: Object.keys(ctx.model.tables).length,\r\n }\r\n }\r\n\r\n const countRows = async (table: string, model: Model, query: Record<string, unknown>) => {\r\n const key = primaryOf(model)[0] ?? columnsOf(model)[0]?.name\r\n return Number(await ctx.database.select(table as any, query as any).execute((row: any) => $.count(row[key]))) || 0\r\n }\r\n\r\n const tables = async (): Promise<TableInfo[]> => {\r\n const list: TableInfo[] = []\r\n for (const [name, model] of Object.entries(ctx.model.tables)) {\r\n const count = await countRows(name, model, {})\r\n list.push({ name, group: name.split('_')[0], primary: primaryOf(model), columns: columnsOf(model), count: Number(count) || 0 })\r\n }\r\n return list.sort((a, b) => a.name.localeCompare(b.name))\r\n }\r\n\r\n ctx.actions.define({\r\n name: 'database.status',\r\n description: 'Which database driver runs, the SQLite file and its size, and how many tables exist.',\r\n risk: 'read',\r\n input: z.object({}),\r\n handler: status,\r\n })\r\n\r\n ctx.actions.define({\r\n name: 'database.tables',\r\n description: 'Every table in the database with its columns, primary key and row count. Tables are grouped by the plugin name before the first underscore, e.g. flows_var.',\r\n risk: 'read',\r\n input: z.object({}),\r\n handler: tables,\r\n })\r\n\r\n ctx.actions.define({\r\n name: 'database.rows',\r\n description: 'A page of rows from one table, read-only: optional sort and filters. Returns the rows and the total that match.',\r\n risk: 'read',\r\n input: z.object({\r\n table: z.string(),\r\n offset: z.number().int().min(0).default(0),\r\n limit: z.number().int().min(1).max(PAGE_LIMIT).default(50),\r\n sort: z.object({ column: z.string(), order: z.enum(['asc', 'desc']).default('asc') }).optional(),\r\n filter: Filter,\r\n }),\r\n handler: async ({ table, offset, limit, sort, filter }) => {\r\n const model = tableOf(table)\r\n const query = queryOf(model, filter)\r\n let selection = ctx.database.select(table as any, query as any)\r\n if (sort) {\r\n if (!columnsOf(model).some((column) => column.name === sort.column)) throw new MushinError('invalid_input', `Table \"${table}\" has no column \"${sort.column}\"`)\r\n selection = selection.orderBy(sort.column as any, sort.order)\r\n }\r\n const [rows, total] = await Promise.all([\r\n selection.offset(offset).limit(limit).execute(),\r\n countRows(table, model, query),\r\n ])\r\n return { rows: rows.map((row) => plain(row)), total }\r\n },\r\n })\r\n\r\n ctx.actions.define({\r\n name: 'database.update',\r\n description: 'Changes columns of one row, found by its primary key. Writes to plugin data directly, so the change takes effect without any checks the plugin would make.',\r\n risk: 'dangerous',\r\n input: z.object({ table: z.string(), key: z.record(z.string(), z.unknown()), data: z.record(z.string(), z.unknown()) }),\r\n handler: async ({ table, key, data }) => {\r\n const model = tableOf(table)\r\n const columns = new Set(columnsOf(model).map((column) => column.name))\r\n for (const name of Object.keys(data)) {\r\n if (!columns.has(name)) throw new MushinError('invalid_input', `Table \"${table}\" has no column \"${name}\"`)\r\n if (primaryOf(model).includes(name)) throw new MushinError('invalid_input', `\"${name}\" is part of the primary key and cannot be changed`)\r\n }\r\n const result = await ctx.database.set(table as any, keyQuery(model, key) as any, data as any)\r\n if (!result.matched) throw new MushinError('not_found', 'No row has that key')\r\n return { updated: result.modified ?? result.matched }\r\n },\r\n })\r\n\r\n ctx.actions.define({\r\n name: 'database.remove',\r\n description: 'Deletes rows from a table by their primary keys. Cannot be undone; make a backup first if unsure.',\r\n risk: 'dangerous',\r\n input: z.object({ table: z.string(), keys: z.array(z.record(z.string(), z.unknown())).min(1).max(PAGE_LIMIT) }),\r\n handler: async ({ table, keys }) => {\r\n const model = tableOf(table)\r\n let removed = 0\r\n for (const key of keys) removed += (await ctx.database.remove(table as any, keyQuery(model, key) as any)).removed ?? 0\r\n return { removed }\r\n },\r\n })\r\n\r\n ctx.actions.define({\r\n name: 'database.export',\r\n description: 'The rows of a table (matching optional filters) as CSV or JSON text, for downloading.',\r\n risk: 'read',\r\n input: z.object({ table: z.string(), format: z.enum(['csv', 'json']).default('csv'), filter: Filter }),\r\n handler: async ({ table, format, filter }) => {\r\n const model = tableOf(table)\r\n const rows = (await ctx.database.select(table as any, queryOf(model, filter) as any).limit(EXPORT_LIMIT).execute()).map((row) => plain(row) as Record<string, unknown>)\r\n const columns = columnsOf(model).map((column) => column.name)\r\n const content = format === 'csv' ? '' + toCsv(columns, rows) : JSON.stringify(rows, null, 2)\r\n return { filename: `${table}.${format}`, content, rows: rows.length, truncated: rows.length >= EXPORT_LIMIT }\r\n },\r\n })\r\n\r\n ctx.actions.define({\r\n name: 'database.backup',\r\n description: 'Copies the SQLite database to data/backups/ under a dated name while Mushin.js keeps running. Returns the backup\\'s path.',\r\n risk: 'write',\r\n input: z.object({}),\r\n handler: async () => {\r\n const driver = (ctx.database as any)._driver\r\n if (!file || !driver?.db) throw new MushinError('unavailable', 'Backups need the SQLite driver')\r\n const stamp = new Date().toISOString().replace(/[-:]/g, '').replace('T', '-').slice(0, 15)\r\n const target = join(dirname(file), 'backups', `mushin-${stamp}.db`)\r\n await mkdir(dirname(target), { recursive: true })\r\n driver.db.exec(`VACUUM INTO '${target.replace(/'/g, \"''\")}'`)\r\n const size = (await stat(target)).size\r\n return { path: relative(baseDir, target).split(sep).join('/'), size }\r\n },\r\n })\r\n })\r\n}\r\n"],"mappings":";;;;;;;;AAUA,MAAa,OAAO;AACpB,MAAa,SAAS,CAAC,SAAS;AAEhC,MAAa,SAAS,EAAE,OAAO;CAC7B,QAAQ,EAAE,KAAK,CAAC,UAAU,QAAQ,CAAC,CAAC,CAAC,QAAQ,QAAQ,CAAC,CAAC,KAAK,MAAM,UAAU,2GAA2G,EACrL,SAAS;EAAE,OAAO;EAAM,aAAa;CAAqD,EAC5F,CAAC,CAAC;CACF,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,gBAAgB,CAAC,CAAC,KAAK,MAAM,iBAAiB,kDAAkD,EACrI,SAAS;EAAE,OAAO;EAAS,aAAa;CAAoB,EAC9D,CAAC,CAAC;AACJ,CAAC;AAOD,IAAM,SAAN,cAAsB,aAAqB;CACzC,OAAO,SAAS,CAAC,OAAO;AAC1B;AAEA,IAAM,SAAN,cAAsB,aAAqB;CACzC,OAAO,SAAS,CAAC,OAAO;AAC1B;AAEA,MAAM,aAAa;AACnB,MAAM,eAAe;;AAmBrB,SAAS,MAAM,OAAyB;CACtC,IAAI,iBAAiB,MAAM,OAAO,MAAM,YAAY;CACpD,IAAI,OAAO,UAAU,UAAU,OAAO,MAAM,SAAS;CACrD,IAAI,iBAAiB,eAAe,YAAY,OAAO,KAAK,GAAG,OAAO,WAAY,MAAsB,WAAW;CACnH,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,IAAI,KAAK;CAChD,IAAI,SAAS,OAAO,UAAU,UAAU,OAAO,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,UAAU,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC;CAChI,OAAO;AACT;AAEA,SAAS,QAAQ,OAAgB;CAC/B,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM,OAAO;CAClD,MAAM,OAAO,OAAO,UAAU,WAAW,KAAK,UAAU,KAAK,IAAI,OAAO,KAAK;CAC7E,OAAO,WAAW,KAAK,IAAI,IAAI,IAAI,KAAK,QAAQ,MAAM,MAAI,EAAE,KAAK;AACnE;AAEA,SAAgB,MAAM,SAAmB,MAAiC;CACxE,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,CAAC,KAAK,GAAG,GAAG,GAAG,KAAK,KAAK,QAAQ,QAAQ,KAAK,WAAW,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI;AACxI;AAEA,MAAM,0BAAU,IAAI,IAAI;CAAC;CAAW;CAAY;CAAS;CAAU;CAAW;AAAQ,CAAC;AACvF,MAAM,0BAAU,IAAI,IAAI;CAAC;CAAQ;CAAU;AAAM,CAAC;AAElD,MAAM,gBAAgB,SAAiB,KAAK,QAAQ,uBAAuB,MAAM;AAEjF,MAAM,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,mGAAmG;;;;;;AAOvK,SAAgB,MAAM,KAAc,QAAgB;CAClD,MAAM,UAAU,IAAI,WAAW,QAAQ,IAAI;CAC3C,MAAM,OAAO,OAAO,WAAW,WAAY,WAAW,OAAO,IAAI,IAAI,OAAO,OAAO,QAAQ,SAAS,OAAO,IAAI,IAAK,KAAA;CAEpH,IAAI,OAAO,QAAQ;CACnB,IAAI,MACF,IAAI,OAAO,CAAC,OAAO,GAAG,OAAO,QAAQ;EACnC,MAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;EAE9C,IAAI,OAAO,QAAe,EAAE,MAAM,cAAc,IAAI,CAAC,CAAC,KAAK,CAAC;CAC9D,CAAC;MAED,IAAI,OAAO,QAAe,CAAC,CAAC;CAG9B,IAAI,OAAO,CAAC,SAAS,UAAU,IAAI,QAAQ;EACzC,MAAM,WAAW,SAAwB;GACvC,MAAM,QAAQ,IAAI,MAAM,OAAO;GAC/B,IAAI,CAAC,OAAO,MAAM,IAAI,YAAY,aAAa,mBAAmB,KAAK,EAAE;GACzE,OAAO;EACT;EACA,MAAM,aAAa,UAAkB,MAAM,QAAQ,MAAM,OAAO,IAAI,MAAM,UAAU,CAAC,MAAM,OAAO;EAClG,MAAM,aAAa,UAA+B;GAChD,MAAM,UAAU,UAAU,KAAK;GAC/B,OAAO,OAAO,QAAQ,MAAM,MAA2C,CAAC,CACrE,QAAQ,GAAG,UAAU,QAAQ,CAAC,KAAK,cAAc,CAAC,KAAK,YAAY,CAAC,KAAK,IAAI,CAAC,CAC9E,KAAK,CAAC,MAAM,WAAW;IAAE;IAAM,MAAM,OAAO,KAAM,WAAW,KAAM,MAAM,QAAQ,EAAE;IAAG,SAAS,QAAQ,SAAS,IAAI;IAAG,UAAU,CAAC,CAAC,KAAM;GAAS,EAAE;EACzJ;EACA,MAAM,WAAW,OAAc,WAA+C;GAC5E,MAAM,QAAiC,CAAC;GACxC,MAAM,UAAU,IAAI,IAAI,UAAU,KAAK,CAAC,CAAC,KAAK,WAAW,CAAC,OAAO,MAAM,MAAM,CAAC,CAAC;GAC/E,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,UAAU,CAAC,CAAC,GAAG;IACvD,IAAI,SAAS,IAAI;IACjB,MAAM,SAAS,QAAQ,IAAI,IAAI;IAC/B,IAAI,CAAC,QAAQ,MAAM,IAAI,YAAY,iBAAiB,UAAU,MAAM,KAAK,mBAAmB,KAAK,EAAE;IACnG,IAAI,QAAQ,IAAI,OAAO,IAAI,GAAG;KAC5B,MAAM,QAAQ,OAAO,IAAI;KACzB,IAAI,CAAC,OAAO,SAAS,KAAK,GAAG,MAAM,IAAI,YAAY,iBAAiB,IAAI,KAAK,kBAAkB;KAC/F,MAAM,QAAQ;IAChB,OAAO,IAAI,OAAO,SAAS,WACzB,MAAM,QAAQ,SAAS,UAAU,SAAS;SACrC,IAAI,QAAQ,IAAI,OAAO,IAAI,GAChC,MAAM,QAAQ,EAAE,QAAQ,IAAI,OAAO,aAAa,IAAI,GAAG,GAAG,EAAE;SAE5D,MAAM,IAAI,YAAY,iBAAiB,WAAW,KAAK,KAAK,OAAO,KAAK,qBAAqB;GAEjG;GACA,OAAO;EACT;EACA,MAAM,YAAY,OAAc,QAAiC;GAC/D,MAAM,QAAiC,CAAC;GACxC,KAAK,MAAM,QAAQ,UAAU,KAAK,GAAG;IACnC,IAAI,EAAE,QAAQ,MAAM,MAAM,IAAI,YAAY,iBAAiB,kBAAkB,KAAK,EAAE;IACpF,MAAM,QAAQ,IAAI;GACpB;GACA,OAAO;EACT;EAEA,MAAM,SAAS,YAAY;GACzB,MAAM,OAAO,QAAQ,MAAM,KAAK,IAAI,CAAC,CAAC,YAAY,KAAA,CAAS,EAAA,EAAI,QAAQ,IAAI,KAAA;GAC3E,OAAO;IACL,QAAQ,OAAO;IACf,GAAI,OAAO;KAAE,MAAM,SAAS,SAAS,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG;KAAG;IAAK,IAAI,CAAC;IAC3E,QAAQ,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC,CAAC;GACxC;EACF;EAEA,MAAM,YAAY,OAAO,OAAe,OAAc,UAAmC;GACvF,MAAM,MAAM,UAAU,KAAK,CAAC,CAAC,MAAM,UAAU,KAAK,CAAC,CAAC,EAAE,EAAE;GACxD,OAAO,OAAO,MAAM,IAAI,SAAS,OAAO,OAAc,KAAY,CAAC,CAAC,SAAS,QAAa,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;EACnH;EAEA,MAAM,SAAS,YAAkC;GAC/C,MAAM,OAAoB,CAAC;GAC3B,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,IAAI,MAAM,MAAM,GAAG;IAC5D,MAAM,QAAQ,MAAM,UAAU,MAAM,OAAO,CAAC,CAAC;IAC7C,KAAK,KAAK;KAAE;KAAM,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC;KAAI,SAAS,UAAU,KAAK;KAAG,SAAS,UAAU,KAAK;KAAG,OAAO,OAAO,KAAK,KAAK;IAAE,CAAC;GAChI;GACA,OAAO,KAAK,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;EACzD;EAEA,IAAI,QAAQ,OAAO;GACjB,MAAM;GACN,aAAa;GACb,MAAM;GACN,OAAO,EAAE,OAAO,CAAC,CAAC;GAClB,SAAS;EACX,CAAC;EAED,IAAI,QAAQ,OAAO;GACjB,MAAM;GACN,aAAa;GACb,MAAM;GACN,OAAO,EAAE,OAAO,CAAC,CAAC;GAClB,SAAS;EACX,CAAC;EAED,IAAI,QAAQ,OAAO;GACjB,MAAM;GACN,aAAa;GACb,MAAM;GACN,OAAO,EAAE,OAAO;IACd,OAAO,EAAE,OAAO;IAChB,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;IACzC,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,QAAQ,EAAE;IACzD,MAAM,EAAE,OAAO;KAAE,QAAQ,EAAE,OAAO;KAAG,OAAO,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,QAAQ,KAAK;IAAE,CAAC,CAAC,CAAC,SAAS;IAC/F,QAAQ;GACV,CAAC;GACD,SAAS,OAAO,EAAE,OAAO,QAAQ,OAAO,MAAM,aAAa;IACzD,MAAM,QAAQ,QAAQ,KAAK;IAC3B,MAAM,QAAQ,QAAQ,OAAO,MAAM;IACnC,IAAI,YAAY,IAAI,SAAS,OAAO,OAAc,KAAY;IAC9D,IAAI,MAAM;KACR,IAAI,CAAC,UAAU,KAAK,CAAC,CAAC,MAAM,WAAW,OAAO,SAAS,KAAK,MAAM,GAAG,MAAM,IAAI,YAAY,iBAAiB,UAAU,MAAM,mBAAmB,KAAK,OAAO,EAAE;KAC7J,YAAY,UAAU,QAAQ,KAAK,QAAe,KAAK,KAAK;IAC9D;IACA,MAAM,CAAC,MAAM,SAAS,MAAM,QAAQ,IAAI,CACtC,UAAU,OAAO,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,QAAQ,GAC9C,UAAU,OAAO,OAAO,KAAK,CAC/B,CAAC;IACD,OAAO;KAAE,MAAM,KAAK,KAAK,QAAQ,MAAM,GAAG,CAAC;KAAG;IAAM;GACtD;EACF,CAAC;EAED,IAAI,QAAQ,OAAO;GACjB,MAAM;GACN,aAAa;GACb,MAAM;GACN,OAAO,EAAE,OAAO;IAAE,OAAO,EAAE,OAAO;IAAG,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;IAAG,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;GAAE,CAAC;GACtH,SAAS,OAAO,EAAE,OAAO,KAAK,WAAW;IACvC,MAAM,QAAQ,QAAQ,KAAK;IAC3B,MAAM,UAAU,IAAI,IAAI,UAAU,KAAK,CAAC,CAAC,KAAK,WAAW,OAAO,IAAI,CAAC;IACrE,KAAK,MAAM,QAAQ,OAAO,KAAK,IAAI,GAAG;KACpC,IAAI,CAAC,QAAQ,IAAI,IAAI,GAAG,MAAM,IAAI,YAAY,iBAAiB,UAAU,MAAM,mBAAmB,KAAK,EAAE;KACzG,IAAI,UAAU,KAAK,CAAC,CAAC,SAAS,IAAI,GAAG,MAAM,IAAI,YAAY,iBAAiB,IAAI,KAAK,mDAAmD;IAC1I;IACA,MAAM,SAAS,MAAM,IAAI,SAAS,IAAI,OAAc,SAAS,OAAO,GAAG,GAAU,IAAW;IAC5F,IAAI,CAAC,OAAO,SAAS,MAAM,IAAI,YAAY,aAAa,qBAAqB;IAC7E,OAAO,EAAE,SAAS,OAAO,YAAY,OAAO,QAAQ;GACtD;EACF,CAAC;EAED,IAAI,QAAQ,OAAO;GACjB,MAAM;GACN,aAAa;GACb,MAAM;GACN,OAAO,EAAE,OAAO;IAAE,OAAO,EAAE,OAAO;IAAG,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,UAAU;GAAE,CAAC;GAC9G,SAAS,OAAO,EAAE,OAAO,WAAW;IAClC,MAAM,QAAQ,QAAQ,KAAK;IAC3B,IAAI,UAAU;IACd,KAAK,MAAM,OAAO,MAAM,YAAY,MAAM,IAAI,SAAS,OAAO,OAAc,SAAS,OAAO,GAAG,CAAQ,EAAA,CAAG,WAAW;IACrH,OAAO,EAAE,QAAQ;GACnB;EACF,CAAC;EAED,IAAI,QAAQ,OAAO;GACjB,MAAM;GACN,aAAa;GACb,MAAM;GACN,OAAO,EAAE,OAAO;IAAE,OAAO,EAAE,OAAO;IAAG,QAAQ,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,QAAQ,KAAK;IAAG,QAAQ;GAAO,CAAC;GACrG,SAAS,OAAO,EAAE,OAAO,QAAQ,aAAa;IAC5C,MAAM,QAAQ,QAAQ,KAAK;IAC3B,MAAM,QAAQ,MAAM,IAAI,SAAS,OAAO,OAAc,QAAQ,OAAO,MAAM,CAAQ,CAAC,CAAC,MAAM,YAAY,CAAC,CAAC,QAAQ,EAAA,CAAG,KAAK,QAAQ,MAAM,GAAG,CAA4B;IACtK,MAAM,UAAU,UAAU,KAAK,CAAC,CAAC,KAAK,WAAW,OAAO,IAAI;IAC5D,MAAM,UAAU,WAAW,QAAQ,MAAM,MAAM,SAAS,IAAI,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC;IAC5F,OAAO;KAAE,UAAU,GAAG,MAAM,GAAG;KAAU;KAAS,MAAM,KAAK;KAAQ,WAAW,KAAK,UAAU;IAAa;GAC9G;EACF,CAAC;EAED,IAAI,QAAQ,OAAO;GACjB,MAAM;GACN,aAAa;GACb,MAAM;GACN,OAAO,EAAE,OAAO,CAAC,CAAC;GAClB,SAAS,YAAY;IACnB,MAAM,SAAU,IAAI,SAAiB;IACrC,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,MAAM,IAAI,YAAY,eAAe,gCAAgC;IAC/F,MAAM,yBAAQ,IAAI,KAAK,EAAA,CAAE,YAAY,CAAC,CAAC,QAAQ,SAAS,EAAE,CAAC,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC,MAAM,GAAG,EAAE;IACzF,MAAM,SAAS,KAAK,QAAQ,IAAI,GAAG,WAAW,UAAU,MAAM,IAAI;IAClE,MAAM,MAAM,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;IAChD,OAAO,GAAG,KAAK,gBAAgB,OAAO,QAAQ,MAAM,IAAI,EAAE,EAAE;IAC5D,MAAM,QAAQ,MAAM,KAAK,MAAM,EAAA,CAAG;IAClC,OAAO;KAAE,MAAM,SAAS,SAAS,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG;KAAG;IAAK;GACtE;EACF,CAAC;CACH,CAAC;AACH"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { mkdir, stat } from 'node:fs/promises'\nimport { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { type Context, field, MushinError, z } from '@mushinjs/core'\nimport MemoryDriver from '@minatojs/driver-memory'\nimport SQLiteDriver from '@minatojs/driver-sqlite'\nimport { $, Database, type Field, type Model } from 'minato'\n\nexport type { Database, Driver, Field, Model, Query, Selection, Tables, Types } from 'minato'\n\nexport const name = 'database'\nexport const inject = ['actions']\n\nexport const Config = z.object({\n driver: z.enum(['sqlite', 'memory']).default('sqlite').meta(field('Driver', 'sqlite keeps everything in one file; memory loses the data when Mushin.js stops (for trying things out)', {\n 'zh-CN': { title: '驱动', description: 'sqlite 把数据存在一个文件里;memory 在 Mushin.js 停止时丢失数据(用于试用)' },\n })),\n path: z.string().trim().min(1).default('data/mushin.db').meta(field('Database file', 'For SQLite; relative to the instance directory', {\n 'zh-CN': { title: '数据库文件', description: 'SQLite 使用;相对于实例目录' },\n })),\n})\n\nexport type Config = z.infer<typeof Config>\n\n// The drivers inject a `logger` service that Cordis 4 keeps as a built-in property instead, so\n// they would wait for it forever. They only call `ctx.logger?.info()` and the like, which the\n// built-in logger answers, so only the model is injected here.\nclass SQLite extends (SQLiteDriver as any) {\n static inject = ['model']\n}\n\nclass Memory extends (MemoryDriver as any) {\n static inject = ['model']\n}\n\nconst PAGE_LIMIT = 500\nconst EXPORT_LIMIT = 100_000\n\nexport interface ColumnInfo {\n name: string\n type: string\n primary: boolean\n nullable: boolean\n}\n\nexport interface TableInfo {\n name: string\n /** The part of the name before the first underscore, which by convention names the plugin. */\n group: string\n primary: string[]\n columns: ColumnInfo[]\n count: number\n}\n\n/** Rows as JSON can carry them: dates as ISO text, binary as a note, big integers as text. */\nfunction plain(value: unknown): unknown {\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'bigint') return value.toString()\n if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) return `<binary ${(value as ArrayBuffer).byteLength} bytes>`\n if (Array.isArray(value)) return value.map(plain)\n if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, plain(item)]))\n return value\n}\n\nfunction csvCell(value: unknown) {\n if (value === undefined || value === null) return ''\n const text = typeof value === 'object' ? JSON.stringify(value) : String(value)\n return /[\",\\r\\n]/.test(text) ? `\"${text.replace(/\"/g, '\"\"')}\"` : text\n}\n\nexport function toCsv(columns: string[], rows: Record<string, unknown>[]) {\n return [columns.map(csvCell).join(','), ...rows.map((row) => columns.map((column) => csvCell(row[column])).join(','))].join('\\r\\n') + '\\r\\n'\n}\n\nconst numeric = new Set(['integer', 'unsigned', 'float', 'double', 'decimal', 'bigint'])\nconst textual = new Set(['char', 'string', 'text'])\n\nconst escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n\nconst Filter = z.record(z.string(), z.string()).optional().describe('Column → text. Text columns match when they contain the text; numbers and booleans must be equal.')\n\n/**\n * The database service (`ctx.database`, from minato) and the actions behind the Database page.\n * Plugins declare tables with `ctx.model.extend()` in a context that injects both `model` and\n * `database`; by convention a table's name starts with the plugin's name and an underscore.\n */\nexport function apply(ctx: Context, config: Config) {\n const baseDir = ctx.baseDir ?? process.cwd()\n const file = config.driver === 'sqlite' ? (isAbsolute(config.path) ? config.path : resolve(baseDir, config.path)) : undefined\n\n ctx.plugin(Database)\n if (file) {\n ctx.inject(['model'], async (ctx) => {\n await mkdir(dirname(file), { recursive: true })\n // The driver resolves the path against `ctx.baseUrl`; a file URL resolves to itself.\n ctx.plugin(SQLite as any, { path: pathToFileURL(file).href })\n })\n } else {\n ctx.plugin(Memory as any, {})\n }\n\n ctx.inject(['model', 'database'], (ctx) => {\n const tableOf = (name: string): Model => {\n const model = ctx.model.tables[name]\n if (!model) throw new MushinError('not_found', `No table named \"${name}\"`)\n return model\n }\n const primaryOf = (model: Model) => (Array.isArray(model.primary) ? model.primary : [model.primary]) as string[]\n const columnsOf = (model: Model): ColumnInfo[] => {\n const primary = primaryOf(model)\n return Object.entries(model.fields as Record<string, Field | undefined>)\n .filter(([, item]) => item && !item.deprecated && !item.relation && !item.expr)\n .map(([name, item]) => ({ name, type: String(item!.deftype ?? item!.type?.type ?? ''), primary: primary.includes(name), nullable: !!item!.nullable }))\n }\n const queryOf = (model: Model, filter: Record<string, string> | undefined) => {\n const query: Record<string, unknown> = {}\n const columns = new Map(columnsOf(model).map((column) => [column.name, column]))\n for (const [name, text] of Object.entries(filter ?? {})) {\n if (text === '') continue\n const column = columns.get(name)\n if (!column) throw new MushinError('invalid_input', `Table \"${model.name}\" has no column \"${name}\"`)\n if (numeric.has(column.type)) {\n const value = Number(text)\n if (!Number.isFinite(value)) throw new MushinError('invalid_input', `\"${text}\" is not a number`)\n query[name] = value\n } else if (column.type === 'boolean') {\n query[name] = text === 'true' || text === '1'\n } else if (textual.has(column.type)) {\n query[name] = { $regex: new RegExp(escapeRegExp(text), 'i') }\n } else {\n throw new MushinError('invalid_input', `Column \"${name}\" (${column.type}) cannot be filtered`)\n }\n }\n return query\n }\n const keyQuery = (model: Model, key: Record<string, unknown>) => {\n const query: Record<string, unknown> = {}\n for (const name of primaryOf(model)) {\n if (!(name in key)) throw new MushinError('invalid_input', `The key needs \"${name}\"`)\n query[name] = key[name]\n }\n return query\n }\n\n const status = async () => {\n const size = file ? (await stat(file).catch(() => undefined))?.size ?? 0 : undefined\n return {\n driver: config.driver,\n ...(file ? { path: relative(baseDir, file).split(sep).join('/'), size } : {}),\n tables: Object.keys(ctx.model.tables).length,\n }\n }\n\n const countRows = async (table: string, model: Model, query: Record<string, unknown>) => {\n const key = primaryOf(model)[0] ?? columnsOf(model)[0]?.name\n return Number(await ctx.database.select(table as any, query as any).execute((row: any) => $.count(row[key]))) || 0\n }\n\n const tables = async (): Promise<TableInfo[]> => {\n const list: TableInfo[] = []\n for (const [name, model] of Object.entries(ctx.model.tables)) {\n const count = await countRows(name, model, {})\n list.push({ name, group: name.split('_')[0], primary: primaryOf(model), columns: columnsOf(model), count: Number(count) || 0 })\n }\n return list.sort((a, b) => a.name.localeCompare(b.name))\n }\n\n ctx.actions.define({\n name: 'database.status',\n description: 'Which database driver runs, the SQLite file and its size, and how many tables exist.',\n risk: 'read',\n input: z.object({}),\n handler: status,\n })\n\n ctx.actions.define({\n name: 'database.tables',\n description: 'Every table in the database with its columns, primary key and row count. Tables are grouped by the plugin name before the first underscore, e.g. flows_var.',\n risk: 'read',\n input: z.object({}),\n handler: tables,\n })\n\n ctx.actions.define({\n name: 'database.rows',\n description: 'A page of rows from one table, read-only: optional sort and filters. Returns the rows and the total that match.',\n risk: 'read',\n input: z.object({\n table: z.string(),\n offset: z.number().int().min(0).default(0),\n limit: z.number().int().min(1).max(PAGE_LIMIT).default(50),\n sort: z.object({ column: z.string(), order: z.enum(['asc', 'desc']).default('asc') }).optional(),\n filter: Filter,\n }),\n handler: async ({ table, offset, limit, sort, filter }) => {\n const model = tableOf(table)\n const query = queryOf(model, filter)\n let selection = ctx.database.select(table as any, query as any)\n if (sort) {\n if (!columnsOf(model).some((column) => column.name === sort.column)) throw new MushinError('invalid_input', `Table \"${table}\" has no column \"${sort.column}\"`)\n selection = selection.orderBy(sort.column as any, sort.order)\n }\n const [rows, total] = await Promise.all([\n selection.offset(offset).limit(limit).execute(),\n countRows(table, model, query),\n ])\n return { rows: rows.map((row) => plain(row)), total }\n },\n })\n\n ctx.actions.define({\n name: 'database.update',\n description: 'Changes columns of one row, found by its primary key. Writes to plugin data directly, so the change takes effect without any checks the plugin would make.',\n risk: 'dangerous',\n input: z.object({ table: z.string(), key: z.record(z.string(), z.unknown()), data: z.record(z.string(), z.unknown()) }),\n handler: async ({ table, key, data }) => {\n const model = tableOf(table)\n const columns = new Set(columnsOf(model).map((column) => column.name))\n for (const name of Object.keys(data)) {\n if (!columns.has(name)) throw new MushinError('invalid_input', `Table \"${table}\" has no column \"${name}\"`)\n if (primaryOf(model).includes(name)) throw new MushinError('invalid_input', `\"${name}\" is part of the primary key and cannot be changed`)\n }\n const result = await ctx.database.set(table as any, keyQuery(model, key) as any, data as any)\n if (!result.matched) throw new MushinError('not_found', 'No row has that key')\n return { updated: result.modified ?? result.matched }\n },\n })\n\n ctx.actions.define({\n name: 'database.remove',\n description: 'Deletes rows from a table by their primary keys. Cannot be undone; make a backup first if unsure.',\n risk: 'dangerous',\n input: z.object({ table: z.string(), keys: z.array(z.record(z.string(), z.unknown())).min(1).max(PAGE_LIMIT) }),\n handler: async ({ table, keys }) => {\n const model = tableOf(table)\n let removed = 0\n for (const key of keys) removed += (await ctx.database.remove(table as any, keyQuery(model, key) as any)).removed ?? 0\n return { removed }\n },\n })\n\n ctx.actions.define({\n name: 'database.export',\n description: 'The rows of a table (matching optional filters) as CSV or JSON text, for downloading.',\n risk: 'read',\n input: z.object({ table: z.string(), format: z.enum(['csv', 'json']).default('csv'), filter: Filter }),\n handler: async ({ table, format, filter }) => {\n const model = tableOf(table)\n const rows = (await ctx.database.select(table as any, queryOf(model, filter) as any).limit(EXPORT_LIMIT).execute()).map((row) => plain(row) as Record<string, unknown>)\n const columns = columnsOf(model).map((column) => column.name)\n const content = format === 'csv' ? '' + toCsv(columns, rows) : JSON.stringify(rows, null, 2)\n return { filename: `${table}.${format}`, content, rows: rows.length, truncated: rows.length >= EXPORT_LIMIT }\n },\n })\n\n ctx.actions.define({\n name: 'database.backup',\n description: 'Copies the SQLite database to data/backups/ under a dated name while Mushin.js keeps running. Returns the backup\\'s path.',\n risk: 'write',\n input: z.object({}),\n handler: async () => {\n const driver = (ctx.database as any)._driver\n if (!file || !driver?.db) throw new MushinError('unavailable', 'Backups need the SQLite driver')\n const stamp = new Date().toISOString().replace(/[-:]/g, '').replace('T', '-').slice(0, 15)\n const target = join(dirname(file), 'backups', `mushin-${stamp}.db`)\n await mkdir(dirname(target), { recursive: true })\n driver.db.exec(`VACUUM INTO '${target.replace(/'/g, \"''\")}'`)\n const size = (await stat(target)).size\n return { path: relative(baseDir, target).split(sep).join('/'), size }\n },\n })\n })\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mushinjs/plugin-database",
3
- "version": "0.2.0-next.20261112165327",
3
+ "version": "0.2.0-next.20261113103537",
4
4
  "description": "A database for plugins, SQLite by default",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -46,7 +46,7 @@
46
46
  "dependencies": {
47
47
  "@minatojs/driver-memory": "4.0.0",
48
48
  "@minatojs/driver-sqlite": "5.0.3",
49
- "@mushinjs/core": "0.2.0-next.20261112165327",
49
+ "@mushinjs/core": "0.2.0-next.20261113103537",
50
50
  "cordis": "4.0.0-rc.11",
51
51
  "minato": "4.0.1"
52
52
  }