acrylblends 1.0.2 → 1.0.3

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.
@@ -30,6 +30,11 @@ interface AppHome {
30
30
  readonly home: string
31
31
  }
32
32
 
33
+ /** The project folder of an acrylblends app (`appInstance.root`), when the instance has one: where a relative path in a row's config is read from, since such an app has no Loader base URL. */
34
+ function appRoot(value: unknown): string | undefined {
35
+ return typeof value === 'object' && value !== null && 'root' in value && typeof value.root === 'string' ? value.root : undefined
36
+ }
37
+
33
38
  function isAppHome(value: unknown): value is AppHome {
34
39
  return typeof value === 'object' && value !== null && 'home' in value && typeof value.home === 'string'
35
40
  }
@@ -51,12 +56,14 @@ export const Config: z<Config> = z.object({
51
56
  /** The file name used in the app home when `path` is not configured. */
52
57
  export const DEFAULT_FILE = 'data.sqlite'
53
58
 
54
- /** A configured path as a file path: absolute as given, relative to the Loader's base URL (the app) otherwise. */
59
+ /** A configured path as a file path: absolute as given, relative to the Loader's base URL (the app) otherwise, or to the app's project folder (`appInstance.root`) where there is no base URL. */
55
60
  function resolveFromApp(ctx: Context, path: string, option: string): string {
56
61
  if (path.startsWith('file:')) return fileURLToPath(path)
57
62
  if (isAbsolute(path)) return path
58
63
  const base = ctx.baseUrl
59
64
  if (base === undefined || !base.startsWith('file:')) {
65
+ const root = appRoot(ctx.get('appInstance'))
66
+ if (root !== undefined) return resolve(root, path)
60
67
  throw new Error(`acryl-db-sqlite: ${option} ${JSON.stringify(path)} is relative, but this app has no file base URL to resolve it from; give an absolute path`)
61
68
  }
62
69
  const appDir = base.endsWith('/') ? fileURLToPath(base) : dirname(fileURLToPath(base))
@@ -8,7 +8,7 @@
8
8
  "@deepseek-ai/cordis": "4.0.4",
9
9
  "@deepseek-ai/schemastery": "3.18.4"
10
10
  },
11
- "mount": "mount it after instance: root.plugin(kvJson, {}) (the file is kv.json in the app home, or set path); then a plugin that declares inject: ['kv'] calls await ctx.kv.set('theme', 'dark') and ctx.kv.update('visits', (n) => (n ?? 0) + 1)",
11
+ "mount": "mount it after instance, as a row in blend.yaml (`- id: kv` / `name: acryl-kv-json`, registered in src/app.ts as 'acryl-kv-json') or as root.plugin(kvJson, {}) (the file is kv.json in the app home, or set path; a relative path is read from the app's folder); then a plugin that declares inject: ['kv'] calls await ctx.kv.set('theme', 'dark') and ctx.kv.update('visits', (n) => (n ?? 0) + 1)",
12
12
  "omit": ["**/*.test.*", "**/*.spec.*", "**/*.case.ts"],
13
13
  "imports": {
14
14
  "#acryl-kv-json": "acryl-kv-json/src/index.ts"
@@ -41,12 +41,19 @@ export const Config: z<Config> = z.object({
41
41
  /** The file name used in the app home when `path` is not configured. */
42
42
  export const DEFAULT_FILE = 'kv.json'
43
43
 
44
- /** A configured path as a file path: absolute as given, relative to the Loader's base URL (the app) otherwise. */
44
+ /** The project folder of an acrylblends app (`appInstance.root`), when the instance has one: where a relative path in a row's config is read from, since such an app has no Loader base URL. */
45
+ function appRoot(value: unknown): string | undefined {
46
+ return typeof value === 'object' && value !== null && 'root' in value && typeof value.root === 'string' ? value.root : undefined
47
+ }
48
+
49
+ /** A configured path as a file path: absolute as given, relative to the Loader's base URL (the app) otherwise, or to the app's project folder (`appInstance.root`) where there is no base URL. */
45
50
  function resolveFromApp(ctx: Context, path: string): string {
46
51
  if (path.startsWith('file:')) return fileURLToPath(path)
47
52
  if (isAbsolute(path)) return path
48
53
  const base = ctx.baseUrl
49
54
  if (base === undefined || !base.startsWith('file:')) {
55
+ const root = appRoot(ctx.get('appInstance'))
56
+ if (root !== undefined) return resolve(root, path)
50
57
  throw new Error(`acryl-kv-json: path ${JSON.stringify(path)} is relative, but this app has no file base URL to resolve it from; give an absolute path`)
51
58
  }
52
59
  const appDir = base.endsWith('/') ? fileURLToPath(base) : dirname(fileURLToPath(base))
@@ -135,8 +135,9 @@ const readBodyBufferedBeforeDisconnect = (
135
135
  result = newBodyUnusableError()
136
136
  } else {
137
137
  const bodyChunks = chunks ?? []
138
- const chunk = incoming.read() as Buffer | string | null
139
- if (chunk !== null) {
138
+ // Drain what is still buffered. On a destroyed stream one read() returns all of it on some Node versions and only the first chunk on others (Node 26), so read until
139
+ // the stream says there is no more: that is what Readable#read() promises, and it needs nothing of Node's internals.
140
+ for (let chunk = incoming.read() as Buffer | string | null; chunk !== null; chunk = incoming.read()) {
140
141
  bodyChunks.push(toBufferChunk(chunk, incoming.readableEncoding))
141
142
  }
142
143
  const buffer = bodyChunks.length === 1 ? bodyChunks[0] : Buffer.concat(bodyChunks)