@coffer-org/server 1.1.0 → 1.2.0

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.
Files changed (85) hide show
  1. package/dist/collection-io.d.ts +17 -13
  2. package/dist/collection-io.js +99 -43
  3. package/dist/db.d.ts +1 -1
  4. package/dist/db.js +22 -25
  5. package/dist/dialect.d.ts +4 -0
  6. package/dist/dialect.js +14 -0
  7. package/dist/embed.d.ts +14 -0
  8. package/dist/embed.js +46 -0
  9. package/dist/embeddings.d.ts +38 -0
  10. package/dist/embeddings.js +109 -0
  11. package/dist/entity-schema.d.ts +6 -17
  12. package/dist/entity-schema.js +103 -68
  13. package/dist/extend-io.d.ts +3 -4
  14. package/dist/extend-io.js +24 -11
  15. package/dist/extend-table.d.ts +5 -4
  16. package/dist/extend-table.js +26 -17
  17. package/dist/fs-list.d.ts +11 -0
  18. package/dist/fs-list.js +18 -0
  19. package/dist/index.d.ts +0 -1
  20. package/dist/index.js +268 -190
  21. package/dist/local-api.d.ts +4 -5
  22. package/dist/local-api.js +14 -42
  23. package/dist/migrations.d.ts +24 -0
  24. package/dist/migrations.js +198 -0
  25. package/dist/msg-log.d.ts +12 -0
  26. package/dist/msg-log.js +5 -0
  27. package/dist/mutate.d.ts +6 -4
  28. package/dist/mutate.js +47 -37
  29. package/dist/plugin-discovery.d.ts +1 -12
  30. package/dist/plugin-discovery.js +15 -28
  31. package/dist/plugin-hooks.d.ts +36 -26
  32. package/dist/plugin-hooks.js +0 -2
  33. package/dist/plugin-runtime.d.ts +0 -23
  34. package/dist/plugin-runtime.js +33 -67
  35. package/dist/plugin-state.d.ts +2 -0
  36. package/dist/plugin-state.js +11 -0
  37. package/dist/plugins-api.d.ts +0 -1
  38. package/dist/plugins-api.js +2 -10
  39. package/dist/records-api.d.ts +19 -0
  40. package/dist/records-api.js +116 -0
  41. package/dist/registry-context.d.ts +1 -7
  42. package/dist/registry-context.js +4 -3
  43. package/dist/schema-api.d.ts +1 -0
  44. package/dist/schema-api.js +11 -0
  45. package/dist/schema-sync.d.ts +0 -1
  46. package/dist/schema-sync.js +1 -10
  47. package/dist/secrets.d.ts +7 -0
  48. package/dist/secrets.js +60 -0
  49. package/dist/seeds.d.ts +14 -0
  50. package/dist/seeds.js +23 -0
  51. package/dist/temporal.d.ts +0 -16
  52. package/dist/temporal.js +0 -6
  53. package/dist/uploads.d.ts +4 -0
  54. package/dist/uploads.js +12 -0
  55. package/package.json +12 -4
  56. package/dist/collection-io.d.ts.map +0 -1
  57. package/dist/collection-io.js.map +0 -1
  58. package/dist/db.d.ts.map +0 -1
  59. package/dist/db.js.map +0 -1
  60. package/dist/entity-schema.d.ts.map +0 -1
  61. package/dist/entity-schema.js.map +0 -1
  62. package/dist/extend-io.d.ts.map +0 -1
  63. package/dist/extend-io.js.map +0 -1
  64. package/dist/extend-table.d.ts.map +0 -1
  65. package/dist/extend-table.js.map +0 -1
  66. package/dist/index.d.ts.map +0 -1
  67. package/dist/index.js.map +0 -1
  68. package/dist/local-api.d.ts.map +0 -1
  69. package/dist/local-api.js.map +0 -1
  70. package/dist/mutate.d.ts.map +0 -1
  71. package/dist/mutate.js.map +0 -1
  72. package/dist/plugin-discovery.d.ts.map +0 -1
  73. package/dist/plugin-discovery.js.map +0 -1
  74. package/dist/plugin-hooks.d.ts.map +0 -1
  75. package/dist/plugin-hooks.js.map +0 -1
  76. package/dist/plugin-runtime.d.ts.map +0 -1
  77. package/dist/plugin-runtime.js.map +0 -1
  78. package/dist/plugins-api.d.ts.map +0 -1
  79. package/dist/plugins-api.js.map +0 -1
  80. package/dist/registry-context.d.ts.map +0 -1
  81. package/dist/registry-context.js.map +0 -1
  82. package/dist/schema-sync.d.ts.map +0 -1
  83. package/dist/schema-sync.js.map +0 -1
  84. package/dist/temporal.d.ts.map +0 -1
  85. package/dist/temporal.js.map +0 -1
@@ -1,13 +1,5 @@
1
- /**
2
- * MikroORM EntitySchema — похідна від ModuleDef/ExtendDef (джерело істини: f.*).
3
- * DB-agnostic: імпортує лише @mikro-orm/core + типи ядра.
4
- * Колонки: text→text, integer→integer, real→float, boolean→boolean.
5
- *
6
- * Фізичну схему створює runtime-синк (schema-sync.ts) з цих описів.
7
- * Тут — лише in-memory опис для типобезпечних запитів.
8
- */
9
1
  import { EntitySchema } from '@mikro-orm/core';
10
- import { fieldEntries, collectionGroups } from '@coffer-org/sdk/module';
2
+ import { fieldEntries, collectionGroups, storageColumns } from '@coffer-org/sdk/module';
11
3
  function mikroType(col) {
12
4
  if (col === 'integer')
13
5
  return 'integer';
@@ -23,103 +15,91 @@ function mikroType(col) {
23
15
  return 'datetime';
24
16
  return 'text';
25
17
  }
26
- /** Ім'я сутності (= ім'я таблиці) для модуля: vault__module. */
18
+ function columnProp(field, nullable) {
19
+ const p = { type: mikroType(field.column), nullable };
20
+ if (field.default !== undefined)
21
+ p['default'] = field.default;
22
+ return p;
23
+ }
24
+ function addFieldProps(properties, fields, plainNullable) {
25
+ for (const [key, field] of fieldEntries(fields)) {
26
+ if (field.virtual)
27
+ continue;
28
+ if (field.columns) {
29
+ for (const [col, type] of storageColumns(key, field))
30
+ properties[col] = { type: mikroType(type), nullable: true };
31
+ }
32
+ else {
33
+ properties[key] = columnProp(field, plainNullable(field));
34
+ }
35
+ }
36
+ }
27
37
  export function moduleTableName(vault, module) {
28
38
  return `${vault}__${module}`;
29
39
  }
30
- /** EntitySchema для одного модуля плагіна. */
31
40
  export function buildEntitySchema(m) {
32
- // Використовуємо Record<string, any> — EntitySchemaProperty — складний умовний тип,
33
- // тому будуємо properties динамічно і передаємо через as any.
34
41
  const properties = {
35
- id: { type: 'text', primary: true }, // ЄДИНЕ обов'язкове системне поле
36
- created_at: { type: 'text' }, // інфраструктура (авто-керована, поза fields)
42
+ id: { type: 'integer', primary: true, autoincrement: true },
43
+ created_at: { type: 'text' },
37
44
  updated_at: { type: 'text' },
38
45
  };
39
- for (const [key, field] of fieldEntries(m.fields)) {
40
- if (field.virtual)
41
- continue;
42
- properties[key] = {
43
- type: mikroType(field.column),
44
- nullable: !field.required,
45
- };
46
- }
46
+ addFieldProps(properties, m.fields, (f) => !f.required);
47
47
  const name = moduleTableName(m.vault, m.module);
48
48
  return new EntitySchema({ name, tableName: name, properties });
49
49
  }
50
- /** EntitySchema для extend-таблиці (PK = base_id). */
51
50
  export function buildExtendEntitySchema(e) {
52
51
  const properties = {
53
- base_id: { type: 'text', primary: true },
52
+ base_id: { type: 'integer', primary: true },
54
53
  };
55
- for (const [key, field] of fieldEntries(e.fields)) {
56
- if (field.virtual)
57
- continue;
58
- properties[key] = { type: mikroType(field.column), nullable: true };
59
- }
54
+ addFieldProps(properties, e.fields, () => true);
60
55
  const name = `extend__${e.id}`;
61
56
  return new EntitySchema({ name, tableName: name, properties });
62
57
  }
63
- /** EntitySchema для singleton-таблиці налаштувань плагіна. PK = plugin_id. */
64
58
  export function buildSettingsEntitySchema(pluginId, settings) {
65
59
  const properties = {
66
60
  plugin_id: { type: 'text', primary: true },
67
61
  };
68
- for (const [key, field] of fieldEntries(settings.fields)) {
69
- if (field.virtual)
70
- continue;
71
- properties[key] = {
72
- type: mikroType(field.column),
73
- nullable: !field.required,
74
- };
75
- }
62
+ addFieldProps(properties, settings.fields, (f) => !f.required);
76
63
  const name = `_settings__${pluginId}`;
77
64
  return new EntitySchema({ name, tableName: name, properties });
78
65
  }
79
- // ─── Collection-групи → child-таблиці ──────────────────────────────────────
80
- /** Ім'я child-таблиці collection-групи. */
81
66
  export function childTableName(vault, module, key) {
82
67
  return `${vault}__${module}__${key}`;
83
68
  }
84
- /** Child EntitySchema для однієї collection-групи. */
85
- function buildChildSchema(vault, module, c) {
69
+ function buildChildSchemaAt(prefix, c) {
86
70
  const properties = {
87
- id: { type: 'text', primary: true },
88
- parent_id: { type: 'text', index: true },
71
+ id: { type: 'integer', primary: true, autoincrement: true },
72
+ parent_id: { type: 'integer', index: true },
89
73
  position: { type: 'integer' },
90
74
  };
91
- for (const [key, fm] of fieldEntries(c.group.fields)) {
92
- if (fm.virtual)
93
- continue;
94
- properties[key] = { type: mikroType(fm.column), nullable: !fm.required };
95
- }
96
- const name = childTableName(vault, module, c.key);
75
+ addFieldProps(properties, c.group.fields, (f) => !f.required);
76
+ const name = `${prefix}__${c.key}`;
97
77
  return new EntitySchema({ name, tableName: name, properties });
98
78
  }
99
- /** Усі child-схеми модуля (рекурсивно: collection усередині collection). */
100
- export function buildCollectionEntities(m) {
79
+ function buildCollectionEntitiesAt(prefix, fields) {
101
80
  const out = [];
102
- for (const c of collectionGroups(m.fields)) {
103
- out.push(buildChildSchema(m.vault, m.module, c));
104
- const nested = collectionGroups(c.group.fields);
105
- for (const g of nested) {
106
- out.push(buildChildSchema(m.vault, `${m.module}__${c.key}`, g));
107
- }
81
+ for (const c of collectionGroups(fields)) {
82
+ out.push(buildChildSchemaAt(prefix, c));
83
+ out.push(...buildCollectionEntitiesAt(`${prefix}__${c.key}`, c.group.fields));
108
84
  }
109
85
  return out;
110
86
  }
111
- // ─── Системні схеми (плагін core) ──────────────────────────────────────────
112
- // Авторитетне джерело колонок: ці схеми (фізичну схему створює schema-sync.ts).
87
+ export function buildCollectionEntities(m) {
88
+ return buildCollectionEntitiesAt(`${m.vault}__${m.module}`, m.fields);
89
+ }
90
+ export function buildExtendCollectionEntities(e) {
91
+ return buildCollectionEntitiesAt(`extend__${e.id}`, e.fields);
92
+ }
113
93
  export const EventSchema = new EntitySchema({
114
94
  name: '_Event',
115
95
  tableName: '_events',
116
96
  properties: {
117
- id: { type: 'text', primary: true },
97
+ id: { type: 'integer', primary: true, autoincrement: true },
118
98
  ts: { type: 'text' },
119
99
  actor: { type: 'text' },
120
100
  op: { type: 'text' },
121
101
  type: { type: 'text' },
122
- record_id: { type: 'text' },
102
+ record_id: { type: 'integer', nullable: true },
123
103
  before: { type: 'text', nullable: true },
124
104
  after: { type: 'text', nullable: true },
125
105
  },
@@ -133,15 +113,70 @@ export const PluginRowSchema = new EntitySchema({
133
113
  updated_at: { type: 'text' },
134
114
  },
135
115
  });
136
- /** Схеми сутностей плагінів (модулі власних vault'ів + vaultModules + extends + settings). */
116
+ export const MigrationRowSchema = new EntitySchema({
117
+ name: '_Migration',
118
+ tableName: '_migrations',
119
+ properties: {
120
+ plugin_id: { type: 'text', primary: true },
121
+ version: { type: 'integer', nullable: false },
122
+ },
123
+ });
124
+ export const SeedRowSchema = new EntitySchema({
125
+ name: '_Seed',
126
+ tableName: '_seeds',
127
+ properties: {
128
+ plugin_id: { type: 'text', primary: true },
129
+ name: { type: 'text', primary: true },
130
+ },
131
+ });
132
+ export const EmbeddingSchema = new EntitySchema({
133
+ name: '_Embedding',
134
+ tableName: '_embeddings',
135
+ properties: {
136
+ type: { type: 'text', primary: true },
137
+ record_id: { type: 'integer', primary: true },
138
+ snippet: { type: 'text' },
139
+ vector: { type: 'blob' },
140
+ model: { type: 'text' },
141
+ dim: { type: 'integer' },
142
+ updated_at: { type: 'text' },
143
+ },
144
+ });
145
+ export const PluginStateSchema = new EntitySchema({
146
+ name: '_PluginState',
147
+ tableName: '_plugin_state',
148
+ properties: {
149
+ plugin: { type: 'text', primary: true },
150
+ key: { type: 'text', primary: true },
151
+ value: { type: 'text' },
152
+ },
153
+ });
137
154
  export function buildPluginEntities(plugins) {
138
155
  return plugins.flatMap((p) => [
139
156
  ...(p.vaults ?? []).flatMap((v) => v.modules.flatMap((m) => [buildEntitySchema(m), ...buildCollectionEntities(m)])),
140
157
  ...(p.vaultModules ?? []).flatMap((m) => [buildEntitySchema(m), ...buildCollectionEntities(m)]),
141
- ...(p.extends_ ?? []).map(buildExtendEntitySchema),
158
+ ...(p.extends_ ?? []).flatMap((e) => [buildExtendEntitySchema(e), ...buildExtendCollectionEntities(e)]),
142
159
  ...(p.settings && p.settings.fields.length > 0 ? [buildSettingsEntitySchema(p.id, p.settings)] : []),
143
160
  ]);
144
161
  }
145
- /** Системні схеми реєструються в initDb() ДО читання _plugins. */
146
- export const systemEntities = [EventSchema, PluginRowSchema];
147
- //# sourceMappingURL=entity-schema.js.map
162
+ export const MsgLogSchema = new EntitySchema({
163
+ name: '_OrchMsgLog',
164
+ tableName: '_orch_msg_log',
165
+ properties: {
166
+ id: { type: 'integer', primary: true, autoincrement: true },
167
+ ts: { type: 'text' },
168
+ connector: { type: 'text' },
169
+ chat_id: { type: 'text' },
170
+ user_id: { type: 'text' },
171
+ role: { type: 'text' },
172
+ text: { type: 'text' },
173
+ session_id: { type: 'text', nullable: true },
174
+ tokens_in: { type: 'integer', nullable: true },
175
+ tokens_out: { type: 'integer', nullable: true },
176
+ ms: { type: 'integer', nullable: true },
177
+ },
178
+ });
179
+ export const systemEntities = [
180
+ EventSchema, PluginRowSchema, MigrationRowSchema, SeedRowSchema,
181
+ EmbeddingSchema, PluginStateSchema, MsgLogSchema,
182
+ ];
@@ -1,8 +1,7 @@
1
- /** Розділити тіло запиту: базові поля + extend-дані (_extend_<id>). */
2
1
  export declare function splitBody(body: unknown): {
3
2
  base: Record<string, unknown>;
4
3
  extData: Record<string, Record<string, unknown>>;
5
4
  };
6
- /** Зберегти extend-дані після успішного save базового запису. */
7
- export declare function saveExtends(vault: string, module: string, baseId: string, extData: Record<string, Record<string, unknown>>): Promise<void>;
8
- //# sourceMappingURL=extend-io.d.ts.map
5
+ export declare function deleteExtends(vault: string, module: string, id: number): Promise<void>;
6
+ export declare function validateExtends(vault: string, module: string, extData: Record<string, Record<string, unknown>>): void;
7
+ export declare function saveExtends(vault: string, module: string, baseId: number, extData: Record<string, Record<string, unknown>>): Promise<void>;
package/dist/extend-io.js CHANGED
@@ -1,11 +1,7 @@
1
- /**
2
- * Спільна логіка вводу/виводу extend-даних для HTTP-роутів (index.ts) та
3
- * in-process API (local-api.ts). Уникає дублювання splitBody/saveExtends.
4
- */
5
- import { getExtendsFor } from './registry-context.js';
1
+ import { getExtendsFor } from "./registry-context.js";
6
2
  import { buildExtendZodPartial } from '@coffer-org/sdk/extend';
7
- import { extendEntityName, upsertExtendRecord } from './extend-table.js';
8
- /** Розділити тіло запиту: базові поля + extend-дані (_extend_<id>). */
3
+ import { upsertExtendRecord, deleteExtendRecord } from "./extend-table.js";
4
+ import { toIssue, ValidationError } from "./mutate.js";
9
5
  export function splitBody(body) {
10
6
  const raw = (body ?? {});
11
7
  const base = {};
@@ -18,17 +14,34 @@ export function splitBody(body) {
18
14
  }
19
15
  return { base, extData };
20
16
  }
21
- /** Зберегти extend-дані після успішного save базового запису. */
17
+ export async function deleteExtends(vault, module, id) {
18
+ for (const e of getExtendsFor(vault, module)) {
19
+ await deleteExtendRecord(e, id);
20
+ }
21
+ }
22
+ export function validateExtends(vault, module, extData) {
23
+ const issues = [];
24
+ for (const e of getExtendsFor(vault, module)) {
25
+ const data = extData[e.id];
26
+ if (data === undefined)
27
+ continue;
28
+ const parsed = buildExtendZodPartial(e).safeParse(data);
29
+ if (!parsed.success)
30
+ for (const i of parsed.error.issues)
31
+ issues.push(toIssue(i));
32
+ }
33
+ if (issues.length)
34
+ throw new ValidationError(issues);
35
+ }
22
36
  export async function saveExtends(vault, module, baseId, extData) {
37
+ validateExtends(vault, module, extData);
23
38
  for (const e of getExtendsFor(vault, module)) {
24
39
  const data = extData[e.id];
25
40
  if (data === undefined)
26
41
  continue;
27
- // Валідуємо extend-дані
28
42
  const parsed = buildExtendZodPartial(e).safeParse(data);
29
43
  if (parsed.success && Object.keys(parsed.data).length > 0) {
30
- await upsertExtendRecord(extendEntityName(e), baseId, parsed.data);
44
+ await upsertExtendRecord(e, baseId, parsed.data);
31
45
  }
32
46
  }
33
47
  }
34
- //# sourceMappingURL=extend-io.js.map
@@ -1,6 +1,7 @@
1
1
  import type { ExtendDef } from '@coffer-org/sdk/extend';
2
+ type Row = Record<string, unknown>;
2
3
  export declare function extendEntityName(e: ExtendDef): string;
3
- export declare function getExtendRecord(name: string, baseId: string): Promise<Record<string, unknown> | undefined>;
4
- export declare function upsertExtendRecord(name: string, baseId: string, data: Record<string, unknown>): Promise<void>;
5
- export declare function deleteExtendRecord(name: string, baseId: string): Promise<void>;
6
- //# sourceMappingURL=extend-table.d.ts.map
4
+ export declare function getExtendRecord(e: ExtendDef, baseId: number): Promise<Row | undefined>;
5
+ export declare function upsertExtendRecord(e: ExtendDef, baseId: number, data: Row): Promise<void>;
6
+ export declare function deleteExtendRecord(e: ExtendDef, baseId: number): Promise<void>;
7
+ export {};
@@ -1,28 +1,37 @@
1
- /**
2
- * Підтримка extend-таблиць на рівні сервера.
3
- * Кожен ExtendDef → сутність `extend__<id>` з base_id PK.
4
- */
5
1
  import { serialize } from '@mikro-orm/core';
6
- import { getEm } from './db.js';
2
+ import { getEm } from "./db.js";
3
+ import { splitAt, writeAt, readAt, deleteAt } from "./collection-io.js";
7
4
  export function extendEntityName(e) {
8
5
  return `extend__${e.id}`;
9
6
  }
10
- // ─── CRUD ─────────────────────────────────────────────────────────────────────
11
- export async function getExtendRecord(name, baseId) {
7
+ export async function getExtendRecord(e, baseId) {
12
8
  const fork = getEm().fork();
9
+ const name = extendEntityName(e);
13
10
  const row = await fork.findOne(name, { base_id: baseId });
14
11
  if (!row)
15
12
  return undefined;
16
- return serialize(row);
13
+ const flat = serialize(row);
14
+ const collections = await readAt(fork, name, e.fields, baseId);
15
+ return { ...flat, ...collections };
17
16
  }
18
- export async function upsertExtendRecord(name, baseId, data) {
19
- const fork = getEm().fork();
20
- await fork.upsert(name, { base_id: baseId, ...data });
17
+ export async function upsertExtendRecord(e, baseId, data) {
18
+ const name = extendEntityName(e);
19
+ const { base, collections } = splitAt(e.fields, data);
20
+ await getEm()
21
+ .fork()
22
+ .transactional(async (tx) => {
23
+ await tx.upsert(name, { base_id: baseId, ...base });
24
+ await writeAt(tx, name, e.fields, baseId, collections);
25
+ });
21
26
  }
22
- export async function deleteExtendRecord(name, baseId) {
23
- const fork = getEm().fork();
24
- const ref = fork.getReference(name, baseId);
25
- fork.remove(ref);
26
- await fork.flush();
27
+ export async function deleteExtendRecord(e, baseId) {
28
+ const name = extendEntityName(e);
29
+ await getEm()
30
+ .fork()
31
+ .transactional(async (tx) => {
32
+ await deleteAt(tx, name, e.fields, baseId);
33
+ const ref = tx.getReference(name, baseId);
34
+ tx.remove(ref);
35
+ await tx.flush();
36
+ });
27
37
  }
28
- //# sourceMappingURL=extend-table.js.map
@@ -0,0 +1,11 @@
1
+ export interface FsEntry {
2
+ name: string;
3
+ isDir: boolean;
4
+ }
5
+ export declare function resolveWithinHome(input: string, home: string): {
6
+ abs: string;
7
+ } | {
8
+ error: string;
9
+ };
10
+ export declare function filterSort(entries: FsEntry[], exts?: string[]): FsEntry[];
11
+ export declare function listDir(absDir: string): Promise<FsEntry[]>;
@@ -0,0 +1,18 @@
1
+ import { join, resolve, sep } from 'node:path';
2
+ import { readdir } from 'node:fs/promises';
3
+ export function resolveWithinHome(input, home) {
4
+ const expanded = !input || input === '~' ? home : input.startsWith('~/') ? join(home, input.slice(2)) : input;
5
+ const abs = resolve(expanded);
6
+ if (abs !== home && !abs.startsWith(home + sep))
7
+ return { error: 'outside_home' };
8
+ return { abs };
9
+ }
10
+ export function filterSort(entries, exts) {
11
+ const ex = exts?.map((e) => e.toLowerCase().replace(/^\./, '')).filter(Boolean);
12
+ const out = entries.filter((e) => e.isDir || !ex || ex.length === 0 || ex.includes((e.name.split('.').pop() ?? '').toLowerCase()));
13
+ return out.sort((a, b) => (a.isDir === b.isDir ? a.name.localeCompare(b.name) : a.isDir ? -1 : 1));
14
+ }
15
+ export async function listDir(absDir) {
16
+ const ents = await readdir(absDir, { withFileTypes: true });
17
+ return ents.map((d) => ({ name: d.name, isDir: d.isDirectory() }));
18
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=index.d.ts.map