@iwo-szapar/data-mcp 0.4.0 → 0.6.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 (36) hide show
  1. package/dist/adapter/factory.d.ts +2 -2
  2. package/dist/adapter/factory.js +7 -3
  3. package/dist/adapter/owner-scope.d.ts +31 -0
  4. package/dist/adapter/owner-scope.js +155 -0
  5. package/dist/adapter/types.d.ts +2 -1
  6. package/dist/config.d.ts +20 -4
  7. package/dist/config.js +14 -1
  8. package/dist/server.js +2 -2
  9. package/dist/tools/memory/contact-create.js +3 -1
  10. package/dist/tools/memory/goal-create.js +3 -1
  11. package/dist/tools/memory/knowledge-decide.js +3 -1
  12. package/dist/tools/memory/knowledge-learn.js +3 -1
  13. package/dist/tools/memory/knowledge-list.js +5 -1
  14. package/dist/tools/memory/knowledge-recall.js +22 -7
  15. package/dist/tools/memory/knowledge-store.js +11 -5
  16. package/dist/tools/memory/link-create.js +15 -9
  17. package/dist/tools/memory/link-delete.js +1 -1
  18. package/dist/tools/memory/link-related.js +1 -1
  19. package/dist/tools/memory/link-suggest.js +1 -1
  20. package/dist/tools/memory/session-log.js +3 -1
  21. package/dist/tools/memory/task-create.js +3 -1
  22. package/dist/tools/register.js +3 -1
  23. package/dist/tools/setup/setup-bootstrap.js +71 -0
  24. package/dist/tools/setup/setup-migrate.js +20 -16
  25. package/dist/tools/setup/setup-status.js +11 -6
  26. package/migrations/pocketbase/001_core_schema.js +24 -28
  27. package/migrations/pocketbase/002_goals_tasks.js +15 -18
  28. package/migrations/pocketbase/003_contacts.js +9 -12
  29. package/migrations/pocketbase/004_entity_aliases.js +9 -12
  30. package/migrations/pocketbase/005_prospects.js +11 -14
  31. package/migrations/pocketbase/006_business.js +29 -33
  32. package/migrations/pocketbase/007_newsletter_affiliates.js +16 -19
  33. package/migrations/pocketbase/008_knowledge_links.js +34 -37
  34. package/migrations/supabase/009_align_to_production.sql +14 -11
  35. package/package.json +1 -1
  36. package/README.md +0 -286
@@ -24,6 +24,7 @@ export function registerSessionLog(server, adapter) {
24
24
  })).optional().describe('Patterns learned during the session'),
25
25
  knowledge_created: z.number().int().min(0).optional().describe('Number of knowledge items created'),
26
26
  knowledge_updated: z.number().int().min(0).optional().describe('Number of knowledge items updated'),
27
+ owner_scope: z.enum(['private', 'shared']).optional().describe('Store privately for this user or in shared team memory'),
27
28
  metadata: z.record(z.unknown()).optional().describe('Additional metadata'),
28
29
  }, withGracefulDegradation('sessions', adapter, async (params) => {
29
30
  try {
@@ -40,6 +41,7 @@ export function registerSessionLog(server, adapter) {
40
41
  patterns_learned: params.patterns_learned ?? [],
41
42
  knowledge_created: params.knowledge_created ?? 0,
42
43
  knowledge_updated: params.knowledge_updated ?? 0,
44
+ ...(adapter.ownerScopeEnabled ? { owner_scope: params.owner_scope } : {}),
43
45
  metadata: params.metadata ?? null,
44
46
  });
45
47
  return makeToolResponse({
@@ -53,4 +55,4 @@ export function registerSessionLog(server, adapter) {
53
55
  }
54
56
  }));
55
57
  }
56
- //# sourceMappingURL=session-log.js.map
58
+ //# sourceMappingURL=session-log.js.map
@@ -13,6 +13,7 @@ export function registerTaskCreate(server, adapter) {
13
13
  due_date: z.string().max(50).optional().describe('Due date (ISO format)'),
14
14
  tags: z.array(z.string().max(100)).max(20).optional().describe('Tags for categorization'),
15
15
  goal_id: z.string().min(1).optional().describe('Related goal ID'),
16
+ owner_scope: z.enum(['private', 'shared']).optional().describe('Store privately for this user or in shared team memory'),
16
17
  }, withGracefulDegradation('tasks', adapter, async (params) => {
17
18
  try {
18
19
  const record = await adapter.create('tasks', {
@@ -23,6 +24,7 @@ export function registerTaskCreate(server, adapter) {
23
24
  due_date: params.due_date ?? null,
24
25
  tags: params.tags ?? [],
25
26
  goal_id: params.goal_id ?? null,
27
+ ...(adapter.ownerScopeEnabled ? { owner_scope: params.owner_scope } : {}),
26
28
  });
27
29
  return makeToolResponse({
28
30
  created: true,
@@ -35,4 +37,4 @@ export function registerTaskCreate(server, adapter) {
35
37
  }
36
38
  }));
37
39
  }
38
- //# sourceMappingURL=task-create.js.map
40
+ //# sourceMappingURL=task-create.js.map
@@ -28,9 +28,10 @@ import { registerLinkCreate } from './memory/link-create.js';
28
28
  import { registerLinkDelete } from './memory/link-delete.js';
29
29
  import { registerLinkRelated } from './memory/link-related.js';
30
30
  import { registerLinkSuggest } from './memory/link-suggest.js';
31
- // Setup tools (3)
31
+ // Setup tools (4)
32
32
  import { registerSetupStatus } from './setup/setup-status.js';
33
33
  import { registerSetupMigrate } from './setup/setup-migrate.js';
34
+ import { registerSetupBootstrap } from './setup/setup-bootstrap.js';
34
35
  import { registerSetupSeed } from './setup/setup-seed.js';
35
36
  // Business tools (11)
36
37
  import { registerProspectCreate } from './business/prospect-create.js';
@@ -75,6 +76,7 @@ export function registerAllTools(server, adapter) {
75
76
  // Setup tools
76
77
  registerSetupStatus(server, adapter);
77
78
  registerSetupMigrate(server, adapter);
79
+ registerSetupBootstrap(server, adapter);
78
80
  registerSetupSeed(server, adapter);
79
81
  // Business tools
80
82
  registerProspectCreate(server, adapter);
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Tool: setup_bootstrap
3
+ *
4
+ * RC-2 fix (incident 2026-05-11): Generates a single SQL block containing
5
+ * every bundled Supabase migration concatenated in numeric order, followed
6
+ * by NOTIFY pgrst, 'reload schema'. Customer pastes the block into the
7
+ * Supabase SQL Editor and runs it once. Stops short of running the SQL
8
+ * itself because Supabase service-role over PostgREST does not expose
9
+ * arbitrary SQL execution — that needs either a custom exec_sql function
10
+ * (Approach B, opt-in by customer) or the Management API with a personal
11
+ * access token (Approach C, adds auth surface). This is Approach A: lowest
12
+ * friction, no new auth, works on Cloud + self-hosted.
13
+ *
14
+ * After the customer runs the block, setup_migrate will report all
15
+ * collections present.
16
+ */
17
+ import { readFileSync, readdirSync } from 'node:fs';
18
+ import { dirname, join } from 'node:path';
19
+ import { fileURLToPath } from 'node:url';
20
+ import { makeToolResponse, makeErrorResponse } from '../shared.js';
21
+ const MIGRATIONS_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'migrations', 'supabase');
22
+ export function registerSetupBootstrap(server, adapter) {
23
+ server.tool('setup_bootstrap', 'Generate a paste-ready SQL block (all bundled Supabase migrations + PostgREST reload). Open the returned sql_editor_url, paste sql, run. Idempotent. Supabase backend only — PocketBase customers use setup_migrate.', {}, async () => {
24
+ if (adapter.backend !== 'supabase') {
25
+ return makeErrorResponse('setup_bootstrap is Supabase-only. PocketBase customers use setup_migrate.');
26
+ }
27
+ try {
28
+ const files = readdirSync(MIGRATIONS_DIR).filter((f) => f.endsWith('.sql')).sort();
29
+ if (files.length === 0) {
30
+ return makeErrorResponse(`No bundled migration files found at ${MIGRATIONS_DIR}. Package install may be corrupted.`);
31
+ }
32
+ const blocks = files.map((f) => `-- ===== ${f} =====\n${readFileSync(join(MIGRATIONS_DIR, f), 'utf-8').trimEnd()}\n`);
33
+ const sql = [
34
+ ...blocks,
35
+ '-- ===== Reload PostgREST schema cache =====',
36
+ "-- Without this, PostgREST keeps serving stale cache and knowledge_* calls fail.",
37
+ "NOTIFY pgrst, 'reload schema';",
38
+ '',
39
+ ].join('\n');
40
+ // Supabase URL format: https://<ref>.supabase.co. The SQL Editor URL is
41
+ // https://supabase.com/dashboard/project/<ref>/sql/new. We try to extract
42
+ // the ref; if the URL doesn't match the expected format (self-hosted,
43
+ // etc.), we fall back to the generic dashboard.
44
+ const supabaseUrl = adapter.supabaseUrl || '';
45
+ const refMatch = supabaseUrl.match(/https?:\/\/([a-z0-9]+)\.supabase\.co/);
46
+ const sqlEditorUrl = refMatch
47
+ ? `https://supabase.com/dashboard/project/${refMatch[1]}/sql/new`
48
+ : 'https://supabase.com/dashboard/project/_/sql/new';
49
+ return makeToolResponse({
50
+ backend: 'supabase',
51
+ migrations_path: MIGRATIONS_DIR,
52
+ files_count: files.length,
53
+ files,
54
+ sql,
55
+ sql_editor_url: sqlEditorUrl,
56
+ instructions: [
57
+ `1. Open ${sqlEditorUrl}`,
58
+ '2. Paste the contents of the `sql` field above into the editor.',
59
+ '3. Run.',
60
+ '4. Verify with setup_migrate (it should report 0 missing collections).',
61
+ ],
62
+ });
63
+ }
64
+ catch (err) {
65
+ const msg = err instanceof Error ? err.message : 'unknown';
66
+ console.error('[setup_bootstrap] Error:', err);
67
+ return makeErrorResponse(`Failed to assemble bootstrap SQL from ${MIGRATIONS_DIR}: ${msg}`);
68
+ }
69
+ });
70
+ }
71
+ //# sourceMappingURL=setup-bootstrap.js.map
@@ -3,8 +3,16 @@
3
3
  *
4
4
  * Apply migrations — additive only, skips existing collections.
5
5
  * Creates all expected collections in the database.
6
+ *
7
+ * RC-3 fix (incident 2026-05-11): Replaced hardcoded project-relative
8
+ * "migrations/supabase/" string with runtime-resolved absolute path to
9
+ * the bundled SQL files inside this package.
6
10
  */
11
+ import { dirname, join } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
7
13
  import { makeToolResponse, makeErrorResponse } from '../shared.js';
14
+ const PACKAGE_MIGRATIONS_SUPABASE = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'migrations', 'supabase');
15
+ const PACKAGE_MIGRATIONS_POCKETBASE = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'migrations', 'pocketbase');
8
16
  /** Collection schemas for PocketBase creation */
9
17
  const COLLECTION_SCHEMAS = [
10
18
  { name: 'knowledge', description: 'Knowledge items (facts, patterns, insights, lessons, references)' },
@@ -23,10 +31,11 @@ const COLLECTION_SCHEMAS = [
23
31
  { name: 'affiliates', description: 'Affiliate partners and commissions' },
24
32
  ];
25
33
  export function registerSetupMigrate(server, adapter) {
26
- server.tool('setup_migrate', 'Create missing database collections/tables. Additive only — existing collections are skipped. For PocketBase, creates collections via API. For Supabase, reports SQL migrations to run manually.', {}, async () => {
34
+ server.tool('setup_migrate', 'Create missing database collections/tables. Additive only — existing collections are skipped. For PocketBase, creates collections via API. For Supabase, reports SQL migrations and recommends running setup_bootstrap for paste-ready SQL.', {}, async () => {
27
35
  try {
28
36
  const skipped = [];
29
37
  const needsMigration = [];
38
+ const migrationsPath = adapter.backend === 'supabase' ? PACKAGE_MIGRATIONS_SUPABASE : PACKAGE_MIGRATIONS_POCKETBASE;
30
39
  for (const schema of COLLECTION_SCHEMAS) {
31
40
  try {
32
41
  const exists = await adapter.collectionExists(schema.name);
@@ -34,18 +43,12 @@ export function registerSetupMigrate(server, adapter) {
34
43
  skipped.push(schema.name);
35
44
  continue;
36
45
  }
37
- if (adapter.backend === 'supabase') {
38
- needsMigration.push({
39
- name: schema.name,
40
- instruction: 'Run SQL migrations from migrations/supabase/ directory.',
41
- });
42
- }
43
- else {
44
- needsMigration.push({
45
- name: schema.name,
46
- instruction: 'Run PocketBase migrations from migrations/pocketbase/ directory.',
47
- });
48
- }
46
+ needsMigration.push({
47
+ name: schema.name,
48
+ instruction: adapter.backend === 'supabase'
49
+ ? `Apply SQL from ${migrationsPath}/. Easiest path: call setup_bootstrap for a paste-ready SQL block.`
50
+ : `Apply migration from ${migrationsPath}/.`,
51
+ });
49
52
  }
50
53
  catch (err) {
51
54
  const msg = err instanceof Error ? err.message : 'Unknown error';
@@ -55,6 +58,7 @@ export function registerSetupMigrate(server, adapter) {
55
58
  }
56
59
  return makeToolResponse({
57
60
  backend: adapter.backend,
61
+ migrations_path: migrationsPath,
58
62
  existing: skipped.length,
59
63
  needs_migration: needsMigration.length,
60
64
  details: {
@@ -62,8 +66,8 @@ export function registerSetupMigrate(server, adapter) {
62
66
  needs_migration: needsMigration.length > 0 ? needsMigration : undefined,
63
67
  },
64
68
  message: adapter.backend === 'pocketbase'
65
- ? `Schema check complete. ${skipped.length} existing, ${needsMigration.length} need migration files. Run PocketBase migrations from migrations/pocketbase/ directory.`
66
- : `Schema check complete. ${skipped.length} existing, ${needsMigration.length} need SQL migrations. Apply migrations from migrations/supabase/ directory.`,
69
+ ? `Schema check complete. ${skipped.length} existing, ${needsMigration.length} need migration files. Apply from ${migrationsPath}/.`
70
+ : `Schema check complete. ${skipped.length} existing, ${needsMigration.length} need SQL. Run setup_bootstrap for a paste-ready block, or apply from ${migrationsPath}/ manually.`,
67
71
  });
68
72
  }
69
73
  catch (error) {
@@ -72,4 +76,4 @@ export function registerSetupMigrate(server, adapter) {
72
76
  }
73
77
  });
74
78
  }
75
- //# sourceMappingURL=setup-migrate.js.map
79
+ //# sourceMappingURL=setup-migrate.js.map
@@ -2,6 +2,13 @@
2
2
  * Tool: setup_status
3
3
  *
4
4
  * Check backend connection, list existing/missing collections, report status.
5
+ *
6
+ * RC-1 fix (incident 2026-05-11): Previously called adapter.listCollections()
7
+ * which uses rpc('get_public_tables') — a custom Postgres function absent from
8
+ * fresh Supabase projects. Fallback to information_schema also failed because
9
+ * PostgREST only exposes the public schema. Now uses the same code path as
10
+ * setup_migrate (adapter.collectionExists per expected collection) so the
11
+ * diagnostic agrees with the data tools.
5
12
  */
6
13
  import { makeToolResponse, makeErrorResponse } from '../shared.js';
7
14
  const EXPECTED_COLLECTIONS = [
@@ -23,20 +30,18 @@ const EXPECTED_COLLECTIONS = [
23
30
  export function registerSetupStatus(server, adapter) {
24
31
  server.tool('setup_status', 'Check database connection status, list existing and missing collections, and report schema readiness.', {}, { readOnlyHint: true }, async () => {
25
32
  try {
26
- // Test connection by listing collections
27
- const existing = await adapter.listCollections();
28
- const existingSet = new Set(existing);
29
33
  const present = [];
30
34
  const missing = [];
31
35
  for (const collection of EXPECTED_COLLECTIONS) {
32
- if (existingSet.has(collection)) {
36
+ const exists = await adapter.collectionExists(collection);
37
+ if (exists) {
33
38
  present.push(collection);
34
39
  }
35
40
  else {
36
41
  missing.push(collection);
37
42
  }
38
43
  }
39
- // Check for settings table and schema version
44
+ const existingSet = new Set(present);
40
45
  let schemaVersion = null;
41
46
  if (existingSet.has('settings')) {
42
47
  try {
@@ -75,4 +80,4 @@ export function registerSetupStatus(server, adapter) {
75
80
  }
76
81
  });
77
82
  }
78
- //# sourceMappingURL=setup-status.js.map
83
+ //# sourceMappingURL=setup-status.js.map
@@ -2,26 +2,24 @@
2
2
  * PocketBase Migration 001: Core schema
3
3
  *
4
4
  * Creates knowledge, decisions, sessions collections.
5
- * PocketBase v0.23+ field format.
6
5
  */
7
6
 
8
7
  /// <reference path="../pb_data/types.d.ts" />
9
8
 
10
9
  migrate((app) => {
10
+ // knowledge collection
11
11
  const knowledge = new Collection({
12
12
  name: 'knowledge',
13
13
  type: 'base',
14
- fields: [
15
- { name: 'type', type: 'select', required: true, maxSelect: 1, values: ['fact', 'knowledge', 'pattern', 'insight', 'lesson', 'reference'] },
16
- { name: 'title', type: 'text', required: true, max: 500 },
17
- { name: 'content', type: 'editor', required: true, maxSize: 50000 },
18
- { name: 'summary', type: 'text', max: 2000 },
14
+ schema: [
15
+ { name: 'type', type: 'select', required: true, options: { values: ['fact', 'knowledge', 'pattern', 'insight', 'lesson', 'reference'] } },
16
+ { name: 'title', type: 'text', required: true, options: { maxSize: 500 } },
17
+ { name: 'content', type: 'editor', required: true, options: { maxSize: 50000 } },
18
+ { name: 'summary', type: 'text', options: { maxSize: 2000 } },
19
19
  { name: 'tags', type: 'json' },
20
- { name: 'source', type: 'text', max: 500 },
21
- { name: 'confidence', type: 'number', min: 0, max: 1 },
20
+ { name: 'source', type: 'text', options: { maxSize: 500 } },
21
+ { name: 'confidence', type: 'number', options: { min: 0, max: 1 } },
22
22
  { name: 'last_validated_at', type: 'date' },
23
- { name: 'created', type: 'autodate', onCreate: true },
24
- { name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
25
23
  ],
26
24
  indexes: [
27
25
  'CREATE INDEX idx_knowledge_type ON knowledge (type)',
@@ -30,47 +28,45 @@ migrate((app) => {
30
28
  });
31
29
  app.save(knowledge);
32
30
 
31
+ // decisions collection
33
32
  const decisions = new Collection({
34
33
  name: 'decisions',
35
34
  type: 'base',
36
- fields: [
37
- { name: 'title', type: 'text', required: true, max: 500 },
38
- { name: 'context', type: 'editor', maxSize: 5000 },
35
+ schema: [
36
+ { name: 'title', type: 'text', required: true, options: { maxSize: 500 } },
37
+ { name: 'context', type: 'editor', options: { maxSize: 5000 } },
39
38
  { name: 'options_considered', type: 'json', required: true },
40
- { name: 'chosen_option', type: 'text', required: true, max: 500 },
41
- { name: 'rationale', type: 'editor', maxSize: 5000 },
42
- { name: 'outcome', type: 'editor', maxSize: 5000 },
39
+ { name: 'chosen_option', type: 'text', required: true, options: { maxSize: 500 } },
40
+ { name: 'rationale', type: 'editor', options: { maxSize: 5000 } },
41
+ { name: 'outcome', type: 'editor', options: { maxSize: 5000 } },
43
42
  { name: 'tags', type: 'json' },
44
- { name: 'created', type: 'autodate', onCreate: true },
45
- { name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
46
43
  ],
47
44
  });
48
45
  app.save(decisions);
49
46
 
47
+ // sessions collection
50
48
  const sessions = new Collection({
51
49
  name: 'sessions',
52
50
  type: 'base',
53
- fields: [
54
- { name: 'title', type: 'text', required: true, max: 500 },
55
- { name: 'summary', type: 'editor', required: true, maxSize: 10000 },
51
+ schema: [
52
+ { name: 'title', type: 'text', required: true, options: { maxSize: 500 } },
53
+ { name: 'summary', type: 'editor', required: true, options: { maxSize: 10000 } },
56
54
  { name: 'session_date', type: 'date' },
57
55
  { name: 'skills_used', type: 'json' },
58
56
  { name: 'files_changed', type: 'json' },
59
57
  { name: 'decisions_made', type: 'json' },
60
58
  { name: 'duration_minutes', type: 'number' },
61
- { name: 'task_id', type: 'text', max: 100 },
62
- { name: 'branch', type: 'text', max: 200 },
59
+ { name: 'task_id', type: 'text', options: { maxSize: 100 } },
60
+ { name: 'branch', type: 'text', options: { maxSize: 200 } },
63
61
  { name: 'patterns_learned', type: 'json' },
64
62
  { name: 'knowledge_created', type: 'number' },
65
63
  { name: 'knowledge_updated', type: 'number' },
66
64
  { name: 'metadata', type: 'json' },
67
- { name: 'created', type: 'autodate', onCreate: true },
68
- { name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
69
65
  ],
70
66
  });
71
67
  app.save(sessions);
72
68
  }, (app) => {
73
- const c1 = app.findCollectionByNameOrId('sessions'); if (c1) app.delete(c1);
74
- const c2 = app.findCollectionByNameOrId('decisions'); if (c2) app.delete(c2);
75
- const c3 = app.findCollectionByNameOrId('knowledge'); if (c3) app.delete(c3);
69
+ app.delete(app.findCollectionByNameOrId('sessions'));
70
+ app.delete(app.findCollectionByNameOrId('decisions'));
71
+ app.delete(app.findCollectionByNameOrId('knowledge'));
76
72
  });
@@ -1,23 +1,21 @@
1
1
  /**
2
2
  * PocketBase Migration 002: Goals and tasks
3
- * PocketBase v0.23+ field format.
4
3
  */
5
4
 
6
5
  /// <reference path="../pb_data/types.d.ts" />
7
6
 
8
7
  migrate((app) => {
8
+ // goals collection
9
9
  const goals = new Collection({
10
10
  name: 'goals',
11
11
  type: 'base',
12
- fields: [
13
- { name: 'title', type: 'text', required: true, max: 500 },
14
- { name: 'description', type: 'editor', maxSize: 5000 },
15
- { name: 'timeframe', type: 'select', required: true, maxSelect: 1, values: ['daily', 'weekly', 'monthly', 'quarterly', 'yearly'] },
16
- { name: 'status', type: 'select', required: true, maxSelect: 1, values: ['active', 'completed', 'paused', 'abandoned'] },
12
+ schema: [
13
+ { name: 'title', type: 'text', required: true, options: { maxSize: 500 } },
14
+ { name: 'description', type: 'editor', options: { maxSize: 5000 } },
15
+ { name: 'timeframe', type: 'select', required: true, options: { values: ['daily', 'weekly', 'monthly', 'quarterly', 'yearly'] } },
16
+ { name: 'status', type: 'select', required: true, options: { values: ['active', 'completed', 'paused', 'abandoned'] } },
17
17
  { name: 'key_results', type: 'json' },
18
18
  { name: 'tags', type: 'json' },
19
- { name: 'created', type: 'autodate', onCreate: true },
20
- { name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
21
19
  ],
22
20
  indexes: [
23
21
  'CREATE INDEX idx_goals_status ON goals (status)',
@@ -26,19 +24,18 @@ migrate((app) => {
26
24
  });
27
25
  app.save(goals);
28
26
 
27
+ // tasks collection
29
28
  const tasks = new Collection({
30
29
  name: 'tasks',
31
30
  type: 'base',
32
- fields: [
33
- { name: 'title', type: 'text', required: true, max: 500 },
34
- { name: 'description', type: 'editor', maxSize: 5000 },
35
- { name: 'status', type: 'select', required: true, maxSelect: 1, values: ['todo', 'in_progress', 'done', 'cancelled'] },
36
- { name: 'priority', type: 'select', required: true, maxSelect: 1, values: ['low', 'medium', 'high', 'urgent'] },
31
+ schema: [
32
+ { name: 'title', type: 'text', required: true, options: { maxSize: 500 } },
33
+ { name: 'description', type: 'editor', options: { maxSize: 5000 } },
34
+ { name: 'status', type: 'select', required: true, options: { values: ['todo', 'in_progress', 'done', 'cancelled'] } },
35
+ { name: 'priority', type: 'select', required: true, options: { values: ['low', 'medium', 'high', 'urgent'] } },
37
36
  { name: 'due_date', type: 'date' },
38
37
  { name: 'tags', type: 'json' },
39
- { name: 'goal_id', type: 'text', max: 100 },
40
- { name: 'created', type: 'autodate', onCreate: true },
41
- { name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
38
+ { name: 'goal_id', type: 'text', options: { maxSize: 100 } },
42
39
  ],
43
40
  indexes: [
44
41
  'CREATE INDEX idx_tasks_status ON tasks (status)',
@@ -47,6 +44,6 @@ migrate((app) => {
47
44
  });
48
45
  app.save(tasks);
49
46
  }, (app) => {
50
- const t = app.findCollectionByNameOrId('tasks'); if (t) app.delete(t);
51
- const g = app.findCollectionByNameOrId('goals'); if (g) app.delete(g);
47
+ app.delete(app.findCollectionByNameOrId('tasks'));
48
+ app.delete(app.findCollectionByNameOrId('goals'));
52
49
  });
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * PocketBase Migration 003: Contacts
3
- * PocketBase v0.23+ field format.
4
3
  */
5
4
 
6
5
  /// <reference path="../pb_data/types.d.ts" />
@@ -9,18 +8,16 @@ migrate((app) => {
9
8
  const contacts = new Collection({
10
9
  name: 'contacts',
11
10
  type: 'base',
12
- fields: [
13
- { name: 'name', type: 'text', required: true, max: 200 },
14
- { name: 'company', type: 'text', max: 200 },
15
- { name: 'role', type: 'text', max: 200 },
16
- { name: 'email', type: 'email' },
17
- { name: 'phone', type: 'text', max: 50 },
18
- { name: 'relationship', type: 'select', maxSelect: 1, values: ['colleague', 'client', 'prospect', 'partner', 'other'] },
19
- { name: 'notes', type: 'editor', maxSize: 5000 },
11
+ schema: [
12
+ { name: 'name', type: 'text', required: true, options: { maxSize: 200 } },
13
+ { name: 'company', type: 'text', options: { maxSize: 200 } },
14
+ { name: 'role', type: 'text', options: { maxSize: 200 } },
15
+ { name: 'email', type: 'email', options: { maxSize: 200 } },
16
+ { name: 'phone', type: 'text', options: { maxSize: 50 } },
17
+ { name: 'relationship', type: 'select', options: { values: ['colleague', 'client', 'prospect', 'partner', 'other'] } },
18
+ { name: 'notes', type: 'editor', options: { maxSize: 5000 } },
20
19
  { name: 'tags', type: 'json' },
21
20
  { name: 'last_contact_date', type: 'date' },
22
- { name: 'created', type: 'autodate', onCreate: true },
23
- { name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
24
21
  ],
25
22
  indexes: [
26
23
  'CREATE INDEX idx_contacts_name ON contacts (name)',
@@ -28,5 +25,5 @@ migrate((app) => {
28
25
  });
29
26
  app.save(contacts);
30
27
  }, (app) => {
31
- const c = app.findCollectionByNameOrId('contacts'); if (c) app.delete(c);
28
+ app.delete(app.findCollectionByNameOrId('contacts'));
32
29
  });
@@ -1,19 +1,17 @@
1
1
  /**
2
2
  * PocketBase Migration 004: Entity aliases + settings
3
- * PocketBase v0.23+ field format.
4
3
  */
5
4
 
6
5
  /// <reference path="../pb_data/types.d.ts" />
7
6
 
8
7
  migrate((app) => {
8
+ // entity_aliases collection
9
9
  const aliases = new Collection({
10
10
  name: 'entity_aliases',
11
11
  type: 'base',
12
- fields: [
13
- { name: 'canonical', type: 'text', required: true, max: 100 },
14
- { name: 'alias', type: 'text', required: true, max: 200 },
15
- { name: 'created', type: 'autodate', onCreate: true },
16
- { name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
12
+ schema: [
13
+ { name: 'canonical', type: 'text', required: true, options: { maxSize: 100 } },
14
+ { name: 'alias', type: 'text', required: true, options: { maxSize: 200 } },
17
15
  ],
18
16
  indexes: [
19
17
  'CREATE UNIQUE INDEX idx_entity_aliases_unique ON entity_aliases (canonical, alias)',
@@ -23,14 +21,13 @@ migrate((app) => {
23
21
  });
24
22
  app.save(aliases);
25
23
 
24
+ // settings collection
26
25
  const settings = new Collection({
27
26
  name: 'settings',
28
27
  type: 'base',
29
- fields: [
30
- { name: 'key', type: 'text', required: true, max: 100 },
28
+ schema: [
29
+ { name: 'key', type: 'text', required: true, options: { maxSize: 100 } },
31
30
  { name: 'value', type: 'editor' },
32
- { name: 'created', type: 'autodate', onCreate: true },
33
- { name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
34
31
  ],
35
32
  indexes: [
36
33
  'CREATE UNIQUE INDEX idx_settings_key ON settings (key)',
@@ -38,6 +35,6 @@ migrate((app) => {
38
35
  });
39
36
  app.save(settings);
40
37
  }, (app) => {
41
- const s = app.findCollectionByNameOrId('settings'); if (s) app.delete(s);
42
- const a = app.findCollectionByNameOrId('entity_aliases'); if (a) app.delete(a);
38
+ app.delete(app.findCollectionByNameOrId('settings'));
39
+ app.delete(app.findCollectionByNameOrId('entity_aliases'));
43
40
  });
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * PocketBase Migration 005: Prospects (CRM)
3
- * PocketBase v0.23+ field format.
4
3
  */
5
4
 
6
5
  /// <reference path="../pb_data/types.d.ts" />
@@ -9,22 +8,20 @@ migrate((app) => {
9
8
  const prospects = new Collection({
10
9
  name: 'prospects',
11
10
  type: 'base',
12
- fields: [
13
- { name: 'name', type: 'text', required: true, max: 200 },
14
- { name: 'email', type: 'email' },
15
- { name: 'company', type: 'text', max: 200 },
16
- { name: 'role', type: 'text', max: 200 },
17
- { name: 'stage', type: 'select', required: true, maxSelect: 1, values: ['new', 'contacted', 'responded', 'interested', 'ready_to_buy', 'proposal_sent', 'negotiating', 'closed_won', 'closed_lost', 'nurturing'] },
18
- { name: 'source', type: 'text', max: 200 },
11
+ schema: [
12
+ { name: 'name', type: 'text', required: true, options: { maxSize: 200 } },
13
+ { name: 'email', type: 'email', options: { maxSize: 200 } },
14
+ { name: 'company', type: 'text', options: { maxSize: 200 } },
15
+ { name: 'role', type: 'text', options: { maxSize: 200 } },
16
+ { name: 'stage', type: 'select', required: true, options: { values: ['new', 'contacted', 'responded', 'interested', 'ready_to_buy', 'proposal_sent', 'negotiating', 'closed_won', 'closed_lost', 'nurturing'] } },
17
+ { name: 'source', type: 'text', options: { maxSize: 200 } },
19
18
  { name: 'estimated_value', type: 'number' },
20
- { name: 'next_action_type', type: 'text', max: 100 },
19
+ { name: 'next_action_type', type: 'text', options: { maxSize: 100 } },
21
20
  { name: 'next_followup_date', type: 'date' },
22
21
  { name: 'last_contact_date', type: 'date' },
23
- { name: 'notes', type: 'editor', maxSize: 10000 },
22
+ { name: 'notes', type: 'editor', options: { maxSize: 10000 } },
24
23
  { name: 'tags', type: 'json' },
25
- { name: 'linkedin_url', type: 'url' },
26
- { name: 'created', type: 'autodate', onCreate: true },
27
- { name: 'updated', type: 'autodate', onCreate: true, onUpdate: true },
24
+ { name: 'linkedin_url', type: 'url', options: { maxSize: 500 } },
28
25
  ],
29
26
  indexes: [
30
27
  'CREATE INDEX idx_prospects_stage ON prospects (stage)',
@@ -32,5 +29,5 @@ migrate((app) => {
32
29
  });
33
30
  app.save(prospects);
34
31
  }, (app) => {
35
- const p = app.findCollectionByNameOrId('prospects'); if (p) app.delete(p);
32
+ app.delete(app.findCollectionByNameOrId('prospects'));
36
33
  });