@openparachute/hub 0.5.13 → 0.5.14-rc.2
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/package.json +2 -2
- package/src/__tests__/account-home-ui.test.ts +163 -0
- package/src/__tests__/admin-handlers.test.ts +74 -0
- package/src/__tests__/admin-host-admin-token.test.ts +62 -0
- package/src/__tests__/admin-vault-admin-token.test.ts +44 -0
- package/src/__tests__/api-account.test.ts +191 -1
- package/src/__tests__/api-modules-ops.test.ts +97 -0
- package/src/__tests__/api-modules.test.ts +32 -32
- package/src/__tests__/api-users.test.ts +383 -11
- package/src/__tests__/chrome-strip.test.ts +15 -15
- package/src/__tests__/hub-db.test.ts +194 -29
- package/src/__tests__/hub-server.test.ts +23 -23
- package/src/__tests__/notes-redirect.test.ts +20 -20
- package/src/__tests__/oauth-handlers.test.ts +722 -28
- package/src/__tests__/serve.test.ts +9 -9
- package/src/__tests__/services-manifest.test.ts +40 -40
- package/src/__tests__/setup-wizard.test.ts +493 -25
- package/src/__tests__/setup.test.ts +1 -1
- package/src/__tests__/status.test.ts +39 -0
- package/src/__tests__/users.test.ts +396 -9
- package/src/__tests__/well-known.test.ts +9 -9
- package/src/account-home-ui.ts +434 -0
- package/src/admin-handlers.ts +49 -17
- package/src/admin-host-admin-token.ts +25 -0
- package/src/admin-vault-admin-token.ts +17 -0
- package/src/api-account.ts +72 -6
- package/src/api-modules-ops.ts +52 -16
- package/src/api-modules.ts +3 -3
- package/src/api-users.ts +468 -55
- package/src/bun-link.ts +55 -0
- package/src/chrome-strip.ts +6 -6
- package/src/commands/install.ts +8 -21
- package/src/commands/status.ts +10 -1
- package/src/help.ts +2 -2
- package/src/hub-db.ts +42 -0
- package/src/hub-server.ts +69 -10
- package/src/hub-settings.ts +2 -2
- package/src/hub.ts +6 -6
- package/src/notes-redirect.ts +5 -5
- package/src/oauth-handlers.ts +278 -173
- package/src/oauth-ui.ts +18 -2
- package/src/service-spec.ts +39 -18
- package/src/setup-wizard.ts +489 -42
- package/src/users.ts +307 -29
- package/web/ui/dist/assets/index-tRmPbbC7.js +61 -0
- package/web/ui/dist/index.html +1 -1
- package/web/ui/dist/assets/index-Dzrbe6EP.js +0 -61
|
@@ -52,11 +52,11 @@ describe("seedInitialAdminIfNeeded", () => {
|
|
|
52
52
|
expect(log.mock.calls[0]?.[0] ?? "").toContain("ops");
|
|
53
53
|
// Multi-user Phase 1 (design 2026-05-20-multi-user-phase-1.md §wizard
|
|
54
54
|
// interaction): env-seeded admin chose their password via env vars, so
|
|
55
|
-
// skip the force-change-password redirect. `
|
|
56
|
-
// — admin posture.
|
|
55
|
+
// skip the force-change-password redirect. `assignedVaults` stays []
|
|
56
|
+
// — admin posture (multi-user Phase 2 PR 2 lifted single → array).
|
|
57
57
|
const seeded = getUserByUsername(db, "ops");
|
|
58
58
|
expect(seeded?.passwordChanged).toBe(true);
|
|
59
|
-
expect(seeded?.
|
|
59
|
+
expect(seeded?.assignedVaults).toEqual([]);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
test("returns 'exists' when an admin already exists, even with env vars set", async () => {
|
|
@@ -287,12 +287,12 @@ describe("resolveStartupIssuer — precedence chain (hub#365)", () => {
|
|
|
287
287
|
test("strips trailing slashes from any source for canonical form", () => {
|
|
288
288
|
expect(resolveStartupIssuer({ issuer: "https://x.example/" }, {})).toBe("https://x.example");
|
|
289
289
|
expect(resolveStartupIssuer({ issuer: "https://x.example//" }, {})).toBe("https://x.example");
|
|
290
|
-
expect(
|
|
291
|
-
|
|
292
|
-
)
|
|
293
|
-
expect(
|
|
294
|
-
|
|
295
|
-
)
|
|
290
|
+
expect(resolveStartupIssuer({}, { PARACHUTE_HUB_ORIGIN: "https://x.example/" })).toBe(
|
|
291
|
+
"https://x.example",
|
|
292
|
+
);
|
|
293
|
+
expect(resolveStartupIssuer({}, { RENDER_EXTERNAL_URL: "https://x.example/" })).toBe(
|
|
294
|
+
"https://x.example",
|
|
295
|
+
);
|
|
296
296
|
});
|
|
297
297
|
|
|
298
298
|
test("returns undefined when no source has a value", () => {
|
|
@@ -232,10 +232,10 @@ describe("services-manifest", () => {
|
|
|
232
232
|
// runner) round-trip byte-identically.
|
|
233
233
|
describe("ServiceEntry.uis hierarchical sub-units (hub#313)", () => {
|
|
234
234
|
const app: ServiceEntry = {
|
|
235
|
-
name: "parachute-
|
|
235
|
+
name: "parachute-surface",
|
|
236
236
|
port: 1946,
|
|
237
|
-
paths: ["/
|
|
238
|
-
health: "/
|
|
237
|
+
paths: ["/surface"],
|
|
238
|
+
health: "/surface/healthz",
|
|
239
239
|
version: "0.1.0",
|
|
240
240
|
};
|
|
241
241
|
|
|
@@ -1026,17 +1026,17 @@ describe("legacy short-name row de-dupe (parachute-app#13 / runner#4)", () => {
|
|
|
1026
1026
|
JSON.stringify({
|
|
1027
1027
|
services: [
|
|
1028
1028
|
{
|
|
1029
|
-
name: "parachute-
|
|
1029
|
+
name: "parachute-surface",
|
|
1030
1030
|
port: 1946,
|
|
1031
|
-
paths: ["/
|
|
1032
|
-
health: "/
|
|
1031
|
+
paths: ["/surface"],
|
|
1032
|
+
health: "/surface/healthz",
|
|
1033
1033
|
version: "0.2.0",
|
|
1034
1034
|
},
|
|
1035
1035
|
{
|
|
1036
1036
|
name: "app",
|
|
1037
1037
|
port: 1946,
|
|
1038
|
-
paths: ["/
|
|
1039
|
-
health: "/
|
|
1038
|
+
paths: ["/surface"],
|
|
1039
|
+
health: "/surface/healthz",
|
|
1040
1040
|
version: "0.2.0",
|
|
1041
1041
|
},
|
|
1042
1042
|
],
|
|
@@ -1044,7 +1044,7 @@ describe("legacy short-name row de-dupe (parachute-app#13 / runner#4)", () => {
|
|
|
1044
1044
|
);
|
|
1045
1045
|
const m = readManifest(path);
|
|
1046
1046
|
expect(m.services).toHaveLength(1);
|
|
1047
|
-
expect(m.services[0]?.name).toBe("parachute-
|
|
1047
|
+
expect(m.services[0]?.name).toBe("parachute-surface");
|
|
1048
1048
|
} finally {
|
|
1049
1049
|
cleanup();
|
|
1050
1050
|
}
|
|
@@ -1099,10 +1099,10 @@ describe("legacy short-name row de-dupe (parachute-app#13 / runner#4)", () => {
|
|
|
1099
1099
|
JSON.stringify({
|
|
1100
1100
|
services: [
|
|
1101
1101
|
{
|
|
1102
|
-
name: "
|
|
1102
|
+
name: "widget",
|
|
1103
1103
|
port: 1946,
|
|
1104
|
-
paths: ["/
|
|
1105
|
-
health: "/
|
|
1104
|
+
paths: ["/surface"],
|
|
1105
|
+
health: "/surface/healthz",
|
|
1106
1106
|
version: "0.2.0",
|
|
1107
1107
|
},
|
|
1108
1108
|
],
|
|
@@ -1110,7 +1110,7 @@ describe("legacy short-name row de-dupe (parachute-app#13 / runner#4)", () => {
|
|
|
1110
1110
|
);
|
|
1111
1111
|
const m = readManifest(path);
|
|
1112
1112
|
expect(m.services).toHaveLength(1);
|
|
1113
|
-
expect(m.services[0]?.name).toBe("
|
|
1113
|
+
expect(m.services[0]?.name).toBe("widget");
|
|
1114
1114
|
} finally {
|
|
1115
1115
|
cleanup();
|
|
1116
1116
|
}
|
|
@@ -1124,17 +1124,17 @@ describe("legacy short-name row de-dupe (parachute-app#13 / runner#4)", () => {
|
|
|
1124
1124
|
JSON.stringify({
|
|
1125
1125
|
services: [
|
|
1126
1126
|
{
|
|
1127
|
-
name: "parachute-
|
|
1127
|
+
name: "parachute-surface",
|
|
1128
1128
|
port: 1946,
|
|
1129
|
-
paths: ["/
|
|
1130
|
-
health: "/
|
|
1129
|
+
paths: ["/surface"],
|
|
1130
|
+
health: "/surface/healthz",
|
|
1131
1131
|
version: "0.2.0",
|
|
1132
1132
|
},
|
|
1133
1133
|
{
|
|
1134
|
-
name: "
|
|
1134
|
+
name: "widget",
|
|
1135
1135
|
port: 9999,
|
|
1136
|
-
paths: ["/
|
|
1137
|
-
health: "/
|
|
1136
|
+
paths: ["/widget"],
|
|
1137
|
+
health: "/widget/health",
|
|
1138
1138
|
version: "1.0.0",
|
|
1139
1139
|
},
|
|
1140
1140
|
],
|
|
@@ -1142,7 +1142,7 @@ describe("legacy short-name row de-dupe (parachute-app#13 / runner#4)", () => {
|
|
|
1142
1142
|
);
|
|
1143
1143
|
const m = readManifest(path);
|
|
1144
1144
|
expect(m.services).toHaveLength(2);
|
|
1145
|
-
expect(m.services.map((s) => s.name).sort()).toEqual(["
|
|
1145
|
+
expect(m.services.map((s) => s.name).sort()).toEqual(["parachute-surface", "widget"]);
|
|
1146
1146
|
} finally {
|
|
1147
1147
|
cleanup();
|
|
1148
1148
|
}
|
|
@@ -1164,10 +1164,10 @@ describe("legacy short-name row de-dupe (parachute-app#13 / runner#4)", () => {
|
|
|
1164
1164
|
JSON.stringify({
|
|
1165
1165
|
services: [
|
|
1166
1166
|
{
|
|
1167
|
-
name: "parachute-
|
|
1167
|
+
name: "parachute-surface",
|
|
1168
1168
|
port: 1946,
|
|
1169
|
-
paths: ["/
|
|
1170
|
-
health: "/
|
|
1169
|
+
paths: ["/surface"],
|
|
1170
|
+
health: "/surface/healthz",
|
|
1171
1171
|
version: "0.2.0",
|
|
1172
1172
|
},
|
|
1173
1173
|
{
|
|
@@ -1197,17 +1197,17 @@ describe("legacy short-name row de-dupe (parachute-app#13 / runner#4)", () => {
|
|
|
1197
1197
|
JSON.stringify({
|
|
1198
1198
|
services: [
|
|
1199
1199
|
{
|
|
1200
|
-
name: "parachute-
|
|
1200
|
+
name: "parachute-surface",
|
|
1201
1201
|
port: 1946,
|
|
1202
|
-
paths: ["/
|
|
1203
|
-
health: "/
|
|
1202
|
+
paths: ["/surface"],
|
|
1203
|
+
health: "/surface/healthz",
|
|
1204
1204
|
version: "0.2.0",
|
|
1205
1205
|
},
|
|
1206
1206
|
{
|
|
1207
1207
|
name: "app",
|
|
1208
1208
|
port: 1946,
|
|
1209
|
-
paths: ["/
|
|
1210
|
-
health: "/
|
|
1209
|
+
paths: ["/surface"],
|
|
1210
|
+
health: "/surface/healthz",
|
|
1211
1211
|
version: "0.2.0",
|
|
1212
1212
|
},
|
|
1213
1213
|
],
|
|
@@ -1296,10 +1296,10 @@ describe("retired-module row de-dupe (hub#334)", () => {
|
|
|
1296
1296
|
JSON.stringify({
|
|
1297
1297
|
services: [
|
|
1298
1298
|
{
|
|
1299
|
-
name: "parachute-
|
|
1299
|
+
name: "parachute-surface",
|
|
1300
1300
|
port: 1946,
|
|
1301
|
-
paths: ["/
|
|
1302
|
-
health: "/
|
|
1301
|
+
paths: ["/surface"],
|
|
1302
|
+
health: "/surface/healthz",
|
|
1303
1303
|
version: "0.2.0",
|
|
1304
1304
|
},
|
|
1305
1305
|
],
|
|
@@ -1308,7 +1308,7 @@ describe("retired-module row de-dupe (hub#334)", () => {
|
|
|
1308
1308
|
const mtimeBefore = statSync(path).mtimeMs;
|
|
1309
1309
|
const m = readManifest(path);
|
|
1310
1310
|
expect(m.services).toHaveLength(1);
|
|
1311
|
-
expect(m.services[0]?.name).toBe("parachute-
|
|
1311
|
+
expect(m.services[0]?.name).toBe("parachute-surface");
|
|
1312
1312
|
// No rewrite when there's nothing to clean.
|
|
1313
1313
|
expect(statSync(path).mtimeMs).toBe(mtimeBefore);
|
|
1314
1314
|
} finally {
|
|
@@ -1334,10 +1334,10 @@ describe("retired-module row de-dupe (hub#334)", () => {
|
|
|
1334
1334
|
version: "0.1.4",
|
|
1335
1335
|
},
|
|
1336
1336
|
{
|
|
1337
|
-
name: "parachute-
|
|
1337
|
+
name: "parachute-surface",
|
|
1338
1338
|
port: 1946,
|
|
1339
|
-
paths: ["/
|
|
1340
|
-
health: "/
|
|
1339
|
+
paths: ["/surface"],
|
|
1340
|
+
health: "/surface/healthz",
|
|
1341
1341
|
version: "0.2.0",
|
|
1342
1342
|
},
|
|
1343
1343
|
],
|
|
@@ -1345,7 +1345,7 @@ describe("retired-module row de-dupe (hub#334)", () => {
|
|
|
1345
1345
|
);
|
|
1346
1346
|
const m = readManifest(path);
|
|
1347
1347
|
expect(m.services).toHaveLength(1);
|
|
1348
|
-
expect(m.services[0]?.name).toBe("parachute-
|
|
1348
|
+
expect(m.services[0]?.name).toBe("parachute-surface");
|
|
1349
1349
|
} finally {
|
|
1350
1350
|
cleanup();
|
|
1351
1351
|
}
|
|
@@ -1411,8 +1411,8 @@ describe("readManifestLenient — skips bad entries instead of throwing (hub#406
|
|
|
1411
1411
|
JSON.stringify({
|
|
1412
1412
|
services: [
|
|
1413
1413
|
{ name: "parachute-vault", port: 1940, paths: ["/vault/default"], health: "/vault/default/health", version: "0.4.8-rc.10" },
|
|
1414
|
-
{ name: "parachute-
|
|
1415
|
-
{ name: "
|
|
1414
|
+
{ name: "parachute-surface", port: 1946, paths: ["/surface"], health: "/surface/healthz", version: "0.2.0-rc.13" },
|
|
1415
|
+
{ name: "widget", port: 0, paths: ["/widget"], health: "/widget/health", version: "0.0.1" },
|
|
1416
1416
|
],
|
|
1417
1417
|
}),
|
|
1418
1418
|
);
|
|
@@ -1420,7 +1420,7 @@ describe("readManifestLenient — skips bad entries instead of throwing (hub#406
|
|
|
1420
1420
|
const log = { warn: (m: string) => warnings.push(m) };
|
|
1421
1421
|
const m = readManifestLenient(path, log);
|
|
1422
1422
|
const names = m.services.map((s) => s.name).sort();
|
|
1423
|
-
expect(names).toEqual(["parachute-
|
|
1423
|
+
expect(names).toEqual(["parachute-surface", "parachute-vault"]);
|
|
1424
1424
|
expect(warnings.some((w) => w.includes("port") && w.includes("integer"))).toBe(true);
|
|
1425
1425
|
} finally {
|
|
1426
1426
|
cleanup();
|
|
@@ -1479,7 +1479,7 @@ describe("readManifestLenient — skips bad entries instead of throwing (hub#406
|
|
|
1479
1479
|
writeFileSync(
|
|
1480
1480
|
path,
|
|
1481
1481
|
JSON.stringify({
|
|
1482
|
-
services: [{ name: "
|
|
1482
|
+
services: [{ name: "widget", port: 0, paths: ["/widget"], health: "/widget/health", version: "0.0.1" }],
|
|
1483
1483
|
}),
|
|
1484
1484
|
);
|
|
1485
1485
|
expect(() => readManifest(path)).toThrow(ServicesManifestError);
|