@coffer-org/server 3.3.0 → 4.0.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 (112) hide show
  1. package/dist/auth-api.js +4 -5
  2. package/dist/cache-clock.d.ts +8 -0
  3. package/dist/cache-clock.js +103 -0
  4. package/dist/cache-refresh.d.ts +7 -0
  5. package/dist/cache-refresh.js +322 -0
  6. package/dist/collection-io.d.ts +13 -0
  7. package/dist/collection-io.js +42 -28
  8. package/dist/compute-unit.d.ts +66 -0
  9. package/dist/compute-unit.js +369 -0
  10. package/dist/condition-where.js +1 -3
  11. package/dist/data-dir.d.ts +3 -0
  12. package/dist/data-dir.js +39 -0
  13. package/dist/db.js +2 -0
  14. package/dist/entity-schema.d.ts +1 -0
  15. package/dist/entity-schema.js +25 -2
  16. package/dist/extend-io.d.ts +3 -2
  17. package/dist/extend-io.js +4 -4
  18. package/dist/extend-table.d.ts +3 -2
  19. package/dist/extend-table.js +48 -11
  20. package/dist/file-fields.js +50 -23
  21. package/dist/global-search.js +1 -1
  22. package/dist/http-errors.d.ts +7 -0
  23. package/dist/http-errors.js +24 -0
  24. package/dist/index.js +31 -35
  25. package/dist/mcp-contract/client.d.ts +24 -0
  26. package/dist/mcp-contract/client.js +12 -0
  27. package/dist/mcp-contract/schema.d.ts +46 -0
  28. package/dist/mcp-contract/schema.js +90 -0
  29. package/dist/mcp-contract/tools.d.ts +19 -0
  30. package/dist/mcp-contract/tools.js +160 -0
  31. package/dist/mcp-local.d.ts +1 -1
  32. package/dist/mcp-local.js +4 -2
  33. package/dist/mcp-tools.d.ts +12 -1
  34. package/dist/mcp-tools.js +41 -8
  35. package/dist/migrations.d.ts +30 -2
  36. package/dist/migrations.js +368 -19
  37. package/dist/mutate.js +86 -57
  38. package/dist/orchestrator/agent-capabilities.d.ts +2 -0
  39. package/dist/orchestrator/agent-capabilities.js +201 -0
  40. package/dist/orchestrator/allow.d.ts +16 -0
  41. package/dist/orchestrator/allow.js +65 -0
  42. package/dist/orchestrator/attachments.d.ts +2 -0
  43. package/dist/orchestrator/attachments.js +11 -0
  44. package/dist/orchestrator/config.d.ts +4 -0
  45. package/dist/orchestrator/config.js +19 -0
  46. package/dist/orchestrator/db.d.ts +17 -0
  47. package/dist/orchestrator/db.js +12 -0
  48. package/dist/orchestrator/diagnostics.d.ts +9 -0
  49. package/dist/orchestrator/diagnostics.js +13 -0
  50. package/dist/orchestrator/environment.d.ts +3 -0
  51. package/dist/orchestrator/environment.js +30 -0
  52. package/dist/orchestrator/file-inspection.d.ts +3 -0
  53. package/dist/orchestrator/file-inspection.js +61 -0
  54. package/dist/orchestrator/format.d.ts +1 -0
  55. package/dist/orchestrator/format.js +6 -0
  56. package/dist/orchestrator/index.d.ts +30 -0
  57. package/dist/orchestrator/index.js +56 -0
  58. package/dist/orchestrator/live-message.d.ts +14 -0
  59. package/dist/orchestrator/live-message.js +100 -0
  60. package/dist/orchestrator/pipeline.d.ts +11 -0
  61. package/dist/orchestrator/pipeline.js +198 -0
  62. package/dist/orchestrator/registry.d.ts +13 -0
  63. package/dist/orchestrator/registry.js +74 -0
  64. package/dist/orchestrator/starters.d.ts +19 -0
  65. package/dist/orchestrator/starters.js +81 -0
  66. package/dist/orchestrator/suggestion-capabilities.d.ts +5 -0
  67. package/dist/orchestrator/suggestion-capabilities.js +33 -0
  68. package/dist/orchestrator/system-assembly.d.ts +7 -0
  69. package/dist/orchestrator/system-assembly.js +13 -0
  70. package/dist/orchestrator/system-capabilities.d.ts +2 -0
  71. package/dist/orchestrator/system-capabilities.js +63 -0
  72. package/dist/orchestrator/types.d.ts +144 -0
  73. package/dist/orchestrator/types.js +1 -0
  74. package/dist/part-errors.d.ts +3 -0
  75. package/dist/part-errors.js +6 -0
  76. package/dist/part-injection.d.ts +11 -3
  77. package/dist/part-injection.js +72 -92
  78. package/dist/part-ref.d.ts +13 -0
  79. package/dist/part-ref.js +109 -0
  80. package/dist/plugin-hooks.d.ts +1 -0
  81. package/dist/plugin-i18n.js +2 -1
  82. package/dist/plugin-runtime.d.ts +2 -2
  83. package/dist/plugin-runtime.js +21 -11
  84. package/dist/plugin-updates.d.ts +2 -2
  85. package/dist/plugin-updates.js +6 -12
  86. package/dist/public-url.js +2 -1
  87. package/dist/recompute-derived.d.ts +2 -1
  88. package/dist/recompute-derived.js +123 -26
  89. package/dist/records-api.d.ts +5 -1
  90. package/dist/records-api.js +47 -15
  91. package/dist/registry-context.d.ts +1 -1
  92. package/dist/registry-context.js +3 -0
  93. package/dist/rename-guard.d.ts +1 -0
  94. package/dist/rename-guard.js +9 -0
  95. package/dist/schema-sync.js +38 -1
  96. package/dist/search-index.js +1 -1
  97. package/dist/search-indexer.d.ts +1 -1
  98. package/dist/search-indexer.js +3 -3
  99. package/dist/settings-groups.d.ts +14 -0
  100. package/dist/settings-groups.js +25 -0
  101. package/dist/settings-write.d.ts +4 -4
  102. package/dist/settings-write.js +23 -26
  103. package/dist/system-settings.d.ts +3 -0
  104. package/dist/system-settings.js +27 -0
  105. package/dist/thread-store.d.ts +3 -2
  106. package/dist/thread-store.js +6 -5
  107. package/dist/trigger-queue.d.ts +33 -0
  108. package/dist/trigger-queue.js +432 -0
  109. package/dist/uploads.js +6 -3
  110. package/dist/value-compare.d.ts +1 -0
  111. package/dist/value-compare.js +7 -0
  112. package/package.json +17 -3
@@ -0,0 +1,160 @@
1
+ import { z } from 'zod';
2
+ import { ValidationError, NotFoundError, ConnectionError } from "./client.js";
3
+ const ok = (data) => ({
4
+ content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
5
+ });
6
+ const fail = (text) => ({ content: [{ type: 'text', text }], isError: true });
7
+ const ISSUE_HINTS = {
8
+ file_remote_url: 'a file field stores an uploaded file, not a remote URL',
9
+ file_unknown_key: 'a file entry may only carry name, mime and size',
10
+ file_name: 'name must be a bare filename, without a path',
11
+ file_not_uploaded: 'no file by that name has been uploaded',
12
+ };
13
+ const FILE_RECIPE = '\n\nTo put a file in a file/image/media field:\n' +
14
+ '1. create_upload_ticket → {token, upload_url} — upload_url is the absolute address to post to\n' +
15
+ '2. curl -H "Authorization: Bearer <token>" -F "file=@<local path>" <upload_url> → {"name":"<filename>"}\n' +
16
+ '3. write the field as {"name":"<filename>"} (or an array of those).\n' +
17
+ 'Downloading a remote image is your side of the job: fetch it to a local file first, then upload that file. ' +
18
+ 'mime and size are set by the server. To remove a file, drop its entry — entries cannot be edited in place.';
19
+ async function guard(fn) {
20
+ try {
21
+ return ok(await fn());
22
+ }
23
+ catch (e) {
24
+ if (e instanceof ValidationError) {
25
+ const lines = e.issues.map((i) => {
26
+ const o = i;
27
+ const code = o.code ?? 'invalid';
28
+ const hint = ISSUE_HINTS[code];
29
+ return `- ${o.field ?? '?'}: ${code}${hint ? ` — ${hint}` : ''}`;
30
+ });
31
+ const fileIssue = e.issues.some((i) => (i.code ?? '').startsWith('file_'));
32
+ return fail(`Validation error:\n${lines.join('\n')}${fileIssue ? FILE_RECIPE : ''}`);
33
+ }
34
+ if (e instanceof NotFoundError)
35
+ return fail('Record or shelf not found.');
36
+ if (e instanceof ConnectionError)
37
+ return fail(e.message);
38
+ return fail(`Unexpected error: ${e.message}`);
39
+ }
40
+ }
41
+ export const LIST_DEFAULT_LIMIT = 25;
42
+ export const LIST_MAX_LIMIT = 200;
43
+ export function buildTools(client, cache) {
44
+ const library = z.string().describe('Library identifier, e.g. "things"');
45
+ const shelf = z.string().describe('Shelf identifier, e.g. "item"');
46
+ const id = z.coerce.number().int().describe('record id');
47
+ return [
48
+ {
49
+ name: 'list_libraries',
50
+ description: 'Lightweight index of all libraries and shelves (library, shelf, label). Starting point for exploring content.',
51
+ inputSchema: {},
52
+ handler: () => guard(() => cache.index()),
53
+ },
54
+ {
55
+ name: 'describe_shelf',
56
+ description: 'Detailed shelf schema: fields with kind/prim/required and relation/options. A collection is one entry with ' +
57
+ 'multiple:true and its row shape in fields[]. A field with derived:true is computed by the server and rejects ' +
58
+ 'writes — omit it when writing. Call BEFORE create_record/update_record.',
59
+ inputSchema: { library, shelf },
60
+ handler: (a) => guard(() => cache.describeShelf(a.library, a.shelf)),
61
+ },
62
+ {
63
+ name: 'refresh_schema',
64
+ description: 'Re-read the schema from the server. Call after enabling/installing a plugin (server restart required).',
65
+ inputSchema: {},
66
+ handler: async () => {
67
+ cache.refresh();
68
+ return ok({ refreshed: true });
69
+ },
70
+ },
71
+ {
72
+ name: 'list_records',
73
+ description: `List records of a shelf, one page at a time. q — free-text search over the shelf's text fields ` +
74
+ `(this is how you FIND a record by part of its name: q "minisforum" matches "Minisforum UM790"); ` +
75
+ `filters — exact field match; folderId — folder filter; ` +
76
+ `limit — page size (default ${LIST_DEFAULT_LIMIT}, max ${LIST_MAX_LIMIT}); offset — rows to skip. ` +
77
+ `Returns {rows, total, limit, offset}: "total" is how many records match IN FULL, so answer "how many do I have" ` +
78
+ `from it instead of listing everything, and never conclude a record is missing from one unfiltered page — ` +
79
+ `search with q first.`,
80
+ inputSchema: {
81
+ library,
82
+ shelf,
83
+ q: z.string().optional().describe('Free-text search over text fields; matches partial words'),
84
+ filters: z
85
+ .record(z.string(), z.union([z.string(), z.number()]))
86
+ .optional()
87
+ .describe('Exact field=value match'),
88
+ folderId: z.string().optional(),
89
+ limit: z.number().int().positive().optional(),
90
+ offset: z.number().int().nonnegative().optional(),
91
+ },
92
+ handler: async (a) => guard(async () => {
93
+ const q = { ...a.filters };
94
+ if (a.folderId !== undefined)
95
+ q.folderId = a.folderId;
96
+ if (typeof a.q === 'string' && a.q.trim())
97
+ q.q = a.q.trim();
98
+ const limit = Math.min(typeof a.limit === 'number' ? a.limit : LIST_DEFAULT_LIMIT, LIST_MAX_LIMIT);
99
+ q.limit = limit;
100
+ const offset = typeof a.offset === 'number' ? a.offset : 0;
101
+ if (offset > 0)
102
+ q.offset = offset;
103
+ const res = await client.listRecords(a.library, a.shelf, q);
104
+ const paged = res;
105
+ const rows = Array.isArray(res) ? res : Array.isArray(paged.rows) ? paged.rows : [];
106
+ const total = typeof paged.total === 'number' ? paged.total : rows.length;
107
+ return { rows, total, limit, offset };
108
+ }),
109
+ },
110
+ {
111
+ name: 'get_record',
112
+ description: 'A single record by id (with _extends, if any).',
113
+ inputSchema: { library, shelf, id },
114
+ handler: (a) => guard(() => client.getRecord(a.library, a.shelf, a.id)),
115
+ },
116
+ {
117
+ name: 'create_record',
118
+ description: 'Create a record. fields — an object of field values (see describe_shelf). name is required.',
119
+ inputSchema: { library, shelf, fields: z.record(z.string(), z.unknown()).describe('Record field values') },
120
+ handler: (a) => guard(() => client.createRecord(a.library, a.shelf, a.fields)),
121
+ },
122
+ {
123
+ name: 'update_record',
124
+ description: 'Partial record update. fields — only the fields to change. Read the record first, call describe_shelf first, and preserve every unmentioned field; never overwrite conflicts silently.',
125
+ inputSchema: { library, shelf, id, fields: z.record(z.string(), z.unknown()) },
126
+ handler: (a) => guard(() => client.updateRecord(a.library, a.shelf, a.id, a.fields)),
127
+ },
128
+ {
129
+ name: 'delete_record',
130
+ description: 'Move a record to the trash by id (reversible soft-delete; data remains in the database). Read the complete record first and get explicit user confirmation for a non-obvious deletion. Never blank fields to simulate deletion.',
131
+ inputSchema: { library, shelf, id },
132
+ handler: (a) => guard(() => client.deleteRecord(a.library, a.shelf, a.id)),
133
+ },
134
+ {
135
+ name: 'list_trash',
136
+ description: 'List every record currently in the trash, including its full data. Use this before restoring or permanently purging a record; normal list_records/get_record calls hide trashed records.',
137
+ inputSchema: {},
138
+ handler: () => guard(() => client.listTrash()),
139
+ },
140
+ {
141
+ name: 'restore_record',
142
+ description: 'Restore a record from the trash. Inspect it with list_trash first and confirm the target if the user did not name the exact record.',
143
+ inputSchema: { library, shelf, id },
144
+ handler: (a) => guard(() => client.restoreRecord(a.library, a.shelf, a.id)),
145
+ },
146
+ {
147
+ name: 'purge_record',
148
+ description: 'Permanently delete a record that is already in the trash. This is irreversible: inspect list_trash and require explicit user confirmation immediately before calling it.',
149
+ inputSchema: {
150
+ library,
151
+ shelf,
152
+ id,
153
+ confirmed: z.literal(true).describe('Must be true only after the user explicitly confirmed permanent deletion'),
154
+ },
155
+ handler: async (a) => a.confirmed === true
156
+ ? guard(() => client.purgeRecord(a.library, a.shelf, a.id))
157
+ : fail('Permanent deletion requires explicit user confirmation.'),
158
+ },
159
+ ];
160
+ }
@@ -1,4 +1,4 @@
1
- import type { CofferClientApi } from '@coffer-org/mcp/client';
1
+ import type { CofferClientApi } from './mcp-contract/client.ts';
2
2
  export declare function mapError(e: unknown): never;
3
3
  export declare function splitListQuery(raw: Record<string, unknown>): {
4
4
  query: Record<string, unknown>;
package/dist/mcp-local.js CHANGED
@@ -1,5 +1,5 @@
1
- import { ValidationError, NotFoundError } from '@coffer-org/mcp/client';
2
- import { recordList, recordListPage, recordGet, recordCreate, recordUpdate, recordDelete, UnknownShelfError, recordTrashList, recordRestore, recordPurge, SingleShelfError, } from "./records-api.js";
1
+ import { ValidationError, NotFoundError } from "./mcp-contract/client.js";
2
+ import { recordList, recordListPage, recordGet, recordCreate, recordUpdate, recordDelete, UnknownShelfError, recordTrashList, recordRestore, recordPurge, SingleShelfError, FilterError, } from "./records-api.js";
3
3
  import { ValidationError as ServerValidationError } from "./mutate.js";
4
4
  import { buildClientSchema } from "./schema-api.js";
5
5
  export function mapError(e) {
@@ -9,6 +9,8 @@ export function mapError(e) {
9
9
  throw new NotFoundError('not_found');
10
10
  if (e instanceof SingleShelfError)
11
11
  throw new ValidationError([{ field: 'shelf', code: 'single_shelf', message: e.message }]);
12
+ if (e instanceof FilterError)
13
+ throw new ValidationError([{ field: 'query', code: 'bad_filter', message: e.message }]);
12
14
  throw e;
13
15
  }
14
16
  export function splitListQuery(raw) {
@@ -1,6 +1,6 @@
1
1
  import type { EntityManager } from '@mikro-orm/core';
2
2
  import { z } from 'zod';
3
- import { type ToolResult } from '@coffer-org/mcp';
3
+ import { type ToolResult } from './mcp-contract/tools.ts';
4
4
  import { type AuthRole, type PluginHooks } from './plugin-hooks.ts';
5
5
  import { type Condition } from '@coffer-org/sdk/condition';
6
6
  import { type RagHit } from './rag-search.ts';
@@ -69,5 +69,16 @@ export declare function collectLibraryPurposes(reg?: {
69
69
  }[];
70
70
  }): LibraryPurpose[];
71
71
  export declare function buildDomainSections(): Promise<string[]>;
72
+ export declare function collectStarterHints(hooks?: Record<string, PluginHooks>, emFactory?: () => EntityManager, reg?: {
73
+ libraries: {
74
+ meta: {
75
+ id: string;
76
+ starterHint?: string;
77
+ };
78
+ }[];
79
+ }): Promise<{
80
+ combined: string;
81
+ hash: string;
82
+ }>;
72
83
  export declare function buildMcpInstructions(sections: string[]): string;
73
84
  export {};
package/dist/mcp-tools.js CHANGED
@@ -1,6 +1,7 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import { z } from 'zod';
2
- import { buildTools } from '@coffer-org/mcp';
3
- import { SchemaCache } from '@coffer-org/mcp/schema';
3
+ import { buildTools } from "./mcp-contract/tools.js";
4
+ import { SchemaCache } from "./mcp-contract/schema.js";
4
5
  import { LocalClient } from "./mcp-local.js";
5
6
  import { frontendInstructions } from "./frontend-agent.js";
6
7
  import { mintUploadTicket } from "./upload-ticket.js";
@@ -81,7 +82,7 @@ export async function collectMcpTools(opts = {}) {
81
82
  how_to: `curl -H "Authorization: Bearer <token>" -F "file=@<path>" ${uploadUrl} → {"name":"<name>"}. Then set a file field to {"name":"<name>"}. mime/size are filled in by the server — do not send your own.` +
82
83
  (base
83
84
  ? ''
84
- : " The server has no public URL configured (core settings publicUrl / PUBLIC_URL env), so the path is relative — resolve it against this MCP server's own origin."),
85
+ : " The server has no public URL configured (system settings publicUrl / PUBLIC_URL env), so the path is relative — resolve it against this MCP server's own origin."),
85
86
  });
86
87
  },
87
88
  });
@@ -204,7 +205,7 @@ export async function collectMcpTools(opts = {}) {
204
205
  server: 'coffer',
205
206
  bareName: 'list_settings',
206
207
  httpName: 'list_settings',
207
- description: 'List every plugin and system settings group (including "core"): each field\'s key/kind/required and the current values (secrets masked). Call before update_settings.',
208
+ description: "List every settings group (the instance's own `system` group and each plugin's): each field's key/kind/required and the current values (secrets masked). Call before update_settings.",
208
209
  inputSchema: {},
209
210
  scope: 'settings',
210
211
  role: 'admin',
@@ -221,13 +222,13 @@ export async function collectMcpTools(opts = {}) {
221
222
  server: 'coffer',
222
223
  bareName: 'update_settings',
223
224
  httpName: 'update_settings',
224
- description: 'Update a plugin or system settings group. plugin — the plugin id (e.g. "claude-agent", "core"); fields — only the settings to change (see list_settings). Send real secret values; the masked placeholder (********) is treated as unchanged.',
225
- inputSchema: { plugin: z.string(), fields: z.record(z.string(), z.unknown()) },
225
+ description: 'Update a plugin or system settings group. group — the settings group id (e.g. "system", "claude-agent"); fields — only the settings to change (see list_settings). Send real secret values; the masked placeholder (********) is treated as unchanged.',
226
+ inputSchema: { group: z.string(), fields: z.record(z.string(), z.unknown()) },
226
227
  scope: 'settings',
227
228
  role: 'admin',
228
229
  handler: async (args) => {
229
230
  try {
230
- const row = await writePluginSettings(getEm().fork(), args.plugin, args.fields, actor);
231
+ const row = await writePluginSettings(getEm().fork(), args.group, args.fields, actor);
231
232
  return ok(row);
232
233
  }
233
234
  catch (e) {
@@ -239,7 +240,7 @@ export async function collectMcpTools(opts = {}) {
239
240
  return fail(`Validation error:\n${lines.join('\n')}`);
240
241
  }
241
242
  if (e instanceof NotFoundError)
242
- return fail(`Plugin '${String(args.plugin)}' has no settings.`);
243
+ return fail(`No settings group '${String(args.group)}'.`);
243
244
  return fail(`Error: ${e.message}`);
244
245
  }
245
246
  },
@@ -349,6 +350,38 @@ export async function buildDomainSections() {
349
350
  ...rules,
350
351
  ];
351
352
  }
353
+ export async function collectStarterHints(hooks = pluginHooks, emFactory = () => getEm().fork(), reg) {
354
+ const parts = [];
355
+ for (const [id, h] of Object.entries(hooks)) {
356
+ const hint = h.agent?.starterHint;
357
+ if (hint == null)
358
+ continue;
359
+ try {
360
+ const text = typeof hint === 'function' ? await hint(pluginCtx(id, emFactory())) : hint;
361
+ if (text)
362
+ parts.push(`- ${id}: ${text}`);
363
+ }
364
+ catch (e) {
365
+ log.warn(`plugin ${id}: starterHint skipped — ${e.message}`);
366
+ }
367
+ }
368
+ let registry = reg;
369
+ if (!registry) {
370
+ try {
371
+ registry = getActiveRegistry();
372
+ }
373
+ catch {
374
+ registry = undefined;
375
+ }
376
+ }
377
+ for (const v of registry?.libraries ?? []) {
378
+ if (v.meta.starterHint)
379
+ parts.push(`- ${v.meta.id}: ${v.meta.starterHint}`);
380
+ }
381
+ const combined = parts.join('\n');
382
+ const hash = createHash('sha1').update(combined).digest('hex');
383
+ return { combined, hash };
384
+ }
352
385
  export function buildMcpInstructions(sections) {
353
386
  const base = [
354
387
  "Coffer is the user's personal database, organized into libraries (kitchen, people, finance, health, devices, home, garden, documents, travel, and more), each holding typed records.",
@@ -1,5 +1,7 @@
1
1
  import type { EntityManager, EntitySchema } from '@mikro-orm/core';
2
2
  import type { Migration, PluginHooks, TableOps } from './plugin-hooks.ts';
3
+ export declare function markColumnHandedOver(table: string, column: string): void;
4
+ export declare function isColumnHandedOver(table: string, column: string): boolean;
3
5
  export declare function introspectTable(em: EntityManager, table: string): Promise<{
4
6
  exists: boolean;
5
7
  columns: Set<string>;
@@ -9,8 +11,29 @@ export declare function introspectTable(em: EntityManager, table: string): Promi
9
11
  primary: boolean;
10
12
  }[];
11
13
  }>;
12
- export declare function makeTable(em: EntityManager, table: string): TableOps;
14
+ export declare function introspectAllColumns(em: EntityManager, tables: string[]): Promise<Map<string, Set<string>>>;
15
+ export type MigrationOpCode = 'no-table' | 'no-rows' | 'no-change' | 'no-source-column' | 'target-column-exists' | 'converted-nothing';
16
+ export declare const CONCERNING_CODES: ReadonlySet<MigrationOpCode>;
17
+ export interface MigrationOp {
18
+ op: 'renameColumn' | 'fill' | 'changeType' | 'convert' | 'dropColumn';
19
+ table: string;
20
+ performed: boolean;
21
+ rows?: number;
22
+ scanned?: number;
23
+ emptyTargets?: string[];
24
+ strandedSources?: string[];
25
+ code?: MigrationOpCode;
26
+ reason?: string;
27
+ }
28
+ export type OpRecorder = (op: MigrationOp) => void;
29
+ export declare function describeMigrationConcerns(plugin: string, version: number, ops: MigrationOp[]): string | null;
30
+ export declare function makeTable(em: EntityManager, table: string, record?: OpRecorder): TableOps;
13
31
  export declare function renameSystemShelfKey(em: EntityManager): Promise<void>;
32
+ export declare const SYSTEM_MIGRATIONS: {
33
+ name: string;
34
+ up(em: EntityManager): Promise<void>;
35
+ }[];
36
+ export declare function runSystemMigrations(em: EntityManager): Promise<void>;
14
37
  export declare function validateMigrations(pluginId: string, list: Migration[]): void;
15
38
  interface RunArgs {
16
39
  em: EntityManager;
@@ -19,7 +42,12 @@ interface RunArgs {
19
42
  tables?: string[];
20
43
  }[];
21
44
  hooks: Record<string, PluginHooks>;
45
+ onApplied?: (r: {
46
+ plugin: string;
47
+ version: number;
48
+ ops: MigrationOp[];
49
+ }) => void;
22
50
  }
23
- export declare function runMigrations({ em, plugins, hooks }: RunArgs): Promise<void>;
51
+ export declare function runMigrations({ em, plugins, hooks, onApplied }: RunArgs): Promise<void>;
24
52
  export declare function assertSafeRequired(em: EntityManager, entities: EntitySchema[]): Promise<void>;
25
53
  export {};