@mushinjs/plugin-storage 0.2.0-next.20261113103537 → 0.2.0-next.20261113105814
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.d.ts +24 -5
- package/dist/index.js +49 -13
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +56 -0
- package/dist/layout.js +193 -0
- package/dist/layout.js.map +1 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LAYOUT_VERSION, LayoutDirs, MigrationResult, Move, Root, childrenOf, dataFile, layoutVersion, locate, migrateLayout, pathOf } from "./layout.js";
|
|
2
|
+
import { Context, EnvFile, Service, z } from "@mushinjs/core";
|
|
2
3
|
declare module '@mushinjs/core' {
|
|
3
4
|
interface Context {
|
|
4
5
|
storage: Storage;
|
|
5
6
|
}
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
|
-
* JSON documents under the
|
|
9
|
-
*
|
|
9
|
+
* JSON documents under the instance directory. Plugins name documents by key, such as `flows/abc`;
|
|
10
|
+
* `layout.ts` decides the file (`data/plugins/flows/documents/abc.json`). Logs use `.jsonl` files
|
|
11
|
+
* through `append` and `tail`.
|
|
10
12
|
*/
|
|
11
13
|
export declare class Storage extends Service {
|
|
12
14
|
static Config: z.ZodObject<{
|
|
13
15
|
dir: z.ZodDefault<z.ZodString>;
|
|
16
|
+
logs: z.ZodDefault<z.ZodString>;
|
|
17
|
+
cache: z.ZodDefault<z.ZodString>;
|
|
14
18
|
}, z.core.$strip>;
|
|
19
|
+
/** The data directory: the one folder to back up. */
|
|
15
20
|
readonly dir: string;
|
|
21
|
+
readonly dirs: LayoutDirs;
|
|
22
|
+
/** The instance's `.env` file, where secrets live. Documents hold `${NAME}` references to them. */
|
|
23
|
+
readonly secrets: EnvFile;
|
|
24
|
+
private readonly baseDir;
|
|
16
25
|
constructor(ctx: Context, config: z.infer<typeof Storage.Config>);
|
|
17
26
|
private file;
|
|
27
|
+
/** Absolute path of a key without extension; also the folder for files a plugin keeps under that key. */
|
|
28
|
+
path(key: string): string;
|
|
29
|
+
/** Names directly under a key prefix, documents or folders, such as the bots under `messages`. */
|
|
30
|
+
children(prefix: string): Promise<string[]>;
|
|
18
31
|
get<T>(key: string): Promise<T | undefined>;
|
|
19
32
|
set(key: string, value: unknown): Promise<void>;
|
|
20
33
|
delete(key: string): Promise<void>;
|
|
@@ -24,7 +37,13 @@ export declare class Storage extends Service {
|
|
|
24
37
|
append(key: string, value: unknown): Promise<void>;
|
|
25
38
|
/** The last `limit` entries of `<key>.jsonl`, oldest first. */
|
|
26
39
|
tail<T>(key: string, limit: number): Promise<T[]>;
|
|
27
|
-
/** Path of a storage key relative to the
|
|
40
|
+
/** Path of a storage key relative to the instance directory, for display. */
|
|
28
41
|
describe(key: string): string;
|
|
29
42
|
}
|
|
30
|
-
|
|
43
|
+
/** The data, log and cache directories of an instance, from the storage plugin's config. */
|
|
44
|
+
export declare function resolveDirs(baseDir: string, config?: {
|
|
45
|
+
dir?: string;
|
|
46
|
+
logs?: string;
|
|
47
|
+
cache?: string;
|
|
48
|
+
}): LayoutDirs;
|
|
49
|
+
export { LAYOUT_VERSION, LayoutDirs, MigrationResult, Move, Root, Storage as default, childrenOf, dataFile, layoutVersion, locate, migrateLayout, pathOf };
|
package/dist/index.js
CHANGED
|
@@ -1,25 +1,53 @@
|
|
|
1
|
+
import { LAYOUT_VERSION, childrenOf, dataFile, layoutVersion, locate, migrateLayout, pathOf } from "./layout.js";
|
|
1
2
|
import { appendFile, mkdir, readFile, readdir, rm } from "node:fs/promises";
|
|
2
3
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
3
|
-
import { Service, field, readJson, writeJsonAtomic, z } from "@mushinjs/core";
|
|
4
|
+
import { EnvFile, Service, field, readJson, writeJsonAtomic, z } from "@mushinjs/core";
|
|
4
5
|
// src/index.ts
|
|
5
6
|
const keyPattern = /^[A-Za-z0-9_-]+(\/[A-Za-z0-9_.-]+)*$/;
|
|
6
7
|
/**
|
|
7
|
-
* JSON documents under the
|
|
8
|
-
*
|
|
8
|
+
* JSON documents under the instance directory. Plugins name documents by key, such as `flows/abc`;
|
|
9
|
+
* `layout.ts` decides the file (`data/plugins/flows/documents/abc.json`). Logs use `.jsonl` files
|
|
10
|
+
* through `append` and `tail`.
|
|
9
11
|
*/
|
|
10
12
|
var Storage = class extends Service {
|
|
11
|
-
static Config = z.object({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
static Config = z.object({
|
|
14
|
+
dir: z.string().default("data").meta(field("Data directory", "Relative to the instance directory", { "zh-CN": {
|
|
15
|
+
title: "数据目录",
|
|
16
|
+
description: "相对于实例目录"
|
|
17
|
+
} })),
|
|
18
|
+
logs: z.string().default("logs").meta(field("Log directory", "Relative to the instance directory; safe to delete", { "zh-CN": {
|
|
19
|
+
title: "日志目录",
|
|
20
|
+
description: "相对于实例目录,可以随时删除"
|
|
21
|
+
} })),
|
|
22
|
+
cache: z.string().default("cache").meta(field("Cache directory", "Relative to the instance directory; safe to delete", { "zh-CN": {
|
|
23
|
+
title: "缓存目录",
|
|
24
|
+
description: "相对于实例目录,可以随时删除"
|
|
25
|
+
} }))
|
|
26
|
+
});
|
|
27
|
+
/** The data directory: the one folder to back up. */
|
|
15
28
|
dir;
|
|
29
|
+
dirs;
|
|
30
|
+
/** The instance's `.env` file, where secrets live. Documents hold `${NAME}` references to them. */
|
|
31
|
+
secrets;
|
|
32
|
+
baseDir;
|
|
16
33
|
constructor(ctx, config) {
|
|
17
34
|
super(ctx, "storage");
|
|
18
|
-
this.
|
|
35
|
+
this.baseDir = ctx.baseDir ?? process.cwd();
|
|
36
|
+
this.dirs = resolveDirs(this.baseDir, config);
|
|
37
|
+
this.dir = this.dirs.data;
|
|
38
|
+
this.secrets = new EnvFile(join(this.baseDir, ".env")).loadSync();
|
|
19
39
|
}
|
|
20
40
|
file(key, ext) {
|
|
41
|
+
return this.path(key) + ext;
|
|
42
|
+
}
|
|
43
|
+
/** Absolute path of a key without extension; also the folder for files a plugin keeps under that key. */
|
|
44
|
+
path(key) {
|
|
21
45
|
if (!keyPattern.test(key) || key.split("/").includes("..")) throw new Error(`invalid storage key "${key}"`);
|
|
22
|
-
return
|
|
46
|
+
return pathOf(this.dirs, key);
|
|
47
|
+
}
|
|
48
|
+
/** Names directly under a key prefix, documents or folders, such as the bots under `messages`. */
|
|
49
|
+
children(prefix) {
|
|
50
|
+
return childrenOf(this.dirs, prefix);
|
|
23
51
|
}
|
|
24
52
|
async get(key) {
|
|
25
53
|
return readJson(this.file(key, ".json"));
|
|
@@ -32,7 +60,7 @@ var Storage = class extends Service {
|
|
|
32
60
|
}
|
|
33
61
|
/** Keys of the JSON documents directly under a prefix, such as `agent`. */
|
|
34
62
|
async list(prefix) {
|
|
35
|
-
const dir =
|
|
63
|
+
const dir = dirname(this.path(`${prefix}/_`));
|
|
36
64
|
return (await readdir(dir).catch(() => [])).filter((name) => name.endsWith(".json")).map((name) => `${prefix}/${name.slice(0, -5)}`).sort();
|
|
37
65
|
}
|
|
38
66
|
/** Appends one JSON line to `<key>.jsonl`. */
|
|
@@ -58,11 +86,19 @@ var Storage = class extends Service {
|
|
|
58
86
|
}
|
|
59
87
|
});
|
|
60
88
|
}
|
|
61
|
-
/** Path of a storage key relative to the
|
|
89
|
+
/** Path of a storage key relative to the instance directory, for display. */
|
|
62
90
|
describe(key) {
|
|
63
|
-
return relative(this.
|
|
91
|
+
return relative(this.baseDir, this.file(key, ".json")).split(sep).join("/");
|
|
64
92
|
}
|
|
65
93
|
};
|
|
66
|
-
|
|
94
|
+
/** The data, log and cache directories of an instance, from the storage plugin's config. */
|
|
95
|
+
function resolveDirs(baseDir, config = {}) {
|
|
96
|
+
return {
|
|
97
|
+
data: resolve(baseDir, config.dir ?? "data"),
|
|
98
|
+
logs: resolve(baseDir, config.logs ?? "logs"),
|
|
99
|
+
cache: resolve(baseDir, config.cache ?? "cache")
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export { LAYOUT_VERSION, Storage, Storage as default, childrenOf, dataFile, layoutVersion, locate, migrateLayout, pathOf, resolveDirs };
|
|
67
103
|
|
|
68
104
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { appendFile, mkdir, readdir, readFile, rm } from 'node:fs/promises'\nimport { dirname, join, relative, resolve, sep } from 'node:path'\nimport { Context, field, readJson, Service, writeJsonAtomic, z } from '@mushinjs/core'\n\ndeclare module '@mushinjs/core' {\n interface Context {\n storage: Storage\n }\n}\n\nconst keyPattern = /^[A-Za-z0-9_-]+(\\/[A-Za-z0-9_.-]+)*$/\n\n/**\n * JSON documents under the
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { appendFile, mkdir, readdir, readFile, rm } from 'node:fs/promises'\nimport { dirname, join, relative, resolve, sep } from 'node:path'\nimport { Context, EnvFile, field, readJson, Service, writeJsonAtomic, z } from '@mushinjs/core'\nimport { childrenOf, type LayoutDirs, pathOf } from './layout'\n\nexport * from './layout'\n\ndeclare module '@mushinjs/core' {\n interface Context {\n storage: Storage\n }\n}\n\nconst keyPattern = /^[A-Za-z0-9_-]+(\\/[A-Za-z0-9_.-]+)*$/\n\n/**\n * JSON documents under the instance directory. Plugins name documents by key, such as `flows/abc`;\n * `layout.ts` decides the file (`data/plugins/flows/documents/abc.json`). Logs use `.jsonl` files\n * through `append` and `tail`.\n */\nexport class Storage extends Service {\n static Config = z.object({\n dir: z.string().default('data').meta(field('Data directory', 'Relative to the instance directory', {\n 'zh-CN': { title: '数据目录', description: '相对于实例目录' },\n })),\n logs: z.string().default('logs').meta(field('Log directory', 'Relative to the instance directory; safe to delete', {\n 'zh-CN': { title: '日志目录', description: '相对于实例目录,可以随时删除' },\n })),\n cache: z.string().default('cache').meta(field('Cache directory', 'Relative to the instance directory; safe to delete', {\n 'zh-CN': { title: '缓存目录', description: '相对于实例目录,可以随时删除' },\n })),\n })\n\n /** The data directory: the one folder to back up. */\n readonly dir: string\n readonly dirs: LayoutDirs\n /** The instance's `.env` file, where secrets live. Documents hold `${NAME}` references to them. */\n readonly secrets: EnvFile\n private readonly baseDir: string\n\n constructor(ctx: Context, config: z.infer<typeof Storage.Config>) {\n super(ctx, 'storage')\n this.baseDir = ctx.baseDir ?? process.cwd()\n this.dirs = resolveDirs(this.baseDir, config)\n this.dir = this.dirs.data\n this.secrets = new EnvFile(join(this.baseDir, '.env')).loadSync()\n }\n\n private file(key: string, ext: '.json' | '.jsonl') {\n return this.path(key) + ext\n }\n\n /** Absolute path of a key without extension; also the folder for files a plugin keeps under that key. */\n path(key: string) {\n if (!keyPattern.test(key) || key.split('/').includes('..')) {\n throw new Error(`invalid storage key \"${key}\"`)\n }\n return pathOf(this.dirs, key)\n }\n\n /** Names directly under a key prefix, documents or folders, such as the bots under `messages`. */\n children(prefix: string) {\n return childrenOf(this.dirs, prefix)\n }\n\n async get<T>(key: string): Promise<T | undefined> {\n return readJson<T>(this.file(key, '.json'))\n }\n\n async set(key: string, value: unknown) {\n return writeJsonAtomic(this.file(key, '.json'), value)\n }\n\n async delete(key: string) {\n await rm(this.file(key, '.json'), { force: true })\n }\n\n /** Keys of the JSON documents directly under a prefix, such as `agent`. */\n async list(prefix: string) {\n const dir = dirname(this.path(`${prefix}/_`))\n const names = await readdir(dir).catch(() => [] as string[])\n return names\n .filter((name) => name.endsWith('.json'))\n .map((name) => `${prefix}/${name.slice(0, -'.json'.length)}`)\n .sort()\n }\n\n /** Appends one JSON line to `<key>.jsonl`. */\n async append(key: string, value: unknown) {\n const file = this.file(key, '.jsonl')\n await mkdir(dirname(file), { recursive: true })\n await appendFile(file, JSON.stringify(value) + '\\n', 'utf8')\n }\n\n /** The last `limit` entries of `<key>.jsonl`, oldest first. */\n async tail<T>(key: string, limit: number): Promise<T[]> {\n let text: string\n try {\n text = await readFile(this.file(key, '.jsonl'), 'utf8')\n } catch (error: any) {\n if (error?.code === 'ENOENT') return []\n throw error\n }\n const lines = text.split('\\n').filter(Boolean)\n return lines.slice(-limit).flatMap((line) => {\n try {\n return [JSON.parse(line) as T]\n } catch {\n return []\n }\n })\n }\n\n /** Path of a storage key relative to the instance directory, for display. */\n describe(key: string) {\n return relative(this.baseDir, this.file(key, '.json')).split(sep).join('/')\n }\n}\n\n/** The data, log and cache directories of an instance, from the storage plugin's config. */\nexport function resolveDirs(baseDir: string, config: { dir?: string; logs?: string; cache?: string } = {}): LayoutDirs {\n return {\n data: resolve(baseDir, config.dir ?? 'data'),\n logs: resolve(baseDir, config.logs ?? 'logs'),\n cache: resolve(baseDir, config.cache ?? 'cache'),\n }\n}\n\nexport default Storage\n"],"mappings":"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"}
|
package/dist/layout.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where storage keys live on disk. Keys stay what plugins already use (`bots`, `flows/<id>`,
|
|
3
|
+
* `messages/<bot>/<day>`); this table decides the folder, so the layout is defined in one place:
|
|
4
|
+
*
|
|
5
|
+
* data/core/ small documents of the core plugins: bots, providers, permissions, sign-in
|
|
6
|
+
* data/bots/<id>/messages/ one bot's message log
|
|
7
|
+
* data/agents/ Agent sessions, skills and settings
|
|
8
|
+
* data/plugins/<name>/ everything else, one folder per plugin
|
|
9
|
+
* logs/bots/<id>/ bot logs, safe to delete
|
|
10
|
+
*
|
|
11
|
+
* `data/mushin.db` (the database) and `data/backups/` sit beside these folders.
|
|
12
|
+
*/
|
|
13
|
+
export type Root = 'data' | 'logs' | 'cache';
|
|
14
|
+
/** Layout version written to `data/layout.json`. Version 1 is the flat layout: `data/<key>.json`. */
|
|
15
|
+
export declare const LAYOUT_VERSION = 2;
|
|
16
|
+
/** The root and path segments (without extension) of a key. */
|
|
17
|
+
export declare function locate(key: string): {
|
|
18
|
+
root: Root;
|
|
19
|
+
path: string[];
|
|
20
|
+
};
|
|
21
|
+
export interface LayoutDirs {
|
|
22
|
+
data: string;
|
|
23
|
+
logs: string;
|
|
24
|
+
cache: string;
|
|
25
|
+
}
|
|
26
|
+
/** Absolute path of a key, without extension. */
|
|
27
|
+
export declare function pathOf(dirs: LayoutDirs, key: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Names directly under a key prefix. Under `messages` (a `:bot` segment) these are the bots that
|
|
30
|
+
* have a message folder; under `agent/skills` they are the names in that folder.
|
|
31
|
+
*/
|
|
32
|
+
export declare function childrenOf(dirs: LayoutDirs, prefix: string): Promise<string[]>;
|
|
33
|
+
export interface Move {
|
|
34
|
+
from: string;
|
|
35
|
+
to: string;
|
|
36
|
+
}
|
|
37
|
+
export interface MigrationResult {
|
|
38
|
+
/** The layout version found, or `undefined` for an empty or new data folder. */
|
|
39
|
+
from?: number;
|
|
40
|
+
moves: Move[];
|
|
41
|
+
/** Where the copy of the old data folder went. */
|
|
42
|
+
backup?: string;
|
|
43
|
+
}
|
|
44
|
+
/** The current layout version of a data folder: `undefined` when it holds nothing yet. */
|
|
45
|
+
export declare function layoutVersion(dataDir: string): Promise<number | undefined>;
|
|
46
|
+
/**
|
|
47
|
+
* Moves a data folder in the flat layout (version 1) to the current one. It copies the folder to
|
|
48
|
+
* `data/backups/<time>-layout-1/` first. Running it again changes nothing. With `dryRun`, it only
|
|
49
|
+
* reports the moves.
|
|
50
|
+
*/
|
|
51
|
+
export declare function migrateLayout(dirs: LayoutDirs, options?: {
|
|
52
|
+
dryRun?: boolean;
|
|
53
|
+
now?: Date;
|
|
54
|
+
}): Promise<MigrationResult>;
|
|
55
|
+
/** The `.json` file of a key that lives in the data directory, for tools that run without a started instance. */
|
|
56
|
+
export declare function dataFile(dataDir: string, key: string): string;
|
package/dist/layout.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { cp, mkdir, readdir, rename, rmdir, stat } from "node:fs/promises";
|
|
2
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
3
|
+
import { readJson, writeJsonAtomic } from "@mushinjs/core";
|
|
4
|
+
// src/layout.ts
|
|
5
|
+
/** Layout version written to `data/layout.json`. Version 1 is the flat layout: `data/<key>.json`. */
|
|
6
|
+
const LAYOUT_VERSION = 2;
|
|
7
|
+
const LAYOUT_FILE = "layout.json";
|
|
8
|
+
const rule = (key, to) => {
|
|
9
|
+
const at = to.indexOf(":");
|
|
10
|
+
return {
|
|
11
|
+
key: key.split("/"),
|
|
12
|
+
root: to.slice(0, at),
|
|
13
|
+
path: to.slice(at + 1).split("/")
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
const RULES = [
|
|
17
|
+
rule("bots", "data:core/bots"),
|
|
18
|
+
rule("providers", "data:core/providers"),
|
|
19
|
+
rule("permissions", "data:core/permissions"),
|
|
20
|
+
rule("auth", "data:core/auth"),
|
|
21
|
+
rule("messages/:bot/*", "data:bots/:bot/messages/*"),
|
|
22
|
+
rule("logs/:bot/*", "logs:bots/:bot/*"),
|
|
23
|
+
rule("agent/skills/*", "data:agents/skills/*"),
|
|
24
|
+
rule("agent/settings/*", "data:agents/settings/*"),
|
|
25
|
+
rule("agent/*", "data:agents/sessions/*"),
|
|
26
|
+
rule("flows/*", "data:plugins/flows/documents/*"),
|
|
27
|
+
rule("flows-tests/*", "data:plugins/flows/tests/*"),
|
|
28
|
+
rule("flows-tables", "data:plugins/flows/tables"),
|
|
29
|
+
rule("flows-usage", "data:plugins/flows/usage"),
|
|
30
|
+
rule("demo", "data:plugins/demo/state"),
|
|
31
|
+
rule(":plugin/*", "data:plugins/:plugin/*"),
|
|
32
|
+
rule(":plugin", "data:plugins/:plugin/index")
|
|
33
|
+
];
|
|
34
|
+
/** The root and path segments (without extension) of a key. */
|
|
35
|
+
function locate(key) {
|
|
36
|
+
const segments = key.split("/");
|
|
37
|
+
for (const { key: pattern, root, path } of RULES) {
|
|
38
|
+
const bound = match(pattern, segments);
|
|
39
|
+
if (bound) return {
|
|
40
|
+
root,
|
|
41
|
+
path: path.flatMap((part) => part === "*" ? bound.rest : part.startsWith(":") ? [bound.params[part]] : [part])
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`no storage location for "${key}"`);
|
|
45
|
+
}
|
|
46
|
+
function match(pattern, segments) {
|
|
47
|
+
const params = {};
|
|
48
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
49
|
+
const part = pattern[i];
|
|
50
|
+
if (part === "*") {
|
|
51
|
+
const rest = segments.slice(i);
|
|
52
|
+
return rest.length ? {
|
|
53
|
+
params,
|
|
54
|
+
rest
|
|
55
|
+
} : void 0;
|
|
56
|
+
}
|
|
57
|
+
const segment = segments[i];
|
|
58
|
+
if (segment === void 0) return void 0;
|
|
59
|
+
if (part.startsWith(":")) params[part] = segment;
|
|
60
|
+
else if (part !== segment) return void 0;
|
|
61
|
+
}
|
|
62
|
+
return segments.length === pattern.length ? {
|
|
63
|
+
params,
|
|
64
|
+
rest: []
|
|
65
|
+
} : void 0;
|
|
66
|
+
}
|
|
67
|
+
/** Absolute path of a key, without extension. */
|
|
68
|
+
function pathOf(dirs, key) {
|
|
69
|
+
const { root, path } = locate(key);
|
|
70
|
+
return join(dirs[root], ...path);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Names directly under a key prefix. Under `messages` (a `:bot` segment) these are the bots that
|
|
74
|
+
* have a message folder; under `agent/skills` they are the names in that folder.
|
|
75
|
+
*/
|
|
76
|
+
async function childrenOf(dirs, prefix) {
|
|
77
|
+
const segments = prefix.split("/");
|
|
78
|
+
for (const { key: pattern, root, path } of RULES) {
|
|
79
|
+
const next = pattern[segments.length];
|
|
80
|
+
const bound = next ? match(pattern.slice(0, segments.length), segments) : void 0;
|
|
81
|
+
if (!next || !bound) continue;
|
|
82
|
+
if (next.startsWith(":")) {
|
|
83
|
+
const at = path.indexOf(next);
|
|
84
|
+
const fill = (parts) => parts.map((part) => bound.params[part] ?? part);
|
|
85
|
+
const parent = join(dirs[root], ...fill(path.slice(0, at)));
|
|
86
|
+
const suffix = fill(path.slice(at + 1)).filter((part) => part !== "*");
|
|
87
|
+
const kept = [];
|
|
88
|
+
for (const name of await readdir(parent).catch(() => [])) if (await exists(join(parent, name, ...suffix))) kept.push(name);
|
|
89
|
+
return kept;
|
|
90
|
+
}
|
|
91
|
+
if (next === "*") {
|
|
92
|
+
const at = path.indexOf("*");
|
|
93
|
+
const fill = path.slice(0, at).map((part) => bound.params[part] ?? part);
|
|
94
|
+
return readdir(join(dirs[root], ...fill)).catch(() => []);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
const KEEP = /* @__PURE__ */ new Set([
|
|
100
|
+
"mushin.db",
|
|
101
|
+
"mushin.db-journal",
|
|
102
|
+
"mushin.db-wal",
|
|
103
|
+
"mushin.db-shm",
|
|
104
|
+
"backups",
|
|
105
|
+
LAYOUT_FILE
|
|
106
|
+
]);
|
|
107
|
+
async function exists(path) {
|
|
108
|
+
return stat(path).then(() => true, () => false);
|
|
109
|
+
}
|
|
110
|
+
async function filesUnder(dir) {
|
|
111
|
+
const files = [];
|
|
112
|
+
for (const entry of await readdir(dir, { withFileTypes: true }).catch(() => [])) {
|
|
113
|
+
const full = join(dir, entry.name);
|
|
114
|
+
if (entry.isDirectory()) files.push(...await filesUnder(full));
|
|
115
|
+
else files.push(full);
|
|
116
|
+
}
|
|
117
|
+
return files;
|
|
118
|
+
}
|
|
119
|
+
/** The current layout version of a data folder: `undefined` when it holds nothing yet. */
|
|
120
|
+
async function layoutVersion(dataDir) {
|
|
121
|
+
const marker = await readJson(join(dataDir, LAYOUT_FILE));
|
|
122
|
+
if (marker?.version) return marker.version;
|
|
123
|
+
return (await readdir(dataDir).catch(() => [])).filter((name) => !KEEP.has(name)).length ? 1 : void 0;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Moves a data folder in the flat layout (version 1) to the current one. It copies the folder to
|
|
127
|
+
* `data/backups/<time>-layout-1/` first. Running it again changes nothing. With `dryRun`, it only
|
|
128
|
+
* reports the moves.
|
|
129
|
+
*/
|
|
130
|
+
async function migrateLayout(dirs, options = {}) {
|
|
131
|
+
const from = await layoutVersion(dirs.data);
|
|
132
|
+
if (from === 2) return {
|
|
133
|
+
from,
|
|
134
|
+
moves: []
|
|
135
|
+
};
|
|
136
|
+
if (from !== void 0 && from > 2) throw new Error(`the data folder uses layout version ${from}, newer than this Mushin.js understands (2); upgrade Mushin.js`);
|
|
137
|
+
const moves = [];
|
|
138
|
+
if (from === 1) for (const file of await filesUnder(dirs.data)) {
|
|
139
|
+
const rel = relative(dirs.data, file).split(sep);
|
|
140
|
+
if (KEEP.has(rel[0])) continue;
|
|
141
|
+
const name = rel.at(-1);
|
|
142
|
+
const dot = name.lastIndexOf(".");
|
|
143
|
+
const ext = dot > 0 && /^\.jsonl?$/.test(name.slice(dot)) ? name.slice(dot) : "";
|
|
144
|
+
const to = pathOf(dirs, [...rel.slice(0, -1), ext ? name.slice(0, dot) : name].join("/")) + ext;
|
|
145
|
+
if (to !== file) moves.push({
|
|
146
|
+
from: file,
|
|
147
|
+
to
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (options.dryRun) return {
|
|
151
|
+
from,
|
|
152
|
+
moves
|
|
153
|
+
};
|
|
154
|
+
let backup;
|
|
155
|
+
if (moves.length) {
|
|
156
|
+
const stamp = (options.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
157
|
+
backup = join(dirs.data, "backups", `${stamp}-layout-${from}`);
|
|
158
|
+
await mkdir(backup, { recursive: true });
|
|
159
|
+
for (const name of await readdir(dirs.data)) {
|
|
160
|
+
if (name === "backups") continue;
|
|
161
|
+
await cp(join(dirs.data, name), join(backup, name), {
|
|
162
|
+
recursive: true,
|
|
163
|
+
preserveTimestamps: true
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
for (const { from: source, to } of moves) {
|
|
167
|
+
if (await exists(to)) throw new Error(`cannot move ${source}: ${to} already exists`);
|
|
168
|
+
await mkdir(dirname(to), { recursive: true });
|
|
169
|
+
await rename(source, to);
|
|
170
|
+
}
|
|
171
|
+
const emptied = new Set(moves.flatMap(({ from: source }) => {
|
|
172
|
+
const parents = [];
|
|
173
|
+
for (let dir = dirname(source); dir !== dirs.data && dir.startsWith(dirs.data); dir = dirname(dir)) parents.push(dir);
|
|
174
|
+
return parents;
|
|
175
|
+
}));
|
|
176
|
+
for (const dir of [...emptied].sort((a, b) => b.length - a.length)) await rmdir(dir).catch(() => {});
|
|
177
|
+
}
|
|
178
|
+
await writeJsonAtomic(join(dirs.data, LAYOUT_FILE), { version: 2 });
|
|
179
|
+
return {
|
|
180
|
+
from,
|
|
181
|
+
moves,
|
|
182
|
+
backup
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/** The `.json` file of a key that lives in the data directory, for tools that run without a started instance. */
|
|
186
|
+
function dataFile(dataDir, key) {
|
|
187
|
+
const { root, path } = locate(key);
|
|
188
|
+
if (root !== "data") throw new Error(`"${key}" is not stored in the data directory`);
|
|
189
|
+
return join(dataDir, ...path) + ".json";
|
|
190
|
+
}
|
|
191
|
+
export { LAYOUT_VERSION, childrenOf, dataFile, layoutVersion, locate, migrateLayout, pathOf };
|
|
192
|
+
|
|
193
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.js","names":[],"sources":["../src/layout.ts"],"sourcesContent":["import { cp, mkdir, readdir, rename, rmdir, stat } from 'node:fs/promises'\nimport { dirname, join, relative, sep } from 'node:path'\nimport { readJson, writeJsonAtomic } from '@mushinjs/core'\n\n/**\n * Where storage keys live on disk. Keys stay what plugins already use (`bots`, `flows/<id>`,\n * `messages/<bot>/<day>`); this table decides the folder, so the layout is defined in one place:\n *\n * data/core/ small documents of the core plugins: bots, providers, permissions, sign-in\n * data/bots/<id>/messages/ one bot's message log\n * data/agents/ Agent sessions, skills and settings\n * data/plugins/<name>/ everything else, one folder per plugin\n * logs/bots/<id>/ bot logs, safe to delete\n *\n * `data/mushin.db` (the database) and `data/backups/` sit beside these folders.\n */\n\nexport type Root = 'data' | 'logs' | 'cache'\n\n/** Layout version written to `data/layout.json`. Version 1 is the flat layout: `data/<key>.json`. */\nexport const LAYOUT_VERSION = 2\nconst LAYOUT_FILE = 'layout.json'\n\ninterface Rule {\n /** Key segments: a literal, `:name` for one segment, or `*` (last) for one or more. */\n key: string[]\n root: Root\n path: string[]\n}\n\nconst rule = (key: string, to: string): Rule => {\n const at = to.indexOf(':')\n return { key: key.split('/'), root: to.slice(0, at) as Root, path: to.slice(at + 1).split('/') }\n}\n\n// First match wins.\nconst RULES: Rule[] = [\n rule('bots', 'data:core/bots'),\n rule('providers', 'data:core/providers'),\n rule('permissions', 'data:core/permissions'),\n rule('auth', 'data:core/auth'),\n rule('messages/:bot/*', 'data:bots/:bot/messages/*'),\n rule('logs/:bot/*', 'logs:bots/:bot/*'),\n rule('agent/skills/*', 'data:agents/skills/*'),\n rule('agent/settings/*', 'data:agents/settings/*'),\n rule('agent/*', 'data:agents/sessions/*'),\n rule('flows/*', 'data:plugins/flows/documents/*'),\n rule('flows-tests/*', 'data:plugins/flows/tests/*'),\n rule('flows-tables', 'data:plugins/flows/tables'),\n rule('flows-usage', 'data:plugins/flows/usage'),\n rule('demo', 'data:plugins/demo/state'),\n // Any other key belongs to the plugin its first segment names.\n rule(':plugin/*', 'data:plugins/:plugin/*'),\n rule(':plugin', 'data:plugins/:plugin/index'),\n]\n\n/** The root and path segments (without extension) of a key. */\nexport function locate(key: string): { root: Root; path: string[] } {\n const segments = key.split('/')\n for (const { key: pattern, root, path } of RULES) {\n const bound = match(pattern, segments)\n if (bound) return { root, path: path.flatMap((part) => part === '*' ? bound.rest : part.startsWith(':') ? [bound.params[part]!] : [part]) }\n }\n throw new Error(`no storage location for \"${key}\"`)\n}\n\nfunction match(pattern: string[], segments: string[]) {\n const params: Record<string, string> = {}\n for (let i = 0; i < pattern.length; i++) {\n const part = pattern[i]!\n if (part === '*') {\n const rest = segments.slice(i)\n return rest.length ? { params, rest } : undefined\n }\n const segment = segments[i]\n if (segment === undefined) return undefined\n if (part.startsWith(':')) params[part] = segment\n else if (part !== segment) return undefined\n }\n return segments.length === pattern.length ? { params, rest: [] } : undefined\n}\n\nexport interface LayoutDirs {\n data: string\n logs: string\n cache: string\n}\n\n/** Absolute path of a key, without extension. */\nexport function pathOf(dirs: LayoutDirs, key: string) {\n const { root, path } = locate(key)\n return join(dirs[root], ...path)\n}\n\n/**\n * Names directly under a key prefix. Under `messages` (a `:bot` segment) these are the bots that\n * have a message folder; under `agent/skills` they are the names in that folder.\n */\nexport async function childrenOf(dirs: LayoutDirs, prefix: string): Promise<string[]> {\n const segments = prefix.split('/')\n for (const { key: pattern, root, path } of RULES) {\n const next = pattern[segments.length]\n const bound = next ? match(pattern.slice(0, segments.length), segments) : undefined\n if (!next || !bound) continue\n if (next.startsWith(':')) {\n // The names sit where the parameter does in the path; keep those that have the rest of it.\n const at = path.indexOf(next)\n const fill = (parts: string[]) => parts.map((part) => bound.params[part] ?? part)\n const parent = join(dirs[root], ...fill(path.slice(0, at)))\n const suffix = fill(path.slice(at + 1)).filter((part) => part !== '*')\n const kept: string[] = []\n for (const name of await readdir(parent).catch(() => [] as string[])) {\n if (await exists(join(parent, name, ...suffix))) kept.push(name)\n }\n return kept\n }\n if (next === '*') {\n const at = path.indexOf('*')\n const fill = path.slice(0, at).map((part) => bound.params[part] ?? part)\n return readdir(join(dirs[root], ...fill)).catch(() => [] as string[])\n }\n }\n return []\n}\n\nexport interface Move {\n from: string\n to: string\n}\n\nexport interface MigrationResult {\n /** The layout version found, or `undefined` for an empty or new data folder. */\n from?: number\n moves: Move[]\n /** Where the copy of the old data folder went. */\n backup?: string\n}\n\nconst KEEP = new Set(['mushin.db', 'mushin.db-journal', 'mushin.db-wal', 'mushin.db-shm', 'backups', LAYOUT_FILE])\n\nasync function exists(path: string) {\n return stat(path).then(() => true, () => false)\n}\n\nasync function filesUnder(dir: string): Promise<string[]> {\n const files: string[] = []\n for (const entry of await readdir(dir, { withFileTypes: true }).catch(() => [])) {\n const full = join(dir, entry.name)\n if (entry.isDirectory()) files.push(...await filesUnder(full))\n else files.push(full)\n }\n return files\n}\n\n/** The current layout version of a data folder: `undefined` when it holds nothing yet. */\nexport async function layoutVersion(dataDir: string) {\n const marker = await readJson<{ version?: number }>(join(dataDir, LAYOUT_FILE))\n if (marker?.version) return marker.version\n const names = (await readdir(dataDir).catch(() => [] as string[])).filter((name) => !KEEP.has(name))\n return names.length ? 1 : undefined\n}\n\n/**\n * Moves a data folder in the flat layout (version 1) to the current one. It copies the folder to\n * `data/backups/<time>-layout-1/` first. Running it again changes nothing. With `dryRun`, it only\n * reports the moves.\n */\nexport async function migrateLayout(dirs: LayoutDirs, options: { dryRun?: boolean; now?: Date } = {}): Promise<MigrationResult> {\n const from = await layoutVersion(dirs.data)\n if (from === LAYOUT_VERSION) return { from, moves: [] }\n if (from !== undefined && from > LAYOUT_VERSION) {\n throw new Error(`the data folder uses layout version ${from}, newer than this Mushin.js understands (${LAYOUT_VERSION}); upgrade Mushin.js`)\n }\n const moves: Move[] = []\n if (from === 1) {\n for (const file of await filesUnder(dirs.data)) {\n const rel = relative(dirs.data, file).split(sep)\n if (KEEP.has(rel[0]!)) continue\n const name = rel.at(-1)!\n const dot = name.lastIndexOf('.')\n // `.json` and `.jsonl` documents are keys; other files (such as knowledge base sources) keep their name.\n const ext = dot > 0 && /^\\.jsonl?$/.test(name.slice(dot)) ? name.slice(dot) : ''\n const key = [...rel.slice(0, -1), ext ? name.slice(0, dot) : name].join('/')\n const to = pathOf(dirs, key) + ext\n if (to !== file) moves.push({ from: file, to })\n }\n }\n if (options.dryRun) return { from, moves }\n\n let backup: string | undefined\n if (moves.length) {\n const stamp = (options.now ?? new Date()).toISOString().replace(/[:.]/g, '-')\n backup = join(dirs.data, 'backups', `${stamp}-layout-${from}`)\n await mkdir(backup, { recursive: true })\n for (const name of await readdir(dirs.data)) {\n if (name === 'backups') continue\n await cp(join(dirs.data, name), join(backup, name), { recursive: true, preserveTimestamps: true })\n }\n for (const { from: source, to } of moves) {\n if (await exists(to)) throw new Error(`cannot move ${source}: ${to} already exists`)\n await mkdir(dirname(to), { recursive: true })\n await rename(source, to)\n }\n // Remove the old folders the moves emptied, deepest first.\n const emptied = new Set(moves.flatMap(({ from: source }) => {\n const parents: string[] = []\n for (let dir = dirname(source); dir !== dirs.data && dir.startsWith(dirs.data); dir = dirname(dir)) parents.push(dir)\n return parents\n }))\n for (const dir of [...emptied].sort((a, b) => b.length - a.length)) await rmdir(dir).catch(() => {})\n }\n await writeJsonAtomic(join(dirs.data, LAYOUT_FILE), { version: LAYOUT_VERSION })\n return { from, moves, backup }\n}\n\n/** The `.json` file of a key that lives in the data directory, for tools that run without a started instance. */\nexport function dataFile(dataDir: string, key: string) {\n const { root, path } = locate(key)\n if (root !== 'data') throw new Error(`\"${key}\" is not stored in the data directory`)\n return join(dataDir, ...path) + '.json'\n}\n"],"mappings":";;;;;AAoBA,MAAa,iBAAiB;AAC9B,MAAM,cAAc;AASpB,MAAM,QAAQ,KAAa,OAAqB;CAC9C,MAAM,KAAK,GAAG,QAAQ,GAAG;CACzB,OAAO;EAAE,KAAK,IAAI,MAAM,GAAG;EAAG,MAAM,GAAG,MAAM,GAAG,EAAE;EAAW,MAAM,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG;CAAE;AACjG;AAGA,MAAM,QAAgB;CACpB,KAAK,QAAQ,gBAAgB;CAC7B,KAAK,aAAa,qBAAqB;CACvC,KAAK,eAAe,uBAAuB;CAC3C,KAAK,QAAQ,gBAAgB;CAC7B,KAAK,mBAAmB,2BAA2B;CACnD,KAAK,eAAe,kBAAkB;CACtC,KAAK,kBAAkB,sBAAsB;CAC7C,KAAK,oBAAoB,wBAAwB;CACjD,KAAK,WAAW,wBAAwB;CACxC,KAAK,WAAW,gCAAgC;CAChD,KAAK,iBAAiB,4BAA4B;CAClD,KAAK,gBAAgB,2BAA2B;CAChD,KAAK,eAAe,0BAA0B;CAC9C,KAAK,QAAQ,yBAAyB;CAEtC,KAAK,aAAa,wBAAwB;CAC1C,KAAK,WAAW,4BAA4B;AAC9C;;AAGA,SAAgB,OAAO,KAA6C;CAClE,MAAM,WAAW,IAAI,MAAM,GAAG;CAC9B,KAAK,MAAM,EAAE,KAAK,SAAS,MAAM,UAAU,OAAO;EAChD,MAAM,QAAQ,MAAM,SAAS,QAAQ;EACrC,IAAI,OAAO,OAAO;GAAE;GAAM,MAAM,KAAK,SAAS,SAAS,SAAS,MAAM,MAAM,OAAO,KAAK,WAAW,GAAG,IAAI,CAAC,MAAM,OAAO,KAAM,IAAI,CAAC,IAAI,CAAC;EAAE;CAC5I;CACA,MAAM,IAAI,MAAM,4BAA4B,IAAI,EAAE;AACpD;AAEA,SAAS,MAAM,SAAmB,UAAoB;CACpD,MAAM,SAAiC,CAAC;CACxC,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,OAAO,QAAQ;EACrB,IAAI,SAAS,KAAK;GAChB,MAAM,OAAO,SAAS,MAAM,CAAC;GAC7B,OAAO,KAAK,SAAS;IAAE;IAAQ;GAAK,IAAI,KAAA;EAC1C;EACA,MAAM,UAAU,SAAS;EACzB,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;EAClC,IAAI,KAAK,WAAW,GAAG,GAAG,OAAO,QAAQ;OACpC,IAAI,SAAS,SAAS,OAAO,KAAA;CACpC;CACA,OAAO,SAAS,WAAW,QAAQ,SAAS;EAAE;EAAQ,MAAM,CAAC;CAAE,IAAI,KAAA;AACrE;;AASA,SAAgB,OAAO,MAAkB,KAAa;CACpD,MAAM,EAAE,MAAM,SAAS,OAAO,GAAG;CACjC,OAAO,KAAK,KAAK,OAAO,GAAG,IAAI;AACjC;;;;;AAMA,eAAsB,WAAW,MAAkB,QAAmC;CACpF,MAAM,WAAW,OAAO,MAAM,GAAG;CACjC,KAAK,MAAM,EAAE,KAAK,SAAS,MAAM,UAAU,OAAO;EAChD,MAAM,OAAO,QAAQ,SAAS;EAC9B,MAAM,QAAQ,OAAO,MAAM,QAAQ,MAAM,GAAG,SAAS,MAAM,GAAG,QAAQ,IAAI,KAAA;EAC1E,IAAI,CAAC,QAAQ,CAAC,OAAO;EACrB,IAAI,KAAK,WAAW,GAAG,GAAG;GAExB,MAAM,KAAK,KAAK,QAAQ,IAAI;GAC5B,MAAM,QAAQ,UAAoB,MAAM,KAAK,SAAS,MAAM,OAAO,SAAS,IAAI;GAChF,MAAM,SAAS,KAAK,KAAK,OAAO,GAAG,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC;GAC1D,MAAM,SAAS,KAAK,KAAK,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,SAAS,SAAS,GAAG;GACrE,MAAM,OAAiB,CAAC;GACxB,KAAK,MAAM,QAAQ,MAAM,QAAQ,MAAM,CAAC,CAAC,YAAY,CAAC,CAAa,GACjE,IAAI,MAAM,OAAO,KAAK,QAAQ,MAAM,GAAG,MAAM,CAAC,GAAG,KAAK,KAAK,IAAI;GAEjE,OAAO;EACT;EACA,IAAI,SAAS,KAAK;GAChB,MAAM,KAAK,KAAK,QAAQ,GAAG;GAC3B,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK,SAAS,MAAM,OAAO,SAAS,IAAI;GACvE,OAAO,QAAQ,KAAK,KAAK,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAa;EACtE;CACF;CACA,OAAO,CAAC;AACV;AAeA,MAAM,uBAAO,IAAI,IAAI;CAAC;CAAa;CAAqB;CAAiB;CAAiB;CAAW;AAAW,CAAC;AAEjH,eAAe,OAAO,MAAc;CAClC,OAAO,KAAK,IAAI,CAAC,CAAC,WAAW,YAAY,KAAK;AAChD;AAEA,eAAe,WAAW,KAAgC;CACxD,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,SAAS,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,GAAG;EAC/E,MAAM,OAAO,KAAK,KAAK,MAAM,IAAI;EACjC,IAAI,MAAM,YAAY,GAAG,MAAM,KAAK,GAAG,MAAM,WAAW,IAAI,CAAC;OACxD,MAAM,KAAK,IAAI;CACtB;CACA,OAAO;AACT;;AAGA,eAAsB,cAAc,SAAiB;CACnD,MAAM,SAAS,MAAM,SAA+B,KAAK,SAAS,WAAW,CAAC;CAC9E,IAAI,QAAQ,SAAS,OAAO,OAAO;CAEnC,QADe,MAAM,QAAQ,OAAO,CAAC,CAAC,YAAY,CAAC,CAAa,EAAA,CAAG,QAAQ,SAAS,CAAC,KAAK,IAAI,IAAI,CACvF,CAAC,CAAC,SAAS,IAAI,KAAA;AAC5B;;;;;;AAOA,eAAsB,cAAc,MAAkB,UAA4C,CAAC,GAA6B;CAC9H,MAAM,OAAO,MAAM,cAAc,KAAK,IAAI;CAC1C,IAAI,SAAA,GAAyB,OAAO;EAAE;EAAM,OAAO,CAAC;CAAE;CACtD,IAAI,SAAS,KAAA,KAAa,OAAA,GACxB,MAAM,IAAI,MAAM,uCAAuC,KAAK,+DAA+E;CAE7I,MAAM,QAAgB,CAAC;CACvB,IAAI,SAAS,GACX,KAAK,MAAM,QAAQ,MAAM,WAAW,KAAK,IAAI,GAAG;EAC9C,MAAM,MAAM,SAAS,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,GAAG;EAC/C,IAAI,KAAK,IAAI,IAAI,EAAG,GAAG;EACvB,MAAM,OAAO,IAAI,GAAG,EAAE;EACtB,MAAM,MAAM,KAAK,YAAY,GAAG;EAEhC,MAAM,MAAM,MAAM,KAAK,aAAa,KAAK,KAAK,MAAM,GAAG,CAAC,IAAI,KAAK,MAAM,GAAG,IAAI;EAE9E,MAAM,KAAK,OAAO,MADN,CAAC,GAAG,IAAI,MAAM,GAAG,EAAE,GAAG,MAAM,KAAK,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,CAAC,KAAK,GAC9C,CAAC,IAAI;EAC/B,IAAI,OAAO,MAAM,MAAM,KAAK;GAAE,MAAM;GAAM;EAAG,CAAC;CAChD;CAEF,IAAI,QAAQ,QAAQ,OAAO;EAAE;EAAM;CAAM;CAEzC,IAAI;CACJ,IAAI,MAAM,QAAQ;EAChB,MAAM,SAAS,QAAQ,uBAAO,IAAI,KAAK,EAAA,CAAG,YAAY,CAAC,CAAC,QAAQ,SAAS,GAAG;EAC5E,SAAS,KAAK,KAAK,MAAM,WAAW,GAAG,MAAM,UAAU,MAAM;EAC7D,MAAM,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;EACvC,KAAK,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,GAAG;GAC3C,IAAI,SAAS,WAAW;GACxB,MAAM,GAAG,KAAK,KAAK,MAAM,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG;IAAE,WAAW;IAAM,oBAAoB;GAAK,CAAC;EACnG;EACA,KAAK,MAAM,EAAE,MAAM,QAAQ,QAAQ,OAAO;GACxC,IAAI,MAAM,OAAO,EAAE,GAAG,MAAM,IAAI,MAAM,eAAe,OAAO,IAAI,GAAG,gBAAgB;GACnF,MAAM,MAAM,QAAQ,EAAE,GAAG,EAAE,WAAW,KAAK,CAAC;GAC5C,MAAM,OAAO,QAAQ,EAAE;EACzB;EAEA,MAAM,UAAU,IAAI,IAAI,MAAM,SAAS,EAAE,MAAM,aAAa;GAC1D,MAAM,UAAoB,CAAC;GAC3B,KAAK,IAAI,MAAM,QAAQ,MAAM,GAAG,QAAQ,KAAK,QAAQ,IAAI,WAAW,KAAK,IAAI,GAAG,MAAM,QAAQ,GAAG,GAAG,QAAQ,KAAK,GAAG;GACpH,OAAO;EACT,CAAC,CAAC;EACF,KAAK,MAAM,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;CACrG;CACA,MAAM,gBAAgB,KAAK,KAAK,MAAM,WAAW,GAAG,EAAE,SAAA,EAAwB,CAAC;CAC/E,OAAO;EAAE;EAAM;EAAO;CAAO;AAC/B;;AAGA,SAAgB,SAAS,SAAiB,KAAa;CACrD,MAAM,EAAE,MAAM,SAAS,OAAO,GAAG;CACjC,IAAI,SAAS,QAAQ,MAAM,IAAI,MAAM,IAAI,IAAI,sCAAsC;CACnF,OAAO,KAAK,SAAS,GAAG,IAAI,IAAI;AAClC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mushinjs/plugin-storage",
|
|
3
|
-
"version": "0.2.0-next.
|
|
3
|
+
"version": "0.2.0-next.20261113105814",
|
|
4
4
|
"description": "JSON document storage",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,6 +41,6 @@
|
|
|
41
41
|
]
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@mushinjs/core": "0.2.0-next.
|
|
44
|
+
"@mushinjs/core": "0.2.0-next.20261113105814"
|
|
45
45
|
}
|
|
46
46
|
}
|