acrylblends 0.7.6 → 0.7.7
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/index.js +89 -5
- package/dist/index.js.map +1 -1
- package/docs/framework/UPSTREAM.md +1 -1
- package/docs/framework/guides/data.md +66 -2
- package/docs/framework/upstream.json +2 -2
- package/lib/database-D_UN6We8.d.ts +44 -0
- package/lib/db/sqlite.d.ts +1 -1
- package/lib/db.d.ts +84 -1
- package/lib/kv/db.d.ts +12 -0
- package/lib/kv/db.js +57 -0
- package/lib/kv/json.d.ts +11 -0
- package/lib/kv/json.js +161 -0
- package/lib/kv-DWDm0Lja.d.ts +39 -0
- package/lib/kv.d.ts +93 -0
- package/lib/kv.js +2 -0
- package/lib/src-DRFVEQQb.js +90 -0
- package/package.json +13 -1
- package/plugins/acryl-db/upstream.json +1 -1
- package/plugins/acryl-db-sqlite/upstream.json +1 -1
- package/plugins/acryl-inspect/upstream.json +1 -1
- package/plugins/acryl-kv/LICENSE +47 -0
- package/plugins/acryl-kv/plugin.json +12 -0
- package/plugins/acryl-kv/src/check.ts +58 -0
- package/plugins/acryl-kv/src/copy.ts +39 -0
- package/plugins/acryl-kv/src/cordis.ts +13 -0
- package/plugins/acryl-kv/src/index.ts +14 -0
- package/plugins/acryl-kv/src/kv.ts +37 -0
- package/plugins/acryl-kv/upstream.json +18 -0
- package/plugins/acryl-kv-db/LICENSE +47 -0
- package/plugins/acryl-kv-db/plugin.json +13 -0
- package/plugins/acryl-kv-db/src/index.ts +37 -0
- package/plugins/acryl-kv-db/src/open.ts +80 -0
- package/plugins/acryl-kv-db/upstream.json +21 -0
- package/plugins/acryl-kv-json/LICENSE +47 -0
- package/plugins/acryl-kv-json/plugin.json +13 -0
- package/plugins/acryl-kv-json/src/index.ts +71 -0
- package/plugins/acryl-kv-json/src/open.ts +185 -0
- package/plugins/acryl-kv-json/upstream.json +22 -0
- package/plugins/acryl-node-server/src/index.ts +12 -1
- package/plugins/acryl-node-server/upstream.json +2 -2
- package/plugins/acryl-router/upstream.json +1 -1
- package/plugins/acryl-router-http/upstream.json +1 -1
- package/plugins/acryl-state/upstream.json +1 -1
- package/plugins/desktop/upstream.json +1 -1
- package/plugins/desktop-web/upstream.json +1 -1
- package/plugins/tsconfig.json +2 -0
- package/templates/todo/upstream.json +1 -1
- package/lib/index-D4FAsBZu.d.ts +0 -85
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plain way to open the JSON-file provider, for code without Cordis (an acrylblends app): `openKv({ path })` reads one JSON file into
|
|
3
|
+
* memory and resolves the `kv` port with the function that closes it. The Cordis plugin is a thin wrapper over this, so both run one code
|
|
4
|
+
* path. This entry imports nothing from Cordis.
|
|
5
|
+
*
|
|
6
|
+
* Every write queues behind the writes before it and persists the whole map: serialize to a temporary file in the same folder, flush it,
|
|
7
|
+
* rename it over the file, and flush the folder where the platform allows. The call resolves after the rename, so a crash leaves the old file
|
|
8
|
+
* or the new one, never half of one. One process owns the file: two writers would overwrite each other.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { mkdir, open, readdir, readFile, rename, rm } from 'node:fs/promises'
|
|
12
|
+
import { basename, dirname, join, resolve } from 'node:path'
|
|
13
|
+
import { checkKey, compareKeys, decodeValue, encodeValue, type Kv, type KvOpened, runChange } from '../../acryl-kv/src/index.ts'
|
|
14
|
+
|
|
15
|
+
export type { Kv, KvOpened } from '../../acryl-kv/src/index.ts'
|
|
16
|
+
|
|
17
|
+
export interface OpenKvOptions {
|
|
18
|
+
/** The JSON file; its folder is created if missing. */
|
|
19
|
+
readonly path: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** The file's own format version, for a later change of layout. */
|
|
23
|
+
const FORMAT = 1
|
|
24
|
+
|
|
25
|
+
/** Stray temporary files from a crash end in `.tmp` and start with the file's name. */
|
|
26
|
+
const tempPrefix = (file: string): string => `${basename(file)}.`
|
|
27
|
+
|
|
28
|
+
/** The file content for a map of key to JSON text, keys in code point order, so the same data is the same bytes. */
|
|
29
|
+
function serialize(entries: ReadonlyMap<string, string>): string {
|
|
30
|
+
const body = [...entries.keys()].sort(compareKeys).map(key => `${JSON.stringify(key)}:${entries.get(key)}`).join(',')
|
|
31
|
+
return `{"version":${FORMAT},"entries":{${body}}}\n`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function unreadable(file: string, reason: string): Error {
|
|
35
|
+
return new Error(`acryl-kv-json: ${file} is not a readable kv file (${reason}); it was not changed`)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The entries of a file, or none when it does not exist. A file that exists and does not parse is an error: it is never started over. */
|
|
39
|
+
async function load(file: string): Promise<Map<string, string>> {
|
|
40
|
+
let text: string
|
|
41
|
+
try {
|
|
42
|
+
text = await readFile(file, 'utf8')
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return new Map()
|
|
45
|
+
throw error
|
|
46
|
+
}
|
|
47
|
+
let parsed: unknown
|
|
48
|
+
try {
|
|
49
|
+
parsed = JSON.parse(text)
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw unreadable(file, (error as Error).message)
|
|
52
|
+
}
|
|
53
|
+
if (typeof parsed !== 'object' || parsed === null || (parsed as { version?: unknown }).version !== FORMAT) throw unreadable(file, 'not a version 1 kv file')
|
|
54
|
+
const entries = (parsed as { entries?: unknown }).entries
|
|
55
|
+
if (typeof entries !== 'object' || entries === null || Array.isArray(entries)) throw unreadable(file, 'no entries object')
|
|
56
|
+
return new Map(Object.entries(entries).map(([key, value]) => [key, JSON.stringify(value)]))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Remove the temporary files an earlier crash left beside the file. */
|
|
60
|
+
async function removeStrays(file: string): Promise<void> {
|
|
61
|
+
const prefix = tempPrefix(file)
|
|
62
|
+
for (const name of await readdir(dirname(file))) {
|
|
63
|
+
if (name.startsWith(prefix) && name.endsWith('.tmp')) await rm(join(dirname(file), name), { force: true })
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const RENAME_RETRIES = 8
|
|
68
|
+
|
|
69
|
+
/** Rename over the target. Windows refuses while a scanner or an indexer holds the target for a moment, so it is retried briefly. */
|
|
70
|
+
async function replace(from: string, to: string): Promise<void> {
|
|
71
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
72
|
+
try {
|
|
73
|
+
await rename(from, to)
|
|
74
|
+
return
|
|
75
|
+
} catch (error) {
|
|
76
|
+
const code = (error as NodeJS.ErrnoException).code
|
|
77
|
+
if (attempt >= RENAME_RETRIES || (code !== 'EPERM' && code !== 'EBUSY' && code !== 'EACCES')) throw error
|
|
78
|
+
await new Promise(resolveWait => setTimeout(resolveWait, 10 * (attempt + 1)))
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Flush the folder entry of a renamed file (POSIX); Windows cannot open a folder, and a filesystem that refuses is no worse than before. */
|
|
84
|
+
async function flushFolder(folder: string): Promise<void> {
|
|
85
|
+
if (process.platform === 'win32') return
|
|
86
|
+
try {
|
|
87
|
+
const handle = await open(folder, 'r')
|
|
88
|
+
try {
|
|
89
|
+
await handle.sync()
|
|
90
|
+
} finally {
|
|
91
|
+
await handle.close()
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
// Best effort: the rename itself is already atomic.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Open (and create, when missing) a JSON-file store. @throws when the file exists and is not a readable kv file. */
|
|
99
|
+
export async function openKv(options: OpenKvOptions): Promise<KvOpened> {
|
|
100
|
+
const file = resolve(options.path)
|
|
101
|
+
await mkdir(dirname(file), { recursive: true })
|
|
102
|
+
await removeStrays(file)
|
|
103
|
+
let state = await load(file)
|
|
104
|
+
let closed = false
|
|
105
|
+
let sequence = 0
|
|
106
|
+
let tail: Promise<unknown> = Promise.resolve()
|
|
107
|
+
|
|
108
|
+
const guard = (): void => {
|
|
109
|
+
if (closed) throw new Error(`acryl-kv-json: ${file} is closed`)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Persist a map: the file is the old one or this one, never between. */
|
|
113
|
+
const persist = async (next: ReadonlyMap<string, string>): Promise<void> => {
|
|
114
|
+
sequence += 1
|
|
115
|
+
const temp = join(dirname(file), `${tempPrefix(file)}${process.pid}.${sequence}.tmp`)
|
|
116
|
+
try {
|
|
117
|
+
const handle = await open(temp, 'w')
|
|
118
|
+
try {
|
|
119
|
+
await handle.writeFile(serialize(next), 'utf8')
|
|
120
|
+
await handle.sync()
|
|
121
|
+
} finally {
|
|
122
|
+
await handle.close()
|
|
123
|
+
}
|
|
124
|
+
await replace(temp, file)
|
|
125
|
+
} catch (error) {
|
|
126
|
+
await rm(temp, { force: true })
|
|
127
|
+
throw error
|
|
128
|
+
}
|
|
129
|
+
await flushFolder(dirname(file))
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Run a change after every earlier one; the map it works on becomes the state only once it is on disk. */
|
|
133
|
+
const write = <T>(change: (draft: Map<string, string>) => T): Promise<T> => {
|
|
134
|
+
guard()
|
|
135
|
+
const task = tail.then(async () => {
|
|
136
|
+
const draft = new Map(state)
|
|
137
|
+
const result = change(draft)
|
|
138
|
+
await persist(draft)
|
|
139
|
+
state = draft
|
|
140
|
+
return result
|
|
141
|
+
})
|
|
142
|
+
tail = task.catch(() => undefined)
|
|
143
|
+
return task
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const kv: Kv = {
|
|
147
|
+
async get<T>(key: string) {
|
|
148
|
+
guard()
|
|
149
|
+
checkKey(key)
|
|
150
|
+
const text = state.get(key)
|
|
151
|
+
return text === undefined ? undefined : decodeValue<T>(text)
|
|
152
|
+
},
|
|
153
|
+
async set(key, value) {
|
|
154
|
+
checkKey(key)
|
|
155
|
+
const text = encodeValue(value)
|
|
156
|
+
await write(draft => { draft.set(key, text) })
|
|
157
|
+
},
|
|
158
|
+
async update<T>(key: string, change: (current: T | undefined) => T) {
|
|
159
|
+
checkKey(key)
|
|
160
|
+
return write(draft => {
|
|
161
|
+
const text = draft.get(key)
|
|
162
|
+
const next = runChange(change, text === undefined ? undefined : decodeValue<T>(text))
|
|
163
|
+
const encoded = encodeValue(next)
|
|
164
|
+
draft.set(key, encoded)
|
|
165
|
+
return decodeValue<T>(encoded)
|
|
166
|
+
})
|
|
167
|
+
},
|
|
168
|
+
async delete(key) {
|
|
169
|
+
checkKey(key)
|
|
170
|
+
await write(draft => { draft.delete(key) })
|
|
171
|
+
},
|
|
172
|
+
async list(prefix = '') {
|
|
173
|
+
guard()
|
|
174
|
+
return [...state.keys()].filter(key => key.startsWith(prefix)).sort(compareKeys)
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
kv,
|
|
180
|
+
close: async () => {
|
|
181
|
+
closed = true
|
|
182
|
+
await tail
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"source": {
|
|
3
|
+
"repo": "acryldev/acryl",
|
|
4
|
+
"path": "plugins/acryl-kv-json",
|
|
5
|
+
"commit": "f8e385e4389fe2fdc6fd2a4366a7f2a7389faacb"
|
|
6
|
+
},
|
|
7
|
+
"vendored": [
|
|
8
|
+
"src",
|
|
9
|
+
"tests",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
12
|
+
"rewrittenSpecifiers": 6,
|
|
13
|
+
"digest": "sha256:d8e3e7f9c98399d0f21f29f91323b4ce57646e59e312d4175978259d618a88cf",
|
|
14
|
+
"testsNotRun": {
|
|
15
|
+
"tests/loader.spec.ts": "loads the plugin through the real Loader (@deepseek-ai/cordis-plugin-loader), which generated apps do not install; the plain opener it wraps is tested here by open.spec.ts and conformance.spec.ts",
|
|
16
|
+
"tests/crash.spec.ts": "kills a child process that imports the package build (lib/), which a copy vendored into an app does not have; it runs in ACRYL",
|
|
17
|
+
"tests/plain-entry-imports.spec.ts": "a copy vendored into an app (with relative imports, no build of its own) has neither `lib/`; the app's own typecheck is what proves the copy names no Cordis type it does not provide."
|
|
18
|
+
},
|
|
19
|
+
"notice": [
|
|
20
|
+
"LICENSE"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -93,13 +93,24 @@ export class NodeServer extends Service {
|
|
|
93
93
|
this.bound = (this.server.address() as AddressInfo).port
|
|
94
94
|
return
|
|
95
95
|
} catch (error) {
|
|
96
|
-
if ((error
|
|
96
|
+
if (!isPortUnavailable(error)) throw error
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
throw new Error(`acryl-node-server: no free port from ${String(port)} to ${String(last)} on ${hostname}`)
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Whether a failed bind means "this port, try the next one". A port another socket holds answers `EADDRINUSE` everywhere. On Windows a port
|
|
105
|
+
* inside a range the system reserves (Hyper-V, WSL and Docker exclude ranges, `netsh interface ipv4 show excludedportrange`) answers
|
|
106
|
+
* `EACCES`, which is a property of that port too; elsewhere `EACCES` means the process may not bind low ports at all, so moving on would
|
|
107
|
+
* only hide the reason.
|
|
108
|
+
*/
|
|
109
|
+
export function isPortUnavailable(error: unknown, platform: NodeJS.Platform = process.platform): boolean {
|
|
110
|
+
const code = (error as NodeJS.ErrnoException | undefined)?.code
|
|
111
|
+
return code === 'EADDRINUSE' || (platform === 'win32' && code === 'EACCES')
|
|
112
|
+
}
|
|
113
|
+
|
|
103
114
|
/** Loads once the server listens, so a plugin that injects `nodeServer` can rely on `url`. A failure to bind fails the plugin. */
|
|
104
115
|
export async function apply(ctx: Context, config: Partial<Config> = {}): Promise<void> {
|
|
105
116
|
const server = new NodeServer(ctx, { hostname: '127.0.0.1', port: 0, scan: 20, ...config })
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"source": {
|
|
3
3
|
"repo": "acryldev/acryl",
|
|
4
4
|
"path": "plugins/acryl-node-server",
|
|
5
|
-
"commit": "
|
|
5
|
+
"commit": "f8e385e4389fe2fdc6fd2a4366a7f2a7389faacb"
|
|
6
6
|
},
|
|
7
7
|
"vendored": [
|
|
8
8
|
"src",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"LICENSE"
|
|
11
11
|
],
|
|
12
12
|
"rewrittenSpecifiers": 31,
|
|
13
|
-
"digest": "sha256:
|
|
13
|
+
"digest": "sha256:e4f98422082758912834a6e454505230a18ac543b4851f6eb697b628a1acec4e",
|
|
14
14
|
"testsNotRun": {
|
|
15
15
|
"tests/client-modules.spec.ts": "links typescript from the plugin's own node_modules (ACRYL's workspace layout); clientModules and build-client are exercised here by the templates' tests and verify:scaffolder",
|
|
16
16
|
"tests/register.spec.ts": "links typescript from the plugin's own node_modules (ACRYL's workspace layout); the hook is exercised here by the templates' own .tsx tests and verify:scaffolder"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"apps/acryl-desktop/src/shell/desktop-user-data.ts": "user-data.ts",
|
|
6
6
|
"apps/acryl-desktop/src/shell/shutdown.ts": "shutdown.ts"
|
|
7
7
|
},
|
|
8
|
-
"commit": "
|
|
8
|
+
"commit": "f8e385e4389fe2fdc6fd2a4366a7f2a7389faacb"
|
|
9
9
|
},
|
|
10
10
|
"rewrittenSpecifiers": 0,
|
|
11
11
|
"digest": "sha256:7dfe960a8bbb476c35540d2202a7aaa62b33492c2ac3e34314e2ecfca45f32f2"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"apps/acryl-desktop/src/shell/desktop-user-data.ts": "user-data.ts",
|
|
6
6
|
"apps/acryl-desktop/src/shell/shutdown.ts": "shutdown.ts"
|
|
7
7
|
},
|
|
8
|
-
"commit": "
|
|
8
|
+
"commit": "f8e385e4389fe2fdc6fd2a4366a7f2a7389faacb"
|
|
9
9
|
},
|
|
10
10
|
"rewrittenSpecifiers": 0,
|
|
11
11
|
"digest": "sha256:7dfe960a8bbb476c35540d2202a7aaa62b33492c2ac3e34314e2ecfca45f32f2"
|
package/plugins/tsconfig.json
CHANGED
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
"acryl-node-server/tests/adapter",
|
|
25
25
|
"acryl-router/tests/loader.spec.ts",
|
|
26
26
|
"acryl-db-sqlite/tests/loader.spec.ts",
|
|
27
|
+
"acryl-kv-json/tests/loader.spec.ts",
|
|
28
|
+
"acryl-kv-db/tests/loader.spec.ts",
|
|
27
29
|
"acryl-state/src/react.ts",
|
|
28
30
|
"acryl-state/tests/react.spec.ts",
|
|
29
31
|
"acryl-router-http/tests/adapter.spec.ts",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"examples/acryl-todo/src/index.ts": "src/todo.ts",
|
|
6
6
|
"examples/acryl-todo/src/page.ts": "src/page.ts"
|
|
7
7
|
},
|
|
8
|
-
"commit": "
|
|
8
|
+
"commit": "f8e385e4389fe2fdc6fd2a4366a7f2a7389faacb"
|
|
9
9
|
},
|
|
10
10
|
"rewrittenSpecifiers": 4,
|
|
11
11
|
"digest": "sha256:9093704776563001f7f472f94cc5c2fdd79206342c1cb59c11fce62aa74df3ed"
|
package/lib/index-D4FAsBZu.d.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
//#region ../../plugins/acryl-db/src/facade/database.d.ts
|
|
2
|
-
/** Values supported by the portable SQL drivers. */
|
|
3
|
-
type SqlValue = null | number | bigint | string | Uint8Array;
|
|
4
|
-
/**
|
|
5
|
-
* Asynchronous SQL operations shared by a database and its transaction handles.
|
|
6
|
-
*
|
|
7
|
-
* `exec` runs SQL text without bindings and may contain several statements. `run`, `get`, and `all`
|
|
8
|
-
* execute one statement with positional bindings. Adapters may cache prepared statements by SQL text,
|
|
9
|
-
* so callers pass values as bindings instead of interpolating them. `run` resolves the number of rows
|
|
10
|
-
* the statement changed.
|
|
11
|
-
*/
|
|
12
|
-
interface DbExecutor {
|
|
13
|
-
exec(sql: string): Promise<void>;
|
|
14
|
-
run(sql: string, ...params: SqlValue[]): Promise<{
|
|
15
|
-
readonly changes: number;
|
|
16
|
-
}>;
|
|
17
|
-
get<T extends object>(sql: string, ...params: SqlValue[]): Promise<T | undefined>;
|
|
18
|
-
all<T extends object>(sql: string, ...params: SqlValue[]): Promise<T[]>;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Minimal portable SQL database facade.
|
|
22
|
-
*
|
|
23
|
-
* All operations are asynchronous so adapters may execute outside the caller's runtime.
|
|
24
|
-
*
|
|
25
|
-
* `transaction` passes the callback a transaction handle. All work in the transaction
|
|
26
|
-
* must use that handle; the handle is invalid after the callback settles. Adapters must
|
|
27
|
-
* queue unrelated operations and other transactions until the transaction finishes. The
|
|
28
|
-
* returned promise settles after commit or rollback. Calling the database itself (including
|
|
29
|
-
* `transaction`) from inside a callback therefore waits for that transaction and
|
|
30
|
-
* never settles.
|
|
31
|
-
*
|
|
32
|
-
* When the callback rejects, the adapter must roll the transaction back before rejecting
|
|
33
|
-
* with that same error. If rollback fails, it must reject with a different error (for
|
|
34
|
-
* example an `AggregateError`) so callers cannot mistake the callback error for a
|
|
35
|
-
* guaranteed rollback.
|
|
36
|
-
*
|
|
37
|
-
* Closing is the adapter's: the plugin that opened a database closes it, so the facade an
|
|
38
|
-
* app receives has no `close`.
|
|
39
|
-
*/
|
|
40
|
-
interface Db extends DbExecutor {
|
|
41
|
-
transaction<T>(callback: (transaction: DbExecutor) => Promise<T>): Promise<T>;
|
|
42
|
-
}
|
|
43
|
-
//#endregion
|
|
44
|
-
//#region ../../plugins/acryl-db/src/facade/migrations.d.ts
|
|
45
|
-
type Migration = {
|
|
46
|
-
readonly version: number;
|
|
47
|
-
readonly statements: readonly string[];
|
|
48
|
-
};
|
|
49
|
-
/**
|
|
50
|
-
* Apply all pending migrations atomically, tracking their version in the named metadata table.
|
|
51
|
-
*
|
|
52
|
-
* The bookkeeping SQL runs unchanged on SQLite and Postgres: no `STRICT` table, `ON CONFLICT DO NOTHING`
|
|
53
|
-
* instead of `INSERT OR IGNORE`, and the version written as a checked integer rather than a binding,
|
|
54
|
-
* since the two dialects spell placeholders differently.
|
|
55
|
-
*/
|
|
56
|
-
declare function applyMigrations(database: Db, migrations: readonly Migration[], options: {
|
|
57
|
-
readonly table: string;
|
|
58
|
-
}): Promise<void>;
|
|
59
|
-
//#endregion
|
|
60
|
-
//#region ../../plugins/acryl-db/src/facade/schema.d.ts
|
|
61
|
-
/** Validate a prefix before using it as part of a SQL identifier. */
|
|
62
|
-
declare function validateSchemaPrefix(prefix: string): void;
|
|
63
|
-
//#endregion
|
|
64
|
-
//#region ../../plugins/acryl-db/src/migration-files.d.ts
|
|
65
|
-
/**
|
|
66
|
-
* Read every `NNN-name.sql` file of a folder, in number order.
|
|
67
|
-
* @throws when a `.sql` file is not named `NNN-name.sql`, or the numbers do not run 1, 2, 3, ... without gaps or repeats.
|
|
68
|
-
*/
|
|
69
|
-
declare function readMigrationFiles(folder: string | URL): Promise<Migration[]>;
|
|
70
|
-
//#endregion
|
|
71
|
-
//#region ../../plugins/acryl-db/src/index.d.ts
|
|
72
|
-
/**
|
|
73
|
-
* The `db` port: what an app's code sees of its relational database, whichever provider (`acryl-db-sqlite`, `acryl-db-postgres`) serves
|
|
74
|
-
* it. Plain code (an acrylblends app) opens a database with a provider's `openDb`; a Cordis plugin declares `inject: ['db']`, imports
|
|
75
|
-
* `acryl-db/cordis` for the `ctx.db` type, and never imports a driver.
|
|
76
|
-
*
|
|
77
|
-
* Bindings are positional `?` placeholders on every provider; a provider whose database spells them differently translates them.
|
|
78
|
-
* This entry imports nothing from Cordis, at runtime or in its types.
|
|
79
|
-
*/
|
|
80
|
-
/** The service name every `db` provider registers. */
|
|
81
|
-
declare const DB_SERVICE = "db";
|
|
82
|
-
/** The table that records an app's migration version. */
|
|
83
|
-
declare const MIGRATIONS_TABLE = "_migrations";
|
|
84
|
-
//#endregion
|
|
85
|
-
export { Migration as a, DbExecutor as c, validateSchemaPrefix as i, SqlValue as l, MIGRATIONS_TABLE as n, applyMigrations as o, readMigrationFiles as r, Db as s, DB_SERVICE as t };
|