@smartdoca/plugin-sdk 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/dist/LICENSE +661 -0
- package/dist/LICENSING.md +57 -0
- package/dist/LICENSING.zh-CN.md +48 -0
- package/dist/ai.d.ts +27 -0
- package/dist/ai.js +2 -0
- package/dist/files.d.ts +3 -0
- package/dist/files.js +4 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +342 -0
- package/dist/knowledge.d.ts +3 -0
- package/dist/knowledge.js +3 -0
- package/dist/platform.d.ts +176 -0
- package/dist/platform.js +7 -0
- package/dist/search.d.ts +20 -0
- package/dist/search.js +2 -0
- package/dist/testing.d.ts +20 -0
- package/dist/testing.js +57 -0
- package/dist/web.d.ts +34 -0
- package/dist/web.js +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type { JsonObject, MaybePromise } from "@smartdoca/plugin-contracts";
|
|
2
|
+
export interface PluginPrincipal {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly displayName: string;
|
|
5
|
+
readonly publicId: string;
|
|
6
|
+
readonly admin: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface PluginRequestContext {
|
|
9
|
+
readonly requestId: string;
|
|
10
|
+
readonly principal: PluginPrincipal;
|
|
11
|
+
readonly signal: AbortSignal;
|
|
12
|
+
}
|
|
13
|
+
export interface PluginUser extends PluginPrincipal {
|
|
14
|
+
readonly login: string;
|
|
15
|
+
readonly status: string;
|
|
16
|
+
readonly createdAt: string;
|
|
17
|
+
readonly profile: JsonObject;
|
|
18
|
+
readonly profileRevision: number;
|
|
19
|
+
readonly contacts: readonly {
|
|
20
|
+
kind: string;
|
|
21
|
+
value: string;
|
|
22
|
+
verified: boolean;
|
|
23
|
+
}[];
|
|
24
|
+
}
|
|
25
|
+
export interface DirectoryUser {
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly display_name: string;
|
|
28
|
+
readonly public_id: string | null;
|
|
29
|
+
readonly avatar: string | null;
|
|
30
|
+
readonly avatar_asset_id: string | null;
|
|
31
|
+
}
|
|
32
|
+
export interface UsersServiceV1 {
|
|
33
|
+
/** Trusted server task revalidation; does not grant access to business resources. */
|
|
34
|
+
status(id: string): Promise<{
|
|
35
|
+
readonly id: string;
|
|
36
|
+
readonly status: string;
|
|
37
|
+
} | null>;
|
|
38
|
+
get(context: PluginRequestContext, id: string): Promise<PluginUser | null>;
|
|
39
|
+
/** Trusted server-only inventory for initial reconciliation; never expose directly to browsers. */
|
|
40
|
+
list(input?: {
|
|
41
|
+
after?: string;
|
|
42
|
+
limit?: number;
|
|
43
|
+
}): Promise<{
|
|
44
|
+
items: readonly PluginUser[];
|
|
45
|
+
cursor: string | null;
|
|
46
|
+
}>;
|
|
47
|
+
search(context: PluginRequestContext, query: string): Promise<readonly DirectoryUser[]>;
|
|
48
|
+
}
|
|
49
|
+
export interface PluginResource {
|
|
50
|
+
readonly pluginId: string;
|
|
51
|
+
readonly type: string;
|
|
52
|
+
readonly id: string;
|
|
53
|
+
}
|
|
54
|
+
export interface PermissionSource {
|
|
55
|
+
readonly pluginId: string;
|
|
56
|
+
readonly resourceType: string;
|
|
57
|
+
authorize(principalId: string, resourceId: string, action: string): Promise<boolean>;
|
|
58
|
+
}
|
|
59
|
+
export interface DirectoryRelation {
|
|
60
|
+
readonly userId: string;
|
|
61
|
+
readonly relationId: string;
|
|
62
|
+
readonly revision: string;
|
|
63
|
+
}
|
|
64
|
+
export interface DirectorySource {
|
|
65
|
+
readonly id: string;
|
|
66
|
+
readonly schemaVersion: 1;
|
|
67
|
+
/** Indexed current relations from the plugin's own database. */
|
|
68
|
+
related(principalId: string, input: {
|
|
69
|
+
cursor: string | null;
|
|
70
|
+
limit: number;
|
|
71
|
+
signal: AbortSignal;
|
|
72
|
+
}): Promise<{
|
|
73
|
+
items: readonly DirectoryRelation[];
|
|
74
|
+
cursor: string | null;
|
|
75
|
+
}>;
|
|
76
|
+
/** Recheck candidates against current sharing, returning only still-visible user IDs. */
|
|
77
|
+
verify(principalId: string, candidates: readonly DirectoryRelation[], signal: AbortSignal): Promise<readonly string[]>;
|
|
78
|
+
}
|
|
79
|
+
export interface PermissionsServiceV1 {
|
|
80
|
+
register(source: PermissionSource): () => void;
|
|
81
|
+
registerDirectory(source: DirectorySource): () => void;
|
|
82
|
+
authorize(context: PluginRequestContext, resource: PluginResource, action: string): Promise<boolean>;
|
|
83
|
+
}
|
|
84
|
+
export interface PluginHttpRequest extends PluginRequestContext {
|
|
85
|
+
readonly params: Readonly<Record<string, string>>;
|
|
86
|
+
readonly query: Readonly<Record<string, unknown>>;
|
|
87
|
+
readonly body: unknown;
|
|
88
|
+
readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
89
|
+
readonly rawBody: Uint8Array;
|
|
90
|
+
}
|
|
91
|
+
export interface PluginHttpResponse {
|
|
92
|
+
status(code: number): PluginHttpResponse;
|
|
93
|
+
header(name: string, value: string | readonly string[]): PluginHttpResponse;
|
|
94
|
+
redirect(location: string, code?: 302 | 303 | 307 | 308): void;
|
|
95
|
+
}
|
|
96
|
+
export type PluginExternalRequest = Omit<PluginHttpRequest, "principal"> & {
|
|
97
|
+
readonly principal: null;
|
|
98
|
+
};
|
|
99
|
+
interface PluginRouteBase {
|
|
100
|
+
readonly method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
101
|
+
readonly path: string;
|
|
102
|
+
/** Defaults to 1 MiB. Trusted plugins may explicitly request up to 32 MiB. */
|
|
103
|
+
readonly bodyLimit?: number;
|
|
104
|
+
readonly schema?: {
|
|
105
|
+
readonly body?: JsonObject;
|
|
106
|
+
readonly querystring?: JsonObject;
|
|
107
|
+
readonly params?: JsonObject;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export type PluginRoute = PluginRouteBase & ({
|
|
111
|
+
readonly auth?: "session";
|
|
112
|
+
readonly admin?: boolean;
|
|
113
|
+
handle(request: PluginHttpRequest, response: PluginHttpResponse): MaybePromise<unknown>;
|
|
114
|
+
} | {
|
|
115
|
+
readonly auth: "external";
|
|
116
|
+
/** Verify OAuth state or webhook signature. No host session is implied. */
|
|
117
|
+
verify(request: PluginExternalRequest): MaybePromise<boolean>;
|
|
118
|
+
handle(request: PluginExternalRequest, response: PluginHttpResponse): MaybePromise<unknown>;
|
|
119
|
+
});
|
|
120
|
+
export interface HttpServiceV1 {
|
|
121
|
+
/** Trusted deployment origin, never constructed from request headers. */
|
|
122
|
+
callbackUrl(pluginId: string, path: string): string;
|
|
123
|
+
/** External routes must explicitly verify their external identity. */
|
|
124
|
+
register(pluginId: string, routes: readonly PluginRoute[]): Promise<() => Promise<void>>;
|
|
125
|
+
}
|
|
126
|
+
export interface OperationPolicy {
|
|
127
|
+
readonly id: string;
|
|
128
|
+
check(input: {
|
|
129
|
+
readonly principalId: string;
|
|
130
|
+
readonly action: string;
|
|
131
|
+
readonly facts: JsonObject;
|
|
132
|
+
}): Promise<void>;
|
|
133
|
+
}
|
|
134
|
+
export interface PoliciesServiceV1 {
|
|
135
|
+
register(policy: OperationPolicy): () => void;
|
|
136
|
+
check(principalId: string, action: string, facts?: JsonObject): Promise<void>;
|
|
137
|
+
}
|
|
138
|
+
export declare const usersServiceToken: import("@smartdoca/plugin-contracts").ServiceToken<UsersServiceV1>;
|
|
139
|
+
export declare const permissionsServiceToken: import("@smartdoca/plugin-contracts").ServiceToken<PermissionsServiceV1>;
|
|
140
|
+
export declare const httpServiceToken: import("@smartdoca/plugin-contracts").ServiceToken<HttpServiceV1>;
|
|
141
|
+
export declare const policiesServiceToken: import("@smartdoca/plugin-contracts").ServiceToken<PoliciesServiceV1>;
|
|
142
|
+
export interface PluginIntegrationEvent {
|
|
143
|
+
readonly sequence: number;
|
|
144
|
+
readonly id: string;
|
|
145
|
+
readonly type: string;
|
|
146
|
+
readonly payload: JsonObject;
|
|
147
|
+
readonly createdAt: string;
|
|
148
|
+
}
|
|
149
|
+
export interface EventsServiceV1 {
|
|
150
|
+
/** Durable ordered stream. Persist the last processed sequence in the plugin data store. */
|
|
151
|
+
read(after: number, limit?: number): Promise<readonly PluginIntegrationEvent[]>;
|
|
152
|
+
}
|
|
153
|
+
export declare const eventsServiceToken: import("@smartdoca/plugin-contracts").ServiceToken<EventsServiceV1>;
|
|
154
|
+
export interface PluginNotificationInput {
|
|
155
|
+
readonly recipientId: string;
|
|
156
|
+
readonly key: string;
|
|
157
|
+
readonly title: string;
|
|
158
|
+
readonly body: string;
|
|
159
|
+
/** Local application route, e.g. /mail/inbox?message=123. No origin or hash. */
|
|
160
|
+
readonly path: string;
|
|
161
|
+
readonly resource: {
|
|
162
|
+
readonly type: string;
|
|
163
|
+
readonly id: string;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
export interface NotificationsServiceV1 {
|
|
167
|
+
publish(pluginId: string, input: PluginNotificationInput): Promise<{
|
|
168
|
+
id: string;
|
|
169
|
+
}>;
|
|
170
|
+
withdraw(pluginId: string, input: {
|
|
171
|
+
recipientId: string;
|
|
172
|
+
key: string;
|
|
173
|
+
}): Promise<void>;
|
|
174
|
+
}
|
|
175
|
+
export declare const notificationsServiceToken: import("@smartdoca/plugin-contracts").ServiceToken<NotificationsServiceV1>;
|
|
176
|
+
export {};
|
package/dist/platform.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { defineService } from "./index.js";
|
|
2
|
+
export const usersServiceToken = defineService("users.v1");
|
|
3
|
+
export const permissionsServiceToken = defineService("permissions.v1");
|
|
4
|
+
export const httpServiceToken = defineService("http.v1");
|
|
5
|
+
export const policiesServiceToken = defineService("policies.v1");
|
|
6
|
+
export const eventsServiceToken = defineService("events.v1");
|
|
7
|
+
export const notificationsServiceToken = defineService("notifications.v1");
|
package/dist/search.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PluginRequestContext } from "./platform.js";
|
|
2
|
+
import type { SearchSource, SearchProjection, SearchSourceDescriptor, FederatedSearchResult, SearchRebuildResult } from "@smartdoca/search-host";
|
|
3
|
+
export type * from "@smartdoca/search-host";
|
|
4
|
+
export interface PluginSearchContext {
|
|
5
|
+
readonly principalId: string | null;
|
|
6
|
+
readonly signal?: AbortSignal;
|
|
7
|
+
}
|
|
8
|
+
export interface PluginSearchService {
|
|
9
|
+
register(source: SearchSource<PluginSearchContext>): () => void;
|
|
10
|
+
upsert(source: SearchSourceDescriptor, projections: readonly SearchProjection[]): Promise<void>;
|
|
11
|
+
delete(source: SearchSourceDescriptor, ids: readonly string[]): Promise<void>;
|
|
12
|
+
rebuild(source: SearchSourceDescriptor, signal?: AbortSignal): Promise<SearchRebuildResult>;
|
|
13
|
+
query(context: PluginRequestContext, input: {
|
|
14
|
+
source: SearchSourceDescriptor;
|
|
15
|
+
query: string;
|
|
16
|
+
offset?: number;
|
|
17
|
+
limit?: number;
|
|
18
|
+
}): Promise<FederatedSearchResult>;
|
|
19
|
+
}
|
|
20
|
+
export declare const searchServiceToken: import("@smartdoca/plugin-contracts").ServiceToken<PluginSearchService>;
|
package/dist/search.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type DocaPlugin, type JsonObject, type ServiceToken } from "@smartdoca/plugin-contracts";
|
|
2
|
+
export interface PluginContractHarnessOptions {
|
|
3
|
+
readonly config?: JsonObject;
|
|
4
|
+
readonly services?: readonly {
|
|
5
|
+
readonly token: ServiceToken<any>;
|
|
6
|
+
readonly value: any;
|
|
7
|
+
}[];
|
|
8
|
+
}
|
|
9
|
+
export interface PluginContractHarnessResult {
|
|
10
|
+
readonly pluginId: string;
|
|
11
|
+
readonly phases: readonly string[];
|
|
12
|
+
readonly disposed: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Runs one plugin through the public lifecycle without application globals.
|
|
16
|
+
* Domain harnesses can provide fake capability Services and inspect them after
|
|
17
|
+
* this function returns. Provider/contribution collisions and missing required
|
|
18
|
+
* injections use the same SDK stores as production.
|
|
19
|
+
*/
|
|
20
|
+
export declare function runPluginContractHarness<Config extends JsonObject = JsonObject>(plugin: DocaPlugin<Config>, options?: PluginContractHarnessOptions): Promise<PluginContractHarnessResult>;
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Context, ContributionStore, PluginContext } from "./index.js";
|
|
2
|
+
import { validatePluginConfig, validatePluginManifest, } from "@smartdoca/plugin-contracts";
|
|
3
|
+
/**
|
|
4
|
+
* Runs one plugin through the public lifecycle without application globals.
|
|
5
|
+
* Domain harnesses can provide fake capability Services and inspect them after
|
|
6
|
+
* this function returns. Provider/contribution collisions and missing required
|
|
7
|
+
* injections use the same SDK stores as production.
|
|
8
|
+
*/
|
|
9
|
+
export async function runPluginContractHarness(plugin, options = {}) {
|
|
10
|
+
const manifest = validatePluginManifest(plugin.manifest);
|
|
11
|
+
const config = validatePluginConfig(manifest, options.config);
|
|
12
|
+
const root = new Context();
|
|
13
|
+
const scope = await root.createFiberScope(`testing.${manifest.id}`);
|
|
14
|
+
const store = new ContributionStore();
|
|
15
|
+
const contributions = {
|
|
16
|
+
register(point, id, value) {
|
|
17
|
+
return store.register(scope, manifest.id, point, id, value);
|
|
18
|
+
},
|
|
19
|
+
get(point, id) {
|
|
20
|
+
return store.get(point, id);
|
|
21
|
+
},
|
|
22
|
+
list(point) {
|
|
23
|
+
return store.list(point);
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
const context = new PluginContext(scope, manifest, config, contributions);
|
|
27
|
+
const phases = [];
|
|
28
|
+
let lifecycleStarted = false;
|
|
29
|
+
try {
|
|
30
|
+
for (const service of options.services ?? [])
|
|
31
|
+
root.provide(service.token, service.value);
|
|
32
|
+
lifecycleStarted = true;
|
|
33
|
+
phases.push("discover");
|
|
34
|
+
await plugin.discover?.(context);
|
|
35
|
+
for (const token of plugin.injections?.required ?? [])
|
|
36
|
+
context.inject(token);
|
|
37
|
+
phases.push("initialize");
|
|
38
|
+
await plugin.initialize?.(context);
|
|
39
|
+
phases.push("mount");
|
|
40
|
+
await plugin.mount?.(context);
|
|
41
|
+
phases.push("ready");
|
|
42
|
+
await plugin.ready?.(context);
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
if (lifecycleStarted) {
|
|
46
|
+
phases.push("dispose");
|
|
47
|
+
await plugin.dispose?.(context);
|
|
48
|
+
}
|
|
49
|
+
await scope.dispose();
|
|
50
|
+
await root.dispose();
|
|
51
|
+
}
|
|
52
|
+
return Object.freeze({
|
|
53
|
+
pluginId: manifest.id,
|
|
54
|
+
phases: Object.freeze(phases),
|
|
55
|
+
disposed: scope.disposed,
|
|
56
|
+
});
|
|
57
|
+
}
|
package/dist/web.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type * from "@smartdoca/web-plugin-registry";
|
|
2
|
+
export interface PluginFileReference {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly mime: string;
|
|
6
|
+
readonly size: number;
|
|
7
|
+
}
|
|
8
|
+
export interface PluginFilePickerProps {
|
|
9
|
+
close(): void;
|
|
10
|
+
select?(file: PluginFileReference): void | Promise<void>;
|
|
11
|
+
selectFolder?(folder: {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
}): void | Promise<void>;
|
|
15
|
+
accept?(file: PluginFileReference): boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Host-injected React is the only supported renderer instance. */
|
|
18
|
+
export interface PluginWebHost<ReactRuntime = unknown, Component = (props: PluginFilePickerProps) => any> {
|
|
19
|
+
readonly React: ReactRuntime;
|
|
20
|
+
readonly apiBase: string;
|
|
21
|
+
useEnvironment(): {
|
|
22
|
+
locale: "zh" | "en";
|
|
23
|
+
theme: "light" | "soft";
|
|
24
|
+
};
|
|
25
|
+
navigate(path: string): void;
|
|
26
|
+
toast(message: string, tone?: "info" | "success" | "warning" | "error"): void;
|
|
27
|
+
confirm(message: string): Promise<boolean>;
|
|
28
|
+
request<T>(path: string, init?: RequestInit): Promise<T>;
|
|
29
|
+
readonly FilePicker: Component;
|
|
30
|
+
}
|
|
31
|
+
export interface PluginHttpError extends Error {
|
|
32
|
+
readonly status: number;
|
|
33
|
+
readonly requestId?: string;
|
|
34
|
+
}
|
package/dist/web.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@smartdoca/plugin-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "AGPL-3.0-only",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./testing": {
|
|
12
|
+
"types": "./dist/testing.d.ts",
|
|
13
|
+
"import": "./dist/testing.js"
|
|
14
|
+
},
|
|
15
|
+
"./platform": {
|
|
16
|
+
"types": "./dist/platform.d.ts",
|
|
17
|
+
"import": "./dist/platform.js"
|
|
18
|
+
},
|
|
19
|
+
"./files": {
|
|
20
|
+
"types": "./dist/files.d.ts",
|
|
21
|
+
"import": "./dist/files.js"
|
|
22
|
+
},
|
|
23
|
+
"./ai": {
|
|
24
|
+
"types": "./dist/ai.d.ts",
|
|
25
|
+
"import": "./dist/ai.js"
|
|
26
|
+
},
|
|
27
|
+
"./search": {
|
|
28
|
+
"types": "./dist/search.d.ts",
|
|
29
|
+
"import": "./dist/search.js"
|
|
30
|
+
},
|
|
31
|
+
"./knowledge": {
|
|
32
|
+
"types": "./dist/knowledge.d.ts",
|
|
33
|
+
"import": "./dist/knowledge.js"
|
|
34
|
+
},
|
|
35
|
+
"./web": {
|
|
36
|
+
"types": "./dist/web.d.ts",
|
|
37
|
+
"import": "./dist/web.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
42
|
+
"@smartdoca/plugin-contracts": "0.1.0",
|
|
43
|
+
"@smartdoca/files-capability": "0.1.0",
|
|
44
|
+
"@smartdoca/search-host": "0.1.0",
|
|
45
|
+
"@smartdoca/knowledge-capability": "0.1.0",
|
|
46
|
+
"@smartdoca/web-plugin-registry": "0.1.0"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist"
|
|
50
|
+
],
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
}
|
|
54
|
+
}
|