@capybara-hq/kernel 0.1.0-rc.13 → 0.1.0-rc.15
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/dist/admin/admin.schemas.d.ts +5 -13
- package/dist/catalog/admin-catalog.controller.d.ts +39 -5
- package/dist/catalog/admin-catalog.controller.js +75 -6
- package/dist/catalog/admin-catalog.schemas.d.ts +10 -0
- package/dist/catalog/admin-catalog.schemas.js +7 -0
- package/dist/catalog/appearance.schemas.d.ts +10 -0
- package/dist/catalog/appearance.schemas.js +20 -0
- package/dist/catalog/appearance.service.d.ts +105 -0
- package/dist/catalog/appearance.service.js +306 -0
- package/dist/catalog/catalog-content.d.ts +9 -1
- package/dist/catalog/catalog-content.js +2 -1
- package/dist/catalog/catalog-files.d.ts +3 -2
- package/dist/catalog/catalog-files.js +8 -5
- package/dist/catalog/catalog-import.module.js +3 -0
- package/dist/catalog/catalog-items.d.ts +11 -0
- package/dist/catalog/catalog-items.js +21 -0
- package/dist/catalog/catalog-scope.service.d.ts +7 -0
- package/dist/catalog/catalog-scope.service.js +55 -15
- package/dist/catalog/catalog.service.js +10 -2
- package/dist/catalog/cli.js +3 -0
- package/dist/catalog/component-scope.service.d.ts +14 -1
- package/dist/catalog/component-scope.service.js +12 -2
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/org-catalog.controller.d.ts +5 -4
- package/dist/catalog/org-catalog.controller.js +5 -4
- package/dist/catalog/org-catalog.schemas.d.ts +18 -2
- package/dist/catalog/org-catalog.schemas.js +16 -2
- package/dist/catalog/org-catalog.service.js +8 -2
- package/dist/catalog/org-extension.controller.d.ts +27 -7
- package/dist/catalog/org-extension.controller.js +48 -6
- package/dist/catalog/plugin-import.d.ts +28 -4
- package/dist/catalog/plugin-import.js +96 -20
- package/dist/catalog/plugin-package.d.ts +4 -2
- package/dist/catalog/plugin-package.js +66 -12
- package/dist/catalog/plugin-upgrade.d.ts +15 -1
- package/dist/catalog/plugin-upgrade.js +18 -5
- package/dist/channels/connections.controller.d.ts +1 -1
- package/dist/channels/connections.controller.js +1 -1
- package/dist/channels/connections.service.d.ts +22 -5
- package/dist/channels/connections.service.js +56 -10
- package/dist/channels/webhook.d.ts +21 -1
- package/dist/channels/webhook.js +39 -4
- package/dist/client/client-access.service.d.ts +101 -0
- package/dist/client/client-access.service.js +294 -0
- package/dist/client/client-builds.service.d.ts +109 -0
- package/dist/client/client-builds.service.js +229 -0
- package/dist/client/client-delivery.controller.d.ts +25 -0
- package/dist/client/client-delivery.controller.js +66 -0
- package/dist/client/client-files.d.ts +48 -0
- package/dist/client/client-files.js +136 -0
- package/dist/client/client-reports.cron.d.ts +13 -0
- package/dist/client/client-reports.cron.js +42 -0
- package/dist/client/client-reports.service.d.ts +30 -0
- package/dist/client/client-reports.service.js +102 -0
- package/dist/client/client.controller.d.ts +45 -0
- package/dist/client/client.controller.js +121 -0
- package/dist/client/client.module.d.ts +7 -0
- package/dist/client/client.module.js +26 -0
- package/dist/client/client.schemas.d.ts +55 -0
- package/dist/client/client.schemas.js +27 -0
- package/dist/ctx/create-ctx.d.ts +19 -4
- package/dist/ctx/create-ctx.js +51 -3
- package/dist/db/kernel-schema.d.ts +58 -2
- package/dist/errors.d.ts +33 -0
- package/dist/errors.js +33 -0
- package/dist/extensions/admin-ops.d.ts +4 -1
- package/dist/extensions/admin-ops.js +22 -1
- package/dist/extensions/admin-view.d.ts +8 -0
- package/dist/extensions/admin-view.js +78 -0
- package/dist/extensions/blobs.d.ts +14 -1
- package/dist/extensions/blobs.js +48 -5
- package/dist/extensions/guide-places.d.ts +66 -0
- package/dist/extensions/guide-places.js +152 -0
- package/dist/extensions/host.d.ts +6 -1
- package/dist/extensions/host.js +23 -12
- package/dist/extensions/instance.d.ts +5 -1
- package/dist/extensions/instance.js +8 -0
- package/dist/extensions/ledger.d.ts +1 -1
- package/dist/extensions/package.d.ts +4 -3
- package/dist/extensions/package.js +5 -1
- package/dist/extensions/permission-set.d.ts +35 -5
- package/dist/extensions/permission-set.js +36 -5
- package/dist/extensions/permission-view.d.ts +38 -7
- package/dist/extensions/permission-view.js +27 -8
- package/dist/extensions/plugin-guides.d.ts +207 -54
- package/dist/extensions/plugin-guides.js +436 -165
- package/dist/extensions/sources.d.ts +6 -0
- package/dist/extensions/sources.js +2 -0
- package/dist/extensions/state.module.js +9 -1
- package/dist/http/api.module.js +16 -0
- package/dist/identity/settings.d.ts +41 -26
- package/dist/identity/settings.js +87 -4
- package/dist/infra/lock/lock.service.d.ts +8 -0
- package/dist/infra/lock/lock.service.js +27 -0
- package/dist/infra/throttler/named-throttlers.d.ts +8 -1
- package/dist/infra/throttler/named-throttlers.js +30 -1
- package/dist/infra/throttler/throttler.guard.js +1 -1
- package/dist/kernel.module.js +3 -0
- package/dist/orgs/settings.service.d.ts +2 -26
- package/dist/overrides.d.ts +3 -1
- package/dist/overrides.js +2 -0
- package/dist/platform/exclusions.js +37 -6
- package/dist/platform/operation.js +4 -0
- package/dist/platform/ops/admin.ops.js +17 -2
- package/dist/platform/ops/catalog-admin.ops.d.ts +19 -1
- package/dist/platform/ops/catalog-admin.ops.js +85 -2
- package/dist/platform/ops/connectors.ops.js +2 -0
- package/dist/platform/ops/org-plugins.ops.d.ts +7 -1
- package/dist/platform/ops/org-plugins.ops.js +63 -4
- package/dist/platform/ops/org-settings.ops.js +2 -2
- package/dist/platform/ops/orgs.ops.js +1 -0
- package/dist/platform/ops/setup.ops.d.ts +24 -0
- package/dist/platform/ops/setup.ops.js +212 -0
- package/dist/platform/ops/ui.ops.js +1 -1
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +7 -0
- package/dist/platform/route-schemas.js +8 -0
- package/dist/roles/worker.module.js +4 -0
- package/dist/setup/kernel-checks.d.ts +91 -0
- package/dist/setup/kernel-checks.js +295 -0
- package/dist/setup/oauth-token-probe.d.ts +47 -0
- package/dist/setup/oauth-token-probe.js +108 -0
- package/dist/setup/redact-check.d.ts +18 -0
- package/dist/setup/redact-check.js +46 -0
- package/dist/setup/setup-check-registry.d.ts +86 -0
- package/dist/setup/setup-check-registry.js +75 -0
- package/dist/setup/setup-checks.controller.d.ts +28 -0
- package/dist/setup/setup-checks.controller.js +78 -0
- package/dist/setup/setup-checks.schemas.d.ts +38 -0
- package/dist/setup/setup-checks.schemas.js +43 -0
- package/dist/setup/setup-checks.service.d.ts +144 -0
- package/dist/setup/setup-checks.service.js +683 -0
- package/dist/setup/setup-guides.controller.d.ts +18 -0
- package/dist/setup/setup-guides.controller.js +48 -0
- package/dist/setup/setup-guides.schemas.d.ts +10 -0
- package/dist/setup/setup-guides.schemas.js +6 -0
- package/dist/setup/setup-guides.service.d.ts +126 -0
- package/dist/setup/setup-guides.service.js +315 -0
- package/dist/setup/setup.module.d.ts +8 -0
- package/dist/setup/setup.module.js +31 -0
- package/dist/themes/render-css.d.ts +2 -1
- package/dist/themes/render-css.js +5 -2
- package/dist/themes/theme-delivery.service.d.ts +4 -2
- package/dist/themes/theme-delivery.service.js +8 -3
- package/dist/themes/theme-state.d.ts +28 -7
- package/dist/themes/theme-state.js +24 -6
- package/dist/themes/themes-public.controller.d.ts +7 -2
- package/dist/themes/themes-public.controller.js +11 -5
- package/dist/themes/themes.service.js +2 -2
- package/dist/tools/connectors.controller.d.ts +28 -11
- package/dist/tools/connectors.controller.js +49 -14
- package/dist/tools/connectors.schemas.d.ts +4 -0
- package/dist/tools/connectors.schemas.js +4 -0
- package/dist/tools/connectors.service.d.ts +30 -2
- package/dist/tools/connectors.service.js +98 -3
- package/dist/tools/credentials.d.ts +20 -0
- package/dist/tools/credentials.js +90 -1
- package/dist/tools/oauth/tokens.d.ts +20 -0
- package/dist/tools/oauth/tokens.js +49 -24
- package/dist/tools/pipeline.d.ts +3 -2
- package/dist/ui/arrangement.d.ts +41 -50
- package/dist/ui/arrangement.js +88 -15
- package/dist/ui/registry.d.ts +13 -3
- package/dist/ui/registry.js +15 -4
- package/dist/ui/ui.schemas.d.ts +4 -0
- package/dist/ui/views.d.ts +36 -9
- package/dist/ui/views.js +58 -28
- package/package.json +3 -3
- package/src/migrations/0106_setup_guides_v2.sql +10 -0
- package/src/migrations/0107_extension_client_builds.sql +50 -0
|
@@ -12,11 +12,11 @@ import type { ExtensionSource } from "./sources.js";
|
|
|
12
12
|
export declare function reviewHash(set: PermissionSet, warnings?: readonly PackageWarning[]): string;
|
|
13
13
|
/** 权限清单展示用的一组:按类别分组,`sensitive` 标出需要管理员特别留意的类别。 */
|
|
14
14
|
export interface PermissionGroup {
|
|
15
|
-
category: "provides" | "requires" | "optional" | "config" | "tables" | "secrets" | "storage" | "http" | "publicRoutes" | "tools" | "hooks" | "models" | "network" | "ui";
|
|
15
|
+
category: "provides" | "requires" | "optional" | "config" | "tables" | "secrets" | "storage" | "http" | "publicRoutes" | "tools" | "hooks" | "models" | "network" | "ui" | "client";
|
|
16
16
|
/**
|
|
17
|
-
* Sensitive category: secrets, public routes, tools, hooks; also the "provides" group
|
|
18
|
-
* one of {@link SENSITIVE_EXTENSION_POINTS}, and the "UI" group when it holds an
|
|
19
|
-
* (embed: the extension's own page in an iframe).
|
|
17
|
+
* Sensitive category: secrets, public routes, tools, hooks, client code; also the "provides" group
|
|
18
|
+
* when it holds one of {@link SENSITIVE_EXTENSION_POINTS}, and the "UI" group when it holds an
|
|
19
|
+
* embedded app (embed: the extension's own page in an iframe) or a client component.
|
|
20
20
|
*/
|
|
21
21
|
sensitive: boolean;
|
|
22
22
|
items: string[];
|
|
@@ -25,10 +25,12 @@ export interface PermissionGroup {
|
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
27
|
* Sensitive reason codes (the UI translates them by code): secrets, public routes, tools, hooks,
|
|
28
|
-
* embedded apps, and
|
|
29
|
-
*
|
|
28
|
+
* embedded apps, client code and client components (client extensions spec §5.4: code that runs with
|
|
29
|
+
* full trust in members' browsers), and sensitive extension points
|
|
30
|
+
* ({@link SENSITIVE_EXTENSION_POINTS}; the code is the extension point name without `@<major>`, with
|
|
31
|
+
* `-` replaced by `_`).
|
|
30
32
|
*/
|
|
31
|
-
export type SensitiveReason = "secrets" | "public_routes" | "tools" | "hooks" | "ui_embed" | "channel" | "directory" | "identity_source" | "sandbox" | "object_store" | "migration_warnings";
|
|
33
|
+
export type SensitiveReason = "secrets" | "public_routes" | "tools" | "hooks" | "ui_embed" | "client_code" | "ui_component" | "channel" | "directory" | "identity_source" | "sandbox" | "object_store" | "migration_warnings";
|
|
32
34
|
/** 权限清单里的一项(分组里的一项,或 `added` 里的一个权限键)。 */
|
|
33
35
|
export interface PermissionItem {
|
|
34
36
|
value: string;
|
|
@@ -151,6 +153,35 @@ export interface AdminExtensionView {
|
|
|
151
153
|
ref: string;
|
|
152
154
|
reason: UiDropReason;
|
|
153
155
|
}[];
|
|
156
|
+
/**
|
|
157
|
+
* The client build that runs (client extensions spec §9.1, §9.3, §10.4): the one a live replica
|
|
158
|
+
* serves (the install's own package when it runs, else the older one still served beside an
|
|
159
|
+
* upgrade awaiting approval or failed), or the install record package's build when no replica
|
|
160
|
+
* serves one; its id (the package sha256), every file with its size, and the client API
|
|
161
|
+
* version; null when that package has no client code (or the view is degraded).
|
|
162
|
+
*/
|
|
163
|
+
client: {
|
|
164
|
+
build: string;
|
|
165
|
+
files: {
|
|
166
|
+
path: string;
|
|
167
|
+
bytes: number;
|
|
168
|
+
}[];
|
|
169
|
+
apiVersion: number;
|
|
170
|
+
} | null;
|
|
171
|
+
/**
|
|
172
|
+
* Crash and load reports of that build, newest first, at most 50: `orgId` is null for admin
|
|
173
|
+
* pages and may name an org deleted since (rows stay until the 30-day prune). Empty without a
|
|
174
|
+
* client build.
|
|
175
|
+
*/
|
|
176
|
+
clientReports: {
|
|
177
|
+
orgId: string | null;
|
|
178
|
+
contribution: string | null;
|
|
179
|
+
kind: string;
|
|
180
|
+
count: number;
|
|
181
|
+
firstAt: string;
|
|
182
|
+
lastAt: string;
|
|
183
|
+
lastMessage: string;
|
|
184
|
+
}[];
|
|
154
185
|
/**
|
|
155
186
|
* Only with status `conflict`: the single-provider extension point and the other extensions that
|
|
156
187
|
* provide it (they keep this one from loading; disabling them loads it, no restart).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { pluginSchemaName } from "../ctx/create-ctx.js";
|
|
3
3
|
import { isDestructiveSql, splitSqlStatements } from "../db/sql-statements.js";
|
|
4
|
-
import { permissionKeys, WARNINGS_KEY_PREFIX } from "./permission-set.js";
|
|
4
|
+
import { CLIENT_KEY, permissionKeys, UI_COMPONENT_KEY_PREFIX, WARNINGS_KEY_PREFIX, } from "./permission-set.js";
|
|
5
5
|
/**
|
|
6
6
|
* Review digest: the first 16 hex digits of `sha256(permissionKeys(set).join("\n"))`; with migration
|
|
7
7
|
* warnings their digest (kind, file, line and statement) is folded in, without warnings it equals the
|
|
@@ -30,23 +30,28 @@ export const SENSITIVE_EXTENSION_POINTS = [
|
|
|
30
30
|
"sandbox@2",
|
|
31
31
|
"object-store@1",
|
|
32
32
|
];
|
|
33
|
+
/** The one item of the "client" group (client extensions spec §5.4). */
|
|
34
|
+
const CLIENT_ITEM = "client code";
|
|
33
35
|
/** 总是敏感的类别。 */
|
|
34
36
|
const ALWAYS_SENSITIVE = new Set([
|
|
35
37
|
"secrets",
|
|
36
38
|
"publicRoutes",
|
|
37
39
|
"tools",
|
|
38
40
|
"hooks",
|
|
41
|
+
"client",
|
|
39
42
|
]);
|
|
40
43
|
const CATEGORY_REASON = {
|
|
41
44
|
secrets: "secrets",
|
|
42
45
|
publicRoutes: "public_routes",
|
|
43
46
|
tools: "tools",
|
|
44
47
|
hooks: "hooks",
|
|
48
|
+
client: "client_code",
|
|
45
49
|
};
|
|
46
50
|
/**
|
|
47
51
|
* Why an item in a category is sensitive (undefined when it is not; shared by the grouped list, the
|
|
48
|
-
* per-item `added` and the CLI markers): secrets, public routes, tools and
|
|
49
|
-
* sensitive; {@link SENSITIVE_EXTENSION_POINTS} in "provides"; embedded apps (`embed:<id>`)
|
|
52
|
+
* per-item `added` and the CLI markers): secrets, public routes, tools, hooks and client code are
|
|
53
|
+
* always sensitive; {@link SENSITIVE_EXTENSION_POINTS} in "provides"; embedded apps (`embed:<id>`)
|
|
54
|
+
* and client components (`component:<id>`) in "UI".
|
|
50
55
|
*/
|
|
51
56
|
function itemReason(category, item) {
|
|
52
57
|
// storage 不算敏感:ctx.storage 的键由内核加前缀 `plugins/<插件>/<orgId>/`,插件只能读写自己的前缀
|
|
@@ -55,8 +60,13 @@ function itemReason(category, item) {
|
|
|
55
60
|
? item.split("@")[0].replaceAll("-", "_")
|
|
56
61
|
: undefined;
|
|
57
62
|
}
|
|
58
|
-
if (category === "ui")
|
|
59
|
-
|
|
63
|
+
if (category === "ui") {
|
|
64
|
+
if (item.startsWith("embed:"))
|
|
65
|
+
return "ui_embed";
|
|
66
|
+
if (item.startsWith("component:"))
|
|
67
|
+
return "ui_component";
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
60
70
|
return ALWAYS_SENSITIVE.has(category) ? CATEGORY_REASON[category] : undefined;
|
|
61
71
|
}
|
|
62
72
|
const itemOf = (category, value) => {
|
|
@@ -68,6 +78,8 @@ function keyItem(key) {
|
|
|
68
78
|
const after = (prefix) => (key.startsWith(prefix) ? key.slice(prefix.length) : null);
|
|
69
79
|
if (key === "permissions.secrets")
|
|
70
80
|
return { category: "secrets", item: "secret store" };
|
|
81
|
+
if (key === CLIENT_KEY)
|
|
82
|
+
return { category: "client", item: CLIENT_ITEM };
|
|
71
83
|
if (/^config\.(deployment|org)\.secret:/.test(key))
|
|
72
84
|
return { category: "secrets", item: key.slice(key.indexOf(":") + 1) };
|
|
73
85
|
const table = [
|
|
@@ -77,6 +89,7 @@ function keyItem(key) {
|
|
|
77
89
|
["permissions.hooks:", "hooks", ""],
|
|
78
90
|
["permissions.hooks.policy:", "hooks", "policy:"],
|
|
79
91
|
["ui.embed:", "ui", "embed:"],
|
|
92
|
+
[UI_COMPONENT_KEY_PREFIX, "ui", "component:"],
|
|
80
93
|
["ui.view:", "ui", "view:"],
|
|
81
94
|
];
|
|
82
95
|
for (const [prefix, category, itemPrefix] of table) {
|
|
@@ -113,8 +126,9 @@ export function permissionKeyItems(keys) {
|
|
|
113
126
|
/** 把权限集合整理成分组清单(固定顺序,空组省略)。纯函数。 */
|
|
114
127
|
export function permissionGroups(set) {
|
|
115
128
|
const p = set.permissions;
|
|
116
|
-
//
|
|
117
|
-
const ui = p.ui ?? { views: [], embeds: [] };
|
|
129
|
+
// older snapshots lack `ui`, `ui.components` and `client` (read as empty / false)
|
|
130
|
+
const ui = p.ui ?? { views: [], embeds: [], components: [] };
|
|
131
|
+
const components = ui.components ?? [];
|
|
118
132
|
const secretFields = [...set.config.deployment, ...set.config.org]
|
|
119
133
|
.filter((f) => f.secret)
|
|
120
134
|
.map((f) => f.path || "<root>");
|
|
@@ -148,8 +162,13 @@ export function permissionGroups(set) {
|
|
|
148
162
|
{ category: "network", items: p.network },
|
|
149
163
|
{
|
|
150
164
|
category: "ui",
|
|
151
|
-
items: [
|
|
165
|
+
items: [
|
|
166
|
+
...ui.views.map((v) => `view:${v}`),
|
|
167
|
+
...ui.embeds.map((e) => `embed:${e}`),
|
|
168
|
+
...components.map((c) => `component:${c}`),
|
|
169
|
+
],
|
|
152
170
|
},
|
|
171
|
+
{ category: "client", items: p.client === true ? [CLIENT_ITEM] : [] },
|
|
153
172
|
];
|
|
154
173
|
return groups
|
|
155
174
|
.filter((g) => g.items.length > 0)
|
|
@@ -1,92 +1,225 @@
|
|
|
1
|
-
import { type PluginSetupGuide, type
|
|
1
|
+
import { type PluginSetupGuide, type SetupGuideAudience, type SetupGuideBlock, type SetupGuidePlace } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { GuideAckJson } from "../db/kernel-schema.js";
|
|
3
3
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
4
4
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
5
|
+
import type { KernelLogger } from "../log.js";
|
|
5
6
|
import { type OrgRole } from "../orgs/roles.js";
|
|
6
|
-
|
|
7
|
+
import { type GuideTarget, type GuideTargetQuery, type UnresolvedReason } from "./guide-places.js";
|
|
8
|
+
export { PLACE_ACTIONS, type UnresolvedReason } from "./guide-places.js";
|
|
9
|
+
/**
|
|
10
|
+
* The setup guide a target was set up with (`channel_connections.guide_ack`,
|
|
11
|
+
* `connectors.guide_ack`): the guide, the plugin version, the drift hash of the admin audience and
|
|
12
|
+
* the mode (null for a place without modes).
|
|
13
|
+
*/
|
|
7
14
|
export type GuideAck = GuideAckJson;
|
|
8
|
-
/** Where a
|
|
15
|
+
/** Where a page finds its plugin's guide (`GET /orgs/:orgId/catalog/:itemId/guides/:guideId`). */
|
|
9
16
|
export interface SetupGuideRef {
|
|
10
17
|
itemId: string;
|
|
11
18
|
guideId: string;
|
|
12
19
|
/** The item's latest version (the guide's). */
|
|
13
20
|
version: string;
|
|
14
21
|
}
|
|
15
|
-
/** The guide fields of a channel connection page. */
|
|
22
|
+
/** The guide fields of a channel connection or connector page. */
|
|
16
23
|
export interface ConnectionGuideState {
|
|
17
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* The plugin's channel-connection guide (or the connector's template guide) the org may see,
|
|
26
|
+
* null without one.
|
|
27
|
+
*/
|
|
18
28
|
setupGuide: SetupGuideRef | null;
|
|
19
29
|
/**
|
|
20
|
-
* The plugin changed the guide's
|
|
21
|
-
* version acked and the current one; null otherwise (or without an
|
|
30
|
+
* The plugin changed the guide's code blocks or checks for the ack's mode since the connection
|
|
31
|
+
* was set up (its ack): the version acked and the current one; null otherwise (or without an
|
|
32
|
+
* ack).
|
|
22
33
|
*/
|
|
23
34
|
guideDrift: {
|
|
24
35
|
from: string;
|
|
25
36
|
to: string;
|
|
26
37
|
} | null;
|
|
27
38
|
}
|
|
28
|
-
/**
|
|
39
|
+
/** Where a connector template's page finds its guide (`guide` of the template entries). */
|
|
40
|
+
export interface TemplateGuideRef {
|
|
41
|
+
itemId: string;
|
|
42
|
+
guideId: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A template whose guide an update changes (the platform update preview, spec §11): the admin
|
|
46
|
+
* hash differs between the platform's current version and the target. `orgs` counts the orgs with
|
|
47
|
+
* a connector set up with that guide (an ack of it) whose ack the target leaves behind: org admins
|
|
48
|
+
* asked to update their app. Counts only, never org names.
|
|
49
|
+
*/
|
|
50
|
+
export interface TemplateGuideChange {
|
|
51
|
+
templateId: string;
|
|
52
|
+
guideId: string;
|
|
53
|
+
/** The platform's current version (null: never synced). */
|
|
54
|
+
from: string | null;
|
|
55
|
+
to: string;
|
|
56
|
+
orgs: number;
|
|
57
|
+
}
|
|
58
|
+
/** A guide rendered for an org or the platform (spec 2026-11-09-setup-guides-v2-design.md §7.2). */
|
|
29
59
|
export interface RenderedSetupGuide {
|
|
30
60
|
itemId: string;
|
|
31
61
|
plugin: string;
|
|
32
62
|
version: string;
|
|
33
63
|
id: string;
|
|
34
64
|
place: SetupGuidePlace;
|
|
35
|
-
|
|
36
|
-
|
|
65
|
+
/** The connector template a `connector-template` guide is for; null for other places. */
|
|
66
|
+
template: string | null;
|
|
67
|
+
/** The document rendered (zh falls back to en). */
|
|
68
|
+
locale: "en" | "zh";
|
|
69
|
+
title: string;
|
|
70
|
+
/** The modes the document's sections use; empty = no mode switch. */
|
|
37
71
|
modes: string[];
|
|
38
|
-
/** The mode rendered
|
|
72
|
+
/** The mode rendered; null for a place without modes. */
|
|
39
73
|
mode: string | null;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
74
|
+
/** The audiences the document has sections for. */
|
|
75
|
+
audiences: SetupGuideAudience[];
|
|
76
|
+
audience: SetupGuideAudience;
|
|
77
|
+
blocks: SetupGuideBlock[];
|
|
78
|
+
/** The check rows, in document order. */
|
|
79
|
+
checks: {
|
|
80
|
+
id: string;
|
|
81
|
+
label: string;
|
|
82
|
+
source: "plugin" | "kernel";
|
|
83
|
+
}[];
|
|
84
|
+
/** Known variables the rendered text uses without a value (left as written), with why. */
|
|
85
|
+
unresolved: {
|
|
86
|
+
name: string;
|
|
87
|
+
reason: UnresolvedReason;
|
|
46
88
|
}[];
|
|
47
|
-
/**
|
|
48
|
-
unresolved: string[];
|
|
49
|
-
/** The template hash of this mode (what an ack records). */
|
|
89
|
+
/** The drift hash of this mode and audience (what an ack records). */
|
|
50
90
|
hash: string;
|
|
91
|
+
target: GuideTarget | null;
|
|
92
|
+
}
|
|
93
|
+
/** The query of the guide route. */
|
|
94
|
+
export interface GuideQuery extends GuideTargetQuery {
|
|
95
|
+
mode?: string | undefined;
|
|
96
|
+
audience?: SetupGuideAudience | undefined;
|
|
97
|
+
locale?: "en" | "zh" | undefined;
|
|
51
98
|
}
|
|
52
99
|
type VersionFile = {
|
|
53
100
|
path: string;
|
|
54
101
|
size: number;
|
|
55
102
|
sha256: string;
|
|
56
103
|
};
|
|
104
|
+
/** The live items (with their latest version) by name or id. */
|
|
105
|
+
declare function latestItems(db: KernelDb, by: "name" | "id", keys: string[]): Promise<{
|
|
106
|
+
awaiting_confirmation: boolean;
|
|
107
|
+
builtin: boolean;
|
|
108
|
+
disabled: boolean;
|
|
109
|
+
files: {
|
|
110
|
+
path: string;
|
|
111
|
+
size: number;
|
|
112
|
+
sha256: string;
|
|
113
|
+
}[];
|
|
114
|
+
id: string;
|
|
115
|
+
latest_version: string;
|
|
116
|
+
manifest: unknown;
|
|
117
|
+
name: string;
|
|
118
|
+
owner_org_id: string | null;
|
|
119
|
+
scope: "all" | "named" | "optional";
|
|
120
|
+
shareable: boolean;
|
|
121
|
+
shown_to_others: boolean;
|
|
122
|
+
}[]>;
|
|
123
|
+
/** A live item with its latest version, as {@link PluginGuides} reads it. */
|
|
124
|
+
export type ItemRow = Awaited<ReturnType<typeof latestItems>>[number];
|
|
57
125
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* rendered files for the same variables.
|
|
126
|
+
* A connector template's guide in a version's manifest (v2 entries only), for callers that already
|
|
127
|
+
* hold the manifest (the org catalog detail).
|
|
61
128
|
*/
|
|
62
|
-
export declare
|
|
129
|
+
export declare const templateGuideOf: (manifest: unknown, templateId: string) => PluginSetupGuide | undefined;
|
|
63
130
|
/**
|
|
64
|
-
* Plugin setup guides (spec `2026-11-
|
|
65
|
-
* catalog item (the item of the same name, its latest version), renders a guide
|
|
66
|
-
*
|
|
67
|
-
* secret), and computes the
|
|
68
|
-
*
|
|
69
|
-
* Stateless: everything comes from PG (version rows,
|
|
70
|
-
* `catalog_files`).
|
|
131
|
+
* Plugin setup guides v2 (spec `2026-11-09-setup-guides-v2-design.md`): resolves an extension's
|
|
132
|
+
* catalog item (the item of the same name, its latest version), renders a guide document for a
|
|
133
|
+
* place, audience, mode and target with the place's safe variables (`guide-places.ts`; never a
|
|
134
|
+
* secret), and computes the drift hashes targets record to tell when an upgrade changed their
|
|
135
|
+
* setup. Org reads follow the catalog icon's visibility ({@link orgVisibleItems}), the platform
|
|
136
|
+
* preview any live item. Stateless: everything comes from PG (version rows, target rows, the
|
|
137
|
+
* documents in `catalog_files`).
|
|
71
138
|
*/
|
|
72
139
|
export declare class PluginGuides {
|
|
73
140
|
private readonly db;
|
|
74
141
|
private readonly config;
|
|
75
|
-
|
|
76
|
-
|
|
142
|
+
private readonly log;
|
|
143
|
+
constructor(db: KernelDb, config: Pick<KernelConfigService, "get">, log: Pick<KernelLogger, "warn">);
|
|
77
144
|
private latest;
|
|
145
|
+
/** The rows the org's catalog shows ({@link orgVisibleItems}); all of them without an org. */
|
|
146
|
+
private visibleTo;
|
|
147
|
+
/** The English documents of guides, by `<itemId>/<guideId>` (one PG read); absent when not stored. */
|
|
148
|
+
private englishSources;
|
|
149
|
+
/** The admin drift hash of a guide for a mode. */
|
|
150
|
+
private static hashOf;
|
|
78
151
|
/**
|
|
79
|
-
* The ack of a plugin's current
|
|
80
|
-
* when it is created or switches mode
|
|
81
|
-
*
|
|
152
|
+
* The ack of a plugin's current guide for a place (and template) and mode, admin audience:
|
|
153
|
+
* recorded on a target when it is created or switches mode. Null when the plugin has no catalog
|
|
154
|
+
* item, no such guide or no stored document. Without `org` kernel-internal (no org visibility:
|
|
155
|
+
* the target belongs to the plugin either way); with `org` (a person acking the guide) null
|
|
156
|
+
* also when the org's catalog does not show the item, the guide read's rule.
|
|
82
157
|
*/
|
|
83
|
-
currentAck(plugin: string,
|
|
158
|
+
currentAck(plugin: string, place: SetupGuidePlace, o: {
|
|
159
|
+
mode: string | null;
|
|
160
|
+
templateId?: string;
|
|
161
|
+
org?: {
|
|
162
|
+
orgId: string;
|
|
163
|
+
role: OrgRole;
|
|
164
|
+
};
|
|
165
|
+
}): Promise<GuideAck | null>;
|
|
84
166
|
/**
|
|
85
|
-
* Whether an ack is behind the plugin's current guide (
|
|
86
|
-
*
|
|
87
|
-
*
|
|
167
|
+
* Whether an ack is behind the plugin's current guide (spec §11): the same guide whose hash for
|
|
168
|
+
* the ack's mode changed. False without an ack, a catalog item or the guide. Kernel-internal: no
|
|
169
|
+
* org visibility.
|
|
88
170
|
*/
|
|
89
|
-
ackDrifted(plugin: string, ack: GuideAck | null
|
|
171
|
+
ackDrifted(plugin: string, place: SetupGuidePlace, ack: GuideAck | null, o?: {
|
|
172
|
+
templateId?: string;
|
|
173
|
+
}): Promise<boolean>;
|
|
174
|
+
/**
|
|
175
|
+
* The ack of a connector template's current guide (admin audience, no mode): recorded on a
|
|
176
|
+
* connector when it is created from the template. Null when the item has no guide for the
|
|
177
|
+
* template or no stored document. Without `org` kernel-internal (no org visibility: the caller is
|
|
178
|
+
* creating or reading the org's own connector of that template); with `org` (a person acking the
|
|
179
|
+
* guide) null also when the org's catalog does not show the item.
|
|
180
|
+
*/
|
|
181
|
+
templateAck(itemId: string, templateId: string, org?: {
|
|
182
|
+
orgId: string;
|
|
183
|
+
role: OrgRole;
|
|
184
|
+
}): Promise<GuideAck | null>;
|
|
185
|
+
/**
|
|
186
|
+
* `<itemId>/<templateId>` -> the template's guide in the item's latest version, for the template
|
|
187
|
+
* entries of the catalog detail and the components list; templates without one are absent.
|
|
188
|
+
* Kernel-internal: callers list only items and templates the org already sees.
|
|
189
|
+
*/
|
|
190
|
+
templateGuides(pairs: {
|
|
191
|
+
itemId: string;
|
|
192
|
+
templateId: string;
|
|
193
|
+
}[]): Promise<Map<string, TemplateGuideRef>>;
|
|
194
|
+
/**
|
|
195
|
+
* The guide fields of connectors (spec §9.3, §11): a connector made from a template
|
|
196
|
+
* (`itemId` / `templateId` of its platform template) whose item the org's catalog shows gets the
|
|
197
|
+
* template's guide; drift = an ack of the same guide whose admin hash changed. Custom connectors
|
|
198
|
+
* (no template) get nulls. One query each for items and documents.
|
|
199
|
+
*/
|
|
200
|
+
connectorStates(connectors: {
|
|
201
|
+
id: string;
|
|
202
|
+
itemId: string | null;
|
|
203
|
+
templateId: string | null;
|
|
204
|
+
guideAck: GuideAck | null;
|
|
205
|
+
}[], org: {
|
|
206
|
+
orgId: string;
|
|
207
|
+
role: OrgRole;
|
|
208
|
+
}): Promise<Map<string, ConnectionGuideState>>;
|
|
209
|
+
/**
|
|
210
|
+
* The connector template guides an update of an item changes (the platform update preview,
|
|
211
|
+
* {@link TemplateGuideChange}): per template guide of the target whose admin hash differs from
|
|
212
|
+
* the platform's current version's (a guide new in the target counts only when connectors hold
|
|
213
|
+
* an ack of it). Documents a version does not store are skipped. Kernel-internal (the super
|
|
214
|
+
* admin's or the owner's preview).
|
|
215
|
+
*/
|
|
216
|
+
templateGuideChanges(itemId: string, from: string | null, to: {
|
|
217
|
+
version: string;
|
|
218
|
+
manifest: unknown;
|
|
219
|
+
files: VersionFile[];
|
|
220
|
+
}): Promise<TemplateGuideChange[]>;
|
|
221
|
+
/** The admin drift hash of a guide in a version (no mode), or undefined when not stored. */
|
|
222
|
+
private storedHash;
|
|
90
223
|
/**
|
|
91
224
|
* Plugin name -> its channel-connection guide, for plugins whose item the org's catalog shows
|
|
92
225
|
* (`GET channels/schema/:plugin` and the connection pages); others are absent.
|
|
@@ -96,9 +229,9 @@ export declare class PluginGuides {
|
|
|
96
229
|
role: OrgRole;
|
|
97
230
|
}): Promise<Map<string, SetupGuideRef & {
|
|
98
231
|
guide: PluginSetupGuide;
|
|
99
|
-
|
|
232
|
+
row: ItemRow;
|
|
100
233
|
}>>;
|
|
101
|
-
/** The guide fields of channel connections (one query for
|
|
234
|
+
/** The guide fields of channel connections (one query each for guides and documents; spec §11). */
|
|
102
235
|
connectionStates(connections: {
|
|
103
236
|
id: string;
|
|
104
237
|
plugin: string;
|
|
@@ -107,23 +240,43 @@ export declare class PluginGuides {
|
|
|
107
240
|
orgId: string;
|
|
108
241
|
role: OrgRole;
|
|
109
242
|
}): Promise<Map<string, ConnectionGuideState>>;
|
|
243
|
+
/**
|
|
244
|
+
* The documents of a guide of an item's latest version (no visibility check: callers check it):
|
|
245
|
+
* the source in `locale` (falling back to English), the English source (hashes, check lookup) and
|
|
246
|
+
* the locale actually read. 404 `catalog.item_not_found` / `catalog.guide_not_found`.
|
|
247
|
+
*/
|
|
248
|
+
loadDocument(itemId: string, guideId: string, locale: "en" | "zh"): Promise<{
|
|
249
|
+
row: ItemRow;
|
|
250
|
+
guide: PluginSetupGuide;
|
|
251
|
+
source: string;
|
|
252
|
+
enSource: string;
|
|
253
|
+
locale: "en" | "zh";
|
|
254
|
+
}>;
|
|
255
|
+
/** Reads and checks a guide's stored documents (one PG read). */
|
|
256
|
+
private documents;
|
|
257
|
+
/** Logs the stored guide entries a version skips (once per render). */
|
|
258
|
+
private warnSkipped;
|
|
110
259
|
/**
|
|
111
260
|
* Renders a guide for an org (`GET /orgs/:orgId/catalog/:itemId/guides/:guideId`): the item must
|
|
112
261
|
* be one the org's catalog shows (else `catalog.item_not_found`), the guide one its latest
|
|
113
|
-
* version has (else `catalog.guide_not_found`), and
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
262
|
+
* version has (else `catalog.guide_not_found`), `mode` and `audience` the place's (else
|
|
263
|
+
* `validation_failed`), and the caller must hold the place and audience's action
|
|
264
|
+
* ({@link PLACE_ACTIONS}, else `forbidden`). The target parameters are the place's
|
|
265
|
+
* (`guide-places.ts`): a target must be a live object of the org on the plugin, else 404.
|
|
117
266
|
*/
|
|
118
267
|
render(org: {
|
|
119
268
|
orgId: string;
|
|
120
269
|
role: OrgRole;
|
|
121
|
-
}, itemId: string, guideId: string, q:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
270
|
+
}, itemId: string, guideId: string, q: GuideQuery): Promise<RenderedSetupGuide>;
|
|
271
|
+
/**
|
|
272
|
+
* Renders a guide for the platform (the super admin's preview, `GET /admin/catalog/:itemId/guides/
|
|
273
|
+
* :guideId`): any live item, the admin audience, no org (`orgName` unresolved) and no target.
|
|
274
|
+
*/
|
|
275
|
+
renderForPlatform(itemId: string, guideId: string, q: {
|
|
276
|
+
locale?: "en" | "zh" | undefined;
|
|
125
277
|
}): Promise<RenderedSetupGuide>;
|
|
126
|
-
/**
|
|
127
|
-
private
|
|
278
|
+
/** The audience asked for, after checking `mode` and `audience` against the place. */
|
|
279
|
+
private checkQuery;
|
|
280
|
+
/** The rendered guide: the mode picked, the document rendered, the hash and the reasons. */
|
|
281
|
+
private renderDoc;
|
|
128
282
|
}
|
|
129
|
-
export {};
|