structured-context 0.16.0 → 0.17.1

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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +15 -15
  3. package/dist/api.d.ts +4 -2
  4. package/dist/api.js +2 -0
  5. package/dist/browser.d.ts +14 -0
  6. package/dist/browser.js +13 -0
  7. package/dist/commands/dump.d.ts +2 -0
  8. package/dist/commands/dump.js +3 -1
  9. package/dist/commands/schemas.js +3 -2
  10. package/dist/commands/spaces.js +4 -6
  11. package/dist/commands/validate.js +20 -19
  12. package/dist/config/health.d.ts +10 -6
  13. package/dist/config/health.js +11 -44
  14. package/dist/config/index.d.ts +4 -0
  15. package/dist/config/index.js +15 -6
  16. package/dist/filter/augment-nodes.d.ts +2 -2
  17. package/dist/filter/augment-nodes.js +2 -2
  18. package/dist/filter/expand-include.js +2 -2
  19. package/dist/index.js +9 -1
  20. package/dist/judge/context.js +1 -1
  21. package/dist/load-space-graph.js +1 -13
  22. package/dist/plugins/markdown/parse-embedded.js +4 -4
  23. package/dist/plugins/miro/cache.js +1 -1
  24. package/dist/plugins/miro/sync.js +2 -2
  25. package/dist/read/resolve-graph-edges.js +3 -3
  26. package/dist/schema/list-schemas.js +2 -2
  27. package/dist/schema/metadata-contract.d.ts +34 -0
  28. package/dist/schema/metadata-contract.js +2 -2
  29. package/dist/schema/validate-graph.d.ts +2 -2
  30. package/dist/schema/validate-graph.js +4 -4
  31. package/dist/schemas/generated/space-graph/1.json +216 -0
  32. package/dist/schemas/generated/space-graph/latest.json +216 -0
  33. package/dist/space-context.js +11 -1
  34. package/dist/space-graph-data.d.ts +237 -0
  35. package/dist/space-graph-data.js +129 -0
  36. package/dist/space-graph.d.ts +12 -1
  37. package/dist/space-graph.js +25 -1
  38. package/dist/testing.js +3 -3
  39. package/dist/types.d.ts +2 -2
  40. package/docs/concepts.md +8 -4
  41. package/package.json +6 -2
  42. package/schemas/generated/space-graph/1.json +216 -0
  43. package/schemas/generated/space-graph/latest.json +216 -0
@@ -21,13 +21,13 @@ function getRefs(rawField, multiple) {
21
21
  * @param nodesByType Map of node type to nodes
22
22
  * @param targetIndex Map of link targets to nodes
23
23
  * @param edge The edge definition (child type, parent type, field, fieldOn, multiple)
24
- * @param source Whether this edge comes from hierarchy levels or relationships
24
+ * @param kind Whether this edge comes from hierarchy levels or relationships
25
25
  * @param hierarchy Name of the hierarchy the edge belongs to (hierarchy edges only)
26
26
  * @param selfRef Whether child and parent are the same node type
27
27
  * @param unresolvedRefs Array to push broken/invalid link entries into
28
28
  * @param typeAliases Optional type aliases for resolution
29
29
  */
30
- function resolveEdge(nodesByType, targetIndex, edge, source, hierarchy, selfRef, unresolvedRefs, typeAliases) {
30
+ function resolveEdge(nodesByType, targetIndex, edge, kind, hierarchy, selfRef, unresolvedRefs, typeAliases) {
31
31
  const { type: rawChildType, parent: rawParentType, field, fieldOn, multiple } = edge;
32
32
  const childType = resolveNodeType(rawChildType, typeAliases);
33
33
  const parentType = resolveNodeType(rawParentType, typeAliases);
@@ -38,7 +38,7 @@ function resolveEdge(nodesByType, targetIndex, edge, source, hierarchy, selfRef,
38
38
  id: parentId,
39
39
  field,
40
40
  fieldOn,
41
- source,
41
+ kind,
42
42
  ...(hierarchy !== undefined ? { hierarchy } : {}),
43
43
  selfRef,
44
44
  });
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readdirSync } from 'node:fs';
2
2
  import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
3
- import { loadConfig, resolveConfigPath, resolveSchemaPath } from '../config';
3
+ import { loadConfigOrEmpty, resolveConfigPath, resolveSchemaPath } from '../config';
4
4
  import { PLUGIN_PREFIX } from '../constants';
5
5
  import { CONFIG_PLUGINS_DIR, resolvePluginSchemasDir, resolveSpacePluginSchemas, shortenPluginName, } from '../plugins/util';
6
6
  import { bundledSchemasDir, readRawSchema } from './schema';
@@ -32,7 +32,7 @@ function jsonFiles(dir) {
32
32
  * schema file the config points at. Each schema appears once, under the place its file lives.
33
33
  */
34
34
  export async function listSchemas(options = {}) {
35
- const config = options.config ?? loadConfig({ startDir: options.startDir });
35
+ const config = options.config ?? loadConfigOrEmpty({ startDir: options.startDir });
36
36
  const listings = [];
37
37
  const byPath = new Map();
38
38
  const add = (listing, location) => {
@@ -7,6 +7,40 @@ export declare const SCHEMA_META_FILE = "_structured_context_schema_meta.json";
7
7
  export declare function schemaMetaId(folder: string): string;
8
8
  /** The URL schemas set as `$schema` to follow the newest dialect. Resolves to the newest versioned meta-schema. */
9
9
  export declare const SCHEMA_META_ID: string;
10
+ /** Graph edge routing fields shared by hierarchy levels and relationships. */
11
+ export declare const EDGE_PROPS: {
12
+ readonly field: {
13
+ readonly type: "string";
14
+ readonly minLength: 1;
15
+ };
16
+ readonly fieldOn: {
17
+ readonly enum: readonly ["child", "parent"];
18
+ };
19
+ readonly multiple: {
20
+ readonly type: "boolean";
21
+ };
22
+ };
23
+ /** Embedding/template hint fields shared by hierarchy levels and relationships. */
24
+ export declare const EMBEDDING_PROPS: {
25
+ readonly templateFormat: {
26
+ readonly enum: readonly ["heading", "list", "table", "page"];
27
+ };
28
+ readonly matchers: {
29
+ readonly type: "array";
30
+ readonly items: {
31
+ readonly type: "string";
32
+ readonly minLength: 1;
33
+ };
34
+ readonly minItems: 1;
35
+ };
36
+ readonly embeddedTemplateFields: {
37
+ readonly type: "array";
38
+ readonly items: {
39
+ readonly type: "string";
40
+ readonly minLength: 1;
41
+ };
42
+ };
43
+ };
10
44
  /** Schema objects wrapping each prop group for type derivation via FromSchema. */
11
45
  declare const EDGE_SCHEMA: {
12
46
  readonly type: "object";
@@ -9,13 +9,13 @@ export function schemaMetaId(folder) {
9
9
  /** The URL schemas set as `$schema` to follow the newest dialect. Resolves to the newest versioned meta-schema. */
10
10
  export const SCHEMA_META_ID = schemaMetaId('latest');
11
11
  /** Graph edge routing fields shared by hierarchy levels and relationships. */
12
- const EDGE_PROPS = {
12
+ export const EDGE_PROPS = {
13
13
  field: { type: 'string', minLength: 1 },
14
14
  fieldOn: { enum: ['child', 'parent'] },
15
15
  multiple: { type: 'boolean' },
16
16
  };
17
17
  /** Embedding/template hint fields shared by hierarchy levels and relationships. */
18
- const EMBEDDING_PROPS = {
18
+ export const EMBEDDING_PROPS = {
19
19
  templateFormat: { enum: ['heading', 'list', 'table', 'page'] },
20
20
  matchers: { type: 'array', items: { type: 'string', minLength: 1 }, minItems: 1 },
21
21
  embeddedTemplateFields: { type: 'array', items: { type: 'string', minLength: 1 } },
@@ -16,8 +16,8 @@ export declare function validateGraph(nodes: SpaceNode[], metadata: SchemaMetada
16
16
  /**
17
17
  * Validate that resolved parents follow hierarchy level rules and relationship type constraints.
18
18
  *
19
- * For hierarchy edges (fieldOn:'child', source:'hierarchy'): validates level ordering within the edge's hierarchy.
20
- * For relationship edges (fieldOn:'child', source:'relationship'): validates parent type.
19
+ * For hierarchy edges (fieldOn:'child', kind:'hierarchy'): validates level ordering within the edge's hierarchy.
20
+ * For relationship edges (fieldOn:'child', kind:'relationship'): validates parent type.
21
21
  * For fieldOn:'parent' edges: validates child type, violation attributed to the field-owner node.
22
22
  *
23
23
  * Assumes resolveGraphEdges has already been called to populate node.resolvedParents.
@@ -16,8 +16,8 @@ export function validateGraph(nodes, metadata, unresolvedRefs = []) {
16
16
  /**
17
17
  * Validate that resolved parents follow hierarchy level rules and relationship type constraints.
18
18
  *
19
- * For hierarchy edges (fieldOn:'child', source:'hierarchy'): validates level ordering within the edge's hierarchy.
20
- * For relationship edges (fieldOn:'child', source:'relationship'): validates parent type.
19
+ * For hierarchy edges (fieldOn:'child', kind:'hierarchy'): validates level ordering within the edge's hierarchy.
20
+ * For relationship edges (fieldOn:'child', kind:'relationship'): validates parent type.
21
21
  * For fieldOn:'parent' edges: validates child type, violation attributed to the field-owner node.
22
22
  *
23
23
  * Assumes resolveGraphEdges has already been called to populate node.resolvedParents.
@@ -96,7 +96,7 @@ export function validateHierarchyStructure(nodes, metadata) {
96
96
  });
97
97
  }
98
98
  }
99
- else if (parentRef.source === 'relationship') {
99
+ else if (parentRef.kind === 'relationship') {
100
100
  // Relationship edge (fieldOn:'child'): validate parent type matches relationship definition.
101
101
  const allowedTypes = typeRules.get(nodeType)?.get(parentRef.field);
102
102
  if (allowedTypes && !allowedTypes.has(parentType)) {
@@ -112,7 +112,7 @@ export function validateHierarchyStructure(nodes, metadata) {
112
112
  }
113
113
  }
114
114
  else {
115
- // Hierarchy edge (fieldOn:'child', source:'hierarchy'): validate level ordering within its hierarchy.
115
+ // Hierarchy edge (fieldOn:'child', kind:'hierarchy'): validate level ordering within its hierarchy.
116
116
  const edgeHierarchy = parentRef.hierarchy !== undefined ? hierarchiesByName.get(parentRef.hierarchy) : undefined;
117
117
  if (!edgeHierarchy)
118
118
  continue;
@@ -0,0 +1,216 @@
1
+ {
2
+ "$id": "https://raw.githubusercontent.com/mindsocket/structured-context/main/schemas/generated/space-graph/1.json",
3
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
4
+ "title": "structured-context space graph",
5
+ "type": "object",
6
+ "properties": {
7
+ "formatVersion": {
8
+ "const": 1
9
+ },
10
+ "hierarchies": {
11
+ "type": "array",
12
+ "items": {
13
+ "type": "object",
14
+ "properties": {
15
+ "name": {
16
+ "type": "string"
17
+ },
18
+ "levels": {
19
+ "type": "array",
20
+ "items": {
21
+ "type": "object",
22
+ "properties": {
23
+ "type": {
24
+ "type": "string"
25
+ },
26
+ "field": {
27
+ "type": "string",
28
+ "minLength": 1
29
+ },
30
+ "fieldOn": {
31
+ "enum": ["child", "parent"]
32
+ },
33
+ "multiple": {
34
+ "type": "boolean"
35
+ },
36
+ "templateFormat": {
37
+ "enum": ["heading", "list", "table", "page"]
38
+ },
39
+ "matchers": {
40
+ "type": "array",
41
+ "items": {
42
+ "type": "string",
43
+ "minLength": 1
44
+ },
45
+ "minItems": 1
46
+ },
47
+ "embeddedTemplateFields": {
48
+ "type": "array",
49
+ "items": {
50
+ "type": "string",
51
+ "minLength": 1
52
+ }
53
+ },
54
+ "selfRef": {
55
+ "type": "boolean"
56
+ },
57
+ "selfRefField": {
58
+ "type": "string"
59
+ }
60
+ },
61
+ "required": ["type", "field", "fieldOn", "multiple", "selfRef"],
62
+ "additionalProperties": false
63
+ }
64
+ },
65
+ "allowSkipLevels": {
66
+ "type": "boolean"
67
+ }
68
+ },
69
+ "required": ["name", "levels"],
70
+ "additionalProperties": false
71
+ }
72
+ },
73
+ "mainHierarchy": {
74
+ "type": "string"
75
+ },
76
+ "nodes": {
77
+ "type": "array",
78
+ "items": {
79
+ "type": "object",
80
+ "properties": {
81
+ "id": {
82
+ "type": "string"
83
+ },
84
+ "title": {
85
+ "type": "string"
86
+ },
87
+ "schemaData": {
88
+ "type": "object"
89
+ },
90
+ "linkTargets": {
91
+ "type": "array",
92
+ "items": {
93
+ "type": "string"
94
+ }
95
+ },
96
+ "type": {
97
+ "type": "string"
98
+ },
99
+ "content": {
100
+ "type": "string"
101
+ },
102
+ "contentLinks": {
103
+ "type": "array",
104
+ "items": {
105
+ "type": "object",
106
+ "properties": {
107
+ "text": {
108
+ "type": "string"
109
+ },
110
+ "target": {
111
+ "type": "string"
112
+ },
113
+ "action": {
114
+ "enum": ["link", "embed"]
115
+ },
116
+ "anchor": {
117
+ "type": "string"
118
+ },
119
+ "linkSyntax": {
120
+ "enum": ["wikilink", "markdown"]
121
+ }
122
+ },
123
+ "required": ["text", "target", "action", "linkSyntax"],
124
+ "additionalProperties": false
125
+ }
126
+ },
127
+ "source": {
128
+ "type": "object",
129
+ "properties": {
130
+ "file": {
131
+ "type": "string"
132
+ },
133
+ "line": {
134
+ "type": "integer"
135
+ },
136
+ "column": {
137
+ "type": "integer"
138
+ }
139
+ },
140
+ "required": ["file"],
141
+ "additionalProperties": false
142
+ },
143
+ "resolvedType": {
144
+ "type": "string"
145
+ },
146
+ "resolvedParents": {
147
+ "type": "array",
148
+ "items": {
149
+ "type": "object",
150
+ "properties": {
151
+ "id": {
152
+ "type": "string"
153
+ },
154
+ "field": {
155
+ "type": "string"
156
+ },
157
+ "kind": {
158
+ "enum": ["hierarchy", "relationship"]
159
+ },
160
+ "hierarchy": {
161
+ "type": "string"
162
+ },
163
+ "selfRef": {
164
+ "type": "boolean"
165
+ },
166
+ "fieldOn": {
167
+ "enum": ["child", "parent"]
168
+ }
169
+ },
170
+ "required": ["id", "field", "kind", "selfRef", "fieldOn"],
171
+ "additionalProperties": false
172
+ }
173
+ },
174
+ "resolvedLinks": {
175
+ "type": "array",
176
+ "items": {
177
+ "type": "object",
178
+ "properties": {
179
+ "text": {
180
+ "type": "string"
181
+ },
182
+ "target": {
183
+ "type": "string"
184
+ },
185
+ "action": {
186
+ "enum": ["link", "embed"]
187
+ },
188
+ "anchor": {
189
+ "type": "string"
190
+ },
191
+ "location": {
192
+ "enum": ["node", "internal", "external", "system", "protocol"]
193
+ }
194
+ },
195
+ "required": ["text", "target", "action", "location"],
196
+ "additionalProperties": false
197
+ }
198
+ }
199
+ },
200
+ "required": [
201
+ "id",
202
+ "title",
203
+ "schemaData",
204
+ "linkTargets",
205
+ "type",
206
+ "resolvedType",
207
+ "resolvedParents",
208
+ "resolvedLinks"
209
+ ],
210
+ "additionalProperties": false
211
+ }
212
+ }
213
+ },
214
+ "required": ["formatVersion", "hierarchies", "nodes"],
215
+ "additionalProperties": false
216
+ }
@@ -0,0 +1,216 @@
1
+ {
2
+ "$id": "https://raw.githubusercontent.com/mindsocket/structured-context/main/schemas/generated/space-graph/1.json",
3
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
4
+ "title": "structured-context space graph",
5
+ "type": "object",
6
+ "properties": {
7
+ "formatVersion": {
8
+ "const": 1
9
+ },
10
+ "hierarchies": {
11
+ "type": "array",
12
+ "items": {
13
+ "type": "object",
14
+ "properties": {
15
+ "name": {
16
+ "type": "string"
17
+ },
18
+ "levels": {
19
+ "type": "array",
20
+ "items": {
21
+ "type": "object",
22
+ "properties": {
23
+ "type": {
24
+ "type": "string"
25
+ },
26
+ "field": {
27
+ "type": "string",
28
+ "minLength": 1
29
+ },
30
+ "fieldOn": {
31
+ "enum": ["child", "parent"]
32
+ },
33
+ "multiple": {
34
+ "type": "boolean"
35
+ },
36
+ "templateFormat": {
37
+ "enum": ["heading", "list", "table", "page"]
38
+ },
39
+ "matchers": {
40
+ "type": "array",
41
+ "items": {
42
+ "type": "string",
43
+ "minLength": 1
44
+ },
45
+ "minItems": 1
46
+ },
47
+ "embeddedTemplateFields": {
48
+ "type": "array",
49
+ "items": {
50
+ "type": "string",
51
+ "minLength": 1
52
+ }
53
+ },
54
+ "selfRef": {
55
+ "type": "boolean"
56
+ },
57
+ "selfRefField": {
58
+ "type": "string"
59
+ }
60
+ },
61
+ "required": ["type", "field", "fieldOn", "multiple", "selfRef"],
62
+ "additionalProperties": false
63
+ }
64
+ },
65
+ "allowSkipLevels": {
66
+ "type": "boolean"
67
+ }
68
+ },
69
+ "required": ["name", "levels"],
70
+ "additionalProperties": false
71
+ }
72
+ },
73
+ "mainHierarchy": {
74
+ "type": "string"
75
+ },
76
+ "nodes": {
77
+ "type": "array",
78
+ "items": {
79
+ "type": "object",
80
+ "properties": {
81
+ "id": {
82
+ "type": "string"
83
+ },
84
+ "title": {
85
+ "type": "string"
86
+ },
87
+ "schemaData": {
88
+ "type": "object"
89
+ },
90
+ "linkTargets": {
91
+ "type": "array",
92
+ "items": {
93
+ "type": "string"
94
+ }
95
+ },
96
+ "type": {
97
+ "type": "string"
98
+ },
99
+ "content": {
100
+ "type": "string"
101
+ },
102
+ "contentLinks": {
103
+ "type": "array",
104
+ "items": {
105
+ "type": "object",
106
+ "properties": {
107
+ "text": {
108
+ "type": "string"
109
+ },
110
+ "target": {
111
+ "type": "string"
112
+ },
113
+ "action": {
114
+ "enum": ["link", "embed"]
115
+ },
116
+ "anchor": {
117
+ "type": "string"
118
+ },
119
+ "linkSyntax": {
120
+ "enum": ["wikilink", "markdown"]
121
+ }
122
+ },
123
+ "required": ["text", "target", "action", "linkSyntax"],
124
+ "additionalProperties": false
125
+ }
126
+ },
127
+ "source": {
128
+ "type": "object",
129
+ "properties": {
130
+ "file": {
131
+ "type": "string"
132
+ },
133
+ "line": {
134
+ "type": "integer"
135
+ },
136
+ "column": {
137
+ "type": "integer"
138
+ }
139
+ },
140
+ "required": ["file"],
141
+ "additionalProperties": false
142
+ },
143
+ "resolvedType": {
144
+ "type": "string"
145
+ },
146
+ "resolvedParents": {
147
+ "type": "array",
148
+ "items": {
149
+ "type": "object",
150
+ "properties": {
151
+ "id": {
152
+ "type": "string"
153
+ },
154
+ "field": {
155
+ "type": "string"
156
+ },
157
+ "kind": {
158
+ "enum": ["hierarchy", "relationship"]
159
+ },
160
+ "hierarchy": {
161
+ "type": "string"
162
+ },
163
+ "selfRef": {
164
+ "type": "boolean"
165
+ },
166
+ "fieldOn": {
167
+ "enum": ["child", "parent"]
168
+ }
169
+ },
170
+ "required": ["id", "field", "kind", "selfRef", "fieldOn"],
171
+ "additionalProperties": false
172
+ }
173
+ },
174
+ "resolvedLinks": {
175
+ "type": "array",
176
+ "items": {
177
+ "type": "object",
178
+ "properties": {
179
+ "text": {
180
+ "type": "string"
181
+ },
182
+ "target": {
183
+ "type": "string"
184
+ },
185
+ "action": {
186
+ "enum": ["link", "embed"]
187
+ },
188
+ "anchor": {
189
+ "type": "string"
190
+ },
191
+ "location": {
192
+ "enum": ["node", "internal", "external", "system", "protocol"]
193
+ }
194
+ },
195
+ "required": ["text", "target", "action", "location"],
196
+ "additionalProperties": false
197
+ }
198
+ }
199
+ },
200
+ "required": [
201
+ "id",
202
+ "title",
203
+ "schemaData",
204
+ "linkTargets",
205
+ "type",
206
+ "resolvedType",
207
+ "resolvedParents",
208
+ "resolvedLinks"
209
+ ],
210
+ "additionalProperties": false
211
+ }
212
+ }
213
+ },
214
+ "required": ["formatVersion", "hierarchies", "nodes"],
215
+ "additionalProperties": false
216
+ }
@@ -1,4 +1,5 @@
1
1
  import { resolveSchema } from './config';
2
+ import { SctxError } from './errors';
2
3
  import { resolveSpacePluginSchemas } from './plugins/util';
3
4
  import { loadSchema } from './schema/schema';
4
5
  /** Thrown by createSpaceContext when no space matches the given name. */
@@ -33,7 +34,16 @@ export function createSpaceContext(spaceName, config, options = {}) {
33
34
  }
34
35
  const resolvedSchemaPath = resolveSchema(config, space, { configDir });
35
36
  const pluginSchemas = resolveSpacePluginSchemas(space, config, configDir);
36
- const { schema, schemaRefRegistry, schemaValidator } = loadSchema(resolvedSchemaPath, pluginSchemas);
37
+ let loaded;
38
+ try {
39
+ loaded = loadSchema(resolvedSchemaPath, pluginSchemas);
40
+ }
41
+ catch (err) {
42
+ if (err instanceof SctxError)
43
+ throw err;
44
+ throw new SctxError('E_SCHEMA_INVALID', `Space "${spaceName}": ${err instanceof Error ? err.message : String(err)}`);
45
+ }
46
+ const { schema, schemaRefRegistry, schemaValidator } = loaded;
37
47
  return {
38
48
  space,
39
49
  config,