@arbidocs/coedit 0.5.20 → 0.5.22

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.
@@ -2,6 +2,28 @@ import * as _docx_editor_dev_core_store from '@docx-editor.dev/core/store';
2
2
  import { OoxmlPackage } from '@docx-editor.dev/core/store';
3
3
  import * as Y from 'yjs';
4
4
 
5
+ /**
6
+ * Elements of the mirror, in both directions: plain `XmlEl` objects to Yjs types and back, and
7
+ * `XmlEl` to XML text.
8
+ *
9
+ * Three forms:
10
+ *
11
+ * - A paragraph is held as a rich paragraph (see rich.ts) whenever it can be represented exactly:
12
+ * its properties stay in `children`, and its inline content is the single Y.Text `inline`.
13
+ * - An element with no editable text anywhere below it (a style, a numbering definition, a font, a
14
+ * setting, a theme, paragraph or table properties, a section's setup) is packed: the map holds
15
+ * its whole subtree as one XML string under `xml`. It is opened into a plain tree when an edit
16
+ * touches it and written back whole, so concurrent edits to the same packed element are last
17
+ * writer wins, while different elements (two styles) still merge. Held as a tree, every element,
18
+ * attribute and namespace URI was its own CRDT item: a 350 KB styles part took 3.2 MB of room
19
+ * state and over 100 MB of heap to load.
20
+ * - Every other element (the body, a table, a cell, a footnote: the path to editable text) is a
21
+ * generic map with a `children` array.
22
+ */
23
+
24
+ /** Prefixes in scope for an element; innermost binding wins. */
25
+ type Scope = ReadonlyMap<string, string>;
26
+
5
27
  /**
6
28
  * Identity registry between core's logical node ids and the Yjs mirror.
7
29
  *
@@ -14,10 +36,12 @@ import * as Y from 'yjs';
14
36
  * under construction, detached, or about to move are therefore held as plain objects and converted
15
37
  * to Yjs types when they are attached to a live parent. `snapshot` is the live to plain direction.
16
38
  *
17
- * Rich paragraphs (see rich.ts) hold their runs as attributes on one Y.Text, so their inner nodes
18
- * have no Yjs type of their own. The first effect that touches one opens it: it is read into a plain
19
- * tree, the journal's effects change that tree, and `flushRich` writes the result back as a minimal
20
- * edit of the text. The tree stays open, mirroring the editor's own, until a remote change re-reads it.
39
+ * Rich paragraphs (see rich.ts) hold their runs as attributes on one Y.Text, and packed elements
40
+ * (see mirror.ts) hold their subtree as one XML string, so their inner nodes have no Yjs type of
41
+ * their own. The first effect that touches one opens it: it is read into a plain tree, the journal's
42
+ * effects change that tree, and `flushRich` writes the result back (a minimal edit of the text, or
43
+ * the packed element's new XML). The tree stays open, mirroring the editor's own, until a remote
44
+ * change re-reads it.
21
45
  */
22
46
 
23
47
  interface PlainElement {
@@ -69,7 +93,7 @@ declare class NodeRegistry {
69
93
  /**
70
94
  * Walk a mirror root and register every node under its positional id, `${part}#0` for the root and
71
95
  * `${parent}.${index}` below it. This is the id scheme core assigns when it reads a part. A rich
72
- * paragraph is registered as one node; its inner nodes appear when it is opened.
96
+ * paragraph or a packed element is registered as one node; its inner nodes appear when it is opened.
73
97
  */
74
98
  registerTree(partName: string, root: Y.Map<unknown>): void;
75
99
  private own;
@@ -85,10 +109,10 @@ declare class NodeRegistry {
85
109
  touch(node: PlainNode): void;
86
110
  /** Nodes just placed under `parent` belong to the same paragraph as it, if any. */
87
111
  adopt(parent: PlainElement, children: readonly PlainNode[]): void;
88
- /** Write every changed open paragraph back into the mirror. Call at the end of a journal. */
112
+ /** Write every changed open paragraph or packed element back into the mirror. Call at the end of a journal. */
89
113
  flushRich(): void;
90
114
  private writeBack;
91
- /** The paragraph can no longer be held as rich text: store it as plain elements instead. */
115
+ /** The paragraph can no longer be held as rich text, or the element packed: store it as a tree. */
92
116
  private degrade;
93
117
  /** Deep copy of a node into plain objects carrying the same ids. */
94
118
  snapshot(id: string): PlainNode;
@@ -99,17 +123,28 @@ declare class NodeRegistry {
99
123
  */
100
124
  detach(node: LiveNode): void;
101
125
  private detachInto;
102
- /** Build Yjs types for a plain subtree and register every node as live. */
103
- toLive(plain: PlainNode): LiveNode;
126
+ /**
127
+ * Build Yjs types for a plain subtree placed where `scope` is in force, and register every node as
128
+ * live. A rich paragraph or packed element stays open on the plain tree, which is already current.
129
+ * A part's `root` is never packed.
130
+ */
131
+ toLive(plain: PlainNode, scope?: Scope, root?: boolean): LiveNode;
104
132
  private toGenericLive;
105
133
  /**
106
- * A live node that can be inserted into a parent. Anything not yet attached is built fresh; an
107
- * attached node is copied, because a Yjs type cannot have two parents.
134
+ * A live node that can be inserted into a parent (`scope` is in force there), or a part's `root`.
135
+ * Anything not yet attached is built fresh; an attached node is copied, because a Yjs type cannot
136
+ * have two parents.
108
137
  */
109
- attachable(id: string): LiveNode;
138
+ attachable(id: string, scope?: Scope, root?: boolean): LiveNode;
110
139
  }
111
140
 
112
- declare const COLLAB_FORMAT = "arbi-docx/1";
141
+ /**
142
+ * The room format this build writes. `arbi-docx/2` holds elements without editable text packed (see
143
+ * mirror.ts); it reads `arbi-docx/1` rooms, whose elements are all trees, unchanged. A build that
144
+ * only knows `/1` cannot read a packed element, so the client handshake requires this exact format.
145
+ */
146
+ declare const COLLAB_FORMAT = "arbi-docx/2";
147
+ declare function isReadableFormat(format: string | undefined): boolean;
113
148
  interface AttrJson {
114
149
  ns: string;
115
150
  local: string;
@@ -150,6 +185,7 @@ interface CanonicalText {
150
185
  readonly kind: 'textValue';
151
186
  readonly value: string;
152
187
  }
188
+ /** The Yjs form of a part's root element. */
153
189
  declare function encodeElement(el: CanonicalElement): Y.Map<unknown>;
154
190
  declare function encodeText(value: string): Y.Text;
155
191
  /** Seed an empty Yjs document from a loaded package. Call once, inside a transaction. */
@@ -191,4 +227,4 @@ declare function materializeBytes(doc: Y.Doc): Uint8Array;
191
227
  /** Load DOCX bytes and seed an empty room from them. */
192
228
  declare function bootstrapFromBytes(doc: Y.Doc, bytes: Uint8Array): OoxmlPackage;
193
229
 
194
- export { type AttrJson, type BindingJson, COLLAB_FORMAT, MaterializeCache, MaterializeError, type RelationshipJson, bootstrap, bootstrapFromBytes, contentTypeOf, encodeElement, encodeText, isBootstrapped, materialize, materializeBytes, readFormat, registerMirror, serializePart };
230
+ export { type AttrJson, type BindingJson, COLLAB_FORMAT, MaterializeCache, MaterializeError, type RelationshipJson, bootstrap, bootstrapFromBytes, contentTypeOf, encodeElement, encodeText, isBootstrapped, isReadableFormat, materialize, materializeBytes, readFormat, registerMirror, serializePart };
@@ -2,6 +2,28 @@ import * as _docx_editor_dev_core_store from '@docx-editor.dev/core/store';
2
2
  import { OoxmlPackage } from '@docx-editor.dev/core/store';
3
3
  import * as Y from 'yjs';
4
4
 
5
+ /**
6
+ * Elements of the mirror, in both directions: plain `XmlEl` objects to Yjs types and back, and
7
+ * `XmlEl` to XML text.
8
+ *
9
+ * Three forms:
10
+ *
11
+ * - A paragraph is held as a rich paragraph (see rich.ts) whenever it can be represented exactly:
12
+ * its properties stay in `children`, and its inline content is the single Y.Text `inline`.
13
+ * - An element with no editable text anywhere below it (a style, a numbering definition, a font, a
14
+ * setting, a theme, paragraph or table properties, a section's setup) is packed: the map holds
15
+ * its whole subtree as one XML string under `xml`. It is opened into a plain tree when an edit
16
+ * touches it and written back whole, so concurrent edits to the same packed element are last
17
+ * writer wins, while different elements (two styles) still merge. Held as a tree, every element,
18
+ * attribute and namespace URI was its own CRDT item: a 350 KB styles part took 3.2 MB of room
19
+ * state and over 100 MB of heap to load.
20
+ * - Every other element (the body, a table, a cell, a footnote: the path to editable text) is a
21
+ * generic map with a `children` array.
22
+ */
23
+
24
+ /** Prefixes in scope for an element; innermost binding wins. */
25
+ type Scope = ReadonlyMap<string, string>;
26
+
5
27
  /**
6
28
  * Identity registry between core's logical node ids and the Yjs mirror.
7
29
  *
@@ -14,10 +36,12 @@ import * as Y from 'yjs';
14
36
  * under construction, detached, or about to move are therefore held as plain objects and converted
15
37
  * to Yjs types when they are attached to a live parent. `snapshot` is the live to plain direction.
16
38
  *
17
- * Rich paragraphs (see rich.ts) hold their runs as attributes on one Y.Text, so their inner nodes
18
- * have no Yjs type of their own. The first effect that touches one opens it: it is read into a plain
19
- * tree, the journal's effects change that tree, and `flushRich` writes the result back as a minimal
20
- * edit of the text. The tree stays open, mirroring the editor's own, until a remote change re-reads it.
39
+ * Rich paragraphs (see rich.ts) hold their runs as attributes on one Y.Text, and packed elements
40
+ * (see mirror.ts) hold their subtree as one XML string, so their inner nodes have no Yjs type of
41
+ * their own. The first effect that touches one opens it: it is read into a plain tree, the journal's
42
+ * effects change that tree, and `flushRich` writes the result back (a minimal edit of the text, or
43
+ * the packed element's new XML). The tree stays open, mirroring the editor's own, until a remote
44
+ * change re-reads it.
21
45
  */
22
46
 
23
47
  interface PlainElement {
@@ -69,7 +93,7 @@ declare class NodeRegistry {
69
93
  /**
70
94
  * Walk a mirror root and register every node under its positional id, `${part}#0` for the root and
71
95
  * `${parent}.${index}` below it. This is the id scheme core assigns when it reads a part. A rich
72
- * paragraph is registered as one node; its inner nodes appear when it is opened.
96
+ * paragraph or a packed element is registered as one node; its inner nodes appear when it is opened.
73
97
  */
74
98
  registerTree(partName: string, root: Y.Map<unknown>): void;
75
99
  private own;
@@ -85,10 +109,10 @@ declare class NodeRegistry {
85
109
  touch(node: PlainNode): void;
86
110
  /** Nodes just placed under `parent` belong to the same paragraph as it, if any. */
87
111
  adopt(parent: PlainElement, children: readonly PlainNode[]): void;
88
- /** Write every changed open paragraph back into the mirror. Call at the end of a journal. */
112
+ /** Write every changed open paragraph or packed element back into the mirror. Call at the end of a journal. */
89
113
  flushRich(): void;
90
114
  private writeBack;
91
- /** The paragraph can no longer be held as rich text: store it as plain elements instead. */
115
+ /** The paragraph can no longer be held as rich text, or the element packed: store it as a tree. */
92
116
  private degrade;
93
117
  /** Deep copy of a node into plain objects carrying the same ids. */
94
118
  snapshot(id: string): PlainNode;
@@ -99,17 +123,28 @@ declare class NodeRegistry {
99
123
  */
100
124
  detach(node: LiveNode): void;
101
125
  private detachInto;
102
- /** Build Yjs types for a plain subtree and register every node as live. */
103
- toLive(plain: PlainNode): LiveNode;
126
+ /**
127
+ * Build Yjs types for a plain subtree placed where `scope` is in force, and register every node as
128
+ * live. A rich paragraph or packed element stays open on the plain tree, which is already current.
129
+ * A part's `root` is never packed.
130
+ */
131
+ toLive(plain: PlainNode, scope?: Scope, root?: boolean): LiveNode;
104
132
  private toGenericLive;
105
133
  /**
106
- * A live node that can be inserted into a parent. Anything not yet attached is built fresh; an
107
- * attached node is copied, because a Yjs type cannot have two parents.
134
+ * A live node that can be inserted into a parent (`scope` is in force there), or a part's `root`.
135
+ * Anything not yet attached is built fresh; an attached node is copied, because a Yjs type cannot
136
+ * have two parents.
108
137
  */
109
- attachable(id: string): LiveNode;
138
+ attachable(id: string, scope?: Scope, root?: boolean): LiveNode;
110
139
  }
111
140
 
112
- declare const COLLAB_FORMAT = "arbi-docx/1";
141
+ /**
142
+ * The room format this build writes. `arbi-docx/2` holds elements without editable text packed (see
143
+ * mirror.ts); it reads `arbi-docx/1` rooms, whose elements are all trees, unchanged. A build that
144
+ * only knows `/1` cannot read a packed element, so the client handshake requires this exact format.
145
+ */
146
+ declare const COLLAB_FORMAT = "arbi-docx/2";
147
+ declare function isReadableFormat(format: string | undefined): boolean;
113
148
  interface AttrJson {
114
149
  ns: string;
115
150
  local: string;
@@ -150,6 +185,7 @@ interface CanonicalText {
150
185
  readonly kind: 'textValue';
151
186
  readonly value: string;
152
187
  }
188
+ /** The Yjs form of a part's root element. */
153
189
  declare function encodeElement(el: CanonicalElement): Y.Map<unknown>;
154
190
  declare function encodeText(value: string): Y.Text;
155
191
  /** Seed an empty Yjs document from a loaded package. Call once, inside a transaction. */
@@ -191,4 +227,4 @@ declare function materializeBytes(doc: Y.Doc): Uint8Array;
191
227
  /** Load DOCX bytes and seed an empty room from them. */
192
228
  declare function bootstrapFromBytes(doc: Y.Doc, bytes: Uint8Array): OoxmlPackage;
193
229
 
194
- export { type AttrJson, type BindingJson, COLLAB_FORMAT, MaterializeCache, MaterializeError, type RelationshipJson, bootstrap, bootstrapFromBytes, contentTypeOf, encodeElement, encodeText, isBootstrapped, materialize, materializeBytes, readFormat, registerMirror, serializePart };
230
+ export { type AttrJson, type BindingJson, COLLAB_FORMAT, MaterializeCache, MaterializeError, type RelationshipJson, bootstrap, bootstrapFromBytes, contentTypeOf, encodeElement, encodeText, isBootstrapped, isReadableFormat, materialize, materializeBytes, readFormat, registerMirror, serializePart };
package/dist/encoding.js CHANGED
@@ -312,6 +312,20 @@ function writeUnits(text, units) {
312
312
  function isRichParagraph(map) {
313
313
  return map.has("inline");
314
314
  }
315
+ var PACKED = "xml";
316
+ function isPackedElement(map) {
317
+ return map.has(PACKED);
318
+ }
319
+ var TEXT_BEARING = /* @__PURE__ */ new Set(["p", "t", "delText", "instrText", "delInstrText"]);
320
+ function packable(xml) {
321
+ if (xml.ns === WML && TEXT_BEARING.has(xml.local)) return false;
322
+ return xml.children.every((c) => typeof c === "string" || packable(c));
323
+ }
324
+ function createPacked(xml, scope) {
325
+ const map = new Y.Map();
326
+ map.set(PACKED, serializeXml(xml, scope));
327
+ return map;
328
+ }
315
329
  function isParagraphEl(el) {
316
330
  return el.ns === WML && el.local === "p";
317
331
  }
@@ -331,57 +345,47 @@ function newText(value) {
331
345
  text.insert(0, value);
332
346
  return text;
333
347
  }
334
- function createRichParagraph(xml, encoded) {
348
+ function createRichParagraph(xml, encoded, scope = /* @__PURE__ */ new Map()) {
335
349
  const map = new Y.Map();
336
350
  fieldsOf(map, xml);
351
+ const inner = withBindings(scope, xml.bindings);
337
352
  const children = new Y.Array();
338
- children.push(encoded.head.map((el) => xmlToLive(el)));
353
+ children.push(encoded.head.map((el) => xmlToLive(el, inner)));
339
354
  map.set("children", children);
340
355
  const inline = new Y.Text();
341
356
  writeUnits(inline, encoded.units);
342
357
  map.set("inline", inline);
343
358
  return map;
344
359
  }
345
- function xmlToLive(xml) {
360
+ function withBindings(scope, bindings) {
361
+ const inner = new Map(scope);
362
+ for (const b of bindings) inner.set(b.prefix, b.ns);
363
+ return inner;
364
+ }
365
+ function xmlToLive(xml, scope = /* @__PURE__ */ new Map(), root = false) {
346
366
  if (isParagraphEl(xml)) {
347
367
  const encoded = encodeParagraph(xml.children);
348
- if (encoded) return createRichParagraph(xml, encoded);
368
+ if (encoded) return createRichParagraph(xml, encoded, scope);
349
369
  }
350
- return xmlToGenericLive(xml);
351
- }
352
- function xmlToGenericLive(xml) {
370
+ if (!root && packable(xml)) return createPacked(xml, scope);
353
371
  const map = new Y.Map();
354
372
  fieldsOf(map, xml);
373
+ const inner = withBindings(scope, xml.bindings);
355
374
  const children = new Y.Array();
356
- children.push(xml.children.map((c) => typeof c === "string" ? newText(c) : xmlToLive(c)));
375
+ children.push(xml.children.map((c) => typeof c === "string" ? newText(c) : xmlToLive(c, inner)));
357
376
  map.set("children", children);
358
377
  return map;
359
378
  }
360
- function liveToXml(map) {
361
- const prefix = map.get("prefix");
362
- const stored = map.get("children").toArray().map((child) => child instanceof Y.Text ? child.toString() : liveToXml(child));
363
- const children = isRichParagraph(map) ? [...stored, ...decodeInline(readUnits(map.get("inline")))] : stored;
364
- const xml = {
365
- ns: map.get("ns"),
366
- local: map.get("local"),
367
- attrs: map.get("attrs").toArray().map((a) => ({ ...a })),
368
- bindings: map.get("bindings").toArray().map((b) => ({ ...b })),
369
- children
370
- };
371
- if (prefix !== void 0) xml.prefix = prefix;
372
- return xml;
373
- }
374
379
  var escapeText = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
375
380
  var escapeAttr = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/\t/g, "&#9;").replace(/\n/g, "&#10;").replace(/\r/g, "&#13;");
376
381
  var qualified = (prefix, local) => prefix ? `${prefix}:${local}` : local;
377
- function serializeXml(el, scope) {
378
- const inScope = new Map(scope);
379
- for (const b of el.bindings) inScope.set(b.prefix, b.ns);
380
- let out = `<${qualified(el.prefix, el.local)}`;
381
- for (const b of el.bindings) {
382
+ function startTag(prefix, local, bindings, attrs, scope) {
383
+ const inScope = withBindings(scope, bindings);
384
+ let out = `<${qualified(prefix, local)}`;
385
+ for (const b of bindings) {
382
386
  out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
383
387
  }
384
- for (const a of el.attrs) {
388
+ for (const a of attrs) {
385
389
  let p = a.prefix;
386
390
  if (a.ns !== "" && !p) {
387
391
  for (const [candidate, uri] of inScope) {
@@ -393,48 +397,41 @@ function serializeXml(el, scope) {
393
397
  }
394
398
  out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
395
399
  }
400
+ return [out, inScope];
401
+ }
402
+ function serializeXml(el, scope) {
403
+ const [out, inScope] = startTag(el.prefix, el.local, el.bindings, el.attrs, scope);
396
404
  if (el.children.length === 0) return `${out}/>`;
397
- out += ">";
405
+ let body = "";
398
406
  for (const child of el.children) {
399
- out += typeof child === "string" ? escapeText(child) : serializeXml(child, inScope);
407
+ body += typeof child === "string" ? escapeText(child) : serializeXml(child, inScope);
400
408
  }
401
- return `${out}</${qualified(el.prefix, el.local)}>`;
409
+ return `${out}>${body}</${qualified(el.prefix, el.local)}>`;
402
410
  }
403
411
  function serializeLive(el, scope) {
404
- if (isRichParagraph(el)) return serializeXml(liveToXml(el), scope);
412
+ if (isPackedElement(el)) return el.get(PACKED);
405
413
  const prefix = el.get("prefix");
406
414
  const local = el.get("local");
407
415
  const bindings = el.get("bindings").toArray();
408
416
  const attrs = el.get("attrs").toArray();
409
417
  const children = el.get("children").toArray();
410
- const inScope = new Map(scope);
411
- for (const b of bindings) inScope.set(b.prefix, b.ns);
412
- let out = `<${qualified(prefix, local)}`;
413
- for (const b of bindings) {
414
- out += b.prefix === "" ? ` xmlns="${escapeAttr(b.ns)}"` : ` xmlns:${b.prefix}="${escapeAttr(b.ns)}"`;
415
- }
416
- for (const a of attrs) {
417
- let p = a.prefix;
418
- if (a.ns !== "" && !p) {
419
- for (const [candidate, uri] of inScope) {
420
- if (candidate !== "" && uri === a.ns) {
421
- p = candidate;
422
- break;
423
- }
424
- }
425
- }
426
- out += ` ${qualified(p, a.local)}="${escapeAttr(a.value)}"`;
427
- }
428
- if (children.length === 0) return `${out}/>`;
429
- out += ">";
418
+ const [out, inScope] = startTag(prefix, local, bindings, attrs, scope);
419
+ const inline = isRichParagraph(el) ? decodeInline(readUnits(el.get("inline"))) : [];
420
+ if (children.length === 0 && inline.length === 0) return `${out}/>`;
421
+ let body = "";
430
422
  for (const child of children) {
431
- out += child instanceof Y.Text ? escapeText(child.toString()) : serializeLive(child, inScope);
423
+ body += child instanceof Y.Text ? escapeText(child.toString()) : serializeLive(child, inScope);
432
424
  }
433
- return `${out}</${qualified(prefix, local)}>`;
425
+ for (const child of inline) body += serializeXml(child, inScope);
426
+ return `${out}>${body}</${qualified(prefix, local)}>`;
434
427
  }
435
428
 
436
429
  // src/encoding.ts
437
- var COLLAB_FORMAT = "arbi-docx/1";
430
+ var COLLAB_FORMAT = "arbi-docx/2";
431
+ var READABLE_FORMATS = /* @__PURE__ */ new Set(["arbi-docx/1", COLLAB_FORMAT]);
432
+ function isReadableFormat(format) {
433
+ return format !== void 0 && READABLE_FORMATS.has(format);
434
+ }
438
435
  function isText(node) {
439
436
  return node.kind === "textValue";
440
437
  }
@@ -455,7 +452,7 @@ function canonicalToXml(node) {
455
452
  return xml;
456
453
  }
457
454
  function encodeElement(el) {
458
- return xmlToLive(canonicalToXml(el));
455
+ return xmlToLive(canonicalToXml(el), /* @__PURE__ */ new Map(), true);
459
456
  }
460
457
  function encodeText(value) {
461
458
  return newText(value);
@@ -711,6 +708,6 @@ function bootstrapFromBytes(doc, bytes) {
711
708
  return result.package;
712
709
  }
713
710
 
714
- export { COLLAB_FORMAT, MaterializeCache, MaterializeError, bootstrap, bootstrapFromBytes, contentTypeOf, encodeElement, encodeText, isBootstrapped, materialize, materializeBytes, readFormat, registerMirror, serializePart };
711
+ export { COLLAB_FORMAT, MaterializeCache, MaterializeError, bootstrap, bootstrapFromBytes, contentTypeOf, encodeElement, encodeText, isBootstrapped, isReadableFormat, materialize, materializeBytes, readFormat, registerMirror, serializePart };
715
712
  //# sourceMappingURL=encoding.js.map
716
713
  //# sourceMappingURL=encoding.js.map