rapier-html 1.1.58 → 1.1.62
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/BUILD.json +4 -4
- package/README.md +8 -8
- package/dist/ledger/LICENSE +8 -2
- package/dist/ledger/carried.mjs +51 -0
- package/dist/page.mjs +8 -114
- package/dist/rapier.html +174 -142
- package/dist/wrap.mjs +108 -0
- package/package.json +1 -1
package/BUILD.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "rapier-html",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.62",
|
|
4
4
|
"path": "dist/rapier.html",
|
|
5
|
-
"bytes":
|
|
6
|
-
"sha256": "
|
|
7
|
-
"builtAt": "2026-10-
|
|
5
|
+
"bytes": 1838935,
|
|
6
|
+
"sha256": "160c45f037b0badded610c60016729527f05a26b68e04570862ec4137a4ea342",
|
|
7
|
+
"builtAt": "2026-10-05T00:10:34.442Z",
|
|
8
8
|
"node": "v22.22.2",
|
|
9
9
|
"canonical": true,
|
|
10
10
|
"packing": "zopfli",
|
package/README.md
CHANGED
|
@@ -14,14 +14,14 @@ editor, offline, with the document inside.
|
|
|
14
14
|
Node 22 or newer.
|
|
15
15
|
|
|
16
16
|
```sh
|
|
17
|
-
npx -- rapier-html@1.1.
|
|
18
|
-
npx -- rapier-html@1.1.
|
|
19
|
-
npx -- rapier-html@1.1.
|
|
20
|
-
npx -- rapier-html@1.1.
|
|
21
|
-
npx -- rapier-html@1.1.
|
|
22
|
-
npx -- rapier-html@1.1.
|
|
23
|
-
npx -- rapier-html@1.1.
|
|
24
|
-
npm install rapier-html@1.1.
|
|
17
|
+
npx -- rapier-html@1.1.62 notes.md # the editor, notes.md inside it, written beside the file
|
|
18
|
+
npx -- rapier-html@1.1.62 notes.md --view draw # opens on Draw: sketch and paint, the document behind it
|
|
19
|
+
npx -- rapier-html@1.1.62 notes.md --view notes # opens the Notes library
|
|
20
|
+
npx -- rapier-html@1.1.62 notes.md --drawing sketch.svg # carries a drawing, opened on Draw over the document
|
|
21
|
+
npx -- rapier-html@1.1.62 proposal.md --base original.md # opens on the diff of a proposed change
|
|
22
|
+
npx -- rapier-html@1.1.62 notes.md --return "$RETURN_URL" --return-expires-at "$RETURN_EXPIRES_AT" # Send back
|
|
23
|
+
npx -- rapier-html@1.1.62 notes.md out.html # a named output
|
|
24
|
+
npm install rapier-html@1.1.62 # as a library: wrap, unwrap
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
The first `--` is for npm, so `--help` reaches the command. It never overwrites a file.
|
package/dist/ledger/LICENSE
CHANGED
|
@@ -24,13 +24,19 @@ Files under this licence carry `SPDX-License-Identifier: MIT` in their first lin
|
|
|
24
24
|
grammar, marks and reference style (`spec/md-layout.mjs`, `spec/md-marks.mjs`, `spec/flowchart.mjs`, `spec/markdown-style.css`), the layout model and line planner
|
|
25
25
|
(`layout/model.mjs`, `layout/line-plan.mjs`), the Will grammar (`agent/will.mjs`), the picture appendix reader and writer
|
|
26
26
|
(`spec/md-assets.mjs`), the two shared-page reference readers (`tools/read-shared-page.mjs`,
|
|
27
|
-
`tools/read-shared-page.py`), and the host helper `packages/rapier-embed/embed.mjs
|
|
27
|
+
`tools/read-shared-page.py`), and the host helper `packages/rapier-embed/embed.mjs` with its
|
|
28
|
+
contract owner `packages/rapier-embed/contract.mjs`. The vendored Pretext (`agent/vendor/pretext`) and micromark carry their
|
|
28
29
|
own MIT notices. The edit-ledger module (`kit/rapier-ledger.mjs`, `kit/ledger/*.mjs`) and
|
|
29
30
|
its editor compatibility import (`editor/journal-records.mjs`) are also covered. The
|
|
30
31
|
record validator/replayer and text-only conflict merger were moved, not copied,
|
|
31
32
|
into that module; the Notes index merger and the rest of the agent/editor remain
|
|
32
33
|
AGPL-3.0-only. The wrapper's carrier-staging helper
|
|
33
|
-
(`skills/rapier-html/package-files.mjs`) is MIT.
|
|
34
|
+
(`skills/rapier-html/package-files.mjs`) is MIT. The kit's MIT-marked facades,
|
|
35
|
+
`kit/render-entry.mjs`, `kit/render*.mjs`, `kit/stage.mjs` and MIT-marked conformance
|
|
36
|
+
modules are covered too. These names describe the Rapier-owned files carrying the
|
|
37
|
+
MIT notice, not a grant over the editor or third-party code. Other Rapier-owned
|
|
38
|
+
files retain their stated licence; the editor is AGPL-3.0-only (see `LICENSE`).
|
|
39
|
+
Third-party components retain their own copyright and licence notices.
|
|
34
40
|
|
|
35
41
|
The picture appendix reader and writer used to be part of `images/assets.mjs`, a file that also
|
|
36
42
|
holds JPEG XL/raster header inspection and SVG sanitizing -- both of which reach the editor's own
|
package/dist/ledger/carried.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
import {readLedger} from './format.mjs';
|
|
3
|
+
import {sha256} from './hash.mjs';
|
|
3
4
|
import {authorship as projectAuthorship, readAuthorship} from './authorship.mjs';
|
|
4
5
|
import {canonicalJSON, fault} from './data.mjs';
|
|
5
6
|
export function encodeCarried(text) {
|
|
@@ -67,3 +68,53 @@ export function readDocument(source) {
|
|
|
67
68
|
const text = source.slice(0, at);
|
|
68
69
|
return {text, ...validateParts(text, parts)};
|
|
69
70
|
}
|
|
71
|
+
|
|
72
|
+
// One portable baseline. A digest is integrity evidence, not authentication of the named proposer.
|
|
73
|
+
export function readBase(value) {
|
|
74
|
+
if (!value || typeof value !== 'object' || typeof value.text !== 'string' ||
|
|
75
|
+
typeof value.sha256 !== 'string' || !/^[a-f0-9]{64}$/.test(value.sha256) || sha256(value.text) !== value.sha256)
|
|
76
|
+
throw fault('proposal base SHA-256 differs from its text');
|
|
77
|
+
if (typeof value.revision !== 'string' || !value.revision || value.revision.length > 256 || /[\u0000-\u001f\u007f]/.test(value.revision))
|
|
78
|
+
throw fault('invalid proposal base revision');
|
|
79
|
+
if (typeof value.name !== 'string' || !value.name || value.name.length > 255 || /[\\/\u0000-\u001f\u007f]/.test(value.name))
|
|
80
|
+
throw fault('invalid proposal base name');
|
|
81
|
+
if (typeof value.by !== 'string' || !value.by.trim() || value.by.length > 96 || /[\u0000-\u001f\u007f]/.test(value.by))
|
|
82
|
+
throw fault('a proposal needs its proposer name');
|
|
83
|
+
if (typeof value.at !== 'string' || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/.test(value.at) || !Number.isFinite(Date.parse(value.at)))
|
|
84
|
+
throw fault('the time of a proposal is an ISO 8601 UTC timestamp');
|
|
85
|
+
return {text: value.text, sha256: value.sha256, revision: value.revision, name: value.name, by: value.by, at: value.at};
|
|
86
|
+
}
|
|
87
|
+
const escapeAttribute = value => value.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
88
|
+
const readAttribute = value => value.replace(/&(quot|lt|gt|amp);/g, (_, name) => ({quot: '"', lt: '<', gt: '>', amp: '&'})[name]);
|
|
89
|
+
export function writeBase(text, value) {
|
|
90
|
+
if (value == null) return '';
|
|
91
|
+
const base = readBase(value);
|
|
92
|
+
if (base.text === text) return '';
|
|
93
|
+
return '<script type="text/markdown" id="rapier-base"' + ['name', 'sha256', 'revision', 'by', 'at']
|
|
94
|
+
.map(key => ' data-' + key + '="' + escapeAttribute(base[key]) + '"').join('') + '>' + encodeCarried(base.text) + '</script>';
|
|
95
|
+
}
|
|
96
|
+
export function readBaseElements(elements) {
|
|
97
|
+
const rows = [...elements].filter(element => element.id === 'rapier-base');
|
|
98
|
+
if (!rows.length) return null;
|
|
99
|
+
const row = rows[0];
|
|
100
|
+
if (rows.length !== 1 || row.type !== 'text/markdown' || (row.tagName && row.tagName.toLowerCase() !== 'script'))
|
|
101
|
+
throw fault('duplicate or executable proposal base');
|
|
102
|
+
const get = name => row.getAttribute ? row.getAttribute('data-' + name) : row[name];
|
|
103
|
+
return readBase({text: decodeCarried(row.textContent), ...Object.fromEntries(['name', 'sha256', 'revision', 'by', 'at'].map(key => [key, get(key)]))});
|
|
104
|
+
}
|
|
105
|
+
export function takeBase(html) {
|
|
106
|
+
const elements = [];
|
|
107
|
+
const stripped = html.replace(/<script\b([^>]*)>([\s\S]*?)<\/script\s*>/gi, (whole, attrs, content) => {
|
|
108
|
+
const pairs = [...attrs.matchAll(/(?:^|\s)([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/g)].map(m => [m[1].toLowerCase(), readAttribute(m[2] ?? m[3] ?? m[4])]);
|
|
109
|
+
if (!pairs.some(([key, value]) => key === 'id' && value === 'rapier-base')) return whole;
|
|
110
|
+
const seen = new Set(), values = {};
|
|
111
|
+
for (const [key, value] of pairs) {
|
|
112
|
+
if (seen.has(key)) throw fault('ambiguous proposal base');
|
|
113
|
+
seen.add(key); values[key] = value;
|
|
114
|
+
}
|
|
115
|
+
elements.push({id: values.id, type: values.type, textContent: content,
|
|
116
|
+
...Object.fromEntries(['name', 'sha256', 'revision', 'by', 'at'].map(key => [key, values['data-' + key]]))});
|
|
117
|
+
return '';
|
|
118
|
+
});
|
|
119
|
+
return {html: stripped, base: readBaseElements(elements)};
|
|
120
|
+
}
|
package/dist/page.mjs
CHANGED
|
@@ -1,123 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
2
|
import {readFile, writeFile} from 'node:fs/promises';
|
|
3
3
|
import {basename, dirname, join} from 'node:path';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const DRAWING_START = '<script type="text/plain" id="rapier-drawing"';
|
|
8
|
-
// The text the document was proposed against (docs/page-door.md, "2. A change carried"): the page opens on its diff.
|
|
9
|
-
const BASE_START = '<script type="text/markdown" id="rapier-base"';
|
|
10
|
-
const END = '</script>';
|
|
11
|
-
// The page's search words (rapier.html's RAPIER_SEO regions: description, canonical address, previews, structured data and the plain guide
|
|
12
|
-
// a crawler reads) are rapier.website's own. A page that carries someone's document never claims that address or that description.
|
|
13
|
-
const SEARCH_WORDS = /<!-- RAPIER_SEO_BEGIN -->[\s\S]*?<!-- RAPIER_SEO_END -->\n?/g;
|
|
14
|
-
|
|
15
|
-
// One encoding, exactly reversible (docs/page-door.md, "What a block may hold"): no `<` before `/` or `!`, no CR, no NUL.
|
|
16
|
-
// engine.js _rapierDecodeCarried is its inverse.
|
|
17
|
-
export {encodeCarried, decodeCarried};
|
|
18
|
-
|
|
19
|
-
const PROPOSED_AT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z$/;
|
|
20
|
-
const attribute = text => text.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
21
|
-
const unattribute = text => text.replace(/&(quot|lt|gt|amp);/g, (_, e) => ({quot: '"', lt: '<', gt: '>', amp: '&'})[e]);
|
|
22
|
-
function safeName(name, fallback) {
|
|
23
|
-
return String(name || fallback).replace(/[\\/\0"<>&]/g, '-').slice(0, 255) || fallback;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function wrap(pageHtml, text, name, options) {
|
|
27
|
-
if (typeof pageHtml !== 'string' || !pageHtml.includes('</body>')) throw new Error('not a Rapier page: no </body>');
|
|
28
|
-
if (typeof text !== 'string') throw new Error('the document must be text');
|
|
29
|
-
const saved = readDocument(text);
|
|
30
|
-
text = saved.text;
|
|
31
|
-
const opts = {...saved, ...(options || {})};
|
|
32
|
-
const docName = safeName(name, 'document.md');
|
|
33
|
-
// `view`: the view the page opens on with the document behind it, `draw` or `notes` (docs/page-door.md).
|
|
34
|
-
if (opts.view != null && !['draw', 'notes'].includes(opts.view)) throw new Error('the view is draw or notes');
|
|
35
|
-
const address = opts.return == null ? null : returnAddress(opts.return);
|
|
36
|
-
const expiry = opts.return_expires_at == null ? null : returnExpiresAt(opts.return_expires_at);
|
|
37
|
-
if (!!address !== !!expiry) throw new Error('a return needs its URL and return_expires_at from document.create_return');
|
|
38
|
-
const block = START + ' data-name="' + docName + '"' + (opts.view ? ' data-view="' + opts.view + '"' : '') +
|
|
39
|
-
(address ? ' data-return="' + address + '" data-return-expires-at="' + expiry + '"' : '') + '>' + encodeCarried(text) + END;
|
|
40
|
-
let carriedBlocks = '\n' + writeParts(text, opts);
|
|
41
|
-
if (opts.drawing != null) {
|
|
42
|
-
if (typeof opts.drawing !== 'string') throw new Error('the drawing must be SVG text');
|
|
43
|
-
const drawingName = safeName(opts.drawingName, 'drawing.svg');
|
|
44
|
-
carriedBlocks += '\n' + DRAWING_START + ' data-name="' + drawingName + '">' + encodeCarried(opts.drawing) + END;
|
|
45
|
-
}
|
|
46
|
-
if ((opts.by != null || opts.at != null) && opts.base == null) throw new Error('a proposer needs the base it proposes over');
|
|
47
|
-
if (opts.base != null) {
|
|
48
|
-
if (typeof opts.base !== 'string') throw new Error('the base must be text');
|
|
49
|
-
// A proposal page (docs/briefs/ledger.md section 3): who proposed it, in the host's own words, and when.
|
|
50
|
-
const by = opts.by == null ? null : String(opts.by).replace(/[\0-\x1f\x7f]/g, ' ').trim().slice(0, 96);
|
|
51
|
-
const at = opts.at == null ? null : String(opts.at);
|
|
52
|
-
if (at != null && !(PROPOSED_AT.test(at) && Number.isFinite(Date.parse(at)))) throw new Error('the time of a proposal is an ISO 8601 UTC timestamp');
|
|
53
|
-
carriedBlocks += '\n' + BASE_START + ' data-name="' + safeName(opts.baseName, docName) + '"' + (by ? ' data-by="' + attribute(by) + '"' : '') +
|
|
54
|
-
(at ? ' data-at="' + at + '"' : '') + '>' + encodeCarried(opts.base) + END;
|
|
55
|
-
}
|
|
56
|
-
// The agent seed goes after the charset (or <head>), once: a page that already carries it, wrapped again, loses its old one.
|
|
57
|
-
const unseeded = unwrap(pageHtml).html.replace(SEARCH_WORDS, '').split(PAGE_SEED + '\n').join('');
|
|
58
|
-
const headTag = /<meta charset[^>]*>\n?/i.exec(unseeded) || /<head[^>]*>\n?/.exec(unseeded);
|
|
59
|
-
if (!headTag) throw new Error('not a Rapier page: no <head>');
|
|
60
|
-
const seededAt = headTag.index + headTag[0].length;
|
|
61
|
-
const stripped = unseeded.slice(0, seededAt) + PAGE_SEED + '\n' + unseeded.slice(seededAt);
|
|
62
|
-
// Before Rapier's scripts: parsed before the engine boots.
|
|
63
|
-
const bodyTag = /<body[^>]*>/.exec(stripped);
|
|
64
|
-
if (!bodyTag) throw new Error('not a Rapier page: no <body>');
|
|
65
|
-
const at = bodyTag.index + bodyTag[0].length;
|
|
66
|
-
// A function, not a string: `$'`, `$&` and `` $` `` are replacement patterns.
|
|
67
|
-
const titled = stripped.replace(/<title>[^<]*<\/title>/, () => '<title>' + docName.replace(/&/g, '&').replace(/</g, '<') + ' — Rapier</title>');
|
|
68
|
-
const shift = titled.length - stripped.length;
|
|
69
|
-
return titled.slice(0, at + shift) + '\n' + block + carriedBlocks + titled.slice(at + shift);
|
|
70
|
-
}
|
|
71
|
-
export function unwrap(pageHtml) {
|
|
72
|
-
let html = pageHtml, text = null, name = null, view = null, address = null, expiry = null;
|
|
73
|
-
const a = pageHtml.indexOf(START);
|
|
74
|
-
if (a >= 0) {
|
|
75
|
-
const open = pageHtml.indexOf('>', a), b = pageHtml.indexOf(END, open);
|
|
76
|
-
if (open < 0 || b < 0) throw new Error('a carried document without its end');
|
|
77
|
-
name = /data-name="([^"]*)"/.exec(pageHtml.slice(a, open))?.[1] ?? null;
|
|
78
|
-
view = /data-view="(draw|notes)"/.exec(pageHtml.slice(a, open))?.[1] ?? null;
|
|
79
|
-
const carriedReturn = /data-return="([^"]*)"/.exec(pageHtml.slice(a, open));
|
|
80
|
-
if (carriedReturn) address = returnAddress(carriedReturn[1]);
|
|
81
|
-
const carriedExpiry = /data-return-expires-at="([^"]*)"/.exec(pageHtml.slice(a, open));
|
|
82
|
-
if (carriedExpiry) expiry = returnExpiresAt(carriedExpiry[1]);
|
|
83
|
-
if (!!address !== !!expiry) throw new Error('a carried return needs its URL and return_expires_at');
|
|
84
|
-
text = decodeCarried(pageHtml.slice(open + 1, b));
|
|
85
|
-
html = pageHtml.slice(0, a) + pageHtml.slice(b + END.length);
|
|
86
|
-
if (a > 0 && html[a - 1] === '\n') html = html.slice(0, a - 1) + html.slice(a);
|
|
87
|
-
}
|
|
88
|
-
let drawing = null, drawingName = null;
|
|
89
|
-
const d = html.indexOf(DRAWING_START);
|
|
90
|
-
if (d >= 0) {
|
|
91
|
-
const open = html.indexOf('>', d), b = html.indexOf(END, open);
|
|
92
|
-
if (open < 0 || b < 0) throw new Error('a carried drawing without its end');
|
|
93
|
-
drawingName = /data-name="([^"]*)"/.exec(html.slice(d, open))?.[1] ?? null;
|
|
94
|
-
drawing = decodeCarried(html.slice(open + 1, b));
|
|
95
|
-
html = html.slice(0, d) + html.slice(b + END.length);
|
|
96
|
-
if (d > 0 && html[d - 1] === '\n') html = html.slice(0, d - 1) + html.slice(d);
|
|
97
|
-
}
|
|
98
|
-
let base = null, baseName = null, by = null, at = null;
|
|
99
|
-
const e = html.indexOf(BASE_START);
|
|
100
|
-
if (e >= 0) {
|
|
101
|
-
const open = html.indexOf('>', e), b = html.indexOf(END, open);
|
|
102
|
-
if (open < 0 || b < 0) throw new Error('a carried base without its end');
|
|
103
|
-
baseName = /data-name="([^"]*)"/.exec(html.slice(e, open))?.[1] ?? null;
|
|
104
|
-
const proposer = /data-by="([^"]*)"/.exec(html.slice(e, open))?.[1];
|
|
105
|
-
by = proposer == null ? null : unattribute(proposer);
|
|
106
|
-
at = /data-at="([^"]*)"/.exec(html.slice(e, open))?.[1] ?? null;
|
|
107
|
-
base = decodeCarried(html.slice(open + 1, b));
|
|
108
|
-
html = html.slice(0, e) + html.slice(b + END.length);
|
|
109
|
-
if (e > 0 && html[e - 1] === '\n') html = html.slice(0, e - 1) + html.slice(e);
|
|
110
|
-
}
|
|
111
|
-
const parts = takeParts(html, text);
|
|
112
|
-
return {...parts, text, name, view, drawing, drawingName, base, baseName, by, at, return: address, return_expires_at: expiry};
|
|
113
|
-
}
|
|
4
|
+
import {wrap} from './wrap.mjs';
|
|
5
|
+
import {returnAddress, returnExpiresAt} from './return-address.mjs';
|
|
6
|
+
export {wrap, unwrap, encodeCarried, decodeCarried} from './wrap.mjs';
|
|
114
7
|
// Unknown options, a bare --drawing or --base and a third name are refused, never dropped.
|
|
115
8
|
const USAGE = 'usage: rapier-html <document.md> [out.html] [--view draw|notes] [--drawing sketch.svg] [--base original.md [--by <your name>]] [--return <url> --return-expires-at <timestamp>]\n' +
|
|
116
9
|
' writes <document>.rapier.html beside the file: Rapier with the document inside it, one file, offline.\n' +
|
|
117
10
|
' It never overwrites: an output that already exists, the document itself included, is refused.\n' +
|
|
118
11
|
' --view opens the page on Draw (sketch and paint, the document behind it) or on Notes\n' +
|
|
119
12
|
' --drawing carries an SVG drawing alongside the document, opened on Draw as the page boots\n' +
|
|
120
|
-
' --base
|
|
13
|
+
' --propose <base.md> <proposed.md>: carries the original base for per-change review\n' +
|
|
14
|
+
' --base the older spelling of --propose; carries the text the document was proposed against; the page opens on their diff\n' +
|
|
121
15
|
' --by with --base: who proposes it, shown on the diff with the time the page was made\n' +
|
|
122
16
|
' --return carries a one-use return URL from document.create_return; Share offers Send back\n' +
|
|
123
17
|
' --return-expires-at carries the same mint\'s expiry; Share offers Save once the return expires\n' +
|
|
@@ -135,7 +29,7 @@ export async function main(argv, pagesDir, {log = console.log} = {}) {
|
|
|
135
29
|
if (view !== null || !['draw', 'notes'].includes(argv[i + 1])) throw new Error('--view takes draw or notes');
|
|
136
30
|
view = argv[++i];
|
|
137
31
|
}
|
|
138
|
-
else if (arg === '--base') {
|
|
32
|
+
else if (arg === '--base' || arg === '--propose') {
|
|
139
33
|
if (basePath !== null || !argv[i + 1] || argv[i + 1].startsWith('-')) throw new Error('--base takes one text file (write a name that starts with a dash as ./-original.md)');
|
|
140
34
|
basePath = argv[++i];
|
|
141
35
|
}
|
|
@@ -164,7 +58,7 @@ export async function main(argv, pagesDir, {log = console.log} = {}) {
|
|
|
164
58
|
const wrapOptions = {};
|
|
165
59
|
if (drawingPath) { wrapOptions.drawing = await readFile(drawingPath, 'utf8'); wrapOptions.drawingName = basename(drawingPath); }
|
|
166
60
|
if (by !== null && !basePath) throw new Error('--by names who proposes a change: give the --base it proposes over');
|
|
167
|
-
if (basePath) { wrapOptions.base = await readFile(basePath, 'utf8'); wrapOptions.baseName = basename(basePath); }
|
|
61
|
+
if (basePath) { wrapOptions.base = await readFile(basePath, 'utf8'); wrapOptions.baseName = basename(basePath); wrapOptions.at = new Date().toISOString(); }
|
|
168
62
|
if (by !== null) { wrapOptions.by = by; wrapOptions.at = new Date().toISOString(); }
|
|
169
63
|
if (view) wrapOptions.view = view;
|
|
170
64
|
if (address) wrapOptions.return = address;
|