@prismshadow/penguin-server 0.2.10 → 0.2.13
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 +1 -1
- package/dist/api/types.d.ts +1014 -12
- package/dist/api/types.js +7 -0
- package/dist/auth-token.js +6 -4
- package/dist/auth-token.js.map +1 -1
- package/dist/{chunk-Q65VPAQS.js → chunk-3I2XOPJB.js} +102 -55
- package/dist/chunk-3I2XOPJB.js.map +1 -0
- package/dist/{chunk-ADZJYDNC.js → chunk-A3QANMU5.js} +293 -3
- package/dist/chunk-A3QANMU5.js.map +1 -0
- package/dist/chunk-B5IP37ZQ.js +56 -0
- package/dist/chunk-L2H3K4DT.js +7 -0
- package/dist/chunk-L2H3K4DT.js.map +1 -0
- package/dist/chunk-LPPVGHYD.js +83 -0
- package/dist/chunk-LPPVGHYD.js.map +1 -0
- package/dist/{chunk-TTGGJDNA.js → chunk-S3BI6R7I.js} +2 -2
- package/dist/{chunk-HOS3W3UP.js → chunk-TOOTEEM4.js} +50 -8
- package/dist/chunk-TOOTEEM4.js.map +1 -0
- package/dist/hmr/manifest.d.ts +1 -77
- package/dist/hmr/manifest.js +6 -1
- package/dist/index.js +50757 -20174
- package/dist/index.js.map +1 -1
- package/dist/initial-password.js +1 -0
- package/dist/lock.js +1 -0
- package/dist/machine-status.js +2 -1
- package/dist/machine-status.js.map +1 -1
- package/dist/plugin/index.d.ts +2595 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/reset-admin-password.js +7 -5
- package/dist/reset-admin-password.js.map +1 -1
- package/dist/secret-file.js +2 -0
- package/dist/secret-file.js.map +1 -1
- package/dist/version-report.js +3 -2
- package/package.json +11 -9
- package/web-dist/assets/{addon-clipboard-1cARCaK9.js → addon-clipboard-DrZY90Ks.js} +1 -1
- package/web-dist/assets/{addon-fit-BWdzl8Er.js → addon-fit-CtEAdAax.js} +1 -1
- package/web-dist/assets/{addon-web-links-DjXuvn4Z.js → addon-web-links-DJL13cYY.js} +1 -1
- package/web-dist/assets/c-BG-tZi7o.js +1 -0
- package/web-dist/assets/cpp-CaeG3uDQ.js +1 -0
- package/web-dist/assets/css-Br67jR8j.js +1 -0
- package/web-dist/assets/diff-jW5kmiru.js +1 -0
- package/web-dist/assets/github-dark-BQV5ugrc.js +1 -0
- package/web-dist/assets/github-light-Rcb2q3L5.js +1 -0
- package/web-dist/assets/go-B7VTfpnm.js +1 -0
- package/web-dist/assets/highlighter-CUb4XKtQ.js +2 -0
- package/web-dist/assets/{highlighter-Cu43iYr8.js → highlighter-core-CrBdruEx.js} +2 -2
- package/web-dist/assets/highlighter.worker-CL6f9Fzw.js +152 -0
- package/web-dist/assets/html-BW_6VhB7.js +1 -0
- package/web-dist/assets/index-CmfumtXy.js +570 -0
- package/web-dist/assets/index-Ywc2uM5u.css +1 -0
- package/web-dist/assets/ini-Dix3pTmg.js +1 -0
- package/web-dist/assets/java-BTDmaRNO.js +1 -0
- package/web-dist/assets/javascript-WyGyM1wV.js +1 -0
- package/web-dist/assets/json-B8JryHcM.js +1 -0
- package/web-dist/assets/jsx-8cLv2Six.js +1 -0
- package/web-dist/assets/log-CcaF_xNv.js +1 -0
- package/web-dist/assets/markdown-DX-TNHnc.js +1 -0
- package/web-dist/assets/php-CNeTOyEV.js +1 -0
- package/web-dist/assets/python-B6Ihcp-E.js +1 -0
- package/web-dist/assets/ruby-CYiNE6ps.js +1 -0
- package/web-dist/assets/rust-CxuBE0J8.js +1 -0
- package/web-dist/assets/shellscript-DgJyDXW6.js +1 -0
- package/web-dist/assets/sql-Dq2MiB0T.js +1 -0
- package/web-dist/assets/toml-Ms459DBw.js +1 -0
- package/web-dist/assets/tsx-D6116Ycy.js +1 -0
- package/web-dist/assets/typescript-BqvgUolh.js +1 -0
- package/web-dist/assets/xml-BkAJuBCc.js +1 -0
- package/web-dist/assets/{xterm-DKcY6q-I.js → xterm-B2oS8Mg8.js} +1 -1
- package/web-dist/assets/yaml-Bopit2L3.js +1 -0
- package/web-dist/index.html +2 -2
- package/dist/chunk-4SXGU7RO.js +0 -50
- package/dist/chunk-4SXGU7RO.js.map +0 -1
- package/dist/chunk-ADZJYDNC.js.map +0 -1
- package/dist/chunk-HOS3W3UP.js.map +0 -1
- package/dist/chunk-Q65VPAQS.js.map +0 -1
- package/dist/extension/index.d.ts +0 -212
- package/web-dist/assets/index-Bm9n7ZV-.js +0 -457
- package/web-dist/assets/index-DR4PrRzS.css +0 -1
- /package/dist/{extension/index.js.map → chunk-B5IP37ZQ.js.map} +0 -0
- /package/dist/{chunk-TTGGJDNA.js.map → chunk-S3BI6R7I.js.map} +0 -0
- /package/dist/{extension → plugin}/index.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../node_modules/.pnpm/@prismshadow+penguin-hmr@file+packages+hmr_supports-color@10.2.2_ws@8.21.0/node_modules/@prismshadow/penguin-hmr/src/manifest.ts"],"sourcesContent":["/**\n * The harness.json shape (`<root>/hmr/harness.json`) and pure readers over it.\n *\n * Split out of host.ts so a reader never needs a running HmrHost: `penguin` is a\n * separate, short-lived process per invocation (see packages/cli/src/index.ts) — it\n * has no host instance to ask, only the data root's own harness.json on disk.\n * `resolveCliBundlePath` is that reader: a plain disk read, no HTTP, no boot, no\n * in-memory state.\n *\n * ONE atomic version, THREE independent artifacts: `platform`, `cli`, and `web` are\n * always written together by HmrHost's single merged upgrade path (see host.ts's\n * persistVersion), but each is content-addressed and stored on its own — a push to\n * POST /api/hmr/upgrade carries `platform` and `cli` as two separate single-file ESM\n * sources, so `cli.bundle` points at its OWN file, a different sha from\n * `platform.bundle`. They are read back together too (see host.ts's restore): a\n * partial record (e.g. `platform` present but `web` or `cli` missing) is never\n * trusted — the whole version is treated as absent instead of partially applied.\n */\nimport fs from \"node:fs\";\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { HarnessInfo } from \"@prismshadow/penguin-core\";\n\n/**\n * The committed on-disk record: a runtime restart resumes exactly this CODE (platform,\n * cli, web). It carries no STATE — a restart always boots the resumed platform bundle\n * against its own fresh initial context (bundle.context, see host.ts's PlatformBundle),\n * never a document parked from a previous run (see host.ts's module doc for why: a\n * parked doc's live-resource handles die with the process anyway, so resuming one could\n * only ever produce handles that fail to reclaim). Paths are relative to hmrDir\n * (`<root>/hmr`).\n */\n/**\n * Written into an assets directory once every file in it is on disk. Its absence marks a\n * directory whose materialization was interrupted; no asset path can collide with it\n * (assets arrive as `node_modules/...` paths).\n */\nexport const MATERIALIZED = \".materialized\";\n\nexport interface Manifest {\n platform?: { bundle: string };\n /**\n * The CLI's own bundle pointer — its own independent file (a different sha from\n * `platform.bundle`; the two are separately compiled artifacts), kept as its own\n * field so a reader that only wants \"which bundle does the CLI load right now\"\n * never has to reach into `platform` at all.\n */\n cli?: { bundle: string };\n /** One gzip(JSON.stringify({ files })) artifact, restored straight into memory. */\n web?: { manifest: string };\n /**\n * Files the pushed platform needs as REAL files on disk, unpacked under this directory\n * (relative to hmrDir). The web artifact stays in memory because it is only ever served\n * as bytes; an asset is something the platform hands to the OS — a native `.node` the\n * loader must resolve by path, a helper binary it execs — so it has to exist on the\n * filesystem, with its exec bit intact.\n */\n assets?: { dir: string };\n /**\n * Provenance of the push that produced this version, exactly as the pushing client sent\n * it (scripts/deploy.mjs fills it from its own checkout). Recorded, never executed or\n * resolved. Absent for a version pushed without it, and for every version committed\n * before the field existed — which is why every reader treats it as optional.\n */\n source?: { repo: string; revision: string };\n /** When this version was committed to the store (ISO 8601). Absent on older records. */\n pushedAt?: string;\n /**\n * The sha256 of each part this version was pushed as (platform, cli, every web file):\n * the blobs the store keeps alive so the next push need not carry an unchanged one.\n * Absent on older records.\n */\n blobs?: string[];\n}\n\n/** A string field of an untrusted manifest, or null unless it is a non-empty string. */\nfunction str(value: unknown): string | null {\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\n/**\n * What the store has committed, for version reporting: provenance, commit time, and the\n * three artifact pointers. Null when nothing has ever been pushed to this root.\n *\n * Read defensively rather than cast: harness.json is written only by persistVersion, but a\n * truncated or hand-edited file must degrade a `penguin version` to missing fields, never\n * crash it. A record missing every artifact pointer counts as nothing pushed, and so does an\n * unreadable one — the only reader here that deliberately collapses the two, because its\n * caller is a report with nowhere to put a fault. The host says so at boot instead, and\n * `penguin-hmr` says so through readPushedCli.\n */\nexport async function readHarnessInfo(root: string): Promise<HarnessInfo | null> {\n const manifest = await readManifest(root);\n if (manifest === null) return null;\n\n const bundles = {\n platform: str(manifest.platform?.bundle),\n cli: str(manifest.cli?.bundle),\n web: str(manifest.web?.manifest),\n };\n if (bundles.platform === null && bundles.cli === null && bundles.web === null) return null;\n\n const repo = str(manifest.source?.repo);\n const revision = str(manifest.source?.revision);\n return {\n // Both halves or neither: half a provenance names nothing.\n source: repo !== null && revision !== null ? { repo, revision } : null,\n pushedAt: str(manifest.pushedAt),\n bundles,\n };\n}\n\n/**\n * `<root>/hmr/harness.json`, as one of three answers. No file means nothing was ever pushed\n * here; a file that cannot be read or parsed is a fault on a root that DID commit a version,\n * and the two must not arrive as the same value — a reader that acts on \"nothing pushed\"\n * would quietly ignore a version this root still holds.\n */\ntype ManifestRead =\n { kind: \"none\" } | { kind: \"manifest\"; manifest: Manifest } | { kind: \"broken\"; reason: string };\n\nasync function readManifestFile(root: string): Promise<ManifestRead> {\n const file = path.join(root, \"hmr\", \"harness.json\");\n let raw: string;\n try {\n raw = await fsp.readFile(file, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return { kind: \"none\" };\n return { kind: \"broken\", reason: `${file} could not be read: ${msg(err)}` };\n }\n try {\n return { kind: \"manifest\", manifest: JSON.parse(raw) as Manifest };\n } catch (err) {\n return { kind: \"broken\", reason: `${file} is not readable JSON: ${msg(err)}` };\n }\n}\n\nfunction msg(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Reads and parses `<root>/hmr/harness.json`; null when missing or corrupt. The published\n * `./hmr/manifest` subpath is a contract with installed CLIs, so this keeps its shape;\n * a caller that has to tell the two apart reads it through readPushedCli.\n */\nexport async function readManifest(root: string): Promise<Manifest | null> {\n const read = await readManifestFile(root);\n return read.kind === \"manifest\" ? read.manifest : null;\n}\n\n/**\n * What this root has for the `penguin-hmr` loader. Three answers, never two: a root that\n * pushed no CLI and a root whose record names one it cannot produce are different\n * situations, and collapsing them tells the caller \"nothing pushed\" when the truth is\n * \"the store lost it\".\n */\nexport type PushedCli =\n /** Nothing has pushed a CLI here: a fresh root, or a record with no `cli` entry. */\n | { kind: \"none\" }\n /** The committed CLI bundle, absolute. */\n | { kind: \"bundle\"; file: string }\n /** The record names a CLI this root cannot produce. Says which, so it can be repaired. */\n | { kind: \"broken\"; reason: string };\n\n/**\n * Reads the committed CLI bundle. Never throws: a malformed record is an answer\n * (`broken`), not an exception, because the caller is a CLI entry point that has to print\n * something useful either way.\n *\n * `cli.bundle` is trusted content, written only by persistVersion, but a reader must never\n * let a malformed or malicious value walk it out of the store via `..` segments — an escape\n * is `broken`, like a file the store no longer holds (pruned, see host.ts's pruneStore).\n */\nexport async function readPushedCli(root: string): Promise<PushedCli> {\n const read = await readManifestFile(root);\n if (read.kind === \"none\") return { kind: \"none\" };\n if (read.kind === \"broken\") return { kind: \"broken\", reason: read.reason };\n // No `cli` entry at all is a version that pushed no CLI; an entry that names nothing\n // usable is a record this root cannot act on.\n if (read.manifest.cli === undefined) return { kind: \"none\" };\n const bundle: unknown = read.manifest.cli.bundle;\n if (typeof bundle !== \"string\" || bundle.length === 0) {\n return { kind: \"broken\", reason: \"harness.json has a `cli` entry with no bundle path\" };\n }\n const hmrDir = path.join(root, \"hmr\");\n const abs = path.resolve(hmrDir, bundle);\n if (abs !== hmrDir && !abs.startsWith(hmrDir + path.sep)) {\n return {\n kind: \"broken\",\n reason: `harness.json points \\`cli.bundle\\` outside ${hmrDir}: ${bundle}`,\n };\n }\n if (!fs.existsSync(abs)) {\n return { kind: \"broken\", reason: `the committed CLI bundle is missing from the store: ${abs}` };\n }\n return { kind: \"bundle\", file: abs };\n}\n\n/**\n * The committed CLI bundle's path, or null for anything else. The published `./hmr/manifest`\n * subpath is a contract with installed CLIs, so this keeps its shape; readPushedCli is what\n * says WHY there is no path.\n */\nexport async function resolveCliBundlePath(root: string): Promise<string | null> {\n const cli = await readPushedCli(root);\n return cli.kind === \"bundle\" ? cli.file : null;\n}\n"],"mappings":";AAkBA,OAAO,QAAQ;AACf,OAAO,SAAS;AAChB,OAAO,UAAU;AAiBV,IAAM,eAAe;AAuC5B,SAAS,IAAI,OAA+B;AAC1C,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;AAaA,eAAsB,gBAAgB,MAA2C;AAC/E,QAAM,WAAW,MAAM,aAAa,IAAI;AACxC,MAAI,aAAa,KAAM,QAAO;AAE9B,QAAM,UAAU;IACd,UAAU,IAAI,SAAS,UAAU,MAAM;IACvC,KAAK,IAAI,SAAS,KAAK,MAAM;IAC7B,KAAK,IAAI,SAAS,KAAK,QAAQ;EACjC;AACA,MAAI,QAAQ,aAAa,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,KAAM,QAAO;AAEtF,QAAM,OAAO,IAAI,SAAS,QAAQ,IAAI;AACtC,QAAM,WAAW,IAAI,SAAS,QAAQ,QAAQ;AAC9C,SAAO;;IAEL,QAAQ,SAAS,QAAQ,aAAa,OAAO,EAAE,MAAM,SAAS,IAAI;IAClE,UAAU,IAAI,SAAS,QAAQ;IAC/B;EACF;AACF;AAWA,eAAe,iBAAiB,MAAqC;AACnE,QAAM,OAAO,KAAK,KAAK,MAAM,OAAO,cAAc;AAClD,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,IAAI,SAAS,MAAM,MAAM;EACvC,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO,EAAE,MAAM,OAAO;AAC5E,WAAO,EAAE,MAAM,UAAU,QAAQ,GAAG,IAAI,uBAAuB,IAAI,GAAG,CAAC,GAAG;EAC5E;AACA,MAAI;AACF,WAAO,EAAE,MAAM,YAAY,UAAU,KAAK,MAAM,GAAG,EAAc;EACnE,SAAS,KAAK;AACZ,WAAO,EAAE,MAAM,UAAU,QAAQ,GAAG,IAAI,0BAA0B,IAAI,GAAG,CAAC,GAAG;EAC/E;AACF;AAEA,SAAS,IAAI,KAAsB;AACjC,SAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACxD;AAOA,eAAsB,aAAa,MAAwC;AACzE,QAAM,OAAO,MAAM,iBAAiB,IAAI;AACxC,SAAO,KAAK,SAAS,aAAa,KAAK,WAAW;AACpD;AAyBA,eAAsB,cAAc,MAAkC;AACpE,QAAM,OAAO,MAAM,iBAAiB,IAAI;AACxC,MAAI,KAAK,SAAS,OAAQ,QAAO,EAAE,MAAM,OAAO;AAChD,MAAI,KAAK,SAAS,SAAU,QAAO,EAAE,MAAM,UAAU,QAAQ,KAAK,OAAO;AAGzE,MAAI,KAAK,SAAS,QAAQ,OAAW,QAAO,EAAE,MAAM,OAAO;AAC3D,QAAM,SAAkB,KAAK,SAAS,IAAI;AAC1C,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAAG;AACrD,WAAO,EAAE,MAAM,UAAU,QAAQ,qDAAqD;EACxF;AACA,QAAM,SAAS,KAAK,KAAK,MAAM,KAAK;AACpC,QAAM,MAAM,KAAK,QAAQ,QAAQ,MAAM;AACvC,MAAI,QAAQ,UAAU,CAAC,IAAI,WAAW,SAAS,KAAK,GAAG,GAAG;AACxD,WAAO;MACL,MAAM;MACN,QAAQ,8CAA8C,MAAM,KAAK,MAAM;IACzE;EACF;AACA,MAAI,CAAC,GAAG,WAAW,GAAG,GAAG;AACvB,WAAO,EAAE,MAAM,UAAU,QAAQ,uDAAuD,GAAG,GAAG;EAChG;AACA,SAAO,EAAE,MAAM,UAAU,MAAM,IAAI;AACrC;AAOA,eAAsB,qBAAqB,MAAsC;AAC/E,QAAM,MAAM,MAAM,cAAc,IAAI;AACpC,SAAO,IAAI,SAAS,WAAW,IAAI,OAAO;AAC5C;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readHarnessInfo
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LPPVGHYD.js";
|
|
4
4
|
|
|
5
5
|
// src/version-report.ts
|
|
6
6
|
import { buildInfo } from "@prismshadow/penguin-core";
|
|
@@ -11,4 +11,4 @@ async function versionReport(root) {
|
|
|
11
11
|
export {
|
|
12
12
|
versionReport
|
|
13
13
|
};
|
|
14
|
-
//# sourceMappingURL=chunk-
|
|
14
|
+
//# sourceMappingURL=chunk-S3BI6R7I.js.map
|
|
@@ -1,26 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__decorateElement,
|
|
3
|
+
__decoratorStart,
|
|
4
|
+
__publicField,
|
|
5
|
+
__runInitializers
|
|
6
|
+
} from "./chunk-B5IP37ZQ.js";
|
|
7
|
+
|
|
1
8
|
// src/db/repos/users.ts
|
|
9
|
+
import { Component, Use } from "@prismshadow/penguin-core/kernel";
|
|
2
10
|
function mapRow(r) {
|
|
3
11
|
return {
|
|
4
12
|
userId: r.user_id,
|
|
5
13
|
passwordHash: r.password_hash,
|
|
6
14
|
isAdmin: r.is_admin === 1,
|
|
7
15
|
passwordIsInitial: r.password_is_initial === 1,
|
|
16
|
+
displayName: r.display_name ?? null,
|
|
17
|
+
avatar: r.avatar ?? null,
|
|
8
18
|
createdAt: r.created_at
|
|
9
19
|
};
|
|
10
20
|
}
|
|
21
|
+
var _db_dec, _UsersRepo_decorators, _init;
|
|
22
|
+
_UsersRepo_decorators = [Component()], _db_dec = [Use()];
|
|
11
23
|
var UsersRepo = class {
|
|
12
|
-
constructor(
|
|
13
|
-
this
|
|
24
|
+
constructor() {
|
|
25
|
+
__publicField(this, "db", __runInitializers(_init, 8, this)), __runInitializers(_init, 11, this);
|
|
14
26
|
}
|
|
15
|
-
db;
|
|
16
27
|
insert(row) {
|
|
17
28
|
this.db.prepare(
|
|
18
|
-
"INSERT INTO users (user_id, password_hash, is_admin, password_is_initial, created_at) VALUES (?, ?, ?, ?, ?)"
|
|
29
|
+
"INSERT INTO users (user_id, password_hash, is_admin, password_is_initial, display_name, avatar, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)"
|
|
19
30
|
).run(
|
|
20
31
|
row.userId,
|
|
21
32
|
row.passwordHash,
|
|
22
33
|
row.isAdmin ? 1 : 0,
|
|
23
34
|
row.passwordIsInitial ? 1 : 0,
|
|
35
|
+
row.displayName,
|
|
36
|
+
row.avatar,
|
|
24
37
|
row.createdAt
|
|
25
38
|
);
|
|
26
39
|
}
|
|
@@ -41,22 +54,47 @@ var UsersRepo = class {
|
|
|
41
54
|
updatePassword(userId, passwordHash, isInitial) {
|
|
42
55
|
this.db.prepare("UPDATE users SET password_hash = ?, password_is_initial = ? WHERE user_id = ?").run(passwordHash, isInitial ? 1 : 0, userId);
|
|
43
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Writes the profile fields the patch names, leaving the rest as they are: an absent key
|
|
59
|
+
* keeps the stored value, `null` clears it, a string replaces it. A patch naming neither
|
|
60
|
+
* field writes nothing rather than issuing an UPDATE with an empty SET list.
|
|
61
|
+
*/
|
|
62
|
+
updateProfile(userId, patch) {
|
|
63
|
+
const sets = [];
|
|
64
|
+
const values = [];
|
|
65
|
+
if (patch.displayName !== void 0) {
|
|
66
|
+
sets.push("display_name = ?");
|
|
67
|
+
values.push(patch.displayName);
|
|
68
|
+
}
|
|
69
|
+
if (patch.avatar !== void 0) {
|
|
70
|
+
sets.push("avatar = ?");
|
|
71
|
+
values.push(patch.avatar);
|
|
72
|
+
}
|
|
73
|
+
if (sets.length === 0) return;
|
|
74
|
+
this.db.prepare(`UPDATE users SET ${sets.join(", ")} WHERE user_id = ?`).run(...values, userId);
|
|
75
|
+
}
|
|
44
76
|
/** Used by admin user deletion and account-creation compensation paths (owned Projects must be cleaned up first). */
|
|
45
77
|
delete(userId) {
|
|
46
78
|
this.db.prepare("DELETE FROM users WHERE user_id = ?").run(userId);
|
|
47
79
|
}
|
|
48
80
|
};
|
|
81
|
+
_init = __decoratorStart(null);
|
|
82
|
+
__decorateElement(_init, 5, "db", _db_dec, UsersRepo);
|
|
83
|
+
UsersRepo = __decorateElement(_init, 0, "UsersRepo", _UsersRepo_decorators, UsersRepo);
|
|
84
|
+
__runInitializers(_init, 1, UsersRepo);
|
|
49
85
|
|
|
50
86
|
// src/db/repos/auth-sessions.ts
|
|
51
87
|
import { createHash, randomBytes } from "crypto";
|
|
88
|
+
import { Component as Component2, Use as Use2 } from "@prismshadow/penguin-core/kernel";
|
|
52
89
|
function sessionTokenHash(token) {
|
|
53
90
|
return createHash("sha256").update(token).digest("hex");
|
|
54
91
|
}
|
|
92
|
+
var _db_dec2, _AuthSessionsRepo_decorators, _init2;
|
|
93
|
+
_AuthSessionsRepo_decorators = [Component2()], _db_dec2 = [Use2()];
|
|
55
94
|
var AuthSessionsRepo = class {
|
|
56
|
-
constructor(
|
|
57
|
-
this
|
|
95
|
+
constructor() {
|
|
96
|
+
__publicField(this, "db", __runInitializers(_init2, 8, this)), __runInitializers(_init2, 11, this);
|
|
58
97
|
}
|
|
59
|
-
db;
|
|
60
98
|
/**
|
|
61
99
|
* Mints a session and returns the raw token for a cookie or the terminal. `maxTtlMs` caps a
|
|
62
100
|
* caller-supplied lifetime (`--ttl-seconds`): one longer than an ordinary session would make
|
|
@@ -118,10 +156,14 @@ var AuthSessionsRepo = class {
|
|
|
118
156
|
this.db.prepare("DELETE FROM auth_sessions WHERE user_id = ? AND via = ?").run(userId, via);
|
|
119
157
|
}
|
|
120
158
|
};
|
|
159
|
+
_init2 = __decoratorStart(null);
|
|
160
|
+
__decorateElement(_init2, 5, "db", _db_dec2, AuthSessionsRepo);
|
|
161
|
+
AuthSessionsRepo = __decorateElement(_init2, 0, "AuthSessionsRepo", _AuthSessionsRepo_decorators, AuthSessionsRepo);
|
|
162
|
+
__runInitializers(_init2, 1, AuthSessionsRepo);
|
|
121
163
|
|
|
122
164
|
export {
|
|
123
165
|
UsersRepo,
|
|
124
166
|
sessionTokenHash,
|
|
125
167
|
AuthSessionsRepo
|
|
126
168
|
};
|
|
127
|
-
//# sourceMappingURL=chunk-
|
|
169
|
+
//# sourceMappingURL=chunk-TOOTEEM4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/db/repos/users.ts","../src/db/repos/auth-sessions.ts"],"sourcesContent":["/**\n * users table repo: pure SQL wrapper, no business rules.\n * user_id is the login name (a semantic id, specified at creation, immutable).\n */\nimport { Component, Use } from \"@prismshadow/penguin-core/kernel\";\nimport type { Db } from \"../../hmr/capabilities.js\";\nimport type { Users } from \"../../mechanisms/identity.js\";\n\nexport interface UserRow {\n userId: string;\n passwordHash: string;\n isAdmin: boolean;\n /** Still using the initial password (seeded / set by an admin); cleared to 0 once the user changes it. */\n passwordIsInitial: boolean;\n /** Nickname shown in place of the id; null = never set. */\n displayName: string | null;\n /** Avatar as a data URL; null = never set. */\n avatar: string | null;\n createdAt: string;\n}\n\nfunction mapRow(r: Record<string, unknown>): UserRow {\n return {\n userId: r.user_id as string,\n passwordHash: r.password_hash as string,\n isAdmin: (r.is_admin as number) === 1,\n passwordIsInitial: (r.password_is_initial as number) === 1,\n displayName: (r.display_name as string | null) ?? null,\n avatar: (r.avatar as string | null) ?? null,\n createdAt: r.created_at as string,\n };\n}\n\n@Component()\nexport class UsersRepo implements Users {\n @Use() private readonly db!: Db;\n\n insert(row: UserRow): void {\n this.db\n .prepare(\n \"INSERT INTO users (user_id, password_hash, is_admin, password_is_initial, display_name, avatar, created_at)\" +\n \" VALUES (?, ?, ?, ?, ?, ?, ?)\",\n )\n .run(\n row.userId,\n row.passwordHash,\n row.isAdmin ? 1 : 0,\n row.passwordIsInitial ? 1 : 0,\n row.displayName,\n row.avatar,\n row.createdAt,\n );\n }\n\n findById(userId: string): UserRow | null {\n const r = this.db.prepare(\"SELECT * FROM users WHERE user_id = ?\").get(userId);\n return r ? mapRow(r) : null;\n }\n\n /** All users (for the admin user backend), ordered by creation time ascending. */\n list(): UserRow[] {\n const rows = this.db.prepare(\"SELECT * FROM users ORDER BY created_at ASC, user_id ASC\").all();\n return rows.map(mapRow);\n }\n\n count(): number {\n const r = this.db.prepare(\"SELECT COUNT(*) AS n FROM users\").get();\n return (r?.n as number) ?? 0;\n }\n\n /** Update the password hash; isInitial marks whether the password was set by someone else (seed / admin). */\n updatePassword(userId: string, passwordHash: string, isInitial: boolean): void {\n this.db\n .prepare(\"UPDATE users SET password_hash = ?, password_is_initial = ? WHERE user_id = ?\")\n .run(passwordHash, isInitial ? 1 : 0, userId);\n }\n\n /**\n * Writes the profile fields the patch names, leaving the rest as they are: an absent key\n * keeps the stored value, `null` clears it, a string replaces it. A patch naming neither\n * field writes nothing rather than issuing an UPDATE with an empty SET list.\n */\n updateProfile(\n userId: string,\n patch: { displayName?: string | null; avatar?: string | null },\n ): void {\n const sets: string[] = [];\n const values: (string | null)[] = [];\n if (patch.displayName !== undefined) {\n sets.push(\"display_name = ?\");\n values.push(patch.displayName);\n }\n if (patch.avatar !== undefined) {\n sets.push(\"avatar = ?\");\n values.push(patch.avatar);\n }\n if (sets.length === 0) return;\n this.db.prepare(`UPDATE users SET ${sets.join(\", \")} WHERE user_id = ?`).run(...values, userId);\n }\n\n /** Used by admin user deletion and account-creation compensation paths (owned Projects must be cleaned up first). */\n delete(userId: string): void {\n this.db.prepare(\"DELETE FROM users WHERE user_id = ?\").run(userId);\n }\n}\n","/**\n * auth_sessions table repo (server-side sessions backing the HttpOnly cookie). Stores only\n * sha256(token); the raw token appears only in the cookie. `issue()` is the ONE minting point,\n * so a CLI-minted row cannot drift from one the server issues itself.\n */\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { Component, Use } from \"@prismshadow/penguin-core/kernel\";\nimport type { Db } from \"../../hmr/capabilities.js\";\nimport type { AuthSessions } from \"../../mechanisms/identity.js\";\n\n/** How a session was established, as stored. NULL on rows formed before the column existed. */\nexport type SessionViaValue = \"password\" | \"desktop\" | \"setup\" | \"cli\";\n\nexport interface AuthSessionRow {\n tokenHash: string;\n userId: string;\n createdAt: string;\n expiresAt: string;\n via: SessionViaValue | null;\n}\n\n/** sha256 hex of a raw session token — the only form the database ever holds. */\nexport function sessionTokenHash(token: string): string {\n return createHash(\"sha256\").update(token).digest(\"hex\");\n}\n\n@Component()\nexport class AuthSessionsRepo implements AuthSessions {\n @Use() private readonly db!: Db;\n\n /**\n * Mints a session and returns the raw token for a cookie or the terminal. `maxTtlMs` caps a\n * caller-supplied lifetime (`--ttl-seconds`): one longer than an ordinary session would make\n * a leaked token file outlive every other credential, sliding forever. Expired rows are\n * swept here too, so a cron that only mints does not accumulate them.\n */\n issue(opts: {\n userId: string;\n via: SessionViaValue;\n now: Date;\n ttlMs: number;\n maxTtlMs?: number;\n }): { token: string; expiresAt: string } {\n const ttlMs = Math.max(1_000, Math.min(opts.ttlMs, opts.maxTtlMs ?? opts.ttlMs));\n const token = randomBytes(32).toString(\"base64url\");\n const createdAt = opts.now.toISOString();\n const expiresAt = new Date(opts.now.getTime() + ttlMs).toISOString();\n this.deleteExpired(createdAt);\n this.insert({\n tokenHash: sessionTokenHash(token),\n userId: opts.userId,\n createdAt,\n expiresAt,\n via: opts.via,\n });\n return { token, expiresAt };\n }\n\n insert(row: AuthSessionRow): void {\n this.db\n .prepare(\n \"INSERT INTO auth_sessions (token_hash, user_id, created_at, expires_at, via) VALUES (?, ?, ?, ?, ?)\",\n )\n .run(row.tokenHash, row.userId, row.createdAt, row.expiresAt, row.via);\n }\n\n findByTokenHash(tokenHash: string): AuthSessionRow | null {\n const r = this.db.prepare(\"SELECT * FROM auth_sessions WHERE token_hash = ?\").get(tokenHash);\n if (!r) return null;\n return {\n tokenHash: r.token_hash as string,\n userId: r.user_id as string,\n createdAt: r.created_at as string,\n expiresAt: r.expires_at as string,\n via: (r.via as SessionViaValue | null) ?? null,\n };\n }\n\n /** Sliding renewal: update the expiration time. */\n touch(tokenHash: string, expiresAt: string): void {\n this.db\n .prepare(\"UPDATE auth_sessions SET expires_at = ? WHERE token_hash = ?\")\n .run(expiresAt, tokenHash);\n }\n\n delete(tokenHash: string): void {\n this.db.prepare(\"DELETE FROM auth_sessions WHERE token_hash = ?\").run(tokenHash);\n }\n\n /** Opportunistically clean up expired sessions (called during login/validation). */\n deleteExpired(nowIso: string): void {\n this.db.prepare(\"DELETE FROM auth_sessions WHERE expires_at < ?\").run(nowIso);\n }\n\n /** Clear all sessions for a user (forces re-login after an admin resets the password). */\n deleteByUser(userId: string): void {\n this.db.prepare(\"DELETE FROM auth_sessions WHERE user_id = ?\").run(userId);\n }\n\n /**\n * Clear a user's sessions of one kind. Used to end EVERY first-login session when a password\n * is set: an earlier boot's link may still be live in someone's scrollback, and a `setup`\n * session may change the password without knowing the old one.\n */\n deleteByUserAndVia(userId: string, via: SessionViaValue): void {\n this.db.prepare(\"DELETE FROM auth_sessions WHERE user_id = ? AND via = ?\").run(userId, via);\n }\n}\n"],"mappings":"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","names":["Component","Use","_db_dec","_init","Component","Use"]}
|
package/dist/hmr/manifest.d.ts
CHANGED
|
@@ -1,77 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The committed on-disk record: a runtime restart resumes exactly this CODE (platform,
|
|
5
|
-
* cli, web). It carries no STATE — a restart always boots the resumed platform bundle
|
|
6
|
-
* against its own fresh initial context (bundle.context, see host.ts's PlatformBundle),
|
|
7
|
-
* never a document parked from a previous run (see host.ts's module doc for why: a
|
|
8
|
-
* parked doc's live-resource handles die with the process anyway, so resuming one could
|
|
9
|
-
* only ever produce handles that fail to reclaim). Paths are relative to hmrDir
|
|
10
|
-
* (`<root>/hmr`).
|
|
11
|
-
*/
|
|
12
|
-
interface Manifest {
|
|
13
|
-
platform?: {
|
|
14
|
-
bundle: string;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* The CLI's own bundle pointer — its own independent file (a different sha from
|
|
18
|
-
* `platform.bundle`; the two are separately compiled artifacts), kept as its own
|
|
19
|
-
* field so a reader that only wants "which bundle does the CLI load right now"
|
|
20
|
-
* never has to reach into `platform` at all.
|
|
21
|
-
*/
|
|
22
|
-
cli?: {
|
|
23
|
-
bundle: string;
|
|
24
|
-
};
|
|
25
|
-
/** One gzip(JSON.stringify({ files })) artifact, restored straight into memory. */
|
|
26
|
-
web?: {
|
|
27
|
-
manifest: string;
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* Files the pushed platform needs as REAL files on disk, unpacked under this directory
|
|
31
|
-
* (relative to hmrDir). The web artifact stays in memory because it is only ever served
|
|
32
|
-
* as bytes; an asset is something the platform hands to the OS — a native `.node` the
|
|
33
|
-
* loader must resolve by path, a helper binary it execs — so it has to exist on the
|
|
34
|
-
* filesystem, with its exec bit intact.
|
|
35
|
-
*/
|
|
36
|
-
assets?: {
|
|
37
|
-
dir: string;
|
|
38
|
-
};
|
|
39
|
-
/**
|
|
40
|
-
* Provenance of the push that produced this version, exactly as the pushing client sent
|
|
41
|
-
* it (scripts/deploy.mjs fills it from its own checkout). Recorded, never executed or
|
|
42
|
-
* resolved. Absent for a version pushed without it, and for every version committed
|
|
43
|
-
* before the field existed — which is why every reader treats it as optional.
|
|
44
|
-
*/
|
|
45
|
-
source?: {
|
|
46
|
-
repo: string;
|
|
47
|
-
revision: string;
|
|
48
|
-
};
|
|
49
|
-
/** When this version was committed to the store (ISO 8601). Absent on older records. */
|
|
50
|
-
pushedAt?: string;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* What the store has committed, for version reporting: provenance, commit time, and the
|
|
54
|
-
* three artifact pointers. Null when nothing has ever been pushed to this root.
|
|
55
|
-
*
|
|
56
|
-
* Read defensively rather than cast: harness.json is written only by persistVersion, but a
|
|
57
|
-
* truncated or hand-edited file must degrade a `penguin version` to missing fields, never
|
|
58
|
-
* crash it. A record missing every artifact pointer counts as nothing pushed.
|
|
59
|
-
*/
|
|
60
|
-
declare function readHarnessInfo(root: string): Promise<HarnessInfo | null>;
|
|
61
|
-
/** Reads and parses `<root>/hmr/harness.json`; null when missing or corrupt (nothing committed yet). */
|
|
62
|
-
declare function readManifest(root: string): Promise<Manifest | null>;
|
|
63
|
-
/**
|
|
64
|
-
* Resolves the current CLI bundle's absolute path from a data root, or null when
|
|
65
|
-
* nothing has been pushed yet: a fresh root, a missing/corrupt manifest, no `cli`
|
|
66
|
-
* entry, a `cli.bundle` that isn't a non-empty string (e.g. `{"cli":{}}` — a
|
|
67
|
-
* hand-edited or truncated harness.json), a path that resolves outside `<root>/hmr/`
|
|
68
|
-
* (defense in depth: `manifest.cli.bundle` is trusted content today, written only by
|
|
69
|
-
* this same process's persistVersion, but a reader must never let a malformed or
|
|
70
|
-
* malicious manifest value walk it out of the store directory via `..` segments), or
|
|
71
|
-
* a referenced file that no longer exists (e.g. pruned from the store — see host.ts's
|
|
72
|
-
* pruneStore). A null return means "use the packaged default" to every caller;
|
|
73
|
-
* nothing here ever throws for an ordinary unconfigured or malformed root.
|
|
74
|
-
*/
|
|
75
|
-
declare function resolveCliBundlePath(root: string): Promise<string | null>;
|
|
76
|
-
|
|
77
|
-
export { type Manifest, readHarnessInfo, readManifest, resolveCliBundlePath };
|
|
1
|
+
export * from '@prismshadow/penguin-hmr/manifest';
|
package/dist/hmr/manifest.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
+
MATERIALIZED,
|
|
2
3
|
readHarnessInfo,
|
|
3
4
|
readManifest,
|
|
5
|
+
readPushedCli,
|
|
4
6
|
resolveCliBundlePath
|
|
5
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-LPPVGHYD.js";
|
|
8
|
+
import "../chunk-B5IP37ZQ.js";
|
|
6
9
|
export {
|
|
10
|
+
MATERIALIZED,
|
|
7
11
|
readHarnessInfo,
|
|
8
12
|
readManifest,
|
|
13
|
+
readPushedCli,
|
|
9
14
|
resolveCliBundlePath
|
|
10
15
|
};
|
|
11
16
|
//# sourceMappingURL=manifest.js.map
|