edytor 5.0.0 → 7.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.
Files changed (137) hide show
  1. package/README.md +3 -3
  2. package/dist/cloudflare/DocumentRoom.d.ts +77 -2
  3. package/dist/cloudflare/DocumentRoom.js +37 -1
  4. package/dist/cloudflare/index.d.ts +1 -1
  5. package/dist/cloudflare/index.js +1 -1
  6. package/dist/cloudflare/room/access.d.ts +6 -1
  7. package/dist/cloudflare/room/access.js +30 -4
  8. package/dist/cloudflare/room/admission.d.ts +11 -0
  9. package/dist/cloudflare/room/admission.js +43 -10
  10. package/dist/cloudflare/room/context.d.ts +73 -76
  11. package/dist/cloudflare/room/context.js +17 -6
  12. package/dist/cloudflare/room/forged.d.ts +1 -1
  13. package/dist/cloudflare/room/forged.js +11 -9
  14. package/dist/cloudflare/room/frames.d.ts +2 -0
  15. package/dist/cloudflare/room/frames.js +2 -0
  16. package/dist/cloudflare/room/presence.js +1 -4
  17. package/dist/cloudflare/room/reseed.d.ts +93 -0
  18. package/dist/cloudflare/room/reseed.js +500 -0
  19. package/dist/cloudflare/room/scheduler.d.ts +1 -1
  20. package/dist/cloudflare/room/scheduler.js +9 -1
  21. package/dist/cloudflare/room/shared.d.ts +10 -0
  22. package/dist/cloudflare/room/shared.js +10 -0
  23. package/dist/cloudflare/room/storage.d.ts +7 -0
  24. package/dist/cloudflare/room/storage.js +33 -1
  25. package/dist/components/Edytor.svelte +18 -3
  26. package/dist/components/Edytor.svelte.d.ts +10 -1
  27. package/dist/components/EdytorRoot.svelte +17 -0
  28. package/dist/components/EdytorRoot.svelte.d.ts +4 -0
  29. package/dist/components/Suggestion.svelte +1 -1
  30. package/dist/crdt/attribution/block.d.ts +3 -0
  31. package/dist/crdt/attribution/block.js +3 -2
  32. package/dist/crdt/doc/history.js +2 -3
  33. package/dist/crdt/doc/ops.d.ts +81 -0
  34. package/dist/crdt/doc/ops.js +1898 -0
  35. package/dist/crdt/doc/suggest.d.ts +6 -2
  36. package/dist/crdt/doc/suggest.js +6 -5
  37. package/dist/crdt/edytor-doc.d.ts +157 -157
  38. package/dist/crdt/edytor-doc.js +15 -43
  39. package/dist/crdt/engine-room.d.ts +4 -0
  40. package/dist/crdt/engine-room.js +6 -0
  41. package/dist/crdt/flow.d.ts +4 -46
  42. package/dist/crdt/flow.js +2 -262
  43. package/dist/crdt/index.d.ts +64 -75
  44. package/dist/crdt/operations.d.ts +1 -1
  45. package/dist/crdt/placement/resolve.d.ts +1 -1
  46. package/dist/crdt/protocol.d.ts +2 -2
  47. package/dist/crdt/protocol.js +2 -2
  48. package/dist/crdt/protocols/auth.d.ts +8 -1
  49. package/dist/crdt/protocols/auth.js +14 -1
  50. package/dist/crdt/protocols/awareness.js +46 -81
  51. package/dist/crdt/providers/index.d.ts +11 -22
  52. package/dist/crdt/providers/index.js +22 -3
  53. package/dist/crdt/providers/indexeddb.d.ts +6 -10
  54. package/dist/crdt/providers/indexeddb.js +10 -16
  55. package/dist/crdt/providers/room.d.ts +26 -0
  56. package/dist/crdt/providers/room.js +41 -12
  57. package/dist/crdt/providers/websocket.d.ts +10 -14
  58. package/dist/crdt/providers/websocket.js +43 -23
  59. package/dist/crdt/rangeDelete.d.ts +2 -47
  60. package/dist/crdt/rangeDelete.js +1 -169
  61. package/dist/crdt/reseed.d.ts +127 -0
  62. package/dist/crdt/reseed.js +533 -0
  63. package/dist/crdt/restore.d.ts +13 -1
  64. package/dist/crdt/restore.js +8 -3
  65. package/dist/crdt/schema.d.ts +6 -0
  66. package/dist/crdt/schema.js +6 -0
  67. package/dist/crdt/structs.d.ts +8 -11
  68. package/dist/crdt/structs.js +25 -44
  69. package/dist/crdt/text/index/build.d.ts +119 -0
  70. package/dist/crdt/text/index/build.js +2801 -0
  71. package/dist/crdt/text/index/shared.d.ts +2 -6
  72. package/dist/crdt/text/index/shared.js +2 -5
  73. package/dist/crdt/text/model.js +2 -3
  74. package/dist/crdt/text/runs.d.ts +9 -12
  75. package/dist/crdt/text/runs.js +2 -165
  76. package/dist/crdt/vendor/yjs/UPSTREAM.md +101 -5
  77. package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
  78. package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +10 -10
  79. package/dist/crdt/vendor/yjs/dts/utils/encoding.d.ts +1 -0
  80. package/dist/crdt/vendor/yjs/src/index.js +1 -1
  81. package/dist/crdt/vendor/yjs/src/structs/Item.js +24 -13
  82. package/dist/crdt/vendor/yjs/src/utils/Transaction.js +12 -10
  83. package/dist/crdt/vendor/yjs/src/utils/encoding.js +42 -1
  84. package/dist/events/domTextOffset.d.ts +2 -0
  85. package/dist/events/domTextOffset.js +8 -0
  86. package/dist/export/forms.d.ts +50 -50
  87. package/dist/index.d.ts +1 -1
  88. package/dist/index.js +1 -1
  89. package/dist/plugins/richtext/RichTextPlugin.svelte +63 -61
  90. package/dist/plugins/richtext/richTextOperations.js +26 -32
  91. package/dist/selection/selection.svelte.d.ts +1 -1
  92. package/dist/selection/selection.svelte.js +87 -106
  93. package/dist/selection/selection.utils.js +5 -8
  94. package/dist/session/commands.js +49 -51
  95. package/dist/session/handles.js +3 -7
  96. package/dist/surface/attributes.d.ts +3 -3
  97. package/dist/surface/attributes.js +2 -2
  98. package/dist/surface/observer.svelte.d.ts +7 -7
  99. package/dist/surface/observer.svelte.js +225 -336
  100. package/dist/surface/projector.svelte.js +2 -2
  101. package/dist/utils.d.ts +5 -0
  102. package/dist/utils.js +7 -0
  103. package/package.json +1 -1
  104. package/dist/crdt/doc/capability.d.ts +0 -56
  105. package/dist/crdt/doc/capability.js +0 -207
  106. package/dist/crdt/doc/content.d.ts +0 -16
  107. package/dist/crdt/doc/content.js +0 -77
  108. package/dist/crdt/doc/delete.d.ts +0 -15
  109. package/dist/crdt/doc/delete.js +0 -84
  110. package/dist/crdt/doc/layout.d.ts +0 -9
  111. package/dist/crdt/doc/layout.js +0 -178
  112. package/dist/crdt/doc/meta.d.ts +0 -19
  113. package/dist/crdt/doc/meta.js +0 -198
  114. package/dist/crdt/doc/moves.d.ts +0 -21
  115. package/dist/crdt/doc/moves.js +0 -274
  116. package/dist/crdt/doc/split-merge.d.ts +0 -10
  117. package/dist/crdt/doc/split-merge.js +0 -179
  118. package/dist/crdt/doc/steps.d.ts +0 -65
  119. package/dist/crdt/doc/steps.js +0 -311
  120. package/dist/crdt/text/index/cache.d.ts +0 -16
  121. package/dist/crdt/text/index/cache.js +0 -175
  122. package/dist/crdt/text/index/claims.d.ts +0 -18
  123. package/dist/crdt/text/index/claims.js +0 -203
  124. package/dist/crdt/text/index/fold.d.ts +0 -21
  125. package/dist/crdt/text/index/fold.js +0 -656
  126. package/dist/crdt/text/index/layout.d.ts +0 -22
  127. package/dist/crdt/text/index/layout.js +0 -258
  128. package/dist/crdt/text/index/placement.d.ts +0 -47
  129. package/dist/crdt/text/index/placement.js +0 -738
  130. package/dist/crdt/text/index/records.d.ts +0 -13
  131. package/dist/crdt/text/index/records.js +0 -139
  132. package/dist/crdt/text/index/report.d.ts +0 -18
  133. package/dist/crdt/text/index/report.js +0 -337
  134. package/dist/crdt/text/index/state.d.ts +0 -197
  135. package/dist/crdt/text/index/state.js +0 -116
  136. package/dist/crdt/text/index/streams.d.ts +0 -25
  137. package/dist/crdt/text/index/streams.js +0 -248
@@ -1,178 +0,0 @@
1
- /**
2
- * Layout ops (`layout.*`): the layout kind a new layout takes, where a
3
- * beside placement lands (`besideAt`), and the two layout-making ops,
4
- * `placeBeside` and `wrapInLayout`.
5
- */
6
- import { id as newId } from '../../utils.js';
7
- import { sanitizeSpec } from '../../utils/json.js';
8
- import { ref } from './plan.js';
9
- /** The layout ops of one facade, prepared. */
10
- export const layoutOps = (c) => {
11
- const { roles, view, blockTypeOf, childrenIds, positionOf, ancestorsOf, isIsland, isLines, insideIsland, isLayout, isLayoutItem, holdsLayout, insideItem, live, canPlace, isContainer, fits, fitsIn, REFUSED, plan, moveTo, landing, emptied } = c;
12
- /** The layout kind a new layout takes: `kind` when it is one, else the only one the roles declare. */
13
- const layoutKind = (kind) => {
14
- if (kind !== undefined)
15
- return roles.of(kind).layout === undefined ? undefined : kind;
16
- const kinds = roles.declared().layouts;
17
- return kinds.length === 1 ? kinds[0] : undefined;
18
- };
19
- /** A fresh empty block of `type`. */
20
- const spec = (type) => sanitizeSpec({ id: newId('b'), type, data: {} });
21
- /**
22
- * The block a beside placement at `target` stands beside
23
- * (`layout.place-beside`): a layout or an item for itself; a block whose
24
- * parent shows only its children (a list item → its list, at any depth)
25
- * or is an island of lines (a code line → its code block) for that
26
- * parent; then the block itself when it sits directly in an item or at
27
- * the root, or where a new layout of `kind` (else the only layout kind)
28
- * fits beside it (a toggle's, a callout's or a nested block's child,
29
- * outside any item and island); else its outermost block below the
30
- * root or an item.
31
- */
32
- const besideAt = (target, kind) => {
33
- let at = ref(target);
34
- if (isLayout(at) || isLayoutItem(at))
35
- return at;
36
- for (let parent = positionOf(at)?.parent ?? null; parent !== null && !isLayoutItem(parent) && (isContainer(parent) || isLines(parent)); parent = positionOf(parent)?.parent ?? null)
37
- at = parent;
38
- const parent = positionOf(at)?.parent ?? null;
39
- if (parent === null || isLayoutItem(parent))
40
- return at;
41
- const wrap = layoutKind(kind);
42
- if (wrap !== undefined &&
43
- fits(parent, wrap) &&
44
- !insideItem(parent) &&
45
- !isIsland(parent) &&
46
- !insideIsland(parent))
47
- return at;
48
- for (let up = parent; up !== null && !isLayoutItem(up); up = positionOf(up).parent)
49
- at = up;
50
- return at;
51
- };
52
- /**
53
- * Place `ids` beside `target`, to its `side` (`layout.place-beside`: a
54
- * block dragged to another block's left or right edge). The target
55
- * resolves to its outermost block below the root or below a layout
56
- * item (a list item → its list, a code line → its code block); an item
57
- * stands for itself, a layout for its first or last slot. Beside a
58
- * block in an item, a new item holding `ids` goes beside that item;
59
- * otherwise the block and a new item holding `ids` are wrapped in a new
60
- * layout of the layout `kind` (else the only one the roles declare) at
61
- * its place. Refused when `ids` holds the target or an ancestor of it,
62
- * an item, a layout or a block holding one, a block that does not
63
- * fit an item, a block or a layout inside an island, and, wrapping,
64
- * when there is no layout kind. The sources are cleaned in the same
65
- * plan (`emptying`, `dissolving`); a plain move. `ids`: the moved
66
- * blocks.
67
- */
68
- const placeBeside = (ids, target, side, kind) => {
69
- const moved = ids.map(ref);
70
- target = ref(target);
71
- const v = view();
72
- if ((side !== 'left' && side !== 'right') || !canPlace(moved) || !live(target))
73
- return REFUSED;
74
- if ([target, ...ancestorsOf(target, v)].some((a) => moved.includes(a)))
75
- return REFUSED;
76
- if (moved.some((id) => isLayoutItem(id) || holdsLayout(id)))
77
- return REFUSED;
78
- const right = side === 'right';
79
- // Where the new item goes: beside an item of a layout, or a new layout wrapping `at`.
80
- const at = besideAt(target, kind && ref(kind));
81
- let layout = null;
82
- let index = 0;
83
- if (isLayout(at))
84
- [layout, index] = [at, right ? childrenIds(at).length : 0];
85
- else {
86
- const item = isLayoutItem(at) ? at : positionOf(at).parent;
87
- if (item !== null) {
88
- const pos = positionOf(item);
89
- [layout, index] = [pos.parent, pos.index + (right ? 1 : 0)];
90
- }
91
- }
92
- const wrap = layout === null ? layoutKind(kind && ref(kind)) : blockTypeOf(layout);
93
- const item = wrap === undefined ? undefined : roles.of(wrap).layout;
94
- if (wrap === undefined || item === undefined)
95
- return REFUSED;
96
- if (moved.some((id) => !fitsIn(item, blockTypeOf(id))))
97
- return REFUSED;
98
- if (layout !== null && (isIsland(layout) || insideIsland(layout, v)))
99
- return REFUSED;
100
- const fresh = spec(item);
101
- const into = (parent) => moveTo(moved, parent, 0);
102
- let writes;
103
- let kept;
104
- if (layout !== null) {
105
- writes = [{ op: 'insertBlocks', parent: layout, index, specs: [fresh] }, ...into(fresh.id)];
106
- kept = new Set([fresh.id, ...landing(layout)]);
107
- }
108
- else {
109
- const pos = positionOf(at);
110
- const host = spec(item);
111
- const wrapper = { ...spec(wrap), children: right ? [host, fresh] : [fresh, host] };
112
- writes = [
113
- { op: 'insertBlocks', parent: pos.parent, index: pos.index, specs: [wrapper] },
114
- ...moveTo([at], host.id, 0),
115
- ...into(fresh.id)
116
- ];
117
- kept = new Set([wrapper.id, host.id, fresh.id, ...landing(pos.parent)]);
118
- }
119
- return plan(moved, emptied(moved, writes, kept));
120
- };
121
- /**
122
- * Wrap sibling blocks in a new layout of `columns` items (default: one
123
- * per block), at the first one's place (`layout.wrap`: Turn into N
124
- * columns, as Notion): the blocks fill the first items, one each, in
125
- * document order; each item after them holds one empty block of the
126
- * item's default child (one block turned into 3 columns: it, then two
127
- * empty columns). The layout is of the layout `kind` (else the only one
128
- * the roles declare). Refused for no block, fewer than two items or
129
- * fewer items than blocks, blocks of different parents, an item, a
130
- * layout or a block holding one, where the layout does not fit
131
- * (`fits`: in a list, a code block) or lands inside an item or an
132
- * island, and for a block that does not fit an item. A plain move.
133
- * `ids`: the new layout.
134
- */
135
- const wrapInLayout = (ids, kind, columns = ids.length) => {
136
- const blocks = ids.map(ref);
137
- const v = view();
138
- if (!Number.isInteger(columns) || columns < 2 || blocks.length < 1)
139
- return REFUSED;
140
- if (columns < blocks.length || !canPlace(blocks))
141
- return REFUSED;
142
- const parent = positionOf(blocks[0]).parent;
143
- if (blocks.some((id) => positionOf(id).parent !== parent))
144
- return REFUSED;
145
- if (blocks.some((id) => isLayoutItem(id) || holdsLayout(id)))
146
- return REFUSED;
147
- const wrap = layoutKind(kind && ref(kind));
148
- const item = wrap === undefined ? undefined : roles.of(wrap).layout;
149
- if (wrap === undefined || item === undefined || !fits(parent, wrap))
150
- return REFUSED;
151
- if (parent !== null && (insideItem(parent, v) || isIsland(parent) || insideIsland(parent, v)))
152
- return REFUSED;
153
- if (blocks.some((id) => !fitsIn(item, blockTypeOf(id))))
154
- return REFUSED;
155
- const fill = roles.of(item).child;
156
- if (columns > blocks.length && !fitsIn(item, fill))
157
- return REFUSED;
158
- const ordered = blocks.toSorted((a, b) => positionOf(a).index - positionOf(b).index);
159
- const items = Array.from({ length: columns }, (_, i) => i < ordered.length ? spec(item) : { ...spec(item), children: [spec(fill)] });
160
- const wrapper = { ...spec(wrap), children: items };
161
- const at = positionOf(ordered[0]).index;
162
- const writes = [
163
- { op: 'insertBlocks', parent, index: at, specs: [wrapper] },
164
- ...ordered.flatMap((id, i) => moveTo([id], items[i].id, 0))
165
- ];
166
- const kept = new Set([
167
- wrapper.id,
168
- ...items.map((it) => it.id),
169
- ...landing(parent)
170
- ]);
171
- return plan([wrapper.id], emptied(ordered, writes, kept));
172
- };
173
- return {
174
- besideAt,
175
- placeBeside,
176
- wrapInLayout
177
- };
178
- };
@@ -1,19 +0,0 @@
1
- import { type JSONBlock, type JSONInlineBlock, type JSONText } from '../../utils/json.js';
2
- import { type DataPatch } from '../data.js';
3
- import { type BlockId, type BlockSpec, type ContentItem } from '../placement/model.js';
4
- import type { Prepared, PlanStep, DataTarget } from './types.js';
5
- import type { OpsContext } from './steps.js';
6
- /** The meta ops of one facade, prepared. */
7
- export declare const metaOps: (c: OpsContext) => {
8
- dataSteps: (target: DataTarget, patches: DataPatch[]) => PlanStep[] | null;
9
- replaceData: (data: unknown) => DataPatch[];
10
- setBlockType: (id: BlockId, type: string) => Prepared;
11
- patchData: (target: DataTarget, patches: readonly DataPatch[]) => Prepared;
12
- setBlock: (id: BlockId, value: {
13
- type?: string;
14
- data?: Record<string, unknown>;
15
- content?: (ContentItem | JSONText | JSONInlineBlock)[];
16
- children?: (BlockSpec | JSONBlock)[];
17
- }) => Prepared;
18
- };
19
- export type MetaOps = ReturnType<typeof metaOps>;
@@ -1,198 +0,0 @@
1
- /**
2
- * A block's meta: its type (`setBlockType`, the steps a retype implies) and
3
- * its data, a block's, an atom's or the document's (`patchData`, the data
4
- * steps of a patch), and the baseline `setBlock`.
5
- */
6
- import { randOf } from '../rand.js';
7
- import { TYPE } from '../schema.js';
8
- import { asBlockSpec, asContentItem, sanitizeSpec, sanitizeWireJson } from '../../utils/json.js';
9
- import { patchWrites, leafKey, isObject } from '../data.js';
10
- import { collides } from '../placement/model.js';
11
- import { ref, refused, sanitizeItem } from './plan.js';
12
- /** The meta ops of one facade, prepared. */
13
- export const metaOps = (c) => {
14
- const { doc, roles, roleOf, dataNode, view, blockTypeOf, blockDataOf, childrenIds, positionOf, isLines, isVoid, displayLength, live, contentTarget, REFUSED, plan, attr, leavingIsland, settle, remove, atomOf, writeContent } = c;
15
- /**
16
- * The data step of `patches` (sanitized) on a block, one of its atoms or
17
- * the document (`crdt/data.ts`): none when it changes nothing, `null`
18
- * when a patch is refused.
19
- */
20
- const dataSteps = (target, patches) => {
21
- const [id, inlineId] = typeof target === 'object' && target ? [target.block, target.atom] : [target ?? undefined];
22
- const node = dataNode(id, inlineId);
23
- // A block kind's atomic paths are written as one leaf each (`data.atomic`).
24
- const type = inlineId === undefined && id !== undefined ? blockTypeOf(id) : undefined;
25
- const atomic = (type === undefined ? undefined : roleOf(type)?.atomic) ?? [];
26
- const writes = node
27
- ? patchWrites(node, patches, doc.clientID, randOf(doc), atomic.map((p) => leafKey(typeof p === 'string' ? [p] : p)))
28
- : [];
29
- if (writes === null)
30
- return null; // a patch fits no value there
31
- if (writes.length === 0)
32
- return [];
33
- const offset = inlineId === undefined ? undefined : atomOf(id, inlineId)?.at;
34
- return [
35
- {
36
- op: 'patchData',
37
- ...(id !== undefined && { id }),
38
- ...(inlineId !== undefined && { inlineId, offset }),
39
- ops: patches,
40
- writes
41
- }
42
- ];
43
- };
44
- /** A whole-data replace: a patch of the root. */
45
- const replaceData = (data) => [{ path: [], value: sanitizeWireJson(data) }];
46
- /**
47
- * `data`'s leaves as sets over `current` (`data.retype.keep`): a
48
- * plain object's keys, recursively where `current` holds an object
49
- * there too; anything else (an array, a primitive, an empty object, an
50
- * object where `current` holds none) as one value at its path. Nothing
51
- * else under the root is touched.
52
- */
53
- const leafSets = (data, current) => {
54
- const out = [];
55
- const walk = (v, at, path) => {
56
- if (isObject(v) && Object.keys(v).length > 0 && (path.length === 0 || isObject(at)))
57
- for (const [k, x] of Object.entries(v))
58
- walk(x, isObject(at) ? at[k] : undefined, [...path, k]);
59
- else if (path.length > 0)
60
- out.push({ path, value: v });
61
- };
62
- walk(sanitizeWireJson(data), current, []);
63
- return out;
64
- };
65
- /**
66
- * The target role decides: nothing renders a void's children,
67
- * so a block retyped to a void kind hands them to the slot right after
68
- * it, as the kind they show there (`settledKind`), in their order (where
69
- * the read-time shedding shows a child a peer adds meanwhile). An island
70
- * declared `lines` retyped to an ordinary kind keeps its lines, each
71
- * retyped to the new kind's default child (the document's where that
72
- * renders no content): no line kind outside its island. Any other
73
- * island's children keep their kinds (a table's rows stay rows), as
74
- * `typeOf` shows a child a peer adds meanwhile.
75
- */
76
- const retypeSteps = (id, type) => {
77
- const { kids } = view();
78
- const pos = positionOf(id);
79
- const steps = attr(id, TYPE, type);
80
- const moved = kids.get(id) ?? [];
81
- // A code block retyped to an ordinary kind keeps its lines as that kind's children
82
- // (`leavingIsland`: never a kind that hides their text).
83
- const to = roles.of(type);
84
- if (isLines(id) && !to.island && !to.childless) {
85
- return [
86
- ...steps,
87
- ...moved.flatMap((k) => {
88
- const kind = leavingIsland(id, blockTypeOf(k.id), to.child);
89
- return kind === undefined ? [] : attr(k.id, TYPE, kind);
90
- })
91
- ];
92
- }
93
- if (!to.childless || moved.length === 0 || pos === null)
94
- return steps;
95
- // The children keep their home slots: the read shows them in the void's
96
- // slot (`conc.void-children`), settled for its parent.
97
- return [
98
- ...steps,
99
- ...settle(id, moved.map((k) => k.id), pos.parent)
100
- ];
101
- };
102
- /** Set the block type (attr write — the block keeps its identity; see `retypeSteps`). */
103
- const setBlockType = (id, type) => {
104
- id = ref(id);
105
- return live(id) ? plan([id], retypeSteps(id, ref(type))) : REFUSED;
106
- };
107
- /**
108
- * Patch the data of a block, one of its inline atoms or the document
109
- * (`null`), in order (`crdt/data.ts`): each patch replaces the value at
110
- * its path (`value` absent deletes it), `splice` and `order` edit the
111
- * array there; a path addresses array items by index, resolved here
112
- * to the items' ids. Only the leaves it changes are written, so a
113
- * peer's edit of another key or item is kept. Refused for an absent
114
- * target, a path that is no array of strings, a root value that is no
115
- * object, or an op that fits no value at its path.
116
- */
117
- const patchData = (target, patches) => {
118
- const valid = (p) => Array.isArray(p?.path) &&
119
- p.path.every((k) => typeof k === 'string') &&
120
- (p.path.length > 0 || p.value === undefined || isObject(p.value));
121
- if (!Array.isArray(patches) || !patches.every(valid))
122
- return REFUSED;
123
- // Only the fields a patch has: an absent one is no non-JSON value to report.
124
- const clean = patches.map(({ path, value, splice, order }) => sanitizeWireJson({
125
- path,
126
- ...(value !== undefined && { value }),
127
- ...(splice !== undefined && { splice }),
128
- ...(order !== undefined && { order })
129
- }));
130
- const t = target === null
131
- ? null
132
- : typeof target === 'string'
133
- ? ref(target)
134
- : { block: ref(target.block), atom: ref(target.atom) };
135
- if (typeof t === 'string' ? !live(t) : t !== null && atomOf(t.block, t.atom) === undefined)
136
- return REFUSED;
137
- const steps = dataSteps(t, clean);
138
- return steps ? plan(t === null ? [] : [typeof t === 'string' ? t : t.block], steps) : REFUSED;
139
- };
140
- /**
141
- * Baseline `setBlock`: `type` updates the block in place and `data`
142
- * sets the leaves it names, removing none (`data.retype.keep`: a
143
- * retype keeps the block's properties, as Notion's Turn into does;
144
- * `setBlockData` replaces them); `content`/`children` REPLACE
145
- * wholesale — explicit replacement is a
146
- * new-identity operation; a retype to a void kind without `children`
147
- * unnests the current ones (`retypeSteps`). All-or-nothing: children
148
- * for a block that is `void` after the write, or a replacement id that
149
- * is already taken (a live or deleted
150
- * block, the replaced children included, or a duplicate inside the
151
- * replacement), refuses before any write — `id-collision` for the
152
- * latter. A caller wanting a child back re-creates it with a fresh id.
153
- */
154
- const setBlock = (id, value) => {
155
- id = ref(id);
156
- const content = value.content?.map((item) => sanitizeItem(asContentItem(item)));
157
- const children = value.children?.map((child) => sanitizeSpec(asBlockSpec(child)));
158
- if (!live(id) || (content !== undefined && !contentTarget(id)))
159
- return REFUSED;
160
- const type = value.type === undefined ? undefined : ref(value.type);
161
- const toVoid = type === undefined ? isVoid(id) : roles.of(type).childless;
162
- if (children?.length && toVoid)
163
- return REFUSED;
164
- if (children !== undefined && collides(doc, children))
165
- return refused('id-collision');
166
- const data = value.data === undefined ? [] : dataSteps(id, leafSets(value.data, blockDataOf(id)));
167
- if (data === null)
168
- return REFUSED;
169
- const writes = [
170
- ...(type === undefined
171
- ? []
172
- : children === undefined
173
- ? retypeSteps(id, type)
174
- : attr(id, TYPE, type)),
175
- ...data
176
- ];
177
- if (content !== undefined) {
178
- const length = displayLength(id);
179
- if (length > 0)
180
- writes.push({ op: 'deleteText', id, offset: 0, length });
181
- writeContent(writes, id, 0, content);
182
- }
183
- if (children !== undefined) {
184
- for (const kid of childrenIds(id))
185
- writes.push(remove(kid));
186
- if (children.length > 0)
187
- writes.push({ op: 'insertBlocks', parent: id, index: 0, specs: children });
188
- }
189
- return plan([id], writes);
190
- };
191
- return {
192
- dataSteps,
193
- replaceData,
194
- setBlockType,
195
- patchData,
196
- setBlock
197
- };
198
- };
@@ -1,21 +0,0 @@
1
- import { type JSONBlock } from '../../utils/json.js';
2
- import { type BlockId, type BlockSpec, type Destination } from '../placement/model.js';
3
- import type { Prepared } from './types.js';
4
- import { type OpsContext } from './steps.js';
5
- /** The moves ops of one facade, prepared. */
6
- export declare const moveOps: (c: OpsContext) => {
7
- insertBlocks: (dest: Destination, specs: readonly (BlockSpec | JSONBlock)[]) => Prepared;
8
- moveBlocks: (ids: readonly BlockId[], dest: Destination) => Prepared;
9
- unNestBlocks: (ids: readonly BlockId[]) => Prepared;
10
- unNestBlock: (id: BlockId) => Prepared;
11
- landingOf: (id: BlockId, kind: string, after?: boolean) => {
12
- parent: string | null;
13
- levels: string[];
14
- };
15
- liftOut: (id: BlockId, kind: string, { keep, after }?: {
16
- keep?: boolean;
17
- after?: readonly BlockSpec[];
18
- }) => Prepared;
19
- duplicateBlock: (id: BlockId, freshId?: (oldId: string, kind: "block" | "inline") => string) => Prepared;
20
- };
21
- export type MoveOps = ReturnType<typeof moveOps>;