@google-cloud/kcmd 0.0.0-stage → 0.2.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 (150) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +264 -2
  3. package/build/ts/kcmd/cli.js +20252 -0
  4. package/build/ts/kcmd/gcp/alloydb.d.ts +12 -0
  5. package/build/ts/kcmd/gcp/alloydb.js +70 -0
  6. package/build/ts/kcmd/gcp/api.d.ts +29 -0
  7. package/build/ts/kcmd/gcp/api.js +162 -0
  8. package/build/ts/kcmd/gcp/biglake.d.ts +18 -0
  9. package/build/ts/kcmd/gcp/biglake.js +59 -0
  10. package/build/ts/kcmd/gcp/bigquery.d.ts +57 -0
  11. package/build/ts/kcmd/gcp/bigquery.js +134 -0
  12. package/build/ts/kcmd/gcp/context.d.ts +10 -0
  13. package/build/ts/kcmd/gcp/context.js +74 -0
  14. package/build/ts/kcmd/gcp/crm.d.ts +13 -0
  15. package/build/ts/kcmd/gcp/crm.js +91 -0
  16. package/build/ts/kcmd/gcp/dataplex.d.ts +147 -0
  17. package/build/ts/kcmd/gcp/dataplex.js +406 -0
  18. package/build/ts/kcmd/gcp/index.d.ts +3 -0
  19. package/build/ts/kcmd/gcp/index.js +21 -0
  20. package/build/ts/kcmd/gcp/spanner.d.ts +73 -0
  21. package/build/ts/kcmd/gcp/spanner.js +126 -0
  22. package/build/ts/kcmd/index.d.ts +7 -0
  23. package/build/ts/kcmd/index.js +46 -0
  24. package/build/ts/kcmd/layout.d.ts +16 -0
  25. package/build/ts/kcmd/layout.js +31 -0
  26. package/build/ts/kcmd/layouts/documents.d.ts +24 -0
  27. package/build/ts/kcmd/layouts/documents.js +264 -0
  28. package/build/ts/kcmd/layouts/okf.d.ts +31 -0
  29. package/build/ts/kcmd/layouts/okf.js +386 -0
  30. package/build/ts/kcmd/layouts/semantic-model.d.ts +74 -0
  31. package/build/ts/kcmd/layouts/semantic-model.js +621 -0
  32. package/build/ts/kcmd/layouts/standard.d.ts +13 -0
  33. package/build/ts/kcmd/layouts/standard.js +108 -0
  34. package/build/ts/kcmd/manifest.d.ts +34 -0
  35. package/build/ts/kcmd/manifest.js +262 -0
  36. package/build/ts/kcmd/metadata.d.ts +25 -0
  37. package/build/ts/kcmd/metadata.js +8 -0
  38. package/build/ts/kcmd/okf/catalog_residue.d.ts +14 -0
  39. package/build/ts/kcmd/okf/catalog_residue.js +124 -0
  40. package/build/ts/kcmd/okf/concept.d.ts +14 -0
  41. package/build/ts/kcmd/okf/concept.js +110 -0
  42. package/build/ts/kcmd/okf/concept_to_entry.d.ts +10 -0
  43. package/build/ts/kcmd/okf/concept_to_entry.js +281 -0
  44. package/build/ts/kcmd/okf/init_okf_workspace.d.ts +15 -0
  45. package/build/ts/kcmd/okf/init_okf_workspace.js +113 -0
  46. package/build/ts/kcmd/okf/links.d.ts +23 -0
  47. package/build/ts/kcmd/okf/links.js +468 -0
  48. package/build/ts/kcmd/okf/okf_metadata.d.ts +12 -0
  49. package/build/ts/kcmd/okf/okf_metadata.js +99 -0
  50. package/build/ts/kcmd/semantic/bigquery.d.ts +22 -0
  51. package/build/ts/kcmd/semantic/bigquery.js +469 -0
  52. package/build/ts/kcmd/semantic/binding.d.ts +3 -0
  53. package/build/ts/kcmd/semantic/binding.js +92 -0
  54. package/build/ts/kcmd/semantic/catalog_entry.d.ts +24 -0
  55. package/build/ts/kcmd/semantic/catalog_entry.js +35 -0
  56. package/build/ts/kcmd/semantic/converters/owl/convert.d.ts +26 -0
  57. package/build/ts/kcmd/semantic/converters/owl/convert.js +41 -0
  58. package/build/ts/kcmd/semantic/converters/owl/model.d.ts +98 -0
  59. package/build/ts/kcmd/semantic/converters/owl/model.js +30 -0
  60. package/build/ts/kcmd/semantic/converters/owl/parse.d.ts +11 -0
  61. package/build/ts/kcmd/semantic/converters/owl/parse.js +634 -0
  62. package/build/ts/kcmd/semantic/converters/owl/to_ir.d.ts +19 -0
  63. package/build/ts/kcmd/semantic/converters/owl/to_ir.js +390 -0
  64. package/build/ts/kcmd/semantic/deploy_bigquery.d.ts +20 -0
  65. package/build/ts/kcmd/semantic/deploy_bigquery.js +226 -0
  66. package/build/ts/kcmd/semantic/deploy_knowledge_catalog.d.ts +104 -0
  67. package/build/ts/kcmd/semantic/deploy_knowledge_catalog.js +1367 -0
  68. package/build/ts/kcmd/semantic/deploy_spanner.d.ts +15 -0
  69. package/build/ts/kcmd/semantic/deploy_spanner.js +150 -0
  70. package/build/ts/kcmd/semantic/deployment_target.d.ts +93 -0
  71. package/build/ts/kcmd/semantic/deployment_target.js +379 -0
  72. package/build/ts/kcmd/semantic/graph_ddl.d.ts +44 -0
  73. package/build/ts/kcmd/semantic/graph_ddl.js +656 -0
  74. package/build/ts/kcmd/semantic/import_model.d.ts +72 -0
  75. package/build/ts/kcmd/semantic/import_model.js +1698 -0
  76. package/build/ts/kcmd/semantic/ir.d.ts +589 -0
  77. package/build/ts/kcmd/semantic/ir.js +247 -0
  78. package/build/ts/kcmd/semantic/kc_actions.d.ts +45 -0
  79. package/build/ts/kcmd/semantic/kc_actions.js +531 -0
  80. package/build/ts/kcmd/semantic/kc_constraints.d.ts +44 -0
  81. package/build/ts/kcmd/semantic/kc_constraints.js +279 -0
  82. package/build/ts/kcmd/semantic/kc_converter.d.ts +37 -0
  83. package/build/ts/kcmd/semantic/kc_converter.js +839 -0
  84. package/build/ts/kcmd/semantic/kc_custom_types.d.ts +63 -0
  85. package/build/ts/kcmd/semantic/kc_custom_types.js +751 -0
  86. package/build/ts/kcmd/semantic/kc_entries.d.ts +155 -0
  87. package/build/ts/kcmd/semantic/kc_entries.js +284 -0
  88. package/build/ts/kcmd/semantic/kc_ids.d.ts +102 -0
  89. package/build/ts/kcmd/semantic/kc_ids.js +188 -0
  90. package/build/ts/kcmd/semantic/knowledge_catalog.d.ts +29 -0
  91. package/build/ts/kcmd/semantic/knowledge_catalog.js +960 -0
  92. package/build/ts/kcmd/semantic/loader.d.ts +258 -0
  93. package/build/ts/kcmd/semantic/loader.js +3576 -0
  94. package/build/ts/kcmd/semantic/names.d.ts +10 -0
  95. package/build/ts/kcmd/semantic/names.js +85 -0
  96. package/build/ts/kcmd/semantic/osi_converter.d.ts +60 -0
  97. package/build/ts/kcmd/semantic/osi_converter.js +910 -0
  98. package/build/ts/kcmd/semantic/parameters.d.ts +19 -0
  99. package/build/ts/kcmd/semantic/parameters.js +171 -0
  100. package/build/ts/kcmd/semantic/pull_kc.d.ts +10 -0
  101. package/build/ts/kcmd/semantic/pull_kc.js +272 -0
  102. package/build/ts/kcmd/semantic/resolve_inheritance.d.ts +59 -0
  103. package/build/ts/kcmd/semantic/resolve_inheritance.js +346 -0
  104. package/build/ts/kcmd/semantic/resolve_profiles.d.ts +291 -0
  105. package/build/ts/kcmd/semantic/resolve_profiles.js +2003 -0
  106. package/build/ts/kcmd/semantic/runtime/agent_tools.d.ts +93 -0
  107. package/build/ts/kcmd/semantic/runtime/agent_tools.js +243 -0
  108. package/build/ts/kcmd/semantic/runtime/dialect.d.ts +13 -0
  109. package/build/ts/kcmd/semantic/runtime/dialect.js +39 -0
  110. package/build/ts/kcmd/semantic/runtime/runtime.d.ts +88 -0
  111. package/build/ts/kcmd/semantic/runtime/runtime.js +301 -0
  112. package/build/ts/kcmd/semantic/runtime/store.d.ts +112 -0
  113. package/build/ts/kcmd/semantic/runtime/store.js +344 -0
  114. package/build/ts/kcmd/semantic/skills.d.ts +110 -0
  115. package/build/ts/kcmd/semantic/skills.js +697 -0
  116. package/build/ts/kcmd/semantic/source_dialect.d.ts +17 -0
  117. package/build/ts/kcmd/semantic/source_dialect.js +311 -0
  118. package/build/ts/kcmd/semantic/source_entries.d.ts +12 -0
  119. package/build/ts/kcmd/semantic/source_entries.js +341 -0
  120. package/build/ts/kcmd/semantic/spanner.d.ts +18 -0
  121. package/build/ts/kcmd/semantic/spanner.js +60 -0
  122. package/build/ts/kcmd/semantic/sql_expr_utils.d.ts +23 -0
  123. package/build/ts/kcmd/semantic/sql_expr_utils.js +394 -0
  124. package/build/ts/kcmd/semantic/sql_identifiers.d.ts +6 -0
  125. package/build/ts/kcmd/semantic/sql_identifiers.js +267 -0
  126. package/build/ts/kcmd/semantic/sql_parser.d.ts +47 -0
  127. package/build/ts/kcmd/semantic/sql_parser.js +400 -0
  128. package/build/ts/kcmd/semantic/transpile.d.ts +76 -0
  129. package/build/ts/kcmd/semantic/transpile.js +581 -0
  130. package/build/ts/kcmd/semantic/validate.d.ts +70 -0
  131. package/build/ts/kcmd/semantic/validate.js +2266 -0
  132. package/build/ts/kcmd/semantic/yaml_options.d.ts +6 -0
  133. package/build/ts/kcmd/semantic/yaml_options.js +58 -0
  134. package/build/ts/kcmd/semantic/yaml_update.d.ts +26 -0
  135. package/build/ts/kcmd/semantic/yaml_update.js +782 -0
  136. package/build/ts/kcmd/snapshot.d.ts +37 -0
  137. package/build/ts/kcmd/snapshot.js +386 -0
  138. package/build/ts/kcmd/source.d.ts +31 -0
  139. package/build/ts/kcmd/source.js +119 -0
  140. package/build/ts/kcmd/sources/bq-dataset.d.ts +17 -0
  141. package/build/ts/kcmd/sources/bq-dataset.js +131 -0
  142. package/build/ts/kcmd/sources/entrygroup.d.ts +21 -0
  143. package/build/ts/kcmd/sources/entrygroup.js +83 -0
  144. package/build/ts/kcmd/sources/kb.d.ts +21 -0
  145. package/build/ts/kcmd/sources/kb.js +82 -0
  146. package/build/ts/kcmd/sources/semantic-model.d.ts +18 -0
  147. package/build/ts/kcmd/sources/semantic-model.js +48 -0
  148. package/build/ts/kcmd/sync.d.ts +35 -0
  149. package/build/ts/kcmd/sync.js +734 -0
  150. package/package.json +62 -4
@@ -0,0 +1,782 @@
1
+ "use strict";
2
+ // Changes the text of a YAML document to hold a new value. Each part whose
3
+ // value stays comes back byte for byte, and a changed part is written in the
4
+ // file's indentation and line endings.
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.parseForUpdate = parseForUpdate;
40
+ exports.linkNodes = linkNodes;
41
+ exports.updateText = updateText;
42
+ const yaml = __importStar(require("yaml"));
43
+ const loader_1 = require("./loader");
44
+ const yaml_options_1 = require("./yaml_options");
45
+ /**
46
+ * Parses `text` for `updateText`. A JSON file, which is one flow mapping, is
47
+ * first rewritten as block YAML, which the returned `text` holds.
48
+ */
49
+ function parseForUpdate(text) {
50
+ const parse = (t) => yaml.parseDocument(t, { ...yaml_options_1.YAML_OPTIONS, keepSourceTokens: true });
51
+ const document = parse(text);
52
+ const root = document.contents;
53
+ if (document.errors.length > 0 || !yaml.isMap(root) || !root.flow) {
54
+ return { document, text };
55
+ }
56
+ // An empty list or mapping stays `[]` or `{}`. A blank line before a
57
+ // collection's first item is dropped, since it would print as a line of
58
+ // spaces or a `- ` alone on its line. A string a YAML 1.1 reader takes for
59
+ // another type, such as `yes`, is quoted, as in every changed part.
60
+ yaml.visit(document, {
61
+ Collection: (_, n) => {
62
+ n.flow = n.items.length === 0;
63
+ const first = n.items[0];
64
+ const owner = yaml.isPair(first) ? first.key : first;
65
+ if (yaml.isNode(owner))
66
+ owner.spaceBefore = false;
67
+ },
68
+ Scalar: (_, n) => void delete n.type,
69
+ });
70
+ document.setSchema('1.2', { ...yaml_options_1.YAML_OPTIONS, compat: 'yaml-1.1' });
71
+ const yamlText = document.toString({ lineWidth: 0 });
72
+ return { document: parse(yamlText), text: yamlText };
73
+ }
74
+ /**
75
+ * Maps each object and array in `value` to the node it was copied from.
76
+ * `value` must be a plain copy of `node`.
77
+ */
78
+ function linkNodes(value, node, links = new Map()) {
79
+ if (Array.isArray(value) && yaml.isSeq(node)) {
80
+ links.set(value, node);
81
+ value.forEach((v, i) => linkNodes(v, node.items[i], links));
82
+ }
83
+ else if ((0, loader_1.isRecord)(value) && yaml.isMap(node)) {
84
+ links.set(value, node);
85
+ for (const pair of node.items) {
86
+ if (yaml.isScalar(pair.key)) {
87
+ linkNodes(value[keyOf(pair)], pair.value, links);
88
+ }
89
+ }
90
+ }
91
+ return links;
92
+ }
93
+ /**
94
+ * Returns `text`, which `doc` parses, changed to hold `value`. An object or
95
+ * array keeps the node `links` maps it to, and any other value keeps the node
96
+ * under the same key, or the list item with the same `name` or value. A kept
97
+ * node whose value stays keeps its text and the comments and blank lines
98
+ * around it. A changed scalar keeps its quoting, and a removed item takes its
99
+ * comments and the blank lines above it. A flow collection over several lines
100
+ * that changes is written again in block style. A value that would be written
101
+ * as a block scalar that keeps its final line breaks, as `|+` does, is
102
+ * double-quoted instead.
103
+ */
104
+ function updateText(text, doc, value, links) {
105
+ const pairs = [];
106
+ yaml.visit(doc, { Pair: (_, p, path) => void pairs.push([p, path.at(-1)]) });
107
+ const u = {
108
+ text,
109
+ doc,
110
+ links,
111
+ pairs,
112
+ held: new Map(),
113
+ used: new Set(),
114
+ copies: new Map([...links].map(([v, n]) => [n, v])),
115
+ edits: [],
116
+ dirty: new Set(),
117
+ aliases: [],
118
+ nl: /\r?\n/.exec(text)?.[0] ?? '\n',
119
+ ...indents(doc, text),
120
+ };
121
+ hold(u, value, doc.contents);
122
+ patch(u, value, doc.contents, { col: 0, prefix: '', flow: false });
123
+ // An alias whose anchor changed or went is written out.
124
+ for (const [alias, v, slot] of u.aliases) {
125
+ const anchor = alias.resolve(doc);
126
+ const [a, b] = anchor.range;
127
+ if (u.dirty.has(anchor) || u.edits.some(([p, q]) => p < b && q > a)) {
128
+ replace(u, v, alias, slot);
129
+ }
130
+ }
131
+ // The empty lines after a block scalar that keeps its final line breaks, as
132
+ // `|+` does, are its text. An edit that removes the text right after one
133
+ // whose text stays also removes the blank lines it would leave there.
134
+ const ends = new Set();
135
+ yaml.visit(doc, {
136
+ Scalar: (_, n) => {
137
+ const [a, b] = n.range;
138
+ if (keepsBreaks(text, n) && !u.edits.some(([p, q]) => p < b && q > a)) {
139
+ ends.add(b);
140
+ }
141
+ },
142
+ });
143
+ const edits = u.edits.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
144
+ edits.forEach(([from, to, s], i) => {
145
+ const [p, q, inserted] = edits[i - 1] ?? [];
146
+ if (s || !ends.has(from) || p === from && q === from && inserted)
147
+ return;
148
+ edits[i][1] = skipBlank(text, to, edits[i + 1]?.[0] ?? text.length);
149
+ ends.add(edits[i][1]);
150
+ });
151
+ let out = '';
152
+ let at = 0;
153
+ for (const [from, to, s] of edits) {
154
+ // An edit inside another would write part of the text twice.
155
+ if (from < at)
156
+ throw new Error(`Overlapping edits at offset ${from}.`);
157
+ out += text.slice(at, from) + s;
158
+ at = to;
159
+ }
160
+ out += text.slice(at);
161
+ // A text with no final newline keeps none, unless the text of a block scalar
162
+ // that keeps its final line breaks reaches its end.
163
+ return text.endsWith('\n') || ends.has(text.length) ?
164
+ out :
165
+ out.replace(/\r?\n$/, '');
166
+ }
167
+ /**
168
+ * Fills `held` for `value` in place of `node`: the node `links` maps each
169
+ * object to, and each mapping or list whose items `patch` keeps. The value
170
+ * that keeps a node's items in place holds it.
171
+ */
172
+ function hold(u, value, node) {
173
+ if (!isObject(value))
174
+ return;
175
+ const linked = u.links.get(value);
176
+ if (linked && !u.held.has(linked))
177
+ u.held.set(linked, value);
178
+ const kept = isFull(value) && yaml.isCollection(node) &&
179
+ Array.isArray(value) === yaml.isSeq(node) ?
180
+ matchItems(u, entriesOf(value), node) :
181
+ [];
182
+ if (kept.some(i => i >= 0))
183
+ u.held.set(node, value);
184
+ entriesOf(value).forEach(([, v], i) => {
185
+ const item = yaml.isCollection(node) ? node.items[kept[i] ?? -1] : undefined;
186
+ hold(u, v, yaml.isPair(item) ? item.value : item);
187
+ });
188
+ }
189
+ function patch(u, value, node, slot) {
190
+ const edits = u.edits.length;
191
+ // A flow collection over several lines that changes is written again in
192
+ // block style.
193
+ if (yaml.isCollection(node) && node.flow && spansLines(u, node) &&
194
+ canonical(node.toJS(u.doc)) !== canonical(value)) {
195
+ replace(u, value, node, slot);
196
+ }
197
+ else if (isFull(value) && yaml.isCollection(node) &&
198
+ Array.isArray(value) === yaml.isSeq(node)) {
199
+ patchItems(u, value, node, slot);
200
+ }
201
+ else if (yaml.isScalar(node) && !isObject(value)) {
202
+ if (node.value !== value)
203
+ setScalar(u, node, value, slot);
204
+ }
205
+ else if (isObject(value) && !isFull(value) && yaml.isCollection(node) &&
206
+ node.items.length === 0 && Array.isArray(value) === yaml.isSeq(node)) {
207
+ // An empty list or mapping that stays empty keeps its text.
208
+ }
209
+ else if (yaml.isAlias(node) && canonical(node.toJS(u.doc)) === canonical(value)) {
210
+ u.aliases.push([node, value, slot]);
211
+ }
212
+ else {
213
+ replace(u, value, node, slot);
214
+ }
215
+ if (u.edits.length > edits)
216
+ u.dirty.add(node);
217
+ }
218
+ function patchItems(u, value, node, slot) {
219
+ const t = u.text;
220
+ const flow = slot.flow || !!node.flow;
221
+ const items = node.items;
222
+ const entries = entriesOf(value);
223
+ const at = matchItems(u, entries, node);
224
+ if (at.every(i => i < 0))
225
+ return replace(u, value, node, slot);
226
+ // The kept item each new entry goes after, or -1.
227
+ const follows = at.map((j, i) => j < 0 ? Math.max(-1, ...at.slice(0, i)) : NaN);
228
+ const g = geometry(u, node, flow);
229
+ const col = g[0].start - lineStart(t, g[0].start);
230
+ // The text between two items.
231
+ const sep = g.length > 1 ? t.slice(g[0].end, g[1].start) : ', ';
232
+ const slotOf = (i) => {
233
+ const item = items[i];
234
+ const sep = yaml.isPair(item) ?
235
+ item.srcToken?.sep?.find(s => s.type === 'map-value-ind')?.offset :
236
+ g[i].start;
237
+ const prefix = yaml.isPair(item) ? ':' : '-';
238
+ return { col, prefix, after: sep === undefined ? sep : sep + 1, flow };
239
+ };
240
+ // Removes each run of items no entry keeps.
241
+ for (let a = 0; a < items.length; a++) {
242
+ if (at.includes(a))
243
+ continue;
244
+ let b = a;
245
+ while (b + 1 < items.length && !at.includes(b + 1))
246
+ b++;
247
+ const next = g[b + 1];
248
+ if (flow && next) {
249
+ u.edits.push([g[a].start, next.start, '']);
250
+ }
251
+ else if (flow) {
252
+ // The comma after the item that becomes last goes, unless a new entry
253
+ // comes after it.
254
+ u.edits.push([follows.includes(a - 1) ? g[a].start : g[a - 1].end, g[b].end, '']);
255
+ }
256
+ else if (g[a].inline && next) {
257
+ // The next item moves up onto the `- ` line, and its comments go above
258
+ // that line. A comment indented deeper than that line takes its
259
+ // indentation.
260
+ const above = lineStart(t, g[a].start);
261
+ const own = lineStart(t, next.start);
262
+ const indent = /^[ \t]*/.exec(t.slice(above))[0];
263
+ const comments = t.slice(skipBlank(t, next.head, own), own)
264
+ .replace(/^[ \t]*(?=#)/gm, w => w.length > indent.length ? indent : w);
265
+ u.edits.push([above, above, comments], [g[a].start, next.start, '']);
266
+ }
267
+ else {
268
+ // The blank lines above an old first item stay, and those above the
269
+ // item that becomes first go.
270
+ const first = a === 0 && at[0] >= 0;
271
+ const from = first ? skipBlank(t, g[a].head, g[a].start) : g[a].head;
272
+ const to = first && next ? skipBlank(t, g[b].end, next.start) : g[b].end;
273
+ u.edits.push([from, to, '']);
274
+ }
275
+ a = b;
276
+ }
277
+ // Updates each kept item, and adds each new one after the item before it.
278
+ const added = new Map();
279
+ entries.forEach(([k, v], i) => {
280
+ const item = items[at[i]];
281
+ if (yaml.isPair(item)) {
282
+ const key = item.key;
283
+ // A key renamed in the document, such as `datasets` to `entities`.
284
+ if (yaml.isScalar(key) && typeof key.value === 'string' &&
285
+ key.source !== key.value) {
286
+ setScalar(u, key, key.value, slotOf(at[i]));
287
+ }
288
+ patch(u, v, item.value, slotOf(at[i]));
289
+ }
290
+ else if (at[i] >= 0) {
291
+ patch(u, v, item, slotOf(at[i]));
292
+ }
293
+ else {
294
+ const before = follows[i];
295
+ const key = yaml.isMap(node) ? `${scalarText(u, k, 0, flow)}:` : '-';
296
+ const text = !flow ? block(u, key, v, col, String(k)) :
297
+ yaml.isMap(node) ? [`${key} ${flowValue(u, String(k), v)}`] :
298
+ [scalarText(u, v, 0, true)];
299
+ added.set(before, [...(added.get(before) ?? []), ...text]);
300
+ }
301
+ });
302
+ for (const [j, parts] of added) {
303
+ const near = g[Math.max(j, 0)];
304
+ if (flow) {
305
+ // New items go before the item after the one they follow.
306
+ const text = parts.join(sep);
307
+ const next = g[j + 1];
308
+ const last = !at.some(i => i > j);
309
+ u.edits.push(next ? [next.start, next.start, last ? text : text + sep] :
310
+ [near.end, near.end, sep + text]);
311
+ }
312
+ else if (j < 0 && near.inline) {
313
+ const text = parts.join(u.nl).slice(col) + u.nl + ' '.repeat(col);
314
+ u.edits.push([near.start, near.start, text]);
315
+ }
316
+ else {
317
+ const pos = j < 0 ? near.head : near.end;
318
+ const text = parts.join(u.nl);
319
+ u.edits.push(pos === t.length && !t.endsWith('\n') ? [pos, pos, u.nl + text] :
320
+ [pos, pos, text + u.nl]);
321
+ }
322
+ }
323
+ }
324
+ /**
325
+ * For each of `entries`, the index of the item of `node` it keeps, or -1. A
326
+ * mapping's entry keeps the pair with its key. A list's entry keeps the item
327
+ * `links` maps it to, or else a free item with the same `name` or value, and
328
+ * the kept items stay in their order.
329
+ */
330
+ function matchItems(u, entries, node) {
331
+ const items = node.items;
332
+ if (yaml.isMap(node)) {
333
+ return entries.map(([k]) => node.items.findIndex(p => keyOf(p) === k));
334
+ }
335
+ const at = entries.map(([, v]) => {
336
+ const linked = isObject(v) ? u.links.get(v) : undefined;
337
+ return linked ? items.indexOf(linked) : -2;
338
+ });
339
+ const free = items.flatMap((n, j) => at.includes(j) ?
340
+ [] :
341
+ [{ j, key: itemKey(u.copies.get(n) ?? n.toJS(u.doc)) }]);
342
+ let last = -1;
343
+ return at.map((j, i) => {
344
+ if (j === -2) {
345
+ const f = free.findIndex(f => f.key === itemKey(entries[i][1]));
346
+ j = f < 0 ? -1 : free.splice(f, 1)[0].j;
347
+ }
348
+ if (j >= 0 && j < last)
349
+ return -1;
350
+ last = Math.max(last, j);
351
+ return j;
352
+ });
353
+ }
354
+ function geometry(u, node, flow) {
355
+ const t = u.text;
356
+ const dash = flow ? [] : dashes(node);
357
+ let prevEnd = 0;
358
+ return node.items.map((item, i) => {
359
+ const first = (yaml.isPair(item) ? item.key : item);
360
+ const last = (yaml.isPair(item) ? item.value ?? item.key : item);
361
+ const start = yaml.isPair(item) || flow ? first.range[0] : dash[i];
362
+ const ls = lineStart(t, start);
363
+ const inline = !flow && t.slice(ls, start).trim() !== '';
364
+ // An empty value, as in `a:`, ends on its key's line, and so does the item.
365
+ const end = flow ? last.range[1] :
366
+ blockEnd(t, Math.max(last.range[2], nextLine(t, start)), start - ls, last);
367
+ // The root's first item owns no comments: those above it are the file's.
368
+ const floor = i > 0 ? prevEnd : node === u.doc.contents ? ls : 0;
369
+ let head = inline || flow ? start : ls;
370
+ while (head > floor &&
371
+ /^[ \t]*(#.*)?\r?\n$/.test(t.slice(lineStart(t, head - 1), head))) {
372
+ head = lineStart(t, head - 1);
373
+ }
374
+ prevEnd = end;
375
+ return { start, end, head, inline };
376
+ });
377
+ }
378
+ /**
379
+ * Where a block item at column `col` ends, given where its `node` ends: at a
380
+ * line's start, before the blank lines and the comments at its column or less
381
+ * under it, since those are above the next item. When `node` ends in a block
382
+ * scalar, the lines of that scalar's text stay in the item, such as the empty
383
+ * lines that end a `|+` block scalar.
384
+ */
385
+ function blockEnd(t, end, col, node) {
386
+ if (t.slice(lineStart(t, end), end).trim() === '')
387
+ end = lineStart(t, end);
388
+ let last = node;
389
+ while (yaml.isCollection(last) && last.items.length > 0) {
390
+ const item = last.items.at(-1);
391
+ last = yaml.isPair(item) ? item.value : item;
392
+ }
393
+ const floor = yaml.isScalar(last) && /^BLOCK/.test(last.type ?? '') ?
394
+ lineStart(t, last.range[1]) :
395
+ 0;
396
+ let p = end;
397
+ for (let q = lineStart(t, p - 1); p > floor; p = q, q = lineStart(t, p - 1)) {
398
+ const line = t.slice(q, lineEnd(t, q));
399
+ if (line.trim() && (!/^\s*#/.test(line) || line.search(/\S/) > col))
400
+ break;
401
+ }
402
+ return p;
403
+ }
404
+ function setScalar(u, node, value, slot) {
405
+ const [from, to, lead] = rangeOf(u, node);
406
+ const kept = typeof node.value === 'string' && typeof value === 'string';
407
+ const type = kept ? node.type : undefined;
408
+ let s = scalarText(u, value, slot.col, slot.flow, type);
409
+ // A value of several lines in place of a plain or double-quoted scalar is
410
+ // double-quoted. As a block scalar it would take the comment after it into
411
+ // its text. A single-quoted one stays single-quoted.
412
+ if (!/^(BLOCK|QUOTE_SINGLE)/.test(node.type ?? '') && s.includes('\n')) {
413
+ s = scalarText(u, value, slot.col, slot.flow, 'QUOTE_DOUBLE');
414
+ }
415
+ // The text after a block scalar's header, such as a comment, stays after
416
+ // the new header, or after the new value when it is not a block scalar.
417
+ const tail = headerTail(u.text, from) ?? '';
418
+ s = /^[|>]/.test(s) ? s.replace(/^[|>][-+0-9]*/, h => h + tail) : s + tail;
419
+ s = lead + s;
420
+ u.edits.push([from, to, u.text[to - 1] === '\n' ? s + u.nl : s]);
421
+ }
422
+ /**
423
+ * Writes `value` in place of `node`. The comment on the line where `node`
424
+ * starts stays on that line.
425
+ */
426
+ function replace(u, value, node, slot) {
427
+ const t = u.text;
428
+ const [from, to, lead] = yaml.isNode(node) && node.range ?
429
+ rangeOf(u, node) :
430
+ [slot.after ?? 0, slot.after ?? 0, ''];
431
+ // A flow collection on one line stays flow.
432
+ if (slot.flow || slot.after === undefined ||
433
+ yaml.isCollection(node) && node.flow && isObject(value) &&
434
+ !spansLines(u, node)) {
435
+ u.edits.push([from, to, lead + scalarText(u, value, slot.col, true)]);
436
+ return;
437
+ }
438
+ const keyLine = lineEnd(t, slot.after);
439
+ // A block collection's range can run over the comments after it and into
440
+ // the next line's indentation. The comments at its key's column or less
441
+ // stay.
442
+ const stop = blockEnd(t, to, slot.col, node);
443
+ const end = lineEnd(t, Math.max(from, stop - 1));
444
+ // A scalar, alias or flow collection on the line under its key is replaced
445
+ // there, so the comments above it and after it stay.
446
+ const under = from > keyLine && (!yaml.isCollection(node) || node.flow);
447
+ const col = under && isFull(value) ? from - lineStart(t, from) : slot.col;
448
+ const [first, ...rest] = under && isFull(value) ?
449
+ items(u, value, col) :
450
+ block(u, slot.prefix, value, col);
451
+ // The text after the node on its last line stays, or after a block
452
+ // scalar's header unless the scalar's text moved into the new value. A
453
+ // block collection of several lines keeps none.
454
+ const header = headerTail(t, from);
455
+ const moved = u.pairs.some(([p]) => p.value === node && u.used.has(p));
456
+ const after = header !== undefined ? (moved ? '' : header) :
457
+ end === lineEnd(t, from) || !yaml.isCollection(node) || node.flow ?
458
+ t.slice(to, end) :
459
+ '';
460
+ if (under && !isFull(value)) {
461
+ // As a block scalar, text of several lines would take the text after it.
462
+ const type = typeof value === 'string' && value.includes('\n') ?
463
+ 'QUOTE_DOUBLE' :
464
+ undefined;
465
+ u.edits.push([from, end, scalarText(u, value, slot.col, false, type) + after]);
466
+ return;
467
+ }
468
+ if (under) {
469
+ u.edits.push([from, end, [first.slice(col) + after, ...rest].join(u.nl)]);
470
+ return;
471
+ }
472
+ const tail = from > keyLine ? t.slice(slot.after, keyLine) : after;
473
+ const head = first.slice(col + slot.prefix.length);
474
+ u.edits.push([slot.after, end, [head + tail, ...rest].join(u.nl)]);
475
+ }
476
+ /**
477
+ * The lines of `prefix`, a key and `:` or a `-` at column `col`, and `value`
478
+ * after it, in the file's indentation. `key` is the key `prefix` writes. A
479
+ * value that moved keeps its text.
480
+ */
481
+ function block(u, prefix, value, col, key) {
482
+ const pad = ' '.repeat(col);
483
+ const moved = prefix.endsWith(':') ? movedText(u, key, value, col) : undefined;
484
+ if (moved)
485
+ return [pad + prefix + moved[0], ...moved.slice(1)];
486
+ if (!isFull(value)) {
487
+ const s = scalarText(u, value, col, false);
488
+ // A block scalar's header is a line of its own, so that a comment after
489
+ // it stays out of the text.
490
+ const line = `${pad}${prefix} ${s}`;
491
+ return /^[|>]/.test(s) ? line.split(u.nl) : [line];
492
+ }
493
+ if (prefix === '-' && (0, loader_1.isRecord)(value)) {
494
+ const lines = items(u, value, col + u.itemIndent);
495
+ // In a file whose items start on the line after their '-', so do new ones.
496
+ if (u.itemAlone)
497
+ return [pad + '-', ...lines];
498
+ const [first, ...rest] = lines;
499
+ return [pad + '-' + first.slice(col + 1), ...rest];
500
+ }
501
+ const nested = prefix === '-' ? 2 : Array.isArray(value) ? u.seqIndent : u.step;
502
+ return [pad + prefix, ...items(u, value, col + nested)];
503
+ }
504
+ /** The lines of each item of the collection `value`, at column `col`. */
505
+ function items(u, value, col) {
506
+ release(u, value);
507
+ return entriesOf(value).flatMap(([k, v]) => {
508
+ const prefix = Array.isArray(value) ? '-' : `${scalarText(u, k, 0, false)}:`;
509
+ return block(u, prefix, v, col, String(k));
510
+ });
511
+ }
512
+ /**
513
+ * Lets the entries of `value`, which is written entry by entry, move from the
514
+ * node `links` maps it to, unless another value keeps that node in place.
515
+ */
516
+ function release(u, value) {
517
+ const node = u.links.get(value);
518
+ if (u.held.get(node) === value)
519
+ u.held.delete(node);
520
+ }
521
+ /**
522
+ * The text of the input value that `value` moved from, for `key` at column
523
+ * `col`, or in a flow mapping when `col` is undefined: what follows the key's
524
+ * `:` on its line, then each line under it, re-indented. An object or array
525
+ * moves from the node `links` maps it to, unless another value keeps that
526
+ * node in place. A scalar under `key` moves from a
527
+ * pair with the same key and value whose text the new value does not keep.
528
+ * Undefined when nothing moved, or when the text would not read back as
529
+ * `value` at its new place, as when it holds an alias of an anchor outside it.
530
+ * A value that is or holds a block scalar that keeps its final line breaks,
531
+ * as `|+` does, is written fresh, since its text would take the blank lines
532
+ * after its new place.
533
+ */
534
+ function movedText(u, key, value, col) {
535
+ const node = isObject(value) ? u.links.get(value) : undefined;
536
+ const found = u.pairs.find(([p, parent]) => !u.used.has(p) &&
537
+ (node ? p.value === node && u.held.get(node) === value :
538
+ key !== undefined && keyOf(p) === key && yaml.isScalar(p.value) &&
539
+ p.value.value === value &&
540
+ u.held.get(parent)?.[key] !==
541
+ value));
542
+ const p = found?.[0];
543
+ const v = p?.value;
544
+ if (!p || !v?.range || !yaml.isNode(p.key) || keeps(u, v))
545
+ return undefined;
546
+ const t = u.text;
547
+ const keyCol = p.key.range[0] - lineStart(t, p.key.range[0]);
548
+ const from = v.range[0];
549
+ // The comments after the value at its key's column or less are above the
550
+ // next key.
551
+ const to = blockEnd(t, v.range[1], keyCol);
552
+ const shift = (col ?? 0) - keyCol;
553
+ const own = t.slice(lineStart(t, from), from).trim() === '';
554
+ // A mapping or list on the lines under its key starts on the line after the
555
+ // key, so the comments above its first key or item move with it. The
556
+ // comments above a scalar under its key stay with the key.
557
+ const start = !own ? from :
558
+ yaml.isCollection(v) ? nextLine(t, p.key.range[1]) :
559
+ lineStart(t, from);
560
+ const stop = t.slice(lineStart(t, to), to).trim() ? to : lineStart(t, to);
561
+ const lines = t.slice(start, stop)
562
+ .replace(/\r?\n$/, '')
563
+ .split(/\r?\n/)
564
+ .map((l, i) => !own && i === 0 || l === '' ? l :
565
+ shift >= 0 ? ' '.repeat(shift) + l :
566
+ l.slice(Math.min(-shift, indentOf(l))));
567
+ // An empty value moves as nothing after the key's `:`.
568
+ const moved = own ? ['', ...lines] :
569
+ [lines[0] && ' ' + lines[0], ...lines.slice(1)];
570
+ const reads = col === undefined ?
571
+ moved.length === 1 && readsInFlow(moved[0].trim(), value) :
572
+ readsInBlock([' '.repeat(col) + '_:' + moved[0], ...moved.slice(1)].join('\n'), value);
573
+ if (!reads)
574
+ return undefined;
575
+ u.used.add(p);
576
+ return moved;
577
+ }
578
+ /**
579
+ * `value` under `key` in a flow mapping. A value that moved keeps its text
580
+ * when that is one line that reads the same there.
581
+ */
582
+ function flowValue(u, key, value) {
583
+ return movedText(u, key, value)?.[0].trim() ?? scalarText(u, value, 0, true);
584
+ }
585
+ /** Whether `text` reads as `value` in a flow collection. */
586
+ function readsInFlow(text, value) {
587
+ try {
588
+ return canonical(yaml.parse(`[${text}]`, yaml_options_1.YAML_OPTIONS)[0]) === canonical(value);
589
+ }
590
+ catch {
591
+ return false;
592
+ }
593
+ }
594
+ /**
595
+ * Whether `text`, one key and its value, reads as that key holding `value`.
596
+ * An alias in it must have its anchor in it.
597
+ */
598
+ function readsInBlock(text, value) {
599
+ try {
600
+ const read = yaml.parse(text, yaml_options_1.YAML_OPTIONS);
601
+ return (0, loader_1.isRecord)(read) && canonical(Object.values(read)[0]) === canonical(value);
602
+ }
603
+ catch {
604
+ return false;
605
+ }
606
+ }
607
+ /** Whether the text of `node` holds a line break. */
608
+ function spansLines(u, node) {
609
+ return u.text.slice(node.range[0], node.range[1]).includes('\n');
610
+ }
611
+ /** Whether `node` is or holds a block scalar that keeps its final line breaks. */
612
+ function keeps(u, node) {
613
+ let found = false;
614
+ yaml.visit(node, { Scalar: (_, n) => void (found ||= keepsBreaks(u.text, n)) });
615
+ return found;
616
+ }
617
+ /** Whether `node` is a block scalar that keeps its final line breaks. */
618
+ function keepsBreaks(t, node) {
619
+ return yaml.isScalar(node) &&
620
+ /^[|>][0-9]*\+/.test(t.slice(node.range[0], lineEnd(t, node.range[0])));
621
+ }
622
+ /** The number of spaces and tabs that start `line`. */
623
+ function indentOf(line) {
624
+ return /^[ \t]*/.exec(line)[0].length;
625
+ }
626
+ /**
627
+ * `value` written as a scalar or flow collection. Each line after the first
628
+ * that is not empty is indented under column `col`. In a flow collection, a
629
+ * string of several lines is one double-quoted line.
630
+ */
631
+ function scalarText(u, value, col, flow, type) {
632
+ if (flow && isFull(value)) {
633
+ release(u, value);
634
+ const seq = Array.isArray(value);
635
+ const pad = seq ? u.pad.seq : u.pad.map;
636
+ const parts = entriesOf(value).map(([k, v]) => seq ?
637
+ scalarText(u, v, col, true) :
638
+ `${scalarText(u, k, 0, true)}: ${flowValue(u, String(k), v)}`);
639
+ return `${seq ? '[' : '{'}${pad}${parts.join(', ')}${pad}${seq ? ']' : '}'}`;
640
+ }
641
+ const lines = flow && typeof value === 'string' && value.includes('\n');
642
+ const node = isObject(value) ? value : Object.assign(new yaml.Scalar(value), { type: lines ? 'QUOTE_DOUBLE' : type });
643
+ const text = yaml.stringify(flow ? [node] : { _: node }, {
644
+ ...yaml_options_1.YAML_OPTIONS,
645
+ compat: 'yaml-1.1',
646
+ indent: u.step,
647
+ lineWidth: 0,
648
+ doubleQuotedMinMultiLineLength: flow ? Infinity : undefined,
649
+ flowCollectionPadding: false,
650
+ collectionStyle: flow ? 'flow' : 'any',
651
+ });
652
+ // A block scalar that keeps its final line breaks would take the blank
653
+ // lines after it, so such a value is double-quoted.
654
+ if (!flow && /^_: [|>][0-9]*\+/.test(text)) {
655
+ return scalarText(u, value, col, flow, 'QUOTE_DOUBLE');
656
+ }
657
+ // A block scalar's indentation indicator, which yaml writes as 2, is the
658
+ // file's.
659
+ return (flow ? text.slice(1, -2) :
660
+ text.slice(3, -1).replace(/^([|>])2/, `$1${u.step}`))
661
+ .split('\n')
662
+ .map((line, i) => i > 0 && line ? ' '.repeat(col) + line : line)
663
+ .join(u.nl);
664
+ }
665
+ /**
666
+ * The indentation of the file's first nested mapping, list and list item, and
667
+ * the space after the bracket of its first flow mapping and flow list whose
668
+ * first item is on the bracket's line.
669
+ */
670
+ function indents(doc, text) {
671
+ const col = (p) => p - lineStart(text, p);
672
+ const found = {};
673
+ const pad = {};
674
+ let itemAlone;
675
+ yaml.visit(doc, {
676
+ Collection: (_, v) => {
677
+ const first = v.items[0];
678
+ const at = (yaml.isPair(first) ? first.key : first);
679
+ const gap = v.flow && v.range && at?.range ?
680
+ text.slice(v.range[0] + 1, at.range[0]) :
681
+ '\n';
682
+ if (!gap.includes('\n'))
683
+ pad[yaml.isMap(v) ? 'map' : 'seq'] ??= gap;
684
+ },
685
+ Pair: (_, { key, value: v }) => {
686
+ if (!yaml.isNode(key) || !yaml.isCollection(v) || v.flow ||
687
+ v.items.length === 0) {
688
+ return;
689
+ }
690
+ const first = v.items[0];
691
+ const at = yaml.isPair(first) ? first.key.range[0] :
692
+ dashes(v)[0];
693
+ found[yaml.isMap(v) ? 'step' : 'seqIndent'] ??=
694
+ col(at) - col(key.range[0]);
695
+ if (yaml.isMap(first) && !first.flow && first.items[0]) {
696
+ const firstKey = first.items[0].key.range[0];
697
+ found.itemIndent ??= col(firstKey) - col(at);
698
+ itemAlone ??= lineStart(text, firstKey) !== lineStart(text, at);
699
+ }
700
+ },
701
+ });
702
+ return {
703
+ step: 2, seqIndent: 2, itemIndent: 2, ...found,
704
+ itemAlone: itemAlone ?? false, pad: { map: '', seq: '', ...pad },
705
+ };
706
+ }
707
+ /** The offset of each `-` of a block list. */
708
+ function dashes(node) {
709
+ return (node.srcToken?.items ?? [])
710
+ .flatMap(it => it.start.filter(s => s.type === 'seq-item-ind'))
711
+ .map(s => s.offset);
712
+ }
713
+ /**
714
+ * Where the text of `node` starts and ends, and the text that a value written
715
+ * there starts with. An empty scalar, such as the value of `a: # c`, starts
716
+ * right after the `:`, `-`, anchor or tag before it, and also takes the spaces
717
+ * after that when no comment follows. A value written there starts with a
718
+ * space.
719
+ */
720
+ function rangeOf(u, node) {
721
+ const [from, to] = node.range;
722
+ if (from !== to || !yaml.isScalar(node))
723
+ return [from, to, ''];
724
+ let p = from;
725
+ while (p > 0 && /[ \t]/.test(u.text[p - 1]))
726
+ p--;
727
+ return [p, u.text[from] === '#' ? p : from, ' '];
728
+ }
729
+ /**
730
+ * The text on its line after the header of the block scalar at `from`, or
731
+ * undefined when no block scalar starts there.
732
+ */
733
+ function headerTail(t, from) {
734
+ return /^[|>][-+0-9]*(.*)/.exec(t.slice(from, lineEnd(t, from)))?.[1];
735
+ }
736
+ function lineStart(t, p) {
737
+ return t.lastIndexOf('\n', p - 1) + 1;
738
+ }
739
+ /** The offset where the text of the line holding `p` ends. */
740
+ function lineEnd(t, p) {
741
+ const i = t.indexOf('\n', p);
742
+ const e = i < 0 ? t.length : i;
743
+ return t[e - 1] === '\r' ? e - 1 : e;
744
+ }
745
+ /** The offset of the line after the one holding `p`. */
746
+ function nextLine(t, p) {
747
+ return t.indexOf('\n', p) + 1 || t.length;
748
+ }
749
+ /** The offset of the first line from `p` that is not blank, up to `limit`. */
750
+ function skipBlank(t, p, limit) {
751
+ while (p < limit && t.slice(p, nextLine(t, p)).trim() === '') {
752
+ p = nextLine(t, p);
753
+ }
754
+ return p;
755
+ }
756
+ function keyOf(pair) {
757
+ const key = yaml.isScalar(pair.key) ? pair.key.value : pair.key;
758
+ return key === null ? '' : String(key);
759
+ }
760
+ function entriesOf(value) {
761
+ return Array.isArray(value) ?
762
+ value.map((v, i) => [i, v]) :
763
+ Object.entries(value).filter(([, v]) => v !== undefined);
764
+ }
765
+ function isFull(value) {
766
+ return isObject(value) && entriesOf(value).length > 0;
767
+ }
768
+ function itemKey(value) {
769
+ return (0, loader_1.isRecord)(value) && typeof value.name === 'string' ?
770
+ `name:${value.name}` :
771
+ canonical(value);
772
+ }
773
+ /** `value` as JSON text with each mapping's keys sorted. */
774
+ function canonical(value) {
775
+ const sorted = (_, v) => (0, loader_1.isRecord)(v) ?
776
+ Object.fromEntries(Object.entries(v).sort(([a], [b]) => a < b ? -1 : 1)) :
777
+ v;
778
+ return JSON.stringify(value, sorted) ?? 'undefined';
779
+ }
780
+ function isObject(value) {
781
+ return typeof value === 'object' && value !== null;
782
+ }