@nesso-how/vocab-learning 0.1.0-alpha.34 → 0.1.0-alpha.35

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
@@ -1,8 +1,8 @@
1
1
  # @nesso-how/vocab-learning
2
2
 
3
- The **Nesso Learning Vocabulary** for [Nesso](https://nesso.how) — a self-contained graph vocabulary: 52 typed relation types in 8 categories, semantic coefficients, category palettes, and private node parameters (FSRS review fields).
3
+ The **Nesso Learning Vocabulary** for [Nesso](https://nesso.how) — a self-contained graph vocabulary: 52 typed relation types in 8 categories, type properties, category palettes, and private node parameters (FSRS review fields).
4
4
 
5
- Graph JSON files declare which vocabulary they use via `VOCABULARY.id` and a normative `version`.
5
+ Graph JSON files declare which vocabulary they use via `VOCABULARY.id` and a normative `version`. File I/O builds on [`@nesso-how/schema`](../schema/README.md) and adds learning-vocabulary validation (elaboration shape, known relation type ids).
6
6
 
7
7
  ## Install
8
8
 
@@ -12,11 +12,15 @@ npm install @nesso-how/vocab-learning
12
12
 
13
13
  ## Usage
14
14
 
15
+ ### Relation catalog and palettes
16
+
15
17
  ```ts
16
18
  import {
17
19
  VOCABULARY,
20
+ RELATION_CATEGORIES,
18
21
  RELATION_TYPES,
19
22
  RELATION_TYPE_VALUES,
23
+ PALETTES,
20
24
  defaultConceptReviewFields,
21
25
  } from '@nesso-how/vocab-learning'
22
26
 
@@ -27,6 +31,21 @@ const freshNode = defaultConceptReviewFields()
27
31
  // { stability: 0, difficulty: 0, due: 0, ... }
28
32
  ```
29
33
 
34
+ ### Graph document I/O
35
+
36
+ ```ts
37
+ import { deserialize, serialize, type NessoGraphDocument } from '@nesso-how/vocab-learning'
38
+
39
+ const json = serialize({
40
+ name: 'My graph',
41
+ concepts: [{ id: 'n1', label: 'Idea', x: 0, y: 0, data: { elaboration: { ... } } }],
42
+ relations: [{ id: 'e1', source: 'n1', target: 'n2', type: 'causes' }],
43
+ })
44
+ const doc: NessoGraphDocument = deserialize(json)
45
+ ```
46
+
47
+ **Shared content vs review state.** `serialize` / `deserialize` handle the portable graph file: concept labels, positions, `elaboration`, relation types, and layout hints. FSRS fields (`stability`, `difficulty`, `due`, etc.) are runtime node parameters for spaced repetition — they are typed here via `defaultConceptReviewFields()` but are **not** written into shared graph JSON; the Nesso app persists them separately.
48
+
30
49
  Relation types reference: [Relation types](https://nesso.how/docs/reference/relation-types/).
31
50
 
32
51
  ## License
@@ -0,0 +1,5 @@
1
+ import type { NessoGraphDocument, NessoGraphDocumentInput } from './graphDocument.js';
2
+ /** Serialize a Nesso Learning Vocabulary graph document to JSON. */
3
+ export declare function serialize<M extends Record<string, unknown>>(doc: NessoGraphDocumentInput<M>): string;
4
+ /** Parse and validate a Nesso Learning Vocabulary graph document. */
5
+ export declare function deserialize<M extends Record<string, unknown> = Record<string, unknown>>(json: string): NessoGraphDocument<M>;
@@ -0,0 +1,49 @@
1
+ // SPDX-License-Identifier: MIT
2
+ //
3
+ // Learning-vocabulary graph file I/O: wraps `@nesso-how/schema` serialize/deserialize
4
+ // and adds validation (elaboration shape, known `relation.type` ids).
5
+ import { deserialize as deserializeSchema, serialize as serializeSchema } from '@nesso-how/schema';
6
+ import { RELATION_TYPES } from './relationTypes.js';
7
+ const VALID_RELATION_TYPES = new Set(Object.keys(RELATION_TYPES));
8
+ function asRecord(value) {
9
+ return typeof value === 'object' && value !== null ? value : null;
10
+ }
11
+ function validateElaboration(value, path) {
12
+ const elab = asRecord(value);
13
+ if (!elab) {
14
+ throw new Error(`Invalid Nesso graph document: ${path}.elaboration must be an object`);
15
+ }
16
+ if (typeof elab.definition !== 'string') {
17
+ throw new Error(`Invalid Nesso graph document: ${path}.elaboration.definition must be a string`);
18
+ }
19
+ if (typeof elab.examples !== 'string') {
20
+ throw new Error(`Invalid Nesso graph document: ${path}.elaboration.examples must be a string`);
21
+ }
22
+ if (typeof elab.notes !== 'string') {
23
+ throw new Error(`Invalid Nesso graph document: ${path}.elaboration.notes must be a string`);
24
+ }
25
+ }
26
+ function validateNessoDocument(doc) {
27
+ for (let i = 0; i < doc.concepts.length; i++) {
28
+ const data = doc.concepts[i].data;
29
+ if (data?.elaboration !== undefined) {
30
+ validateElaboration(data.elaboration, `concepts[${i}].data`);
31
+ }
32
+ }
33
+ for (let i = 0; i < doc.relations.length; i++) {
34
+ const rel = doc.relations[i];
35
+ if (rel.type !== undefined && !VALID_RELATION_TYPES.has(rel.type)) {
36
+ throw new Error(`Invalid Nesso graph document: unknown relation type "${rel.type}" at relations[${i}]`);
37
+ }
38
+ }
39
+ return doc;
40
+ }
41
+ /** Serialize a Nesso Learning Vocabulary graph document to JSON. */
42
+ export function serialize(doc) {
43
+ return serializeSchema(doc);
44
+ }
45
+ /** Parse and validate a Nesso Learning Vocabulary graph document. */
46
+ export function deserialize(json) {
47
+ const doc = deserializeSchema(json);
48
+ return validateNessoDocument(doc);
49
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,32 @@
1
+ // SPDX-License-Identifier: MIT
2
+ import { describe, expect, it } from 'vitest';
3
+ import { deserialize, serialize } from './document.js';
4
+ describe('vocab-learning serialize / deserialize', () => {
5
+ it('round-trips a learning vocabulary graph document', () => {
6
+ const doc = {
7
+ vocabulary: { id: '@nesso-how/vocab-learning', version: '0.1.0' },
8
+ name: 'Demo',
9
+ concepts: [
10
+ {
11
+ id: 'n1',
12
+ label: 'Idea',
13
+ x: 0,
14
+ y: 0,
15
+ data: { elaboration: { definition: 'd', examples: 'e', notes: '' } },
16
+ },
17
+ ],
18
+ relations: [{ id: 'e1', source: 'n1', target: 'n1', type: 'causes' }],
19
+ };
20
+ const parsed = deserialize(serialize(doc));
21
+ expect(parsed.concepts[0].label).toBe('Idea');
22
+ expect(parsed.relations[0].type).toBe('causes');
23
+ });
24
+ it('rejects an unknown relation type', () => {
25
+ const json = JSON.stringify({
26
+ name: 'X',
27
+ concepts: [{ id: 'n1', label: 'n1', x: 0, y: 0 }],
28
+ relations: [{ id: 'e1', source: 'n1', target: 'n1', type: 'not-a-real-type' }],
29
+ });
30
+ expect(() => deserialize(json)).toThrow(/unknown relation type/);
31
+ });
32
+ });
@@ -0,0 +1,20 @@
1
+ import type { GraphDocument, GraphDocumentInput } from '@nesso-how/schema';
2
+ export interface ConceptElaboration {
3
+ definition: string;
4
+ examples: string;
5
+ notes: string;
6
+ imageUrl?: string;
7
+ imageTitle?: string;
8
+ imageDescriptionUrl?: string;
9
+ }
10
+ /** Shared concept content in a Nesso graph document (no label, no FSRS). */
11
+ export interface NessoConceptData extends Record<string, unknown> {
12
+ elaboration?: ConceptElaboration;
13
+ }
14
+ /** Shared relation content in a Nesso graph document. */
15
+ export interface NessoRelationData extends Record<string, unknown> {
16
+ curveFlip?: boolean;
17
+ curveFlipPinned?: boolean;
18
+ }
19
+ export type NessoGraphDocument<M extends Record<string, unknown> = Record<string, unknown>> = GraphDocument<NessoConceptData, NessoRelationData, M>;
20
+ export type NessoGraphDocumentInput<M extends Record<string, unknown> = Record<string, unknown>> = GraphDocumentInput<NessoConceptData, NessoRelationData, M>;
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export type { ConceptElaboration, NessoConceptData, NessoRelationData, NessoGraphDocument, NessoGraphDocumentInput, } from './graphDocument.js';
1
2
  /** OKG vocabulary identity — version bumps only on normative vocabulary changes, not npm releases. */
2
3
  export declare const VOCABULARY: {
3
4
  readonly id: "@nesso-how/vocab-learning";
@@ -5,63 +6,7 @@ export declare const VOCABULARY: {
5
6
  readonly domain: "learning";
6
7
  readonly version: "0.1.0";
7
8
  };
8
- /** Private dynamic node parameters declared by the Nesso Learning Vocabulary. */
9
- export interface LearningNodeParams extends Record<string, unknown> {
10
- stability: number;
11
- difficulty: number;
12
- reps: number;
13
- lapses: number;
14
- /** ts-fsrs State: 0=New 1=Learning 2=Review 3=Relearning */
15
- fsrsState: number;
16
- /** Unix ms; due when <= now (0 = new / immediate). */
17
- due: number;
18
- /** Unix ms; 0 = never reviewed. */
19
- lastReview: number;
20
- /** 0=unrated, 1=Again 2=Hard 3=Good 4=Easy (matches ts-fsrs Rating). */
21
- lastRating: number;
22
- /** FSRS learning-step index; optional for records saved before it existed. */
23
- learningSteps?: number;
24
- }
25
- /** Fresh private params for a new or shared-import concept (no personal review history). */
26
- export declare function defaultConceptReviewFields(): LearningNodeParams;
27
- export type EdgeCategory = 'taxonomic' | 'structural' | 'causal' | 'dependency' | 'temporal' | 'opposition' | 'similarity' | 'epistemic';
28
- export type EdgeTypeName = 'subtype-of' | 'has-subtype' | 'instance-of' | 'has-instance' | 'part-of' | 'contains' | 'made-of' | 'composes' | 'causes' | 'caused-by' | 'produces' | 'produced-by' | 'enables' | 'enabled-by' | 'prevents' | 'prevented-by' | 'triggers' | 'triggered-by' | 'inhibits' | 'inhibited-by' | 'disables' | 'disabled-by' | 'consumes' | 'consumed-by' | 'delays' | 'delayed-by' | 'requires' | 'required-by' | 'uses' | 'used-by' | 'used-for' | 'purpose-of' | 'precedes' | 'follows' | 'occurs-in' | 'has-occurrence' | 'during' | 'spans' | 'overlaps-with' | 'derives-from' | 'gives-rise-to' | 'contrasts-with' | 'opposite-of' | 'similar-to' | 'analogous-to' | 'supports' | 'supported-by' | 'contradicts' | 'explains' | 'explained-by' | 'defines' | 'defined-by';
29
- export type GlyphKind = 'triangle-up' | 'circle-dot' | 'diamond' | 'diamond-open' | 'hash' | 'arrow-right' | 'asterisk' | 'key' | 'block' | 'spark' | 'anchor' | 'tool' | 'chevron-r' | 'ring' | 'tilde' | 'x' | 'minus' | 'flag' | 'approx' | 'arrows-lr' | 'check' | 'slash' | 'bulb' | 'equals' | 'lock' | 'flame' | 'hourglass' | 'brackets' | 'overlap' | 'branch';
30
- /** Transitivity of a relation: `Y` strict, `N` none, `weak` with decay (algorithms may discount weight per step). */
31
- export type Transitivity = 'Y' | 'N' | 'weak';
32
- /** Polarity in signed-network terms: `+1` positive effect, `-1` antagonistic, `0` neutral/structural. */
33
- export type Polarity = -1 | 0 | 1;
34
- /** Expected mapping cardinality. `N-N` means no a-priori constraint. */
35
- export type Cardinality = '1-1' | '1-N' | 'N-1' | 'N-N';
36
- export interface EdgeTypeDef {
37
- cat: EdgeCategory;
38
- label: string;
39
- line: 'solid' | 'dashed' | 'dotted' | 'double' | 'wavy';
40
- glyph: GlyphKind;
41
- transitive: Transitivity;
42
- /** Canonical inverse in the set; `'self'` for symmetric types. */
43
- inverse: EdgeTypeName | 'self';
44
- /** Per-type semantic weight in 0..1; intensity, not per-edge confidence. */
45
- strength: number;
46
- polarity: Polarity;
47
- cardinality: Cardinality;
48
- }
49
- /** Canonical category labels and prompts. */
50
- export declare const RELATION_CATEGORY_META: Record<EdgeCategory, {
51
- label: string;
52
- subtitle: string;
53
- }>;
54
- export declare const RELATION_TYPES: Record<EdgeTypeName, EdgeTypeDef>;
55
- export declare const RELATION_TYPE_VALUES: EdgeTypeName[];
56
- /** Forward member of each inverse pair (first in `RELATION_TYPE_VALUES`); all symmetric types. */
57
- export declare function isPrimaryRelationType(id: EdgeTypeName): boolean;
58
- export type CategoryPalette = 'default' | 'vivid' | 'muted' | 'monoCat';
59
- /** Per-category hex colors for each named palette. */
60
- export declare const PALETTES: Record<CategoryPalette, Record<EdgeCategory, string>>;
61
- /**
62
- * Raw SVG inner content for each glyph kind, on a 14×14 viewBox.
63
- * Uses `currentColor` for stroke/fill — set CSS `color` on the parent to tint.
64
- * Default stroke attrs: fill=none, stroke=currentColor, stroke-width=1.4,
65
- * stroke-linecap=round, stroke-linejoin=round.
66
- */
67
- export declare const GLYPH_PATHS: Record<GlyphKind, string>;
9
+ export { defaultConceptReviewFields, type ConceptNodeData, type LearningNodeParams, } from './nodeParams.js';
10
+ export { RELATION_CATEGORIES, RELATION_TYPES, RELATION_TYPE_VALUES, isPrimaryRelationType, type Cardinality, type GlyphKind, type Polarity, type RelationCategory, type RelationTypeDef, type RelationTypeName, type Transitivity, } from './relationTypes.js';
11
+ export { CategoryPalette, GLYPH_PATHS, PALETTES } from './visual.js';
12
+ export { deserialize, serialize } from './document.js';