@jini-ai/admin 0.1.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/LICENSE +201 -0
- package/README.md +138 -0
- package/dist/browser/index.d.ts +8 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +8 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/navigation.d.ts +39 -0
- package/dist/browser/navigation.d.ts.map +1 -0
- package/dist/browser/navigation.js +113 -0
- package/dist/browser/navigation.js.map +1 -0
- package/dist/core/gated/types.d.ts +87 -0
- package/dist/core/gated/types.d.ts.map +1 -0
- package/dist/core/gated/types.js +37 -0
- package/dist/core/gated/types.js.map +1 -0
- package/dist/core/index.d.ts +20 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +15 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/manifest/rules.d.ts +84 -0
- package/dist/core/manifest/rules.d.ts.map +1 -0
- package/dist/core/manifest/rules.js +130 -0
- package/dist/core/manifest/rules.js.map +1 -0
- package/dist/core/manifest/types.d.ts +154 -0
- package/dist/core/manifest/types.d.ts.map +1 -0
- package/dist/core/manifest/types.js +31 -0
- package/dist/core/manifest/types.js.map +1 -0
- package/dist/core/permissions/rules.d.ts +32 -0
- package/dist/core/permissions/rules.d.ts.map +1 -0
- package/dist/core/permissions/rules.js +34 -0
- package/dist/core/permissions/rules.js.map +1 -0
- package/dist/core/ports/analytics.d.ts +35 -0
- package/dist/core/ports/analytics.d.ts.map +1 -0
- package/dist/core/ports/analytics.js +14 -0
- package/dist/core/ports/analytics.js.map +1 -0
- package/dist/core/ports/auth.d.ts +49 -0
- package/dist/core/ports/auth.d.ts.map +1 -0
- package/dist/core/ports/auth.js +19 -0
- package/dist/core/ports/auth.js.map +1 -0
- package/dist/core/ports/comments.d.ts +90 -0
- package/dist/core/ports/comments.d.ts.map +1 -0
- package/dist/core/ports/comments.js +24 -0
- package/dist/core/ports/comments.js.map +1 -0
- package/dist/core/ports/database.d.ts +101 -0
- package/dist/core/ports/database.d.ts.map +1 -0
- package/dist/core/ports/database.js +2 -0
- package/dist/core/ports/database.js.map +1 -0
- package/dist/core/ports/forms.d.ts +130 -0
- package/dist/core/ports/forms.d.ts.map +1 -0
- package/dist/core/ports/forms.js +62 -0
- package/dist/core/ports/forms.js.map +1 -0
- package/dist/core/ports/identity.d.ts +114 -0
- package/dist/core/ports/identity.d.ts.map +1 -0
- package/dist/core/ports/identity.js +28 -0
- package/dist/core/ports/identity.js.map +1 -0
- package/dist/core/ports/index.d.ts +21 -0
- package/dist/core/ports/index.d.ts.map +1 -0
- package/dist/core/ports/index.js +6 -0
- package/dist/core/ports/index.js.map +1 -0
- package/dist/core/ports/integrations.d.ts +81 -0
- package/dist/core/ports/integrations.d.ts.map +1 -0
- package/dist/core/ports/integrations.js +21 -0
- package/dist/core/ports/integrations.js.map +1 -0
- package/dist/core/ports/media.d.ts +74 -0
- package/dist/core/ports/media.d.ts.map +1 -0
- package/dist/core/ports/media.js +28 -0
- package/dist/core/ports/media.js.map +1 -0
- package/dist/core/ports/members.d.ts +50 -0
- package/dist/core/ports/members.d.ts.map +1 -0
- package/dist/core/ports/members.js +17 -0
- package/dist/core/ports/members.js.map +1 -0
- package/dist/core/ports/menus.d.ts +179 -0
- package/dist/core/ports/menus.d.ts.map +1 -0
- package/dist/core/ports/menus.js +70 -0
- package/dist/core/ports/menus.js.map +1 -0
- package/dist/core/ports/plugins.d.ts +68 -0
- package/dist/core/ports/plugins.d.ts.map +1 -0
- package/dist/core/ports/plugins.js +27 -0
- package/dist/core/ports/plugins.js.map +1 -0
- package/dist/core/ports/recovery.d.ts +171 -0
- package/dist/core/ports/recovery.d.ts.map +1 -0
- package/dist/core/ports/recovery.js +2 -0
- package/dist/core/ports/recovery.js.map +1 -0
- package/dist/core/ports/redirects.d.ts +139 -0
- package/dist/core/ports/redirects.d.ts.map +1 -0
- package/dist/core/ports/redirects.js +55 -0
- package/dist/core/ports/redirects.js.map +1 -0
- package/dist/core/ports/seo.d.ts +147 -0
- package/dist/core/ports/seo.d.ts.map +1 -0
- package/dist/core/ports/seo.js +39 -0
- package/dist/core/ports/seo.js.map +1 -0
- package/dist/core/ports/settings.d.ts +87 -0
- package/dist/core/ports/settings.d.ts.map +1 -0
- package/dist/core/ports/settings.js +23 -0
- package/dist/core/ports/settings.js.map +1 -0
- package/dist/core/ports/workspace.d.ts +44 -0
- package/dist/core/ports/workspace.d.ts.map +1 -0
- package/dist/core/ports/workspace.js +29 -0
- package/dist/core/ports/workspace.js.map +1 -0
- package/dist/core/routing/rules.d.ts +54 -0
- package/dist/core/routing/rules.d.ts.map +1 -0
- package/dist/core/routing/rules.js +113 -0
- package/dist/core/routing/rules.js.map +1 -0
- package/dist/core/routing/types.d.ts +36 -0
- package/dist/core/routing/types.d.ts.map +1 -0
- package/dist/core/routing/types.js +17 -0
- package/dist/core/routing/types.js.map +1 -0
- package/dist/core/transport/errors.d.ts +53 -0
- package/dist/core/transport/errors.d.ts.map +1 -0
- package/dist/core/transport/errors.js +63 -0
- package/dist/core/transport/errors.js.map +1 -0
- package/dist/core/transport/http.d.ts +63 -0
- package/dist/core/transport/http.d.ts.map +1 -0
- package/dist/core/transport/http.js +63 -0
- package/dist/core/transport/http.js.map +1 -0
- package/dist/core/transport/index.d.ts +5 -0
- package/dist/core/transport/index.d.ts.map +1 -0
- package/dist/core/transport/index.js +3 -0
- package/dist/core/transport/index.js.map +1 -0
- package/dist/core/transport/types.d.ts +81 -0
- package/dist/core/transport/types.d.ts.map +1 -0
- package/dist/core/transport/types.js +43 -0
- package/dist/core/transport/types.js.map +1 -0
- package/dist/react/components/ConfirmButton/ConfirmButton.d.ts +40 -0
- package/dist/react/components/ConfirmButton/ConfirmButton.d.ts.map +1 -0
- package/dist/react/components/ConfirmButton/ConfirmButton.hooks.d.ts +22 -0
- package/dist/react/components/ConfirmButton/ConfirmButton.hooks.d.ts.map +1 -0
- package/dist/react/components/ConfirmButton/ConfirmButton.hooks.js +59 -0
- package/dist/react/components/ConfirmButton/ConfirmButton.hooks.js.map +1 -0
- package/dist/react/components/ConfirmButton/ConfirmButton.js +40 -0
- package/dist/react/components/ConfirmButton/ConfirmButton.js.map +1 -0
- package/dist/react/components/ConfirmButton.d.ts +39 -0
- package/dist/react/components/ConfirmButton.d.ts.map +1 -0
- package/dist/react/components/ConfirmButton.js +79 -0
- package/dist/react/components/ConfirmButton.js.map +1 -0
- package/dist/react/components/ConfirmDialog/ConfirmDialog.d.ts +90 -0
- package/dist/react/components/ConfirmDialog/ConfirmDialog.d.ts.map +1 -0
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.d.ts +49 -0
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.d.ts.map +1 -0
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.js +81 -0
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.js.map +1 -0
- package/dist/react/components/ConfirmDialog/ConfirmDialog.js +92 -0
- package/dist/react/components/ConfirmDialog/ConfirmDialog.js.map +1 -0
- package/dist/react/components/ConfirmDialog.d.ts +57 -0
- package/dist/react/components/ConfirmDialog.d.ts.map +1 -0
- package/dist/react/components/ConfirmDialog.js +85 -0
- package/dist/react/components/ConfirmDialog.js.map +1 -0
- package/dist/react/components/DataTable.d.ts +97 -0
- package/dist/react/components/DataTable.d.ts.map +1 -0
- package/dist/react/components/DataTable.js +20 -0
- package/dist/react/components/DataTable.js.map +1 -0
- package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.d.ts +25 -0
- package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.d.ts.map +1 -0
- package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.js +148 -0
- package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.js.map +1 -0
- package/dist/react/components/RowMenu/RowMenu.d.ts +77 -0
- package/dist/react/components/RowMenu/RowMenu.d.ts.map +1 -0
- package/dist/react/components/RowMenu/RowMenu.hooks.d.ts +30 -0
- package/dist/react/components/RowMenu/RowMenu.hooks.d.ts.map +1 -0
- package/dist/react/components/RowMenu/RowMenu.hooks.js +154 -0
- package/dist/react/components/RowMenu/RowMenu.hooks.js.map +1 -0
- package/dist/react/components/RowMenu/RowMenu.js +46 -0
- package/dist/react/components/RowMenu/RowMenu.js.map +1 -0
- package/dist/react/components/RowMenu.d.ts +44 -0
- package/dist/react/components/RowMenu.d.ts.map +1 -0
- package/dist/react/components/RowMenu.js +149 -0
- package/dist/react/components/RowMenu.js.map +1 -0
- package/dist/react/components/Sidebar.d.ts +128 -0
- package/dist/react/components/Sidebar.d.ts.map +1 -0
- package/dist/react/components/Sidebar.js +168 -0
- package/dist/react/components/Sidebar.js.map +1 -0
- package/dist/react/hooks/use-nav-sections.d.ts +50 -0
- package/dist/react/hooks/use-nav-sections.d.ts.map +1 -0
- package/dist/react/hooks/use-nav-sections.js +112 -0
- package/dist/react/hooks/use-nav-sections.js.map +1 -0
- package/dist/react/hooks/use-sidebar-rail.d.ts +39 -0
- package/dist/react/hooks/use-sidebar-rail.d.ts.map +1 -0
- package/dist/react/hooks/use-sidebar-rail.js +93 -0
- package/dist/react/hooks/use-sidebar-rail.js.map +1 -0
- package/dist/react/index.d.ts +53 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +53 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/types.d.ts +28 -0
- package/dist/react/types.d.ts.map +1 -0
- package/dist/react/types.js +23 -0
- package/dist/react/types.js.map +1 -0
- package/dist/server/composio/bounded-data.d.ts +36 -0
- package/dist/server/composio/bounded-data.d.ts.map +1 -0
- package/dist/server/composio/bounded-data.js +181 -0
- package/dist/server/composio/bounded-data.js.map +1 -0
- package/dist/server/composio/catalog.d.ts +147 -0
- package/dist/server/composio/catalog.d.ts.map +1 -0
- package/dist/server/composio/catalog.js +126 -0
- package/dist/server/composio/catalog.js.map +1 -0
- package/dist/server/composio/composio-config.d.ts +42 -0
- package/dist/server/composio/composio-config.d.ts.map +1 -0
- package/dist/server/composio/composio-config.js +145 -0
- package/dist/server/composio/composio-config.js.map +1 -0
- package/dist/server/composio/composio-descriptions.d.ts +16 -0
- package/dist/server/composio/composio-descriptions.d.ts.map +1 -0
- package/dist/server/composio/composio-descriptions.js +766 -0
- package/dist/server/composio/composio-descriptions.js.map +1 -0
- package/dist/server/composio/composio.d.ts +364 -0
- package/dist/server/composio/composio.d.ts.map +1 -0
- package/dist/server/composio/composio.js +1902 -0
- package/dist/server/composio/composio.js.map +1 -0
- package/dist/server/composio/errors.d.ts +15 -0
- package/dist/server/composio/errors.d.ts.map +1 -0
- package/dist/server/composio/errors.js +16 -0
- package/dist/server/composio/errors.js.map +1 -0
- package/dist/server/composio/file-lock.d.ts +13 -0
- package/dist/server/composio/file-lock.d.ts.map +1 -0
- package/dist/server/composio/file-lock.js +92 -0
- package/dist/server/composio/file-lock.js.map +1 -0
- package/dist/server/composio/index.d.ts +18 -0
- package/dist/server/composio/index.d.ts.map +1 -0
- package/dist/server/composio/index.js +7 -0
- package/dist/server/composio/index.js.map +1 -0
- package/dist/server/composio/json-schema.d.ts +93 -0
- package/dist/server/composio/json-schema.d.ts.map +1 -0
- package/dist/server/composio/json-schema.js +498 -0
- package/dist/server/composio/json-schema.js.map +1 -0
- package/dist/server/composio/output-protection.d.ts +32 -0
- package/dist/server/composio/output-protection.d.ts.map +1 -0
- package/dist/server/composio/output-protection.js +105 -0
- package/dist/server/composio/output-protection.js.map +1 -0
- package/dist/server/composio/service.d.ts +181 -0
- package/dist/server/composio/service.d.ts.map +1 -0
- package/dist/server/composio/service.js +758 -0
- package/dist/server/composio/service.js.map +1 -0
- package/dist/server/index.d.ts +15 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +15 -0
- package/dist/server/index.js.map +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminMenusPort` — editable navigation trees (menus), and their assignment to named theme
|
|
3
|
+
* locations.
|
|
4
|
+
*
|
|
5
|
+
* ## `AdminMenuTarget` and the content-model leak
|
|
6
|
+
*
|
|
7
|
+
* The reference implementation's own link-target union ties two of its four kinds directly to its
|
|
8
|
+
* content model: `entryRef` points at a content-item id, `termRef` points at a taxonomy-term id plus its
|
|
9
|
+
* taxonomy name. Baking exactly those four kinds into this port as a CLOSED union would mean any
|
|
10
|
+
* host without an "entry"/"taxonomy term" concept (or with additional native reference kinds of
|
|
11
|
+
* its own — a product, a collection, ...) could never express its own link targets through this
|
|
12
|
+
* contract at all; it would have to fake them as `url` targets, defeating the point of a typed
|
|
13
|
+
* target. `AdminMenuTarget` therefore keeps the 4 reference-implementation kinds as their own typed
|
|
14
|
+
* variants (they are reasonably generic CMS concepts, not literally "post"/"page") but adds
|
|
15
|
+
* `AdminMenuCustomTarget` as a fifth, open-`kind` escape hatch for anything else a host needs.
|
|
16
|
+
*
|
|
17
|
+
* This has a real, deliberate type-safety cost worth knowing before you rely on it: because
|
|
18
|
+
* `AdminMenuCustomTarget.kind` is `string & {}`, it structurally overlaps every literal kind below
|
|
19
|
+
* it — narrowing `AdminMenuTarget` by `target.kind === "entryRef"` does not, on its own, fully
|
|
20
|
+
* exclude `AdminMenuCustomTarget` from the narrowed type the way it would exclude
|
|
21
|
+
* `AdminMenuTermTarget`/`AdminMenuUrlTarget`/`AdminMenuRouteTarget`. In practice this only shows up
|
|
22
|
+
* as a compile error if you then try to access a known-target-only field (e.g. `.entryId`) without
|
|
23
|
+
* having ruled out the custom-target case first, since `AdminMenuCustomTarget` carries no such
|
|
24
|
+
* field — TypeScript will refuse the access rather than silently mistype it. A `switch` over all 5
|
|
25
|
+
* `kind` values (with `AdminMenuCustomTarget` as the `default` arm) is the straightforward way to
|
|
26
|
+
* stay correct.
|
|
27
|
+
*
|
|
28
|
+
* **This is a deliberately deferred decision, not a settled one — record it as such.** `entryRef`/
|
|
29
|
+
* `termRef` encode a host content model (an "entry" and a "taxonomy term" are the reference
|
|
30
|
+
* implementation's own vocabulary for its content system, not a vendor-neutral abstraction this
|
|
31
|
+
* package has designed). A host with no entries or taxonomies should emit only `url`/`route`
|
|
32
|
+
* targets and ignore those two variants entirely; it is not expected to reinterpret them. This was
|
|
33
|
+
* deliberately NOT generalized into a single vendor-neutral "content reference" shape, because
|
|
34
|
+
* there is exactly one host implementation in this corpus today — designing a generic content-reference abstraction from a single
|
|
35
|
+
* example is how a package acquires the wrong abstraction permanently, not how it becomes generic.
|
|
36
|
+
* Widening `kind` (this file) costs nothing and preserves the option without committing to a shape.
|
|
37
|
+
* **Trigger for revisiting:** a second host needs a link-target kind that is neither `entryRef`,
|
|
38
|
+
* `termRef`, `url`, nor `route` — at that point there are two real examples to generalize from
|
|
39
|
+
* instead of one, and a genuine vendor-neutral reference shape (rather than another bespoke
|
|
40
|
+
* `kind`-per-host member) becomes the right call.
|
|
41
|
+
*
|
|
42
|
+
* ## `deleteMenu` is a two-rung ladder driven by call count, not by `force` alone
|
|
43
|
+
*
|
|
44
|
+
* A single method models a two-step deletion flow: the FIRST call against a non-trashed menu
|
|
45
|
+
* always soft-deletes it (status flips, resolves `{ purged: false }`) regardless of `force` or
|
|
46
|
+
* location bindings. Only a SECOND call — against a menu that is already trashed — attempts the
|
|
47
|
+
* real purge, which is rejected (conflict-class, with the still-bound location keys) if the menu is
|
|
48
|
+
* still assigned to any theme location, unless `options.force` is set. This is a third distinct
|
|
49
|
+
* deletion shape in this port set, different from both `AdminIntegrationsPort`'s permanent-soft-
|
|
50
|
+
* delete-only and `AdminMediaPort`'s two-separate-methods (`trashMedia`/`deleteMedia`) split: here
|
|
51
|
+
* it is the SAME method, and which rung it hits depends on the menu's current state, not on an
|
|
52
|
+
* argument you pass. A panel must track (or re-fetch) whether a menu is already trashed to know
|
|
53
|
+
* what calling `deleteMenu` again will actually do.
|
|
54
|
+
*
|
|
55
|
+
* ## `assignMenuLocation` can displace another menu (last-writer-wins)
|
|
56
|
+
*
|
|
57
|
+
* Assigning a menu to a location key that is already bound to a *different* menu unassigns that
|
|
58
|
+
* other menu from the location (dropping the key from its own `locations`) rather than rejecting
|
|
59
|
+
* the call. `AdminAssignMenuLocationResult.displacedMenu` carries that menu's post-displacement
|
|
60
|
+
* state so a panel can show what changed; it is `null` when the location was unbound or already
|
|
61
|
+
* held by the menu being assigned.
|
|
62
|
+
*
|
|
63
|
+
* ## Open vs. closed unions
|
|
64
|
+
*
|
|
65
|
+
* `NavTargetKind` and `MenuStatus` are reference-implementation-specific vocabularies (the former further discussed
|
|
66
|
+
* above) — OPEN, via the `T | (string & {})` idiom `seo.ts`'s file header introduces for this port
|
|
67
|
+
* set.
|
|
68
|
+
*/
|
|
69
|
+
/** The known target-kind discriminants — open, see file header. */
|
|
70
|
+
export type NavTargetKind = "entryRef" | "termRef" | "url" | "route" | (string & {});
|
|
71
|
+
/** A link to another content item, by opaque id. */
|
|
72
|
+
export interface AdminMenuEntryTarget {
|
|
73
|
+
readonly kind: "entryRef";
|
|
74
|
+
readonly entryId: string;
|
|
75
|
+
}
|
|
76
|
+
/** A link to a taxonomy term (category/tag archive, or a host's closest equivalent grouping). */
|
|
77
|
+
export interface AdminMenuTermTarget {
|
|
78
|
+
readonly kind: "termRef";
|
|
79
|
+
readonly termId: string;
|
|
80
|
+
/** Which taxonomy `termId` belongs to. */
|
|
81
|
+
readonly taxonomy: string;
|
|
82
|
+
}
|
|
83
|
+
/** An absolute or site-relative external URL. */
|
|
84
|
+
export interface AdminMenuUrlTarget {
|
|
85
|
+
readonly kind: "url";
|
|
86
|
+
readonly href: string;
|
|
87
|
+
}
|
|
88
|
+
/** A named host route (e.g. `home`, `search`). */
|
|
89
|
+
export interface AdminMenuRouteTarget {
|
|
90
|
+
readonly kind: "route";
|
|
91
|
+
readonly route: string;
|
|
92
|
+
readonly params?: Record<string, unknown>;
|
|
93
|
+
}
|
|
94
|
+
/** A host-specific target kind beyond the 4 above — see file header for the narrowing tradeoff
|
|
95
|
+
* this member introduces. `data` is opaque to this port; the host defines its own shape. */
|
|
96
|
+
export interface AdminMenuCustomTarget {
|
|
97
|
+
readonly kind: string & {};
|
|
98
|
+
readonly data?: Record<string, unknown>;
|
|
99
|
+
}
|
|
100
|
+
export type AdminMenuTarget = AdminMenuEntryTarget | AdminMenuTermTarget | AdminMenuUrlTarget | AdminMenuRouteTarget | AdminMenuCustomTarget;
|
|
101
|
+
export interface AdminMenuItemAttrs {
|
|
102
|
+
readonly openInNewTab?: boolean;
|
|
103
|
+
/** `rel` tokens (e.g. `nofollow`). */
|
|
104
|
+
readonly rel?: string;
|
|
105
|
+
readonly cssClass?: string;
|
|
106
|
+
readonly description?: string;
|
|
107
|
+
readonly icon?: string;
|
|
108
|
+
}
|
|
109
|
+
/** One node in a menu's item tree. Ordering is array order among siblings; nesting is `children`.
|
|
110
|
+
* `id` must be stable across edits (see `updateMenuTree`) — never regenerate it on reorder. */
|
|
111
|
+
export interface AdminMenuItem {
|
|
112
|
+
readonly id: string;
|
|
113
|
+
/** As stored — this port does not fall back to the target's own title when absent. (The
|
|
114
|
+
* reference implementation only applies that fallback in its separate, unexposed render-time
|
|
115
|
+
* resolution model; the raw item this port returns leaves an absent label absent.) A panel
|
|
116
|
+
* wanting a display fallback must compute one itself. */
|
|
117
|
+
readonly label?: string;
|
|
118
|
+
readonly target: AdminMenuTarget;
|
|
119
|
+
readonly attrs?: AdminMenuItemAttrs;
|
|
120
|
+
readonly children?: readonly AdminMenuItem[];
|
|
121
|
+
}
|
|
122
|
+
/** Lifecycle of a menu itself — open, see file header. */
|
|
123
|
+
export type MenuStatus = "draft" | "published" | "trash" | (string & {});
|
|
124
|
+
export interface AdminMenu {
|
|
125
|
+
readonly id: string;
|
|
126
|
+
readonly slug: string;
|
|
127
|
+
readonly title: string;
|
|
128
|
+
readonly status: MenuStatus;
|
|
129
|
+
readonly items: readonly AdminMenuItem[];
|
|
130
|
+
/** Theme-location keys this menu currently fills. */
|
|
131
|
+
readonly locations: readonly string[];
|
|
132
|
+
readonly updatedAt: string;
|
|
133
|
+
readonly version: number;
|
|
134
|
+
}
|
|
135
|
+
export interface AdminMenuCreateInput {
|
|
136
|
+
readonly title: string;
|
|
137
|
+
readonly slug: string;
|
|
138
|
+
/** Defaults to an empty menu when omitted. Every item needs a stable `id`. */
|
|
139
|
+
readonly items?: readonly AdminMenuItem[];
|
|
140
|
+
}
|
|
141
|
+
export interface AdminMenuUpdateTreeInput {
|
|
142
|
+
/** The `version` this edit was based on — optimistic-concurrency guard; expect a conflict-class
|
|
143
|
+
* rejection if the menu was modified since. */
|
|
144
|
+
readonly expectedVersion: number;
|
|
145
|
+
readonly title?: string;
|
|
146
|
+
readonly slug?: string;
|
|
147
|
+
/** The FULL replacement tree — this is a whole-tree replace, not a per-item patch. */
|
|
148
|
+
readonly items: readonly AdminMenuItem[];
|
|
149
|
+
}
|
|
150
|
+
export interface AdminMenuBinding {
|
|
151
|
+
readonly locationKey: string;
|
|
152
|
+
readonly menuId: string;
|
|
153
|
+
readonly boundAt: string;
|
|
154
|
+
}
|
|
155
|
+
export interface AdminAssignMenuLocationResult {
|
|
156
|
+
readonly menu: AdminMenu;
|
|
157
|
+
readonly binding: AdminMenuBinding;
|
|
158
|
+
/** See file header (last-writer-wins displacement). */
|
|
159
|
+
readonly displacedMenu: AdminMenu | null;
|
|
160
|
+
}
|
|
161
|
+
export interface AdminDeleteMenuResult {
|
|
162
|
+
/** Null only after a successful purge (the second-rung outcome) — see file header. */
|
|
163
|
+
readonly menu: AdminMenu | null;
|
|
164
|
+
readonly purged: boolean;
|
|
165
|
+
}
|
|
166
|
+
export interface AdminMenusPort {
|
|
167
|
+
listMenus(): Promise<readonly AdminMenu[]>;
|
|
168
|
+
getMenu(id: string): Promise<AdminMenu>;
|
|
169
|
+
createMenu(input: AdminMenuCreateInput): Promise<AdminMenu>;
|
|
170
|
+
updateMenuTree(id: string, input: AdminMenuUpdateTreeInput): Promise<AdminMenu>;
|
|
171
|
+
/** Two-rung ladder driven by the menu's current trashed state, not solely by `options.force` —
|
|
172
|
+
* see file header before assuming what a given call will do. */
|
|
173
|
+
deleteMenu(id: string, options?: {
|
|
174
|
+
force?: boolean;
|
|
175
|
+
}): Promise<AdminDeleteMenuResult>;
|
|
176
|
+
/** May displace another menu already bound to `locationKey` — see file header. */
|
|
177
|
+
assignMenuLocation(id: string, locationKey: string): Promise<AdminAssignMenuLocationResult>;
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=menus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menus.d.ts","sourceRoot":"","sources":["../../../src/core/ports/menus.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminMenusPort` — editable navigation trees (menus), and their assignment to named theme
|
|
3
|
+
* locations.
|
|
4
|
+
*
|
|
5
|
+
* ## `AdminMenuTarget` and the content-model leak
|
|
6
|
+
*
|
|
7
|
+
* The reference implementation's own link-target union ties two of its four kinds directly to its
|
|
8
|
+
* content model: `entryRef` points at a content-item id, `termRef` points at a taxonomy-term id plus its
|
|
9
|
+
* taxonomy name. Baking exactly those four kinds into this port as a CLOSED union would mean any
|
|
10
|
+
* host without an "entry"/"taxonomy term" concept (or with additional native reference kinds of
|
|
11
|
+
* its own — a product, a collection, ...) could never express its own link targets through this
|
|
12
|
+
* contract at all; it would have to fake them as `url` targets, defeating the point of a typed
|
|
13
|
+
* target. `AdminMenuTarget` therefore keeps the 4 reference-implementation kinds as their own typed
|
|
14
|
+
* variants (they are reasonably generic CMS concepts, not literally "post"/"page") but adds
|
|
15
|
+
* `AdminMenuCustomTarget` as a fifth, open-`kind` escape hatch for anything else a host needs.
|
|
16
|
+
*
|
|
17
|
+
* This has a real, deliberate type-safety cost worth knowing before you rely on it: because
|
|
18
|
+
* `AdminMenuCustomTarget.kind` is `string & {}`, it structurally overlaps every literal kind below
|
|
19
|
+
* it — narrowing `AdminMenuTarget` by `target.kind === "entryRef"` does not, on its own, fully
|
|
20
|
+
* exclude `AdminMenuCustomTarget` from the narrowed type the way it would exclude
|
|
21
|
+
* `AdminMenuTermTarget`/`AdminMenuUrlTarget`/`AdminMenuRouteTarget`. In practice this only shows up
|
|
22
|
+
* as a compile error if you then try to access a known-target-only field (e.g. `.entryId`) without
|
|
23
|
+
* having ruled out the custom-target case first, since `AdminMenuCustomTarget` carries no such
|
|
24
|
+
* field — TypeScript will refuse the access rather than silently mistype it. A `switch` over all 5
|
|
25
|
+
* `kind` values (with `AdminMenuCustomTarget` as the `default` arm) is the straightforward way to
|
|
26
|
+
* stay correct.
|
|
27
|
+
*
|
|
28
|
+
* **This is a deliberately deferred decision, not a settled one — record it as such.** `entryRef`/
|
|
29
|
+
* `termRef` encode a host content model (an "entry" and a "taxonomy term" are the reference
|
|
30
|
+
* implementation's own vocabulary for its content system, not a vendor-neutral abstraction this
|
|
31
|
+
* package has designed). A host with no entries or taxonomies should emit only `url`/`route`
|
|
32
|
+
* targets and ignore those two variants entirely; it is not expected to reinterpret them. This was
|
|
33
|
+
* deliberately NOT generalized into a single vendor-neutral "content reference" shape, because
|
|
34
|
+
* there is exactly one host implementation in this corpus today — designing a generic content-reference abstraction from a single
|
|
35
|
+
* example is how a package acquires the wrong abstraction permanently, not how it becomes generic.
|
|
36
|
+
* Widening `kind` (this file) costs nothing and preserves the option without committing to a shape.
|
|
37
|
+
* **Trigger for revisiting:** a second host needs a link-target kind that is neither `entryRef`,
|
|
38
|
+
* `termRef`, `url`, nor `route` — at that point there are two real examples to generalize from
|
|
39
|
+
* instead of one, and a genuine vendor-neutral reference shape (rather than another bespoke
|
|
40
|
+
* `kind`-per-host member) becomes the right call.
|
|
41
|
+
*
|
|
42
|
+
* ## `deleteMenu` is a two-rung ladder driven by call count, not by `force` alone
|
|
43
|
+
*
|
|
44
|
+
* A single method models a two-step deletion flow: the FIRST call against a non-trashed menu
|
|
45
|
+
* always soft-deletes it (status flips, resolves `{ purged: false }`) regardless of `force` or
|
|
46
|
+
* location bindings. Only a SECOND call — against a menu that is already trashed — attempts the
|
|
47
|
+
* real purge, which is rejected (conflict-class, with the still-bound location keys) if the menu is
|
|
48
|
+
* still assigned to any theme location, unless `options.force` is set. This is a third distinct
|
|
49
|
+
* deletion shape in this port set, different from both `AdminIntegrationsPort`'s permanent-soft-
|
|
50
|
+
* delete-only and `AdminMediaPort`'s two-separate-methods (`trashMedia`/`deleteMedia`) split: here
|
|
51
|
+
* it is the SAME method, and which rung it hits depends on the menu's current state, not on an
|
|
52
|
+
* argument you pass. A panel must track (or re-fetch) whether a menu is already trashed to know
|
|
53
|
+
* what calling `deleteMenu` again will actually do.
|
|
54
|
+
*
|
|
55
|
+
* ## `assignMenuLocation` can displace another menu (last-writer-wins)
|
|
56
|
+
*
|
|
57
|
+
* Assigning a menu to a location key that is already bound to a *different* menu unassigns that
|
|
58
|
+
* other menu from the location (dropping the key from its own `locations`) rather than rejecting
|
|
59
|
+
* the call. `AdminAssignMenuLocationResult.displacedMenu` carries that menu's post-displacement
|
|
60
|
+
* state so a panel can show what changed; it is `null` when the location was unbound or already
|
|
61
|
+
* held by the menu being assigned.
|
|
62
|
+
*
|
|
63
|
+
* ## Open vs. closed unions
|
|
64
|
+
*
|
|
65
|
+
* `NavTargetKind` and `MenuStatus` are reference-implementation-specific vocabularies (the former further discussed
|
|
66
|
+
* above) — OPEN, via the `T | (string & {})` idiom `seo.ts`'s file header introduces for this port
|
|
67
|
+
* set.
|
|
68
|
+
*/
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=menus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menus.js","sourceRoot":"","sources":["../../../src/core/ports/menus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminExtensionsPort` — installed extensions (the reference implementation calls them
|
|
3
|
+
* "plugins") and their
|
|
4
|
+
* enabled/disabled state.
|
|
5
|
+
*
|
|
6
|
+
* ## Why the port is "Extensions" but the DTO and methods say "Plugin"
|
|
7
|
+
*
|
|
8
|
+
* The port interface is named generically because not every host will call this mechanism
|
|
9
|
+
* "plugins" — some call the same concept "add-ons" or "extensions" in their own product copy.
|
|
10
|
+
* The DTO (`AdminPlugin`) and method names (`listPlugins`/`setPluginEnabled`) keep the reference
|
|
11
|
+
* implementation's own vocabulary rather than being renamed to match, because "plugin" is itself
|
|
12
|
+
* already a generic, industry-standard term (not product-specific jargon the way `workspace-local`
|
|
13
|
+
* or `/api/admin/v1` are) and because the calling code this port replaces already uses these exact
|
|
14
|
+
* names — renaming
|
|
15
|
+
* both the port and its members would be a bigger, noisier diff for no behavioral gain. A host
|
|
16
|
+
* that genuinely prefers "extension" throughout is free to alias at the call site.
|
|
17
|
+
*
|
|
18
|
+
* ## `changeSetId` is an opaque audit handle, not a claim every host has "change sets"
|
|
19
|
+
*
|
|
20
|
+
* `setExtensionEnabled`'s response carries a `changeSetId` because the reference implementation
|
|
21
|
+
* versions every mutating admin action through a change-set/revert subsystem. This port keeps the
|
|
22
|
+
* field — real callers may want to link to an audit trail — but treats it as an opaque string a
|
|
23
|
+
* host can use however its own audit mechanism works, not a promise that "change set" is a concept
|
|
24
|
+
* every implementation shares.
|
|
25
|
+
*/
|
|
26
|
+
/** An installed extension and its trust/validity state. */
|
|
27
|
+
export interface AdminPlugin {
|
|
28
|
+
readonly id: string;
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly version: string;
|
|
31
|
+
/** `"built-in"` ships with the host itself; `"site"` was installed for this particular
|
|
32
|
+
* installation/tenant. */
|
|
33
|
+
readonly source: "built-in" | "site";
|
|
34
|
+
/** Trust tier driving how much a panel should warn before enabling this extension — lower
|
|
35
|
+
* numbers are more trusted. Not ordered numerically on the wire on purpose (host-defined
|
|
36
|
+
* labels), see `../transport/errors.ts`'s header for the same "vocabulary belongs to the host"
|
|
37
|
+
* reasoning applied to error codes. */
|
|
38
|
+
readonly tier: "tier-1" | "tier-2" | "tier-3";
|
|
39
|
+
/** Whether the extension currently passes the host's own structural/compatibility checks —
|
|
40
|
+
* independent of `enabled`. An extension can be `"valid"` and still `enabled: false`, or
|
|
41
|
+
* `"invalid"`/`"incompatible"` while still `enabled: true` from a prior state the host has not
|
|
42
|
+
* auto-disabled. */
|
|
43
|
+
readonly status: "valid" | "invalid" | "incompatible";
|
|
44
|
+
readonly enabled: boolean;
|
|
45
|
+
readonly errors: readonly {
|
|
46
|
+
code: string;
|
|
47
|
+
file: string | null;
|
|
48
|
+
message: string;
|
|
49
|
+
}[];
|
|
50
|
+
}
|
|
51
|
+
/** `setExtensionEnabled`'s response — a trimmed extension summary plus an audit handle, not the
|
|
52
|
+
* full `AdminPlugin`. See the file header on `changeSetId`. */
|
|
53
|
+
export interface AdminExtensionEnabledResult {
|
|
54
|
+
readonly extension: {
|
|
55
|
+
id: string;
|
|
56
|
+
version: string;
|
|
57
|
+
enabled: boolean;
|
|
58
|
+
updatedAt: string;
|
|
59
|
+
};
|
|
60
|
+
readonly changeSetId: string;
|
|
61
|
+
}
|
|
62
|
+
export interface AdminExtensionsPort {
|
|
63
|
+
listPlugins(): Promise<readonly AdminPlugin[]>;
|
|
64
|
+
setPluginEnabled(id: string, input: {
|
|
65
|
+
enabled: boolean;
|
|
66
|
+
}): Promise<AdminExtensionEnabledResult>;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=plugins.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../../../src/core/ports/plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;+BAC2B;IAC3B,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC;IACrC;;;4CAGwC;IACxC,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC9C;;;yBAGqB;IACrB,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,cAAc,CAAC;IACtD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACpF;AAED;gEACgE;AAChE,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,SAAS,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACzF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,IAAI,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC,CAAC;IAC/C,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;CACjG"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminExtensionsPort` — installed extensions (the reference implementation calls them
|
|
3
|
+
* "plugins") and their
|
|
4
|
+
* enabled/disabled state.
|
|
5
|
+
*
|
|
6
|
+
* ## Why the port is "Extensions" but the DTO and methods say "Plugin"
|
|
7
|
+
*
|
|
8
|
+
* The port interface is named generically because not every host will call this mechanism
|
|
9
|
+
* "plugins" — some call the same concept "add-ons" or "extensions" in their own product copy.
|
|
10
|
+
* The DTO (`AdminPlugin`) and method names (`listPlugins`/`setPluginEnabled`) keep the reference
|
|
11
|
+
* implementation's own vocabulary rather than being renamed to match, because "plugin" is itself
|
|
12
|
+
* already a generic, industry-standard term (not product-specific jargon the way `workspace-local`
|
|
13
|
+
* or `/api/admin/v1` are) and because the calling code this port replaces already uses these exact
|
|
14
|
+
* names — renaming
|
|
15
|
+
* both the port and its members would be a bigger, noisier diff for no behavioral gain. A host
|
|
16
|
+
* that genuinely prefers "extension" throughout is free to alias at the call site.
|
|
17
|
+
*
|
|
18
|
+
* ## `changeSetId` is an opaque audit handle, not a claim every host has "change sets"
|
|
19
|
+
*
|
|
20
|
+
* `setExtensionEnabled`'s response carries a `changeSetId` because the reference implementation
|
|
21
|
+
* versions every mutating admin action through a change-set/revert subsystem. This port keeps the
|
|
22
|
+
* field — real callers may want to link to an audit trail — but treats it as an opaque string a
|
|
23
|
+
* host can use however its own audit mechanism works, not a promise that "change set" is a concept
|
|
24
|
+
* every implementation shares.
|
|
25
|
+
*/
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=plugins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugins.js","sourceRoot":"","sources":["../../../src/core/ports/plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminRecoveryPort` — point-in-time restore: what can be restored to, what would be lost,
|
|
3
|
+
* and the restore ceremony itself.
|
|
4
|
+
*
|
|
5
|
+
* `AdminRestorePoint` is imported from `./database.js` rather than redeclared — see that file's
|
|
6
|
+
* header for why Database and Recovery are two views onto one persisted set.
|
|
7
|
+
*
|
|
8
|
+
* ## The restore triad IS a `GatedOperation` — with operation-specific confirm/execute input
|
|
9
|
+
*
|
|
10
|
+
* `./database.js`'s `migrateForward` is the simple case: none of its three calls needs input
|
|
11
|
+
* beyond the token the previous step produced. Restore needs two more fields —
|
|
12
|
+
*
|
|
13
|
+
* - `confirm` also carries `disclosureAcknowledged`: the operator must affirmatively accept the
|
|
14
|
+
* data-loss disclosure `computeRecoveryDisclosure` computed, not merely echo back a token; and
|
|
15
|
+
* - `execute` also carries `restorePointId`: belt-and-suspenders against a confirmation token
|
|
16
|
+
* being replayed against a different target than the one it was confirmed for.
|
|
17
|
+
*
|
|
18
|
+
* `GatedOperation` carries `TConfirmInput`/`TExecuteInput` type parameters (defaulting to `string`)
|
|
19
|
+
* for exactly this, so restore is declared as a real `GatedOperation` rather than three loose
|
|
20
|
+
* methods. See `../gated/types.js` for the full rationale — in short, `disclosureAcknowledged`
|
|
21
|
+
* shows the protocol is *recorded informed consent* rather than a two-step token handshake, and
|
|
22
|
+
* restore is the most destructive operation a host exposes, so it is the last thing that should sit
|
|
23
|
+
* outside the abstraction built for that ceremony.
|
|
24
|
+
*
|
|
25
|
+
* A first draft of this file argued the opposite, against an earlier `confirm(token: string)`
|
|
26
|
+
* signature, and predicted the widening that has since happened. Kept as a note because the
|
|
27
|
+
* inference was right: if a fourth ceremony needs something this shape still cannot express, that
|
|
28
|
+
* is a signal about `GatedOperation`, not a reason for the caller to opt out of it.
|
|
29
|
+
*
|
|
30
|
+
* ## The deep-link envelope carries display continuity only, never authority
|
|
31
|
+
*
|
|
32
|
+
* `DatabaseContextEnvelope` (used by `resolveRecoveryDeepLink`) is how a panel preserves "which
|
|
33
|
+
* restore point was I just looking at" across a navigation — e.g. a link from the database timeline
|
|
34
|
+
* into recovery. A host must always re-verify `restorePointId` server-side rather than trusting the
|
|
35
|
+
* envelope's contents as authorization; the envelope existing does not mean the operation it
|
|
36
|
+
* references is still valid or still permitted for the caller presenting it.
|
|
37
|
+
*/
|
|
38
|
+
import type { AdminRestorePoint, RestorePointCostClass } from './database.js';
|
|
39
|
+
import type { GatedOperation } from '../gated/types.js';
|
|
40
|
+
/** A count that may be unknown rather than a real number — used where computing an exact count
|
|
41
|
+
* would be prohibitively expensive for at least one category. */
|
|
42
|
+
export type CategoryCount = number | "unknown";
|
|
43
|
+
/**
|
|
44
|
+
* What would be lost or affected by restoring to a given point, broken down by category. `partial`
|
|
45
|
+
* is always `true`: this is a best-effort disclosure, not a guarantee that no other consequence
|
|
46
|
+
* exists — see `watermarkBaselineAvailable` for one specific case where the disclosure itself is
|
|
47
|
+
* degraded.
|
|
48
|
+
*/
|
|
49
|
+
export interface AdminDisclosureResult {
|
|
50
|
+
readonly partial: true;
|
|
51
|
+
/** `false` means the disclosure could not establish a watermark baseline to diff against — the
|
|
52
|
+
* `counts` below may be less trustworthy than usual, and a panel should say so rather than
|
|
53
|
+
* present them with the same confidence as a normal disclosure. */
|
|
54
|
+
readonly watermarkBaselineAvailable: boolean;
|
|
55
|
+
readonly counts: Readonly<Record<string, CategoryCount>>;
|
|
56
|
+
}
|
|
57
|
+
/** Reasons the recovery feature itself may be degraded right now, independent of any one restore
|
|
58
|
+
* point — surfaced by `getRecoveryStatus` so a panel can explain *why* recovery is unavailable
|
|
59
|
+
* rather than just failing silently. */
|
|
60
|
+
export type DegradedBannerKind = "migration-interrupted" | "pending-migration" | "operation-in-flight" | "cost-unavailable" | "watermark-baseline-unavailable";
|
|
61
|
+
export type DegradedBannerActionKind = "deep-link-to-database-migration" | "unblock-interrupted-migration" | "none";
|
|
62
|
+
export interface AdminDegradedBanner {
|
|
63
|
+
readonly kind: DegradedBannerKind;
|
|
64
|
+
/** Pre-composed, host-owned copy — render as-is rather than deriving text from `kind`, for the
|
|
65
|
+
* same reason `GatedPlanResult.warnings` is rendered as-is (see `../gated/types.ts`). */
|
|
66
|
+
readonly accessibleText: string;
|
|
67
|
+
readonly actionKind: DegradedBannerActionKind;
|
|
68
|
+
}
|
|
69
|
+
export interface AdminRecoveryStatus {
|
|
70
|
+
readonly costClass: RestorePointCostClass;
|
|
71
|
+
readonly banner: AdminDegradedBanner | null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* An opaque envelope a panel round-trips through a deep link to preserve display continuity across
|
|
75
|
+
* navigation. Treat every field as informational — see the file header on why `restorePointId`
|
|
76
|
+
* here is never authoritative.
|
|
77
|
+
*/
|
|
78
|
+
export interface DatabaseContextEnvelope {
|
|
79
|
+
readonly v: number;
|
|
80
|
+
readonly correlationId: string;
|
|
81
|
+
readonly siteId: string;
|
|
82
|
+
readonly ledgerEventId: string | null;
|
|
83
|
+
readonly restorePointId: string | null;
|
|
84
|
+
/** Host-defined description of how far the envelope's view of the world has drifted from
|
|
85
|
+
* current reality by the time it is resolved (e.g. `"none"`, `"stale"`). */
|
|
86
|
+
readonly drift: string;
|
|
87
|
+
/** What the envelope was created to let the operator do (e.g. `"view"`, `"restore"`). */
|
|
88
|
+
readonly intent: string;
|
|
89
|
+
readonly issuedAt: string;
|
|
90
|
+
}
|
|
91
|
+
export interface AdminRecoveryDeepLinkResult {
|
|
92
|
+
readonly found: boolean;
|
|
93
|
+
readonly restorePoint: {
|
|
94
|
+
restorePointId: string;
|
|
95
|
+
capturedAt: string;
|
|
96
|
+
} | null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* What `restore.confirm` takes. The `token` comes from `restore.plan`'s `GatedPlanResult`.
|
|
100
|
+
*
|
|
101
|
+
* ## Adapter obligation: `token` is opaque, and a host may have to compose it
|
|
102
|
+
*
|
|
103
|
+
* Deliberately one opaque `token`, not the reference host's raw field pair. Its wire protocol
|
|
104
|
+
* confirms a restore with `{planId, planHash}` — two fields — and its plan step returns them
|
|
105
|
+
* separately. Exposing that pair here would contradict `GatedPlanResult.token`'s own contract
|
|
106
|
+
* ("opaque; panels must not parse or construct these"): a panel assembling `planId` + `planHash`
|
|
107
|
+
* into a confirm call *is* reconstructing the token, which is the thing that contract forbids.
|
|
108
|
+
*
|
|
109
|
+
* So the composition is the **route-factory adapter's** job, not the panel's: encode the host's
|
|
110
|
+
* plan identity into one string on the way out of `plan`, decode it on the way into `confirm`. A
|
|
111
|
+
* host whose protocol already uses a single token does nothing. This is the same division of labour
|
|
112
|
+
* as `settings.ts`'s `value`, where the adapter owns any JSON encoding the wire format needs.
|
|
113
|
+
*
|
|
114
|
+
* Whoever writes the reference implementation's adapter: this is a real responsibility with no compiler support. Getting it
|
|
115
|
+
* wrong surfaces as a confirm that is rejected server-side for a plan the operator can see on
|
|
116
|
+
* screen — so encode both halves, and fail loudly on a token you cannot decode rather than sending
|
|
117
|
+
* a partial one.
|
|
118
|
+
*/
|
|
119
|
+
export interface RestoreConfirmInput {
|
|
120
|
+
readonly token: string;
|
|
121
|
+
/**
|
|
122
|
+
* Must be `true` — the operator has seen `computeRecoveryDisclosure`'s result and accepted it.
|
|
123
|
+
*
|
|
124
|
+
* A host must **reject** the confirm outright when this is `false`, not merely warn. This field
|
|
125
|
+
* is the recorded-consent half of the ceremony; treating it as advisory turns the whole gated
|
|
126
|
+
* protocol back into a confirmation dialog.
|
|
127
|
+
*/
|
|
128
|
+
readonly disclosureAcknowledged: boolean;
|
|
129
|
+
}
|
|
130
|
+
/** What `restore.execute` takes. `confirmToken` comes from `restore.confirm`'s result. */
|
|
131
|
+
export interface RestoreExecuteInput {
|
|
132
|
+
readonly confirmToken: string;
|
|
133
|
+
/** Re-stated so the server can verify the token was confirmed for *this* target. A host must
|
|
134
|
+
* re-verify server-side rather than trusting the caller-supplied id. */
|
|
135
|
+
readonly restorePointId: string;
|
|
136
|
+
}
|
|
137
|
+
/** `restore.execute`'s terminal result. */
|
|
138
|
+
export interface RestoreExecuteResult {
|
|
139
|
+
readonly restoreRunId: string;
|
|
140
|
+
/** Host-defined run state (e.g. `"completed"`, `"failed"`). */
|
|
141
|
+
readonly state: string;
|
|
142
|
+
readonly databaseTimelineDeepLink?: {
|
|
143
|
+
v: 1;
|
|
144
|
+
siteId: string;
|
|
145
|
+
intent: "view";
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* `true` when the restore physically swapped the underlying database and the server process
|
|
149
|
+
* needs an operator-triggered restart to pick it up. Until restarted, the running process may
|
|
150
|
+
* keep serving pre-restore data from an already-open file handle — a panel should surface this
|
|
151
|
+
* as a required follow-up action, not a background detail.
|
|
152
|
+
*/
|
|
153
|
+
readonly restartRequired?: boolean;
|
|
154
|
+
}
|
|
155
|
+
export interface AdminRecoveryPort {
|
|
156
|
+
/** Same underlying set as `AdminDatabasePort.listDatabaseRestorePoints` — see the file header. */
|
|
157
|
+
listRecoveryRestorePoints(): Promise<readonly AdminRestorePoint[]>;
|
|
158
|
+
computeRecoveryDisclosure(restorePointId: string): Promise<AdminDisclosureResult>;
|
|
159
|
+
/** Resolves a deep-link envelope back into a restore point reference. `found: false` means the
|
|
160
|
+
* envelope no longer resolves to anything (e.g. the restore point it named was since purged) —
|
|
161
|
+
* not an error, a valid negative result a panel should render distinctly from a thrown error. */
|
|
162
|
+
resolveRecoveryDeepLink(envelope: DatabaseContextEnvelope): Promise<AdminRecoveryDeepLinkResult>;
|
|
163
|
+
getRecoveryStatus(): Promise<AdminRecoveryStatus>;
|
|
164
|
+
/**
|
|
165
|
+
* The restore ceremony: `restore.plan(restorePointId)` -> `restore.confirm(...)` ->
|
|
166
|
+
* `restore.execute(...)`. A real `GatedOperation`, so a shared confirmation component can drive
|
|
167
|
+
* it without knowing it is a restore — see the file header.
|
|
168
|
+
*/
|
|
169
|
+
readonly restore: GatedOperation<string, unknown, RestoreExecuteResult, RestoreConfirmInput, RestoreExecuteInput>;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../../src/core/ports/recovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../../src/core/ports/recovery.ts"],"names":[],"mappings":""}
|