@rtif-sdk/core 1.0.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH;;;GAGG;AACH,MAAM,OAAO,SAAU,SAAQ,KAAK;IACzB,IAAI,CAAgB;IAE7B,YAAY,IAAmB,EAAE,OAAe;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,16 @@
1
+ export type { Document, Block, Span, Position, Selection } from './model.js';
2
+ export { RtifError } from './error.js';
3
+ export type { RtifErrorCode } from './error.js';
4
+ export type { Operation, InsertText, DeleteText, SplitBlock, MergeBlock, SetBlockAttrs, SetBlockType, SetSpanMarks, SetMeta, } from './operations.js';
5
+ export { apply } from './apply.js';
6
+ export type { ApplyResult } from './apply.js';
7
+ export { normalizeSpans, marksEqual } from './normalize.js';
8
+ export { resolve, blockTextLength, docLength } from './resolve.js';
9
+ export type { ResolvedPosition } from './resolve.js';
10
+ export { validate } from './validate.js';
11
+ export type { ValidationResult, ValidationError } from './validate.js';
12
+ export { getMarksAtOffset, getMarksInRange, getBlockAtOffset } from './queries.js';
13
+ export type { RangeMarksResult } from './queries.js';
14
+ export { createMarkSerializerRegistry } from './serialization.js';
15
+ export type { MarkSerializer, MarkSerializerRegistry, SerializationFormat, } from './serialization.js';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG7E,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,YAAY,EACV,SAAS,EACT,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,OAAO,GACR,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5D,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACnE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEvE,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACnF,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAClE,YAAY,EACV,cAAc,EACd,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ // Error types
2
+ export { RtifError } from './error.js';
3
+ // Core functions
4
+ export { apply } from './apply.js';
5
+ export { normalizeSpans, marksEqual } from './normalize.js';
6
+ export { resolve, blockTextLength, docLength } from './resolve.js';
7
+ export { validate } from './validate.js';
8
+ export { getMarksAtOffset, getMarksInRange, getBlockAtOffset } from './queries.js';
9
+ // Serialization contracts
10
+ export { createMarkSerializerRegistry } from './serialization.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc;AACd,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAgBvC,iBAAiB;AACjB,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5D,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGnE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGnF,0BAA0B;AAC1B,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Core RTIF data model types.
3
+ * See SPEC.md §2 for full specification.
4
+ */
5
+ /** Root document container */
6
+ export interface Document {
7
+ /** Spec version for forward compatibility */
8
+ version: 1;
9
+ /** Ordered list of content blocks */
10
+ blocks: Block[];
11
+ /**
12
+ * Global metadata (optional).
13
+ * Plugins may store data here (e.g., document-level settings).
14
+ * Core ignores unknown keys.
15
+ */
16
+ meta?: Record<string, unknown>;
17
+ }
18
+ /** A line-level container (paragraph, heading, list item, etc.) */
19
+ export interface Block {
20
+ /** Unique ID, generated once, stable across edits */
21
+ id: string;
22
+ /**
23
+ * Block type. Core only knows "text".
24
+ * Plugins register additional types via the BlockType registry.
25
+ */
26
+ type: string;
27
+ /** Inline content within the block */
28
+ spans: Span[];
29
+ /**
30
+ * Block-level attributes (optional).
31
+ * Plugins use this for heading level, list depth, indentation, etc.
32
+ */
33
+ attrs?: Record<string, unknown>;
34
+ }
35
+ /** A contiguous run of characters sharing the same set of format marks */
36
+ export interface Span {
37
+ /** The raw text content */
38
+ text: string;
39
+ /**
40
+ * Set of active format marks on this span.
41
+ * Core defines no built-in marks — they are all plugin-provided.
42
+ * An empty object (or omitted) means plain, unformatted text.
43
+ */
44
+ marks?: Record<string, unknown>;
45
+ }
46
+ /** A single point in the document (absolute character offset, 0-indexed) */
47
+ export interface Position {
48
+ offset: number;
49
+ }
50
+ /**
51
+ * A selection defined by anchor (where the user started) and focus (cursor).
52
+ * When anchor.offset === focus.offset, this is a collapsed cursor (caret).
53
+ */
54
+ export interface Selection {
55
+ anchor: Position;
56
+ focus: Position;
57
+ }
58
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,8BAA8B;AAC9B,MAAM,WAAW,QAAQ;IACvB,6CAA6C;IAC7C,OAAO,EAAE,CAAC,CAAC;IAEX,qCAAqC;IACrC,MAAM,EAAE,KAAK,EAAE,CAAC;IAEhB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,mEAAmE;AACnE,MAAM,WAAW,KAAK;IACpB,qDAAqD;IACrD,EAAE,EAAE,MAAM,CAAC;IAEX;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb,sCAAsC;IACtC,KAAK,EAAE,IAAI,EAAE,CAAC;IAEd;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,0EAA0E;AAC1E,MAAM,WAAW,IAAI;IACnB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IAEb;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,4EAA4E;AAC5E,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,QAAQ,CAAC;CACjB"}
package/dist/model.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Core RTIF data model types.
3
+ * See SPEC.md §2 for full specification.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Span normalization — ensures documents are always in canonical form.
3
+ * See SPEC.md §3.3 for specification.
4
+ */
5
+ import type { Span } from './model.js';
6
+ /**
7
+ * Check if two mark objects are deeply equal.
8
+ * Treats `undefined` and `{}` as equivalent (both mean "no marks").
9
+ *
10
+ * @param a - First marks object
11
+ * @param b - Second marks object
12
+ * @returns true if marks are semantically equal
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * marksEqual({ bold: true }, { bold: true }); // => true
17
+ * marksEqual(undefined, {}); // => true
18
+ * marksEqual({ bold: true }, { italic: true }); // => false
19
+ * ```
20
+ */
21
+ export declare function marksEqual(a: Record<string, unknown> | undefined, b: Record<string, unknown> | undefined): boolean;
22
+ /**
23
+ * Normalize an array of spans:
24
+ * 1. Adjacent spans with identical marks are merged into one.
25
+ * 2. Empty spans (text === "") are removed.
26
+ * 3. If the result is empty, returns one span: `{ text: "" }`.
27
+ *
28
+ * This guarantees two semantically identical documents have identical JSON.
29
+ *
30
+ * @param spans - The spans to normalize
31
+ * @returns A new normalized array of spans
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * normalizeSpans([{ text: 'a' }, { text: 'b' }]);
36
+ * // => [{ text: 'ab' }]
37
+ * ```
38
+ */
39
+ export declare function normalizeSpans(spans: ReadonlyArray<Span>): Span[];
40
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CACxB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EACtC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GACrC,OAAO,CAKT;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CA+BjE"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Span normalization — ensures documents are always in canonical form.
3
+ * See SPEC.md §3.3 for specification.
4
+ */
5
+ /**
6
+ * Check if two mark objects are deeply equal.
7
+ * Treats `undefined` and `{}` as equivalent (both mean "no marks").
8
+ *
9
+ * @param a - First marks object
10
+ * @param b - Second marks object
11
+ * @returns true if marks are semantically equal
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * marksEqual({ bold: true }, { bold: true }); // => true
16
+ * marksEqual(undefined, {}); // => true
17
+ * marksEqual({ bold: true }, { italic: true }); // => false
18
+ * ```
19
+ */
20
+ export function marksEqual(a, b) {
21
+ const aKeys = Object.keys(a ?? {});
22
+ const bKeys = Object.keys(b ?? {});
23
+ if (aKeys.length !== bKeys.length)
24
+ return false;
25
+ return aKeys.every((key) => (a ?? {})[key] === (b ?? {})[key]);
26
+ }
27
+ /**
28
+ * Normalize an array of spans:
29
+ * 1. Adjacent spans with identical marks are merged into one.
30
+ * 2. Empty spans (text === "") are removed.
31
+ * 3. If the result is empty, returns one span: `{ text: "" }`.
32
+ *
33
+ * This guarantees two semantically identical documents have identical JSON.
34
+ *
35
+ * @param spans - The spans to normalize
36
+ * @returns A new normalized array of spans
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * normalizeSpans([{ text: 'a' }, { text: 'b' }]);
41
+ * // => [{ text: 'ab' }]
42
+ * ```
43
+ */
44
+ export function normalizeSpans(spans) {
45
+ const result = [];
46
+ for (const span of spans) {
47
+ if (span.text === '')
48
+ continue;
49
+ const prev = result[result.length - 1];
50
+ if (prev && marksEqual(prev.marks, span.marks)) {
51
+ // Merge into previous span (create new object, don't mutate)
52
+ result[result.length - 1] = {
53
+ text: prev.text + span.text,
54
+ ...(prev.marks && Object.keys(prev.marks).length > 0
55
+ ? { marks: { ...prev.marks } }
56
+ : {}),
57
+ };
58
+ }
59
+ else {
60
+ // Copy span to avoid mutation
61
+ result.push({
62
+ text: span.text,
63
+ ...(span.marks && Object.keys(span.marks).length > 0
64
+ ? { marks: { ...span.marks } }
65
+ : {}),
66
+ });
67
+ }
68
+ }
69
+ if (result.length === 0) {
70
+ return [{ text: '' }];
71
+ }
72
+ return result;
73
+ }
74
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,UAAU,CACxB,CAAsC,EACtC,CAAsC;IAEtC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACnC,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAChD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AACjE,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,cAAc,CAAC,KAA0B;IACvD,MAAM,MAAM,GAAW,EAAE,CAAC;IAE1B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE;YAAE,SAAS;QAE/B,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACvC,IAAI,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/C,6DAA6D;YAC7D,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG;gBAC1B,IAAI,EAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI;gBAC3B,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;oBAClD,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE;oBAC9B,CAAC,CAAC,EAAE,CAAC;aACR,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,8BAA8B;YAC9B,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;oBAClD,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE;oBAC9B,CAAC,CAAC,EAAE,CAAC;aACR,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * RTIF operation types — the complete set of document mutations.
3
+ * See SPEC.md §3.1 for full specification.
4
+ */
5
+ /** Insert characters at a position */
6
+ export interface InsertText {
7
+ type: 'insert_text';
8
+ offset: number;
9
+ text: string;
10
+ }
11
+ /** Delete a range of characters [offset, offset + count) */
12
+ export interface DeleteText {
13
+ type: 'delete_text';
14
+ offset: number;
15
+ count: number;
16
+ /** Deleted text stored for undo. Populated by the engine, not the caller. */
17
+ _deleted?: string;
18
+ }
19
+ /**
20
+ * Split a block at a position, creating a new block after it.
21
+ * This is what "pressing Enter" produces.
22
+ */
23
+ export interface SplitBlock {
24
+ type: 'split_block';
25
+ offset: number;
26
+ /** ID for the newly created block. Generated by engine if omitted. */
27
+ newBlockId?: string;
28
+ /** Original type of the merged block. Populated by merge_block inverse. */
29
+ _mergedBlockType?: string;
30
+ /** Original attrs of the merged block. Populated by merge_block inverse. */
31
+ _mergedBlockAttrs?: Record<string, unknown>;
32
+ }
33
+ /**
34
+ * Merge a block with the previous block.
35
+ * This is what "pressing Backspace at the start of a block" produces.
36
+ */
37
+ export interface MergeBlock {
38
+ type: 'merge_block';
39
+ /** ID of the block being merged into the previous one */
40
+ blockId: string;
41
+ }
42
+ /**
43
+ * Set/update block-level attributes.
44
+ * Shallow merge with existing attrs. Set a key to `null` to remove it.
45
+ */
46
+ export interface SetBlockAttrs {
47
+ type: 'set_block_attrs';
48
+ blockId: string;
49
+ attrs: Record<string, unknown>;
50
+ /** Previous attrs stored for undo. Populated by engine. */
51
+ _prevAttrs?: Record<string, unknown>;
52
+ }
53
+ /**
54
+ * Change a block's type (e.g., "text" → "heading").
55
+ * The block retains its id, spans, and attrs — only `type` changes.
56
+ */
57
+ export interface SetBlockType {
58
+ type: 'set_block_type';
59
+ blockId: string;
60
+ /** The new block type string */
61
+ blockType: string;
62
+ /** Previous type stored for undo. Populated by apply(). */
63
+ _prevType?: string;
64
+ }
65
+ /**
66
+ * Apply/remove marks on a range of text [offset, offset + count).
67
+ * Internally triggers span splitting/merging as needed.
68
+ */
69
+ export interface SetSpanMarks {
70
+ type: 'set_span_marks';
71
+ offset: number;
72
+ count: number;
73
+ /**
74
+ * Marks to set (shallow merge per span).
75
+ * Set a mark key to `null` to remove it.
76
+ */
77
+ marks: Record<string, unknown>;
78
+ }
79
+ /**
80
+ * Set document-level metadata.
81
+ * Shallow merge. Set key to `null` to remove.
82
+ */
83
+ export interface SetMeta {
84
+ type: 'set_meta';
85
+ meta: Record<string, unknown>;
86
+ _prevMeta?: Record<string, unknown>;
87
+ }
88
+ /** Union of all operation types */
89
+ export type Operation = InsertText | DeleteText | SplitBlock | MergeBlock | SetBlockAttrs | SetBlockType | SetSpanMarks | SetMeta;
90
+ //# sourceMappingURL=operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,sCAAsC;AACtC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,4DAA4D;AAC5D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7C;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,aAAa,CAAC;IACpB,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,mCAAmC;AACnC,MAAM,MAAM,SAAS,GACjB,UAAU,GACV,UAAU,GACV,UAAU,GACV,UAAU,GACV,aAAa,GACb,YAAY,GACZ,YAAY,GACZ,OAAO,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * RTIF operation types — the complete set of document mutations.
3
+ * See SPEC.md §3.1 for full specification.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.js","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Document query utilities — read-only queries against document state.
3
+ * Used by engine and platform implementations for toolbar state, accessibility, etc.
4
+ */
5
+ import type { Block, Document } from './model.js';
6
+ /** Result of querying marks across a text range */
7
+ export interface RangeMarksResult {
8
+ /** Marks active across the entire range with identical values */
9
+ readonly common: Record<string, unknown>;
10
+ /** Mark keys present in some spans but not all, or with differing values */
11
+ readonly mixed: readonly string[];
12
+ }
13
+ /**
14
+ * Get the formatting marks at a specific document offset.
15
+ *
16
+ * At span boundaries, returns the marks of the preceding span
17
+ * (consistent with `insert_text` mark inheritance behavior).
18
+ *
19
+ * @param doc - The document to query
20
+ * @param offset - Absolute character offset
21
+ * @returns A copy of the marks at that position (empty object if no marks)
22
+ * @throws {RtifError} OFFSET_OUT_OF_RANGE if offset is invalid
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * // Given: [{ text: 'bold', marks: { bold: true } }, { text: 'plain' }]
27
+ * getMarksAtOffset(doc, 2); // => { bold: true }
28
+ * getMarksAtOffset(doc, 5); // => {}
29
+ * ```
30
+ */
31
+ export declare function getMarksAtOffset(doc: Document, offset: number): Record<string, unknown>;
32
+ /**
33
+ * Get the marks across a text range, identifying which are common and which are mixed.
34
+ *
35
+ * Supports cross-block ranges. Virtual `\n` separators between blocks
36
+ * contribute no marks and are skipped.
37
+ *
38
+ * @param doc - The document to query
39
+ * @param offset - Start of the range (absolute offset)
40
+ * @param count - Number of characters in the range
41
+ * @returns Common marks (same across all spans) and mixed mark keys
42
+ * @throws {RtifError} OFFSET_OUT_OF_RANGE if offset is invalid
43
+ * @throws {RtifError} INVALID_COUNT if count is negative or range exceeds document
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * // Given: [{ text: 'bo', marks: { bold: true } }, { text: 'th', marks: { bold: true, italic: true } }]
48
+ * getMarksInRange(doc, 0, 4);
49
+ * // => { common: { bold: true }, mixed: ['italic'] }
50
+ * ```
51
+ */
52
+ export declare function getMarksInRange(doc: Document, offset: number, count: number): RangeMarksResult;
53
+ /**
54
+ * Get the block containing a specific document offset.
55
+ *
56
+ * @param doc - The document to query
57
+ * @param offset - Absolute character offset
58
+ * @returns The block at that offset
59
+ * @throws {RtifError} OFFSET_OUT_OF_RANGE if offset is invalid
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const block = getBlockAtOffset(doc, 0);
64
+ * // => { id: 'b1', type: 'text', spans: [...] }
65
+ * ```
66
+ */
67
+ export declare function getBlockAtOffset(doc: Document, offset: number): Block;
68
+ //# sourceMappingURL=queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAIlD,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAwBzB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,gBAAgB,CA0FlB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAGrE"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Document query utilities — read-only queries against document state.
3
+ * Used by engine and platform implementations for toolbar state, accessibility, etc.
4
+ */
5
+ import { RtifError } from './error.js';
6
+ import { resolve, blockTextLength, docLength } from './resolve.js';
7
+ /**
8
+ * Get the formatting marks at a specific document offset.
9
+ *
10
+ * At span boundaries, returns the marks of the preceding span
11
+ * (consistent with `insert_text` mark inheritance behavior).
12
+ *
13
+ * @param doc - The document to query
14
+ * @param offset - Absolute character offset
15
+ * @returns A copy of the marks at that position (empty object if no marks)
16
+ * @throws {RtifError} OFFSET_OUT_OF_RANGE if offset is invalid
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // Given: [{ text: 'bold', marks: { bold: true } }, { text: 'plain' }]
21
+ * getMarksAtOffset(doc, 2); // => { bold: true }
22
+ * getMarksAtOffset(doc, 5); // => {}
23
+ * ```
24
+ */
25
+ export function getMarksAtOffset(doc, offset) {
26
+ const { blockIndex, localOffset } = resolve(doc, offset);
27
+ const block = doc.blocks[blockIndex];
28
+ // Empty block — single span with no text
29
+ if (block.spans.length === 1 && block.spans[0].text === '') {
30
+ return { ...(block.spans[0].marks ?? {}) };
31
+ }
32
+ let pos = 0;
33
+ for (const span of block.spans) {
34
+ const spanEnd = pos + span.text.length;
35
+ // Inside this span, or at its trailing boundary
36
+ if (localOffset <= spanEnd) {
37
+ return { ...(span.marks ?? {}) };
38
+ }
39
+ pos = spanEnd;
40
+ }
41
+ // Fallback: end of block (shouldn't reach here if resolve is correct)
42
+ const lastSpan = block.spans[block.spans.length - 1];
43
+ return { ...(lastSpan.marks ?? {}) };
44
+ }
45
+ /**
46
+ * Get the marks across a text range, identifying which are common and which are mixed.
47
+ *
48
+ * Supports cross-block ranges. Virtual `\n` separators between blocks
49
+ * contribute no marks and are skipped.
50
+ *
51
+ * @param doc - The document to query
52
+ * @param offset - Start of the range (absolute offset)
53
+ * @param count - Number of characters in the range
54
+ * @returns Common marks (same across all spans) and mixed mark keys
55
+ * @throws {RtifError} OFFSET_OUT_OF_RANGE if offset is invalid
56
+ * @throws {RtifError} INVALID_COUNT if count is negative or range exceeds document
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * // Given: [{ text: 'bo', marks: { bold: true } }, { text: 'th', marks: { bold: true, italic: true } }]
61
+ * getMarksInRange(doc, 0, 4);
62
+ * // => { common: { bold: true }, mixed: ['italic'] }
63
+ * ```
64
+ */
65
+ export function getMarksInRange(doc, offset, count) {
66
+ if (count < 0) {
67
+ throw new RtifError('INVALID_COUNT', `Count ${count} is negative`);
68
+ }
69
+ if (count === 0) {
70
+ const marks = getMarksAtOffset(doc, offset);
71
+ return { common: marks, mixed: [] };
72
+ }
73
+ const totalLen = docLength(doc);
74
+ if (offset < 0 || offset > totalLen) {
75
+ throw new RtifError('OFFSET_OUT_OF_RANGE', `Offset ${offset} out of range`);
76
+ }
77
+ if (offset + count > totalLen) {
78
+ throw new RtifError('INVALID_COUNT', `Range [${offset}, ${offset + count}) exceeds document length ${totalLen}`);
79
+ }
80
+ const start = resolve(doc, offset);
81
+ // Collect mark objects from each span (or span fragment) in the range
82
+ const allMarkSets = [];
83
+ let remaining = count;
84
+ let blockIdx = start.blockIndex;
85
+ let localPos = start.localOffset;
86
+ while (remaining > 0 && blockIdx < doc.blocks.length) {
87
+ const block = doc.blocks[blockIdx];
88
+ const blockLen = blockTextLength(block);
89
+ const available = blockLen - localPos;
90
+ const consume = Math.min(remaining, available);
91
+ if (consume > 0) {
92
+ // Walk spans in this block, collecting marks for the overlapping portion
93
+ let spanPos = 0;
94
+ for (const span of block.spans) {
95
+ const spanEnd = spanPos + span.text.length;
96
+ if (spanEnd <= localPos) {
97
+ spanPos = spanEnd;
98
+ continue;
99
+ }
100
+ if (spanPos >= localPos + consume)
101
+ break;
102
+ allMarkSets.push(span.marks ?? {});
103
+ spanPos = spanEnd;
104
+ }
105
+ }
106
+ remaining -= consume;
107
+ // Account for virtual \n between blocks
108
+ if (remaining > 0 && blockIdx < doc.blocks.length - 1) {
109
+ remaining -= 1;
110
+ blockIdx++;
111
+ localPos = 0;
112
+ }
113
+ else {
114
+ break;
115
+ }
116
+ }
117
+ if (allMarkSets.length === 0) {
118
+ return { common: {}, mixed: [] };
119
+ }
120
+ // Collect all unique mark keys across every span
121
+ const allKeys = new Set();
122
+ for (const marks of allMarkSets) {
123
+ for (const key of Object.keys(marks)) {
124
+ allKeys.add(key);
125
+ }
126
+ }
127
+ const common = {};
128
+ const mixed = [];
129
+ for (const key of allKeys) {
130
+ const firstValue = allMarkSets[0][key];
131
+ const isCommon = allMarkSets.every((marks) => key in marks && marks[key] === firstValue);
132
+ if (isCommon) {
133
+ common[key] = firstValue;
134
+ }
135
+ else {
136
+ mixed.push(key);
137
+ }
138
+ }
139
+ return { common, mixed };
140
+ }
141
+ /**
142
+ * Get the block containing a specific document offset.
143
+ *
144
+ * @param doc - The document to query
145
+ * @param offset - Absolute character offset
146
+ * @returns The block at that offset
147
+ * @throws {RtifError} OFFSET_OUT_OF_RANGE if offset is invalid
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * const block = getBlockAtOffset(doc, 0);
152
+ * // => { id: 'b1', type: 'text', spans: [...] }
153
+ * ```
154
+ */
155
+ export function getBlockAtOffset(doc, offset) {
156
+ const { blockIndex } = resolve(doc, offset);
157
+ return doc.blocks[blockIndex];
158
+ }
159
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Offset resolution — converts absolute offsets to block-local coordinates.
3
+ * See SPEC.md §2.4 for specification.
4
+ */
5
+ import type { Block, Document } from './model.js';
6
+ export interface ResolvedPosition {
7
+ /** Index of the block containing this offset */
8
+ readonly blockIndex: number;
9
+ /** Character offset within the block */
10
+ readonly localOffset: number;
11
+ }
12
+ /**
13
+ * Compute the text length of a block (sum of all span text lengths).
14
+ *
15
+ * @param block - The block to measure
16
+ * @returns Total character count across all spans
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * blockTextLength({ id: 'b1', type: 'text', spans: [{ text: 'hello' }] });
21
+ * // => 5
22
+ * ```
23
+ */
24
+ export declare function blockTextLength(block: Block): number;
25
+ /**
26
+ * Compute the total document length (all block text + virtual newlines between blocks).
27
+ *
28
+ * @param doc - The document to measure
29
+ * @returns Total character count including virtual newline separators
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * docLength({ version: 1, blocks: [
34
+ * { id: 'b1', type: 'text', spans: [{ text: 'hello' }] },
35
+ * { id: 'b2', type: 'text', spans: [{ text: 'world' }] },
36
+ * ] });
37
+ * // => 11 (5 + 1 + 5)
38
+ * ```
39
+ */
40
+ export declare function docLength(doc: Document): number;
41
+ /**
42
+ * Resolve an absolute document offset to a block index and local offset.
43
+ *
44
+ * Walks blocks, subtracting lengths (including the +1 separator per block
45
+ * boundary), until the offset lands inside a block.
46
+ *
47
+ * @param doc - The document to resolve against
48
+ * @param offset - Absolute character offset from document start
49
+ * @returns Block index and character offset within that block
50
+ * @throws {RtifError} OFFSET_OUT_OF_RANGE if offset is negative or exceeds document length
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * const { blockIndex, localOffset } = resolve(doc, 5);
55
+ * ```
56
+ */
57
+ export declare function resolve(doc: Document, offset: number): ResolvedPosition;
58
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGlD,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wCAAwC;IACxC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAMpD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAO/C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAqCvE"}