@agentshouse/mdmodel 0.1.0-alpha.2 → 0.1.0-alpha.3

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 (59) hide show
  1. package/README.md +24 -3
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/types.d.ts +3 -0
  6. package/dist/types.d.ts.map +1 -1
  7. package/dist/types.js.map +1 -1
  8. package/dist/validate.d.ts +2 -2
  9. package/dist/validate.d.ts.map +1 -1
  10. package/dist/validate.js +9 -5
  11. package/dist/validate.js.map +1 -1
  12. package/dist/version.d.ts +1 -1
  13. package/dist/version.js +1 -1
  14. package/dist/version.js.map +1 -1
  15. package/fixtures/README.md +8 -4
  16. package/fixtures/cases/any-without-type/context.json +1 -0
  17. package/fixtures/cases/binary-any-reference/context.json +5 -0
  18. package/fixtures/cases/binary-any-reference/expected.json +7 -0
  19. package/fixtures/cases/binary-any-reference/library/notes/draft.md +6 -0
  20. package/fixtures/cases/binary-any-reference/library/types/note.md +5 -0
  21. package/fixtures/cases/binary-document-collision/context.json +5 -0
  22. package/fixtures/cases/binary-document-collision/expected.json +6 -0
  23. package/fixtures/cases/binary-document-collision/library/notes/draft.md +3 -0
  24. package/fixtures/cases/binary-document-collision/library/types/note.md +3 -0
  25. package/fixtures/cases/binary-link-outside-collection/context.json +6 -0
  26. package/fixtures/cases/binary-link-outside-collection/expected.json +6 -0
  27. package/fixtures/cases/binary-link-outside-collection/library/notes/draft.md +5 -0
  28. package/fixtures/cases/binary-link-outside-collection/library/types/note.md +3 -0
  29. package/fixtures/cases/binary-prototype-reference/context.json +1 -0
  30. package/fixtures/cases/binary-prototype-reference/expected.json +7 -0
  31. package/fixtures/cases/binary-prototype-reference/library/notes/draft.md +6 -0
  32. package/fixtures/cases/binary-prototype-reference/library/types/note.md +5 -0
  33. package/fixtures/cases/binary-typed-reference/context.json +5 -0
  34. package/fixtures/cases/binary-typed-reference/expected.json +7 -0
  35. package/fixtures/cases/binary-typed-reference/library/notes/draft.md +6 -0
  36. package/fixtures/cases/binary-typed-reference/library/types/note.md +5 -0
  37. package/fixtures/cases/binary-typed-reference/library/types/person.md +3 -0
  38. package/fixtures/cases/declared-binary-links/context.json +6 -0
  39. package/fixtures/cases/declared-binary-links/expected.json +1 -0
  40. package/fixtures/cases/declared-binary-links/library/notes/draft.md +7 -0
  41. package/fixtures/cases/declared-binary-links/library/types/note.md +3 -0
  42. package/fixtures/cases/field-enum/context.json +1 -0
  43. package/fixtures/cases/malformed-definition/context.json +1 -0
  44. package/fixtures/cases/missing-binary-image/context.json +6 -0
  45. package/fixtures/cases/missing-binary-image/expected.json +6 -0
  46. package/fixtures/cases/missing-binary-image/library/notes/draft.md +5 -0
  47. package/fixtures/cases/missing-binary-image/library/types/note.md +3 -0
  48. package/fixtures/cases/missing-binary-link/context.json +6 -0
  49. package/fixtures/cases/missing-binary-link/expected.json +6 -0
  50. package/fixtures/cases/missing-binary-link/library/notes/draft.md +5 -0
  51. package/fixtures/cases/missing-binary-link/library/types/note.md +3 -0
  52. package/fixtures/cases/missing-required-field/context.json +1 -0
  53. package/fixtures/cases/prd-definition/context.json +1 -0
  54. package/fixtures/cases/referenced-missing-file/context.json +1 -0
  55. package/fixtures/cases/structured-section/context.json +1 -0
  56. package/fixtures/cases/unclosed-frontmatter/context.json +1 -0
  57. package/fixtures/cases/unresolved-link/context.json +1 -0
  58. package/fixtures/cases/valid-typed-document/context.json +1 -0
  59. package/package.json +1 -1
package/README.md CHANGED
@@ -5,7 +5,8 @@ deterministic application of one structured change to Document content, the
5
5
  Markdown syntax profile, and the conformance fixtures.
6
6
 
7
7
  The package runs in a browser and in Node. It makes no network call and reads
8
- no filesystem on its own. Callers pass Library state as path-to-content.
8
+ no filesystem on its own. Callers pass text Library state as path-to-content and declare retained binary
9
+ link targets separately in a `ValidationContext`.
9
10
  The public root exposes one ESM artifact. Node 24.20.0 and later can load it
10
11
  through either `import` or `require`; browser bundlers consume the same artifact.
11
12
 
@@ -30,6 +31,13 @@ import {
30
31
  } from '@agentshouse/mdmodel';
31
32
 
32
33
  validate({ 'note.md': '# Hello\n' });
34
+ validate(
35
+ {
36
+ 'types/note.md': '---\ntype: definition\n---\n',
37
+ 'note.md': '---\ntype: note\n---\n![Figure](assets/figure.png)\n',
38
+ },
39
+ { binaryTargets: ['assets/figure.png'] },
40
+ );
33
41
  const parsed = parse(content);
34
42
  if (parsed.ok) {
35
43
  const next = apply(parsed.document, {
@@ -42,8 +50,21 @@ if (parsed.ok) {
42
50
  }
43
51
  ```
44
52
 
45
- `validate` takes one Library state and returns acceptance or one refusal
46
- naming the file, the field or section, the violated rule, and `VERSION`.
53
+ `validate(library, context?)` takes the resulting text Library and one
54
+ `ValidationContext` containing `binaryTargets: readonly string[]`. Omitting the
55
+ context declares no binary targets. Library keys and binary targets use the
56
+ same collection-relative path namespace; callers supply canonical paths, and
57
+ a path declared as both text and binary is refused. Ordinary links and images
58
+ in typed Documents may resolve to either membership, including relative
59
+ normalization from the source Document. A binary target supplies no Document
60
+ or MD Type and cannot satisfy a typed reference, including one to `any`.
61
+ The existing `any` Document rules are unchanged.
62
+
63
+ Validation returns acceptance or one refusal naming the file, the field or
64
+ section, the violated rule, and `VERSION`. Every caller operation that validates
65
+ a resulting collection supplies that collection's context to this same
66
+ `validate` function. `parse`, `apply`, and `serialize` operate on a single text
67
+ Document; they do not validate collection membership or consume binary bytes.
47
68
  `apply` sets one typed field, including a list field as a whole, or replaces
48
69
  one declared section body against that value's prior content. A field change
49
70
  does not reformat the rest of the file.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { apply, parse, serialize } from './document.js';
2
2
  export { MARKDOWN_SYNTAX_PROFILE } from './profile.js';
3
- export type { ApplyResult, Change, Document, Library, ParseResult, Refusal, Validation, } from './types.js';
3
+ export type { ApplyResult, Change, Document, Library, ParseResult, Refusal, Validation, ValidationContext, } from './types.js';
4
4
  export { validate } from './validate.js';
5
5
  export { VERSION } from './version.js';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EACV,WAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,WAAW,EACX,OAAO,EACP,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EACV,WAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,WAAW,EACX,OAAO,EACP,UAAU,EACV,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/workspace/packages/mdmodel/src/index.ts"],"sourcesContent":["export { apply, parse, serialize } from './document.js';\nexport { MARKDOWN_SYNTAX_PROFILE } from './profile.js';\nexport type {\n ApplyResult,\n Change,\n Document,\n Library,\n ParseResult,\n Refusal,\n Validation,\n} from './types.js';\nexport { validate } from './validate.js';\nexport { VERSION } from './version.js';\n"],"names":["apply","parse","serialize","MARKDOWN_SYNTAX_PROFILE","validate","VERSION"],"mappings":"AAAA,SAASA,KAAK,EAAEC,KAAK,EAAEC,SAAS,QAAQ,gBAAgB;AACxD,SAASC,uBAAuB,QAAQ,eAAe;AAUvD,SAASC,QAAQ,QAAQ,gBAAgB;AACzC,SAASC,OAAO,QAAQ,eAAe"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/index.ts"],"sourcesContent":["export { apply, parse, serialize } from './document.js';\nexport { MARKDOWN_SYNTAX_PROFILE } from './profile.js';\nexport type {\n ApplyResult,\n Change,\n Document,\n Library,\n ParseResult,\n Refusal,\n Validation,\n ValidationContext,\n} from './types.js';\nexport { validate } from './validate.js';\nexport { VERSION } from './version.js';\n"],"names":["apply","parse","serialize","MARKDOWN_SYNTAX_PROFILE","validate","VERSION"],"mappings":"AAAA,SAASA,KAAK,EAAEC,KAAK,EAAEC,SAAS,QAAQ,gBAAgB;AACxD,SAASC,uBAAuB,QAAQ,eAAe;AAWvD,SAASC,QAAQ,QAAQ,gBAAgB;AACzC,SAASC,OAAO,QAAQ,eAAe"}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  export type Library = Readonly<Record<string, string>>;
2
+ export type ValidationContext = {
3
+ readonly binaryTargets: readonly string[];
4
+ };
2
5
  export type Document = {
3
6
  readonly content: string;
4
7
  readonly type: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEvD,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACrD,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,GAC9B,CAAC;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,GAAG,OAAO,CAAC,CAAC;AAE/C,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,MAAM,GACd;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEN,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAC5D;IAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACrD,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,GAC9B,CAAC;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,GAAG,OAAO,CAAC,CAAC;AAE/C,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,MAAM,GACd;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEN,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAC5D;IAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/workspace/packages/mdmodel/src/types.ts"],"sourcesContent":["export type Library = Readonly<Record<string, string>>;\n\nexport type Document = {\n readonly content: string;\n readonly type: string | undefined;\n readonly fields: Readonly<Record<string, unknown>>;\n readonly sections: Readonly<Record<string, string>>;\n};\n\nexport type Refusal = {\n readonly file: string;\n readonly rule: string;\n readonly version: string;\n readonly location?: string;\n readonly referrers?: readonly string[];\n};\n\nexport type Validation =\n | { readonly verdict: 'accept' }\n | ({ readonly verdict: 'refuse' } & Refusal);\n\nexport type ParseResult =\n | { readonly ok: true; readonly document: Document }\n | { readonly ok: false; readonly refusal: Refusal };\n\nexport type Change =\n | {\n readonly kind: 'field';\n readonly name: string;\n readonly value: unknown;\n readonly prior: unknown;\n }\n | {\n readonly kind: 'section';\n readonly name: string;\n readonly value: string;\n readonly prior: string;\n };\n\nexport type ApplyResult =\n | { readonly outcome: 'applied'; readonly document: Document }\n | { readonly outcome: 'stale'; readonly document: Document }\n | { readonly outcome: 'refuse'; readonly refusal: Refusal };\n"],"names":[],"mappings":"AAuCA,WAG8D"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/types.ts"],"sourcesContent":["export type Library = Readonly<Record<string, string>>;\n\nexport type ValidationContext = {\n readonly binaryTargets: readonly string[];\n};\n\nexport type Document = {\n readonly content: string;\n readonly type: string | undefined;\n readonly fields: Readonly<Record<string, unknown>>;\n readonly sections: Readonly<Record<string, string>>;\n};\n\nexport type Refusal = {\n readonly file: string;\n readonly rule: string;\n readonly version: string;\n readonly location?: string;\n readonly referrers?: readonly string[];\n};\n\nexport type Validation =\n | { readonly verdict: 'accept' }\n | ({ readonly verdict: 'refuse' } & Refusal);\n\nexport type ParseResult =\n | { readonly ok: true; readonly document: Document }\n | { readonly ok: false; readonly refusal: Refusal };\n\nexport type Change =\n | {\n readonly kind: 'field';\n readonly name: string;\n readonly value: unknown;\n readonly prior: unknown;\n }\n | {\n readonly kind: 'section';\n readonly name: string;\n readonly value: string;\n readonly prior: string;\n };\n\nexport type ApplyResult =\n | { readonly outcome: 'applied'; readonly document: Document }\n | { readonly outcome: 'stale'; readonly document: Document }\n | { readonly outcome: 'refuse'; readonly refusal: Refusal };\n"],"names":[],"mappings":"AA2CA,WAG8D"}
@@ -1,3 +1,3 @@
1
- import type { Library, Validation } from './types.js';
2
- export declare function validate(library: Library): Validation;
1
+ import type { Library, Validation, ValidationContext } from './types.js';
2
+ export declare function validate(library: Library, context?: ValidationContext): Validation;
3
3
  //# sourceMappingURL=validate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAY,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAuChE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CA6GrD"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAY,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAuCnF,wBAAgB,QAAQ,CACtB,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,iBAAyC,GACjD,UAAU,CAkHZ"}
package/dist/validate.js CHANGED
@@ -38,10 +38,14 @@ function declaredTypeOf(content) {
38
38
  if (!parsed.ok) return undefined;
39
39
  return parsed.document.type ?? 'any';
40
40
  }
41
- export function validate(library) {
41
+ export function validate(library, context = {
42
+ binaryTargets: []
43
+ }) {
44
+ const binaryTargets = new Set(context.binaryTargets);
42
45
  const files = Object.keys(library).sort();
43
46
  const documents = new Map();
44
47
  for (const file of files){
48
+ if (binaryTargets.has(file)) return refuse(file, 'binary-target-document');
45
49
  const parsed = parse(library[file], file);
46
50
  documents.set(file, parsed);
47
51
  if (!parsed.ok) return {
@@ -114,10 +118,10 @@ export function validate(library) {
114
118
  if (expected === null) continue;
115
119
  for (const target of targets){
116
120
  const resolved = classifyHref(file, target);
117
- if (resolved.kind !== 'path') return refuse(file, 'unresolved-reference', {
121
+ if (resolved.kind !== 'path' || binaryTargets.has(resolved.path)) return refuse(file, 'unresolved-reference', {
118
122
  location: name
119
123
  });
120
- if (!(resolved.path in library)) continue;
124
+ if (!documents.has(resolved.path)) continue;
121
125
  const targetType = declaredTypeOf(library[resolved.path]);
122
126
  if (targetType !== expected) return refuse(file, 'unresolved-reference', {
123
127
  location: name
@@ -128,7 +132,7 @@ export function validate(library) {
128
132
  for (const href of extractRelativeHrefs(body)){
129
133
  const resolved = classifyHref(file, href);
130
134
  if (resolved.kind === 'skip') continue;
131
- if (resolved.kind === 'invalid' || !(resolved.path in library)) {
135
+ if (resolved.kind === 'invalid' || !documents.has(resolved.path) && !binaryTargets.has(resolved.path)) {
132
136
  return refuse(file, 'unresolved-link');
133
137
  }
134
138
  }
@@ -147,7 +151,7 @@ export function validate(library) {
147
151
  for (const target of referenceTargets(spec, parsed.document.fields[name])){
148
152
  const resolved = classifyHref(file, target);
149
153
  if (resolved.kind !== 'path') continue;
150
- if (resolved.path in library) continue;
154
+ if (documents.has(resolved.path)) continue;
151
155
  const referrers = missing.get(resolved.path) ?? [];
152
156
  referrers.push(file);
153
157
  missing.set(resolved.path, referrers);
@@ -1 +1 @@
1
- {"version":3,"sources":["/workspace/packages/mdmodel/src/validate.ts"],"sourcesContent":["import { readDefinition, type Definition, type FieldSpec } from './definition.js';\nimport { parse } from './document.js';\nimport { extractRelativeHrefs } from './links.js';\nimport { classifyHref, isTypesPath } from './paths.js';\nimport { refusal } from './refuse.js';\nimport { parseListSection } from './sections.js';\nimport { validateFields } from './schema.js';\nimport type { Document, Library, Validation } from './types.js';\nimport { bodyAfterFrontmatter } from './yaml.js';\n\nfunction refuse(\n file: string,\n rule: string,\n extra?: { location?: string; referrers?: readonly string[] },\n): Validation {\n return { verdict: 'refuse', ...refusal(file, rule, extra) };\n}\n\nfunction instanceType(document: Document, file: string): 'any' | 'definition' | 'invalid' | string {\n if (isTypesPath(file)) return 'definition';\n const raw = document.fields.type;\n if (raw === undefined || raw === null) return 'any';\n if (typeof raw !== 'string' || raw === '') return 'invalid';\n return raw;\n}\n\nfunction referenceTargets(spec: FieldSpec, value: unknown): string[] {\n if (spec.kind === 'reference' && typeof value === 'string') return [value];\n if (spec.kind === 'list' && spec.of.kind === 'reference' && Array.isArray(value)) {\n return value.filter((item): item is string => typeof item === 'string');\n }\n return [];\n}\n\nfunction expectedReferenceType(spec: FieldSpec): string | null {\n if (spec.kind === 'reference') return spec.to;\n if (spec.kind === 'list' && spec.of.kind === 'reference') return spec.of.to;\n return null;\n}\n\nfunction declaredTypeOf(content: string): string | undefined {\n const parsed = parse(content);\n if (!parsed.ok) return undefined;\n return parsed.document.type ?? 'any';\n}\n\nexport function validate(library: Library): Validation {\n const files = Object.keys(library).sort();\n const documents = new Map<string, ReturnType<typeof parse>>();\n for (const file of files) {\n const parsed = parse(library[file]!, file);\n documents.set(file, parsed);\n if (!parsed.ok) return { verdict: 'refuse', ...parsed.refusal };\n }\n\n const definitions = new Map<string, Definition>();\n for (const file of files) {\n if (!isTypesPath(file)) continue;\n const parsed = documents.get(file);\n if (parsed === undefined || !parsed.ok) continue;\n const definition = readDefinition(file, parsed.document);\n if ('rule' in definition) return { verdict: 'refuse', ...definition };\n const existing = definitions.get(definition.name);\n if (existing !== undefined)\n return refuse(file, 'definition-duplicate', { location: definition.name });\n definitions.set(definition.name, definition);\n }\n\n for (const file of files) {\n const parsed = documents.get(file);\n if (parsed === undefined || !parsed.ok) continue;\n const kind = instanceType(parsed.document, file);\n if (kind === 'any') continue;\n if (kind === 'invalid' || kind === 'definition') {\n if (kind === 'definition' && isTypesPath(file)) continue;\n return refuse(file, 'unknown-type', { location: 'type' });\n }\n const definition = definitions.get(kind);\n if (definition === undefined) return refuse(file, 'unknown-type', { location: 'type' });\n const fieldFailure = validateFields(definition.fields, parsed.document.fields);\n if (fieldFailure !== undefined) {\n return refuse(file, fieldFailure.rule, { location: fieldFailure.field });\n }\n for (const [name, spec] of Object.entries(definition.sections)) {\n const body = parsed.document.sections[name];\n if (body === undefined) {\n if (spec.required) return refuse(file, 'required-section', { location: name });\n continue;\n }\n if (spec.kind === 'list' && !parseListSection(body)) {\n return refuse(file, 'structured-section', { location: name });\n }\n }\n }\n\n for (const file of files) {\n const parsed = documents.get(file);\n if (parsed === undefined || !parsed.ok) continue;\n const kind = instanceType(parsed.document, file);\n if (kind === 'any' || kind === 'definition' || kind === 'invalid') continue;\n const definition = definitions.get(kind);\n if (definition === undefined) continue;\n for (const [name, spec] of Object.entries(definition.fields)) {\n const targets = referenceTargets(spec, parsed.document.fields[name]);\n const expected = expectedReferenceType(spec);\n if (expected === null) continue;\n for (const target of targets) {\n const resolved = classifyHref(file, target);\n if (resolved.kind !== 'path')\n return refuse(file, 'unresolved-reference', { location: name });\n if (!(resolved.path in library)) continue;\n const targetType = declaredTypeOf(library[resolved.path]!);\n if (targetType !== expected)\n return refuse(file, 'unresolved-reference', { location: name });\n }\n }\n const body = bodyAfterFrontmatter(library[file]!);\n for (const href of extractRelativeHrefs(body)) {\n const resolved = classifyHref(file, href);\n if (resolved.kind === 'skip') continue;\n if (resolved.kind === 'invalid' || !(resolved.path in library)) {\n return refuse(file, 'unresolved-link');\n }\n }\n }\n\n const missing = new Map<string, string[]>();\n for (const file of files) {\n const parsed = documents.get(file);\n if (parsed === undefined || !parsed.ok) continue;\n const kind = instanceType(parsed.document, file);\n if (kind === 'any' || kind === 'definition' || kind === 'invalid') continue;\n const definition = definitions.get(kind);\n if (definition === undefined) continue;\n for (const [name, spec] of Object.entries(definition.fields)) {\n const expected = expectedReferenceType(spec);\n if (expected === null) continue;\n for (const target of referenceTargets(spec, parsed.document.fields[name])) {\n const resolved = classifyHref(file, target);\n if (resolved.kind !== 'path') continue;\n if (resolved.path in library) continue;\n const referrers = missing.get(resolved.path) ?? [];\n referrers.push(file);\n missing.set(resolved.path, referrers);\n }\n }\n }\n const missingPaths = [...missing.keys()].sort();\n if (missingPaths.length > 0) {\n const path = missingPaths[0]!;\n const referrers = [...new Set(missing.get(path) ?? [])].sort();\n return refuse(path, 'referenced-missing-file', { referrers });\n }\n\n return { verdict: 'accept' };\n}\n"],"names":["readDefinition","parse","extractRelativeHrefs","classifyHref","isTypesPath","refusal","parseListSection","validateFields","bodyAfterFrontmatter","refuse","file","rule","extra","verdict","instanceType","document","raw","fields","type","undefined","referenceTargets","spec","value","kind","of","Array","isArray","filter","item","expectedReferenceType","to","declaredTypeOf","content","parsed","ok","validate","library","files","Object","keys","sort","documents","Map","set","definitions","get","definition","existing","name","location","fieldFailure","field","entries","sections","body","required","targets","expected","target","resolved","path","targetType","href","missing","referrers","push","missingPaths","length","Set"],"mappings":"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"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/validate.ts"],"sourcesContent":["import { readDefinition, type Definition, type FieldSpec } from './definition.js';\nimport { parse } from './document.js';\nimport { extractRelativeHrefs } from './links.js';\nimport { classifyHref, isTypesPath } from './paths.js';\nimport { refusal } from './refuse.js';\nimport { parseListSection } from './sections.js';\nimport { validateFields } from './schema.js';\nimport type { Document, Library, Validation, ValidationContext } from './types.js';\nimport { bodyAfterFrontmatter } from './yaml.js';\n\nfunction refuse(\n file: string,\n rule: string,\n extra?: { location?: string; referrers?: readonly string[] },\n): Validation {\n return { verdict: 'refuse', ...refusal(file, rule, extra) };\n}\n\nfunction instanceType(document: Document, file: string): 'any' | 'definition' | 'invalid' | string {\n if (isTypesPath(file)) return 'definition';\n const raw = document.fields.type;\n if (raw === undefined || raw === null) return 'any';\n if (typeof raw !== 'string' || raw === '') return 'invalid';\n return raw;\n}\n\nfunction referenceTargets(spec: FieldSpec, value: unknown): string[] {\n if (spec.kind === 'reference' && typeof value === 'string') return [value];\n if (spec.kind === 'list' && spec.of.kind === 'reference' && Array.isArray(value)) {\n return value.filter((item): item is string => typeof item === 'string');\n }\n return [];\n}\n\nfunction expectedReferenceType(spec: FieldSpec): string | null {\n if (spec.kind === 'reference') return spec.to;\n if (spec.kind === 'list' && spec.of.kind === 'reference') return spec.of.to;\n return null;\n}\n\nfunction declaredTypeOf(content: string): string | undefined {\n const parsed = parse(content);\n if (!parsed.ok) return undefined;\n return parsed.document.type ?? 'any';\n}\n\nexport function validate(\n library: Library,\n context: ValidationContext = { binaryTargets: [] },\n): Validation {\n const binaryTargets = new Set(context.binaryTargets);\n const files = Object.keys(library).sort();\n const documents = new Map<string, ReturnType<typeof parse>>();\n for (const file of files) {\n if (binaryTargets.has(file)) return refuse(file, 'binary-target-document');\n const parsed = parse(library[file]!, file);\n documents.set(file, parsed);\n if (!parsed.ok) return { verdict: 'refuse', ...parsed.refusal };\n }\n\n const definitions = new Map<string, Definition>();\n for (const file of files) {\n if (!isTypesPath(file)) continue;\n const parsed = documents.get(file);\n if (parsed === undefined || !parsed.ok) continue;\n const definition = readDefinition(file, parsed.document);\n if ('rule' in definition) return { verdict: 'refuse', ...definition };\n const existing = definitions.get(definition.name);\n if (existing !== undefined)\n return refuse(file, 'definition-duplicate', { location: definition.name });\n definitions.set(definition.name, definition);\n }\n\n for (const file of files) {\n const parsed = documents.get(file);\n if (parsed === undefined || !parsed.ok) continue;\n const kind = instanceType(parsed.document, file);\n if (kind === 'any') continue;\n if (kind === 'invalid' || kind === 'definition') {\n if (kind === 'definition' && isTypesPath(file)) continue;\n return refuse(file, 'unknown-type', { location: 'type' });\n }\n const definition = definitions.get(kind);\n if (definition === undefined) return refuse(file, 'unknown-type', { location: 'type' });\n const fieldFailure = validateFields(definition.fields, parsed.document.fields);\n if (fieldFailure !== undefined) {\n return refuse(file, fieldFailure.rule, { location: fieldFailure.field });\n }\n for (const [name, spec] of Object.entries(definition.sections)) {\n const body = parsed.document.sections[name];\n if (body === undefined) {\n if (spec.required) return refuse(file, 'required-section', { location: name });\n continue;\n }\n if (spec.kind === 'list' && !parseListSection(body)) {\n return refuse(file, 'structured-section', { location: name });\n }\n }\n }\n\n for (const file of files) {\n const parsed = documents.get(file);\n if (parsed === undefined || !parsed.ok) continue;\n const kind = instanceType(parsed.document, file);\n if (kind === 'any' || kind === 'definition' || kind === 'invalid') continue;\n const definition = definitions.get(kind);\n if (definition === undefined) continue;\n for (const [name, spec] of Object.entries(definition.fields)) {\n const targets = referenceTargets(spec, parsed.document.fields[name]);\n const expected = expectedReferenceType(spec);\n if (expected === null) continue;\n for (const target of targets) {\n const resolved = classifyHref(file, target);\n if (resolved.kind !== 'path' || binaryTargets.has(resolved.path))\n return refuse(file, 'unresolved-reference', { location: name });\n if (!documents.has(resolved.path)) continue;\n const targetType = declaredTypeOf(library[resolved.path]!);\n if (targetType !== expected)\n return refuse(file, 'unresolved-reference', { location: name });\n }\n }\n const body = bodyAfterFrontmatter(library[file]!);\n for (const href of extractRelativeHrefs(body)) {\n const resolved = classifyHref(file, href);\n if (resolved.kind === 'skip') continue;\n if (\n resolved.kind === 'invalid' ||\n (!documents.has(resolved.path) && !binaryTargets.has(resolved.path))\n ) {\n return refuse(file, 'unresolved-link');\n }\n }\n }\n\n const missing = new Map<string, string[]>();\n for (const file of files) {\n const parsed = documents.get(file);\n if (parsed === undefined || !parsed.ok) continue;\n const kind = instanceType(parsed.document, file);\n if (kind === 'any' || kind === 'definition' || kind === 'invalid') continue;\n const definition = definitions.get(kind);\n if (definition === undefined) continue;\n for (const [name, spec] of Object.entries(definition.fields)) {\n const expected = expectedReferenceType(spec);\n if (expected === null) continue;\n for (const target of referenceTargets(spec, parsed.document.fields[name])) {\n const resolved = classifyHref(file, target);\n if (resolved.kind !== 'path') continue;\n if (documents.has(resolved.path)) continue;\n const referrers = missing.get(resolved.path) ?? [];\n referrers.push(file);\n missing.set(resolved.path, referrers);\n }\n }\n }\n const missingPaths = [...missing.keys()].sort();\n if (missingPaths.length > 0) {\n const path = missingPaths[0]!;\n const referrers = [...new Set(missing.get(path) ?? [])].sort();\n return refuse(path, 'referenced-missing-file', { referrers });\n }\n\n return { verdict: 'accept' };\n}\n"],"names":["readDefinition","parse","extractRelativeHrefs","classifyHref","isTypesPath","refusal","parseListSection","validateFields","bodyAfterFrontmatter","refuse","file","rule","extra","verdict","instanceType","document","raw","fields","type","undefined","referenceTargets","spec","value","kind","of","Array","isArray","filter","item","expectedReferenceType","to","declaredTypeOf","content","parsed","ok","validate","library","context","binaryTargets","Set","files","Object","keys","sort","documents","Map","has","set","definitions","get","definition","existing","name","location","fieldFailure","field","entries","sections","body","required","targets","expected","target","resolved","path","targetType","href","missing","referrers","push","missingPaths","length"],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.1.0-alpha.2";
1
+ export declare const VERSION = "0.1.0-alpha.3";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
- export const VERSION = '0.1.0-alpha.2';
1
+ export const VERSION = '0.1.0-alpha.3';
2
2
 
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/workspace/packages/mdmodel/src/version.ts"],"sourcesContent":["export const VERSION = '0.1.0-alpha.2';\n"],"names":["VERSION"],"mappings":"AAAA,OAAO,MAAMA,UAAU,gBAAgB"}
1
+ {"version":3,"sources":["/workspace/packages/mdmodel/src/version.ts"],"sourcesContent":["export const VERSION = '0.1.0-alpha.3';\n"],"names":["VERSION"],"mappings":"AAAA,OAAO,MAAMA,UAAU,gBAAgB"}
@@ -1,8 +1,11 @@
1
1
  # Conformance fixtures
2
2
 
3
- Each directory under `cases/` is one Library state and one expected verdict.
4
- Consumers load the files under `library/` as path-to-content and compare
5
- `validate` against `expected.json`.
3
+ Each directory under `cases/` is one text Library, one validation context,
4
+ and one expected verdict. Consumers load the files under `library/` as
5
+ path-to-content, load the required `context.json` as `ValidationContext`, and
6
+ compare `validate(library, context)` against `expected.json`. Text-only cases
7
+ carry `{ "binaryTargets": [] }`. Binary targets are membership declarations;
8
+ the corpus supplies no binary bytes or placeholder Documents.
6
9
 
7
10
  ## Verdict format
8
11
 
@@ -34,6 +37,7 @@ replace it with the package's semantic version before comparing.
34
37
 
35
38
  | Rule | When |
36
39
  | ------------------------- | ---------------------------------------------------------------------- |
40
+ | `binary-target-document` | A path is declared as both a text Document and a binary target |
37
41
  | `frontmatter-fence` | A present frontmatter fence does not parse as the accepted YAML subset |
38
42
  | `definition-type` | A `types/` file is not a definition, or its type name is empty |
39
43
  | `definition-shape` | A definition carries a key other than `type`, `fields`, and `sections` |
@@ -55,7 +59,7 @@ replace it with the package's semantic version before comparing.
55
59
  | `unresolved-reference` | A reference does not resolve to a current file of the required type |
56
60
  | `unresolved-link` | A relative body link does not resolve to a current path |
57
61
  | `structured-section` | A declared structured-list section does not parse as a list |
58
- | `referenced-missing-file` | Remaining files still reference a path that is not in the Library |
62
+ | `referenced-missing-file` | Remaining files still reference an absent path |
59
63
 
60
64
  ## YAML subset
61
65
 
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1,5 @@
1
+ {
2
+ "binaryTargets": [
3
+ "assets/person.bin"
4
+ ]
5
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "verdict": "refuse",
3
+ "file": "notes/draft.md",
4
+ "rule": "unresolved-reference",
5
+ "version": "<package-version>",
6
+ "location": "subject"
7
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: note
3
+ subject: ../assets/person.bin
4
+ ---
5
+
6
+ [Original](../assets/person.bin)
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: definition
3
+ fields:
4
+ subject: { kind: reference, to: any, required: true }
5
+ ---
@@ -0,0 +1,5 @@
1
+ {
2
+ "binaryTargets": [
3
+ "notes/draft.md"
4
+ ]
5
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "verdict": "refuse",
3
+ "file": "notes/draft.md",
4
+ "rule": "binary-target-document",
5
+ "version": "<package-version>"
6
+ }
@@ -0,0 +1,3 @@
1
+ ---
2
+ type: definition
3
+ ---
@@ -0,0 +1,6 @@
1
+ {
2
+ "binaryTargets": [
3
+ "assets/existing.pdf",
4
+ "../outside.png"
5
+ ]
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "verdict": "refuse",
3
+ "file": "notes/draft.md",
4
+ "rule": "unresolved-link",
5
+ "version": "<package-version>"
6
+ }
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: note
3
+ ---
4
+
5
+ ![Outside](../../outside.png)
@@ -0,0 +1,3 @@
1
+ ---
2
+ type: definition
3
+ ---
@@ -0,0 +1 @@
1
+ { "binaryTargets": ["constructor"] }
@@ -0,0 +1,7 @@
1
+ {
2
+ "verdict": "refuse",
3
+ "file": "notes/draft.md",
4
+ "rule": "unresolved-reference",
5
+ "version": "<package-version>",
6
+ "location": "subject"
7
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: note
3
+ subject: ../constructor
4
+ ---
5
+
6
+ [Original](../constructor)
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: definition
3
+ fields:
4
+ subject: { kind: reference, to: any, required: true }
5
+ ---
@@ -0,0 +1,5 @@
1
+ {
2
+ "binaryTargets": [
3
+ "assets/person.bin"
4
+ ]
5
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "verdict": "refuse",
3
+ "file": "notes/draft.md",
4
+ "rule": "unresolved-reference",
5
+ "version": "<package-version>",
6
+ "location": "subject"
7
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: note
3
+ subject: ../assets/person.bin
4
+ ---
5
+
6
+ [Original](../assets/person.bin)
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: definition
3
+ fields:
4
+ subject: { kind: reference, to: person, required: true }
5
+ ---
@@ -0,0 +1,3 @@
1
+ ---
2
+ type: definition
3
+ ---
@@ -0,0 +1,6 @@
1
+ {
2
+ "binaryTargets": [
3
+ "assets/manual.pdf",
4
+ "assets/figure.png"
5
+ ]
6
+ }
@@ -0,0 +1 @@
1
+ { "verdict": "accept" }
@@ -0,0 +1,7 @@
1
+ ---
2
+ type: note
3
+ ---
4
+
5
+ [Manual](../assets/manual.pdf)
6
+
7
+ ![Figure](../assets/./charts/../figure.png?download=1#detail)
@@ -0,0 +1,3 @@
1
+ ---
2
+ type: definition
3
+ ---
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1,6 @@
1
+ {
2
+ "binaryTargets": [
3
+ "assets/existing.pdf",
4
+ "../outside.png"
5
+ ]
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "verdict": "refuse",
3
+ "file": "notes/draft.md",
4
+ "rule": "unresolved-link",
5
+ "version": "<package-version>"
6
+ }
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: note
3
+ ---
4
+
5
+ ![Missing](../assets/missing.png)
@@ -0,0 +1,3 @@
1
+ ---
2
+ type: definition
3
+ ---
@@ -0,0 +1,6 @@
1
+ {
2
+ "binaryTargets": [
3
+ "assets/existing.pdf",
4
+ "../outside.png"
5
+ ]
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "verdict": "refuse",
3
+ "file": "notes/draft.md",
4
+ "rule": "unresolved-link",
5
+ "version": "<package-version>"
6
+ }
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: note
3
+ ---
4
+
5
+ [Missing](../assets/missing.pdf)
@@ -0,0 +1,3 @@
1
+ ---
2
+ type: definition
3
+ ---
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentshouse/mdmodel",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0-alpha.3",
4
4
  "description": "MD Type language, parser, validator, structured change application, Markdown syntax profile, and conformance fixtures",
5
5
  "license": "MIT",
6
6
  "type": "module",