@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.
- package/README.md +169 -0
- package/agent-meta/manifest.json +16 -0
- package/agent-meta/skills/planning/SKILL.md +248 -0
- package/build/consts.d.ts +126 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +129 -0
- package/build/consts.js.map +1 -0
- package/build/entrypoints.d.ts +14 -0
- package/build/entrypoints.d.ts.map +1 -0
- package/build/entrypoints.js +69 -0
- package/build/entrypoints.js.map +1 -0
- package/build/errors.d.ts +94 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +169 -0
- package/build/errors.js.map +1 -0
- package/build/helpers/apply.d.ts +27 -0
- package/build/helpers/apply.d.ts.map +1 -0
- package/build/helpers/apply.js +162 -0
- package/build/helpers/apply.js.map +1 -0
- package/build/helpers/card.d.ts +17 -0
- package/build/helpers/card.d.ts.map +1 -0
- package/build/helpers/card.js +31 -0
- package/build/helpers/card.js.map +1 -0
- package/build/helpers/changes.d.ts +51 -0
- package/build/helpers/changes.d.ts.map +1 -0
- package/build/helpers/changes.js +277 -0
- package/build/helpers/changes.js.map +1 -0
- package/build/helpers/code.d.ts +24 -0
- package/build/helpers/code.d.ts.map +1 -0
- package/build/helpers/code.js +47 -0
- package/build/helpers/code.js.map +1 -0
- package/build/helpers/index.d.ts +9 -0
- package/build/helpers/index.d.ts.map +1 -0
- package/build/helpers/index.js +9 -0
- package/build/helpers/index.js.map +1 -0
- package/build/helpers/query.d.ts +39 -0
- package/build/helpers/query.d.ts.map +1 -0
- package/build/helpers/query.js +293 -0
- package/build/helpers/query.js.map +1 -0
- package/build/helpers/specification.d.ts +21 -0
- package/build/helpers/specification.d.ts.map +1 -0
- package/build/helpers/specification.js +48 -0
- package/build/helpers/specification.js.map +1 -0
- package/build/helpers/status.d.ts +44 -0
- package/build/helpers/status.d.ts.map +1 -0
- package/build/helpers/status.js +78 -0
- package/build/helpers/status.js.map +1 -0
- package/build/helpers/validate.d.ts +30 -0
- package/build/helpers/validate.d.ts.map +1 -0
- package/build/helpers/validate.js +93 -0
- package/build/helpers/validate.js.map +1 -0
- package/build/i18n/be.json +51 -0
- package/build/i18n/de.json +51 -0
- package/build/i18n/en.json +51 -0
- package/build/i18n/es.json +51 -0
- package/build/i18n/pl.json +51 -0
- package/build/i18n/ru.json +51 -0
- package/build/i18n/uk.json +51 -0
- package/build/i18n.d.ts +2 -0
- package/build/i18n.d.ts.map +1 -0
- package/build/i18n.js +19 -0
- package/build/i18n.js.map +1 -0
- package/build/index.d.ts +10 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +9 -0
- package/build/index.js.map +1 -0
- package/build/models/index.d.ts +7 -0
- package/build/models/index.d.ts.map +1 -0
- package/build/models/index.js +19 -0
- package/build/models/index.js.map +1 -0
- package/build/models/project.d.ts +4 -0
- package/build/models/project.d.ts.map +1 -0
- package/build/models/project.js +17 -0
- package/build/models/project.js.map +1 -0
- package/build/models/specification.d.ts +4 -0
- package/build/models/specification.d.ts.map +1 -0
- package/build/models/specification.js +15 -0
- package/build/models/specification.js.map +1 -0
- package/build/models/workcard.d.ts +14 -0
- package/build/models/workcard.d.ts.map +1 -0
- package/build/models/workcard.js +103 -0
- package/build/models/workcard.js.map +1 -0
- package/build/queue/index.d.ts +25 -0
- package/build/queue/index.d.ts.map +1 -0
- package/build/queue/index.js +14 -0
- package/build/queue/index.js.map +1 -0
- package/build/registry.d.ts +10 -0
- package/build/registry.d.ts.map +1 -0
- package/build/registry.js +75 -0
- package/build/registry.js.map +1 -0
- package/build/schemas.d.ts +56 -0
- package/build/schemas.d.ts.map +1 -0
- package/build/schemas.js +578 -0
- package/build/schemas.js.map +1 -0
- package/build/types.d.ts +785 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/package.json +58 -0
- package/src/consts.ts +136 -0
- package/src/entrypoints.ts +127 -0
- package/src/errors.ts +207 -0
- package/src/helpers/apply.ts +183 -0
- package/src/helpers/card.ts +42 -0
- package/src/helpers/changes.ts +333 -0
- package/src/helpers/code.ts +66 -0
- package/src/helpers/index.ts +8 -0
- package/src/helpers/query.ts +345 -0
- package/src/helpers/specification.ts +65 -0
- package/src/helpers/status.ts +117 -0
- package/src/helpers/validate.ts +107 -0
- package/src/i18n/be.json +51 -0
- package/src/i18n/de.json +51 -0
- package/src/i18n/en.json +51 -0
- package/src/i18n/es.json +51 -0
- package/src/i18n/pl.json +51 -0
- package/src/i18n/ru.json +51 -0
- package/src/i18n/uk.json +51 -0
- package/src/i18n.ts +21 -0
- package/src/index.ts +9 -0
- package/src/models/index.ts +21 -0
- package/src/models/project.ts +24 -0
- package/src/models/specification.ts +21 -0
- package/src/models/workcard.ts +138 -0
- package/src/queue/index.ts +31 -0
- package/src/registry.ts +90 -0
- package/src/schemas.ts +654 -0
- package/src/types.ts +837 -0
- package/tests/apply.spec.ts +176 -0
- package/tests/code.spec.ts +40 -0
- package/tests/entrypoints.spec.ts +57 -0
- package/tests/fixtures.ts +124 -0
- package/tests/i18n.spec.ts +30 -0
- package/tests/models.spec.ts +133 -0
- package/tests/query.spec.ts +90 -0
- package/tests/schemas.spec.ts +74 -0
- package/tests/status.spec.ts +75 -0
- package/tests/tsconfig.json +17 -0
- 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