@owlmeans/planning 0.1.18-rc.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 (139) hide show
  1. package/README.md +169 -0
  2. package/agent-meta/manifest.json +16 -0
  3. package/agent-meta/skills/planning/SKILL.md +248 -0
  4. package/build/consts.d.ts +126 -0
  5. package/build/consts.d.ts.map +1 -0
  6. package/build/consts.js +129 -0
  7. package/build/consts.js.map +1 -0
  8. package/build/entrypoints.d.ts +14 -0
  9. package/build/entrypoints.d.ts.map +1 -0
  10. package/build/entrypoints.js +69 -0
  11. package/build/entrypoints.js.map +1 -0
  12. package/build/errors.d.ts +94 -0
  13. package/build/errors.d.ts.map +1 -0
  14. package/build/errors.js +169 -0
  15. package/build/errors.js.map +1 -0
  16. package/build/helpers/apply.d.ts +27 -0
  17. package/build/helpers/apply.d.ts.map +1 -0
  18. package/build/helpers/apply.js +162 -0
  19. package/build/helpers/apply.js.map +1 -0
  20. package/build/helpers/card.d.ts +17 -0
  21. package/build/helpers/card.d.ts.map +1 -0
  22. package/build/helpers/card.js +31 -0
  23. package/build/helpers/card.js.map +1 -0
  24. package/build/helpers/changes.d.ts +51 -0
  25. package/build/helpers/changes.d.ts.map +1 -0
  26. package/build/helpers/changes.js +277 -0
  27. package/build/helpers/changes.js.map +1 -0
  28. package/build/helpers/code.d.ts +24 -0
  29. package/build/helpers/code.d.ts.map +1 -0
  30. package/build/helpers/code.js +47 -0
  31. package/build/helpers/code.js.map +1 -0
  32. package/build/helpers/index.d.ts +9 -0
  33. package/build/helpers/index.d.ts.map +1 -0
  34. package/build/helpers/index.js +9 -0
  35. package/build/helpers/index.js.map +1 -0
  36. package/build/helpers/query.d.ts +39 -0
  37. package/build/helpers/query.d.ts.map +1 -0
  38. package/build/helpers/query.js +293 -0
  39. package/build/helpers/query.js.map +1 -0
  40. package/build/helpers/specification.d.ts +21 -0
  41. package/build/helpers/specification.d.ts.map +1 -0
  42. package/build/helpers/specification.js +48 -0
  43. package/build/helpers/specification.js.map +1 -0
  44. package/build/helpers/status.d.ts +44 -0
  45. package/build/helpers/status.d.ts.map +1 -0
  46. package/build/helpers/status.js +78 -0
  47. package/build/helpers/status.js.map +1 -0
  48. package/build/helpers/validate.d.ts +30 -0
  49. package/build/helpers/validate.d.ts.map +1 -0
  50. package/build/helpers/validate.js +93 -0
  51. package/build/helpers/validate.js.map +1 -0
  52. package/build/i18n/be.json +51 -0
  53. package/build/i18n/de.json +51 -0
  54. package/build/i18n/en.json +51 -0
  55. package/build/i18n/es.json +51 -0
  56. package/build/i18n/pl.json +51 -0
  57. package/build/i18n/ru.json +51 -0
  58. package/build/i18n/uk.json +51 -0
  59. package/build/i18n.d.ts +2 -0
  60. package/build/i18n.d.ts.map +1 -0
  61. package/build/i18n.js +19 -0
  62. package/build/i18n.js.map +1 -0
  63. package/build/index.d.ts +10 -0
  64. package/build/index.d.ts.map +1 -0
  65. package/build/index.js +9 -0
  66. package/build/index.js.map +1 -0
  67. package/build/models/index.d.ts +7 -0
  68. package/build/models/index.d.ts.map +1 -0
  69. package/build/models/index.js +19 -0
  70. package/build/models/index.js.map +1 -0
  71. package/build/models/project.d.ts +4 -0
  72. package/build/models/project.d.ts.map +1 -0
  73. package/build/models/project.js +17 -0
  74. package/build/models/project.js.map +1 -0
  75. package/build/models/specification.d.ts +4 -0
  76. package/build/models/specification.d.ts.map +1 -0
  77. package/build/models/specification.js +15 -0
  78. package/build/models/specification.js.map +1 -0
  79. package/build/models/workcard.d.ts +14 -0
  80. package/build/models/workcard.d.ts.map +1 -0
  81. package/build/models/workcard.js +103 -0
  82. package/build/models/workcard.js.map +1 -0
  83. package/build/queue/index.d.ts +25 -0
  84. package/build/queue/index.d.ts.map +1 -0
  85. package/build/queue/index.js +14 -0
  86. package/build/queue/index.js.map +1 -0
  87. package/build/registry.d.ts +10 -0
  88. package/build/registry.d.ts.map +1 -0
  89. package/build/registry.js +75 -0
  90. package/build/registry.js.map +1 -0
  91. package/build/schemas.d.ts +56 -0
  92. package/build/schemas.d.ts.map +1 -0
  93. package/build/schemas.js +578 -0
  94. package/build/schemas.js.map +1 -0
  95. package/build/types.d.ts +785 -0
  96. package/build/types.d.ts.map +1 -0
  97. package/build/types.js +2 -0
  98. package/build/types.js.map +1 -0
  99. package/package.json +58 -0
  100. package/src/consts.ts +136 -0
  101. package/src/entrypoints.ts +127 -0
  102. package/src/errors.ts +207 -0
  103. package/src/helpers/apply.ts +183 -0
  104. package/src/helpers/card.ts +42 -0
  105. package/src/helpers/changes.ts +333 -0
  106. package/src/helpers/code.ts +66 -0
  107. package/src/helpers/index.ts +8 -0
  108. package/src/helpers/query.ts +345 -0
  109. package/src/helpers/specification.ts +65 -0
  110. package/src/helpers/status.ts +117 -0
  111. package/src/helpers/validate.ts +107 -0
  112. package/src/i18n/be.json +51 -0
  113. package/src/i18n/de.json +51 -0
  114. package/src/i18n/en.json +51 -0
  115. package/src/i18n/es.json +51 -0
  116. package/src/i18n/pl.json +51 -0
  117. package/src/i18n/ru.json +51 -0
  118. package/src/i18n/uk.json +51 -0
  119. package/src/i18n.ts +21 -0
  120. package/src/index.ts +9 -0
  121. package/src/models/index.ts +21 -0
  122. package/src/models/project.ts +24 -0
  123. package/src/models/specification.ts +21 -0
  124. package/src/models/workcard.ts +138 -0
  125. package/src/queue/index.ts +31 -0
  126. package/src/registry.ts +90 -0
  127. package/src/schemas.ts +654 -0
  128. package/src/types.ts +837 -0
  129. package/tests/apply.spec.ts +176 -0
  130. package/tests/code.spec.ts +40 -0
  131. package/tests/entrypoints.spec.ts +57 -0
  132. package/tests/fixtures.ts +124 -0
  133. package/tests/i18n.spec.ts +30 -0
  134. package/tests/models.spec.ts +133 -0
  135. package/tests/query.spec.ts +90 -0
  136. package/tests/schemas.spec.ts +74 -0
  137. package/tests/status.spec.ts +75 -0
  138. package/tests/tsconfig.json +17 -0
  139. package/tsconfig.json +15 -0
@@ -0,0 +1,74 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import * as schemas from '../src/schemas.js'
3
+ import { TransitionAction, WorkcardKind } from '../src/consts.js'
4
+ import { makeAjv, validateCard } from '../src/helpers/validate.js'
5
+ import { applyTransition } from '../src/helpers/apply.js'
6
+ import { computeChanges } from '../src/helpers/changes.js'
7
+ import { FieldsInvalid } from '../src/errors.js'
8
+ import { AT, STORY_TYPE, makeRegistry, transitionOf } from './fixtures.js'
9
+
10
+ type Node = Record<string, unknown>
11
+
12
+ /** Every fault of the two `$jsonSchema` rules, as `path: reason`. */
13
+ const faultsOf = (node: unknown, path: string): string[] => {
14
+ if (node == null || typeof node !== 'object') {
15
+ return []
16
+ }
17
+ if (Array.isArray(node)) {
18
+ return node.flatMap((entry, index) => faultsOf(entry, `${path}[${index}]`))
19
+ }
20
+ const schema = node as Node
21
+ const faults: string[] = []
22
+ const types = Array.isArray(schema.type) ? schema.type : [schema.type]
23
+ if (types.includes('integer')) {
24
+ faults.push(`${path}: integer`)
25
+ }
26
+ if (schema.properties != null && typeof schema.properties === 'object') {
27
+ const required = new Set((schema.required as string[] | undefined) ?? [])
28
+ for (const [key, property] of Object.entries(schema.properties as Record<string, Node>)) {
29
+ if (!required.has(key) && property.nullable !== true) {
30
+ faults.push(`${path}.${key}: optional but not nullable`)
31
+ }
32
+ }
33
+ }
34
+ for (const [key, value] of Object.entries(schema)) {
35
+ if (key !== 'enum' && key !== 'required') {
36
+ faults.push(...faultsOf(value, `${path}.${key}`))
37
+ }
38
+ }
39
+
40
+ return faults
41
+ }
42
+
43
+ describe('schemas', () => {
44
+ const exported = Object.entries(schemas).filter(([name]) => name.endsWith('Schema'))
45
+
46
+ test('no exported schema uses integer, and every optional property is nullable', () => {
47
+ expect(exported.length).toBeGreaterThan(40)
48
+ expect(exported.flatMap(([name, schema]) => faultsOf(schema, name))).toEqual([])
49
+ })
50
+
51
+ test('every exported schema compiles, and a folded record validates against its own schema', () => {
52
+ const ajv = makeAjv()
53
+ exported.forEach(([, schema]) => ajv.compile(schema as object))
54
+
55
+ const registry = makeRegistry()
56
+ const { changes } = computeChanges(undefined, {
57
+ action: TransitionAction.Create,
58
+ card: { kind: WorkcardKind.Card, type: STORY_TYPE.type, title: 'Story', fields: { area: 'user', primary: true } },
59
+ }, STORY_TYPE, registry, AT)
60
+ const card = applyTransition(undefined, transitionOf({ card: 'c1', seq: 1, action: TransitionAction.Create, changes }))!
61
+
62
+ expect(() => validateCard(card, registry)).not.toThrow()
63
+ expect(() => validateCard({ ...card, fields: { area: 'moon', primary: true } }, registry)).toThrow(FieldsInvalid)
64
+ expect(() => validateCard({ ...card, fields: { 'a.b': 1 } }, registry)).toThrow('keys:a.b')
65
+ })
66
+
67
+ test('an execute request accepts a card id or a create draft, and refuses an unknown action', () => {
68
+ const validate = makeAjv().compile(schemas.ExecuteRequestSchema)
69
+
70
+ expect(validate({ card: 'c1', action: 'transit', transition: 'start', expectSeq: null, wait: true })).toBe(true)
71
+ expect(validate({ card: { kind: 'card', type: 'test:story', title: 'Story' }, action: 'create' })).toBe(true)
72
+ expect(validate({ card: 'c1', action: 'explode' })).toBe(false)
73
+ })
74
+ })
@@ -0,0 +1,75 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import { IntrinsicStatus } from '../src/consts.js'
3
+ import { UnknownStatusFlow, UnknownWorkcardType } from '../src/errors.js'
4
+ import {
5
+ canTransit, initialFlowsOf, initialStatusOf, intrinsicOf, resolveIntrinsic, ruleOf, transitionsFrom,
6
+ } from '../src/helpers/status.js'
7
+ import { PROJECT_TYPE, REVIEW_FLOW, STORY_FLOW, STORY_TYPE, TASK_TYPE, makeRegistry } from './fixtures.js'
8
+
9
+ const registry = makeRegistry()
10
+
11
+ describe('status flows', () => {
12
+ test('every status maps onto its intrinsic state; the initial one is the marked one', () => {
13
+ expect(intrinsicOf(STORY_FLOW, 'failed')).toBe(IntrinsicStatus.Planned)
14
+ expect(intrinsicOf(STORY_FLOW, 'completed')).toBe(IntrinsicStatus.Closed)
15
+ expect(intrinsicOf(STORY_FLOW, 'unknown')).toBeUndefined()
16
+ expect(initialStatusOf(REVIEW_FLOW)).toBe('pending')
17
+ })
18
+
19
+ test('a repeated name picks the rule whose from matches, and a wildcard answers last', () => {
20
+ expect(ruleOf(STORY_FLOW, 'start', 'failed')?.to).toBe('in-progress')
21
+ expect(ruleOf(STORY_FLOW, 'start', 'completed')).toBeUndefined()
22
+ expect(ruleOf(REVIEW_FLOW, 'reopen', 'approved')?.to).toBe('reviewing')
23
+ expect(ruleOf(REVIEW_FLOW, 'reopen', 'reviewing')?.to).toBe('pending')
24
+ })
25
+
26
+ test('canTransit and transitionsFrom read the same rules, one per name', () => {
27
+ expect(canTransit(STORY_FLOW, 'reset', 'planned')).toBe(true)
28
+ expect(canTransit(STORY_FLOW, 'complete', 'planned')).toBe(false)
29
+ expect(transitionsFrom(STORY_FLOW, 'in-progress').map(rule => rule.name)).toEqual(['complete', 'fail', 'reset'])
30
+ expect(transitionsFrom(STORY_FLOW, 'failed', { explicit: true }).map(rule => `${rule.name}→${rule.to}`))
31
+ .toEqual(['start→in-progress', 'reset→planned'])
32
+ })
33
+ })
34
+
35
+ describe('intrinsic resolution', () => {
36
+ test('primary policy reads the primary flow only', () => {
37
+ expect(resolveIntrinsic(STORY_TYPE, { 'test:story': 'in-progress' }, registry)).toBe(IntrinsicStatus.InProgress)
38
+ expect(resolveIntrinsic(PROJECT_TYPE, { 'test:story': 'completed' }, registry)).toBe(IntrinsicStatus.Closed)
39
+ })
40
+
41
+ test('IntrinsicPolicy.All takes the least advanced flow', () => {
42
+ expect(resolveIntrinsic(TASK_TYPE, { 'test:story': 'completed', 'test:review': 'reviewing' }, registry))
43
+ .toBe(IntrinsicStatus.InProgress)
44
+ expect(resolveIntrinsic(TASK_TYPE, { 'test:story': 'completed', 'test:review': 'approved' }, registry))
45
+ .toBe(IntrinsicStatus.Closed)
46
+ expect(resolveIntrinsic(TASK_TYPE, { 'test:story': 'failed', 'test:review': 'approved' }, registry))
47
+ .toBe(IntrinsicStatus.Planned)
48
+ })
49
+
50
+ test('initialFlowsOf starts every flow at its initial status, the primary at the draft status', () => {
51
+ expect(initialFlowsOf(TASK_TYPE, registry)).toEqual({ 'test:story': 'planned', 'test:review': 'pending' })
52
+ expect(initialFlowsOf(TASK_TYPE, registry, 'failed')).toEqual({ 'test:story': 'failed', 'test:review': 'pending' })
53
+ })
54
+ })
55
+
56
+ describe('schema registry', () => {
57
+ test('answers types and flows, refuses unknown ones, and round-trips a bundle', () => {
58
+ expect(registry.primaryFlow(TASK_TYPE.type).id).toBe(STORY_FLOW.id)
59
+ expect(() => registry.type('nope')).toThrow(UnknownWorkcardType)
60
+ expect(() => registry.flow('nope')).toThrow(UnknownStatusFlow)
61
+
62
+ const copy = makeRegistry()
63
+ copy.load({ version: 1, types: [STORY_TYPE], flows: [STORY_FLOW] })
64
+ expect(copy.types().map(type => type.type)).toEqual([STORY_TYPE.type])
65
+ expect(copy.has(TASK_TYPE.type)).toBe(false)
66
+ })
67
+
68
+ test('compiles and caches a validator for a type\'s fields', () => {
69
+ const validate = registry.validator(STORY_TYPE.type)
70
+
71
+ expect(validate({ area: 'user', primary: true })).toBe(true)
72
+ expect(validate({ area: 'moon', primary: true })).toBe(false)
73
+ expect(registry.validator(STORY_TYPE.type)).toBe(validate)
74
+ })
75
+ })
@@ -0,0 +1,17 @@
1
+ {
2
+ "extends": [
3
+ "@owlmeans/dep-config/tsconfig.base.json",
4
+ "@owlmeans/dep-config/tsconfig.node.json"
5
+ ],
6
+ "compilerOptions": {
7
+ "types": [
8
+ "bun"
9
+ ],
10
+ "rootDir": "../",
11
+ "noEmit": true
12
+ },
13
+ "include": [
14
+ "./**/*",
15
+ "../src/**/*"
16
+ ]
17
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": [
3
+ "@owlmeans/dep-config/tsconfig.base.json"
4
+ ],
5
+ "compilerOptions": {
6
+ "rootDir": "./src/",
7
+ "outDir": "./build/"
8
+ },
9
+ "exclude": [
10
+ "./dist/**/*",
11
+ "./build/**/*",
12
+ "./tests/**/*",
13
+ "./*.ts"
14
+ ]
15
+ }