@flowdular/sdk 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 (119) hide show
  1. package/.ai/platform-capabilities.md +2 -0
  2. package/.ai/references/catalog/migrations/0005_catalog_list_indexes.down.sql +3 -0
  3. package/.ai/references/catalog/migrations/0005_catalog_list_indexes.up.sql +11 -0
  4. package/.ai/references/catalog/module.json +11 -1
  5. package/.ai/references/catalog/package.json +2 -2
  6. package/.ai/references/catalog/spec/module.yaml +25 -4
  7. package/.ai/references/catalog/src/agent/tools.ts +19 -10
  8. package/.ai/references/catalog/src/api/endpoints.ts +150 -10
  9. package/.ai/references/catalog/src/api/list-cursor.ts +83 -0
  10. package/.ai/references/catalog/src/client/CatalogView.tsrx +505 -159
  11. package/.ai/references/catalog/src/client/api.ts +124 -36
  12. package/.ai/references/catalog/src/client/contribution.tsrx +5 -0
  13. package/.ai/references/catalog/src/client/state.ts +169 -3
  14. package/.ai/references/catalog/src/domain/lists.ts +7 -0
  15. package/.ai/references/catalog/src/domain/types.ts +20 -0
  16. package/.ai/references/catalog/src/platform.ts +20 -0
  17. package/.ai/references/catalog/src/services/catalog-service.ts +143 -8
  18. package/.ai/references/catalog/src/services/database-repository.ts +104 -17
  19. package/.ai/references/catalog/src/services/item-export.ts +81 -0
  20. package/.ai/references/catalog/src/services/migration.ts +27 -1
  21. package/.ai/references/catalog/src/services/repository.ts +31 -2
  22. package/.ai/references/catalog/tests/agent-tools.test.ts +6 -5
  23. package/.ai/references/catalog/tests/client-state.test.ts +124 -0
  24. package/.ai/references/catalog/tests/endpoints.test.ts +269 -0
  25. package/.ai/references/catalog/tests/export.test.ts +134 -0
  26. package/.ai/references/catalog/tests/idempotency.test.ts +15 -14
  27. package/.ai/references/catalog/tests/list.test.ts +217 -0
  28. package/.ai/references/catalog/tests/migrations.test.ts +58 -2
  29. package/.ai/references/catalog/tests/module.test.ts +2 -1
  30. package/.ai/references/catalog/tests/support/database.ts +14 -0
  31. package/.ai/references/catalog/translations/en.json +35 -4
  32. package/.ai/references/catalog/translations/pl.json +35 -4
  33. package/.ai/references/catalog.provenance.json +34 -26
  34. package/README.md +1 -1
  35. package/assets/flowdular-banner.webp +0 -0
  36. package/modules/access/module.json +1 -1
  37. package/modules/access/spec/module.yaml +1 -1
  38. package/modules/agents/module.json +1 -1
  39. package/modules/agents/spec/module.yaml +1 -1
  40. package/modules/approvals/module.json +1 -1
  41. package/modules/approvals/spec/module.yaml +1 -1
  42. package/modules/audit/module.json +1 -1
  43. package/modules/audit/spec/module.yaml +1 -1
  44. package/modules/auth/module.json +2 -2
  45. package/modules/auth/package.json +1 -1
  46. package/modules/auth/spec/module.yaml +2 -2
  47. package/modules/auth/src/middleware/authentication.ts +5 -1
  48. package/modules/auth/src/services/auth-service.ts +26 -0
  49. package/modules/auth/src/services/database-repository.ts +9 -0
  50. package/modules/auth/src/services/repository.ts +2 -0
  51. package/modules/automations/module.json +1 -1
  52. package/modules/automations/spec/module.yaml +1 -1
  53. package/modules/connectors/module.json +1 -1
  54. package/modules/connectors/spec/module.yaml +1 -1
  55. package/modules/directory/module.json +1 -1
  56. package/modules/directory/spec/module.yaml +1 -1
  57. package/modules/documents/module.json +1 -1
  58. package/modules/documents/spec/module.yaml +1 -1
  59. package/modules/exports/module.json +1 -1
  60. package/modules/exports/spec/module.yaml +1 -1
  61. package/modules/import/module.json +1 -1
  62. package/modules/import/spec/module.yaml +1 -1
  63. package/modules/metering/module.json +1 -1
  64. package/modules/metering/spec/module.yaml +1 -1
  65. package/modules/notifications/module.json +1 -1
  66. package/modules/notifications/spec/module.yaml +1 -1
  67. package/modules/reports/module.json +1 -1
  68. package/modules/reports/spec/module.yaml +1 -1
  69. package/modules/sandbox/module.json +1 -1
  70. package/modules/sandbox/spec/module.yaml +1 -1
  71. package/modules/search/module.json +1 -1
  72. package/modules/search/spec/module.yaml +1 -1
  73. package/modules/system/migrations/0001_system_module_activations.down.sql +2 -0
  74. package/modules/system/migrations/0001_system_module_activations.up.sql +17 -0
  75. package/modules/system/module.json +3 -2
  76. package/modules/system/package.json +2 -2
  77. package/modules/system/spec/module.yaml +98 -1
  78. package/modules/system/src/client/ModulesView.tsrx +101 -8
  79. package/modules/system/src/client/api.ts +52 -11
  80. package/modules/system/src/client/module-columns.tsrx +32 -6
  81. package/modules/system/src/client/state.ts +26 -0
  82. package/modules/system/src/domain/modules.ts +93 -0
  83. package/modules/system/src/index.ts +13 -0
  84. package/modules/system/src/platform.ts +41 -1
  85. package/modules/system/src/server/capability.ts +6 -0
  86. package/modules/system/src/server/endpoints.ts +125 -5
  87. package/modules/system/src/server/index.ts +26 -0
  88. package/modules/system/src/server/module-catalog.ts +19 -0
  89. package/modules/system/src/server/runtime.ts +102 -0
  90. package/modules/system/src/services/database-repository.ts +91 -0
  91. package/modules/system/src/services/migration.ts +39 -0
  92. package/modules/system/src/services/module-activation-service.ts +245 -0
  93. package/modules/system/src/services/repository.ts +17 -0
  94. package/modules/system/translations/en.json +17 -1
  95. package/modules/system/translations/pl.json +17 -1
  96. package/modules/users/module.json +2 -2
  97. package/modules/users/package.json +1 -1
  98. package/modules/users/spec/module.yaml +6 -9
  99. package/modules/users/src/client/UsersView.tsrx +27 -13
  100. package/modules/users/src/client/member-columns.tsrx +13 -4
  101. package/modules/users/src/client/state.ts +16 -0
  102. package/modules/users/src/services/users-service.ts +3 -0
  103. package/modules/users/translations/en.json +2 -1
  104. package/modules/users/translations/pl.json +2 -1
  105. package/modules/workflows/module.json +2 -2
  106. package/modules/workflows/package.json +1 -1
  107. package/modules/workflows/spec/module.yaml +2 -2
  108. package/modules/workflows/src/services/database-repository.ts +93 -53
  109. package/package.json +1 -1
  110. package/packages/client/package.json +1 -0
  111. package/packages/client/src/ApplicationShell.tsrx +46 -5
  112. package/packages/client/src/index.ts +5 -0
  113. package/packages/client/src/shell/modules.ts +49 -0
  114. package/packages/client/src/shell/types.ts +3 -0
  115. package/packages/contracts/src/index.ts +11 -1
  116. package/packages/server/src/endpoint.ts +17 -1
  117. package/packages/server/src/index.ts +10 -0
  118. package/packages/server/src/module-activation.ts +69 -0
  119. 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
  }
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Flowdular: Build your business platform](https://cdn.jsdelivr.net/npm/@flowdular/sdk@latest/assets/flowdular-banner.png)](https://flowdular.com)
1
+ [![Flowdular: Build your business platform](https://cdn.jsdelivr.net/npm/@flowdular/sdk@latest/assets/flowdular-banner.webp)](https://flowdular.com)
2
2
 
3
3
  # Flowdular SDK
4
4
 
Binary file
@@ -14,7 +14,7 @@
14
14
  "dependencies": [
15
15
  {
16
16
  "id": "system.core",
17
- "range": "^0.7.0"
17
+ "range": "^0.8.0"
18
18
  },
19
19
  {
20
20
  "id": "auth.core",
@@ -12,7 +12,7 @@ capabilities:
12
12
  - translations
13
13
  dependencies:
14
14
  - id: system.core
15
- range: ^0.7.0
15
+ range: ^0.8.0
16
16
  - id: auth.core
17
17
  range: ^0.13.0
18
18
  requires:
@@ -20,7 +20,7 @@
20
20
  "dependencies": [
21
21
  {
22
22
  "id": "system.core",
23
- "range": "^0.7.0"
23
+ "range": "^0.8.0"
24
24
  },
25
25
  {
26
26
  "id": "auth.core",
@@ -14,7 +14,7 @@ capabilities:
14
14
  - cli
15
15
  dependencies:
16
16
  - id: system.core
17
- range: ^0.7.0
17
+ range: ^0.8.0
18
18
  - id: auth.core
19
19
  range: ^0.13.0
20
20
  - id: metering.core
@@ -14,7 +14,7 @@
14
14
  "dependencies": [
15
15
  {
16
16
  "id": "system.core",
17
- "range": "^0.7.0"
17
+ "range": "^0.8.0"
18
18
  },
19
19
  {
20
20
  "id": "auth.core",
@@ -12,7 +12,7 @@ capabilities:
12
12
  - translations
13
13
  dependencies:
14
14
  - id: system.core
15
- range: ^0.7.0
15
+ range: ^0.8.0
16
16
  - id: auth.core
17
17
  range: ^0.13.0
18
18
  tenancy: required
@@ -14,7 +14,7 @@
14
14
  "dependencies": [
15
15
  {
16
16
  "id": "system.core",
17
- "range": "^0.7.0"
17
+ "range": "^0.8.0"
18
18
  },
19
19
  {
20
20
  "id": "auth.core",
@@ -13,7 +13,7 @@ capabilities:
13
13
  - cli
14
14
  dependencies:
15
15
  - id: system.core
16
- range: ^0.7.0
16
+ range: ^0.8.0
17
17
  - id: auth.core
18
18
  range: ^0.13.0
19
19
  provides:
@@ -3,7 +3,7 @@
3
3
  "schemaVersion": 1,
4
4
  "id": "auth.core",
5
5
  "package": "@flowdular/module-auth",
6
- "version": "0.13.10",
6
+ "version": "0.13.11",
7
7
  "profile": "full",
8
8
  "capabilities": ["api", "database", "client", "translations", "cli"],
9
9
  "platform": {
@@ -12,7 +12,7 @@
12
12
  "dependencies": [
13
13
  {
14
14
  "id": "system.core",
15
- "range": "^0.7.0"
15
+ "range": "^0.8.0"
16
16
  }
17
17
  ],
18
18
  "tenancy": "required",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowdular/module-auth",
3
- "version": "0.13.10",
3
+ "version": "0.13.11",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts",
@@ -1,6 +1,6 @@
1
1
  schemaVersion: 2
2
2
  id: auth.core
3
- specVersion: 0.13.10
3
+ specVersion: 0.13.11
4
4
  status: approved
5
5
  name: Authentication Core
6
6
  description: Provides tenant-aware accounts, password authentication, sessions, scopes, tenant roles, API tokens for machine clients, the module settings store, the auth audit trail, security headers, authentication screens, and server authorization middleware.
@@ -13,7 +13,7 @@ capabilities:
13
13
  - cli
14
14
  dependencies:
15
15
  - id: system.core
16
- range: ^0.7.0
16
+ range: ^0.8.0
17
17
  tenancy: required
18
18
  locales:
19
19
  - en
@@ -70,7 +70,11 @@ export function endpointIdentityFromContext(
70
70
  ): EndpointIdentity | null {
71
71
  const principal = principalFromContext(context);
72
72
  return principal
73
- ? { subjectId: principal.accountId, permissions: new Set(principal.scopes) }
73
+ ? {
74
+ subjectId: principal.accountId,
75
+ tenantId: principal.tenantId,
76
+ permissions: new Set(principal.scopes),
77
+ }
74
78
  : null;
75
79
  }
76
80
 
@@ -385,6 +385,8 @@ export const AUDIT_ACTIONS = Object.freeze({
385
385
  roleDeleted: 'auth.role.deleted',
386
386
  settingsUpdated: 'settings.updated',
387
387
  settingsFlagChanged: 'settings.flag.changed',
388
+ moduleActivated: 'system.module.activated',
389
+ moduleDeactivated: 'system.module.deactivated',
388
390
  });
389
391
 
390
392
  export const AUDIT_ACTION_LIST = Object.freeze(Object.values(AUDIT_ACTIONS));
@@ -886,6 +888,13 @@ export class AuthService {
886
888
  );
887
889
  }
888
890
 
891
+ /** How many members hold the owner role, counted the same way for the same screen. */
892
+ countTenantOwners(tenantId: string): Promise<number> {
893
+ return this.#repository.countTenantOwners(
894
+ this.#identifier(tenantId, 'tenantId'),
895
+ );
896
+ }
897
+
889
898
  /**
890
899
  * The identity bindings this workspace's own providers assert about its
891
900
  * members, one bounded page per call. A binding names the account, the
@@ -2912,6 +2921,23 @@ export class AuthService {
2912
2921
  );
2913
2922
  }
2914
2923
 
2924
+ /** The one audit row a committed per-workspace module activation change owes. */
2925
+ async recordModuleActivation(
2926
+ actor: AuthActor,
2927
+ change: { readonly moduleId: string; readonly active: boolean },
2928
+ ): Promise<void> {
2929
+ await this.#audit(
2930
+ actor.tenantId,
2931
+ this.#actorOf(actor),
2932
+ change.active
2933
+ ? AUDIT_ACTIONS.moduleActivated
2934
+ : AUDIT_ACTIONS.moduleDeactivated,
2935
+ 'module',
2936
+ change.moduleId,
2937
+ { active: change.active },
2938
+ );
2939
+ }
2940
+
2915
2941
  async queryAudit(query: AuditQuery): Promise<AuditActorPage> {
2916
2942
  const limit = Math.min(
2917
2943
  Math.max(1, Math.trunc(query.limit)),
@@ -939,6 +939,15 @@ export class DatabaseAuthRepository implements AuthRepository {
939
939
  return rows[0] ? integer(rows[0].total, 'total') : 0;
940
940
  }
941
941
 
942
+ async countTenantOwners(tenantId: string): Promise<number> {
943
+ const rows = await this.#query<CountRow>(tenantId, {
944
+ text: `SELECT count(*) AS total FROM auth_memberships
945
+ WHERE tenant_id = $1 AND role = 'owner'`,
946
+ parameters: [tenantId],
947
+ });
948
+ return rows[0] ? integer(rows[0].total, 'total') : 0;
949
+ }
950
+
942
951
  async findTenantMember(
943
952
  tenantId: string,
944
953
  accountId: string,
@@ -363,6 +363,8 @@ export interface AuthRepository {
363
363
  ): Promise<TenantMemberSortedPage>;
364
364
  /** How many memberships the workspace holds, an index range over 0030. */
365
365
  countTenantMembers(tenantId: string): Promise<number>;
366
+ /** How many of those memberships hold the owner role, whatever their status. */
367
+ countTenantOwners(tenantId: string): Promise<number>;
366
368
  /** One member, for a caller that needs a single account rather than the roll. */
367
369
  findTenantMember(
368
370
  tenantId: string,
@@ -13,7 +13,7 @@
13
13
  "dependencies": [
14
14
  {
15
15
  "id": "system.core",
16
- "range": "^0.7.0"
16
+ "range": "^0.8.0"
17
17
  },
18
18
  {
19
19
  "id": "auth.core",
@@ -13,7 +13,7 @@ capabilities:
13
13
  - cli
14
14
  dependencies:
15
15
  - id: system.core
16
- range: ^0.7.0
16
+ range: ^0.8.0
17
17
  - id: auth.core
18
18
  range: ^0.13.0
19
19
  - id: agents.core
@@ -14,7 +14,7 @@
14
14
  "dependencies": [
15
15
  {
16
16
  "id": "system.core",
17
- "range": "^0.7.0"
17
+ "range": "^0.8.0"
18
18
  },
19
19
  {
20
20
  "id": "auth.core",
@@ -13,7 +13,7 @@ capabilities:
13
13
  - cli
14
14
  dependencies:
15
15
  - id: system.core
16
- range: ^0.7.0
16
+ range: ^0.8.0
17
17
  - id: auth.core
18
18
  range: ^0.13.0
19
19
  tenancy: required
@@ -14,7 +14,7 @@
14
14
  "dependencies": [
15
15
  {
16
16
  "id": "system.core",
17
- "range": "^0.7.0"
17
+ "range": "^0.8.0"
18
18
  },
19
19
  {
20
20
  "id": "auth.core",