@kernhq/module-inventory 0.2.0 → 0.3.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 (193) hide show
  1. package/README.md +104 -25
  2. package/dist/contract/capabilities.d.ts +4 -4
  3. package/dist/contract/capabilities.d.ts.map +1 -1
  4. package/dist/contract/capabilities.js +51 -9
  5. package/dist/contract/capabilities.js.map +1 -1
  6. package/dist/contract/events.d.ts +43 -0
  7. package/dist/contract/events.d.ts.map +1 -1
  8. package/dist/contract/events.js +40 -0
  9. package/dist/contract/events.js.map +1 -1
  10. package/dist/contract/index.d.ts +1 -0
  11. package/dist/contract/index.d.ts.map +1 -1
  12. package/dist/contract/index.js +1 -0
  13. package/dist/contract/index.js.map +1 -1
  14. package/dist/contract/models.d.ts +322 -0
  15. package/dist/contract/models.d.ts.map +1 -1
  16. package/dist/contract/models.js +213 -0
  17. package/dist/contract/models.js.map +1 -1
  18. package/dist/contract/notifications.d.ts +24 -0
  19. package/dist/contract/notifications.d.ts.map +1 -0
  20. package/dist/contract/notifications.js +72 -0
  21. package/dist/contract/notifications.js.map +1 -0
  22. package/dist/contract/permissions.d.ts +52 -2
  23. package/dist/contract/permissions.d.ts.map +1 -1
  24. package/dist/contract/permissions.js +55 -2
  25. package/dist/contract/permissions.js.map +1 -1
  26. package/dist/contract/router.d.ts +1052 -0
  27. package/dist/contract/router.d.ts.map +1 -1
  28. package/dist/contract/router.js +225 -1
  29. package/dist/contract/router.js.map +1 -1
  30. package/dist/contract/settings.d.ts +2 -0
  31. package/dist/contract/settings.d.ts.map +1 -1
  32. package/dist/contract/settings.js +24 -4
  33. package/dist/contract/settings.js.map +1 -1
  34. package/dist/server/index.d.ts +8 -4
  35. package/dist/server/index.d.ts.map +1 -1
  36. package/dist/server/index.js +289 -5
  37. package/dist/server/index.js.map +1 -1
  38. package/dist/server/jobs.d.ts +48 -0
  39. package/dist/server/jobs.d.ts.map +1 -0
  40. package/dist/server/jobs.js +358 -0
  41. package/dist/server/jobs.js.map +1 -0
  42. package/dist/server/router.d.ts +1337 -0
  43. package/dist/server/router.d.ts.map +1 -1
  44. package/dist/server/router.js +363 -7
  45. package/dist/server/router.js.map +1 -1
  46. package/dist/server/schema.d.ts +134 -1
  47. package/dist/server/schema.d.ts.map +1 -1
  48. package/dist/server/schema.js +158 -4
  49. package/dist/server/schema.js.map +1 -1
  50. package/dist/server/services/assets.d.ts +69 -3
  51. package/dist/server/services/assets.d.ts.map +1 -1
  52. package/dist/server/services/assets.js +199 -31
  53. package/dist/server/services/assets.js.map +1 -1
  54. package/dist/server/services/attachments.d.ts +80 -0
  55. package/dist/server/services/attachments.d.ts.map +1 -0
  56. package/dist/server/services/attachments.js +182 -0
  57. package/dist/server/services/attachments.js.map +1 -0
  58. package/dist/server/services/audience.d.ts +15 -0
  59. package/dist/server/services/audience.d.ts.map +1 -0
  60. package/dist/server/services/audience.js +64 -0
  61. package/dist/server/services/audience.js.map +1 -0
  62. package/dist/server/services/categories.d.ts +57 -0
  63. package/dist/server/services/categories.d.ts.map +1 -0
  64. package/dist/server/services/categories.js +124 -0
  65. package/dist/server/services/categories.js.map +1 -0
  66. package/dist/server/services/cursor.d.ts +68 -0
  67. package/dist/server/services/cursor.d.ts.map +1 -0
  68. package/dist/server/services/cursor.js +39 -0
  69. package/dist/server/services/cursor.js.map +1 -0
  70. package/dist/server/services/custody.d.ts +175 -0
  71. package/dist/server/services/custody.d.ts.map +1 -0
  72. package/dist/server/services/custody.js +367 -0
  73. package/dist/server/services/custody.js.map +1 -0
  74. package/dist/server/services/db-errors.d.ts +7 -0
  75. package/dist/server/services/db-errors.d.ts.map +1 -0
  76. package/dist/server/services/db-errors.js +32 -0
  77. package/dist/server/services/db-errors.js.map +1 -0
  78. package/dist/server/services/index.d.ts +16 -0
  79. package/dist/server/services/index.d.ts.map +1 -1
  80. package/dist/server/services/index.js +25 -1
  81. package/dist/server/services/index.js.map +1 -1
  82. package/dist/server/services/members.d.ts +27 -0
  83. package/dist/server/services/members.d.ts.map +1 -0
  84. package/dist/server/services/members.js +39 -0
  85. package/dist/server/services/members.js.map +1 -0
  86. package/dist/server/services/notify.d.ts +43 -1
  87. package/dist/server/services/notify.d.ts.map +1 -1
  88. package/dist/server/services/notify.js +47 -5
  89. package/dist/server/services/notify.js.map +1 -1
  90. package/dist/server/services/offboarding.d.ts +70 -0
  91. package/dist/server/services/offboarding.d.ts.map +1 -0
  92. package/dist/server/services/offboarding.js +116 -0
  93. package/dist/server/services/offboarding.js.map +1 -0
  94. package/dist/server/services/repairs.d.ts +204 -0
  95. package/dist/server/services/repairs.d.ts.map +1 -0
  96. package/dist/server/services/repairs.js +476 -0
  97. package/dist/server/services/repairs.js.map +1 -0
  98. package/dist/server/services/search.d.ts +85 -0
  99. package/dist/server/services/search.d.ts.map +1 -0
  100. package/dist/server/services/search.js +142 -0
  101. package/dist/server/services/search.js.map +1 -0
  102. package/dist/server/services/stats.d.ts +42 -0
  103. package/dist/server/services/stats.d.ts.map +1 -0
  104. package/dist/server/services/stats.js +80 -0
  105. package/dist/server/services/stats.js.map +1 -0
  106. package/dist/server/services/status.d.ts +102 -0
  107. package/dist/server/services/status.d.ts.map +1 -0
  108. package/dist/server/services/status.js +71 -0
  109. package/dist/server/services/status.js.map +1 -0
  110. package/migrations/0002_custody_and_categories.sql +23 -0
  111. package/migrations/0003_repairs.sql +23 -0
  112. package/migrations/0004_platform_surfaces.sql +51 -0
  113. package/migrations/0005_repair_dates.sql +35 -0
  114. package/migrations/0006_workspace_registry_read.sql +50 -0
  115. package/migrations/0007_history_sequence.sql +83 -0
  116. package/migrations/meta/0002_snapshot.json +1054 -0
  117. package/migrations/meta/0003_snapshot.json +1070 -0
  118. package/migrations/meta/0004_snapshot.json +1130 -0
  119. package/migrations/meta/0005_snapshot.json +1135 -0
  120. package/migrations/meta/_journal.json +42 -0
  121. package/package.json +1 -1
  122. package/src/client/api-instance.ts +27 -2
  123. package/src/client/bidi.test.ts +148 -0
  124. package/src/client/bidi.ts +85 -0
  125. package/src/client/components/AssetDetailPanel.svelte +614 -0
  126. package/src/client/components/AssetFormDialog.svelte +71 -22
  127. package/src/client/components/AssetPhoto.svelte +178 -0
  128. package/src/client/components/AttachmentsSection.svelte +327 -0
  129. package/src/client/components/CustodyDialog.svelte +201 -0
  130. package/src/client/components/RepairDialog.svelte +271 -0
  131. package/src/client/components/RepairsSection.svelte +318 -0
  132. package/src/client/components/Timeline.svelte +347 -0
  133. package/src/client/components/TimelineText.svelte +124 -0
  134. package/src/client/core-api.ts +71 -0
  135. package/src/client/custody.test.ts +31 -0
  136. package/src/client/custody.ts +34 -0
  137. package/src/client/errors.test.ts +365 -0
  138. package/src/client/errors.ts +201 -0
  139. package/src/client/links.test.ts +74 -0
  140. package/src/client/links.ts +44 -0
  141. package/src/client/members.test.ts +132 -0
  142. package/src/client/members.ts +116 -0
  143. package/src/client/messages.test.ts +127 -0
  144. package/src/client/messages.ts +1043 -18
  145. package/src/client/mock.test.ts +395 -1
  146. package/src/client/mock.ts +1066 -79
  147. package/src/client/module.ts +54 -0
  148. package/src/client/pages/AssetsPage.svelte +317 -45
  149. package/src/client/permissions.ts +7 -0
  150. package/src/client/price.test.ts +151 -3
  151. package/src/client/price.ts +155 -11
  152. package/src/client/query.ts +36 -0
  153. package/src/client/repairs.test.ts +38 -0
  154. package/src/client/repairs.ts +38 -0
  155. package/src/client/settings/CategoriesSettings.svelte +421 -0
  156. package/src/client/settings/GeneralSettings.svelte +0 -0
  157. package/src/client/status.ts +29 -0
  158. package/src/client/timeline.test.ts +175 -0
  159. package/src/client/timeline.ts +206 -0
  160. package/src/client/widgets/OverviewWidget.svelte +127 -26
  161. package/src/client/widgets/RepairsWidget.svelte +124 -0
  162. package/src/contract/capabilities.ts +53 -9
  163. package/src/contract/events.ts +49 -0
  164. package/src/contract/index.ts +1 -0
  165. package/src/contract/models.ts +237 -0
  166. package/src/contract/notifications.ts +73 -0
  167. package/src/contract/permissions.ts +55 -2
  168. package/src/contract/router.ts +255 -1
  169. package/src/contract/settings.ts +24 -4
  170. package/src/module.test.ts +194 -4
  171. package/src/server/index.ts +311 -4
  172. package/src/server/inventory.int.test.ts +3573 -18
  173. package/src/server/jobs.ts +444 -0
  174. package/src/server/migrations.test.ts +113 -0
  175. package/src/server/router.ts +462 -6
  176. package/src/server/schema.ts +160 -3
  177. package/src/server/services/assets.ts +214 -54
  178. package/src/server/services/attachments.ts +215 -0
  179. package/src/server/services/audience.ts +77 -0
  180. package/src/server/services/categories.ts +136 -0
  181. package/src/server/services/cursor.ts +104 -0
  182. package/src/server/services/custody.ts +471 -0
  183. package/src/server/services/db-errors.ts +42 -0
  184. package/src/server/services/index.ts +34 -1
  185. package/src/server/services/members.ts +54 -0
  186. package/src/server/services/notify.ts +52 -7
  187. package/src/server/services/offboarding.ts +150 -0
  188. package/src/server/services/repairs.ts +567 -0
  189. package/src/server/services/search.ts +166 -0
  190. package/src/server/services/stats.ts +88 -0
  191. package/src/server/services/status.test.ts +34 -0
  192. package/src/server/services/status.ts +143 -0
  193. package/src/client/settings/core-api.ts +0 -32
@@ -1,3 +1,6 @@
1
+ import { readdirSync, readFileSync } from 'node:fs'
2
+ import { dirname, join } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
1
4
  import { describe, expect, it } from 'vitest'
2
5
  import { ar, de, en, fa, inventoryMessageBundles, tr } from './messages.js'
3
6
 
@@ -80,6 +83,38 @@ describe('bundles', () => {
80
83
  }
81
84
  })
82
85
 
86
+ /**
87
+ * The slip that six forms invite: writing five of them and abbreviating the sixth.
88
+ *
89
+ * `many` is the form for 11–99, where Arabic puts the counted noun in the singular accusative —
90
+ * "{n} أصلاً". Because that form is written *differently* from `other`, it gets written from
91
+ * scratch rather than copied, and a compound noun loses half of itself on the way: the shell's
92
+ * `modules_meta_objects` said "{n} نوعاً" — "{n} type" — where every other form said "نوع كائن",
93
+ * "object type". Nothing catches that. It compiles, it has all six categories, it has the right
94
+ * placeholder, and it is missing the word the sentence is about.
95
+ *
96
+ * Word count against `other` is a heuristic and is named as one: a form with *fewer* words than
97
+ * `other` has dropped one, and in a counted phrase the word it drops is the noun. Inflection
98
+ * changes a word's letters, not how many words there are, so this stays true across all six.
99
+ */
100
+ it('never drops a word from an Arabic plural form that the `other` form has', () => {
101
+ const words = (form: string) => form.split(/\s+/).filter((word) => word && !word.includes('{')).length
102
+ for (const [key, value] of Object.entries(ar)) {
103
+ if (typeof value === 'string') continue
104
+ const other = (value as Record<string, string>).other ?? ''
105
+ for (const [category, form] of Object.entries(value as Record<string, string>)) {
106
+ // `zero` is a negation — "لا أصول" — and is the one form entitled to a different shape.
107
+ if (category === 'zero') continue
108
+ expect({ key, category, form, atLeast: words(other) }).toEqual({
109
+ key,
110
+ category,
111
+ form,
112
+ atLeast: Math.min(words(form), words(other)),
113
+ })
114
+ }
115
+ }
116
+ })
117
+
83
118
  it('makes a plural of a key wherever English has one', () => {
84
119
  for (const [key, value] of Object.entries(en)) {
85
120
  if (typeof value === 'string') continue
@@ -93,6 +128,71 @@ describe('bundles', () => {
93
128
  })
94
129
  })
95
130
 
131
+ // ------------------------------------------------------ keys nothing asks for any more
132
+
133
+ const HERE = dirname(fileURLToPath(import.meta.url))
134
+
135
+ /** Everything in this client that could name a key — components included, catalogue excluded. */
136
+ function clientSource(): string {
137
+ const files = readdirSync(HERE, { recursive: true, encoding: 'utf8' }).filter(
138
+ (file) =>
139
+ (file.endsWith('.ts') || file.endsWith('.svelte')) &&
140
+ !file.endsWith('.test.ts') &&
141
+ !file.endsWith('messages.ts'),
142
+ )
143
+ return files.map((file) => readFileSync(join(HERE, file), 'utf8')).join('\n')
144
+ }
145
+
146
+ /**
147
+ * The two families of key that are *built* rather than written, and so are never literals.
148
+ *
149
+ * `t(`status_${status}`)` and `actionKey()`'s `history_${action}`. Both lists are duplicated here
150
+ * on purpose — the file is data-only and importing the contract to check a catalogue would be a
151
+ * circular sort of proof — and `status labels` below already holds the first one to the contract.
152
+ */
153
+ const BUILT = new Set([
154
+ ...['in_stock', 'assigned', 'reserved', 'under_repair', 'lost', 'retired'].map((s) => `status_${s}`),
155
+ ...[
156
+ 'created',
157
+ 'updated',
158
+ 'assigned',
159
+ 'transferred',
160
+ 'returned',
161
+ 'retired',
162
+ 'restored',
163
+ 'repair_logged',
164
+ 'repair_completed',
165
+ 'attachment_added',
166
+ 'attachment_removed',
167
+ ].map((a) => `history_${a}`),
168
+ ])
169
+
170
+ describe('every key is one something asks for', () => {
171
+ /**
172
+ * `inventory.settings_save_error` — "The settings could not be saved" — sat in all five bundles
173
+ * with nothing reading it: the save bar renders `errorMessage(err, t)`, which names the actual
174
+ * refusal instead. A dead key is five translations somebody maintains, reviews and re-reads for
175
+ * a sentence that cannot appear, and nothing about it looks wrong from inside the catalogue.
176
+ *
177
+ * Literals rather than a runtime trace, because a `.svelte` file cannot be unit-tested here —
178
+ * the same reason `errors.test.ts` reads `src/server` off disk. It misses a key that happens to
179
+ * appear as an unrelated string somewhere, which is a false *pass*; it never fails a key that is
180
+ * really used, which is the direction that matters for a check nobody wants to fight.
181
+ */
182
+ it('has nothing in the catalogue that no screen can reach', () => {
183
+ const source = clientSource()
184
+ const dead = Object.keys(en)
185
+ .map((key) => key.slice('inventory.'.length))
186
+ .filter((name) => !BUILT.has(name))
187
+ .filter((name) => !(source.includes(`'${name}'`) || source.includes(`"${name}"`)))
188
+ expect(dead).toEqual([])
189
+ })
190
+
191
+ it('is reading the client rather than passing on an empty sweep', () => {
192
+ expect(clientSource().length).toBeGreaterThan(10_000)
193
+ })
194
+ })
195
+
96
196
  /**
97
197
  * Every status the contract can hold has a label, in every language.
98
198
  *
@@ -167,3 +267,30 @@ describe('counted messages', () => {
167
267
  expect(pick('en', 'inventory.count', 5)).toContain('5')
168
268
  })
169
269
  })
270
+
271
+ /**
272
+ * One word per idea, across the whole product and not just across this module.
273
+ *
274
+ * A module is read beside the shell, so a second Turkish word for un-archiving something is a
275
+ * product that says two things for one action. `ws_archive_hint` and `ws_archive_confirm_body` in
276
+ * `shell/messages/tr.json` both say **geri getir** about an archived workspace; this bundle said
277
+ * *geri yükle*, three lines away from "Daha fazla yükle" (load more) and "sayfayı yeniden yükleyin"
278
+ * (reload) — one verb doing three jobs in one panel.
279
+ *
280
+ * Pinned here because it cannot be checked against the shell: a module is built standalone, and
281
+ * `shell/messages/*.json` is not in this package. The word is the decision; the test is the memory.
282
+ */
283
+ describe('the words Kern already uses', () => {
284
+ it('un-archives with the shell’s own Turkish verb, and never overloads “yükle”', () => {
285
+ expect(tr['inventory.restore']).toBe('Geri getir')
286
+ const overloaded = Object.entries(tr)
287
+ .filter(([, value]) => typeof value === 'string' && (value as string).includes('geri yükle'))
288
+ .map(([key]) => key)
289
+ expect(overloaded).toEqual([])
290
+ })
291
+
292
+ /** «إعادة التحميل» is what the shell calls Reload (`pwa_update_reload`), so a reload says that. */
293
+ it('reloads with the shell’s own words in Arabic', () => {
294
+ expect(ar['inventory.error_conflict']).toContain('أعد تحميل الصفحة')
295
+ })
296
+ })