@kernhq/module-inventory 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +55 -9
  2. package/dist/contract/capabilities.d.ts +49 -0
  3. package/dist/contract/capabilities.d.ts.map +1 -0
  4. package/dist/contract/capabilities.js +52 -0
  5. package/dist/contract/capabilities.js.map +1 -0
  6. package/dist/contract/events.d.ts +33 -0
  7. package/dist/contract/events.d.ts.map +1 -0
  8. package/dist/contract/events.js +22 -0
  9. package/dist/contract/events.js.map +1 -0
  10. package/dist/contract/index.d.ts +15 -0
  11. package/dist/contract/index.d.ts.map +1 -0
  12. package/dist/contract/index.js +15 -0
  13. package/dist/contract/index.js.map +1 -0
  14. package/dist/contract/models.d.ts +146 -0
  15. package/dist/contract/models.d.ts.map +1 -0
  16. package/dist/contract/models.js +107 -0
  17. package/dist/contract/models.js.map +1 -0
  18. package/dist/contract/permissions.d.ts +22 -0
  19. package/dist/contract/permissions.d.ts.map +1 -0
  20. package/dist/contract/permissions.js +26 -0
  21. package/dist/contract/permissions.js.map +1 -0
  22. package/dist/{contract.d.ts → contract/router.d.ts} +24 -85
  23. package/dist/contract/router.d.ts.map +1 -0
  24. package/dist/contract/router.js +42 -0
  25. package/dist/contract/router.js.map +1 -0
  26. package/dist/contract/settings.d.ts +18 -0
  27. package/dist/contract/settings.d.ts.map +1 -0
  28. package/dist/contract/settings.js +29 -0
  29. package/dist/contract/settings.js.map +1 -0
  30. package/dist/server/index.d.ts +4 -1
  31. package/dist/server/index.d.ts.map +1 -1
  32. package/dist/server/index.js +9 -13
  33. package/dist/server/index.js.map +1 -1
  34. package/dist/server/{_impl.d.ts → router.d.ts} +33 -8
  35. package/dist/server/{_impl.d.ts.map → router.d.ts.map} +1 -1
  36. package/dist/server/router.js +83 -0
  37. package/dist/server/router.js.map +1 -0
  38. package/dist/server/schema.d.ts +25 -11
  39. package/dist/server/schema.d.ts.map +1 -1
  40. package/dist/server/schema.js +28 -10
  41. package/dist/server/schema.js.map +1 -1
  42. package/dist/server/services/assets.d.ts +73 -0
  43. package/dist/server/services/assets.d.ts.map +1 -0
  44. package/dist/server/services/assets.js +261 -0
  45. package/dist/server/services/assets.js.map +1 -0
  46. package/dist/server/services/index.d.ts +10 -0
  47. package/dist/server/services/index.d.ts.map +1 -0
  48. package/dist/server/services/index.js +15 -0
  49. package/dist/server/services/index.js.map +1 -0
  50. package/dist/server/services/notify.d.ts +63 -0
  51. package/dist/server/services/notify.d.ts.map +1 -0
  52. package/dist/server/services/notify.js +105 -0
  53. package/dist/server/services/notify.js.map +1 -0
  54. package/migrations/0000_init.sql +12 -3
  55. package/migrations/0001_rls.sql +24 -0
  56. package/migrations/meta/0000_snapshot.json +40 -13
  57. package/migrations/meta/_journal.json +2 -2
  58. package/package.json +5 -4
  59. package/src/client/api.ts +1 -1
  60. package/src/client/components/AssetFormDialog.svelte +130 -47
  61. package/src/client/i18n.ts +11 -166
  62. package/src/client/index.ts +8 -1
  63. package/src/client/messages.test.ts +169 -0
  64. package/src/client/messages.ts +399 -0
  65. package/src/client/mock.test.ts +161 -0
  66. package/src/client/mock.ts +267 -45
  67. package/src/client/module.ts +22 -2
  68. package/src/client/pages/AssetsPage.svelte +358 -137
  69. package/src/client/permissions.ts +1 -1
  70. package/src/client/price.test.ts +106 -0
  71. package/src/client/price.ts +135 -0
  72. package/src/client/query.test.ts +58 -0
  73. package/src/client/query.ts +15 -2
  74. package/src/client/settings/GeneralSettings.svelte +0 -0
  75. package/src/client/settings/core-api.ts +32 -0
  76. package/src/client/widgets/OverviewWidget.svelte +16 -3
  77. package/src/contract/capabilities.ts +55 -0
  78. package/src/contract/events.ts +34 -0
  79. package/src/contract/index.ts +15 -0
  80. package/src/contract/models.ts +123 -0
  81. package/src/contract/permissions.ts +26 -0
  82. package/src/contract/router.ts +46 -0
  83. package/src/contract/settings.ts +30 -0
  84. package/src/module.test.ts +140 -7
  85. package/src/server/index.ts +16 -13
  86. package/src/server/inventory.int.test.ts +819 -0
  87. package/src/server/migrations.test.ts +138 -0
  88. package/src/server/router.ts +118 -0
  89. package/src/server/schema.ts +27 -10
  90. package/src/server/services/assets.ts +368 -0
  91. package/src/server/services/index.ts +23 -0
  92. package/src/server/services/notify.ts +151 -0
  93. package/tsconfig.base.json +22 -0
  94. package/tsconfig.client.json +1 -1
  95. package/tsconfig.json +1 -1
  96. package/vitest.config.ts +18 -3
  97. package/dist/contract.d.ts.map +0 -1
  98. package/dist/contract.js +0 -119
  99. package/dist/contract.js.map +0 -1
  100. package/dist/server/_impl.js +0 -204
  101. package/dist/server/_impl.js.map +0 -1
  102. package/src/contract.ts +0 -143
  103. package/src/server/_impl.ts +0 -275
package/README.md CHANGED
@@ -1,16 +1,62 @@
1
1
  # @kernhq/module-inventory
2
2
 
3
- The asset register for [Kern](https://github.com/KernAIO/app): what the company owns, who holds each
4
- item, and everything that happened to it.
3
+ The asset register for [Kern](https://github.com/KernAIO/app): a list of what the company owns, item
4
+ by item.
5
5
 
6
- - **Assets** with an auto-assigned tag (`INV-0042`), serial number, purchase date and vendor, price,
7
- warranty expiry, location, photo and description.
8
- - **Custody**: hand an item to a member and it stays theirs — with the full history of who held it
9
- before until somebody hands it on or takes it back.
10
- - **History**: every change to an asset is recorded as an append-only timeline entry: what moved,
11
- from what, to what, by whom.
6
+ This is a first-party Kern module. Contract, server, screens, strings and manifest ship in one
7
+ package `core` imports `inventoryModule`, the app shell registers `inventoryClientModule`, and
8
+ deleting the package removes the feature completely. Enable it per workspace in
9
+ **Settings Modules**.
12
10
 
13
- Part of a Kern instance; enable it per workspace in **Settings → Modules**.
11
+ ## What it does today
12
+
13
+ An **asset** carries an asset tag the server assigns (`INV-0042`), a name and description, a serial
14
+ number, a purchase date and vendor, a price, a warranty expiry, a location, a photo and a status.
15
+
16
+ The status column holds one of `in_stock`, `assigned`, `reserved`, `under_repair`, `lost` and
17
+ `retired`, and the screen can filter by any of them — but until custody and repairs ship, nothing
18
+ sets it, so every asset is `in_stock` and the other five filters correctly return nothing. The
19
+ filter is not wrong; there is simply nothing else to find yet.
20
+
21
+ - The **Assets** screen lists them in a table, searches by name, tag or serial number, filters by
22
+ status, hides archived rows until the switch asks for them, and pages a workspace with more
23
+ assets than one screen holds. All four are one server query; nothing is filtered in the browser.
24
+ - Adding an asset opens a dialog, and editing one opens the same dialog seeded from the row. The tag
25
+ is the server's job — people read tags off stickers rather than inventing them.
26
+ - Each row carries a menu: edit, archive, restore. Archiving asks first and names what it is about
27
+ to archive; restoring does not, because it takes nothing away.
28
+ - A **dashboard card** shows the most recently added assets.
29
+ - **Settings → Inventory → General** sets the shape of an asset tag — the prefix and how many digits
30
+ it is padded to, so a workspace that already labels its laptops `LT-` keeps doing that. The number
31
+ itself is a counter in the database rather than a setting: a number an administrator can edit is a
32
+ number that produces a duplicate tag.
33
+ - Two permissions gate it: `inventory.asset.view` and `inventory.asset.manage`.
34
+ - Its strings ship in the five languages the platform speaks — English, Arabic, German, Persian,
35
+ Turkish.
36
+
37
+ The whole API is five procedures under `/api/inventory`: list, get, create, update and archive an
38
+ asset. An asset's photo is settable through `create` and `update` and by nothing on the screen yet;
39
+ its custom values are settable by neither, and wait on custom fields below.
40
+
41
+ ## Not built yet
42
+
43
+ Nothing here has a screen. Most of it is a table in `mod_inventory` and nothing more; each bullet
44
+ says how far it actually got. A register is worth designing whole, and a migration that adds a
45
+ column later is cheaper than one that reshapes a table. A table is not a feature, and this section
46
+ stays until each one ships.
47
+
48
+ - **Custody** — who is holding an item now, who held it before, and handing it on.
49
+ - **History** — an append-only timeline of every change to an asset. The writes exist: create,
50
+ update, archive and restore each append an `asset_history` row, and the integration suite holds
51
+ them to it. Nothing reads them back — there is no procedure and no screen.
52
+ - **Repairs** — what went away for repair, to whom, what it cost and when it came back.
53
+ - **Categories** — the column and the list filter exist; nothing can create a category.
54
+ - **Custom fields** — a workspace's own fields on an asset. `assets.custom` holds the values;
55
+ nothing defines a field, so `create` and `update` refuse to write into it at all.
56
+ - **Attachments** — receipts, warranties and manuals against an asset or a repair.
57
+
58
+ Further off, and with nothing in the schema yet: locations and stock control (bins, counts, reorder
59
+ points), purchasing, depreciation, and reservations.
14
60
 
15
61
  ## Developing
16
62
 
@@ -0,0 +1,49 @@
1
+ /**
2
+ * How much Inventory this workspace has.
3
+ *
4
+ * The module answers two questions that look like one: *what do we own and who has it* (an office
5
+ * with forty laptops) and *how much of this do we have and where* (a warehouse with bins and
6
+ * reorder points). Both are "things the company has", both want the same categories, locations,
7
+ * suppliers, attachments and search — and almost nobody wants both halves at once. Capabilities are
8
+ * what keep the small case small: an office switches on nothing beyond the register and never meets
9
+ * the word "bin".
10
+ *
11
+ * **Declared here only once something is behind it.** A switch that changes nothing teaches an
12
+ * administrator that the switchboard does not mean anything, so this list grows with the module.
13
+ *
14
+ * Two rules decide whether something belongs here at all:
15
+ *
16
+ * - **Not a permission.** "May Ada write off a laptop" is a permission — true for her, false for
17
+ * somebody else, in the same workspace. "Does this company track stock levels" is a capability:
18
+ * one answer for everyone, the owner included, and the answer is 404 rather than 403.
19
+ * - **Reversible without a migration.** Switching one off writes a boolean into module settings and
20
+ * the rows stay exactly where they are. Anything that would need data thrown away to reverse is
21
+ * not a capability, however much it looks like one.
22
+ *
23
+ * The rest arrive with the work that makes them mean something: `repairs`, `attachments` and
24
+ * `labels` with the finished register; `custom_fields` and `locations` beside them; `stock` and
25
+ * `procurement` with stock control; `depreciation` and `reservations` last. Adding one here before
26
+ * its procedures exist is the mistake this comment is here to prevent.
27
+ */
28
+ export declare const inventoryCapabilities: readonly {
29
+ id: string;
30
+ label: string;
31
+ dependsOn: string[];
32
+ defaultEnabled: boolean;
33
+ level: 3 | 1 | 2;
34
+ required: boolean;
35
+ description?: string | undefined;
36
+ }[];
37
+ export type InventoryCapabilityId = (typeof inventoryCapabilities)[number]['id'];
38
+ /**
39
+ * Which procedures sit behind which capability.
40
+ *
41
+ * Declared as data because a missing `requiresCapability` is invisible: the procedure compiles,
42
+ * every other test passes, and the only symptom is a workspace calling a feature it switched off.
43
+ * `module.test.ts` reads this and fails when a procedure named here is not carrying the middleware.
44
+ *
45
+ * A procedure absent from this map belongs to the module as a whole and is reachable whenever
46
+ * Inventory is on — which for `core` is always, because it is `required`.
47
+ */
48
+ export declare const inventoryCapabilityProcedures: Record<string, readonly string[]>;
49
+ //# sourceMappingURL=capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/contract/capabilities.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;GAWhC,CAAA;AAEF,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAA;AAEhF;;;;;;;;;GASG;AACH,eAAO,MAAM,6BAA6B,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAM,CAAA"}
@@ -0,0 +1,52 @@
1
+ import { defineCapabilities } from '@kernhq/contracts';
2
+ /**
3
+ * How much Inventory this workspace has.
4
+ *
5
+ * The module answers two questions that look like one: *what do we own and who has it* (an office
6
+ * with forty laptops) and *how much of this do we have and where* (a warehouse with bins and
7
+ * reorder points). Both are "things the company has", both want the same categories, locations,
8
+ * suppliers, attachments and search — and almost nobody wants both halves at once. Capabilities are
9
+ * what keep the small case small: an office switches on nothing beyond the register and never meets
10
+ * the word "bin".
11
+ *
12
+ * **Declared here only once something is behind it.** A switch that changes nothing teaches an
13
+ * administrator that the switchboard does not mean anything, so this list grows with the module.
14
+ *
15
+ * Two rules decide whether something belongs here at all:
16
+ *
17
+ * - **Not a permission.** "May Ada write off a laptop" is a permission — true for her, false for
18
+ * somebody else, in the same workspace. "Does this company track stock levels" is a capability:
19
+ * one answer for everyone, the owner included, and the answer is 404 rather than 403.
20
+ * - **Reversible without a migration.** Switching one off writes a boolean into module settings and
21
+ * the rows stay exactly where they are. Anything that would need data thrown away to reverse is
22
+ * not a capability, however much it looks like one.
23
+ *
24
+ * The rest arrive with the work that makes them mean something: `repairs`, `attachments` and
25
+ * `labels` with the finished register; `custom_fields` and `locations` beside them; `stock` and
26
+ * `procurement` with stock control; `depreciation` and `reservations` last. Adding one here before
27
+ * its procedures exist is the mistake this comment is here to prevent.
28
+ */
29
+ export const inventoryCapabilities = defineCapabilities([
30
+ {
31
+ id: 'core',
32
+ label: 'Assets',
33
+ // Says only what the module answers today. Custody and the change history are what the schema
34
+ // is shaped for and neither has a procedure yet, so naming them here promised a workspace two
35
+ // features it would then go looking for.
36
+ description: 'The asset register: what the company owns, item by item',
37
+ required: true,
38
+ level: 1,
39
+ },
40
+ ]);
41
+ /**
42
+ * Which procedures sit behind which capability.
43
+ *
44
+ * Declared as data because a missing `requiresCapability` is invisible: the procedure compiles,
45
+ * every other test passes, and the only symptom is a workspace calling a feature it switched off.
46
+ * `module.test.ts` reads this and fails when a procedure named here is not carrying the middleware.
47
+ *
48
+ * A procedure absent from this map belongs to the module as a whole and is reachable whenever
49
+ * Inventory is on — which for `core` is always, because it is `required`.
50
+ */
51
+ export const inventoryCapabilityProcedures = {};
52
+ //# sourceMappingURL=capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/contract/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;IACtD;QACE,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,QAAQ;QACf,8FAA8F;QAC9F,8FAA8F;QAC9F,yCAAyC;QACzC,WAAW,EAAE,yDAAyD;QACtE,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,CAAC;KACT;CACF,CAAC,CAAA;AAIF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAsC,EAAE,CAAA"}
@@ -0,0 +1,33 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * `<module>.<entity>.<action>`. Anything that emits one declares it here.
4
+ *
5
+ * Payloads carry **ids, never rows**. A subscriber that needs the record asks for it with its own
6
+ * principal, so an event cannot become a way to read data past a permission check.
7
+ */
8
+ export declare const inventoryEvents: {
9
+ assetCreated: import("@kernhq/contracts").EventDef<"inventory.asset.created", z.ZodObject<{
10
+ assetId: z.ZodUUID;
11
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
12
+ }, z.core.$strip>>;
13
+ assetUpdated: import("@kernhq/contracts").EventDef<"inventory.asset.updated", z.ZodObject<{
14
+ assetId: z.ZodUUID;
15
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
16
+ }, z.core.$strip>>;
17
+ assetArchived: import("@kernhq/contracts").EventDef<"inventory.asset.archived", z.ZodObject<{
18
+ assetId: z.ZodUUID;
19
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
20
+ }, z.core.$strip>>;
21
+ /**
22
+ * The other half of `archive`, which is also the restore path (`archived: false`).
23
+ *
24
+ * One procedure emitting `archived` whichever way the flag pointed told every subscriber that a
25
+ * restored item had just been retired — and the module's own `asset_history` row already knew
26
+ * better, recording `retired` and `restored` separately.
27
+ */
28
+ assetRestored: import("@kernhq/contracts").EventDef<"inventory.asset.restored", z.ZodObject<{
29
+ assetId: z.ZodUUID;
30
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
31
+ }, z.core.$strip>>;
32
+ };
33
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/contract/events.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;GAKG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;IAa1B;;;;;;OAMG;;;;;CAKJ,CAAA"}
@@ -0,0 +1,22 @@
1
+ import { defineEvent, WorkspaceId } from '@kernhq/contracts';
2
+ import { z } from 'zod';
3
+ /**
4
+ * `<module>.<entity>.<action>`. Anything that emits one declares it here.
5
+ *
6
+ * Payloads carry **ids, never rows**. A subscriber that needs the record asks for it with its own
7
+ * principal, so an event cannot become a way to read data past a permission check.
8
+ */
9
+ export const inventoryEvents = {
10
+ assetCreated: defineEvent('inventory.asset.created', z.object({ assetId: z.uuid(), workspaceId: WorkspaceId })),
11
+ assetUpdated: defineEvent('inventory.asset.updated', z.object({ assetId: z.uuid(), workspaceId: WorkspaceId })),
12
+ assetArchived: defineEvent('inventory.asset.archived', z.object({ assetId: z.uuid(), workspaceId: WorkspaceId })),
13
+ /**
14
+ * The other half of `archive`, which is also the restore path (`archived: false`).
15
+ *
16
+ * One procedure emitting `archived` whichever way the flag pointed told every subscriber that a
17
+ * restored item had just been retired — and the module's own `asset_history` row already knew
18
+ * better, recording `retired` and `restored` separately.
19
+ */
20
+ assetRestored: defineEvent('inventory.asset.restored', z.object({ assetId: z.uuid(), workspaceId: WorkspaceId })),
21
+ };
22
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/contract/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,YAAY,EAAE,WAAW,CACvB,yBAAyB,EACzB,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC1D;IACD,YAAY,EAAE,WAAW,CACvB,yBAAyB,EACzB,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC1D;IACD,aAAa,EAAE,WAAW,CACxB,0BAA0B,EAC1B,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC1D;IACD;;;;;;OAMG;IACH,aAAa,EAAE,WAAW,CACxB,0BAA0B,EAC1B,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAC1D;CACF,CAAA"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * What this module offers, as data.
3
+ *
4
+ * A barrel and nothing else: every symbol is defined in the file beside it. Imported by **both**
5
+ * halves — the server implements the contract, the client calls it — so nothing reachable from here
6
+ * may touch Node. A procedure that exists here and not in the router is a lie that compiles, and
7
+ * `module.test.ts` checks exactly that.
8
+ */
9
+ export * from './capabilities.js';
10
+ export * from './events.js';
11
+ export * from './models.js';
12
+ export * from './permissions.js';
13
+ export * from './router.js';
14
+ export * from './settings.js';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * What this module offers, as data.
3
+ *
4
+ * A barrel and nothing else: every symbol is defined in the file beside it. Imported by **both**
5
+ * halves — the server implements the contract, the client calls it — so nothing reachable from here
6
+ * may touch Node. A procedure that exists here and not in the router is a lie that compiles, and
7
+ * `module.test.ts` checks exactly that.
8
+ */
9
+ export * from './capabilities.js';
10
+ export * from './events.js';
11
+ export * from './models.js';
12
+ export * from './permissions.js';
13
+ export * from './router.js';
14
+ export * from './settings.js';
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,mBAAmB,CAAA;AACjC,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA"}
@@ -0,0 +1,146 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * The nouns this module owns, and the shapes that cross the wire.
4
+ *
5
+ * Imported by **both** halves — the server implements against them, the client calls against them —
6
+ * so nothing here may touch Node. Types only; the procedures live in `router.ts`.
7
+ */
8
+ /** Lowercase, 2-32 characters. Names the API prefix, the Postgres schema `mod_<id>` and every event. */
9
+ export declare const MODULE_ID = "inventory";
10
+ /**
11
+ * An asset's lifecycle. `in_stock` and `assigned` follow custody (an open row in `custody_periods`
12
+ * means assigned); `under_repair` follows an open repair; `reserved` follows a booking that has not
13
+ * been collected; `lost` and `retired` are set by hand when an item stops being usable.
14
+ *
15
+ * Stored rather than derived, because every list filter asks for it — and kept in step inside the
16
+ * same transaction that writes the row it derives from, never by a job afterwards.
17
+ */
18
+ export declare const AssetStatus: z.ZodEnum<{
19
+ in_stock: "in_stock";
20
+ assigned: "assigned";
21
+ reserved: "reserved";
22
+ under_repair: "under_repair";
23
+ lost: "lost";
24
+ retired: "retired";
25
+ }>;
26
+ export type AssetStatus = z.infer<typeof AssetStatus>;
27
+ /**
28
+ * A value a workspace defined for itself, stored under its `key` in `assets.custom`.
29
+ *
30
+ * `unknown` rather than a union: the field definition says what the type is, and validating a value
31
+ * against a definition the client may not have loaded yet would fail honest input. The server checks
32
+ * it against `field_defs` before writing.
33
+ */
34
+ export declare const CustomValues: z.ZodRecord<z.ZodString, z.ZodUnknown>;
35
+ export type CustomValues = z.infer<typeof CustomValues>;
36
+ export declare const Asset: z.ZodObject<{
37
+ id: z.ZodUUID;
38
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
39
+ code: z.ZodString;
40
+ name: z.ZodString;
41
+ description: z.ZodString;
42
+ categoryId: z.ZodNullable<z.ZodUUID>;
43
+ status: z.ZodEnum<{
44
+ in_stock: "in_stock";
45
+ assigned: "assigned";
46
+ reserved: "reserved";
47
+ under_repair: "under_repair";
48
+ lost: "lost";
49
+ retired: "retired";
50
+ }>;
51
+ custodianUserId: z.ZodNullable<z.ZodUUID>;
52
+ custodySince: z.ZodNullable<z.ZodString>;
53
+ serialNumber: z.ZodNullable<z.ZodString>;
54
+ location: z.ZodNullable<z.ZodString>;
55
+ purchasedOn: z.ZodNullable<z.ZodString>;
56
+ purchasedFrom: z.ZodNullable<z.ZodString>;
57
+ priceMinor: z.ZodNullable<z.ZodNumber>;
58
+ currency: z.ZodNullable<z.ZodString>;
59
+ warrantyUntil: z.ZodNullable<z.ZodString>;
60
+ photoFileId: z.ZodNullable<z.ZodUUID>;
61
+ custom: z.ZodRecord<z.ZodString, z.ZodUnknown>;
62
+ createdAt: z.ZodString;
63
+ updatedAt: z.ZodString;
64
+ archivedAt: z.ZodNullable<z.ZodString>;
65
+ }, z.core.$strip>;
66
+ export type Asset = z.infer<typeof Asset>;
67
+ /**
68
+ * How a list is ordered, and therefore what a page cursor is a bookmark *into*.
69
+ *
70
+ * Exported rather than written inline in `router.ts` because the server validates a cursor against
71
+ * this same list: a bookmark issued under one sort is meaningless under another, and the only way
72
+ * to say so is for both halves to read one enum.
73
+ */
74
+ export declare const AssetSort: z.ZodEnum<{
75
+ code: "code";
76
+ name: "name";
77
+ recent: "recent";
78
+ }>;
79
+ export type AssetSort = z.infer<typeof AssetSort>;
80
+ /**
81
+ * Everything a person can say about an asset. `create` requires `name`; `update` takes any subset.
82
+ *
83
+ * **No field here carries `.default()`, and that is load-bearing.** `update` is built from
84
+ * `AssetInput.partial()`, and `.partial()` does not strip a default — it only wraps the field in
85
+ * `optional`, so zod still substitutes the default for a key the request never sent. `description`
86
+ * had `.default('')`, so `PATCH {assetId, name}` reached the handler as
87
+ * `{assetId, name, description: ''}`; the service correctly read a present value as "set it" and a
88
+ * rename destroyed the text, writing a bogus `description` diff into `asset_history` as it went.
89
+ * The care the service takes over `undefined` versus `null` is defeated one layer above it, here.
90
+ * A value `create` should fill in belongs on `AssetCreateInput` below, which is never partialled.
91
+ */
92
+ export declare const AssetInput: z.ZodObject<{
93
+ name: z.ZodString;
94
+ description: z.ZodOptional<z.ZodString>;
95
+ categoryId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
96
+ serialNumber: z.ZodOptional<z.ZodNullable<z.ZodString>>;
97
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
98
+ purchasedFrom: z.ZodOptional<z.ZodNullable<z.ZodString>>;
99
+ purchasedOn: z.ZodOptional<z.ZodNullable<z.ZodISODate>>;
100
+ warrantyUntil: z.ZodOptional<z.ZodNullable<z.ZodISODate>>;
101
+ priceMinor: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
102
+ currency: z.ZodOptional<z.ZodNullable<z.ZodString>>;
103
+ photoFileId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
104
+ }, z.core.$strip>;
105
+ export type AssetInput = z.infer<typeof AssetInput>;
106
+ /**
107
+ * What `create` accepts: the same fields, with the one value a new row may not go without.
108
+ *
109
+ * `description` is `not null` in the database, so a create with no description needs *something*.
110
+ * Defaulting it here rather than in `AssetInput` is what keeps `update` able to tell "leave it
111
+ * alone" from "clear it" — see the note above.
112
+ */
113
+ export declare const AssetCreateInput: z.ZodObject<{
114
+ name: z.ZodString;
115
+ categoryId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
116
+ serialNumber: z.ZodOptional<z.ZodNullable<z.ZodString>>;
117
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
118
+ purchasedFrom: z.ZodOptional<z.ZodNullable<z.ZodString>>;
119
+ purchasedOn: z.ZodOptional<z.ZodNullable<z.ZodISODate>>;
120
+ warrantyUntil: z.ZodOptional<z.ZodNullable<z.ZodISODate>>;
121
+ priceMinor: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
122
+ currency: z.ZodOptional<z.ZodNullable<z.ZodString>>;
123
+ photoFileId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
124
+ description: z.ZodDefault<z.ZodString>;
125
+ }, z.core.$strip>;
126
+ export type AssetCreateInput = z.infer<typeof AssetCreateInput>;
127
+ /** What `update` accepts: any subset, and nothing filled in for a key that never arrived. */
128
+ export declare const AssetPatchInput: z.ZodObject<{
129
+ name: z.ZodOptional<z.ZodString>;
130
+ description: z.ZodOptional<z.ZodOptional<z.ZodString>>;
131
+ categoryId: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodUUID>>>;
132
+ serialNumber: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
133
+ location: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
134
+ purchasedFrom: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
135
+ purchasedOn: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodISODate>>>;
136
+ warrantyUntil: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodISODate>>>;
137
+ priceMinor: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodNumber>>>;
138
+ currency: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
139
+ photoFileId: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodUUID>>>;
140
+ }, z.core.$strip>;
141
+ export type AssetPatchInput = z.infer<typeof AssetPatchInput>;
142
+ /** Shared by every workspace-scoped procedure, which is all of them. */
143
+ export declare const ws: z.ZodObject<{
144
+ workspaceId: z.core.$ZodBranded<z.ZodUUID, "WorkspaceId", "out">;
145
+ }, z.core.$strip>;
146
+ //# sourceMappingURL=models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/contract/models.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;GAKG;AAEH,wGAAwG;AACxG,eAAO,MAAM,SAAS,cAAc,CAAA;AAEpC;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW;;;;;;;EAAkF,CAAA;AAC1G,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAA;AAErD;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,wCAAoC,CAAA;AAC7D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AAEvD,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4BhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,CAAA;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,SAAS;;;;EAAqC,CAAA;AAC3D,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA;AAEjD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;iBAerB,CAAA;AACF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAA;AAEnD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;iBAE3B,CAAA;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAE/D,6FAA6F;AAC7F,eAAO,MAAM,eAAe;;;;;;;;;;;;iBAAuB,CAAA;AACnD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAE7D,wEAAwE;AACxE,eAAO,MAAM,EAAE;;iBAAyC,CAAA"}
@@ -0,0 +1,107 @@
1
+ import { WorkspaceId } from '@kernhq/contracts';
2
+ import { z } from 'zod';
3
+ /**
4
+ * The nouns this module owns, and the shapes that cross the wire.
5
+ *
6
+ * Imported by **both** halves — the server implements against them, the client calls against them —
7
+ * so nothing here may touch Node. Types only; the procedures live in `router.ts`.
8
+ */
9
+ /** Lowercase, 2-32 characters. Names the API prefix, the Postgres schema `mod_<id>` and every event. */
10
+ export const MODULE_ID = 'inventory';
11
+ /**
12
+ * An asset's lifecycle. `in_stock` and `assigned` follow custody (an open row in `custody_periods`
13
+ * means assigned); `under_repair` follows an open repair; `reserved` follows a booking that has not
14
+ * been collected; `lost` and `retired` are set by hand when an item stops being usable.
15
+ *
16
+ * Stored rather than derived, because every list filter asks for it — and kept in step inside the
17
+ * same transaction that writes the row it derives from, never by a job afterwards.
18
+ */
19
+ export const AssetStatus = z.enum(['in_stock', 'assigned', 'reserved', 'under_repair', 'lost', 'retired']);
20
+ /**
21
+ * A value a workspace defined for itself, stored under its `key` in `assets.custom`.
22
+ *
23
+ * `unknown` rather than a union: the field definition says what the type is, and validating a value
24
+ * against a definition the client may not have loaded yet would fail honest input. The server checks
25
+ * it against `field_defs` before writing.
26
+ */
27
+ export const CustomValues = z.record(z.string(), z.unknown());
28
+ export const Asset = z.object({
29
+ id: z.uuid(),
30
+ workspaceId: WorkspaceId,
31
+ /** Human-facing asset tag (`INV-0001`), assigned by the server, unique per workspace. */
32
+ code: z.string().min(1).max(40),
33
+ name: z.string().min(1).max(200),
34
+ description: z.string().max(4000),
35
+ categoryId: z.uuid().nullable(),
36
+ status: AssetStatus,
37
+ /**
38
+ * The member currently holding the item. A plain uuid — cross-schema foreign keys are what the
39
+ * module boundary exists to prevent — resolved against core membership at read time.
40
+ */
41
+ custodianUserId: z.uuid().nullable(),
42
+ custodySince: z.string().nullable(),
43
+ serialNumber: z.string().max(200).nullable(),
44
+ location: z.string().max(200).nullable(),
45
+ purchasedOn: z.string().nullable(),
46
+ purchasedFrom: z.string().max(200).nullable(),
47
+ /** Minor units (cents), the convention billing established; formatted on the client. */
48
+ priceMinor: z.number().int().nullable(),
49
+ currency: z.string().length(3).nullable(),
50
+ warrantyUntil: z.string().nullable(),
51
+ photoFileId: z.uuid().nullable(),
52
+ custom: CustomValues,
53
+ createdAt: z.string(),
54
+ updatedAt: z.string(),
55
+ archivedAt: z.string().nullable(),
56
+ });
57
+ /**
58
+ * How a list is ordered, and therefore what a page cursor is a bookmark *into*.
59
+ *
60
+ * Exported rather than written inline in `router.ts` because the server validates a cursor against
61
+ * this same list: a bookmark issued under one sort is meaningless under another, and the only way
62
+ * to say so is for both halves to read one enum.
63
+ */
64
+ export const AssetSort = z.enum(['recent', 'name', 'code']);
65
+ /**
66
+ * Everything a person can say about an asset. `create` requires `name`; `update` takes any subset.
67
+ *
68
+ * **No field here carries `.default()`, and that is load-bearing.** `update` is built from
69
+ * `AssetInput.partial()`, and `.partial()` does not strip a default — it only wraps the field in
70
+ * `optional`, so zod still substitutes the default for a key the request never sent. `description`
71
+ * had `.default('')`, so `PATCH {assetId, name}` reached the handler as
72
+ * `{assetId, name, description: ''}`; the service correctly read a present value as "set it" and a
73
+ * rename destroyed the text, writing a bogus `description` diff into `asset_history` as it went.
74
+ * The care the service takes over `undefined` versus `null` is defeated one layer above it, here.
75
+ * A value `create` should fill in belongs on `AssetCreateInput` below, which is never partialled.
76
+ */
77
+ export const AssetInput = z.object({
78
+ name: z.string().min(1).max(200),
79
+ description: z.string().max(4000).optional(),
80
+ categoryId: z.uuid().nullish(),
81
+ serialNumber: z.string().max(200).nullish(),
82
+ location: z.string().max(200).nullish(),
83
+ purchasedFrom: z.string().max(200).nullish(),
84
+ purchasedOn: z.iso.date().nullish(),
85
+ warrantyUntil: z.iso.date().nullish(),
86
+ priceMinor: z.number().int().min(0).nullish(),
87
+ currency: z.string().length(3).nullish(),
88
+ photoFileId: z.uuid().nullish(),
89
+ // `custom` is deliberately absent: there is nothing to validate a value against until
90
+ // `fields.*` exists, and accepting arbitrary JSON into a column a workspace has not defined
91
+ // is how a schemaless field bag becomes permanent. It arrives with the field definitions.
92
+ });
93
+ /**
94
+ * What `create` accepts: the same fields, with the one value a new row may not go without.
95
+ *
96
+ * `description` is `not null` in the database, so a create with no description needs *something*.
97
+ * Defaulting it here rather than in `AssetInput` is what keeps `update` able to tell "leave it
98
+ * alone" from "clear it" — see the note above.
99
+ */
100
+ export const AssetCreateInput = AssetInput.extend({
101
+ description: z.string().max(4000).default(''),
102
+ });
103
+ /** What `update` accepts: any subset, and nothing filled in for a key that never arrived. */
104
+ export const AssetPatchInput = AssetInput.partial();
105
+ /** Shared by every workspace-scoped procedure, which is all of them. */
106
+ export const ws = z.object({ workspaceId: WorkspaceId });
107
+ //# sourceMappingURL=models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/contract/models.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `<module>.<resource>.<action>`, each with the narrowest scope that works and the roles that hold
3
+ * it by default. A workspace can add or remove any of them afterwards with a custom role.
4
+ *
5
+ * A key with nothing checking it is a role editor full of switches that do nothing, so these arrive
6
+ * with the procedures that enforce them — `custody.manage`, `repair.manage`, `category.manage`,
7
+ * `field.manage`, `value.view` and the stock and purchasing keys with their own phases.
8
+ */
9
+ export declare const inventoryPermissions: readonly [{
10
+ readonly key: "inventory.asset.view";
11
+ readonly label: "View assets";
12
+ readonly scope: "workspace";
13
+ readonly defaultRoles: ["owner", "admin", "member", "guest"];
14
+ readonly dangerous: false;
15
+ }, {
16
+ readonly key: "inventory.asset.manage";
17
+ readonly label: "Create and edit assets";
18
+ readonly scope: "workspace";
19
+ readonly defaultRoles: ["owner", "admin", "member"];
20
+ readonly dangerous: false;
21
+ }];
22
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/contract/permissions.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;EAe/B,CAAA"}
@@ -0,0 +1,26 @@
1
+ import { definePermissions } from '@kernhq/contracts';
2
+ /**
3
+ * `<module>.<resource>.<action>`, each with the narrowest scope that works and the roles that hold
4
+ * it by default. A workspace can add or remove any of them afterwards with a custom role.
5
+ *
6
+ * A key with nothing checking it is a role editor full of switches that do nothing, so these arrive
7
+ * with the procedures that enforce them — `custody.manage`, `repair.manage`, `category.manage`,
8
+ * `field.manage`, `value.view` and the stock and purchasing keys with their own phases.
9
+ */
10
+ export const inventoryPermissions = definePermissions([
11
+ {
12
+ key: 'inventory.asset.view',
13
+ label: 'View assets',
14
+ scope: 'workspace',
15
+ defaultRoles: ['owner', 'admin', 'member', 'guest'],
16
+ dangerous: false,
17
+ },
18
+ {
19
+ key: 'inventory.asset.manage',
20
+ label: 'Create and edit assets',
21
+ scope: 'workspace',
22
+ defaultRoles: ['owner', 'admin', 'member'],
23
+ dangerous: false,
24
+ },
25
+ ]);
26
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/contract/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAErD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;IACpD;QACE,GAAG,EAAE,sBAAsB;QAC3B,KAAK,EAAE,aAAa;QACpB,KAAK,EAAE,WAAW;QAClB,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC;QACnD,SAAS,EAAE,KAAK;KACjB;IACD;QACE,GAAG,EAAE,wBAAwB;QAC7B,KAAK,EAAE,wBAAwB;QAC/B,KAAK,EAAE,WAAW;QAClB,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC;QAC1C,SAAS,EAAE,KAAK;KACjB;CACF,CAAC,CAAA"}