hollware 0.5.0 → 0.8.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/README.md +3 -2
- package/dist/{d1-D5A6OVDD.mjs → d1-CK4nF7p5.mjs} +16 -4
- package/dist/{deploy-B89D8gdO.mjs → deploy-4tusYOMV.mjs} +37 -6
- package/dist/{doctor-Djx_3mLE.mjs → doctor-DiKAktVd.mjs} +3 -3
- package/dist/index.mjs +6 -6
- package/dist/{init-D-q1JzcO.mjs → init-CBZPzLBs.mjs} +50 -11
- package/dist/{invite-PpZ5QNLx.mjs → invite-Ck-aqpN8.mjs} +3 -3
- package/dist/{migrate-D6Sgxnwx.mjs → migrate-BBfF-mkE.mjs} +35 -5
- package/dist/migrate-Ka2Mm06e.mjs +2 -0
- package/dist/{public-url-DURHzCaC.mjs → public-url-NLPRzxCN.mjs} +1 -1
- package/dist/token-BND4Rsxj.mjs +2 -0
- package/dist/{token-aUnd79vh.mjs → token-CLQpD4ed.mjs} +1 -1
- package/package.json +2 -2
- package/dist/migrate-R7NAXgQT.mjs +0 -2
- package/dist/token-DeVLRP0z.mjs +0 -2
package/README.md
CHANGED
|
@@ -63,8 +63,9 @@ reported, and the command exits with code 1. The token never appears in its outp
|
|
|
63
63
|
|
|
64
64
|
`init`, `migrate`, `deploy`, `doctor` and `token` run the project's own `wrangler` (the nearest
|
|
65
65
|
`node_modules/.bin/wrangler`) against its D1 database, and read the binding from `wrangler.jsonc`.
|
|
66
|
-
`import` speaks MCP to `<url>/mcp` with the token as a bearer token. The
|
|
67
|
-
`
|
|
66
|
+
`import` speaks MCP to `<url>/mcp` with the token as a bearer token. The structure of the sites
|
|
67
|
+
lives in the database (ADR-033): `init` gives a new site its first definition from a preset, and
|
|
68
|
+
its owner shapes it in the admin, or an agent over MCP.
|
|
68
69
|
|
|
69
70
|
## Links
|
|
70
71
|
|
|
@@ -157,18 +157,29 @@ function insertToken(token) {
|
|
|
157
157
|
const kind = token.kind === void 0 ? ["", ""] : [", kind", `, ${quote(token.kind)}`];
|
|
158
158
|
return `INSERT INTO tokens (id, site_id, user_id, label, hash, scopes, expires_at, created_at, updated_at${kind[0]}) VALUES (${quote(token.id)}, ${quote(token.site)}, ${quote(token.userId)}, ${quote(token.label)}, ${quote(token.hash)}, ${quote(JSON.stringify(token.scopes))}, ${quote(token.expiresAt)}, ${quote(token.now)}, ${quote(token.now)}${kind[1]});`;
|
|
159
159
|
}
|
|
160
|
+
/** The row of the site: with a definition, given only to a site that has none yet. */
|
|
161
|
+
function siteRow(site, now, structure) {
|
|
162
|
+
if (structure === void 0) return `INSERT INTO sites (id, created_at, updated_at) VALUES (${quote(site)}, ${quote(now)}, ${quote(now)}) ON CONFLICT (id) DO NOTHING;`;
|
|
163
|
+
return `INSERT INTO sites (id, definition, version, created_at, updated_at) VALUES (${quote(site)}, ${quote(JSON.stringify(structure.definition))}, 1, ${quote(now)}, ${quote(now)}) ON CONFLICT (id) DO UPDATE SET definition = excluded.definition, version = 1, updated_at = excluded.updated_at WHERE sites.definition IS NULL;`;
|
|
164
|
+
}
|
|
165
|
+
/** The change that made version 1 of the site, by its owner, if this script made it. */
|
|
166
|
+
function siteChange(site, ownerId, now, structure) {
|
|
167
|
+
if (structure === void 0) return [];
|
|
168
|
+
return [`INSERT OR IGNORE INTO site_changes (id, site_id, version, actor_user_id, actor_meta, before, after, created_at, updated_at) SELECT ${quote(structure.changeId)}, id, 1, ${quote(ownerId)}, NULL, NULL, definition, ${quote(now)}, ${quote(now)} FROM sites WHERE id = ${quote(site)} AND version = 1 AND updated_at = ${quote(now)};`];
|
|
169
|
+
}
|
|
160
170
|
/**
|
|
161
171
|
* What `init` writes, in one script: the site (left alone if it is already there), its owner, a
|
|
162
172
|
* member of the site with the role owner, and the owner's first token. `users.role` gets the
|
|
163
173
|
* owner too, the one name of a role that its CHECK and `members` share.
|
|
164
174
|
*/
|
|
165
|
-
function initScript({ site, ownerId, memberId, ownerName, token }) {
|
|
175
|
+
function initScript({ site, ownerId, memberId, ownerName, token, structure }) {
|
|
166
176
|
const name = ownerName === void 0 ? "NULL" : quote(ownerName);
|
|
167
177
|
return [
|
|
168
|
-
|
|
178
|
+
siteRow(site, token.now, structure),
|
|
169
179
|
`INSERT INTO users (id, name, role, created_at, updated_at) VALUES (${quote(ownerId)}, ${name}, 'owner', ${quote(token.now)}, ${quote(token.now)});`,
|
|
170
180
|
`INSERT INTO members (id, site_id, user_id, role, created_at, updated_at) VALUES (${quote(memberId)}, ${quote(site)}, ${quote(ownerId)}, 'owner', ${quote(token.now)}, ${quote(token.now)});`,
|
|
171
181
|
insertToken(token),
|
|
182
|
+
...siteChange(site, ownerId, token.now, structure),
|
|
172
183
|
""
|
|
173
184
|
].join("\n");
|
|
174
185
|
}
|
|
@@ -176,11 +187,12 @@ function initScript({ site, ownerId, memberId, ownerName, token }) {
|
|
|
176
187
|
* What `init` writes for a site of a deployment that has an owner already: the site, the owner as
|
|
177
188
|
* its owner too (one person across the sites of a deployment), and a token of that site.
|
|
178
189
|
*/
|
|
179
|
-
function anotherSiteScript({ site, ownerId, memberId, token }) {
|
|
190
|
+
function anotherSiteScript({ site, ownerId, memberId, token, structure }) {
|
|
180
191
|
return [
|
|
181
|
-
|
|
192
|
+
siteRow(site, token.now, structure),
|
|
182
193
|
`INSERT INTO members (id, site_id, user_id, role, created_at, updated_at) VALUES (${quote(memberId)}, ${quote(site)}, ${quote(ownerId)}, 'owner', ${quote(token.now)}, ${quote(token.now)});`,
|
|
183
194
|
insertToken(token),
|
|
195
|
+
...siteChange(site, ownerId, token.now, structure),
|
|
184
196
|
""
|
|
185
197
|
].join("\n");
|
|
186
198
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { r as CliError } from "./format-BpRYEsGB.mjs";
|
|
2
2
|
import { n as failureReason, r as stripAnsi } from "./wrangler-BLIfwbcu.mjs";
|
|
3
3
|
import { t as parseFlags } from "./args-CljWh-kZ.mjs";
|
|
4
|
-
import { f as ownerQuery, i as resolveDatabase, r as query } from "./d1-
|
|
5
|
-
import { t as publicUrlOf } from "./public-url-
|
|
6
|
-
import { i as migrate } from "./migrate-
|
|
7
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { f as ownerQuery, i as resolveDatabase, r as query } from "./d1-CK4nF7p5.mjs";
|
|
5
|
+
import { t as publicUrlOf } from "./public-url-NLPRzxCN.mjs";
|
|
6
|
+
import { i as migrate } from "./migrate-BBfF-mkE.mjs";
|
|
7
|
+
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
//#region src/commands/deploy.ts
|
|
10
10
|
const DEPLOY_USAGE = [
|
|
@@ -75,6 +75,7 @@ async function deploy(argv, io) {
|
|
|
75
75
|
io.out(`Set HOLLWARE_PUBLIC_URL to ${url} in wrangler.jsonc. Deploying again with it ...`);
|
|
76
76
|
await wranglerDeploy(io);
|
|
77
77
|
}
|
|
78
|
+
await ensureMasterSecret(io);
|
|
78
79
|
await migrate([
|
|
79
80
|
"--remote",
|
|
80
81
|
"--database",
|
|
@@ -90,12 +91,42 @@ async function deploy(argv, io) {
|
|
|
90
91
|
"Commit wrangler.jsonc: it holds the ids of what the deploy created, and the public URL.",
|
|
91
92
|
...owners.length === 0 ? [
|
|
92
93
|
"",
|
|
93
|
-
"Next: create your site and its owner in the remote database
|
|
94
|
-
"
|
|
94
|
+
"Next: create your site and its owner in the remote database. It prints a token for your",
|
|
95
|
+
"agents and a link to set up your passkey; shape the site in the admin afterwards:",
|
|
95
96
|
" hollware init --site <key> --remote"
|
|
96
97
|
] : []
|
|
97
98
|
].join("\n"));
|
|
98
99
|
return 0;
|
|
99
100
|
}
|
|
101
|
+
/** The Worker secret that seals the secrets Hollware makes for the structure (ADR-033). */
|
|
102
|
+
const MASTER_SECRET = "HOLLWARE_SECRET";
|
|
103
|
+
/**
|
|
104
|
+
* Gives the Worker its `HOLLWARE_SECRET` when it has none: 32 random bytes in hex, through a file
|
|
105
|
+
* that only the user may read, deleted at once, so the value is never printed nor kept. One the
|
|
106
|
+
* Worker has is left as it is: a new one would leave the sealed secrets unopenable.
|
|
107
|
+
*/
|
|
108
|
+
async function ensureMasterSecret(io) {
|
|
109
|
+
const listed = await io.wrangler(["secret", "list"]);
|
|
110
|
+
if (listed.code !== 0) throw new CliError(`wrangler could not list the secrets of the Worker: ${failureReason(listed)}.`);
|
|
111
|
+
if (listed.stdout.includes(`"HOLLWARE_SECRET"`)) return;
|
|
112
|
+
const value = Array.from(io.randomBytes(32), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
113
|
+
const directory = io.tempDir();
|
|
114
|
+
try {
|
|
115
|
+
const file = join(directory, "secrets.json");
|
|
116
|
+
writeFileSync(file, JSON.stringify({ [MASTER_SECRET]: value }), { mode: 384 });
|
|
117
|
+
const put = await io.wrangler([
|
|
118
|
+
"secret",
|
|
119
|
+
"bulk",
|
|
120
|
+
file
|
|
121
|
+
]);
|
|
122
|
+
if (put.code !== 0) throw new CliError(`wrangler could not set ${MASTER_SECRET}: ${failureReason(put)}.`);
|
|
123
|
+
} finally {
|
|
124
|
+
rmSync(directory, {
|
|
125
|
+
recursive: true,
|
|
126
|
+
force: true
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
io.out(`Made ${MASTER_SECRET}, which seals the secrets Hollware makes, such as those of webhooks.`);
|
|
130
|
+
}
|
|
100
131
|
//#endregion
|
|
101
132
|
export { deploy };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { r as CliError } from "./format-BpRYEsGB.mjs";
|
|
2
2
|
import { n as failureReason } from "./wrangler-BLIfwbcu.mjs";
|
|
3
3
|
import { t as parseFlags } from "./args-CljWh-kZ.mjs";
|
|
4
|
-
import { S as parseJsonc, f as ownerQuery, r as query, t as chooseTarget } from "./d1-
|
|
5
|
-
import { a as parsePendingMigrations } from "./migrate-
|
|
4
|
+
import { S as parseJsonc, f as ownerQuery, r as query, t as chooseTarget } from "./d1-CK4nF7p5.mjs";
|
|
5
|
+
import { a as parsePendingMigrations } from "./migrate-BBfF-mkE.mjs";
|
|
6
6
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
//#region src/commands/doctor.ts
|
|
@@ -73,7 +73,7 @@ async function checkDatabase(io, target) {
|
|
|
73
73
|
const pending = parsePendingMigrations(listed.stdout);
|
|
74
74
|
if (pending.length > 0) return [fail(`the ${target} database lacks ${pending.length} ${pending.length === 1 ? "migration" : "migrations"}`, `Run "hollware migrate --${target}".`)];
|
|
75
75
|
const [owners = []] = await query(io.wrangler, "DB", target, ownerQuery());
|
|
76
|
-
return [pass(`the ${target} database has every migration`), owners.length > 0 ? pass(`the ${target} database has an owner`) : fail(`the ${target} database has no owner yet`, `Run "hollware init --site <key> --${target}", with the key
|
|
76
|
+
return [pass(`the ${target} database has every migration`), owners.length > 0 ? pass(`the ${target} database has an owner`) : fail(`the ${target} database has no owner yet`, `Run "hollware init --site <key> --${target}", with the key your site is to have, such as my-site.`)];
|
|
77
77
|
}
|
|
78
78
|
async function doctor(argv, io) {
|
|
79
79
|
const { values } = parseFlags({
|
package/dist/index.mjs
CHANGED
|
@@ -105,12 +105,12 @@ async function main(argv, io) {
|
|
|
105
105
|
}
|
|
106
106
|
async function run(command, args, io) {
|
|
107
107
|
switch (command) {
|
|
108
|
-
case "init": return (await import("./init-
|
|
109
|
-
case "migrate": return (await import("./migrate-
|
|
110
|
-
case "deploy": return (await import("./deploy-
|
|
111
|
-
case "doctor": return (await import("./doctor-
|
|
112
|
-
case "token": return (await import("./token-
|
|
113
|
-
case "invite": return (await import("./invite-
|
|
108
|
+
case "init": return (await import("./init-CBZPzLBs.mjs")).init(args, io);
|
|
109
|
+
case "migrate": return (await import("./migrate-Ka2Mm06e.mjs")).migrate(args, io);
|
|
110
|
+
case "deploy": return (await import("./deploy-4tusYOMV.mjs")).deploy(args, io);
|
|
111
|
+
case "doctor": return (await import("./doctor-DiKAktVd.mjs")).doctor(args, io);
|
|
112
|
+
case "token": return (await import("./token-BND4Rsxj.mjs")).token(args, io);
|
|
113
|
+
case "invite": return (await import("./invite-Ck-aqpN8.mjs")).invite(args, io);
|
|
114
114
|
default: return (await import("./import-DG8AABZQ.mjs")).importContent(args, io);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
import { i as UsageError, r as CliError } from "./format-BpRYEsGB.mjs";
|
|
2
2
|
import { t as parseFlags } from "./args-CljWh-kZ.mjs";
|
|
3
|
-
import { _ as parseSiteKey, a as runFile, c as insertInvitation, f as ownerQuery, h as parseOwnerName, i as resolveDatabase, n as describeDatabase, o as anotherSiteScript, r as query, s as initScript, t as chooseTarget } from "./d1-
|
|
4
|
-
import { n as setupLink, t as publicUrlOf } from "./public-url-
|
|
3
|
+
import { _ as parseSiteKey, a as runFile, c as insertInvitation, f as ownerQuery, h as parseOwnerName, i as resolveDatabase, n as describeDatabase, o as anotherSiteScript, r as query, s as initScript, t as chooseTarget } from "./d1-CK4nF7p5.mjs";
|
|
4
|
+
import { n as setupLink, t as publicUrlOf } from "./public-url-NLPRzxCN.mjs";
|
|
5
5
|
import { i as newPersonalToken, n as newId, r as newInvitationSecret, t as inDays } from "./entropy-5uxirwFD.mjs";
|
|
6
|
-
import { rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { existsSync, rmSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
|
-
import { PERSONAL_TOKEN_TTL_DAYS, SCOPES, hashPersonalToken } from "@hollware/core";
|
|
8
|
+
import { PERSONAL_TOKEN_TTL_DAYS, PRESETS, SCOPES, hashPersonalToken, sitePreset } from "@hollware/core";
|
|
9
9
|
//#region src/commands/init.ts
|
|
10
10
|
const INIT_USAGE = [
|
|
11
|
-
"Usage: hollware init --site <key> [--
|
|
11
|
+
"Usage: hollware init --site <key> [--preset <blog | docs | empty>] [--locales <list>]",
|
|
12
|
+
" [--local | --remote] [--owner-name <name>] [--database <name>] [--url <url>]",
|
|
12
13
|
"",
|
|
13
14
|
"Creates a site, its owner and the owner's first personal token in the project's D1 database,",
|
|
14
15
|
"in one step, and prints the token once, with a link to set up the passkey of the admin (valid",
|
|
15
16
|
"for 7 days). On a database that has an owner already, it adds the site to that owner, who signs",
|
|
16
17
|
"in to the admin as before, and prints a token of the new site. It does nothing when the site",
|
|
17
18
|
"has an owner.",
|
|
19
|
+
"The site starts from a preset, which its owner then shapes in the admin, or an agent over MCP",
|
|
20
|
+
"(ADR-033). A project with a hollware.config.ts keeps it instead: the Worker moves it into the",
|
|
21
|
+
"database on its first request.",
|
|
18
22
|
"Run \"hollware migrate\" first: the database needs its tables.",
|
|
19
23
|
"",
|
|
20
24
|
"Options:",
|
|
21
|
-
" --site <key> The site,
|
|
25
|
+
" --site <key> The key of the site, such as my-site; it never changes (required)",
|
|
26
|
+
" --preset <preset> blog, docs or empty: a title alone (default: blog)",
|
|
27
|
+
" --locales <list> The locales, comma-separated, the first the default (default: en)",
|
|
22
28
|
" --local Use the local database (the default)",
|
|
23
29
|
" --remote Use the database in Cloudflare",
|
|
24
30
|
" --owner-name <name> The name of the owner",
|
|
@@ -37,6 +43,8 @@ async function init(argv, io) {
|
|
|
37
43
|
local: { type: "boolean" },
|
|
38
44
|
remote: { type: "boolean" },
|
|
39
45
|
"owner-name": { type: "string" },
|
|
46
|
+
preset: { type: "string" },
|
|
47
|
+
locales: { type: "string" },
|
|
40
48
|
database: { type: "string" },
|
|
41
49
|
url: { type: "string" },
|
|
42
50
|
help: { type: "boolean" }
|
|
@@ -46,8 +54,9 @@ async function init(argv, io) {
|
|
|
46
54
|
io.out(INIT_USAGE);
|
|
47
55
|
return 0;
|
|
48
56
|
}
|
|
49
|
-
if (values.site === void 0) throw new UsageError("--site is required: name the site, as
|
|
57
|
+
if (values.site === void 0) throw new UsageError("--site is required: name the site, such as --site my-site.");
|
|
50
58
|
const site = parseSiteKey(values.site);
|
|
59
|
+
const definition = initialDefinition(io.cwd, values.preset, values.locales);
|
|
51
60
|
const ownerName = values["owner-name"] === void 0 ? void 0 : parseOwnerName(values["owner-name"]);
|
|
52
61
|
const target = chooseTarget(values);
|
|
53
62
|
const database = resolveDatabase(io.cwd, values.database);
|
|
@@ -63,7 +72,8 @@ async function init(argv, io) {
|
|
|
63
72
|
site,
|
|
64
73
|
ownerId: first["id"],
|
|
65
74
|
database,
|
|
66
|
-
target
|
|
75
|
+
target,
|
|
76
|
+
definition
|
|
67
77
|
});
|
|
68
78
|
const now = io.now();
|
|
69
79
|
const timestamp = now.toISOString();
|
|
@@ -83,6 +93,10 @@ async function init(argv, io) {
|
|
|
83
93
|
now: timestamp
|
|
84
94
|
});
|
|
85
95
|
const memberId = newId(io, "member");
|
|
96
|
+
const structure = definition === null ? void 0 : {
|
|
97
|
+
definition,
|
|
98
|
+
changeId: newId(io, "siteChange")
|
|
99
|
+
};
|
|
86
100
|
const script = initScript({
|
|
87
101
|
site,
|
|
88
102
|
ownerId,
|
|
@@ -97,7 +111,8 @@ async function init(argv, io) {
|
|
|
97
111
|
scopes: SCOPES,
|
|
98
112
|
expiresAt,
|
|
99
113
|
now: timestamp
|
|
100
|
-
}
|
|
114
|
+
},
|
|
115
|
+
structure
|
|
101
116
|
}).concat(`${setup}\n`);
|
|
102
117
|
const directory = io.tempDir();
|
|
103
118
|
try {
|
|
@@ -136,16 +151,22 @@ async function init(argv, io) {
|
|
|
136
151
|
}
|
|
137
152
|
/** `init` for one more site of a deployment whose owner exists: that owner owns the new site too. */
|
|
138
153
|
async function addSite(io, where) {
|
|
139
|
-
const { site, ownerId, database, target } = where;
|
|
154
|
+
const { site, ownerId, database, target, definition } = where;
|
|
140
155
|
const now = io.now();
|
|
141
156
|
const timestamp = now.toISOString();
|
|
142
157
|
const tokenId = newId(io, "token");
|
|
143
158
|
const secret = newPersonalToken(io);
|
|
144
159
|
const expiresAt = inDays(now, PERSONAL_TOKEN_TTL_DAYS);
|
|
160
|
+
const memberId = newId(io, "member");
|
|
161
|
+
const structure = definition === null ? void 0 : {
|
|
162
|
+
definition,
|
|
163
|
+
changeId: newId(io, "siteChange")
|
|
164
|
+
};
|
|
145
165
|
const script = anotherSiteScript({
|
|
146
166
|
site,
|
|
147
167
|
ownerId,
|
|
148
|
-
memberId
|
|
168
|
+
memberId,
|
|
169
|
+
structure,
|
|
149
170
|
token: {
|
|
150
171
|
id: tokenId,
|
|
151
172
|
site,
|
|
@@ -185,5 +206,23 @@ async function addSite(io, where) {
|
|
|
185
206
|
].join("\n"));
|
|
186
207
|
return 0;
|
|
187
208
|
}
|
|
209
|
+
/** A locale tag, as a site's definition takes it: `en`, `es-CL`. */
|
|
210
|
+
const LOCALE = /^[a-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/;
|
|
211
|
+
/**
|
|
212
|
+
* The first definition of the site (ADR-033), from `--preset` and `--locales`; `null` for a
|
|
213
|
+
* project with a hollware.config.ts, whose Worker moves it into the database on its first request.
|
|
214
|
+
*/
|
|
215
|
+
function initialDefinition(cwd, preset, locales) {
|
|
216
|
+
if (existsSync(join(cwd, "hollware.config.ts"))) {
|
|
217
|
+
if (preset !== void 0 || locales !== void 0) throw new UsageError("this project has a hollware.config.ts, which the Worker moves into the database: leave out --preset and --locales, or delete the config first.");
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
const chosen = preset ?? "blog";
|
|
221
|
+
if (!PRESETS.some((name) => name === chosen)) throw new UsageError(`--preset is one of ${PRESETS.join(", ")}, not "${chosen}".`);
|
|
222
|
+
const list = (locales ?? "en").split(",").map((locale) => locale.trim()).filter((locale) => locale !== "");
|
|
223
|
+
const bad = list.find((locale) => !LOCALE.test(locale));
|
|
224
|
+
if (list.length === 0 || bad !== void 0) throw new UsageError(`--locales is a comma-separated list of locales, such as en,es; "${bad ?? ""}" is not one.`);
|
|
225
|
+
return sitePreset(chosen, list, list[0] ?? "en");
|
|
226
|
+
}
|
|
188
227
|
//#endregion
|
|
189
228
|
export { init };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { i as UsageError, r as CliError } from "./format-BpRYEsGB.mjs";
|
|
2
2
|
import { t as parseFlags } from "./args-CljWh-kZ.mjs";
|
|
3
|
-
import { _ as parseSiteKey, c as insertInvitation, d as ownerOfSiteQuery, i as resolveDatabase, p as parseExpiresInDays, r as query, t as chooseTarget, x as sitesQuery } from "./d1-
|
|
4
|
-
import { n as setupLink, t as publicUrlOf } from "./public-url-
|
|
3
|
+
import { _ as parseSiteKey, c as insertInvitation, d as ownerOfSiteQuery, i as resolveDatabase, p as parseExpiresInDays, r as query, t as chooseTarget, x as sitesQuery } from "./d1-CK4nF7p5.mjs";
|
|
4
|
+
import { n as setupLink, t as publicUrlOf } from "./public-url-NLPRzxCN.mjs";
|
|
5
5
|
import { n as newId, r as newInvitationSecret, t as inDays } from "./entropy-5uxirwFD.mjs";
|
|
6
|
-
import { o as WHERE_OPTIONS, s as chooseSite } from "./token-
|
|
6
|
+
import { o as WHERE_OPTIONS, s as chooseSite } from "./token-CLQpD4ed.mjs";
|
|
7
7
|
import { hashPersonalToken } from "@hollware/core";
|
|
8
8
|
//#region src/commands/invite.ts
|
|
9
9
|
const INVITE_USAGE = [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { r as CliError } from "./format-BpRYEsGB.mjs";
|
|
2
2
|
import { n as failureReason, r as stripAnsi } from "./wrangler-BLIfwbcu.mjs";
|
|
3
3
|
import { t as parseFlags } from "./args-CljWh-kZ.mjs";
|
|
4
|
-
import { i as resolveDatabase, n as describeDatabase, t as chooseTarget } from "./d1-
|
|
4
|
+
import { i as resolveDatabase, n as describeDatabase, t as chooseTarget } from "./d1-CK4nF7p5.mjs";
|
|
5
5
|
import { existsSync, mkdirSync, statSync } from "node:fs";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
//#region src/commands/migrate.ts
|
|
@@ -9,9 +9,11 @@ const MIGRATE_USAGE = [
|
|
|
9
9
|
"Usage: hollware migrate [--local | --remote] [--check] [--database <name>]",
|
|
10
10
|
"",
|
|
11
11
|
"Applies the D1 migrations the project has not applied yet, with wrangler. Before it applies",
|
|
12
|
-
"anything to the remote database it exports a backup, and it stops if the backup fails.
|
|
13
|
-
"export
|
|
14
|
-
"
|
|
12
|
+
"anything to the remote database it exports a backup, and it stops if the backup fails. D1",
|
|
13
|
+
"cannot export a database with a full-text index (0.5 and later): it takes a Time Travel",
|
|
14
|
+
"bookmark instead, which restores the database as it was. The export can make the database",
|
|
15
|
+
"unavailable for a moment; with nothing pending, or on a new database that has none of the",
|
|
16
|
+
"migrations yet, there is no backup.",
|
|
15
17
|
"",
|
|
16
18
|
"Options:",
|
|
17
19
|
" --local Use the local database (the default)",
|
|
@@ -110,10 +112,38 @@ async function takeBackup(io, database) {
|
|
|
110
112
|
file,
|
|
111
113
|
"--skip-confirmation"
|
|
112
114
|
]);
|
|
113
|
-
if (exported.code !== 0)
|
|
115
|
+
if (exported.code !== 0) {
|
|
116
|
+
const reason = failureReason(exported);
|
|
117
|
+
if (/virtual tables?/i.test(reason)) return takeBookmark(io, database);
|
|
118
|
+
throw new CliError(`the backup of ${where} failed, so no migration was applied: ${reason}`);
|
|
119
|
+
}
|
|
114
120
|
if (!existsSync(file) || statSync(file).size === 0) throw new CliError(`wrangler reported success but wrote no backup to ${file}, so no migration was applied. Check "wrangler d1 export ${database} --remote --output <file>" by hand.`);
|
|
115
121
|
return file;
|
|
116
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* The backup of a database D1 cannot export: a Time Travel bookmark of now, and the command that
|
|
125
|
+
* restores the database to it.
|
|
126
|
+
*/
|
|
127
|
+
async function takeBookmark(io, database) {
|
|
128
|
+
const where = describeDatabase("remote", database);
|
|
129
|
+
io.out(`D1 cannot export ${where}, which has a full-text index; taking a Time Travel bookmark instead ...`);
|
|
130
|
+
const info = await io.wrangler([
|
|
131
|
+
"d1",
|
|
132
|
+
"time-travel",
|
|
133
|
+
"info",
|
|
134
|
+
database,
|
|
135
|
+
"--json"
|
|
136
|
+
]);
|
|
137
|
+
let bookmark = null;
|
|
138
|
+
try {
|
|
139
|
+
const parsed = JSON.parse(stripAnsi(info.stdout));
|
|
140
|
+
bookmark = typeof parsed === "object" && parsed !== null && "bookmark" in parsed ? parsed.bookmark : null;
|
|
141
|
+
} catch {
|
|
142
|
+
bookmark = null;
|
|
143
|
+
}
|
|
144
|
+
if (info.code !== 0 || typeof bookmark !== "string" || bookmark === "") throw new CliError(`no Time Travel bookmark of ${where} could be taken, so no migration was applied: ${info.code === 0 ? "wrangler answered no bookmark" : failureReason(info)}`);
|
|
145
|
+
return `the Time Travel bookmark ${bookmark}; restore it with "npx wrangler d1 time-travel restore ${database} --bookmark=${bookmark}"`;
|
|
146
|
+
}
|
|
117
147
|
/** `20261012T120000Z`: a moment, in UTC, that is safe in a file name. */
|
|
118
148
|
function timestamp(date) {
|
|
119
149
|
return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as UsageError } from "./format-BpRYEsGB.mjs";
|
|
2
|
-
import { S as parseJsonc } from "./d1-
|
|
2
|
+
import { S as parseJsonc } from "./d1-CK4nF7p5.mjs";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
//#region src/public-url.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as UsageError, n as table, r as CliError, t as printable } from "./format-BpRYEsGB.mjs";
|
|
2
2
|
import { t as parseFlags } from "./args-CljWh-kZ.mjs";
|
|
3
|
-
import { _ as parseSiteKey, b as sitesAndOwnerQuery, g as parseScopes, i as resolveDatabase, l as insertToken, m as parseLabel, p as parseExpiresInDays, r as query, t as chooseTarget, u as listTokens, v as parseTokenId, x as sitesQuery, y as revokeToken } from "./d1-
|
|
3
|
+
import { _ as parseSiteKey, b as sitesAndOwnerQuery, g as parseScopes, i as resolveDatabase, l as insertToken, m as parseLabel, p as parseExpiresInDays, r as query, t as chooseTarget, u as listTokens, v as parseTokenId, x as sitesQuery, y as revokeToken } from "./d1-CK4nF7p5.mjs";
|
|
4
4
|
import { a as newReadToken, i as newPersonalToken, n as newId, t as inDays } from "./entropy-5uxirwFD.mjs";
|
|
5
5
|
import { PERSONAL_TOKEN_TTL_DAYS, hashPersonalToken } from "@hollware/core";
|
|
6
6
|
//#region src/commands/token.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hollware",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "The Hollware CLI: init, migrate, token, import and export.",
|
|
5
5
|
"homepage": "https://hollware.com",
|
|
6
6
|
"bugs": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"provenance": true
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@hollware/core": "0.
|
|
34
|
+
"@hollware/core": "0.8.0",
|
|
35
35
|
"@modelcontextprotocol/client": "2.3.1",
|
|
36
36
|
"zod": "4.6.5"
|
|
37
37
|
},
|
package/dist/token-DeVLRP0z.mjs
DELETED