create-flowdular 0.3.1 → 0.3.2

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 (43) hide show
  1. package/README.md +1 -1
  2. package/agent-template/.ai/platform-capabilities.md +2 -0
  3. package/agent-template/.ai/references/catalog/migrations/0005_catalog_list_indexes.down.sql +3 -0
  4. package/agent-template/.ai/references/catalog/migrations/0005_catalog_list_indexes.up.sql +11 -0
  5. package/agent-template/.ai/references/catalog/module.json +11 -1
  6. package/agent-template/.ai/references/catalog/package.json +2 -2
  7. package/agent-template/.ai/references/catalog/spec/module.yaml +25 -4
  8. package/agent-template/.ai/references/catalog/src/agent/tools.ts +19 -10
  9. package/agent-template/.ai/references/catalog/src/api/endpoints.ts +150 -10
  10. package/agent-template/.ai/references/catalog/src/api/list-cursor.ts +83 -0
  11. package/agent-template/.ai/references/catalog/src/client/CatalogView.tsrx +505 -159
  12. package/agent-template/.ai/references/catalog/src/client/api.ts +124 -36
  13. package/agent-template/.ai/references/catalog/src/client/contribution.tsrx +5 -0
  14. package/agent-template/.ai/references/catalog/src/client/state.ts +169 -3
  15. package/agent-template/.ai/references/catalog/src/domain/lists.ts +7 -0
  16. package/agent-template/.ai/references/catalog/src/domain/types.ts +20 -0
  17. package/agent-template/.ai/references/catalog/src/platform.ts +20 -0
  18. package/agent-template/.ai/references/catalog/src/services/catalog-service.ts +143 -8
  19. package/agent-template/.ai/references/catalog/src/services/database-repository.ts +104 -17
  20. package/agent-template/.ai/references/catalog/src/services/item-export.ts +81 -0
  21. package/agent-template/.ai/references/catalog/src/services/migration.ts +27 -1
  22. package/agent-template/.ai/references/catalog/src/services/repository.ts +31 -2
  23. package/agent-template/.ai/references/catalog/tests/agent-tools.test.ts +6 -5
  24. package/agent-template/.ai/references/catalog/tests/client-state.test.ts +124 -0
  25. package/agent-template/.ai/references/catalog/tests/endpoints.test.ts +269 -0
  26. package/agent-template/.ai/references/catalog/tests/export.test.ts +134 -0
  27. package/agent-template/.ai/references/catalog/tests/idempotency.test.ts +15 -14
  28. package/agent-template/.ai/references/catalog/tests/list.test.ts +217 -0
  29. package/agent-template/.ai/references/catalog/tests/migrations.test.ts +58 -2
  30. package/agent-template/.ai/references/catalog/tests/module.test.ts +2 -1
  31. package/agent-template/.ai/references/catalog/tests/support/database.ts +14 -0
  32. package/agent-template/.ai/references/catalog/translations/en.json +35 -4
  33. package/agent-template/.ai/references/catalog/translations/pl.json +35 -4
  34. package/agent-template/.ai/references/catalog.provenance.json +34 -26
  35. package/agent-template/docs/module-distribution.md +2 -2
  36. package/agent-template/docs/modules.md +13 -0
  37. package/assets/flowdular-banner.webp +0 -0
  38. package/package.json +1 -1
  39. package/template/default/modules/example/package.json +1 -1
  40. package/template/default/package.json +1 -1
  41. package/template/default/platform/package.json +1 -1
  42. package/template/default/platform/src/generated/modules.server.ts +7 -4
  43. package/assets/flowdular-banner.png +0 -0
@@ -87,6 +87,7 @@ describe('catalog migrations', () => {
87
87
  it('declares forced PostgreSQL row security for every tenant table', () => {
88
88
  for (const migration of databaseMigrations) {
89
89
  const sql = migration.sql.postgresql ?? '';
90
+ if (!sql.includes('CREATE TABLE')) continue;
90
91
  expect(sql).toContain('ENABLE ROW LEVEL SECURITY');
91
92
  expect(sql).toContain('FORCE ROW LEVEL SECURITY');
92
93
  expect(sql).toContain("current_setting('coreloom.tenant_id', true)");
@@ -110,9 +111,9 @@ describe('catalog migrations', () => {
110
111
  transaction.execute({
111
112
  text: `INSERT INTO catalog_items
112
113
  (id, tenant_id, sku, sku_normalized, name, kind, unit,
113
- base_price_minor, currency, status, created_at)
114
+ base_price_minor, currency, status, created_at, updated_at)
114
115
  VALUES ('item-1', 'tenant-a', 'SKU-1', 'sku-1', 'Bolt', 'product',
115
- 'pcs', 500, 'EUR', 'active', 1)`,
116
+ 'pcs', 500, 'EUR', 'active', 1, 1)`,
116
117
  }),
117
118
  { tenantId: 'tenant-a', access: 'write' },
118
119
  );
@@ -139,6 +140,61 @@ describe('catalog migrations', () => {
139
140
  ).toEqual([{ sku: 'SKU-1' }]);
140
141
  });
141
142
 
143
+ it('adds the update time and the list indexes, and refuses a half-applied schema', async () => {
144
+ await apply();
145
+ const schema = lease.database.schema;
146
+ expect(await schema.hasColumn('catalog_items', 'updated_at')).toBe(true);
147
+ expect(await schema.hasIndex('catalog_items_tenant_name_idx')).toBe(true);
148
+ expect(await schema.hasIndex('catalog_items_tenant_updated_idx')).toBe(
149
+ true,
150
+ );
151
+
152
+ await lease.database.execute({
153
+ text: 'DROP INDEX catalog_items_tenant_name_idx',
154
+ });
155
+ await lease.database.execute({
156
+ text: `DELETE FROM ${DATABASE_MIGRATION_LEDGER} WHERE namespace = 'catalog.core'`,
157
+ });
158
+ expect((await status()).at(-1)).toMatchObject({
159
+ id: '0005_catalog_list_indexes',
160
+ state: 'partial',
161
+ });
162
+ });
163
+
164
+ it('backfills the update time of rows that predate it', async () => {
165
+ await runDatabaseMigrations(
166
+ lease.database,
167
+ 'catalog.core',
168
+ databaseMigrations.slice(0, 4),
169
+ );
170
+ await lease.database.transaction(
171
+ (transaction) =>
172
+ transaction.execute({
173
+ text: `INSERT INTO catalog_items
174
+ (id, tenant_id, sku, sku_normalized, name, kind, unit,
175
+ base_price_minor, currency, status, created_at)
176
+ VALUES ('item-1', 'tenant-a', 'SKU-1', 'sku-1', 'Bolt', 'product',
177
+ 'pcs', 500, 'EUR', 'active', 1234)`,
178
+ }),
179
+ { tenantId: 'tenant-a', access: 'write' },
180
+ );
181
+ expect((await apply()).at(-1)).toMatchObject({
182
+ id: '0005_catalog_list_indexes',
183
+ action: 'applied',
184
+ });
185
+ expect(
186
+ (
187
+ await lease.database.transaction(
188
+ (transaction) =>
189
+ transaction.query<{ updated_at: string | number }>({
190
+ text: 'SELECT updated_at FROM catalog_items',
191
+ }),
192
+ { tenantId: 'tenant-a', access: 'read' },
193
+ )
194
+ ).rows.map((row) => Number(row.updated_at)),
195
+ ).toEqual([1234]);
196
+ });
197
+
142
198
  it('runs clean on a second migration pass', async () => {
143
199
  await apply();
144
200
 
@@ -13,6 +13,7 @@ import {
13
13
  CatalogServiceError,
14
14
  } from '../src/services/catalog-service.ts';
15
15
  import {
16
+ allItems,
16
17
  closeCatalogTestDatabases,
17
18
  createCatalogTestDatabase,
18
19
  type CatalogTestDatabase,
@@ -161,7 +162,7 @@ describe('catalog.core', () => {
161
162
  },
162
163
  TEST_ACTOR,
163
164
  );
164
- expect(await service.list('tenant-b')).toEqual([]);
165
+ expect(await allItems(service, 'tenant-b')).toEqual([]);
165
166
  });
166
167
 
167
168
  it('keeps lifecycle revisions append-only and tenant-scoped', async () => {
@@ -4,6 +4,20 @@ import {
4
4
  DatabaseCatalogRepository,
5
5
  migrateCatalogDatabase,
6
6
  } from '../../src/services/database-repository.ts';
7
+ import {
8
+ FIRST_LIST_PAGE,
9
+ type CatalogService,
10
+ } from '../../src/services/catalog-service.ts';
11
+ import type { CatalogItem } from '../../src/domain/types.ts';
12
+
13
+ /** The first page at the ceiling in SKU order: every item of a small test tenant. */
14
+ export async function allItems(
15
+ service: CatalogService,
16
+ tenantId: string,
17
+ ): Promise<readonly CatalogItem[]> {
18
+ return (await service.listPage(tenantId, { ...FIRST_LIST_PAGE, sort: 'sku' }))
19
+ .items;
20
+ }
7
21
 
8
22
  const TENANT_TABLES = [
9
23
  'catalog_items',
@@ -7,6 +7,16 @@
7
7
  "drawer.editTitle": "Edit catalog item",
8
8
  "action.edit": "Edit",
9
9
  "action.history": "History",
10
+ "action.archiveSelected": "Archive selected",
11
+ "action.restoreSelected": "Restore selected",
12
+ "action.archiveRefused": "Select at least one active item.",
13
+ "action.restoreRefused": "Select at least one archived item.",
14
+ "action.close": "Close",
15
+ "archiveMany.title": "Archive selected items",
16
+ "archiveMany.description": "Archive {count} selected items? Archived items can be restored later.",
17
+ "common.messages": "Messages",
18
+ "denied.title": "Catalog access denied",
19
+ "denied.description": "Your account may not read catalog items in this workspace.",
10
20
  "delete.title": "Delete catalog item",
11
21
  "delete.description": "Delete {name} permanently? This cannot be undone.",
12
22
  "delete.drawerDescription": "This permanently removes the archived catalog item.",
@@ -17,7 +27,13 @@
17
27
  "error.delete": "Could not delete the catalog item.",
18
28
  "error.history": "Could not load catalog item history.",
19
29
  "error.load": "Could not load catalog items.",
30
+ "error.archiveMany": "Could not archive the selected items.",
31
+ "error.restoreMany": "Could not restore the selected items.",
20
32
  "error.request": "The catalog operation failed.",
33
+ "export.action": "Export CSV",
34
+ "export.started": "The catalog export started. It appears on the Exports screen once the file is ready.",
35
+ "export.link": "Open Exports",
36
+ "export.error": "Could not start the export.",
21
37
  "form.cancel": "Cancel",
22
38
  "form.currency": "Currency",
23
39
  "form.kind": "Kind",
@@ -37,27 +53,42 @@
37
53
  "status.active": "Active",
38
54
  "status.archived": "Archived",
39
55
  "filters.label": "Filters",
40
- "filters.includeArchived": "Include archived items",
56
+ "filters.kind": "Kind",
57
+ "filters.allKinds": "All kinds",
58
+ "filters.status": "Status",
59
+ "filters.allStatuses": "All statuses",
41
60
  "lifecycle.title": "Lifecycle",
42
61
  "lifecycle.description": "Archive an item before deleting it permanently.",
43
62
  "lifecycle.history": "View history",
44
63
  "lifecycle.archive": "Archive item",
45
64
  "lifecycle.restore": "Restore item",
46
65
  "lifecycle.delete": "Delete item",
66
+ "notice.archivedMany": "Archived {updated}, missing {missing}, refused {refused}.",
67
+ "notice.restoredMany": "Restored {updated}, missing {missing}, refused {refused}.",
47
68
  "page.action.new": "New item",
48
69
  "page.action.refresh": "Refresh",
49
70
  "page.description": "Products and services with stable tenant-scoped SKUs.",
50
71
  "page.eyebrow": "Commercial master data",
51
72
  "page.title": "Catalog",
73
+ "pagination.label": "Catalog pages",
74
+ "pagination.previous": "Previous",
75
+ "pagination.next": "Next",
76
+ "pagination.size": "Items per page",
77
+ "pagination.summary": "Page {page} · from item {from}",
52
78
  "price.fallback": "{amount} {currency} minor",
79
+ "selection.label": "Select all items on screen",
80
+ "selection.rowLabel": "Select {name}",
81
+ "selection.clear": "Clear selection",
82
+ "selection.summary": "{count} selected",
53
83
  "table.caption": "Catalog items",
54
- "table.column.item": "Item",
84
+ "table.column.name": "Name",
85
+ "table.column.sku": "SKU",
55
86
  "table.column.kind": "Kind",
56
87
  "table.column.price": "Price",
57
88
  "table.column.status": "Status",
89
+ "table.column.updated": "Updated",
58
90
  "table.column.actions": "Actions",
59
- "table.count.one": "1 item",
60
- "table.count.other": "items: {count}",
91
+ "table.count.page": "{count} on this page",
61
92
  "table.empty.hint": "Create the first product or service.",
62
93
  "table.empty.title": "No catalog items yet",
63
94
  "table.emptyFiltered.hint": "No item in this workspace matches the current filter.",
@@ -7,6 +7,16 @@
7
7
  "drawer.editTitle": "Edytuj pozycję katalogu",
8
8
  "action.edit": "Edytuj",
9
9
  "action.history": "Historia",
10
+ "action.archiveSelected": "Archiwizuj zaznaczone",
11
+ "action.restoreSelected": "Przywróć zaznaczone",
12
+ "action.archiveRefused": "Zaznacz co najmniej jedną aktywną pozycję.",
13
+ "action.restoreRefused": "Zaznacz co najmniej jedną zarchiwizowaną pozycję.",
14
+ "action.close": "Zamknij",
15
+ "archiveMany.title": "Archiwizuj zaznaczone pozycje",
16
+ "archiveMany.description": "Zarchiwizować zaznaczone pozycje ({count})? Zarchiwizowane pozycje można później przywrócić.",
17
+ "common.messages": "Komunikaty",
18
+ "denied.title": "Brak dostępu do katalogu",
19
+ "denied.description": "Twoje konto nie może odczytywać pozycji katalogu w tej przestrzeni roboczej.",
10
20
  "delete.title": "Usuń pozycję katalogu",
11
21
  "delete.description": "Usunąć trwale pozycję {name}? Tej operacji nie można cofnąć.",
12
22
  "delete.drawerDescription": "Ta operacja trwale usuwa zarchiwizowaną pozycję katalogu.",
@@ -17,7 +27,13 @@
17
27
  "error.delete": "Nie udało się usunąć pozycji katalogu.",
18
28
  "error.history": "Nie udało się wczytać historii pozycji katalogu.",
19
29
  "error.load": "Nie udało się wczytać pozycji katalogu.",
30
+ "error.archiveMany": "Nie udało się zarchiwizować zaznaczonych pozycji.",
31
+ "error.restoreMany": "Nie udało się przywrócić zaznaczonych pozycji.",
20
32
  "error.request": "Operacja na katalogu nie powiodła się.",
33
+ "export.action": "Eksportuj CSV",
34
+ "export.started": "Eksport katalogu wystartował. Pojawi się na ekranie Eksporty, gdy plik będzie gotowy.",
35
+ "export.link": "Otwórz Eksporty",
36
+ "export.error": "Nie udało się rozpocząć eksportu.",
21
37
  "form.cancel": "Anuluj",
22
38
  "form.currency": "Waluta",
23
39
  "form.kind": "Rodzaj",
@@ -37,27 +53,42 @@
37
53
  "status.active": "Aktywna",
38
54
  "status.archived": "Zarchiwizowana",
39
55
  "filters.label": "Filtry",
40
- "filters.includeArchived": "Pokaż zarchiwizowane pozycje",
56
+ "filters.kind": "Rodzaj",
57
+ "filters.allKinds": "Wszystkie rodzaje",
58
+ "filters.status": "Status",
59
+ "filters.allStatuses": "Wszystkie statusy",
41
60
  "lifecycle.title": "Cykl życia",
42
61
  "lifecycle.description": "Zarchiwizuj pozycję przed jej trwałym usunięciem.",
43
62
  "lifecycle.history": "Zobacz historię",
44
63
  "lifecycle.archive": "Archiwizuj pozycję",
45
64
  "lifecycle.restore": "Przywróć pozycję",
46
65
  "lifecycle.delete": "Usuń pozycję",
66
+ "notice.archivedMany": "Zarchiwizowano {updated}, brak {missing}, odrzucono {refused}.",
67
+ "notice.restoredMany": "Przywrócono {updated}, brak {missing}, odrzucono {refused}.",
47
68
  "page.action.new": "Nowa pozycja",
48
69
  "page.action.refresh": "Odśwież",
49
70
  "page.description": "Produkty i usługi ze stałym SKU w obrębie przestrzeni roboczej.",
50
71
  "page.eyebrow": "Dane podstawowe sprzedaży",
51
72
  "page.title": "Katalog",
73
+ "pagination.label": "Strony katalogu",
74
+ "pagination.previous": "Poprzednia",
75
+ "pagination.next": "Następna",
76
+ "pagination.size": "Pozycji na stronie",
77
+ "pagination.summary": "Strona {page} · od pozycji {from}",
52
78
  "price.fallback": "{amount} {currency} w groszach",
79
+ "selection.label": "Zaznacz wszystkie pozycje na ekranie",
80
+ "selection.rowLabel": "Zaznacz {name}",
81
+ "selection.clear": "Wyczyść zaznaczenie",
82
+ "selection.summary": "Zaznaczono: {count}",
53
83
  "table.caption": "Pozycje katalogu",
54
- "table.column.item": "Pozycja",
84
+ "table.column.name": "Nazwa",
85
+ "table.column.sku": "SKU",
55
86
  "table.column.kind": "Rodzaj",
56
87
  "table.column.price": "Cena",
57
88
  "table.column.status": "Status",
89
+ "table.column.updated": "Zmieniono",
58
90
  "table.column.actions": "Akcje",
59
- "table.count.one": "1 pozycja",
60
- "table.count.other": "pozycje: {count}",
91
+ "table.count.page": "Na tej stronie: {count}",
61
92
  "table.empty.hint": "Dodaj pierwszy produkt lub usługę.",
62
93
  "table.empty.title": "Nie ma jeszcze pozycji katalogu",
63
94
  "table.emptyFiltered.hint": "Żadna pozycja w tej przestrzeni roboczej nie pasuje do filtra.",
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "id": "catalog.core",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "repository": "Flowdular/official-modules",
5
- "sourceCommit": "be391ac460e53e06a812fc9f0ea1260e2daa058f",
6
- "artifactSha256": "7c645f660f44bb4c4540d32dd188d643aafb067f4c505a2722a7f2605a7b54b8",
5
+ "sourceCommit": "edad0ec198877c0359f11512b7c0055f87d5f8b4",
6
+ "artifactSha256": "3e1c8530eed822c35e12f24545492bf7614bbd47e3a9255a869423d7e1662ef4",
7
7
  "files": {
8
8
  "LICENSE": "155d722071bad9d0d832482e06fd5a47f7034389cb63aabcb39f4282aa3499fc",
9
9
  "migrations/0001_catalog_core.down.sql": "0962a1edf0bde959cd4facccf77bd5b634d6cc25f125a0c79a61770c8871093f",
@@ -14,43 +14,51 @@
14
14
  "migrations/0003_catalog_history_service_actors.up.sql": "7caae787ca2fc1e331ccbfda4df5982beec76136e8dc486aab5532ccd338a722",
15
15
  "migrations/0004_catalog_idempotency_ledger.down.sql": "8ba72d8edc4d25ecf6d041888cf321690c25afdaf3723812ddd7d391d75370aa",
16
16
  "migrations/0004_catalog_idempotency_ledger.up.sql": "41e86ba2051159c6cc9672bfae17c1d02051da975f1c43c566a2b66592673cb0",
17
+ "migrations/0005_catalog_list_indexes.down.sql": "3dd547d738e75513abef502afc4bf9b51851ec9405b6b719c6134b897c5b1776",
18
+ "migrations/0005_catalog_list_indexes.up.sql": "dc3ece30b93fba0c91be58f772d1ae421cfbba49439e8c72a06aead6720f5db5",
17
19
  "migrations/README.md": "2a15fd001713c2552a3c82b186246aa5fec136f43143cc2d9aa15bd491a09d41",
18
- "module.json": "f2c66d18e3d54ef1ad8dcc3d26d1ee39a2982463dd3bc3fb26b159039933ed86",
19
- "package.json": "3e05cf299b5cc5d948e4b3569bd93f2648a30b22f4f14d26bb31548a79529092",
20
- "spec/module.yaml": "633a719d765d9779e2bf4ae0a608251767a62cbb4e15305dc13afcb1e52f14c9",
20
+ "module.json": "4a4e5b5414f97a6b023c0ed64d0df313aa3ab4e8771f8e33137e589818ad20fd",
21
+ "package.json": "6dd4972624c2a141b038c024137a29b0e5223a28f9021653f2aa6517fb464b56",
22
+ "spec/module.yaml": "c30518bd28e5d184ef122f194f5c1308ff5c522df6638085ccc8121a78d40971",
21
23
  "src/acl/permissions.ts": "3375521a5a229736ffac5a948140ec347dc7a7e4fba80e778c0baaf9e0622590",
22
- "src/agent/tools.ts": "3204d20886d2b864482adc1ed3303c653aebd207e16c4fce0c67a4979ea8db07",
23
- "src/api/endpoints.ts": "34f1036e759353550be2acf67c6fa254afc790942d09489db2e76a082df5c3a3",
24
+ "src/agent/tools.ts": "3403db7954906dc2c79227ba0420b5cb4bfb57063b7cfc285e6e556f16be9266",
25
+ "src/api/endpoints.ts": "205ef36d058f246f99b77336e7246a23aaca611c696a1f6f09758f66e8a93a29",
26
+ "src/api/list-cursor.ts": "dcb763edc0128a3aac95e9ca3c5158611a7f2c6a4c71484438c89b7f4d79bb0d",
24
27
  "src/client/CatalogHistoryDrawer.tsrx": "6cdba5249be81e70da703cb34c1a2210943ce5e5fb596ee77cf7952e2acf3ee3",
25
28
  "src/client/CatalogItemForm.tsrx": "7dafa4149975d9e7d5784c8a5391b5b64c2405991aaead713d94b61c41168c2c",
26
- "src/client/CatalogView.tsrx": "7c507931f33afea8414b002e3ad815740c8db75016c53d8a81effc6427bd958d",
27
- "src/client/api.ts": "5f404f6b25777bc8fe2eab5a35424593abc8e879fb83fa2ae4d637fc23fdce4b",
28
- "src/client/contribution.tsrx": "e95f755882005ee090c99a119ce2dda2ec70420daed4ce06021193905858c62f",
29
+ "src/client/CatalogView.tsrx": "bc1e543690e07d52b51626832f36afe3fde87bf2dc45642324507df8384d7303",
30
+ "src/client/api.ts": "eaa55af81d26aa0190d19b3102c363c1ee25b2bdc05436446ac95dcde62aea75",
31
+ "src/client/contribution.tsrx": "6cbf79e6188bdf24b9cec4a06ae621e03efd5d0c76ad72b28828eee9155ab893",
29
32
  "src/client/index.ts": "03eb0664cdafccb66cb25f2e1b07e6959c2dfa9bdbfffc3e7edf9b535ba4949a",
30
33
  "src/client/navigation-copy.ts": "9a8757e64f4001a93e2bc2861c686a19f791f9d67a396d9e658023b1ec4cea8f",
31
- "src/client/state.ts": "bc054bc269026b6abf60d99a2fc5dd94eb8a488fc1ce4c81a1ae1065d9db576b",
32
- "src/domain/types.ts": "4b26be80954d792b09a3a34d29d845b9f6119640be505daba325fec820e06ef6",
34
+ "src/client/state.ts": "61601f6037c4bb8a709b69b10c1a37e59b45b0c941240c436f7861c2b034dcc1",
35
+ "src/domain/lists.ts": "83749967f99fa0c4f44abc0c614108b844321e50ce6408553215e6233ad6e350",
36
+ "src/domain/types.ts": "374efeb24c4f7d41e3c660336e721fa362418579837d44a46724076d03287a0b",
33
37
  "src/domain/variables.ts": "131130e57838235b1f3fcb799da44baeae16522bb346d522827e022305c110b5",
34
38
  "src/index.ts": "5b27943018db9a18651d62add7d6f8397d7c492a204381fae74547b99b91990d",
35
- "src/platform.ts": "b5d809b071af812877f3fb09cce3ced4cdaed8f62611c3eb6d5fcaf6cd7333cb",
39
+ "src/platform.ts": "f048082a33c1c688e4952750c762c8b65a4626ff52cc9c4eaca2156014bac631",
36
40
  "src/server/index.ts": "90eae107d863418124e5d10c4d1fe8a119bd14da605b36de708c18d8500c4b5e",
37
41
  "src/server/runtime.ts": "5f4389a6adf8265f6737c489baf686f63cc3550662103d28b2da9800dd9f5b63",
38
- "src/services/catalog-service.ts": "51f1dcd7c0335f7b417fb45ba700c0098e7193246ee91659c27cab8608282693",
42
+ "src/services/catalog-service.ts": "66e61dfbd9797d31e6814c18efcd21cfcb7b220f7df22677868f486f3fa0e9d1",
39
43
  "src/services/data-classes.ts": "ea09b1a7def59fee43d57ca7dab6fdb4d646b51df2cacb60af96676c43602b33",
40
- "src/services/database-repository.ts": "e6ff61ba197899d5618a02ce743c90d4bf09ae64dcfd1bd3a9985b18a2d23e80",
44
+ "src/services/database-repository.ts": "1b72ce91a2f85f6572825766781a806d94fe0d2abb91872ee9acae3b1e70db2e",
41
45
  "src/services/index.ts": "a08aefe5101814538666f861536287fc382e6c655d62ba9b9ef4bbfbc6ede4af",
42
- "src/services/migration.ts": "89c8e333482c80bc819deba0cf4f4e37e94137933af0f6503f4f9ebcb4f5f371",
43
- "src/services/repository.ts": "98d27a40ff02137ec0c6d8799d9a853d40a76eab2564dd500806c4e84f6dc3a3",
46
+ "src/services/item-export.ts": "b2a4613b43a98d0d41699481bb5e85a770f9a60cb2241579b065effb6936c80b",
47
+ "src/services/migration.ts": "57c9a857d4074973e14b5e3a1c4548a92ad0faca371419e7daf2334f3ef701ab",
48
+ "src/services/repository.ts": "9f560033875d05654148b4a11897465ee4eefa0d7fa701439e8f562da9fba1db",
44
49
  "src/services/target-idempotency.ts": "7111b56a30b691eaaaa64f5691895ac94b6e0735b0079794835bbbeba5102cb0",
45
- "tests/agent-tools.test.ts": "3cdda30b5454d58c6e8791a98e3daf6fe58b9852cd5c437b3614c3f6123baa91",
50
+ "tests/agent-tools.test.ts": "7762c14bc353ee3085df46a91cf370eb5c25b2edb7096f0a4b0e033f220ea726",
51
+ "tests/client-state.test.ts": "ea146c793b14d0f9bc5e36d92e92894ced6531b8fcbd11c8be1486b62313b8c3",
46
52
  "tests/data-classes.test.ts": "bea08e4d1d7380ccdebc211374b74a6034714b192fd98bfb90a2218067b35658",
47
- "tests/endpoints.test.ts": "24d05c7dd52820f90c777a1b814529f5188a9a5a9daeaacda07314f17b7483cc",
48
- "tests/idempotency.test.ts": "f2e0896635bcd95271b66181daa2c084411913cdf864ae8b4814f2beeb8c2943",
49
- "tests/migrations.test.ts": "47d3d741eddde8677be287730d2f96af556b4b7bf0d8378f9a12a7309af17d78",
50
- "tests/module.test.ts": "01bedfa1540470cc87c808c75120f6ede7c3b8e3d81e340d2076af0a66f1c7ab",
51
- "tests/support/database.ts": "f108cf6390da7669f8935301fac125a7e5653f06ba75327f4bdc28fdd409ba73",
52
- "translations/en.json": "406a40b77e3778e83cd4d30e7b3aee8f89c75baf7bc52f1befc6d16f3b877ab3",
53
- "translations/pl.json": "db6325b4980d978e1a3098758b5259a68e32469baa09d194b9d6746462776d27",
53
+ "tests/endpoints.test.ts": "a2d700f4c3eb6fbbef5c57ed5591017a27c98e17fe84fba8426fcd3a2820b210",
54
+ "tests/export.test.ts": "de83066272cee5f7f5736d636d7b9b97dab09778ba661816e401bf926b826402",
55
+ "tests/idempotency.test.ts": "faea4a2cfa04e81b7155399cba807af4c1ba4448cdfeab733d1afe801d27734f",
56
+ "tests/list.test.ts": "97605b50020841cbdac62f9d44b65f0f7dce9c4c4972af6409afeb6b9b66e59a",
57
+ "tests/migrations.test.ts": "fb796aecedb997f6395c2c15e45bf4abfae4677953fa8cb420c94e9cfba8222f",
58
+ "tests/module.test.ts": "af961a5bcddc41d304d14841f8810976f43f46b46ab7814142fdb921a773e92a",
59
+ "tests/support/database.ts": "4ff6d093ca2e80baab3dea476018661473d6474db63d67655f1edc667b8472ac",
60
+ "translations/en.json": "a86d70e02ccf46a2a132d5bee0de7c528c0eb56a85c269a78e0c2e7e0de24e37",
61
+ "translations/pl.json": "8157d83467e9dd8d9700e2fb188f1a730073f805d2a8b3f0ba98f02f2ca9a264",
54
62
  "tsconfig.json": "140bb4775df421146563d13d2f78ff74cb3c2decab6737db32938339248a92ba",
55
63
  "vitest.config.ts": "d13725b13c3712ec91c1646e9226fb172eec2599f72f4e6608bece9912e2bd81"
56
64
  }
@@ -8,8 +8,8 @@ release artifacts. The landing is in [Flowdular/landing](https://github.com/Flow
8
8
  ```sh
9
9
  pnpm flowdular module search expenses
10
10
  pnpm flowdular module info expenses.core
11
- pnpm flowdular module install expenses.core@0.7.1
12
- pnpm flowdular module install expenses.core@0.7.1 --apply
11
+ pnpm flowdular module install expenses.core@0.8.0
12
+ pnpm flowdular module install expenses.core@0.8.0 --apply
13
13
  pnpm flowdular module enable expenses.core --apply
14
14
  pnpm flowdular module validate --locked
15
15
  ```
@@ -147,6 +147,19 @@ A running `pnpm dev` picks the change up live: the octane plugin reloads server
147
147
  routes when the generated composition changes and the client hot-reloads, so no
148
148
  rebuild is needed. `pnpm dev` and `pnpm build` run the sync automatically.
149
149
 
150
+ ### Per-workspace activation
151
+
152
+ Enabling composes a module into the application; whether a given workspace
153
+ uses it is an owner's decision made in Administration, Modules. Every composed
154
+ module is active until an owner deactivates it there, and the change applies to
155
+ that workspace alone: its endpoints answer 403 `MODULE_INACTIVE`, and the shell
156
+ hides the module's navigation, views, widgets and command search. `system.core`,
157
+ `auth.core`, `users.core` and `profile.core` cannot be deactivated, and neither
158
+ can a module another active module depends on; the refusal names the
159
+ dependents. Each change is audited. The state lives in `system.core` and is
160
+ published to other modules as the `system.modules.v1` capability; see
161
+ `.ai/platform-capabilities.md`.
162
+
150
163
  ## Versions, ranges and capabilities
151
164
 
152
165
  A module carries one version in three places: `module.json` `version`,
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-flowdular",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "description": "Scaffold a Flowdular application: the platform, one example module and the secrets a fresh install needs.",
6
6
  "license": "MIT",
@@ -16,7 +16,7 @@
16
16
  "dependencies": {
17
17
  "octane": "0.1.51",
18
18
  "segment-state": "0.2.1",
19
- "@flowdular/sdk": "0.3.1"
19
+ "@flowdular/sdk": "0.3.2"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@tsrx/typescript-plugin": "0.3.120",
@@ -25,7 +25,7 @@
25
25
  "devDependencies": {
26
26
  "@tsrx/prettier-plugin": "0.3.120",
27
27
  "prettier": "3.6.2",
28
- "flowdular": "0.3.1",
28
+ "flowdular": "0.3.2",
29
29
  "rulesync": "16.21.0"
30
30
  }
31
31
  }
@@ -15,7 +15,7 @@
15
15
  "@octanejs/vite-plugin": "0.1.51",
16
16
  "octane": "0.1.51",
17
17
  "pg": "8.23.0",
18
- "@flowdular/sdk": "0.3.1"
18
+ "@flowdular/sdk": "0.3.2"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@octanejs/app-core": "0.0.47",
@@ -5,7 +5,10 @@ import type {
5
5
  PlatformServerContext,
6
6
  } from '@flowdular/sdk/modules/auth/server';
7
7
  import type { WebMount } from '@flowdular/sdk/server';
8
- import { createModuleMetrics } from '@flowdular/sdk/server';
8
+ import {
9
+ bindModuleCompositions,
10
+ createModuleMetrics,
11
+ } from '@flowdular/sdk/server';
9
12
  import { createServerComposition as system_core } from '@flowdular/sdk/modules/system/platform';
10
13
  import { createServerComposition as access_core } from '@flowdular/sdk/modules/access/platform';
11
14
  import { createServerComposition as reports_core } from '@flowdular/sdk/modules/reports/platform';
@@ -30,14 +33,14 @@ import { createServerComposition as users_core } from '@flowdular/sdk/modules/us
30
33
  export function composeModuleServer(
31
34
  context: PlatformServerContext,
32
35
  ): readonly PlatformServerComposition[] {
33
- return [
36
+ return bindModuleCompositions([
34
37
  {
35
38
  ...system_core({
36
39
  ...context,
37
40
  agentDefinitions: context.agentDefinitions.forModule('system.core'),
38
41
  dataClasses: context.dataClasses.forModule('system.core'),
39
42
  capabilities: context.capabilities.forModule('system.core', {
40
- provides: [],
43
+ provides: ['system.modules.v1'],
41
44
  requires: [],
42
45
  }),
43
46
  metrics: createModuleMetrics('system.core'),
@@ -316,7 +319,7 @@ export function composeModuleServer(
316
319
  }),
317
320
  moduleId: 'users.core',
318
321
  },
319
- ];
322
+ ]);
320
323
  }
321
324
 
322
325
  export const moduleWebMounts: readonly WebMount[] = [] as const;
Binary file