@gjsify/cli 0.47.0 → 0.49.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.
- package/dist/cli.gjs.mjs +164 -153
- package/lib/actions/build.js +2 -1
- package/lib/commands/affected-classify.js +30 -1
- package/lib/commands/build.js +4 -0
- package/lib/commands/dlx.js +7 -3
- package/lib/commands/foreach.js +9 -2
- package/lib/commands/install.js +21 -1
- package/lib/commands/publish.js +1 -8
- package/lib/commands/run.js +7 -2
- package/lib/commands/ship.js +32 -4
- package/lib/commands/upgrade.js +78 -30
- package/lib/config.js +13 -0
- package/lib/types/cli-build-options.d.ts +6 -0
- package/lib/types/config-data.d.ts +51 -1
- package/lib/utils/base64.d.ts +34 -0
- package/lib/utils/base64.js +47 -0
- package/lib/utils/cli-runtime-closure.d.ts +61 -0
- package/lib/utils/cli-runtime-closure.js +54 -17
- package/lib/utils/detect-native-packages.d.ts +31 -0
- package/lib/utils/detect-native-packages.js +37 -1
- package/lib/utils/double-dash-args.d.ts +8 -0
- package/lib/utils/double-dash-args.js +64 -0
- package/lib/utils/ship/changelog.d.ts +56 -0
- package/lib/utils/ship/changelog.js +166 -0
- package/lib/utils/ship/discover.d.ts +10 -1
- package/lib/utils/ship/discover.js +76 -2
- package/lib/utils/ship/entry-interpreter.d.ts +44 -0
- package/lib/utils/ship/entry-interpreter.js +109 -0
- package/lib/utils/ship/formats.js +5 -0
- package/lib/utils/ship/gi-namespaces.js +175 -12
- package/lib/utils/ship/plan.d.ts +5 -1
- package/lib/utils/ship/plan.js +21 -2
- package/lib/utils/ship/settings.d.ts +48 -2
- package/lib/utils/ship/settings.js +63 -3
- package/lib/utils/ship/stage-manifest.d.ts +22 -3
- package/lib/utils/ship/stage-manifest.js +40 -13
- package/lib/utils/ship/types.d.ts +13 -0
- package/package.json +19 -19
|
@@ -177,6 +177,7 @@ export function resolveShipSettings(input) {
|
|
|
177
177
|
description,
|
|
178
178
|
license,
|
|
179
179
|
licenseFile: discovered.licenseFile,
|
|
180
|
+
changelogFile: discovered.changelogFile,
|
|
180
181
|
homepage: metadata.homepageUrl ?? pkg.homepage,
|
|
181
182
|
section: ship.section ?? sections.section,
|
|
182
183
|
group: ship.group ?? sections.group,
|
|
@@ -203,9 +204,66 @@ export function resolveShipSettings(input) {
|
|
|
203
204
|
};
|
|
204
205
|
return { settings, metadata, warnings };
|
|
205
206
|
}
|
|
206
|
-
/**
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
/**
|
|
208
|
+
* The ONE place a ship target's ENTRY is decided — {@link resolveShipApp}'s twin.
|
|
209
|
+
*
|
|
210
|
+
* The two are twins because they answer one question between them, and #1545 is
|
|
211
|
+
* what answering only half of it costs: `gjsify.ship.app.darwin: "node"` moved
|
|
212
|
+
* the launcher to `node` while the payload stayed `gjsify.main`, which for a
|
|
213
|
+
* project whose primary target is Linux is its `--app gjs` bundle. Both keys
|
|
214
|
+
* were honoured, they contradicted each other, and the `.app` died on its first
|
|
215
|
+
* import with the only line of output on the subject reading as confirmation.
|
|
216
|
+
*
|
|
217
|
+
* A DECLARATION rather than a derivation, and both alternatives were considered
|
|
218
|
+
* where the issue raised them. Deriving the entry from the runtime by filename
|
|
219
|
+
* convention (`app.node.mjs` beside `app.gjs.mjs`) is a heuristic on an output
|
|
220
|
+
* name the project owns — the same guess `tests/e2e/ship`'s fixture exists to
|
|
221
|
+
* refuse. Having ship invoke `gjsify build --app <runtime>` itself cannot work
|
|
222
|
+
* for the phase that needs it most: `--from-stage` packs on a host with no
|
|
223
|
+
* project, no bundler and no sources (ADR 0024 § A2).
|
|
224
|
+
*
|
|
225
|
+
* Fall-through, not requirement: a target with no key keeps `gjsify.main`, so
|
|
226
|
+
* every single-host project is unaffected and nothing new is mandatory.
|
|
227
|
+
*/
|
|
228
|
+
export function resolveShipBundle(input) {
|
|
229
|
+
const declaredTable = input.ship.bundle;
|
|
230
|
+
const perTarget = typeof declaredTable === 'object' && declaredTable !== null ? declaredTable : {};
|
|
231
|
+
// The WHOLE table, for the reason `resolveShipApp` gives: a mis-keyed
|
|
232
|
+
// override is silent by construction, so the run that must report `windows`
|
|
233
|
+
// is the one assembling some OTHER layout.
|
|
234
|
+
for (const os of Object.keys(perTarget)) {
|
|
235
|
+
if (Object.hasOwn(SHIP_APP_OSES, os))
|
|
236
|
+
continue;
|
|
237
|
+
throw new Error(`gjsify ship: \`gjsify.ship.bundle.${os}\` is not an OS this command assembles for. Known: ` +
|
|
238
|
+
`${Object.keys(SHIP_APP_OSES).join(', ')} — the \`process.platform\` spelling, not the ` +
|
|
239
|
+
"`gjsify ship <os>` positional's. An entry under a key nothing reads leaves that target on " +
|
|
240
|
+
'`gjsify.main` with nothing to say so.');
|
|
241
|
+
}
|
|
242
|
+
const projectWide = typeof declaredTable === 'string' ? declaredTable : undefined;
|
|
243
|
+
const fallback = input.pkg.gjsify?.main ?? input.pkg.main;
|
|
244
|
+
const entryFor = (os) => perTarget[os] ?? projectWide ?? fallback;
|
|
245
|
+
const override = perTarget[input.layoutOs];
|
|
246
|
+
const declared = entryFor(input.layoutOs);
|
|
247
|
+
const key = override !== undefined
|
|
248
|
+
? `gjsify.ship.bundle.${input.layoutOs}`
|
|
249
|
+
: projectWide !== undefined
|
|
250
|
+
? 'gjsify.ship.bundle'
|
|
251
|
+
: input.pkg.gjsify?.main !== undefined
|
|
252
|
+
? 'gjsify.main'
|
|
253
|
+
: 'main';
|
|
254
|
+
// RESOLVED per OS, not read off the table — because the entry another target
|
|
255
|
+
// ships is as often the FALL-THROUGH as it is a declared one, and that is
|
|
256
|
+
// #1545's own project shape: `main` is the GJS bundle, only darwin declares
|
|
257
|
+
// its own, and reading the table alone leaves the GJS bundle sitting inside
|
|
258
|
+
// the signed `.app` with nothing to name it. When nothing is keyed per OS
|
|
259
|
+
// every target resolves the same entry and this set is empty by construction.
|
|
260
|
+
const foreign = [
|
|
261
|
+
...new Set(Object.keys(SHIP_APP_OSES)
|
|
262
|
+
.filter((os) => os !== input.layoutOs)
|
|
263
|
+
.map(entryFor)
|
|
264
|
+
.filter((entry) => entry !== undefined && entry !== declared)),
|
|
265
|
+
];
|
|
266
|
+
return { declared, key, overridden: override !== undefined, foreign };
|
|
209
267
|
}
|
|
210
268
|
/** npm package name → a package/binary name both dpkg and rpm accept. */
|
|
211
269
|
export function deriveBinaryName(pkgName) {
|
|
@@ -302,6 +360,8 @@ export function descriptionParagraphs(description) {
|
|
|
302
360
|
}
|
|
303
361
|
/** `LICENSE`, `LICENSE.md`, … — the names a licence file actually has. */
|
|
304
362
|
export const LICENSE_FILE_NAMES = ['LICENSE', 'LICENSE.md', 'LICENSE.txt', 'COPYING', 'COPYING.md'];
|
|
363
|
+
/** `CHANGELOG.md`, … — the names a project's own changelog actually has. */
|
|
364
|
+
export const CHANGELOG_FILE_NAMES = ['CHANGELOG.md', 'CHANGELOG', 'CHANGELOG.txt', 'NEWS.md', 'NEWS'];
|
|
305
365
|
/** The bundle's own basename, used as the launcher's exec target. */
|
|
306
366
|
export function bundleEntryName(bundlePath) {
|
|
307
367
|
return basename(bundlePath);
|
|
@@ -53,8 +53,17 @@ export declare const STAGE_MANIFEST_FILE = ".gjsify-ship-stage.json";
|
|
|
53
53
|
* the one thing the reader can do about it. That is the same shape the 3-to-4
|
|
54
54
|
* paragraph describes, and at 5 the schema check above fires first and says
|
|
55
55
|
* "re-run the `--stage` phase with this gjsify".
|
|
56
|
+
*
|
|
57
|
+
* 6 made an overlay entry carry EITHER `text` or `base64`, for the gzipped
|
|
58
|
+
* `changelog.Debian.gz` Debian Policy § 4.4 requires. That is the meaning case
|
|
59
|
+
* again and the sharpest one yet: a schema-5 reader demands `text` on every
|
|
60
|
+
* entry, so it meets a 6 stage and fails on `overlay.deb[1].text must be a
|
|
61
|
+
* string` — a sentence about a field, over a stage whose only problem is the
|
|
62
|
+
* gjsify reading it. And the inverse is worse than a bad message: a reader that
|
|
63
|
+
* ignored the unknown key would write an entry with no bytes, or write base64 AS
|
|
64
|
+
* text, and ship a `.deb` whose changelog is a wall of ASCII at exit 0.
|
|
56
65
|
*/
|
|
57
|
-
export declare const STAGE_SCHEMA_VERSION =
|
|
66
|
+
export declare const STAGE_SCHEMA_VERSION = 6;
|
|
58
67
|
/** What this stage was assembled FOR, in the repo-wide `${process.platform}-${process.arch}` spelling. */
|
|
59
68
|
export interface StageTarget {
|
|
60
69
|
/** The layout the stage carries, in the `process.platform` spelling — `win32`, never `windows`. */
|
|
@@ -65,9 +74,19 @@ export interface StageTarget {
|
|
|
65
74
|
export interface StageFileRecord extends StagedMode {
|
|
66
75
|
bytes: number;
|
|
67
76
|
}
|
|
68
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* One pre-rendered overlay file. EXACTLY ONE of `text` and `base64` is present —
|
|
79
|
+
* see {@link writeStageManifest} for why a file reference is still refused.
|
|
80
|
+
*
|
|
81
|
+
* `text` is preferred wherever the content is text, because the sidecar is the one
|
|
82
|
+
* artifact in this handoff a human reads: the whole copyright is legible in it. The
|
|
83
|
+
* `base64` half exists for the gzipped Debian changelog, whose bytes must be the
|
|
84
|
+
* bytes that ship — a stage packed on another host cannot re-compress it and get
|
|
85
|
+
* the same file.
|
|
86
|
+
*/
|
|
69
87
|
export interface StageOverlayFile extends StagedMode {
|
|
70
|
-
text
|
|
88
|
+
text?: string;
|
|
89
|
+
base64?: string;
|
|
71
90
|
}
|
|
72
91
|
export interface StageManifest {
|
|
73
92
|
schema: number;
|
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
// * no `staged` — modes come from the plan (see `stage-writer.ts`), so the
|
|
14
14
|
// launcher packs 0644 and the installed `bin/<name>` is not executable.
|
|
15
15
|
// * no `overlay` — the `.deb` ships no `/usr/share/doc/<pkg>/copyright`,
|
|
16
|
-
// which Debian Policy § 12.5 requires and lintian errors on,
|
|
17
|
-
//
|
|
16
|
+
// which Debian Policy § 12.5 requires and lintian errors on, no
|
|
17
|
+
// `/usr/share/doc/<pkg>/changelog.Debian.gz`, which § 4.4 requires and
|
|
18
|
+
// lintian also errors on, and the `.rpm` ships no
|
|
19
|
+
// `/usr/share/licenses/<pkg>/LICENSE`.
|
|
18
20
|
// * no `namespaces` — they are scanned from the BUILD TREE's bundle, so a
|
|
19
21
|
// packing host that re-derived nothing would emit `Depends:` without
|
|
20
22
|
// `gir1.2-gtk-4.0` and `gir1.2-adw-1`: the package installs on a machine
|
|
@@ -36,6 +38,7 @@
|
|
|
36
38
|
// that is right there.
|
|
37
39
|
import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
38
40
|
import { join, sep } from 'node:path';
|
|
41
|
+
import { base64Decode, base64Encode } from '../base64.js';
|
|
39
42
|
import { cliVersion } from '../publish-headers.js';
|
|
40
43
|
import { FORMAT_IDS } from './formats.js';
|
|
41
44
|
import { layoutForOs, LAYOUTS, LAYOUT_NAMES } from './layout.js';
|
|
@@ -93,8 +96,17 @@ export const STAGE_MANIFEST_FILE = '.gjsify-ship-stage.json';
|
|
|
93
96
|
* the one thing the reader can do about it. That is the same shape the 3-to-4
|
|
94
97
|
* paragraph describes, and at 5 the schema check above fires first and says
|
|
95
98
|
* "re-run the `--stage` phase with this gjsify".
|
|
99
|
+
*
|
|
100
|
+
* 6 made an overlay entry carry EITHER `text` or `base64`, for the gzipped
|
|
101
|
+
* `changelog.Debian.gz` Debian Policy § 4.4 requires. That is the meaning case
|
|
102
|
+
* again and the sharpest one yet: a schema-5 reader demands `text` on every
|
|
103
|
+
* entry, so it meets a 6 stage and fails on `overlay.deb[1].text must be a
|
|
104
|
+
* string` — a sentence about a field, over a stage whose only problem is the
|
|
105
|
+
* gjsify reading it. And the inverse is worse than a bad message: a reader that
|
|
106
|
+
* ignored the unknown key would write an entry with no bytes, or write base64 AS
|
|
107
|
+
* text, and ship a `.deb` whose changelog is a wall of ASCII at exit 0.
|
|
96
108
|
*/
|
|
97
|
-
export const STAGE_SCHEMA_VERSION =
|
|
109
|
+
export const STAGE_SCHEMA_VERSION = 6;
|
|
98
110
|
/**
|
|
99
111
|
* The half of {@link ShipSettings} that crosses the host boundary.
|
|
100
112
|
*
|
|
@@ -149,15 +161,17 @@ export function writeStageManifest(input) {
|
|
|
149
161
|
const overlay = {};
|
|
150
162
|
for (const format of input.formats) {
|
|
151
163
|
overlay[format.id] = (input.overlay.get(format.id) ?? []).map((file) => {
|
|
152
|
-
if (file.source.kind
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
throw new Error(`gjsify ship: internal error — the ${format.id} overlay entry ${file.path} is a file ` +
|
|
157
|
-
'reference, and a stage manifest may only carry rendered text. Render it in ' +
|
|
158
|
-
'`planOverlay` instead.');
|
|
164
|
+
if (file.source.kind === 'text')
|
|
165
|
+
return { path: file.path, mode: file.mode, text: file.source.text };
|
|
166
|
+
if (file.source.kind === 'bytes') {
|
|
167
|
+
return { path: file.path, mode: file.mode, base64: base64Encode(file.source.data) };
|
|
159
168
|
}
|
|
160
|
-
|
|
169
|
+
// A FILE reference is still refused. It would be a build-host path, and
|
|
170
|
+
// putting one in here is how the manifest would start naming files the
|
|
171
|
+
// packing host cannot open — the failure the closure exists to prevent.
|
|
172
|
+
throw new Error(`gjsify ship: internal error — the ${format.id} overlay entry ${file.path} is a file ` +
|
|
173
|
+
'reference, and a stage manifest may only carry rendered bytes. Render it in ' +
|
|
174
|
+
'`planOverlay` instead.');
|
|
161
175
|
});
|
|
162
176
|
}
|
|
163
177
|
const manifest = {
|
|
@@ -295,10 +309,21 @@ export function readStageManifest(stageDir) {
|
|
|
295
309
|
// lintian errors on.
|
|
296
310
|
manifest.overlay[id] = expectArray(overlay[id], at(`overlay.${id}`)).map((entry, index) => {
|
|
297
311
|
const file = record(entry, at(`overlay.${id}[${index}]`));
|
|
312
|
+
const where = at(`overlay.${id}[${index}]`);
|
|
313
|
+
// EXACTLY ONE, checked in both directions. Neither would write an empty
|
|
314
|
+
// file at a path Debian policy demands content at; both would leave which
|
|
315
|
+
// one wins to whichever branch `overlayFiles` happens to test first.
|
|
316
|
+
if ((file.text === undefined) === (file.base64 === undefined)) {
|
|
317
|
+
throw new Error(`gjsify ship: ${where} must carry exactly one of \`text\` and \`base64\`. ` +
|
|
318
|
+
'An overlay entry is either rendered text or rendered bytes; an entry with ' +
|
|
319
|
+
'neither would pack an empty file at a path the format requires content at.');
|
|
320
|
+
}
|
|
298
321
|
return {
|
|
299
322
|
path: expectString(file.path, at(`overlay.${id}[${index}].path`)),
|
|
300
323
|
mode: expectInteger(file.mode, at(`overlay.${id}[${index}].mode`)),
|
|
301
|
-
|
|
324
|
+
...(file.text === undefined
|
|
325
|
+
? { base64: expectString(file.base64, at(`overlay.${id}[${index}].base64`)) }
|
|
326
|
+
: { text: expectString(file.text, at(`overlay.${id}[${index}].text`)) }),
|
|
302
327
|
};
|
|
303
328
|
});
|
|
304
329
|
}
|
|
@@ -329,7 +354,9 @@ export function overlayFiles(manifest, format) {
|
|
|
329
354
|
return (manifest.overlay[format] ?? []).map((file) => ({
|
|
330
355
|
path: file.path,
|
|
331
356
|
mode: file.mode,
|
|
332
|
-
source:
|
|
357
|
+
source: file.base64 === undefined
|
|
358
|
+
? { kind: 'text', text: file.text ?? '' }
|
|
359
|
+
: { kind: 'bytes', data: base64Decode(file.base64) },
|
|
333
360
|
}));
|
|
334
361
|
}
|
|
335
362
|
/**
|
|
@@ -224,6 +224,17 @@ export interface FormatDescriptor {
|
|
|
224
224
|
interpreterGap: string;
|
|
225
225
|
/** Where the project's licence text goes in this format's overlay. */
|
|
226
226
|
licenseDest: (binaryName: string) => string;
|
|
227
|
+
/**
|
|
228
|
+
* Where this format wants a packaging changelog, when it wants one at all.
|
|
229
|
+
*
|
|
230
|
+
* OPTIONAL, unlike {@link licenseDest}, and the asymmetry is a policy
|
|
231
|
+
* difference rather than an oversight: Debian Policy § 4.4 makes
|
|
232
|
+
* `share/doc/<package>/changelog.Debian.gz` mandatory and lintian errors
|
|
233
|
+
* without it, while rpm's `%doc` changelog is a convention and `rpm` says
|
|
234
|
+
* nothing when it is absent. A format that leaves this undefined has
|
|
235
|
+
* answered "this format does not require one".
|
|
236
|
+
*/
|
|
237
|
+
changelogDest?: (binaryName: string) => string;
|
|
227
238
|
/**
|
|
228
239
|
* `debian-copyright` renders the machine-readable copyright 1.0 format
|
|
229
240
|
* Debian policy asks for; `plain` copies the licence file verbatim.
|
|
@@ -420,6 +431,8 @@ export interface ShipSettings extends PackSettings {
|
|
|
420
431
|
projectDir: string;
|
|
421
432
|
/** Absolute path to the licence file, when one was found. */
|
|
422
433
|
licenseFile?: string;
|
|
434
|
+
/** Absolute path to the project's own changelog, when one was found. */
|
|
435
|
+
changelogFile?: string;
|
|
423
436
|
/** `'app'` stages a desktop entry and an icon; `'cli'` stages neither. */
|
|
424
437
|
kind: 'app' | 'cli';
|
|
425
438
|
/** Absolute path to the built bundle that `bin/<binaryName>` executes. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.49.0",
|
|
4
4
|
"description": "CLI for Gjsify",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -142,21 +142,21 @@
|
|
|
142
142
|
"cli"
|
|
143
143
|
],
|
|
144
144
|
"dependencies": {
|
|
145
|
-
"@gjsify/buffer": "^0.
|
|
146
|
-
"@gjsify/create-app": "^0.
|
|
147
|
-
"@gjsify/node-globals": "^0.
|
|
148
|
-
"@gjsify/node-polyfills": "^0.
|
|
149
|
-
"@gjsify/npm-registry": "^0.
|
|
150
|
-
"@gjsify/resolve-npm": "^0.
|
|
151
|
-
"@gjsify/rolldown-plugin-gjsify": "^0.
|
|
152
|
-
"@gjsify/vite-plugin-blueprint": "^0.
|
|
153
|
-
"@gjsify/rolldown-plugin-pnp": "^0.
|
|
154
|
-
"@gjsify/semver": "^0.
|
|
155
|
-
"@gjsify/tar": "^0.
|
|
156
|
-
"@gjsify/tsc": "^0.
|
|
157
|
-
"@gjsify/utils": "^0.
|
|
158
|
-
"@gjsify/web-polyfills": "^0.
|
|
159
|
-
"@gjsify/workspace": "^0.
|
|
145
|
+
"@gjsify/buffer": "^0.49.0",
|
|
146
|
+
"@gjsify/create-app": "^0.49.0",
|
|
147
|
+
"@gjsify/node-globals": "^0.49.0",
|
|
148
|
+
"@gjsify/node-polyfills": "^0.49.0",
|
|
149
|
+
"@gjsify/npm-registry": "^0.49.0",
|
|
150
|
+
"@gjsify/resolve-npm": "^0.49.0",
|
|
151
|
+
"@gjsify/rolldown-plugin-gjsify": "^0.49.0",
|
|
152
|
+
"@gjsify/vite-plugin-blueprint": "^0.49.0",
|
|
153
|
+
"@gjsify/rolldown-plugin-pnp": "^0.49.0",
|
|
154
|
+
"@gjsify/semver": "^0.49.0",
|
|
155
|
+
"@gjsify/tar": "^0.49.0",
|
|
156
|
+
"@gjsify/tsc": "^0.49.0",
|
|
157
|
+
"@gjsify/utils": "^0.49.0",
|
|
158
|
+
"@gjsify/web-polyfills": "^0.49.0",
|
|
159
|
+
"@gjsify/workspace": "^0.49.0",
|
|
160
160
|
"acorn": "^8.17.0",
|
|
161
161
|
"cosmiconfig": "^9.0.2",
|
|
162
162
|
"get-tsconfig": "^4.14.0",
|
|
@@ -165,13 +165,13 @@
|
|
|
165
165
|
"yargs": "^18.0.0"
|
|
166
166
|
},
|
|
167
167
|
"devDependencies": {
|
|
168
|
-
"@gjsify/unit": "^0.
|
|
168
|
+
"@gjsify/unit": "^0.49.0",
|
|
169
169
|
"@types/yargs": "^17.0.35",
|
|
170
170
|
"typescript": "^6.0.3"
|
|
171
171
|
},
|
|
172
172
|
"peerDependencies": {
|
|
173
|
-
"@gjsify/oxfmt-native": "^0.
|
|
174
|
-
"@gjsify/rolldown-native": "^0.
|
|
173
|
+
"@gjsify/oxfmt-native": "^0.49.0",
|
|
174
|
+
"@gjsify/rolldown-native": "^0.49.0"
|
|
175
175
|
},
|
|
176
176
|
"peerDependenciesMeta": {
|
|
177
177
|
"@gjsify/oxfmt-native": {
|