tosijs-ui 1.16.23 → 1.16.25
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/CHANGELOG.md +77 -0
- package/bin/format.ts +12 -6
- package/dist/doc-browser.js +15 -4
- package/dist/doc-system/data-client.d.ts +35 -0
- package/dist/doc-system/data-client.js +105 -0
- package/dist/doc-system/site/data-store.d.ts +71 -0
- package/dist/doc-system/site/data-store.js +352 -0
- package/dist/doc-system/site/dev-auth.d.ts +74 -2
- package/dist/doc-system/site/dev-auth.js +171 -2
- package/dist/doc-system/site/dev-server.d.ts +11 -0
- package/dist/doc-system/site/dev-server.js +206 -48
- package/dist/doc-system/site/generate-site.js +1 -1
- package/dist/doc-system/site/index.d.ts +2 -0
- package/dist/doc-system/site/index.js +1 -0
- package/dist/doc-system/site/site-config.d.ts +51 -8
- package/dist/icon-rules.d.ts +4 -2
- package/dist/icon-rules.js +18 -5
- package/dist/icons.js +10 -5
- package/dist/iife.js +102 -94
- package/dist/iife.js.map +9 -8
- package/dist/live-example/component.js +13 -3
- package/dist/live-example/source-access.d.ts +18 -0
- package/dist/live-example/source-access.js +54 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/llms.txt +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,82 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.16.25
|
|
4
|
+
|
|
5
|
+
### Dev server
|
|
6
|
+
|
|
7
|
+
- **`editableData`: folders an editor on the dev page may save to** (#3365). A level or
|
|
8
|
+
scene editor running on a dev-server page can list, read and save the project's data files
|
|
9
|
+
through `/__docstore/data`, behind the same gate as source: this machine, an edit session
|
|
10
|
+
over the tunnel, or the LAN under `trustLan`. Share links stay read-only.
|
|
11
|
+
`editableData: ['static/levels']`, or `{ dirs, extensions, commit }`. Only data formats may
|
|
12
|
+
be allowed (`.json` by default; also `.jsonc` `.json5` `.ndjson` `.txt` `.csv` `.tsv`
|
|
13
|
+
`.yaml` `.yml` `.toml`), so nothing saved there runs or renders. Dot-files and symlinks are
|
|
14
|
+
never written, nor are common configuration names (`package.json`, `tsconfig.json`,
|
|
15
|
+
`firebase.json`, `*.config.*`), though the folders you name are the real boundary. A `.json`
|
|
16
|
+
save must parse, and a save replaces the file whole. A folder outside the project, the
|
|
17
|
+
project itself, one in or around `.git` or `node_modules`, or one overlapping the build
|
|
18
|
+
output stops the server at launch. `tosijs-ui/site` exports the `EditableDataOptions` type
|
|
19
|
+
and the `DATA_EXTENSIONS` list.
|
|
20
|
+
- **`commit: true` makes every save a version.** Each save is its own commit
|
|
21
|
+
(`data: save <file>`, naming who saved it), and other staged work is left alone. The
|
|
22
|
+
repository's hooks run, and each git step gets 20 seconds. Nothing is committed while the
|
|
23
|
+
repository is mid-rebase, mid-merge, cherry-picking, reverting, bisecting or on a detached
|
|
24
|
+
HEAD. `?history=<file>` lists a file's latest 100 versions and `?file=<file>&at=<commit>` reads
|
|
25
|
+
one back. A save that wrote the file but did not commit answers with `commitError` instead of
|
|
26
|
+
reporting a clean save. Each committed save waits for git, so it suits an explicit Save, not
|
|
27
|
+
autosave.
|
|
28
|
+
- **`tosijs-ui/doc-system/data-client`** wraps the endpoint for an editor: `listData`,
|
|
29
|
+
`readData` (text, or `{ json: true }`, or `{ at }`), `saveData` (a string as is, anything
|
|
30
|
+
else as formatted JSON) and `dataHistory`. A refusal rejects with a `DataStoreError` carrying
|
|
31
|
+
the status, and so does a page answering for the endpoint (a static host's catch-all), so
|
|
32
|
+
`listData()` tells an editor whether to offer saving.
|
|
33
|
+
- **`trustLan` can name your network**, so a committed setting trusts nobody on anyone else's:
|
|
34
|
+
`trustLan: '192.168.1.0/24'` or a list, IPv4 or IPv6. Only private addresses inside it are
|
|
35
|
+
trusted. **`TOSIJS_TRUST_LAN`** sets it for one run and wins over the config either way: `1`,
|
|
36
|
+
a network, or `0` to turn a committed setting off. A network that cannot be parsed stops the
|
|
37
|
+
server at launch, and the launch warning names who is trusted and where the setting came from.
|
|
38
|
+
A refused device outside the named networks is told so in the log, and the messages readers
|
|
39
|
+
see when the LAN is refused now suggest these forms rather than `trustLan: true`.
|
|
40
|
+
- **The SPA fallback shows the page the URL names.** Was: a path with no file of its own got
|
|
41
|
+
the root page, which then asked for its assets relative to that path, got 404s and never
|
|
42
|
+
hydrated; with `home` set, a reader saw the home doc. Now the dev server serves the root page
|
|
43
|
+
with its relative URLs made root-relative (in-page `#` links still stay on the page), and
|
|
44
|
+
hydration adopts a pre-rendered article only when it is the doc being shown. Generated pages
|
|
45
|
+
stamp the article with `data-doc` for that. The asset fix is the dev server's; a static host's
|
|
46
|
+
own catch-all rewrite is unchanged.
|
|
47
|
+
|
|
48
|
+
## 1.16.24
|
|
49
|
+
|
|
50
|
+
`dist/iife.js` grows by about 0.2 kB gzip.
|
|
51
|
+
|
|
52
|
+
- **Composition rules chain.** A rule may apply to another rule, so `un:check:square` slashes
|
|
53
|
+
the checked square, and `unCheckLock` draws instead of the placeholder. `iconSvg` reports a
|
|
54
|
+
chained rule as needing the renderer, as it does any rule. Chains stop at four rules deep,
|
|
55
|
+
which keeps a hostile icon name cheap to resolve, and a custom rule whose prefix can match
|
|
56
|
+
nothing no longer recurses. `defineIcons` may now warn about a new name that reads as a
|
|
57
|
+
chain (`unCheckFoo`); it is a warning, and your definition still wins.
|
|
58
|
+
- **The icon composition demo shows the bracket form.** It printed `icons.check:square()`,
|
|
59
|
+
which is not JavaScript; a name with a `:` now shows as `icons['check:square']()`.
|
|
60
|
+
- **Prettier never formats SVG files.** `*.svg` is in `.prettierignore` and a `requirePragma`
|
|
61
|
+
override, and `tosijs-format` excludes it on the command line as it does markdown. Prettier
|
|
62
|
+
has no SVG parser of its own, but an editor association or a plugin such as
|
|
63
|
+
`prettier-plugin-xml` would reflow a drawing tool's export. Adopters using `tosijs-format`
|
|
64
|
+
get the command-line exclusion. To cover editors too, add `*.svg` to `.prettierignore` and
|
|
65
|
+
`{ "files": ["*.svg"], "options": { "requirePragma": true } }` to your Prettier `overrides`.
|
|
66
|
+
|
|
67
|
+
### Dev-server access
|
|
68
|
+
|
|
69
|
+
- **A live example's "Save to source" is offered wherever the dev server admits the page**:
|
|
70
|
+
a LAN device under `trustLan`, or a tunnel visitor with an edit session, not only
|
|
71
|
+
`localhost`. The dev server marks the pages it serves with `editableSources` on, and an
|
|
72
|
+
example on such a page asks with a `HEAD` request when it connects, once per source file; it
|
|
73
|
+
believes only an answer carrying the dev server's own header. A deployed static site is never
|
|
74
|
+
asked.
|
|
75
|
+
- **Every source write admitted by `trustLan` is logged** with the device's address, the file
|
|
76
|
+
(quoted, so a crafted name cannot forge a log line) and whether it was saved.
|
|
77
|
+
- **The dev server suggests `trustLan` only to a refused device on a private network**, the
|
|
78
|
+
one case it would admit; anyone else is pointed at an edit link.
|
|
79
|
+
|
|
3
80
|
## 1.16.23
|
|
4
81
|
|
|
5
82
|
- **`rule:icon` applies an icon composition rule explicitly.** A `:` marks the boundary
|
package/bin/format.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
/*
|
|
3
3
|
tosijs-format — the house format step, shipped as a command (#187).
|
|
4
4
|
|
|
5
|
-
tosijs-format eslint --fix, then prettier --write (markdown excluded)
|
|
6
|
-
tosijs-format --check eslint, then prettier --check (markdown excluded); exit 1 on any finding
|
|
5
|
+
tosijs-format eslint --fix, then prettier --write (markdown and SVG excluded)
|
|
6
|
+
tosijs-format --check eslint, then prettier --check (markdown and SVG excluded); exit 1 on any finding
|
|
7
7
|
|
|
8
8
|
**Prettier never touches markdown** in this stack. The rule itself lives in the Prettier
|
|
9
9
|
CONFIG, where every entry point reads it (editors, lint-staged, a bare `prettier --write`):
|
|
@@ -31,10 +31,11 @@ import { parseArgv } from './resolve-site-config.ts'
|
|
|
31
31
|
|
|
32
32
|
const { has } = parseArgv(process.argv.slice(2), {
|
|
33
33
|
bin: 'tosijs-format',
|
|
34
|
-
summary:
|
|
34
|
+
summary:
|
|
35
|
+
'the house format step: eslint, then prettier (never markdown or SVG)',
|
|
35
36
|
flags: ['check'],
|
|
36
37
|
usage:
|
|
37
|
-
` tosijs-format eslint --fix ., then prettier --write . (markdown excluded)\n` +
|
|
38
|
+
` tosijs-format eslint --fix ., then prettier --write . (markdown and SVG excluded)\n` +
|
|
38
39
|
` tosijs-format --check check only: exit 1 on anything unformatted or any lint error\n\n` +
|
|
39
40
|
`Uses the project's own prettier and eslint (node_modules/.bin). ESLint runs only when the\n` +
|
|
40
41
|
`project has an eslint.config.* file.`,
|
|
@@ -81,13 +82,18 @@ if (hasEslintConfig) {
|
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
|
|
85
|
+
/*
|
|
86
|
+
The exclusion is the point of this command: prettier never touches markdown, or SVG.
|
|
87
|
+
Prettier has no SVG parser of its own, but an editor association or a plugin
|
|
88
|
+
(prettier-plugin-xml) gives it one, and an SVG is a drawing tool's export, not source to
|
|
89
|
+
reflow.
|
|
90
|
+
*/
|
|
85
91
|
if (
|
|
86
92
|
run([
|
|
87
93
|
bin('prettier'),
|
|
88
94
|
check ? '--check' : '--write',
|
|
89
95
|
'.',
|
|
90
|
-
'!**/*.{md,markdown,mdx}',
|
|
96
|
+
'!**/*.{md,markdown,mdx,svg}',
|
|
91
97
|
]) !== 0
|
|
92
98
|
) {
|
|
93
99
|
failed = true
|
package/dist/doc-browser.js
CHANGED
|
@@ -111,6 +111,7 @@ import { tosiLocalized } from './localize.js';
|
|
|
111
111
|
import { popMenu } from './menu.js';
|
|
112
112
|
import { codeEditor } from './code-editor.js';
|
|
113
113
|
import { tosiDiff } from './diff.js';
|
|
114
|
+
import { LAN_REFUSAL_ADVICE } from './live-example/source-access.js';
|
|
114
115
|
/*
|
|
115
116
|
Does this doc contain a REAL ` ```test ` block, as opposed to mentioning one?
|
|
116
117
|
|
|
@@ -954,8 +955,18 @@ export function createDocBrowser(options) {
|
|
|
954
955
|
};
|
|
955
956
|
let adoptInitialContent = contentElement !== undefined && !memoryRouting;
|
|
956
957
|
const showDoc = (doc) => {
|
|
957
|
-
|
|
958
|
-
|
|
958
|
+
/*
|
|
959
|
+
Adopt the pre-render only if it IS this doc. The dev server's SPA fallback serves the root
|
|
960
|
+
page for a path with no file of its own, so `/guide/` can arrive holding the home page's
|
|
961
|
+
article (#3346). The generator stamps `data-doc`; a page without it predates the stamp
|
|
962
|
+
and is adopted as before.
|
|
963
|
+
*/
|
|
964
|
+
const prerendered = contentElement?.dataset.doc;
|
|
965
|
+
const adopt = adoptInitialContent &&
|
|
966
|
+
(prerendered === undefined || prerendered === doc.filename);
|
|
967
|
+
adoptInitialContent = false;
|
|
968
|
+
if (adopt) {
|
|
969
|
+
// leave the pre-rendered HTML untouched
|
|
959
970
|
}
|
|
960
971
|
else {
|
|
961
972
|
// Pass the doc's build-time tjs bakes so this client-rendered page embeds the
|
|
@@ -1291,7 +1302,7 @@ export function createDocBrowser(options) {
|
|
|
1291
1302
|
window.alert('This workspace needs an invite link before it can save.\n\n' +
|
|
1292
1303
|
'Run `tosijs-tunnel --link` on the machine hosting it, open the link once, ' +
|
|
1293
1304
|
'then try again. Your edit is still here.\n\n' +
|
|
1294
|
-
|
|
1305
|
+
LAN_REFUSAL_ADVICE);
|
|
1295
1306
|
}
|
|
1296
1307
|
else {
|
|
1297
1308
|
// Genuinely no write endpoint (a deployed static site) — hand the file back.
|
|
@@ -1306,7 +1317,7 @@ export function createDocBrowser(options) {
|
|
|
1306
1317
|
window.alert(sourceUnauthorized
|
|
1307
1318
|
? 'This workspace needs an invite link before you can edit.\n\n' +
|
|
1308
1319
|
'Run `tosijs-tunnel --link` on the machine hosting it and open the link once.\n\n' +
|
|
1309
|
-
|
|
1320
|
+
LAN_REFUSAL_ADVICE
|
|
1310
1321
|
: `Could not load source for ${doc.path}`);
|
|
1311
1322
|
return;
|
|
1312
1323
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare class DataStoreError extends Error {
|
|
2
|
+
status: number;
|
|
3
|
+
constructor(message: string, status: number);
|
|
4
|
+
}
|
|
5
|
+
/** Every editable file, project-relative, newest first. */
|
|
6
|
+
export declare function listData(): Promise<{
|
|
7
|
+
files: {
|
|
8
|
+
file: string;
|
|
9
|
+
size: number;
|
|
10
|
+
modified: string;
|
|
11
|
+
}[];
|
|
12
|
+
}>;
|
|
13
|
+
/** A file's text, or parsed JSON with `json: true`; `at` reads it as of a commit. */
|
|
14
|
+
export declare function readData(file: string, options: {
|
|
15
|
+
at?: string;
|
|
16
|
+
json: true;
|
|
17
|
+
}): Promise<any>;
|
|
18
|
+
export declare function readData(file: string, options?: {
|
|
19
|
+
at?: string;
|
|
20
|
+
json?: false;
|
|
21
|
+
}): Promise<string>;
|
|
22
|
+
/** Save a file: a string as it is, anything else as formatted JSON. */
|
|
23
|
+
export declare function saveData(file: string, content: unknown): Promise<{
|
|
24
|
+
ok: true;
|
|
25
|
+
file: string;
|
|
26
|
+
commit?: string | null;
|
|
27
|
+
commitError?: string;
|
|
28
|
+
}>;
|
|
29
|
+
/** A file's committed versions, newest first. */
|
|
30
|
+
export declare function dataHistory(file: string): Promise<{
|
|
31
|
+
commit: string;
|
|
32
|
+
date: string;
|
|
33
|
+
author: string;
|
|
34
|
+
subject: string;
|
|
35
|
+
}[]>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/*{ "parent": "Documentation System", "order": 85 }*/
|
|
2
|
+
/*#
|
|
3
|
+
# data client
|
|
4
|
+
|
|
5
|
+
For an editor running on a dev-server page (a level editor, a scene editor) to open and save
|
|
6
|
+
the project's data files through `/__docstore/data`. Turn it on by naming the folders in the
|
|
7
|
+
site config; [`editableData`](/doc-site-system/) in the doc-site system reference has the
|
|
8
|
+
rules.
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
// tosijs-site.config.ts
|
|
12
|
+
editableData: { dirs: ['static/levels'], commit: true }
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { listData, readData, saveData, dataHistory } from 'tosijs-ui/doc-system/data-client'
|
|
17
|
+
|
|
18
|
+
const { files } = await listData()
|
|
19
|
+
const level = await readData('static/levels/ocean.json', { json: true })
|
|
20
|
+
level.spawn = [0, 2, 0]
|
|
21
|
+
const { commit } = await saveData('static/levels/ocean.json', level)
|
|
22
|
+
const versions = await dataHistory('static/levels/ocean.json')
|
|
23
|
+
if (versions.length > 1) {
|
|
24
|
+
const before = await readData('static/levels/ocean.json', { at: versions[1].commit })
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`saveData` writes an object as JSON (two-space indent, trailing newline) and a string as it
|
|
29
|
+
is. With `commit: true` in the config each save is its own commit, and `commit` in the result
|
|
30
|
+
is its hash (`null` when the save changed nothing). A save that wrote but could not commit
|
|
31
|
+
(a failing hook, or a repository mid-rebase) resolves with `commitError` set rather than
|
|
32
|
+
rejecting, because the file was saved. Each committed save runs git and the repository's
|
|
33
|
+
hooks before it answers, so it suits an explicit Save, not autosave.
|
|
34
|
+
|
|
35
|
+
**Dev server only.** Everything else rejects with a `DataStoreError` whose `status` says why:
|
|
36
|
+
`501` when the server has no `editableData`, `403` when this page may not edit files here or
|
|
37
|
+
the file is not editable, `400` for content it refuses (JSON that does not parse). A response
|
|
38
|
+
that is not the endpoint's (a deployed site's catch-all answering with a page) rejects too, so
|
|
39
|
+
`listData()` is the probe for whether to offer saving at all.
|
|
40
|
+
|
|
41
|
+
File names may use letters, digits, `-`, `_`, `.`, `/` and spaces. `dataHistory` returns the
|
|
42
|
+
latest 100 versions.
|
|
43
|
+
*/
|
|
44
|
+
const ENDPOINT = '/__docstore/data';
|
|
45
|
+
export class DataStoreError extends Error {
|
|
46
|
+
status;
|
|
47
|
+
constructor(message, status) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.status = status;
|
|
50
|
+
this.name = 'DataStoreError';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function ask(query, init) {
|
|
54
|
+
const response = await fetch(`${ENDPOINT}${query}`, {
|
|
55
|
+
credentials: 'same-origin',
|
|
56
|
+
...init,
|
|
57
|
+
});
|
|
58
|
+
/*
|
|
59
|
+
The endpoint answers JSON or the file's text, never a page. An HTML answer is something
|
|
60
|
+
else's: a static host's catch-all serving its index for every path.
|
|
61
|
+
*/
|
|
62
|
+
const type = response.headers.get('content-type') ?? '';
|
|
63
|
+
if (response.ok && type.includes('text/html')) {
|
|
64
|
+
throw new DataStoreError(`${init?.method ?? 'GET'} ${ENDPOINT}${query}: answered with a page, not the data endpoint (no dev server here?)`, response.status);
|
|
65
|
+
}
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const text = await response.text();
|
|
68
|
+
let message = text;
|
|
69
|
+
try {
|
|
70
|
+
message = JSON.parse(text).error ?? text;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// plain text
|
|
74
|
+
}
|
|
75
|
+
throw new DataStoreError(`${init?.method ?? 'GET'} ${ENDPOINT}${query}: ${response.status} ${message}`, response.status);
|
|
76
|
+
}
|
|
77
|
+
return response;
|
|
78
|
+
}
|
|
79
|
+
const q = (params) => '?' + new URLSearchParams(params).toString();
|
|
80
|
+
/** Every editable file, project-relative, newest first. */
|
|
81
|
+
export async function listData() {
|
|
82
|
+
return (await ask('?list')).json();
|
|
83
|
+
}
|
|
84
|
+
export async function readData(file, options = {}) {
|
|
85
|
+
const params = { file };
|
|
86
|
+
if (options.at)
|
|
87
|
+
params.at = options.at;
|
|
88
|
+
const text = await (await ask(q(params))).text();
|
|
89
|
+
return options.json ? JSON.parse(text) : text;
|
|
90
|
+
}
|
|
91
|
+
/** Save a file: a string as it is, anything else as formatted JSON. */
|
|
92
|
+
export async function saveData(file, content) {
|
|
93
|
+
const text = typeof content === 'string'
|
|
94
|
+
? content
|
|
95
|
+
: JSON.stringify(content, null, 2) + '\n';
|
|
96
|
+
return (await ask('', {
|
|
97
|
+
method: 'POST',
|
|
98
|
+
headers: { 'Content-Type': 'application/json' },
|
|
99
|
+
body: JSON.stringify({ file, content: text }),
|
|
100
|
+
})).json();
|
|
101
|
+
}
|
|
102
|
+
/** A file's committed versions, newest first. */
|
|
103
|
+
export async function dataHistory(file) {
|
|
104
|
+
return (await (await ask(q({ history: file }))).json()).versions;
|
|
105
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** Formats a data folder may hold. Text only: the endpoint takes and returns strings. */
|
|
2
|
+
export declare const DATA_EXTENSIONS: readonly [".json", ".jsonc", ".json5", ".ndjson", ".txt", ".csv", ".tsv", ".yaml", ".yml", ".toml"];
|
|
3
|
+
/** The largest save accepted, in bytes. A level is kilobytes; this stops a runaway client. */
|
|
4
|
+
export declare const MAX_DATA_BYTES: number;
|
|
5
|
+
export interface EditableDataOptions {
|
|
6
|
+
/** Folders, relative to the project root. */
|
|
7
|
+
dirs: string[];
|
|
8
|
+
/** Extensions to allow, from DATA_EXTENSIONS. Default `['.json']`. */
|
|
9
|
+
extensions?: string[];
|
|
10
|
+
/** Commit each save to git, so every save is a version. Default false. */
|
|
11
|
+
commit?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface DataStore {
|
|
14
|
+
root: string;
|
|
15
|
+
/** Real paths of the folders, and the names they were configured as. */
|
|
16
|
+
dirs: {
|
|
17
|
+
real: string;
|
|
18
|
+
name: string;
|
|
19
|
+
}[];
|
|
20
|
+
extensions: string[];
|
|
21
|
+
commit: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** The folder setting, checked and resolved, or null when the project has none. Throws. */
|
|
24
|
+
export declare function resolveDataStore(setting: string[] | EditableDataOptions | undefined, root: string,
|
|
25
|
+
/** The site's build output, which every build deletes and rewrites. */
|
|
26
|
+
outputDir?: string): DataStore | null;
|
|
27
|
+
/**
|
|
28
|
+
* The absolute path a client's file name may be read or written at, or the reason it may not.
|
|
29
|
+
* `file` is project-relative (`static/levels/ocean.json`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function resolveDataFile(store: DataStore, file: unknown): {
|
|
32
|
+
path: string;
|
|
33
|
+
rel: string;
|
|
34
|
+
} | {
|
|
35
|
+
refused: string;
|
|
36
|
+
};
|
|
37
|
+
/** Why `content` may not be saved as `rel`, or null. JSON must parse: a half-edit is not a level. */
|
|
38
|
+
export declare function dataContentProblem(rel: string, content: unknown): string | null;
|
|
39
|
+
/** Write through a temporary file and a rename, so a reader never sees half a file. */
|
|
40
|
+
export declare function writeDataFile(target: string, content: string): void;
|
|
41
|
+
/** Every editable file in the named folders, project-relative, newest first. */
|
|
42
|
+
export declare function listDataFiles(store: DataStore): {
|
|
43
|
+
file: string;
|
|
44
|
+
size: number;
|
|
45
|
+
modified: string;
|
|
46
|
+
}[];
|
|
47
|
+
/** Whether `root` is in a git work tree, for `commit: true` to be checked at launch. */
|
|
48
|
+
export declare function isGitWorkTree(root: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Save a file. With `commit` on, also commit it, alone, leaving other staged work as it was:
|
|
51
|
+
* `commit` is the hash, or null when the save changed nothing. A save that wrote the file but
|
|
52
|
+
* could not commit it resolves with `commitError`, because the file WAS saved. The repository's
|
|
53
|
+
* hooks run, as for any commit; a repository mid-rebase, mid-merge, bisecting or on a detached
|
|
54
|
+
* HEAD is not committed to, since that commit would land somewhere the developer did not choose.
|
|
55
|
+
*/
|
|
56
|
+
export declare function saveDataFile(store: DataStore, target: {
|
|
57
|
+
path: string;
|
|
58
|
+
rel: string;
|
|
59
|
+
}, content: string, savedBy: string): Promise<{
|
|
60
|
+
commit?: string | null;
|
|
61
|
+
commitError?: string;
|
|
62
|
+
}>;
|
|
63
|
+
/** One file's committed versions, newest first. */
|
|
64
|
+
export declare function dataHistory(store: DataStore, rel: string, limit?: number): Promise<{
|
|
65
|
+
commit: string;
|
|
66
|
+
date: string;
|
|
67
|
+
author: string;
|
|
68
|
+
subject: string;
|
|
69
|
+
}[]>;
|
|
70
|
+
/** A file as it was at `commit`, or null if the commit is not a hash or lacks the file. */
|
|
71
|
+
export declare function readDataAt(store: DataStore, rel: string, commit: string): Promise<string | null>;
|