@relational-fabric/canon 1.0.0 → 1.2.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 CHANGED
@@ -183,7 +183,7 @@ Universal APIs provide functions that work across all registered canons. They ar
183
183
  - Automatic adaptation to different data shapes
184
184
  - Full type safety maintained across all shapes
185
185
 
186
- See [reference/api.md](reference/api.md) for available APIs.
186
+ See [docs/reference/api.md](docs/reference/api.md) for available APIs.
187
187
 
188
188
  ## Package Exports
189
189
 
@@ -306,11 +306,11 @@ Canon provides comprehensive documentation to help you understand and use the sy
306
306
 
307
307
  ### Reference Documentation
308
308
 
309
- - **[API Reference](reference/api.md)** - Complete API documentation
310
- - **[Core Axioms](reference/axioms.md)** - Detailed axiom specifications
311
- - **[Canons Reference](reference/canons.md)** - Canon implementation guide
309
+ - **[API Reference](docs/reference/api.md)** - Complete API documentation
310
+ - **[Core Axioms](docs/reference/axioms.md)** - Detailed axiom specifications
311
+ - **[Canons Reference](docs/reference/canons.md)** - Canon implementation guide
312
312
  - **[Type Testing Utilities](docs/type-testing/)** - Compile-time type assertions and invariants
313
- - **[Third-Party Integrations](reference/third-party.md)** - External library integrations
313
+ - **[Canon Kit & Third-Party Utilities](docs/reference/kit.md)** - Curated third-party surface and transparent access paths
314
314
 
315
315
  ### Examples
316
316
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@relational-fabric/canon",
3
3
  "type": "module",
4
- "version": "1.0.0",
4
+ "version": "1.2.0",
5
5
  "description": "A modern TypeScript package template with ESLint and TypeScript configurations for starting new projects",
6
6
  "author": "Relational Fabric",
7
7
  "license": "MIT",
@@ -24,6 +24,14 @@
24
24
  "types": "./src/index.ts",
25
25
  "import": "./src/index.ts"
26
26
  },
27
+ "./_/*": {
28
+ "types": "./src/_/*.ts",
29
+ "import": "./src/_/*.ts"
30
+ },
31
+ "./index": {
32
+ "types": "./src/index.ts",
33
+ "import": "./src/index.ts"
34
+ },
27
35
  "./radar": {
28
36
  "types": "./src/radar/index.ts",
29
37
  "import": "./src/radar/index.ts"
@@ -85,33 +93,37 @@
85
93
  }
86
94
  },
87
95
  "dependencies": {
88
- "@antfu/eslint-config": "^3.0.0",
89
- "@tsconfig/node-lts": "^20.0.0",
90
- "yaml": "^2.4.1"
96
+ "@antfu/eslint-config": "^6.2.0",
97
+ "@tsconfig/node-lts": "^22.0.2",
98
+ "immutable": "^5.1.4",
99
+ "object-hash": "^3.0.0",
100
+ "reflect-metadata": "^0.2.2",
101
+ "yaml": "^2.8.1"
91
102
  },
92
103
  "optionalDependencies": {
93
104
  "defu": "^6.1.4"
94
105
  },
95
106
  "devDependencies": {
96
107
  "@types/mdast": "^4.0.4",
97
- "@types/node": "^24.7.1",
98
- "@vitest/coverage-v8": "^3.2.4",
108
+ "@types/node": "^24.10.0",
109
+ "@vitest/coverage-v8": "^4.0.8",
99
110
  "adr-tools": "^2.0.4",
100
111
  "chokidar-cli": "^3.0.0",
101
112
  "dedent": "^1.7.0",
102
113
  "depcheck": "^1.4.7",
103
- "eslint": "^9.10.0",
114
+ "eslint": "^9.39.1",
104
115
  "husky": "^9.1.7",
105
116
  "lint-staged": "^16.2.6",
117
+ "npm-check-updates": "^19.1.2",
106
118
  "npm-run-all": "^4.1.5",
107
119
  "remark": "^15.0.1",
108
120
  "remark-parse": "^11.0.0",
109
121
  "remark-stringify": "^11.0.0",
110
- "tsx": "^4.7.0",
111
- "typescript": "^5.4.0",
122
+ "tsx": "^4.20.6",
123
+ "typescript": "^5.9.3",
112
124
  "unified": "^11.0.5",
113
- "vitepress": "^1.0.0",
114
- "vitest": "^3.2.4"
125
+ "vitepress": "^1.6.4",
126
+ "vitest": "^4.0.8"
115
127
  },
116
128
  "husky": {
117
129
  "hooks": {
package/src/_/antfu.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { default } from '@antfu/eslint-config'
2
+ export * from '@antfu/eslint-config'
package/src/_/defu.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { default } from 'defu'
2
+ export * from 'defu'
@@ -0,0 +1,4 @@
1
+ import ImmutableNamespace from 'immutable'
2
+
3
+ export default ImmutableNamespace
4
+ export const Immutable = ImmutableNamespace
@@ -0,0 +1,2 @@
1
+ export { default } from 'object-hash'
2
+ export * from 'object-hash'
package/src/_/yaml.ts ADDED
@@ -0,0 +1 @@
1
+ export * from 'yaml'
package/src/index.ts CHANGED
@@ -9,22 +9,25 @@ export * from './axioms/version.js'
9
9
 
10
10
  export * from './canon.js'
11
11
 
12
+ // Opinionated kit exports
13
+ export * from './kit.js'
14
+
15
+ // Metadata helpers
16
+ export * from './meta.js'
12
17
  // Radar
13
18
  export * from './radar/index.js'
14
19
 
15
20
  // Registry and Shell
16
21
  export * from './registry.js'
22
+
17
23
  export * from './shell.js'
18
24
 
19
25
  // Type testing utilities (compile-time helpers only)
20
26
  export * from './testing.js'
21
-
22
27
  // Type system (includes defineAxiom, defineCanon)
23
28
  export * from './types/index.js'
24
29
 
25
30
  // Utilities
26
31
  export * from './utils/guards.js'
27
- export * from './utils/objects.js'
28
32
 
29
- // Re-export utility libraries used internally
30
- export { defu } from 'defu'
33
+ export * from './utils/objects.js'
package/src/kit.ts ADDED
@@ -0,0 +1,21 @@
1
+ import ImmutableNamespace from 'immutable'
2
+ import * as eslintModule from '../eslint.js'
3
+
4
+ type CreateEslintConfig = (
5
+ options?: Record<string, unknown>,
6
+ ...configs: Record<string, unknown>[]
7
+ ) => Record<string, unknown>
8
+
9
+ const moduleDefault = (eslintModule as { default?: CreateEslintConfig }).default
10
+ const createEslintConfigExport = (
11
+ moduleDefault ?? (eslintModule as unknown as CreateEslintConfig)
12
+ ) as CreateEslintConfig
13
+
14
+ // Third-party dependencies blessed for consumer use
15
+ export { defu } from 'defu'
16
+ export const Immutable = ImmutableNamespace
17
+ export { default as objectHash } from 'object-hash'
18
+ export * from 'object-hash'
19
+ export { parse as parseYaml } from 'yaml'
20
+
21
+ export const createEslintConfig = createEslintConfigExport
package/src/meta.ts ADDED
@@ -0,0 +1,32 @@
1
+ import type { Metadata } from './types/metadata.js'
2
+
3
+ import 'reflect-metadata'
4
+
5
+ const METADATA_KEY = Symbol.for('@relational-fabric/canon:meta')
6
+
7
+ export function metaOf<T extends object, M extends Metadata = Metadata>(obj: T): M {
8
+ const existing = Reflect.getMetadata(METADATA_KEY, obj) as M | undefined
9
+ return (existing ?? ({} as M))
10
+ }
11
+
12
+ export function withMeta<T extends object, M extends Metadata = Metadata>(obj: T, metadata: M): T {
13
+ Reflect.defineMetadata(METADATA_KEY, metadata, obj)
14
+ return obj
15
+ }
16
+
17
+ export function updateMeta<T extends object, M extends Metadata = Metadata>(
18
+ obj: T,
19
+ updater: (metadata?: M) => M | undefined,
20
+ ): T {
21
+ const current = Reflect.getMetadata(METADATA_KEY, obj) as M | undefined
22
+ const next = updater(current)
23
+
24
+ if (next === undefined) {
25
+ if (Reflect.hasMetadata(METADATA_KEY, obj))
26
+ Reflect.deleteMetadata(METADATA_KEY, obj)
27
+ } else {
28
+ Reflect.defineMetadata(METADATA_KEY, next, obj)
29
+ }
30
+
31
+ return obj
32
+ }
@@ -4,9 +4,12 @@
4
4
 
5
5
  import type { CsvRow, QuadrantKey, RadarData, RingKey } from '../types/radar.js'
6
6
 
7
+ import { Console } from 'node:console'
7
8
  import { readFileSync, writeFileSync } from 'node:fs'
8
9
  import process from 'node:process'
9
- import { parse } from 'yaml'
10
+ import { parseYaml } from '../kit.js'
11
+
12
+ const logger = new Console(process.stdout, process.stderr)
10
13
 
11
14
  // Quadrant mapping for CSV output
12
15
  const QUADRANT_MAP: Record<QuadrantKey, string> = {
@@ -28,7 +31,7 @@ const RING_MAP: Record<RingKey, string> = {
28
31
  * Convert YAML radar data to CSV format
29
32
  */
30
33
  export function convertYamlToCsv(yamlContent: string): string {
31
- const data = parse(yamlContent) as RadarData
34
+ const data = parseYaml(yamlContent) as RadarData
32
35
 
33
36
  // Generate CSV content
34
37
  const csvRows: string[] = ['name,ring,quadrant,isNew,description']
@@ -72,7 +75,7 @@ export function convertYamlFileToCsv(yamlPath: string, csvPath: string): void {
72
75
  writeFileSync(csvPath, csvContent)
73
76
  // Success: Converted YAML to CSV
74
77
  } catch (error) {
75
- console.error(
78
+ logger.error(
76
79
  '❌ Error converting YAML to CSV:',
77
80
  error instanceof Error ? error.message : 'Unknown error',
78
81
  )
@@ -112,7 +115,7 @@ function escapeCsvField(field: string): string {
112
115
  * Parse YAML radar data
113
116
  */
114
117
  export function parseRadarYaml(yamlContent: string): RadarData {
115
- return parse(yamlContent) as RadarData
118
+ return parseYaml(yamlContent) as RadarData
116
119
  }
117
120
 
118
121
  /**
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import type { QuadrantKey, RadarData, RingKey } from '../types/radar.js'
6
+ import { parseYaml } from '../kit.js'
6
7
 
7
8
  const VALID_QUADRANTS: QuadrantKey[] = [
8
9
  'tools-libraries',
@@ -288,10 +289,9 @@ function validateRadarEntry(
288
289
  export async function validateRadarFile(filePath: string): Promise<ValidationResult> {
289
290
  try {
290
291
  const fs = await import('node:fs')
291
- const yaml = await import('yaml')
292
292
 
293
293
  const yamlContent = fs.readFileSync(filePath, 'utf8')
294
- const data = yaml.parse(yamlContent) as RadarData
294
+ const data = parseYaml(yamlContent) as RadarData
295
295
 
296
296
  return validateRadarData(data)
297
297
  } catch (error) {
package/src/registry.ts CHANGED
@@ -38,8 +38,8 @@ export class Registry {
38
38
  /**
39
39
  * Make registry iterable - iterates over canon configs
40
40
  */
41
- *[Symbol.iterator](): Iterator<CanonConfig> {
42
- yield * this.canons.values()
41
+ * [Symbol.iterator](): Iterator<CanonConfig> {
42
+ yield* this.canons.values()
43
43
  }
44
44
 
45
45
  /**
package/src/shell.ts CHANGED
@@ -4,8 +4,9 @@
4
4
  * Singleton registry instance with convenience API.
5
5
  */
6
6
 
7
+ import type { Registry } from './registry.js'
7
8
  import type { CanonConfig, Canons } from './types/index.js'
8
- import { createRegistry, type Registry } from './registry.js'
9
+ import { createRegistry } from './registry.js'
9
10
  import { defineCanon } from './types/index.js'
10
11
 
11
12
  /**
@@ -5,8 +5,9 @@
5
5
  * (like ID, type, version) that can be found in different data structures.
6
6
  */
7
7
 
8
+ import type { Expect } from '../testing.js'
8
9
  import type { TypeGuard } from './guards.js'
9
- import { type Expect, invariant } from '../testing.js'
10
+ import { invariant } from '../testing.js'
10
11
 
11
12
  /**
12
13
  * Base axiom type that merges configuration with metadata
@@ -6,8 +6,9 @@
6
6
  * different formats.
7
7
  */
8
8
 
9
+ import type { Expect } from '../testing.js'
9
10
  import type { AxiomConfig, Axioms } from './axioms.js'
10
- import { type Expect, invariant } from '../testing.js'
11
+ import { invariant } from '../testing.js'
11
12
 
12
13
  /**
13
14
  * Canon type that maps axiom labels to their type-level configurations
@@ -2,7 +2,8 @@
2
2
  * Type guard utilities for Canon type system
3
3
  */
4
4
 
5
- import { type Expect, invariant } from '../testing.js'
5
+ import type { Expect } from '../testing.js'
6
+ import { invariant } from '../testing.js'
6
7
 
7
8
  /**
8
9
  * Type guard pattern that preserves specific types when narrowing
@@ -6,5 +6,6 @@ export * from './axioms.js'
6
6
  export * from './canons.js'
7
7
  export * from './guards.js'
8
8
  export * from './js.js'
9
+ export * from './metadata.js'
9
10
  export * from './objects.js'
10
11
  export * from './radar.js'
package/src/types/js.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type Expect, invariant } from '../testing.js'
1
+ import type { Expect } from '../testing.js'
2
+ import { invariant } from '../testing.js'
2
3
 
3
4
  export interface JsType {
4
5
  string: string
@@ -0,0 +1 @@
1
+ export type Metadata = Record<PropertyKey, unknown>
@@ -2,7 +2,8 @@
2
2
  * Object type definitions for Canon
3
3
  */
4
4
 
5
- import { type Expect, invariant } from '../testing.js'
5
+ import type { Expect } from '../testing.js'
6
+ import { invariant } from '../testing.js'
6
7
 
7
8
  /**
8
9
  * Plain old JavaScript object type
@@ -2,7 +2,8 @@
2
2
  * Technology Radar data structures and types
3
3
  */
4
4
 
5
- import { type Expect, invariant } from '../testing.js'
5
+ import type { Expect } from '../testing.js'
6
+ import { invariant } from '../testing.js'
6
7
 
7
8
  export interface RadarEntry {
8
9
  /** The name of the technology or practice */
@@ -83,11 +84,11 @@ export interface CsvRow {
83
84
  description: string
84
85
  }
85
86
 
86
- export type QuadrantKey =
87
- | 'tools-libraries'
88
- | 'techniques-patterns'
89
- | 'features-capabilities'
90
- | 'data-structures-formats'
87
+ export type QuadrantKey
88
+ = | 'tools-libraries'
89
+ | 'techniques-patterns'
90
+ | 'features-capabilities'
91
+ | 'data-structures-formats'
91
92
  export type RingKey = 'adopt' | 'trial' | 'assess' | 'hold'
92
93
 
93
94
  // ---------------------------------------------------------------------------