@owlmeans/planning 0.1.18-rc.2 → 0.1.18-rc.21

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 (228) hide show
  1. package/README.md +69 -26
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/planning/SKILL.md +157 -45
  4. package/build/aliases/types.d.ts +49 -0
  5. package/build/aliases/types.d.ts.map +1 -0
  6. package/build/aliases/types.js +2 -0
  7. package/build/aliases/types.js.map +1 -0
  8. package/build/aliases.d.ts +4 -0
  9. package/build/aliases.d.ts.map +1 -0
  10. package/build/aliases.js +27 -0
  11. package/build/aliases.js.map +1 -0
  12. package/build/consts.d.ts +23 -34
  13. package/build/consts.d.ts.map +1 -1
  14. package/build/consts.js +26 -25
  15. package/build/consts.js.map +1 -1
  16. package/build/entrypoints/types.d.ts +96 -0
  17. package/build/entrypoints/types.d.ts.map +1 -0
  18. package/build/entrypoints/types.js +2 -0
  19. package/build/entrypoints/types.js.map +1 -0
  20. package/build/entrypoints.d.ts +5 -1
  21. package/build/entrypoints.d.ts.map +1 -1
  22. package/build/entrypoints.js +17 -6
  23. package/build/entrypoints.js.map +1 -1
  24. package/build/errors.d.ts +41 -0
  25. package/build/errors.d.ts.map +1 -1
  26. package/build/errors.js +61 -0
  27. package/build/errors.js.map +1 -1
  28. package/build/helpers/apply/consts.local.d.ts +7 -0
  29. package/build/helpers/apply/consts.local.d.ts.map +1 -0
  30. package/build/helpers/apply/consts.local.js +9 -0
  31. package/build/helpers/apply/consts.local.js.map +1 -0
  32. package/build/helpers/apply/types.d.ts +32 -0
  33. package/build/helpers/apply/types.d.ts.map +1 -0
  34. package/build/helpers/apply/types.js +2 -0
  35. package/build/helpers/apply/types.js.map +1 -0
  36. package/build/helpers/apply.d.ts +5 -23
  37. package/build/helpers/apply.d.ts.map +1 -1
  38. package/build/helpers/apply.js +121 -143
  39. package/build/helpers/apply.js.map +1 -1
  40. package/build/helpers/card/types.d.ts +20 -0
  41. package/build/helpers/card/types.d.ts.map +1 -0
  42. package/build/helpers/card/types.js +2 -0
  43. package/build/helpers/card/types.js.map +1 -0
  44. package/build/helpers/card.d.ts +11 -7
  45. package/build/helpers/card.d.ts.map +1 -1
  46. package/build/helpers/card.js +41 -28
  47. package/build/helpers/card.js.map +1 -1
  48. package/build/helpers/changes/consts.local.d.ts +13 -0
  49. package/build/helpers/changes/consts.local.d.ts.map +1 -0
  50. package/build/helpers/changes/consts.local.js +13 -0
  51. package/build/helpers/changes/consts.local.js.map +1 -0
  52. package/build/helpers/changes/types.d.ts +58 -0
  53. package/build/helpers/changes/types.d.ts.map +1 -0
  54. package/build/helpers/changes/types.js +2 -0
  55. package/build/helpers/changes/types.js.map +1 -0
  56. package/build/helpers/changes.d.ts +11 -42
  57. package/build/helpers/changes.d.ts.map +1 -1
  58. package/build/helpers/changes.js +234 -251
  59. package/build/helpers/changes.js.map +1 -1
  60. package/build/helpers/code/types.d.ts +27 -0
  61. package/build/helpers/code/types.d.ts.map +1 -0
  62. package/build/helpers/code/types.js +2 -0
  63. package/build/helpers/code/types.js.map +1 -0
  64. package/build/helpers/code.d.ts +6 -17
  65. package/build/helpers/code.d.ts.map +1 -1
  66. package/build/helpers/code.js +39 -40
  67. package/build/helpers/code.js.map +1 -1
  68. package/build/helpers/index.d.ts +12 -0
  69. package/build/helpers/index.d.ts.map +1 -1
  70. package/build/helpers/index.js +2 -0
  71. package/build/helpers/index.js.map +1 -1
  72. package/build/helpers/query/types.d.ts +23 -0
  73. package/build/helpers/query/types.d.ts.map +1 -0
  74. package/build/helpers/query/types.js +2 -0
  75. package/build/helpers/query/types.js.map +1 -0
  76. package/build/helpers/query.d.ts +25 -17
  77. package/build/helpers/query.d.ts.map +1 -1
  78. package/build/helpers/query.js +108 -280
  79. package/build/helpers/query.js.map +1 -1
  80. package/build/helpers/scoped/types.d.ts +59 -0
  81. package/build/helpers/scoped/types.d.ts.map +1 -0
  82. package/build/helpers/scoped/types.js +2 -0
  83. package/build/helpers/scoped/types.js.map +1 -0
  84. package/build/helpers/scoped.d.ts +24 -0
  85. package/build/helpers/scoped.d.ts.map +1 -0
  86. package/build/helpers/scoped.js +197 -0
  87. package/build/helpers/scoped.js.map +1 -0
  88. package/build/helpers/specification/types.d.ts +24 -0
  89. package/build/helpers/specification/types.d.ts.map +1 -0
  90. package/build/helpers/specification/types.js +2 -0
  91. package/build/helpers/specification/types.js.map +1 -0
  92. package/build/helpers/specification.d.ts +9 -13
  93. package/build/helpers/specification.d.ts.map +1 -1
  94. package/build/helpers/specification.js +47 -44
  95. package/build/helpers/specification.js.map +1 -1
  96. package/build/helpers/status/types.d.ts +48 -0
  97. package/build/helpers/status/types.d.ts.map +1 -0
  98. package/build/helpers/status/types.js +2 -0
  99. package/build/helpers/status/types.js.map +1 -0
  100. package/build/helpers/status.d.ts +16 -29
  101. package/build/helpers/status.d.ts.map +1 -1
  102. package/build/helpers/status.js +77 -73
  103. package/build/helpers/status.js.map +1 -1
  104. package/build/helpers/types.local.d.ts +17 -0
  105. package/build/helpers/types.local.d.ts.map +1 -0
  106. package/build/helpers/types.local.js +2 -0
  107. package/build/helpers/types.local.js.map +1 -0
  108. package/build/helpers/validate/types.d.ts +30 -0
  109. package/build/helpers/validate/types.d.ts.map +1 -0
  110. package/build/helpers/validate/types.js +2 -0
  111. package/build/helpers/validate/types.js.map +1 -0
  112. package/build/helpers/validate.d.ts +8 -19
  113. package/build/helpers/validate.d.ts.map +1 -1
  114. package/build/helpers/validate.js +74 -79
  115. package/build/helpers/validate.js.map +1 -1
  116. package/build/helpers/wire/types.d.ts +28 -0
  117. package/build/helpers/wire/types.d.ts.map +1 -0
  118. package/build/helpers/wire/types.js +2 -0
  119. package/build/helpers/wire/types.js.map +1 -0
  120. package/build/helpers/wire.d.ts +4 -0
  121. package/build/helpers/wire.d.ts.map +1 -0
  122. package/build/helpers/wire.js +215 -0
  123. package/build/helpers/wire.js.map +1 -0
  124. package/build/i18n/be.json +5 -0
  125. package/build/i18n/de.json +5 -0
  126. package/build/i18n/en.json +5 -0
  127. package/build/i18n/es.json +5 -0
  128. package/build/i18n/fr.json +56 -0
  129. package/build/i18n/pl.json +5 -0
  130. package/build/i18n/ru.json +5 -0
  131. package/build/i18n/uk.json +5 -0
  132. package/build/i18n.js +5 -4
  133. package/build/i18n.js.map +1 -1
  134. package/build/index.d.ts +4 -0
  135. package/build/index.d.ts.map +1 -1
  136. package/build/index.js +2 -0
  137. package/build/index.js.map +1 -1
  138. package/build/models/index.d.ts +2 -3
  139. package/build/models/index.d.ts.map +1 -1
  140. package/build/models/index.js +1 -15
  141. package/build/models/index.js.map +1 -1
  142. package/build/models/model.d.ts +5 -0
  143. package/build/models/model.d.ts.map +1 -0
  144. package/build/models/model.js +16 -0
  145. package/build/models/model.js.map +1 -0
  146. package/build/models/project.d.ts +2 -1
  147. package/build/models/project.d.ts.map +1 -1
  148. package/build/models/project.js.map +1 -1
  149. package/build/models/specification.d.ts +2 -1
  150. package/build/models/specification.d.ts.map +1 -1
  151. package/build/models/specification.js.map +1 -1
  152. package/build/models/types.d.ts +69 -0
  153. package/build/models/types.d.ts.map +1 -0
  154. package/build/models/types.js +2 -0
  155. package/build/models/types.js.map +1 -0
  156. package/build/models/workcard.d.ts +2 -1
  157. package/build/models/workcard.d.ts.map +1 -1
  158. package/build/models/workcard.js +12 -12
  159. package/build/models/workcard.js.map +1 -1
  160. package/build/schemas.d.ts +10 -2
  161. package/build/schemas.d.ts.map +1 -1
  162. package/build/schemas.js +87 -1
  163. package/build/schemas.js.map +1 -1
  164. package/build/types.d.ts +199 -148
  165. package/build/types.d.ts.map +1 -1
  166. package/build/utils.d.ts +6 -0
  167. package/build/utils.d.ts.map +1 -0
  168. package/build/utils.js +6 -0
  169. package/build/utils.js.map +1 -0
  170. package/package.json +11 -11
  171. package/src/aliases/types.ts +38 -0
  172. package/src/aliases.ts +31 -0
  173. package/src/consts.ts +26 -25
  174. package/src/entrypoints/types.ts +68 -0
  175. package/src/entrypoints.ts +34 -14
  176. package/src/errors.ts +71 -0
  177. package/src/helpers/apply/consts.local.ts +10 -0
  178. package/src/helpers/apply/types.ts +32 -0
  179. package/src/helpers/apply.ts +135 -152
  180. package/src/helpers/card/types.ts +20 -0
  181. package/src/helpers/card.ts +59 -33
  182. package/src/helpers/changes/consts.local.ts +17 -0
  183. package/src/helpers/changes/types.ts +66 -0
  184. package/src/helpers/changes.ts +267 -280
  185. package/src/helpers/code/types.ts +31 -0
  186. package/src/helpers/code.ts +52 -48
  187. package/src/helpers/index.ts +13 -0
  188. package/src/helpers/query/types.ts +26 -0
  189. package/src/helpers/query.ts +144 -314
  190. package/src/helpers/scoped/types.ts +64 -0
  191. package/src/helpers/scoped.ts +257 -0
  192. package/src/helpers/specification/types.ts +26 -0
  193. package/src/helpers/specification.ts +69 -51
  194. package/src/helpers/status/types.ts +46 -0
  195. package/src/helpers/status.ts +117 -93
  196. package/src/helpers/types.local.ts +18 -0
  197. package/src/helpers/validate/types.ts +30 -0
  198. package/src/helpers/validate.ts +84 -77
  199. package/src/helpers/wire/types.ts +31 -0
  200. package/src/helpers/wire.ts +251 -0
  201. package/src/i18n/be.json +5 -0
  202. package/src/i18n/de.json +5 -0
  203. package/src/i18n/en.json +5 -0
  204. package/src/i18n/es.json +5 -0
  205. package/src/i18n/fr.json +56 -0
  206. package/src/i18n/pl.json +5 -0
  207. package/src/i18n/ru.json +5 -0
  208. package/src/i18n/uk.json +5 -0
  209. package/src/i18n.ts +6 -4
  210. package/src/index.ts +4 -0
  211. package/src/models/index.ts +2 -17
  212. package/src/models/model.ts +18 -0
  213. package/src/models/project.ts +2 -1
  214. package/src/models/specification.ts +2 -1
  215. package/src/models/types.ts +70 -0
  216. package/src/models/workcard.ts +14 -16
  217. package/src/schemas.ts +98 -2
  218. package/src/types.ts +207 -126
  219. package/src/utils.ts +9 -0
  220. package/tests/apply.spec.ts +95 -31
  221. package/tests/code.spec.ts +9 -9
  222. package/tests/entrypoints.spec.ts +18 -2
  223. package/tests/error-status.spec.ts +6 -1
  224. package/tests/models.spec.ts +16 -15
  225. package/tests/query.spec.ts +13 -15
  226. package/tests/schemas.spec.ts +8 -8
  227. package/tests/scoped.spec.ts +154 -0
  228. package/tests/status.spec.ts +31 -22
@@ -0,0 +1,251 @@
1
+ import type { ListOptions, Sort } from '@owlmeans/resource'
2
+ import { IntrinsicStatus, WorkcardKind } from '../consts.js'
3
+ import { PlanningError } from '../errors.js'
4
+ import type { ListWire, RelationshipQuery, RelationshipQueryWire, Specification, SpecificationQuery, SpecificationQueryWire, SummaryQuery, SummaryQueryWire, TransitionQuery, TransitionQueryWire, Workcard, WorkcardQuery, WorkcardQueryWire } from '../types.js'
5
+ import type { WireHelper } from './wire/types.js'
6
+
7
+ const clean = <T extends object>(record: T): T =>
8
+ Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined)) as T
9
+
10
+ export const createWireHelper = (): WireHelper => {
11
+ const encodeList = (value?: string | readonly string[] | null): string | undefined => {
12
+ if (value == null) {
13
+ return undefined
14
+ }
15
+ if (typeof value === 'string') {
16
+ return encodeList([value])
17
+ }
18
+ if (value.length === 0) {
19
+ return undefined
20
+ }
21
+
22
+ return value.some(entry => entry.includes(',') || entry.startsWith('['))
23
+ ? JSON.stringify(value)
24
+ : value.join(',')
25
+ }
26
+
27
+ const decodeList = (value: unknown, key: string = 'list'): string[] | undefined => {
28
+ if (value == null || value === '') {
29
+ return undefined
30
+ }
31
+ if (Array.isArray(value)) {
32
+ const entries = value.filter(entry => entry != null && entry !== '').map(entry => `${entry}`)
33
+ return entries.length > 0 ? entries : undefined
34
+ }
35
+ if (typeof value !== 'string') {
36
+ return [`${value}`]
37
+ }
38
+ if (value.startsWith('[')) {
39
+ const parsed: unknown = (() => {
40
+ try {
41
+ return JSON.parse(value)
42
+ } catch {
43
+ throw new PlanningError(`malformed:query:${key}`)
44
+ }
45
+ })()
46
+ return Array.isArray(parsed) ? parsed.map(entry => `${entry}`) : undefined
47
+ }
48
+ const entries = value.split(',').map(entry => entry.trim()).filter(entry => entry !== '')
49
+
50
+ return entries.length > 0 ? entries : undefined
51
+ }
52
+
53
+ /** A list decoded to a bare value when it holds one — the same criteria either way. */
54
+ const decodeOneOrMany = <T extends string>(value: unknown, key: string): T | T[] | undefined => {
55
+ const entries = decodeList(value, key) as T[] | undefined
56
+ return entries == null ? undefined : entries.length === 1 ? entries[0] : entries
57
+ }
58
+
59
+ const encodeJson = (value: unknown): string | undefined => value == null ? undefined : JSON.stringify(value)
60
+
61
+ const decodeJson = <T>(value: unknown, key: string): T | undefined => {
62
+ if (value == null || value === '') {
63
+ return undefined
64
+ }
65
+ if (typeof value !== 'string') {
66
+ return value as T
67
+ }
68
+ try {
69
+ return JSON.parse(value) as T
70
+ } catch {
71
+ throw new PlanningError(`malformed:query:${key}`)
72
+ }
73
+ }
74
+
75
+ const decodeNumber = (value: unknown, key: string): number | undefined => {
76
+ if (value == null || value === '') {
77
+ return undefined
78
+ }
79
+ const number = typeof value === 'number' ? value : Number(value)
80
+ if (Number.isNaN(number)) {
81
+ throw new PlanningError(`malformed:query:${key}`)
82
+ }
83
+
84
+ return number
85
+ }
86
+
87
+ const decodeBoolean = (value: unknown): boolean | undefined =>
88
+ value == null || value === '' ? undefined : value === true || value === 'true' || value === '1'
89
+
90
+ const decodeText = (value: unknown): string | undefined =>
91
+ value == null || value === '' ? undefined : `${Array.isArray(value) ? value[0] : value}`
92
+
93
+ const encodeSort = <T>(sort?: Sort<T>[] | null): string | undefined =>
94
+ sort == null || sort.length === 0 ? undefined : sort.map(entry => typeof entry === 'string'
95
+ ? entry
96
+ : `${entry.order === 'desc' ? '-' : ''}${entry.field}`).join(',')
97
+
98
+ const decodeSort = <T>(value: unknown): Sort<T>[] | undefined => {
99
+ if (value == null || value === '') {
100
+ return undefined
101
+ }
102
+ const entries: unknown[] = Array.isArray(value) ? value : typeof value === 'string' ? value.split(',') : [value]
103
+ const sort = entries.flatMap((entry): Sort<T>[] => {
104
+ if (typeof entry !== 'string') {
105
+ return entry != null && typeof entry === 'object' && 'field' in entry ? [entry as Sort<T>] : []
106
+ }
107
+ const field = entry.trim()
108
+ if (field === '' || field === '-') {
109
+ return []
110
+ }
111
+ return field.startsWith('-')
112
+ ? [{ field: field.slice(1) as Sort<T> & string, order: 'desc' } as Sort<T>]
113
+ : [field as Sort<T>]
114
+ })
115
+
116
+ return sort.length > 0 ? sort : undefined
117
+ }
118
+
119
+ const encodeListOptions = (query?: ListOptions<any> | null): ListWire => clean({
120
+ page: query?.page,
121
+ size: query?.size,
122
+ sort: encodeSort(query?.sort),
123
+ })
124
+
125
+ const decodeListOptions = <T>(wire: Record<string, unknown>): ListOptions<T> => clean({
126
+ page: decodeNumber(wire.page, 'page'),
127
+ size: decodeNumber(wire.size, 'size'),
128
+ sort: decodeSort<T>(wire.sort),
129
+ })
130
+
131
+ const asRecord = (wire: unknown): Record<string, unknown> =>
132
+ wire != null && typeof wire === 'object' ? wire as Record<string, unknown> : {}
133
+
134
+ const encodeWorkcardQuery = (query?: WorkcardQuery | null): WorkcardQueryWire => clean({
135
+ ...encodeListOptions(query),
136
+ kind: encodeList(query?.kind),
137
+ type: encodeList(query?.type),
138
+ parent: query?.parent,
139
+ within: query?.within,
140
+ status: encodeList(query?.status),
141
+ intrinsic: encodeList(query?.intrinsic),
142
+ flow: encodeJson(query?.flow),
143
+ labels: encodeList(query?.labels),
144
+ code: encodeList(query?.code),
145
+ ids: encodeList(query?.ids),
146
+ fields: query?.fields != null && Object.keys(query.fields).length > 0 ? encodeJson(query.fields) : undefined,
147
+ q: query?.q,
148
+ category: encodeList(query?.category),
149
+ updatedSince: query?.updatedSince,
150
+ })
151
+
152
+ const decodeWorkcardQuery = (wire?: WorkcardQueryWire | WorkcardQuery | null): WorkcardQuery => {
153
+ const value = asRecord(wire)
154
+ return clean({
155
+ ...decodeListOptions<Workcard>(value),
156
+ kind: decodeOneOrMany<WorkcardKind>(value.kind, 'kind'),
157
+ type: decodeOneOrMany(value.type, 'type'),
158
+ parent: decodeText(value.parent),
159
+ within: decodeText(value.within),
160
+ status: decodeOneOrMany(value.status, 'status'),
161
+ intrinsic: decodeOneOrMany<IntrinsicStatus>(value.intrinsic, 'intrinsic'),
162
+ flow: decodeJson<WorkcardQuery['flow']>(value.flow, 'flow'),
163
+ labels: decodeList(value.labels, 'labels'),
164
+ code: decodeOneOrMany(value.code, 'code'),
165
+ ids: decodeList(value.ids, 'ids'),
166
+ fields: decodeJson<Record<string, unknown>>(value.fields, 'fields'),
167
+ q: decodeText(value.q),
168
+ category: decodeOneOrMany(value.category, 'category'),
169
+ updatedSince: decodeText(value.updatedSince),
170
+ })
171
+ }
172
+
173
+ const encodeSummaryQuery = (query: SummaryQuery): SummaryQueryWire => clean({
174
+ parents: encodeList(query.parents) ?? '',
175
+ kind: query.kind,
176
+ type: encodeList(query.type),
177
+ })
178
+
179
+ const decodeSummaryQuery = (wire?: SummaryQueryWire | SummaryQuery | null): SummaryQuery => {
180
+ const value = asRecord(wire)
181
+ return clean({
182
+ parents: decodeList(value.parents, 'parents') ?? [],
183
+ kind: decodeText(value.kind) as WorkcardKind | undefined,
184
+ type: decodeOneOrMany(value.type, 'type'),
185
+ })
186
+ }
187
+
188
+ const encodeTransitionQuery = (query?: TransitionQuery | null): TransitionQueryWire => clean({
189
+ ...encodeListOptions(query),
190
+ card: query?.card,
191
+ project: query?.project,
192
+ sinceSeq: query?.sinceSeq,
193
+ action: encodeList(query?.action),
194
+ state: query?.state,
195
+ })
196
+
197
+ const decodeTransitionQuery = (wire?: TransitionQueryWire | TransitionQuery | null): TransitionQuery => {
198
+ const value = asRecord(wire)
199
+ return clean({
200
+ ...decodeListOptions(value),
201
+ card: decodeText(value.card),
202
+ project: decodeText(value.project),
203
+ sinceSeq: decodeNumber(value.sinceSeq, 'sinceSeq'),
204
+ action: decodeOneOrMany(value.action, 'action'),
205
+ state: decodeText(value.state),
206
+ }) as TransitionQuery
207
+ }
208
+
209
+ const encodeSpecificationQuery = (query?: SpecificationQuery | null): SpecificationQueryWire => clean({
210
+ ...encodeListOptions(query),
211
+ category: encodeList(query?.category),
212
+ all: query?.all,
213
+ })
214
+
215
+ const decodeSpecificationQuery = (wire?: SpecificationQueryWire | SpecificationQuery | null): SpecificationQuery => {
216
+ const value = asRecord(wire)
217
+ return clean({
218
+ ...decodeListOptions<Specification>(value),
219
+ category: decodeOneOrMany(value.category, 'category'),
220
+ all: decodeBoolean(value.all),
221
+ })
222
+ }
223
+
224
+ const encodeRelationshipQuery = (query?: RelationshipQuery | null): RelationshipQueryWire => clean({
225
+ ...encodeListOptions(query),
226
+ from: encodeList(query?.from),
227
+ to: encodeList(query?.to),
228
+ type: encodeList(query?.type),
229
+ })
230
+
231
+ const decodeRelationshipQuery = (wire?: RelationshipQueryWire | RelationshipQuery | null): RelationshipQuery => {
232
+ const value = asRecord(wire)
233
+ return clean({
234
+ ...decodeListOptions(value),
235
+ from: decodeOneOrMany(value.from, 'from'),
236
+ to: decodeOneOrMany(value.to, 'to'),
237
+ type: decodeOneOrMany(value.type, 'type'),
238
+ }) as RelationshipQuery
239
+ }
240
+
241
+ return {
242
+ encodeList, decodeList, encodeSort, decodeSort,
243
+ encodeWorkcardQuery, decodeWorkcardQuery,
244
+ encodeSummaryQuery, decodeSummaryQuery,
245
+ encodeTransitionQuery, decodeTransitionQuery,
246
+ encodeSpecificationQuery, decodeSpecificationQuery,
247
+ encodeRelationshipQuery, decodeRelationshipQuery,
248
+ }
249
+ }
250
+
251
+ export const wireHelper = createWireHelper()
package/src/i18n/be.json CHANGED
@@ -46,6 +46,11 @@
46
46
  "PlanningErrorCommitTimeout": "Змяненне яшчэ захоўваецца — праверце крыху пазней",
47
47
  "PlanningErrorCommitFailed": "Не ўдалося захаваць змяненне",
48
48
  "PlanningErrorScopeMismatch": "Гэты элемент не існуе або недаступны вам",
49
+ "PlanningErrorSchemaConflict": "Гэта вызначэнне змянілі за гэты час — абнавіце і паспрабуйце зноў",
50
+ "PlanningErrorSchemaInUse": "Гэта вызначэнне яшчэ выкарыстоўваецца і не можа быць выведзена з ужытку",
51
+ "PlanningErrorSchemaSealed": "Гэта вызначэнне замацавана і не можа быць зменена тут",
52
+ "PlanningErrorSchemaInvalid": "Гэта вызначэнне несапраўднае",
53
+ "PlanningErrorForbidden": "Вам не дазволена рабіць гэтую змену",
49
54
  "PlanningErrorUnsupported": "Гэтая аперацыя не падтрымліваецца для гэтага элемента"
50
55
  }
51
56
  }
package/src/i18n/de.json CHANGED
@@ -46,6 +46,11 @@
46
46
  "PlanningErrorCommitTimeout": "Die Änderung wird noch gespeichert — prüfe es gleich noch einmal",
47
47
  "PlanningErrorCommitFailed": "Die Änderung konnte nicht gespeichert werden",
48
48
  "PlanningErrorScopeMismatch": "Dieses Element existiert nicht oder ist für dich nicht verfügbar",
49
+ "PlanningErrorSchemaConflict": "Diese Definition wurde inzwischen geändert — neu laden und erneut versuchen",
50
+ "PlanningErrorSchemaInUse": "Diese Definition wird noch verwendet und kann nicht zurückgezogen werden",
51
+ "PlanningErrorSchemaSealed": "Diese Definition ist festgelegt und kann hier nicht geändert werden",
52
+ "PlanningErrorSchemaInvalid": "Diese Definition ist ungültig",
53
+ "PlanningErrorForbidden": "Du darfst diese Änderung nicht vornehmen",
49
54
  "PlanningErrorUnsupported": "Dieser Vorgang wird für dieses Element nicht unterstützt"
50
55
  }
51
56
  }
package/src/i18n/en.json CHANGED
@@ -46,6 +46,11 @@
46
46
  "PlanningErrorCommitTimeout": "The change is still being saved — check again shortly",
47
47
  "PlanningErrorCommitFailed": "The change could not be saved",
48
48
  "PlanningErrorScopeMismatch": "This item does not exist or is not available to you",
49
+ "PlanningErrorSchemaConflict": "This definition was changed in the meantime — reload and try again",
50
+ "PlanningErrorSchemaInUse": "This definition is still in use and cannot be retired",
51
+ "PlanningErrorSchemaSealed": "This definition is fixed and cannot be changed here",
52
+ "PlanningErrorSchemaInvalid": "This definition is not valid",
53
+ "PlanningErrorForbidden": "You are not allowed to make this change",
49
54
  "PlanningErrorUnsupported": "This operation is not supported for this item"
50
55
  }
51
56
  }
package/src/i18n/es.json CHANGED
@@ -46,6 +46,11 @@
46
46
  "PlanningErrorCommitTimeout": "El cambio aún se está guardando — vuelve a comprobarlo en breve",
47
47
  "PlanningErrorCommitFailed": "No se pudo guardar el cambio",
48
48
  "PlanningErrorScopeMismatch": "Este elemento no existe o no está disponible para ti",
49
+ "PlanningErrorSchemaConflict": "Esta definición cambió mientras tanto — recarga e inténtalo de nuevo",
50
+ "PlanningErrorSchemaInUse": "Esta definición todavía está en uso y no se puede retirar",
51
+ "PlanningErrorSchemaSealed": "Esta definición es fija y no se puede cambiar aquí",
52
+ "PlanningErrorSchemaInvalid": "Esta definición no es válida",
53
+ "PlanningErrorForbidden": "No tienes permiso para hacer este cambio",
49
54
  "PlanningErrorUnsupported": "Esta operación no es compatible con este elemento"
50
55
  }
51
56
  }
@@ -0,0 +1,56 @@
1
+ {
2
+ "kind": {
3
+ "card": "Carte",
4
+ "project": "Projet",
5
+ "specification": "Spécification"
6
+ },
7
+ "intrinsic": {
8
+ "planned": "Planifié",
9
+ "in-progress": "En cours",
10
+ "closed": "Fermé"
11
+ },
12
+ "action": {
13
+ "create": "Créé",
14
+ "update": "Mis à jour",
15
+ "transit": "Statut modifié",
16
+ "link": "Lié",
17
+ "unlink": "Délié",
18
+ "delete": "Supprimé"
19
+ },
20
+ "commit": {
21
+ "pending": "Enregistrement…",
22
+ "committed": "Enregistré",
23
+ "failed": "Non enregistré"
24
+ },
25
+ "format": {
26
+ "markdown": "Markdown",
27
+ "json": "JSON",
28
+ "text": "Texte brut"
29
+ },
30
+ "errors": {
31
+ "PlanningError": "La demande de planification n’a pas pu être traitée",
32
+ "PlanningErrorWorkcardNotFound": "Cet élément n’existe pas ou ne vous est pas accessible",
33
+ "PlanningErrorUnknownWorkcardType": "Ce type d’élément est inconnu",
34
+ "PlanningErrorUnknownStatusFlow": "Ce flux de statut est inconnu",
35
+ "PlanningErrorParentNotFound": "L’élément parent n’existe pas",
36
+ "PlanningErrorCardTypeNotAllowed": "Ce type d’élément ne peut pas être ajouté ici",
37
+ "PlanningErrorRefused": "La modification a été refusée",
38
+ "PlanningErrorIllegalTransition": "Ce changement de statut n’est pas autorisé depuis le statut actuel",
39
+ "PlanningErrorFieldsInvalid": "Certains champs ont des valeurs non valides",
40
+ "PlanningErrorLabelNotAllowed": "Cette étiquette n’est pas autorisée ici",
41
+ "PlanningErrorSpecificationSlotUnknown": "Ce type de document n’est pas autorisé ici",
42
+ "PlanningErrorSpecificationRevisionConflict": "Ce document existe déjà : modifiez-le plutôt",
43
+ "PlanningErrorRelationshipRefused": "Ce lien n’est pas autorisé",
44
+ "PlanningErrorCodeTaken": "Ce code est déjà utilisé",
45
+ "PlanningErrorWorkcardConflict": "Quelqu’un a modifié cet élément entre-temps. Actualisez la page puis réessayez.",
46
+ "PlanningErrorCommitTimeout": "La modification est encore en cours d’enregistrement. Vérifiez de nouveau dans un instant.",
47
+ "PlanningErrorCommitFailed": "La modification n’a pas pu être enregistrée",
48
+ "PlanningErrorScopeMismatch": "Cet élément n’existe pas ou ne vous est pas accessible",
49
+ "PlanningErrorSchemaConflict": "Cette définition a été modifiée entre-temps. Actualisez la page puis réessayez.",
50
+ "PlanningErrorSchemaInUse": "Cette définition est encore utilisée et ne peut pas être retirée",
51
+ "PlanningErrorSchemaSealed": "Cette définition est figée et ne peut pas être modifiée ici",
52
+ "PlanningErrorSchemaInvalid": "Cette définition n’est pas valide",
53
+ "PlanningErrorForbidden": "Vous n’êtes pas autorisé à effectuer cette modification",
54
+ "PlanningErrorUnsupported": "Cette opération n’est pas prise en charge pour cet élément"
55
+ }
56
+ }
package/src/i18n/pl.json CHANGED
@@ -46,6 +46,11 @@
46
46
  "PlanningErrorCommitTimeout": "Zmiana jest nadal zapisywana — sprawdź ponownie za chwilę",
47
47
  "PlanningErrorCommitFailed": "Nie udało się zapisać zmiany",
48
48
  "PlanningErrorScopeMismatch": "Ten element nie istnieje lub nie jest dla Ciebie dostępny",
49
+ "PlanningErrorSchemaConflict": "Ta definicja została w międzyczasie zmieniona — odśwież i spróbuj ponownie",
50
+ "PlanningErrorSchemaInUse": "Ta definicja jest nadal używana i nie może zostać wycofana",
51
+ "PlanningErrorSchemaSealed": "Ta definicja jest stała i nie można jej tu zmienić",
52
+ "PlanningErrorSchemaInvalid": "Ta definicja jest nieprawidłowa",
53
+ "PlanningErrorForbidden": "Nie masz uprawnień do wprowadzenia tej zmiany",
49
54
  "PlanningErrorUnsupported": "Ta operacja nie jest obsługiwana dla tego elementu"
50
55
  }
51
56
  }
package/src/i18n/ru.json CHANGED
@@ -46,6 +46,11 @@
46
46
  "PlanningErrorCommitTimeout": "Изменение ещё сохраняется — проверьте чуть позже",
47
47
  "PlanningErrorCommitFailed": "Не удалось сохранить изменение",
48
48
  "PlanningErrorScopeMismatch": "Этот элемент не существует или недоступен вам",
49
+ "PlanningErrorSchemaConflict": "Это определение изменили за это время — обновите и попробуйте снова",
50
+ "PlanningErrorSchemaInUse": "Это определение ещё используется и не может быть выведено из употребления",
51
+ "PlanningErrorSchemaSealed": "Это определение закреплено и не может быть изменено здесь",
52
+ "PlanningErrorSchemaInvalid": "Это определение недействительно",
53
+ "PlanningErrorForbidden": "У вас нет прав на это изменение",
49
54
  "PlanningErrorUnsupported": "Эта операция не поддерживается для этого элемента"
50
55
  }
51
56
  }
package/src/i18n/uk.json CHANGED
@@ -46,6 +46,11 @@
46
46
  "PlanningErrorCommitTimeout": "Зміна ще зберігається — перевірте трохи пізніше",
47
47
  "PlanningErrorCommitFailed": "Не вдалося зберегти зміну",
48
48
  "PlanningErrorScopeMismatch": "Цей елемент не існує або недоступний вам",
49
+ "PlanningErrorSchemaConflict": "Це визначення змінили тим часом — оновіть і спробуйте знову",
50
+ "PlanningErrorSchemaInUse": "Це визначення ще використовується і не може бути виведене з ужитку",
51
+ "PlanningErrorSchemaSealed": "Це визначення закріплене і не може бути змінене тут",
52
+ "PlanningErrorSchemaInvalid": "Це визначення недійсне",
53
+ "PlanningErrorForbidden": "Вам не дозволено вносити цю зміну",
49
54
  "PlanningErrorUnsupported": "Ця операція не підтримується для цього елемента"
50
55
  }
51
56
  }
package/src/i18n.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { addI18nLib } from '@owlmeans/i18n'
1
+
2
2
  import { PLANNING_I18N } from './consts.js'
3
3
 
4
4
  import en from './i18n/en.json' with { type: 'json' }
@@ -8,14 +8,16 @@ import be from './i18n/be.json' with { type: 'json' }
8
8
  import uk from './i18n/uk.json' with { type: 'json' }
9
9
  import es from './i18n/es.json' with { type: 'json' }
10
10
  import de from './i18n/de.json' with { type: 'json' }
11
+ import fr from './i18n/fr.json' with { type: 'json' }
12
+ import { i18nHelper } from '@owlmeans/i18n'
11
13
 
12
14
  /**
13
15
  * Labels under `lib:planning.*`, and every refusal under the shared `errors` resource keyed by its
14
16
  * type name — which is where a panel's error lookup (`errors.<type>`) finds them.
15
17
  */
16
- const LANGUAGES = { en, pl, ru, be, uk, es, de }
18
+ const LANGUAGES = { en, pl, ru, be, uk, es, de, fr }
17
19
 
18
20
  Object.entries(LANGUAGES).forEach(([lng, data]) => {
19
- addI18nLib(lng, PLANNING_I18N, data)
20
- addI18nLib(lng, 'errors', data.errors)
21
+ i18nHelper.addI18nLib(lng, PLANNING_I18N, data)
22
+ i18nHelper.addI18nLib(lng, 'errors', data.errors)
21
23
  })
package/src/index.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  export type * from './types.js'
2
+ export type * from './aliases/types.js'
3
+ export type * from './entrypoints/types.js'
2
4
  export * from './consts.js'
5
+ export * from './aliases.js'
6
+ export * from './utils.js'
3
7
  export * from './schemas.js'
4
8
  export * from './errors.js'
5
9
  export * from './entrypoints.js'
@@ -1,21 +1,6 @@
1
- import { WorkcardKind } from '../consts.js'
2
- import type { PlanningFacade, Project, Specification, Workcard, WorkcardModel } from '../types.js'
3
- import { makeProjectModel } from './project.js'
4
- import { makeSpecificationModel } from './specification.js'
5
- import { makeWorkcardModel } from './workcard.js'
1
+ export type * from './types.js'
6
2
 
7
3
  export { executeFor, makeWorkcardModel } from './workcard.js'
8
4
  export { makeProjectModel } from './project.js'
9
5
  export { makeSpecificationModel } from './specification.js'
10
-
11
- /** The model a record's kind calls for — what a facade's `model()` answers. */
12
- export const modelOf = <T extends Workcard = Workcard>(record: T, facade: PlanningFacade): WorkcardModel<T> => {
13
- switch (record.kind) {
14
- case WorkcardKind.Project:
15
- return makeProjectModel(record as unknown as Project, facade) as unknown as WorkcardModel<T>
16
- case WorkcardKind.Specification:
17
- return makeSpecificationModel(record as unknown as Specification, facade) as unknown as WorkcardModel<T>
18
- default:
19
- return makeWorkcardModel(record, facade)
20
- }
21
- }
6
+ export { modelOf } from './model.js'
@@ -0,0 +1,18 @@
1
+ import { WorkcardKind } from '../consts.js'
2
+ import type { PlanningFacade, Project, Specification, Workcard } from '../types.js'
3
+ import { makeProjectModel } from './project.js'
4
+ import { makeSpecificationModel } from './specification.js'
5
+ import type { WorkcardModel } from './types.js'
6
+ import { makeWorkcardModel } from './workcard.js'
7
+
8
+ /** The model a record's kind calls for — what a facade's `model()` answers. */
9
+ export const modelOf = <T extends Workcard = Workcard>(record: T, facade: PlanningFacade): WorkcardModel<T> => {
10
+ switch (record.kind) {
11
+ case WorkcardKind.Project:
12
+ return makeProjectModel(record as unknown as Project, facade) as unknown as WorkcardModel<T>
13
+ case WorkcardKind.Specification:
14
+ return makeSpecificationModel(record as unknown as Specification, facade) as unknown as WorkcardModel<T>
15
+ default:
16
+ return makeWorkcardModel(record, facade)
17
+ }
18
+ }
@@ -1,5 +1,6 @@
1
1
  import { IntrinsicStatus, WorkcardKind } from '../consts.js'
2
- import type { PlanningFacade, Project, ProjectModel } from '../types.js'
2
+ import type { PlanningFacade, Project } from '../types.js'
3
+ import type { ProjectModel } from './types.js'
3
4
  import { makeWorkcardModel } from './workcard.js'
4
5
 
5
6
  /** A workcard model plus the project's own reads: its cards, its sub-projects, its summary. */
@@ -1,4 +1,5 @@
1
- import type { PlanningFacade, Specification, SpecificationModel } from '../types.js'
1
+ import type { PlanningFacade, Specification } from '../types.js'
2
+ import type { SpecificationModel } from './types.js'
2
3
  import { makeWorkcardModel } from './workcard.js'
3
4
 
4
5
  /** A workcard model over one document: its body, a revision of it, and its history from the log. */
@@ -0,0 +1,70 @@
1
+ import type { ListResult } from '@owlmeans/resource'
2
+ import type { IntrinsicStatus, SpecificationFormat, WorkcardKind } from '../consts.js'
3
+ import type {
4
+ AnyTypeSchema, ExecuteOptions, IntrinsicCounts, Project, Relationship, RelationshipQuery, Specification,
5
+ SpecificationQuery, SpecificationRevision, StatusFlowSchema, StatusTransitionRule, SummaryQuery, Transition,
6
+ TransitionActor, TransitionQuery, TransitionReceipt, Workcard, WorkcardChanges, WorkcardQuery,
7
+ } from '../types.js'
8
+
9
+ export interface ExecuteMeta {
10
+ actor?: TransitionActor
11
+ cause?: string
12
+ key?: string
13
+ expectSeq?: number | null
14
+ }
15
+
16
+ export interface ModelExecuteOptions extends ExecuteOptions, ExecuteMeta {}
17
+
18
+ export interface SpecificationWriteOptions extends ModelExecuteOptions {
19
+ format?: SpecificationFormat
20
+ version?: number
21
+ ref?: string
22
+ /** The title a new document gets; the category when omitted. */
23
+ title?: string
24
+ /** The specification type; resolved from the slot and the registry when omitted. */
25
+ type?: string
26
+ }
27
+
28
+ export interface WorkcardModel<T extends Workcard = Workcard> {
29
+ record: T
30
+ id: string
31
+ kind: WorkcardKind
32
+ type: string
33
+ schema: () => AnyTypeSchema
34
+ flow: (id?: string) => StatusFlowSchema
35
+ statusOf: (flowId?: string) => string
36
+ intrinsicOf: (flowId?: string) => IntrinsicStatus
37
+ can: (transition: string, flowId?: string) => boolean
38
+ available: (flowId?: string) => StatusTransitionRule[]
39
+ pending: () => boolean
40
+ transit: (transition: string, changes?: WorkcardChanges, opts?: ModelExecuteOptions & { flow?: string }) => Promise<TransitionReceipt>
41
+ update: (changes: WorkcardChanges, opts?: ModelExecuteOptions & { unset?: string[] }) => Promise<TransitionReceipt>
42
+ remove: (opts?: ModelExecuteOptions) => Promise<TransitionReceipt>
43
+ link: (type: string, to: string, fields?: Record<string, unknown>, opts?: ModelExecuteOptions) => Promise<TransitionReceipt>
44
+ unlink: (type: string, to: string, opts?: ModelExecuteOptions) => Promise<TransitionReceipt>
45
+ children: (query?: Omit<WorkcardQuery, 'parent'>) => Promise<ListResult<Workcard>>
46
+ relationships: (query?: RelationshipQuery) => Promise<ListResult<Relationship>>
47
+ transitions: (query?: Omit<TransitionQuery, 'card'>) => Promise<ListResult<Transition>>
48
+ specification: (category: string) => Promise<Specification | null>
49
+ specifications: (query?: SpecificationQuery) => Promise<ListResult<Specification>>
50
+ revisions: (category: string, limit?: number) => Promise<SpecificationRevision[]>
51
+ /** Create the slot's document, or revise it when it already exists. */
52
+ write: (category: string, body: string, opts?: SpecificationWriteOptions) => Promise<TransitionReceipt>
53
+ reload: () => Promise<WorkcardModel<T>>
54
+ }
55
+
56
+ export interface ProjectModel extends WorkcardModel<Project> {
57
+ cards: (query?: Omit<WorkcardQuery, 'within' | 'kind'>) => Promise<ListResult<Workcard>>
58
+ projects: (query?: Omit<WorkcardQuery, 'within' | 'kind'>) => Promise<ListResult<Workcard>>
59
+ summary: (query?: Omit<SummaryQuery, 'parents'>) => Promise<IntrinsicCounts>
60
+ /** A `delete` of the project — the store removes everything under it. */
61
+ purge: (opts?: ModelExecuteOptions) => Promise<TransitionReceipt>
62
+ reload: () => Promise<ProjectModel>
63
+ }
64
+
65
+ export interface SpecificationModel extends WorkcardModel<Specification> {
66
+ body: () => string | undefined
67
+ revise: (body: string, opts?: ModelExecuteOptions & { version?: number, ref?: string }) => Promise<TransitionReceipt>
68
+ history: (limit?: number) => Promise<SpecificationRevision[]>
69
+ reload: () => Promise<SpecificationModel>
70
+ }
@@ -1,14 +1,12 @@
1
1
  import { IntrinsicStatus, TransitionAction, WorkcardKind } from '../consts.js'
2
2
  import { PlanningError } from '../errors.js'
3
- import { isPending } from '../helpers/card.js'
4
- import { slotOf, specificationTypeOf } from '../helpers/specification.js'
5
- import {
6
- canTransit, initialStatusOf, intrinsicOf, primaryFlowOf, transitionsFrom,
7
- } from '../helpers/status.js'
3
+ import { cardHelper } from '../helpers/card.js'
4
+ import { specificationHelper } from '../helpers/specification.js'
5
+ import { statusHelper } from '../helpers/status.js'
8
6
  import type {
9
- ExecuteOptions, ModelExecuteOptions, PlanningFacade, TransitionExecution, TransitionReceipt,
10
- Workcard, WorkcardModel,
7
+ ExecuteOptions, PlanningFacade, TransitionExecution, TransitionReceipt, Workcard,
11
8
  } from '../types.js'
9
+ import type { ModelExecuteOptions, WorkcardModel } from './types.js'
12
10
 
13
11
  /**
14
12
  * Run an execution with a model's defaults: `expectSeq` is the record's head (its seq when nothing
@@ -53,25 +51,25 @@ export const makeWorkcardModel = <T extends Workcard = Workcard>(record: T, faca
53
51
 
54
52
  schema: () => facade.schemas.type(record.type),
55
53
 
56
- flow: flowId => facade.schemas.flow(flowId ?? primaryFlowOf(model.schema())),
54
+ flow: flowId => facade.schemas.flow(flowId ?? statusHelper.primaryFlowOf(model.schema())),
57
55
 
58
56
  statusOf: flowId => {
59
- const primary = primaryFlowOf(model.schema())
57
+ const primary = statusHelper.primaryFlowOf(model.schema())
60
58
  if (flowId == null || flowId === primary) {
61
59
  return record.status
62
60
  }
63
- return record.flows[flowId] ?? initialStatusOf(model.flow(flowId))
61
+ return record.flows[flowId] ?? statusHelper.initialStatusOf(model.flow(flowId))
64
62
  },
65
63
 
66
64
  intrinsicOf: flowId => flowId == null
67
65
  ? record.intrinsic
68
- : intrinsicOf(model.flow(flowId), model.statusOf(flowId)) ?? IntrinsicStatus.Planned,
66
+ : statusHelper.intrinsicOf(model.flow(flowId), model.statusOf(flowId)) ?? IntrinsicStatus.Planned,
69
67
 
70
- can: (transition, flowId) => canTransit(model.flow(flowId), transition, model.statusOf(flowId)),
68
+ can: (transition, flowId) => statusHelper.canTransit(model.flow(flowId), transition, model.statusOf(flowId)),
71
69
 
72
- available: flowId => transitionsFrom(model.flow(flowId), model.statusOf(flowId)),
70
+ available: flowId => statusHelper.transitionsFrom(model.flow(flowId), model.statusOf(flowId)),
73
71
 
74
- pending: () => isPending(record),
72
+ pending: () => cardHelper.isPending(record),
75
73
 
76
74
  transit: (transition, changes, opts) => run({
77
75
  action: TransitionAction.Transit, transition, flow: opts?.flow, changes,
@@ -114,11 +112,11 @@ export const makeWorkcardModel = <T extends Workcard = Workcard>(record: T, faca
114
112
  }, opts)
115
113
  }
116
114
 
117
- const slot = slotOf(model.schema(), category)
115
+ const slot = specificationHelper.slotOf(model.schema(), category)
118
116
  return executeFor(facade, null, {
119
117
  card: Object.fromEntries(Object.entries({
120
118
  kind: WorkcardKind.Specification,
121
- type: opts?.type ?? specificationTypeOf(facade.schemas, record.type, slot),
119
+ type: opts?.type ?? specificationHelper.specificationTypeOf(facade.schemas, record.type, slot),
122
120
  parent: id,
123
121
  title: opts?.title ?? category,
124
122
  category,