@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,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminAnalyticsPort` — a rolling window of recent traffic hits, for a live-ish activity
|
|
3
|
+
* view rather than aggregate reporting.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately the thinnest port in the set (one method, no mutation, no pagination cursor). The
|
|
6
|
+
* reference implementation's route returns a bounded recent-hits list, not a queryable analytics store — there is no
|
|
7
|
+
* date-range filter, no aggregation, and no historical query on the contract because the reference
|
|
8
|
+
* implementation has none of those either. A host that needs real analytics reporting (rollups,
|
|
9
|
+
* date ranges, funnels, ...) needs a different, dedicated port; stretching this one to cover that
|
|
10
|
+
* by adding options over time would blur "recent activity feed" into "analytics platform", which
|
|
11
|
+
* is a materially different feature with different performance and retention expectations.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../../src/core/ports/analytics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminAuthPort` — the operator session lifecycle: sign in, sign out, and "who am I".
|
|
3
|
+
*
|
|
4
|
+
* Deliberately the smallest port in the set (3 methods) and deliberately separate from
|
|
5
|
+
* `AdminIdentityPort`. The reference implementation's own API client keeps two different "user" shapes for exactly this
|
|
6
|
+
* split: a minimal `{ id, username }` returned by `login`/`me`, and a much richer
|
|
7
|
+
* `AdminIdentityUser` (principal id, status, role/policy ids, ...) returned by the identity CRUD
|
|
8
|
+
* routes. That is not an oversight to unify — a session only ever needs to answer "who is this and
|
|
9
|
+
* what can they see", while identity administration needs the full principal record. Collapsing
|
|
10
|
+
* them would force every `me()` caller to depend on identity-administration fields it doesn't use.
|
|
11
|
+
*
|
|
12
|
+
* ## No error-code enum
|
|
13
|
+
*
|
|
14
|
+
* Expect a rejection from `login` for invalid credentials, and treat any other 4xx as an opaque
|
|
15
|
+
* authentication failure — the exact `code` vocabulary belongs to the host. See
|
|
16
|
+
* `../transport/errors.ts`'s header for why a shared code->message table is a regression.
|
|
17
|
+
*/
|
|
18
|
+
/** The minimal principal shape a session carries — see the file header for why this is not
|
|
19
|
+
* `AdminIdentityUser`. */
|
|
20
|
+
export interface AdminAuthUser {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly username: string;
|
|
23
|
+
}
|
|
24
|
+
export interface AdminAuthPort {
|
|
25
|
+
/** Expect an authentication-class rejection (invalid credentials, disabled account) rather than
|
|
26
|
+
* a specific code — see the file header. */
|
|
27
|
+
login(input: {
|
|
28
|
+
username: string;
|
|
29
|
+
password: string;
|
|
30
|
+
}): Promise<{
|
|
31
|
+
user: AdminAuthUser;
|
|
32
|
+
}>;
|
|
33
|
+
logout(): Promise<{
|
|
34
|
+
ok: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* The current session's principal, plus (when the host computes it) the permission strings
|
|
38
|
+
* effective for that principal right now. `effectivePermissions` is an affordance list for
|
|
39
|
+
* driving UI visibility — see `../permissions/rules.ts`'s `hasPermission`, which is intentionally
|
|
40
|
+
* not an authorization boundary. A host that has not computed this yet may omit the field
|
|
41
|
+
* entirely rather than send an empty array; callers should not treat "absent" and "empty" as the
|
|
42
|
+
* same thing.
|
|
43
|
+
*/
|
|
44
|
+
me(): Promise<{
|
|
45
|
+
user: AdminAuthUser;
|
|
46
|
+
effectivePermissions?: readonly string[];
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/core/ports/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;2BAC2B;AAC3B,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B;iDAC6C;IAC7C,KAAK,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;IACvF,MAAM,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACnC;;;;;;;OAOG;IACH,EAAE,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAClF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminAuthPort` — the operator session lifecycle: sign in, sign out, and "who am I".
|
|
3
|
+
*
|
|
4
|
+
* Deliberately the smallest port in the set (3 methods) and deliberately separate from
|
|
5
|
+
* `AdminIdentityPort`. The reference implementation's own API client keeps two different "user" shapes for exactly this
|
|
6
|
+
* split: a minimal `{ id, username }` returned by `login`/`me`, and a much richer
|
|
7
|
+
* `AdminIdentityUser` (principal id, status, role/policy ids, ...) returned by the identity CRUD
|
|
8
|
+
* routes. That is not an oversight to unify — a session only ever needs to answer "who is this and
|
|
9
|
+
* what can they see", while identity administration needs the full principal record. Collapsing
|
|
10
|
+
* them would force every `me()` caller to depend on identity-administration fields it doesn't use.
|
|
11
|
+
*
|
|
12
|
+
* ## No error-code enum
|
|
13
|
+
*
|
|
14
|
+
* Expect a rejection from `login` for invalid credentials, and treat any other 4xx as an opaque
|
|
15
|
+
* authentication failure — the exact `code` vocabulary belongs to the host. See
|
|
16
|
+
* `../transport/errors.ts`'s header for why a shared code->message table is a regression.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/core/ports/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminCommentsPort` — the moderation queue for visitor-submitted comments, plus the
|
|
3
|
+
* site-wide comments configuration.
|
|
4
|
+
*
|
|
5
|
+
* ## `moderateComment`/`purgeComment` resolve to `void`
|
|
6
|
+
*
|
|
7
|
+
* The reference implementation's moderation routes answer `204 No Content` on success — there is no updated
|
|
8
|
+
* comment in the response body to return. A panel must re-fetch (or optimistically patch its own
|
|
9
|
+
* local copy of) `listCommentsQueue` to see the new state; do not assume this port hands back the
|
|
10
|
+
* post-moderation row. On a version conflict, expect a conflict-class rejection whose error body
|
|
11
|
+
* carries `currentVersion` (see `../transport/errors.ts`'s "`body` is kept raw" decision) so a
|
|
12
|
+
* panel can refresh and retry without a second round-trip just to learn the current value.
|
|
13
|
+
*
|
|
14
|
+
* ## `purgeComment` has no `expectedVersion` and a different permission than moderation actions
|
|
15
|
+
*
|
|
16
|
+
* The four moderation actions (`approve`/`spam`/`trash`/`restore`, expressed here as
|
|
17
|
+
* `CommentModerationAction`) are all optimistic-concurrency-guarded and share one moderation
|
|
18
|
+
* permission. `purge` is a separate, narrower-permissioned, ungated hard delete — mirroring the
|
|
19
|
+
* same "ordinary action vs `.force`-suffixed destructive action" split documented on
|
|
20
|
+
* `AdminMediaPort.deleteMedia`. Do not fold `purge` into `CommentModerationAction`; it is
|
|
21
|
+
* deliberately not one of the four.
|
|
22
|
+
*/
|
|
23
|
+
export type CommentStatus = "pending" | "approved" | "spam" | "trash";
|
|
24
|
+
/** A comment as it appears in the moderation queue. */
|
|
25
|
+
export interface AdminComment {
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly entryId: string;
|
|
28
|
+
/** Null for a top-level comment. */
|
|
29
|
+
readonly parentId: string | null;
|
|
30
|
+
/** The top-level ancestor of this comment's thread — same value as `id` for a top-level
|
|
31
|
+
* comment itself. */
|
|
32
|
+
readonly threadRootId: string;
|
|
33
|
+
readonly depth: number;
|
|
34
|
+
readonly status: CommentStatus;
|
|
35
|
+
/** Set when the author was a signed-in member; null for an anonymous/guest comment. */
|
|
36
|
+
readonly authorPrincipalId: string | null;
|
|
37
|
+
readonly authorName: string;
|
|
38
|
+
readonly authorEmail: string | null;
|
|
39
|
+
readonly authorUrl: string | null;
|
|
40
|
+
/** A hash, never the raw IP — this port never carries an operator-identifiable raw address. */
|
|
41
|
+
readonly authorIpHash: string | null;
|
|
42
|
+
readonly bodyText: string;
|
|
43
|
+
/** Null when no spam classifier ran (e.g. moderation was manual-only for this comment). */
|
|
44
|
+
readonly spamScore: number | null;
|
|
45
|
+
readonly spamProvider: string | null;
|
|
46
|
+
readonly createdAt: string;
|
|
47
|
+
readonly updatedAt: string;
|
|
48
|
+
/** Optimistic-concurrency version — required as `expectedVersion` on every moderation action;
|
|
49
|
+
* not required by `purgeComment` (see the file header). */
|
|
50
|
+
readonly version: number;
|
|
51
|
+
}
|
|
52
|
+
/** A keyset-paginated page of the moderation queue. */
|
|
53
|
+
export interface AdminCommentsQueuePage {
|
|
54
|
+
readonly items: readonly AdminComment[];
|
|
55
|
+
readonly nextCursor: string | null;
|
|
56
|
+
}
|
|
57
|
+
export interface CommentsSettings {
|
|
58
|
+
readonly enabled: boolean;
|
|
59
|
+
readonly requireModeration: boolean;
|
|
60
|
+
readonly maxDepth: number;
|
|
61
|
+
/** `null` means comments never auto-close by age. A host's own "never" sentinel, if it has one
|
|
62
|
+
* internally, should never be sent to or expected from this port directly — always `null`. */
|
|
63
|
+
readonly closeAfterDays: number | null;
|
|
64
|
+
readonly spamAutoRejectScore: number;
|
|
65
|
+
readonly maxPerIpPerHour: number;
|
|
66
|
+
}
|
|
67
|
+
/** The four real per-comment moderation actions — see the file header for why `purge` is not a
|
|
68
|
+
* fifth member of this union. */
|
|
69
|
+
export type CommentModerationAction = "approve" | "spam" | "trash" | "restore";
|
|
70
|
+
export interface AdminCommentsPort {
|
|
71
|
+
listCommentsQueue(options?: {
|
|
72
|
+
status?: CommentStatus;
|
|
73
|
+
cursor?: string;
|
|
74
|
+
limit?: number;
|
|
75
|
+
}): Promise<AdminCommentsQueuePage>;
|
|
76
|
+
/** See the file header: resolves to `void` even on success. */
|
|
77
|
+
moderateComment(commentId: string, input: {
|
|
78
|
+
action: CommentModerationAction;
|
|
79
|
+
expectedVersion: number;
|
|
80
|
+
}, options?: {
|
|
81
|
+
note?: string;
|
|
82
|
+
}): Promise<void>;
|
|
83
|
+
/** Hard delete, no version guard, separate permission — see the file header. */
|
|
84
|
+
purgeComment(commentId: string, options?: {
|
|
85
|
+
note?: string;
|
|
86
|
+
}): Promise<void>;
|
|
87
|
+
getCommentsSettings(): Promise<CommentsSettings>;
|
|
88
|
+
putCommentsSettings(patch?: Partial<CommentsSettings>): Promise<CommentsSettings>;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=comments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../../src/core/ports/comments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminCommentsPort` — the moderation queue for visitor-submitted comments, plus the
|
|
3
|
+
* site-wide comments configuration.
|
|
4
|
+
*
|
|
5
|
+
* ## `moderateComment`/`purgeComment` resolve to `void`
|
|
6
|
+
*
|
|
7
|
+
* The reference implementation's moderation routes answer `204 No Content` on success — there is no updated
|
|
8
|
+
* comment in the response body to return. A panel must re-fetch (or optimistically patch its own
|
|
9
|
+
* local copy of) `listCommentsQueue` to see the new state; do not assume this port hands back the
|
|
10
|
+
* post-moderation row. On a version conflict, expect a conflict-class rejection whose error body
|
|
11
|
+
* carries `currentVersion` (see `../transport/errors.ts`'s "`body` is kept raw" decision) so a
|
|
12
|
+
* panel can refresh and retry without a second round-trip just to learn the current value.
|
|
13
|
+
*
|
|
14
|
+
* ## `purgeComment` has no `expectedVersion` and a different permission than moderation actions
|
|
15
|
+
*
|
|
16
|
+
* The four moderation actions (`approve`/`spam`/`trash`/`restore`, expressed here as
|
|
17
|
+
* `CommentModerationAction`) are all optimistic-concurrency-guarded and share one moderation
|
|
18
|
+
* permission. `purge` is a separate, narrower-permissioned, ungated hard delete — mirroring the
|
|
19
|
+
* same "ordinary action vs `.force`-suffixed destructive action" split documented on
|
|
20
|
+
* `AdminMediaPort.deleteMedia`. Do not fold `purge` into `CommentModerationAction`; it is
|
|
21
|
+
* deliberately not one of the four.
|
|
22
|
+
*/
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comments.js","sourceRoot":"","sources":["../../../src/core/ports/comments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminDatabasePort` — the append-only operations ledger, on-demand restore points, and the
|
|
3
|
+
* forward-migration ceremony.
|
|
4
|
+
*
|
|
5
|
+
* ## Restore points are shared with `AdminRecoveryPort`; the ledger and migration are not
|
|
6
|
+
*
|
|
7
|
+
* `AdminRestorePoint` is the same entity `AdminRecoveryPort.listRecoveryRestorePoints` lists —
|
|
8
|
+
* Database and Recovery are two different *views* onto one persisted set (Database lets an
|
|
9
|
+
* operator create one on demand and see it in the timeline; Recovery lets them restore to one).
|
|
10
|
+
* This port owns the type since `createDatabaseRestorePoint` is where a restore point actually
|
|
11
|
+
* comes into being; `recovery.ts` imports it rather than redeclaring it.
|
|
12
|
+
*
|
|
13
|
+
* ## `migrateForward` satisfies `GatedOperation`; see `recovery.ts` for the triad that doesn't
|
|
14
|
+
*
|
|
15
|
+
* The reference implementation's `planMigrateForward` takes no input and `executeMigrateForward` takes only the
|
|
16
|
+
* confirmation token — no operation-specific extra fields on any of the three calls. That means
|
|
17
|
+
* this triad is a clean, direct instance of `../gated/types.js`'s `GatedOperation`, unlike
|
|
18
|
+
* Recovery's restore ceremony (see that file's header for why restore is different). A
|
|
19
|
+
* compile-time assertion of that fact lives in `../__tests__/database-gated.test.ts`.
|
|
20
|
+
*/
|
|
21
|
+
import type { GatedConfirmResult, GatedOperation, GatedPlanResult } from '../gated/types.js';
|
|
22
|
+
/** One row in the operations ledger — an append-only record of what happened and when, not an
|
|
23
|
+
* editable entity. */
|
|
24
|
+
export interface AdminLedgerRow {
|
|
25
|
+
readonly id: string;
|
|
26
|
+
/** Host-defined event kind (e.g. `"migration"`, `"restore"`, `"restore-point-created"`) — not
|
|
27
|
+
* enumerated here; see `../transport/errors.ts`'s header for the same reasoning applied to error
|
|
28
|
+
* codes instead of ledger kinds. */
|
|
29
|
+
readonly kind: string;
|
|
30
|
+
readonly createdAt: string;
|
|
31
|
+
/** The restore point this event produced or consumed, if any. */
|
|
32
|
+
readonly restorePointId: string | null;
|
|
33
|
+
/** Host-defined outcome string (e.g. `"success"`, `"failed"`, `"interrupted"`). */
|
|
34
|
+
readonly outcome: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Whether capturing or restoring to a given point is cheap, expensive, or currently impossible.
|
|
38
|
+
* A host surfaces this so a panel can warn an operator before they trigger a costly capture
|
|
39
|
+
* rather than after.
|
|
40
|
+
*/
|
|
41
|
+
export type RestorePointCostClass = "cheap" | "expensive" | "unavailable";
|
|
42
|
+
/** A persisted restore point — the entity `AdminDatabasePort` and `AdminRecoveryPort` share. */
|
|
43
|
+
export interface AdminRestorePoint {
|
|
44
|
+
readonly id: string;
|
|
45
|
+
/** What caused this point to be captured (e.g. `"manual"`, `"pre-migration"`) — host-defined. */
|
|
46
|
+
readonly trigger: string;
|
|
47
|
+
readonly costClass: string;
|
|
48
|
+
readonly kind: string;
|
|
49
|
+
/** Null when the host has no watermark concept or none was available at capture time. */
|
|
50
|
+
readonly watermarkAtCapture: number | null;
|
|
51
|
+
readonly createdAt: string;
|
|
52
|
+
}
|
|
53
|
+
/** The trimmed shape `createDatabaseRestorePoint` returns — a summary, not the full row, since the
|
|
54
|
+
* caller just created it and mainly needs to know it succeeded and how expensive it was. */
|
|
55
|
+
export interface AdminRestorePointSummary {
|
|
56
|
+
readonly id: string;
|
|
57
|
+
readonly costClass: RestorePointCostClass;
|
|
58
|
+
readonly kind: string;
|
|
59
|
+
}
|
|
60
|
+
/** `migrateForward`'s terminal success shape. Literal `true` rather than `boolean`: there is no
|
|
61
|
+
* "migrated: false" response — a non-migration outcome is a rejection, not a `false` payload. */
|
|
62
|
+
export interface MigrateForwardResult {
|
|
63
|
+
readonly migrated: true;
|
|
64
|
+
}
|
|
65
|
+
export interface AdminDatabasePort {
|
|
66
|
+
/**
|
|
67
|
+
* A page of the operations ledger, newest-relevant-first, optionally filtered by `kind` and
|
|
68
|
+
* `outcome` and bounded by `fromDate`/`toDate`. Keyset-paginated via `cursor`/`nextCursor`, not
|
|
69
|
+
* offset-paginated — do not compute a page number from `limit`.
|
|
70
|
+
*/
|
|
71
|
+
getDatabaseTimeline(options?: {
|
|
72
|
+
kind?: string;
|
|
73
|
+
outcome?: string;
|
|
74
|
+
fromDate?: string;
|
|
75
|
+
toDate?: string;
|
|
76
|
+
cursor?: string;
|
|
77
|
+
limit?: number;
|
|
78
|
+
}): Promise<{
|
|
79
|
+
items: readonly AdminLedgerRow[];
|
|
80
|
+
nextCursor: string | null;
|
|
81
|
+
}>;
|
|
82
|
+
listDatabaseRestorePoints(): Promise<readonly AdminRestorePoint[]>;
|
|
83
|
+
/**
|
|
84
|
+
* Captures a new restore point on demand. `costAck` should be `true` once the operator has
|
|
85
|
+
* acknowledged a shown cost warning for a `RestorePointCostClass` of `"expensive"` — a host may
|
|
86
|
+
* reject the call without it rather than silently proceeding.
|
|
87
|
+
*/
|
|
88
|
+
createDatabaseRestorePoint(options?: {
|
|
89
|
+
trigger?: string;
|
|
90
|
+
costAck?: boolean;
|
|
91
|
+
}): Promise<AdminRestorePointSummary>;
|
|
92
|
+
/**
|
|
93
|
+
* The forward-migration ceremony (schema/data migration to the host's current expected state),
|
|
94
|
+
* as a `GatedOperation` — see the file header for why this triad, unlike Recovery's restore, fits
|
|
95
|
+
* the generic shape directly. `plan` takes no meaningful input beyond the ceremony trigger; the
|
|
96
|
+
* plan is computed entirely from the site's current migration state server-side.
|
|
97
|
+
*/
|
|
98
|
+
readonly migrateForward: GatedOperation<void, unknown, MigrateForwardResult>;
|
|
99
|
+
}
|
|
100
|
+
export type { GatedConfirmResult, GatedOperation, GatedPlanResult };
|
|
101
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../src/core/ports/database.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAE7F;uBACuB;AACvB,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;yCAEqC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,WAAW,GAAG,aAAa,CAAC;AAE1E,gGAAgG;AAChG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,iGAAiG;IACjG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yFAAyF;IACzF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;6FAC6F;AAC7F,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;kGACkG;AAClG,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,mBAAmB,CAAC,OAAO,CAAC,EAAE;QAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC7E,yBAAyB,IAAI,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CAAC;IACnE;;;;OAIG;IACH,0BAA0B,CAAC,OAAO,CAAC,EAAE;QACnC,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACtC;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,oBAAoB,CAAC,CAAC;CAC9E;AAID,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../../src/core/ports/database.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminFormsPort` — form definitions (the schema an operator builds) and their submissions
|
|
3
|
+
* (the data visitors send in).
|
|
4
|
+
*
|
|
5
|
+
* ## Form DEFINITIONS have no delete, structurally — only `status`
|
|
6
|
+
*
|
|
7
|
+
* Mirrors `AdminIdentityPort.disableUser`'s reasoning exactly (see that file's header): the
|
|
8
|
+
* reference implementation's `FormDefinitionRepoPort` has no delete method AT ALL — not gated behind a permission, not a
|
|
9
|
+
* two-rung ladder, structurally absent — because a definition is meant to stay resolvable for
|
|
10
|
+
* historical submissions. The only lifecycle move is `active` ⇄ `disabled` via
|
|
11
|
+
* `updateFormDefinition`'s `status` field. There is deliberately no `deleteFormDefinition` on this
|
|
12
|
+
* port; a reference implementation has nothing to call.
|
|
13
|
+
*
|
|
14
|
+
* ## `updateFormDefinition`'s `fields` patch can add or edit, never remove
|
|
15
|
+
*
|
|
16
|
+
* When `patch.fields` is supplied it replaces the whole array, but the reference implementation's write chokepoint rejects
|
|
17
|
+
* (validation-class) any replacement that omits a field `id` present in the current definition —
|
|
18
|
+
* existing fields can be reordered or edited in place, and new ones appended, but never dropped
|
|
19
|
+
* through this call. There is no separate "remove a field" operation on the reference
|
|
20
|
+
* implementation.
|
|
21
|
+
*
|
|
22
|
+
* ## No `slug` in the update patch — and the rule this follows
|
|
23
|
+
*
|
|
24
|
+
* A definition's `slug` is immutable after creation. The reference implementation's write-service silently ignores a
|
|
25
|
+
* `slug` sent in an update patch rather than rejecting it (a documented, deliberate choice on their
|
|
26
|
+
* side) — sent, accepted, and dropped with no error and no effect.
|
|
27
|
+
*
|
|
28
|
+
* This port set applies one rule to that entire family of cases, wherever a reference
|
|
29
|
+
* implementation treats a well-formed, in-contract input specially:
|
|
30
|
+
*
|
|
31
|
+
* - **A silently-ignored input is removed from the contract.** If sending it produces no error and
|
|
32
|
+
* no effect, keeping it as a field would make the type lie about having an effect it doesn't
|
|
33
|
+
* have — which is the exact failure mode these ports exist to prevent. `AdminFormUpdatePatch` has
|
|
34
|
+
* no `slug` for this reason; there is nothing to silently drop because there is nothing to send.
|
|
35
|
+
* - **A loudly-rejected input stays in the contract and gets documented.** If sending it produces a
|
|
36
|
+
* real, typed rejection, the caller learns the truth at runtime — and because the surrounding
|
|
37
|
+
* union is OPEN, a different host may legitimately accept what the reference implementation
|
|
38
|
+
* refuses. That is a host-capability limit, not a contract lie, so removing the value would
|
|
39
|
+
* throw away real information. See `redirects.ts`'s file header for the concrete case
|
|
40
|
+
* (`matchType: "regex"`, kept and documented, never silently dropped).
|
|
41
|
+
*
|
|
42
|
+
* Applied elsewhere in this file: `updateFormDefinition`'s field-removal constraint (below) is a
|
|
43
|
+
* loud rejection (`FormFieldValidationError`), so `fields` stays on `AdminFormUpdatePatch` with the
|
|
44
|
+
* constraint documented, rather than being narrowed to a shape that can't express a removal
|
|
45
|
+
* attempt at all.
|
|
46
|
+
*
|
|
47
|
+
* ## Submissions are the one place with a real hard delete
|
|
48
|
+
*
|
|
49
|
+
* Unlike definitions, `FormSubmissionRepoPort` does expose a permanent delete — the reference repo
|
|
50
|
+
* port's own doc comment calls this out as "the one asymmetry." `deleteFormSubmission` is
|
|
51
|
+
* genuinely irreversible; do not offer it with "undo" affordances.
|
|
52
|
+
*
|
|
53
|
+
* ## Open vs. closed unions
|
|
54
|
+
*
|
|
55
|
+
* `FormFieldType` and `FormDefinitionStatus` are reference-implementation-specific vocabularies — OPEN, via the
|
|
56
|
+
* `T | (string & {})` idiom `seo.ts`'s file header introduces for this port set. `FormFieldType` in
|
|
57
|
+
* particular is a small, deliberately-not-yet-exhaustive set on the reference implementation (four
|
|
58
|
+
* field kinds today); a host offering a richer form builder needs room to add its own without this
|
|
59
|
+
* contract standing in the way.
|
|
60
|
+
*/
|
|
61
|
+
/** A form field's input kind — open, see file header. */
|
|
62
|
+
export type FormFieldType = "text" | "email" | "textarea" | "checkbox" | (string & {});
|
|
63
|
+
/** Lifecycle of a form definition — open, see file header. There is no "deleted" state; see the
|
|
64
|
+
* file header on why this port has no delete for definitions at all. */
|
|
65
|
+
export type FormDefinitionStatus = "active" | "disabled" | (string & {});
|
|
66
|
+
export interface AdminFormField {
|
|
67
|
+
readonly id: string;
|
|
68
|
+
readonly label: string;
|
|
69
|
+
readonly type: FormFieldType;
|
|
70
|
+
readonly required: boolean;
|
|
71
|
+
readonly maxLength?: number | null;
|
|
72
|
+
}
|
|
73
|
+
export interface AdminFormNotifyConfig {
|
|
74
|
+
readonly enabled: boolean;
|
|
75
|
+
readonly recipients: readonly string[];
|
|
76
|
+
}
|
|
77
|
+
export interface AdminFormDefinition {
|
|
78
|
+
readonly id: string;
|
|
79
|
+
readonly name: string;
|
|
80
|
+
readonly slug: string;
|
|
81
|
+
readonly fields: readonly AdminFormField[];
|
|
82
|
+
readonly notify: AdminFormNotifyConfig;
|
|
83
|
+
readonly status: FormDefinitionStatus;
|
|
84
|
+
readonly createdAt: string;
|
|
85
|
+
readonly updatedAt: string;
|
|
86
|
+
}
|
|
87
|
+
export interface AdminFormSubmission {
|
|
88
|
+
readonly id: string;
|
|
89
|
+
readonly formDefinitionId: string;
|
|
90
|
+
/** Keyed by the definition's field ids at submission time. */
|
|
91
|
+
readonly data: Readonly<Record<string, string | boolean>>;
|
|
92
|
+
readonly sourceIp: string;
|
|
93
|
+
readonly submittedAt: string;
|
|
94
|
+
}
|
|
95
|
+
/** A keyset-paginated page of submissions, newest-first. */
|
|
96
|
+
export interface AdminFormSubmissionPage {
|
|
97
|
+
readonly items: readonly AdminFormSubmission[];
|
|
98
|
+
readonly nextCursor: string | null;
|
|
99
|
+
}
|
|
100
|
+
export interface AdminFormCreateInput {
|
|
101
|
+
readonly name: string;
|
|
102
|
+
readonly slug: string;
|
|
103
|
+
readonly fields: readonly AdminFormField[];
|
|
104
|
+
readonly notify?: AdminFormNotifyConfig;
|
|
105
|
+
}
|
|
106
|
+
/** No `slug` — see file header. */
|
|
107
|
+
export interface AdminFormUpdatePatch {
|
|
108
|
+
readonly name?: string;
|
|
109
|
+
/** Whole-array replace with an add/edit-only constraint — see file header. */
|
|
110
|
+
readonly fields?: readonly AdminFormField[];
|
|
111
|
+
readonly notify?: AdminFormNotifyConfig;
|
|
112
|
+
readonly status?: FormDefinitionStatus;
|
|
113
|
+
}
|
|
114
|
+
export interface AdminFormsPort {
|
|
115
|
+
listFormDefinitions(): Promise<readonly AdminFormDefinition[]>;
|
|
116
|
+
getFormDefinition(id: string): Promise<AdminFormDefinition>;
|
|
117
|
+
createFormDefinition(input: AdminFormCreateInput): Promise<AdminFormDefinition>;
|
|
118
|
+
/** See file header: `patch.fields`, when supplied, may add or edit but never omit an existing
|
|
119
|
+
* field id. */
|
|
120
|
+
updateFormDefinition(id: string, patch: AdminFormUpdatePatch): Promise<AdminFormDefinition>;
|
|
121
|
+
listFormSubmissions(formId: string, options?: {
|
|
122
|
+
limit?: number;
|
|
123
|
+
cursor?: string;
|
|
124
|
+
}): Promise<AdminFormSubmissionPage>;
|
|
125
|
+
getFormSubmission(formId: string, submissionId: string): Promise<AdminFormSubmission>;
|
|
126
|
+
/** Permanent delete — the one hard delete in this port. See file header on why form
|
|
127
|
+
* DEFINITIONS have no equivalent. */
|
|
128
|
+
deleteFormSubmission(formId: string, submissionId: string): Promise<void>;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=forms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forms.d.ts","sourceRoot":"","sources":["../../../src/core/ports/forms.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file `AdminFormsPort` — form definitions (the schema an operator builds) and their submissions
|
|
3
|
+
* (the data visitors send in).
|
|
4
|
+
*
|
|
5
|
+
* ## Form DEFINITIONS have no delete, structurally — only `status`
|
|
6
|
+
*
|
|
7
|
+
* Mirrors `AdminIdentityPort.disableUser`'s reasoning exactly (see that file's header): the
|
|
8
|
+
* reference implementation's `FormDefinitionRepoPort` has no delete method AT ALL — not gated behind a permission, not a
|
|
9
|
+
* two-rung ladder, structurally absent — because a definition is meant to stay resolvable for
|
|
10
|
+
* historical submissions. The only lifecycle move is `active` ⇄ `disabled` via
|
|
11
|
+
* `updateFormDefinition`'s `status` field. There is deliberately no `deleteFormDefinition` on this
|
|
12
|
+
* port; a reference implementation has nothing to call.
|
|
13
|
+
*
|
|
14
|
+
* ## `updateFormDefinition`'s `fields` patch can add or edit, never remove
|
|
15
|
+
*
|
|
16
|
+
* When `patch.fields` is supplied it replaces the whole array, but the reference implementation's write chokepoint rejects
|
|
17
|
+
* (validation-class) any replacement that omits a field `id` present in the current definition —
|
|
18
|
+
* existing fields can be reordered or edited in place, and new ones appended, but never dropped
|
|
19
|
+
* through this call. There is no separate "remove a field" operation on the reference
|
|
20
|
+
* implementation.
|
|
21
|
+
*
|
|
22
|
+
* ## No `slug` in the update patch — and the rule this follows
|
|
23
|
+
*
|
|
24
|
+
* A definition's `slug` is immutable after creation. The reference implementation's write-service silently ignores a
|
|
25
|
+
* `slug` sent in an update patch rather than rejecting it (a documented, deliberate choice on their
|
|
26
|
+
* side) — sent, accepted, and dropped with no error and no effect.
|
|
27
|
+
*
|
|
28
|
+
* This port set applies one rule to that entire family of cases, wherever a reference
|
|
29
|
+
* implementation treats a well-formed, in-contract input specially:
|
|
30
|
+
*
|
|
31
|
+
* - **A silently-ignored input is removed from the contract.** If sending it produces no error and
|
|
32
|
+
* no effect, keeping it as a field would make the type lie about having an effect it doesn't
|
|
33
|
+
* have — which is the exact failure mode these ports exist to prevent. `AdminFormUpdatePatch` has
|
|
34
|
+
* no `slug` for this reason; there is nothing to silently drop because there is nothing to send.
|
|
35
|
+
* - **A loudly-rejected input stays in the contract and gets documented.** If sending it produces a
|
|
36
|
+
* real, typed rejection, the caller learns the truth at runtime — and because the surrounding
|
|
37
|
+
* union is OPEN, a different host may legitimately accept what the reference implementation
|
|
38
|
+
* refuses. That is a host-capability limit, not a contract lie, so removing the value would
|
|
39
|
+
* throw away real information. See `redirects.ts`'s file header for the concrete case
|
|
40
|
+
* (`matchType: "regex"`, kept and documented, never silently dropped).
|
|
41
|
+
*
|
|
42
|
+
* Applied elsewhere in this file: `updateFormDefinition`'s field-removal constraint (below) is a
|
|
43
|
+
* loud rejection (`FormFieldValidationError`), so `fields` stays on `AdminFormUpdatePatch` with the
|
|
44
|
+
* constraint documented, rather than being narrowed to a shape that can't express a removal
|
|
45
|
+
* attempt at all.
|
|
46
|
+
*
|
|
47
|
+
* ## Submissions are the one place with a real hard delete
|
|
48
|
+
*
|
|
49
|
+
* Unlike definitions, `FormSubmissionRepoPort` does expose a permanent delete — the reference repo
|
|
50
|
+
* port's own doc comment calls this out as "the one asymmetry." `deleteFormSubmission` is
|
|
51
|
+
* genuinely irreversible; do not offer it with "undo" affordances.
|
|
52
|
+
*
|
|
53
|
+
* ## Open vs. closed unions
|
|
54
|
+
*
|
|
55
|
+
* `FormFieldType` and `FormDefinitionStatus` are reference-implementation-specific vocabularies — OPEN, via the
|
|
56
|
+
* `T | (string & {})` idiom `seo.ts`'s file header introduces for this port set. `FormFieldType` in
|
|
57
|
+
* particular is a small, deliberately-not-yet-exhaustive set on the reference implementation (four
|
|
58
|
+
* field kinds today); a host offering a richer form builder needs room to add its own without this
|
|
59
|
+
* contract standing in the way.
|
|
60
|
+
*/
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=forms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forms.js","sourceRoot":"","sources":["../../../src/core/ports/forms.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG"}
|