@crowi/plugin-api 0.1.0-alpha.0 → 0.1.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +105 -25
- package/dist/index.d.ts +105 -25
- package/dist/index.js.map +1 -1
- package/package.json +9 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod/v3';
|
|
2
2
|
import { Readable } from 'node:stream';
|
|
3
|
+
import { Context } from 'hono';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The context object passed to every plugin callback. It is the only
|
|
@@ -29,6 +30,13 @@ interface PluginContext {
|
|
|
29
30
|
* instead of duplicating the fields in their own configSchema.
|
|
30
31
|
*/
|
|
31
32
|
dependencyConfig<T>(dependencyName: string): T;
|
|
33
|
+
/**
|
|
34
|
+
* Read core application info (the wiki name, …) — settings that live
|
|
35
|
+
* outside this plugin's own config namespace but that an integration
|
|
36
|
+
* may need (e.g. to brand an outbound manifest). Read live at call
|
|
37
|
+
* time, so it reflects admin edits made after boot.
|
|
38
|
+
*/
|
|
39
|
+
appInfo(): AppInfo;
|
|
32
40
|
/** Write a single config field, persisting to Mongo. */
|
|
33
41
|
setConfig(key: string, value: unknown): Promise<void>;
|
|
34
42
|
/** Per-Page metadata accessor for this plugin's namespace. */
|
|
@@ -48,6 +56,30 @@ interface PluginContext {
|
|
|
48
56
|
/** Structured logger scoped to this plugin (auto-prefixed with name). */
|
|
49
57
|
log: PluginLogger;
|
|
50
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Read-only view of core application settings exposed to plugins via
|
|
61
|
+
* `ctx.appInfo()`. Intentionally a small, curated surface (not a generic
|
|
62
|
+
* "read any core config" escape hatch) — add fields here as concrete
|
|
63
|
+
* plugin needs appear.
|
|
64
|
+
*/
|
|
65
|
+
interface AppInfo {
|
|
66
|
+
/**
|
|
67
|
+
* The configured wiki name (core `app:title`), trimmed. Always a
|
|
68
|
+
* non-empty string: when the operator has not set a custom title it
|
|
69
|
+
* defaults to `'Crowi'` (the seed value), so consumers never have to
|
|
70
|
+
* handle an absent name.
|
|
71
|
+
*/
|
|
72
|
+
title: string;
|
|
73
|
+
/**
|
|
74
|
+
* The wiki's public base origin (core `CLIENT_URL` / `getBaseUrl()`),
|
|
75
|
+
* e.g. `https://wiki.example.com`. An **empty string** when no public
|
|
76
|
+
* origin is configured — unlike `title` there is no sensible default,
|
|
77
|
+
* so a plugin that needs an absolute URL (outbound webhook / manifest)
|
|
78
|
+
* must handle the empty case. Plugins read this instead of
|
|
79
|
+
* `process.env.CLIENT_URL` directly.
|
|
80
|
+
*/
|
|
81
|
+
baseUrl: string;
|
|
82
|
+
}
|
|
51
83
|
/**
|
|
52
84
|
* Per-Page metadata read / write helper. Each plugin gets a private
|
|
53
85
|
* namespace at `page.metadata['<plugin-name>']`; this accessor scopes
|
|
@@ -910,28 +942,65 @@ interface RendererRegistry {
|
|
|
910
942
|
}
|
|
911
943
|
|
|
912
944
|
/**
|
|
913
|
-
*
|
|
914
|
-
*
|
|
915
|
-
*
|
|
916
|
-
*
|
|
917
|
-
|
|
918
|
-
|
|
945
|
+
* HTTP method a plugin route can be mounted on. Kept to the verbs the
|
|
946
|
+
* inbound-webhook + admin-action surface actually needs (RFC-0013 §4):
|
|
947
|
+
* `POST` for Slack events / slash / interactivity + `@action` targets,
|
|
948
|
+
* `GET` for OAuth callbacks + simple status endpoints.
|
|
949
|
+
*/
|
|
950
|
+
type PluginRouteMethod = 'GET' | 'POST';
|
|
951
|
+
/**
|
|
952
|
+
* A plugin route handler. It receives the raw Hono `Context` and returns
|
|
953
|
+
* a `Response` (or a promise of one), exactly like a hand-written Hono
|
|
954
|
+
* handler — the scope does **not** wrap it in a typed-route/validator
|
|
955
|
+
* layer.
|
|
919
956
|
*
|
|
920
|
-
*
|
|
921
|
-
*
|
|
922
|
-
*
|
|
923
|
-
*
|
|
924
|
-
*
|
|
925
|
-
*
|
|
926
|
-
*
|
|
957
|
+
* **Raw body invariant** (RFC-0013 §8, a Slack hard requirement): the
|
|
958
|
+
* route is a plain Hono route, NOT a `@hono/zod-openapi` route, so no
|
|
959
|
+
* body-consuming validator runs ahead of the handler. `c.req.text()` /
|
|
960
|
+
* `c.req.raw` therefore yield the *exact* bytes the client sent, which
|
|
961
|
+
* the Slack signature check (`HMAC-SHA256` over `v0:{ts}:{rawBody}`)
|
|
962
|
+
* depends on. `createJwtAuth` (installed on non-public routes) never
|
|
963
|
+
* reads the body, so the invariant holds for authed routes too.
|
|
964
|
+
*/
|
|
965
|
+
type PluginRouteHandler = (c: Context) => Response | Promise<Response>;
|
|
966
|
+
/** Per-route options passed alongside the handler. */
|
|
967
|
+
interface PluginRouteOptions {
|
|
968
|
+
/**
|
|
969
|
+
* When `true`, the route is mounted **without** `createJwtAuth`, so it
|
|
970
|
+
* is reachable by unauthenticated requests (Crowi-auth public). Use for
|
|
971
|
+
* inbound webhooks that authenticate themselves out-of-band — e.g. the
|
|
972
|
+
* Slack Events API endpoint, which is gated by Slack's request-signature
|
|
973
|
+
* check rather than a Crowi session (RFC-0013 §8).
|
|
974
|
+
*
|
|
975
|
+
* Omitted / `false` mounts the route under `createJwtAuth`, so it
|
|
976
|
+
* requires a valid Crowi JWT just like a core authenticated endpoint
|
|
977
|
+
* (admin "Test connection" / `@action` targets, OAuth callbacks).
|
|
978
|
+
*/
|
|
979
|
+
public?: boolean;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Scope passed to `registerRoutes(scope, ctx)`. Lets a plugin contribute
|
|
983
|
+
* HTTP routes that the runtime mounts at
|
|
984
|
+
* `/api/v2/plugins/<plugin-name>/<path>` — the `<plugin-name>` path
|
|
985
|
+
* segment guarantees that core endpoints and other plugins cannot
|
|
986
|
+
* collide (RFC-0013 §4).
|
|
987
|
+
*
|
|
988
|
+
* The scope is built per-plugin inside `buildHonoApp` (the Hono app does
|
|
989
|
+
* not exist yet when plugins activate at boot), so `<plugin-name>` is
|
|
990
|
+
* already closed over — plugins only supply the sub-path.
|
|
927
991
|
*/
|
|
928
992
|
interface PluginRouterScope {
|
|
929
993
|
/**
|
|
930
|
-
*
|
|
931
|
-
*
|
|
932
|
-
*
|
|
994
|
+
* Mount `handler` for `method` at `<path>` under this plugin's
|
|
995
|
+
* namespace. `path` is relative to `/api/v2/plugins/<plugin-name>` and
|
|
996
|
+
* should start with `/` (e.g. `route('POST', '/events', handler, {
|
|
997
|
+
* public: true })` → `POST /api/v2/plugins/<name>/events`).
|
|
998
|
+
*
|
|
999
|
+
* Pass `{ public: true }` to bypass `createJwtAuth` for self-
|
|
1000
|
+
* authenticating inbound webhooks; omit it for routes that require a
|
|
1001
|
+
* Crowi session.
|
|
933
1002
|
*/
|
|
934
|
-
|
|
1003
|
+
route(method: PluginRouteMethod, path: string, handler: PluginRouteHandler, opts?: PluginRouteOptions): void;
|
|
935
1004
|
}
|
|
936
1005
|
|
|
937
1006
|
/**
|
|
@@ -1002,7 +1071,7 @@ interface CrowiPlugin {
|
|
|
1002
1071
|
* from a fixed allow-list to keep the bundle small.
|
|
1003
1072
|
*/
|
|
1004
1073
|
adminPlacement?: {
|
|
1005
|
-
section?: 'settings' | 'shared' | 'storage' | 'mail' | 'notification' | 'auth' | 'search' | 'renderer';
|
|
1074
|
+
section?: 'settings' | 'shared' | 'storage' | 'mail' | 'notification' | 'auth' | 'search' | 'renderer' | 'platform';
|
|
1006
1075
|
label?: string;
|
|
1007
1076
|
icon?: string;
|
|
1008
1077
|
};
|
|
@@ -1052,13 +1121,24 @@ interface CrowiPlugin {
|
|
|
1052
1121
|
*/
|
|
1053
1122
|
registerHooks?: (events: EventBus, ctx: PluginContext) => void;
|
|
1054
1123
|
/**
|
|
1055
|
-
*
|
|
1056
|
-
* `/api/v2/plugins/<name
|
|
1124
|
+
* HTTP routes the plugin contributes, mounted at
|
|
1125
|
+
* `/api/v2/plugins/<name>/<path>` (the `<name>` path segment guarantees
|
|
1057
1126
|
* that core endpoints and other plugins cannot collide). Used for
|
|
1058
|
-
*
|
|
1059
|
-
*
|
|
1060
|
-
*
|
|
1061
|
-
*
|
|
1127
|
+
* inbound webhooks (Slack events / slash / interactivity), "Test
|
|
1128
|
+
* connection" buttons, `@action` targets, OAuth callbacks, etc.
|
|
1129
|
+
*
|
|
1130
|
+
* Each route is a plain Hono handler — `scope.route(method, path,
|
|
1131
|
+
* (c) => Response, opts?)`. The handler receives the raw `Context`, so
|
|
1132
|
+
* `c.req.text()` / `c.req.raw` give the exact request bytes (no
|
|
1133
|
+
* validator consumes the body ahead of it — the Slack signature check
|
|
1134
|
+
* relies on this). Pass `{ public: true }` to bypass `createJwtAuth`
|
|
1135
|
+
* for self-authenticating webhooks; omit it for Crowi-session-gated
|
|
1136
|
+
* routes.
|
|
1137
|
+
*
|
|
1138
|
+
* Called once at boot — but unlike the other `register*` hooks, this
|
|
1139
|
+
* runs inside `buildHonoApp` (the Hono app does not exist yet when
|
|
1140
|
+
* plugins activate), so a plugin's `registerRoutes` fires slightly
|
|
1141
|
+
* later than its `registerStorage` / `registerNotifier` / etc.
|
|
1062
1142
|
*/
|
|
1063
1143
|
registerRoutes?: (scope: PluginRouterScope, ctx: PluginContext) => void;
|
|
1064
1144
|
/**
|
|
@@ -1156,4 +1236,4 @@ interface ActionAnnotation {
|
|
|
1156
1236
|
*/
|
|
1157
1237
|
declare function getActionAnnotation(field: z.ZodTypeAny): ActionAnnotation | null;
|
|
1158
1238
|
|
|
1159
|
-
export { ACTION_FIELD_MARKER, type AuthContext, type AuthDriver, type AuthProfile, type AuthRegistry, type AuthVerifyResult, type CacheEntry, type CacheKey, type CacheStorage, type CodeBlockInfo, type CodeBlockRenderer, type CrowiPlugin, type EmailMessage, type EmbedFragment, type EmbedInput, type EmbedRenderer, type EventBus, type InlineExpansion, type MailSender, type MailSenderRegistry, type NodeRenderer, type NotificationPayload, type NotifierDriver, type NotifierRegistry, type PageMetadataAccessor, type PluginContext, type PluginCrypto, type PluginEvents, type PluginLogger, type PluginRouterScope, type RenderContext, type RenderError, type RenderPhase, type RenderResult, type RendererRegistry, type Reservation, SENSITIVE_FIELD_MARKER, type ScopedCacheStorage, type SearchDriver, type SearchHit, type SearchHits, type SearchPageType, type SearchQuery, type SearchQueryGrants, type SearchQueryViewer, type SearchRegistry, type SearchableDoc, type StorageDriver, type StoragePutMeta, type StoragePutResult, type StorageRegistry, type UrlInlineExpansionRule, getActionAnnotation, isSensitiveField };
|
|
1239
|
+
export { ACTION_FIELD_MARKER, type AppInfo, type AuthContext, type AuthDriver, type AuthProfile, type AuthRegistry, type AuthVerifyResult, type CacheEntry, type CacheKey, type CacheStorage, type CodeBlockInfo, type CodeBlockRenderer, type CrowiPlugin, type EmailMessage, type EmbedFragment, type EmbedInput, type EmbedRenderer, type EventBus, type InlineExpansion, type MailSender, type MailSenderRegistry, type NodeRenderer, type NotificationPayload, type NotifierDriver, type NotifierRegistry, type PageMetadataAccessor, type PluginContext, type PluginCrypto, type PluginEvents, type PluginLogger, type PluginRouteHandler, type PluginRouteMethod, type PluginRouteOptions, type PluginRouterScope, type RenderContext, type RenderError, type RenderPhase, type RenderResult, type RendererRegistry, type Reservation, SENSITIVE_FIELD_MARKER, type ScopedCacheStorage, type SearchDriver, type SearchHit, type SearchHits, type SearchPageType, type SearchQuery, type SearchQueryGrants, type SearchQueryViewer, type SearchRegistry, type SearchableDoc, type StorageDriver, type StoragePutMeta, type StoragePutResult, type StorageRegistry, type UrlInlineExpansionRule, getActionAnnotation, isSensitiveField };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod/v3';
|
|
2
2
|
import { Readable } from 'node:stream';
|
|
3
|
+
import { Context } from 'hono';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The context object passed to every plugin callback. It is the only
|
|
@@ -29,6 +30,13 @@ interface PluginContext {
|
|
|
29
30
|
* instead of duplicating the fields in their own configSchema.
|
|
30
31
|
*/
|
|
31
32
|
dependencyConfig<T>(dependencyName: string): T;
|
|
33
|
+
/**
|
|
34
|
+
* Read core application info (the wiki name, …) — settings that live
|
|
35
|
+
* outside this plugin's own config namespace but that an integration
|
|
36
|
+
* may need (e.g. to brand an outbound manifest). Read live at call
|
|
37
|
+
* time, so it reflects admin edits made after boot.
|
|
38
|
+
*/
|
|
39
|
+
appInfo(): AppInfo;
|
|
32
40
|
/** Write a single config field, persisting to Mongo. */
|
|
33
41
|
setConfig(key: string, value: unknown): Promise<void>;
|
|
34
42
|
/** Per-Page metadata accessor for this plugin's namespace. */
|
|
@@ -48,6 +56,30 @@ interface PluginContext {
|
|
|
48
56
|
/** Structured logger scoped to this plugin (auto-prefixed with name). */
|
|
49
57
|
log: PluginLogger;
|
|
50
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Read-only view of core application settings exposed to plugins via
|
|
61
|
+
* `ctx.appInfo()`. Intentionally a small, curated surface (not a generic
|
|
62
|
+
* "read any core config" escape hatch) — add fields here as concrete
|
|
63
|
+
* plugin needs appear.
|
|
64
|
+
*/
|
|
65
|
+
interface AppInfo {
|
|
66
|
+
/**
|
|
67
|
+
* The configured wiki name (core `app:title`), trimmed. Always a
|
|
68
|
+
* non-empty string: when the operator has not set a custom title it
|
|
69
|
+
* defaults to `'Crowi'` (the seed value), so consumers never have to
|
|
70
|
+
* handle an absent name.
|
|
71
|
+
*/
|
|
72
|
+
title: string;
|
|
73
|
+
/**
|
|
74
|
+
* The wiki's public base origin (core `CLIENT_URL` / `getBaseUrl()`),
|
|
75
|
+
* e.g. `https://wiki.example.com`. An **empty string** when no public
|
|
76
|
+
* origin is configured — unlike `title` there is no sensible default,
|
|
77
|
+
* so a plugin that needs an absolute URL (outbound webhook / manifest)
|
|
78
|
+
* must handle the empty case. Plugins read this instead of
|
|
79
|
+
* `process.env.CLIENT_URL` directly.
|
|
80
|
+
*/
|
|
81
|
+
baseUrl: string;
|
|
82
|
+
}
|
|
51
83
|
/**
|
|
52
84
|
* Per-Page metadata read / write helper. Each plugin gets a private
|
|
53
85
|
* namespace at `page.metadata['<plugin-name>']`; this accessor scopes
|
|
@@ -910,28 +942,65 @@ interface RendererRegistry {
|
|
|
910
942
|
}
|
|
911
943
|
|
|
912
944
|
/**
|
|
913
|
-
*
|
|
914
|
-
*
|
|
915
|
-
*
|
|
916
|
-
*
|
|
917
|
-
|
|
918
|
-
|
|
945
|
+
* HTTP method a plugin route can be mounted on. Kept to the verbs the
|
|
946
|
+
* inbound-webhook + admin-action surface actually needs (RFC-0013 §4):
|
|
947
|
+
* `POST` for Slack events / slash / interactivity + `@action` targets,
|
|
948
|
+
* `GET` for OAuth callbacks + simple status endpoints.
|
|
949
|
+
*/
|
|
950
|
+
type PluginRouteMethod = 'GET' | 'POST';
|
|
951
|
+
/**
|
|
952
|
+
* A plugin route handler. It receives the raw Hono `Context` and returns
|
|
953
|
+
* a `Response` (or a promise of one), exactly like a hand-written Hono
|
|
954
|
+
* handler — the scope does **not** wrap it in a typed-route/validator
|
|
955
|
+
* layer.
|
|
919
956
|
*
|
|
920
|
-
*
|
|
921
|
-
*
|
|
922
|
-
*
|
|
923
|
-
*
|
|
924
|
-
*
|
|
925
|
-
*
|
|
926
|
-
*
|
|
957
|
+
* **Raw body invariant** (RFC-0013 §8, a Slack hard requirement): the
|
|
958
|
+
* route is a plain Hono route, NOT a `@hono/zod-openapi` route, so no
|
|
959
|
+
* body-consuming validator runs ahead of the handler. `c.req.text()` /
|
|
960
|
+
* `c.req.raw` therefore yield the *exact* bytes the client sent, which
|
|
961
|
+
* the Slack signature check (`HMAC-SHA256` over `v0:{ts}:{rawBody}`)
|
|
962
|
+
* depends on. `createJwtAuth` (installed on non-public routes) never
|
|
963
|
+
* reads the body, so the invariant holds for authed routes too.
|
|
964
|
+
*/
|
|
965
|
+
type PluginRouteHandler = (c: Context) => Response | Promise<Response>;
|
|
966
|
+
/** Per-route options passed alongside the handler. */
|
|
967
|
+
interface PluginRouteOptions {
|
|
968
|
+
/**
|
|
969
|
+
* When `true`, the route is mounted **without** `createJwtAuth`, so it
|
|
970
|
+
* is reachable by unauthenticated requests (Crowi-auth public). Use for
|
|
971
|
+
* inbound webhooks that authenticate themselves out-of-band — e.g. the
|
|
972
|
+
* Slack Events API endpoint, which is gated by Slack's request-signature
|
|
973
|
+
* check rather than a Crowi session (RFC-0013 §8).
|
|
974
|
+
*
|
|
975
|
+
* Omitted / `false` mounts the route under `createJwtAuth`, so it
|
|
976
|
+
* requires a valid Crowi JWT just like a core authenticated endpoint
|
|
977
|
+
* (admin "Test connection" / `@action` targets, OAuth callbacks).
|
|
978
|
+
*/
|
|
979
|
+
public?: boolean;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Scope passed to `registerRoutes(scope, ctx)`. Lets a plugin contribute
|
|
983
|
+
* HTTP routes that the runtime mounts at
|
|
984
|
+
* `/api/v2/plugins/<plugin-name>/<path>` — the `<plugin-name>` path
|
|
985
|
+
* segment guarantees that core endpoints and other plugins cannot
|
|
986
|
+
* collide (RFC-0013 §4).
|
|
987
|
+
*
|
|
988
|
+
* The scope is built per-plugin inside `buildHonoApp` (the Hono app does
|
|
989
|
+
* not exist yet when plugins activate at boot), so `<plugin-name>` is
|
|
990
|
+
* already closed over — plugins only supply the sub-path.
|
|
927
991
|
*/
|
|
928
992
|
interface PluginRouterScope {
|
|
929
993
|
/**
|
|
930
|
-
*
|
|
931
|
-
*
|
|
932
|
-
*
|
|
994
|
+
* Mount `handler` for `method` at `<path>` under this plugin's
|
|
995
|
+
* namespace. `path` is relative to `/api/v2/plugins/<plugin-name>` and
|
|
996
|
+
* should start with `/` (e.g. `route('POST', '/events', handler, {
|
|
997
|
+
* public: true })` → `POST /api/v2/plugins/<name>/events`).
|
|
998
|
+
*
|
|
999
|
+
* Pass `{ public: true }` to bypass `createJwtAuth` for self-
|
|
1000
|
+
* authenticating inbound webhooks; omit it for routes that require a
|
|
1001
|
+
* Crowi session.
|
|
933
1002
|
*/
|
|
934
|
-
|
|
1003
|
+
route(method: PluginRouteMethod, path: string, handler: PluginRouteHandler, opts?: PluginRouteOptions): void;
|
|
935
1004
|
}
|
|
936
1005
|
|
|
937
1006
|
/**
|
|
@@ -1002,7 +1071,7 @@ interface CrowiPlugin {
|
|
|
1002
1071
|
* from a fixed allow-list to keep the bundle small.
|
|
1003
1072
|
*/
|
|
1004
1073
|
adminPlacement?: {
|
|
1005
|
-
section?: 'settings' | 'shared' | 'storage' | 'mail' | 'notification' | 'auth' | 'search' | 'renderer';
|
|
1074
|
+
section?: 'settings' | 'shared' | 'storage' | 'mail' | 'notification' | 'auth' | 'search' | 'renderer' | 'platform';
|
|
1006
1075
|
label?: string;
|
|
1007
1076
|
icon?: string;
|
|
1008
1077
|
};
|
|
@@ -1052,13 +1121,24 @@ interface CrowiPlugin {
|
|
|
1052
1121
|
*/
|
|
1053
1122
|
registerHooks?: (events: EventBus, ctx: PluginContext) => void;
|
|
1054
1123
|
/**
|
|
1055
|
-
*
|
|
1056
|
-
* `/api/v2/plugins/<name
|
|
1124
|
+
* HTTP routes the plugin contributes, mounted at
|
|
1125
|
+
* `/api/v2/plugins/<name>/<path>` (the `<name>` path segment guarantees
|
|
1057
1126
|
* that core endpoints and other plugins cannot collide). Used for
|
|
1058
|
-
*
|
|
1059
|
-
*
|
|
1060
|
-
*
|
|
1061
|
-
*
|
|
1127
|
+
* inbound webhooks (Slack events / slash / interactivity), "Test
|
|
1128
|
+
* connection" buttons, `@action` targets, OAuth callbacks, etc.
|
|
1129
|
+
*
|
|
1130
|
+
* Each route is a plain Hono handler — `scope.route(method, path,
|
|
1131
|
+
* (c) => Response, opts?)`. The handler receives the raw `Context`, so
|
|
1132
|
+
* `c.req.text()` / `c.req.raw` give the exact request bytes (no
|
|
1133
|
+
* validator consumes the body ahead of it — the Slack signature check
|
|
1134
|
+
* relies on this). Pass `{ public: true }` to bypass `createJwtAuth`
|
|
1135
|
+
* for self-authenticating webhooks; omit it for Crowi-session-gated
|
|
1136
|
+
* routes.
|
|
1137
|
+
*
|
|
1138
|
+
* Called once at boot — but unlike the other `register*` hooks, this
|
|
1139
|
+
* runs inside `buildHonoApp` (the Hono app does not exist yet when
|
|
1140
|
+
* plugins activate), so a plugin's `registerRoutes` fires slightly
|
|
1141
|
+
* later than its `registerStorage` / `registerNotifier` / etc.
|
|
1062
1142
|
*/
|
|
1063
1143
|
registerRoutes?: (scope: PluginRouterScope, ctx: PluginContext) => void;
|
|
1064
1144
|
/**
|
|
@@ -1156,4 +1236,4 @@ interface ActionAnnotation {
|
|
|
1156
1236
|
*/
|
|
1157
1237
|
declare function getActionAnnotation(field: z.ZodTypeAny): ActionAnnotation | null;
|
|
1158
1238
|
|
|
1159
|
-
export { ACTION_FIELD_MARKER, type AuthContext, type AuthDriver, type AuthProfile, type AuthRegistry, type AuthVerifyResult, type CacheEntry, type CacheKey, type CacheStorage, type CodeBlockInfo, type CodeBlockRenderer, type CrowiPlugin, type EmailMessage, type EmbedFragment, type EmbedInput, type EmbedRenderer, type EventBus, type InlineExpansion, type MailSender, type MailSenderRegistry, type NodeRenderer, type NotificationPayload, type NotifierDriver, type NotifierRegistry, type PageMetadataAccessor, type PluginContext, type PluginCrypto, type PluginEvents, type PluginLogger, type PluginRouterScope, type RenderContext, type RenderError, type RenderPhase, type RenderResult, type RendererRegistry, type Reservation, SENSITIVE_FIELD_MARKER, type ScopedCacheStorage, type SearchDriver, type SearchHit, type SearchHits, type SearchPageType, type SearchQuery, type SearchQueryGrants, type SearchQueryViewer, type SearchRegistry, type SearchableDoc, type StorageDriver, type StoragePutMeta, type StoragePutResult, type StorageRegistry, type UrlInlineExpansionRule, getActionAnnotation, isSensitiveField };
|
|
1239
|
+
export { ACTION_FIELD_MARKER, type AppInfo, type AuthContext, type AuthDriver, type AuthProfile, type AuthRegistry, type AuthVerifyResult, type CacheEntry, type CacheKey, type CacheStorage, type CodeBlockInfo, type CodeBlockRenderer, type CrowiPlugin, type EmailMessage, type EmbedFragment, type EmbedInput, type EmbedRenderer, type EventBus, type InlineExpansion, type MailSender, type MailSenderRegistry, type NodeRenderer, type NotificationPayload, type NotifierDriver, type NotifierRegistry, type PageMetadataAccessor, type PluginContext, type PluginCrypto, type PluginEvents, type PluginLogger, type PluginRouteHandler, type PluginRouteMethod, type PluginRouteOptions, type PluginRouterScope, type RenderContext, type RenderError, type RenderPhase, type RenderResult, type RendererRegistry, type Reservation, SENSITIVE_FIELD_MARKER, type ScopedCacheStorage, type SearchDriver, type SearchHit, type SearchHits, type SearchPageType, type SearchQuery, type SearchQueryGrants, type SearchQueryViewer, type SearchRegistry, type SearchableDoc, type StorageDriver, type StoragePutMeta, type StoragePutResult, type StorageRegistry, type UrlInlineExpansionRule, getActionAnnotation, isSensitiveField };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/schema-markers.ts"],"sourcesContent":["/**\n * @crowi/plugin-api — type-only contract for Crowi 2.0 plugins.\n *\n * Plugins author against this package. The runtime (@crowi/server) loads\n * plugins listed in `crowi.config.json`, calls each plugin's\n * `register*` callbacks, and routes all the side effects (storage,\n * search, auth, notifications) through the typed registries declared\n * here.\n *\n * For the design rationale see `docs/rfcs/0001-plugin-architecture.md`\n * in the Crowi monorepo.\n */\n\nexport type { CrowiPlugin } from './plugin';\n\nexport type { PluginContext, PageMetadataAccessor, PluginCrypto, PluginLogger } from './context';\n\nexport type { StorageDriver, StorageRegistry, StoragePutMeta, StoragePutResult } from './registries/storage';\n\nexport type {\n SearchDriver,\n SearchRegistry,\n SearchableDoc,\n SearchQuery,\n SearchQueryViewer,\n SearchQueryGrants,\n SearchPageType,\n SearchHits,\n SearchHit,\n} from './registries/search';\n\nexport type { AuthDriver, AuthRegistry, AuthProfile, AuthVerifyResult } from './registries/auth';\n\nexport type { NotifierDriver, NotifierRegistry, NotificationPayload } from './registries/notifier';\n\nexport type { MailSender, MailSenderRegistry, EmailMessage } from './registries/mail';\n\nexport type {\n RendererRegistry,\n NodeRenderer,\n CodeBlockRenderer,\n CodeBlockInfo,\n EmbedRenderer,\n EmbedInput,\n EmbedFragment,\n UrlInlineExpansionRule,\n InlineExpansion,\n RenderContext,\n RenderPhase,\n RenderResult,\n RenderError,\n Reservation,\n CacheStorage,\n ScopedCacheStorage,\n CacheKey,\n CacheEntry,\n AuthContext,\n} from './renderer';\n\nexport type { EventBus, PluginEvents } from './events';\n\nexport type { PluginRouterScope } from './routes';\n\nexport { SENSITIVE_FIELD_MARKER, ACTION_FIELD_MARKER, isSensitiveField, getActionAnnotation } from './schema-markers';\n","import type { z } from 'zod/v3';\n\n/**\n * `configSchema` description-string markers.\n *\n * The admin UI walks the schema and looks at each field's\n * `description` (set via `z.string().describe('@sensitive ...')`). A\n * description starting with one of these marker tokens unlocks special\n * UI behaviour without forcing every field to declare a custom Zod\n * type.\n */\n\n/**\n * Marker that flags a config field as sensitive (encrypted at rest).\n * Usage:\n *\n * z.string().describe('@sensitive AWS secret access key')\n *\n * The runtime auto-encrypts on write and decrypts on read, using the\n * same KeyProvider as core sensitive Config. The admin UI renders the\n * field via `<SecretField>` (saved badge / clear pending / undo).\n */\nexport const SENSITIVE_FIELD_MARKER = '@sensitive';\n\n/**\n * Marker that adds an action button next to a config field. Usage:\n *\n * z.string().describe('@action \"Test connection\" POST /test')\n *\n * The admin form renders a button with the given label that calls the\n * plugin's contributed endpoint at the given verb / path (relative to\n * `/api/v2/plugins/<name>/`). Useful for \"Test connection\",\n * \"Authorise with Google\", etc. without forcing every plugin to ship\n * its own React component.\n */\nexport const ACTION_FIELD_MARKER = '@action';\n\n/**\n * True if the schema field is marked `@sensitive`.\n *\n * `field` is `z.ZodTypeAny` (intentionally loose); call sites pass the\n * value type from `configSchema.shape[key]`.\n */\nexport function isSensitiveField(field: z.ZodTypeAny): boolean {\n const description = field.description;\n return typeof description === 'string' && description.trimStart().startsWith(SENSITIVE_FIELD_MARKER);\n}\n\n/**\n * Parsed `@action` annotation extracted from a field's `description`.\n */\nexport interface ActionAnnotation {\n /** Visible button label, e.g. \"Test connection\". */\n label: string;\n /** HTTP verb of the plugin endpoint to call. */\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n /** Path relative to `/api/v2/plugins/<name>/`, with leading slash. */\n path: string;\n}\n\n/**\n * Parse an `@action` annotation off a field, or return null if absent.\n *\n * Format: `@action \"<label>\" <METHOD> <path>`\n * e.g. `@action \"Test connection\" POST /test`\n *\n * The label may include spaces when wrapped in double quotes; the\n * method is one of `GET` / `POST` / `PUT` / `DELETE`; the path begins\n * with `/`.\n */\nexport function getActionAnnotation(field: z.ZodTypeAny): ActionAnnotation | null {\n const description = field.description;\n if (typeof description !== 'string') return null;\n const trimmed = description.trimStart();\n if (!trimmed.startsWith(ACTION_FIELD_MARKER)) return null;\n\n const rest = trimmed.slice(ACTION_FIELD_MARKER.length).trimStart();\n // `\"<label>\" <METHOD> <path>`\n const match = rest.match(/^\"([^\"]+)\"\\s+(GET|POST|PUT|DELETE)\\s+(\\/\\S*)/);\n if (!match) return null;\n\n const [, label, method, path] = match;\n return { label, method: method as ActionAnnotation['method'], path };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsBO,IAAM,yBAAyB;AAa/B,IAAM,sBAAsB;AAQ5B,SAAS,iBAAiB,OAA8B;AAC7D,QAAM,cAAc,MAAM;AAC1B,SAAO,OAAO,gBAAgB,YAAY,YAAY,UAAU,EAAE,WAAW,sBAAsB;AACrG;AAwBO,SAAS,oBAAoB,OAA8C;AAChF,QAAM,cAAc,MAAM;AAC1B,MAAI,OAAO,gBAAgB,SAAU,QAAO;AAC5C,QAAM,UAAU,YAAY,UAAU;AACtC,MAAI,CAAC,QAAQ,WAAW,mBAAmB,EAAG,QAAO;AAErD,QAAM,OAAO,QAAQ,MAAM,oBAAoB,MAAM,EAAE,UAAU;AAEjE,QAAM,QAAQ,KAAK,MAAM,8CAA8C;AACvE,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,CAAC,EAAE,OAAO,QAAQ,IAAI,IAAI;AAChC,SAAO,EAAE,OAAO,QAA8C,KAAK;AACrE;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/schema-markers.ts"],"sourcesContent":["/**\n * @crowi/plugin-api — type-only contract for Crowi 2.0 plugins.\n *\n * Plugins author against this package. The runtime (@crowi/server) loads\n * plugins listed in `crowi.config.json`, calls each plugin's\n * `register*` callbacks, and routes all the side effects (storage,\n * search, auth, notifications) through the typed registries declared\n * here.\n *\n * For the design rationale see `docs/rfcs/0001-plugin-architecture.md`\n * in the Crowi monorepo.\n */\n\nexport type { CrowiPlugin } from './plugin';\n\nexport type { PluginContext, AppInfo, PageMetadataAccessor, PluginCrypto, PluginLogger } from './context';\n\nexport type { StorageDriver, StorageRegistry, StoragePutMeta, StoragePutResult } from './registries/storage';\n\nexport type {\n SearchDriver,\n SearchRegistry,\n SearchableDoc,\n SearchQuery,\n SearchQueryViewer,\n SearchQueryGrants,\n SearchPageType,\n SearchHits,\n SearchHit,\n} from './registries/search';\n\nexport type { AuthDriver, AuthRegistry, AuthProfile, AuthVerifyResult } from './registries/auth';\n\nexport type { NotifierDriver, NotifierRegistry, NotificationPayload } from './registries/notifier';\n\nexport type { MailSender, MailSenderRegistry, EmailMessage } from './registries/mail';\n\nexport type {\n RendererRegistry,\n NodeRenderer,\n CodeBlockRenderer,\n CodeBlockInfo,\n EmbedRenderer,\n EmbedInput,\n EmbedFragment,\n UrlInlineExpansionRule,\n InlineExpansion,\n RenderContext,\n RenderPhase,\n RenderResult,\n RenderError,\n Reservation,\n CacheStorage,\n ScopedCacheStorage,\n CacheKey,\n CacheEntry,\n AuthContext,\n} from './renderer';\n\nexport type { EventBus, PluginEvents } from './events';\n\nexport type { PluginRouterScope, PluginRouteHandler, PluginRouteMethod, PluginRouteOptions } from './routes';\n\nexport { SENSITIVE_FIELD_MARKER, ACTION_FIELD_MARKER, isSensitiveField, getActionAnnotation } from './schema-markers';\n","import type { z } from 'zod/v3';\n\n/**\n * `configSchema` description-string markers.\n *\n * The admin UI walks the schema and looks at each field's\n * `description` (set via `z.string().describe('@sensitive ...')`). A\n * description starting with one of these marker tokens unlocks special\n * UI behaviour without forcing every field to declare a custom Zod\n * type.\n */\n\n/**\n * Marker that flags a config field as sensitive (encrypted at rest).\n * Usage:\n *\n * z.string().describe('@sensitive AWS secret access key')\n *\n * The runtime auto-encrypts on write and decrypts on read, using the\n * same KeyProvider as core sensitive Config. The admin UI renders the\n * field via `<SecretField>` (saved badge / clear pending / undo).\n */\nexport const SENSITIVE_FIELD_MARKER = '@sensitive';\n\n/**\n * Marker that adds an action button next to a config field. Usage:\n *\n * z.string().describe('@action \"Test connection\" POST /test')\n *\n * The admin form renders a button with the given label that calls the\n * plugin's contributed endpoint at the given verb / path (relative to\n * `/api/v2/plugins/<name>/`). Useful for \"Test connection\",\n * \"Authorise with Google\", etc. without forcing every plugin to ship\n * its own React component.\n */\nexport const ACTION_FIELD_MARKER = '@action';\n\n/**\n * True if the schema field is marked `@sensitive`.\n *\n * `field` is `z.ZodTypeAny` (intentionally loose); call sites pass the\n * value type from `configSchema.shape[key]`.\n */\nexport function isSensitiveField(field: z.ZodTypeAny): boolean {\n const description = field.description;\n return typeof description === 'string' && description.trimStart().startsWith(SENSITIVE_FIELD_MARKER);\n}\n\n/**\n * Parsed `@action` annotation extracted from a field's `description`.\n */\nexport interface ActionAnnotation {\n /** Visible button label, e.g. \"Test connection\". */\n label: string;\n /** HTTP verb of the plugin endpoint to call. */\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n /** Path relative to `/api/v2/plugins/<name>/`, with leading slash. */\n path: string;\n}\n\n/**\n * Parse an `@action` annotation off a field, or return null if absent.\n *\n * Format: `@action \"<label>\" <METHOD> <path>`\n * e.g. `@action \"Test connection\" POST /test`\n *\n * The label may include spaces when wrapped in double quotes; the\n * method is one of `GET` / `POST` / `PUT` / `DELETE`; the path begins\n * with `/`.\n */\nexport function getActionAnnotation(field: z.ZodTypeAny): ActionAnnotation | null {\n const description = field.description;\n if (typeof description !== 'string') return null;\n const trimmed = description.trimStart();\n if (!trimmed.startsWith(ACTION_FIELD_MARKER)) return null;\n\n const rest = trimmed.slice(ACTION_FIELD_MARKER.length).trimStart();\n // `\"<label>\" <METHOD> <path>`\n const match = rest.match(/^\"([^\"]+)\"\\s+(GET|POST|PUT|DELETE)\\s+(\\/\\S*)/);\n if (!match) return null;\n\n const [, label, method, path] = match;\n return { label, method: method as ActionAnnotation['method'], path };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsBO,IAAM,yBAAyB;AAa/B,IAAM,sBAAsB;AAQ5B,SAAS,iBAAiB,OAA8B;AAC7D,QAAM,cAAc,MAAM;AAC1B,SAAO,OAAO,gBAAgB,YAAY,YAAY,UAAU,EAAE,WAAW,sBAAsB;AACrG;AAwBO,SAAS,oBAAoB,OAA8C;AAChF,QAAM,cAAc,MAAM;AAC1B,MAAI,OAAO,gBAAgB,SAAU,QAAO;AAC5C,QAAM,UAAU,YAAY,UAAU;AACtC,MAAI,CAAC,QAAQ,WAAW,mBAAmB,EAAG,QAAO;AAErD,QAAM,OAAO,QAAQ,MAAM,oBAAoB,MAAM,EAAE,UAAU;AAEjE,QAAM,QAAQ,KAAK,MAAM,8CAA8C;AACvE,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,CAAC,EAAE,OAAO,QAAQ,IAAI,IAAI;AAChC,SAAO,EAAE,OAAO,QAA8C,KAAK;AACrE;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crowi/plugin-api",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
4
|
"description": "Type-only contract for Crowi 2.0 plugins. See docs/rfcs/0001-plugin-architecture.md.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/crowi/crowi.git",
|
|
8
|
+
"directory": "packages/plugin-api"
|
|
9
|
+
},
|
|
5
10
|
"main": "dist/index.js",
|
|
6
11
|
"module": "dist/index.mjs",
|
|
7
12
|
"types": "dist/index.d.ts",
|
|
@@ -20,10 +25,12 @@
|
|
|
20
25
|
"access": "public"
|
|
21
26
|
},
|
|
22
27
|
"peerDependencies": {
|
|
23
|
-
"
|
|
28
|
+
"hono": "^4.12.25",
|
|
29
|
+
"zod": "^4"
|
|
24
30
|
},
|
|
25
31
|
"devDependencies": {
|
|
26
32
|
"@types/node": "^24",
|
|
33
|
+
"hono": "^4.12.25",
|
|
27
34
|
"tsup": "^8.3.5",
|
|
28
35
|
"typescript": "^5.8.3",
|
|
29
36
|
"zod": "^4.4.3",
|