@ps-generator-bridge/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.
@@ -0,0 +1,457 @@
1
+ import { L as LayerSpec, e as PsPhotoshopProxy } from './PsPhotoshopProxy-BjnvziWn.js';
2
+ export { A as AnchorPosition, w as AnchorPositionValue, x as BlendMode, y as BlendModeValue, D as DocumentMode, z as DocumentModeValue, C as ElementPlacement, F as ElementPlacementValue, G as LayerKind, H as LayerKindValue, J as PhotoshopApp, K as PhotoshopDocument, N as PhotoshopLayer, O as PhotoshopLayers, Q as PhotoshopSelection, T as PsBoundsTuple, U as PsColor, V as SaveOptions, X as SaveOptionsValue, Y as SelectionType, Z as SelectionTypeValue } from './PsPhotoshopProxy-BjnvziWn.js';
3
+
4
+ type PsBounds = {
5
+ left: number;
6
+ right: number;
7
+ top: number;
8
+ bottom: number;
9
+ };
10
+ type PsRect = {
11
+ x: number;
12
+ y: number;
13
+ width: number;
14
+ height: number;
15
+ };
16
+
17
+ /**
18
+ * The slice of JsxRunner a Plugin reaches through `plugin.jsx` (RFC 0003 /
19
+ * RFC 0005). The concrete JsxRunner `implements` this (and `forPlugin` returns a
20
+ * scoped view that also implements it), so the plugin contract can never drift
21
+ * from the implementation; the SDK re-exports it (via src/contract.ts) as the
22
+ * type of `PluginHost.jsx`. Excludes lifecycle (`init`) and the low-level pixmap
23
+ * channel (`openJSXFile`) — plugins only run jsx by name (or raw string).
24
+ *
25
+ * `execute` resolves against *this handle's own* jsx scope: the built-in `jsx/`
26
+ * tree for the host's root runner, or the plugin's own `jsx/` dir for the scoped
27
+ * view `plugin.jsx` returns. `executeBuiltin` always targets the built-in tree,
28
+ * so a plugin can reach a host domain (e.g. `Document/getDocumentInfo`) without
29
+ * knowing its own id or dir. `run` takes a raw script and is scope-independent.
30
+ */
31
+ interface JsxRunnerApi {
32
+ execute<T = unknown>(name: string, params?: Record<string, unknown>, sharedEngineSafe?: boolean): Promise<T>;
33
+ executeBuiltin<T = unknown>(name: string, params?: Record<string, unknown>, sharedEngineSafe?: boolean): Promise<T>;
34
+ run<T = unknown>(script: string): Promise<T>;
35
+ }
36
+
37
+ /**
38
+ * Layer/document bounds in pixels. The console logged this as `[Object]` (not
39
+ * expanded); this is the standard generator bounds shape.
40
+ */
41
+ interface Bounds {
42
+ top: number;
43
+ left: number;
44
+ bottom: number;
45
+ right: number;
46
+ }
47
+ /** One entry in `imageChanged.layers` — a layer touched by the change. */
48
+ interface ImageChangedLayer {
49
+ /** Layer id (stable across the session). */
50
+ id: number;
51
+ /** Present (true) when the layer's pixels changed. */
52
+ pixels?: boolean;
53
+ /** Present (true) when the layer was removed in this change. */
54
+ removed?: boolean;
55
+ /** Layer bounds; present on geometry/pixel changes. */
56
+ bounds?: Bounds;
57
+ }
58
+ /**
59
+ * Payload of the `imageChanged` event. Fields are a union of everything PS may
60
+ * send; only `version` / `timeStamp` / `count` / `id` are always present. A
61
+ * single event carries *either* metadata flags (`metaDataOnly`, `selection`),
62
+ * *or* `layers`, *or* document-level flags (`active` / `file` / `closed`).
63
+ */
64
+ interface ImageChangedEvent {
65
+ /** Generator protocol version, e.g. "1.6.1". */
66
+ version: string;
67
+ /** Seconds since epoch (float), e.g. 1782455135.936. */
68
+ timeStamp: number;
69
+ /** Per-document monotonically increasing change counter (resets per doc id). */
70
+ count: number;
71
+ /** Document id this change belongs to. */
72
+ id: number;
73
+ /** True on the first event for a doc / on activation. */
74
+ active?: boolean;
75
+ /** Document title or full path, e.g. "Test-恢复的.psd" or "C:\\...\\Test.psd". */
76
+ file?: string;
77
+ /** True when the document was closed. */
78
+ closed?: boolean;
79
+ /** True when only metadata changed (no pixel/layer body). */
80
+ metaDataOnly?: boolean;
81
+ /** Selected layer indices; empty array when the selection is cleared. */
82
+ selection?: number[];
83
+ /** Layers touched by this change (pixel edits, bounds, removals). */
84
+ layers?: ImageChangedLayer[];
85
+ }
86
+ /**
87
+ * Map of Photoshop event name -> payload type passed to the listener. Shapes
88
+ * marked "observed" were confirmed from live PS output; the rest are inferred
89
+ * from the generator protocol docs.
90
+ */
91
+ interface PhotoshopEventMap {
92
+ /** [workspace display name] (inferred). */
93
+ workspaceChanged: string;
94
+ /** Tool name, e.g. "paintbrushTool" / "moveTool" (observed). */
95
+ toolChanged: string;
96
+ /** "enter" | "exit" (inferred). */
97
+ quickMaskStateChanged: string;
98
+ /** Document id (observed). */
99
+ documentChanged: number;
100
+ /** Document id of the closed document (observed). */
101
+ closedDocument: number;
102
+ /** Document id (inferred). */
103
+ newDocumentViewCreated: number;
104
+ /** Document id (inferred). */
105
+ activeViewChanged: number;
106
+ /** Document id of the now-current document (observed). */
107
+ currentDocumentChanged: number;
108
+ /** Color as 6-character hex value (inferred). */
109
+ backgroundColorChanged: string;
110
+ /** Color as 6-character hex value (inferred). */
111
+ foregroundColorChanged: string;
112
+ /** Image/document change descriptor (observed). */
113
+ imageChanged: ImageChangedEvent;
114
+ }
115
+ /** Listener for a given Photoshop event key. */
116
+ type PhotoshopEventListener<K extends keyof PhotoshopEventMap> = (payload: PhotoshopEventMap[K]) => void;
117
+ /**
118
+ * Listen-only typed surface a Plugin reaches through `plugin.events` /
119
+ * `this.events`. `EventManager` (an `EventEmitter`) `implements` this; `emit` is
120
+ * deliberately excluded — a Plugin subscribes, it never dispatches Photoshop
121
+ * events.
122
+ */
123
+ interface PhotoshopEvents {
124
+ on<K extends keyof PhotoshopEventMap>(event: K, listener: PhotoshopEventListener<K>): this;
125
+ once<K extends keyof PhotoshopEventMap>(event: K, listener: PhotoshopEventListener<K>): this;
126
+ off<K extends keyof PhotoshopEventMap>(event: K, listener: PhotoshopEventListener<K>): this;
127
+ }
128
+
129
+ /**
130
+ * Action-domain feature module (ADR 0006). Exposes the `Action:*` WS Request
131
+ * methods, each backed by a packaged `jsx/Action/<name>.jsx` script run through
132
+ * the plugin's `JsxRunner` (ADR 0008). A jsx failure follows the `"Error:"`
133
+ * prefix convention: `JsxRunner` throws, and `Registry.dispatch` turns it into an
134
+ * INTERNAL response — the methods themselves do not catch.
135
+ *
136
+ * Migrated from LightAi's `ActionManager`. The `@McpTool` metadata did not carry
137
+ * over (no MCP runtime here — only the `@ws` WS path), but the human-facing
138
+ * descriptions are preserved as method JSDoc.
139
+ */
140
+ /**
141
+ * The Action module surface a Plugin reaches through `plugin.modules.action`
142
+ * (RFC 0003). `ActionModule implements` this; the SDK re-exports it via
143
+ * src/contract.ts.
144
+ */
145
+ interface ActionModuleApi {
146
+ autoCutout(): Promise<boolean>;
147
+ removeBackground(): Promise<{
148
+ success: boolean;
149
+ }>;
150
+ }
151
+
152
+ type PsDocument = {
153
+ id: number;
154
+ name: string;
155
+ width: number;
156
+ height: number;
157
+ resolution: number;
158
+ isDirty: boolean;
159
+ filePath?: string;
160
+ };
161
+ /**
162
+ * The Document module surface a Plugin reaches through `plugin.modules.document`
163
+ * (RFC 0003). `DocumentModule implements` this; the SDK re-exports it via
164
+ * src/contract.ts.
165
+ */
166
+ interface DocumentModuleApi {
167
+ getCurrentDocument(): Promise<PsDocument>;
168
+ exportDocument(params: Record<string, unknown>): Promise<unknown>;
169
+ saveDocument(params: {
170
+ savePath?: string;
171
+ }): Promise<unknown>;
172
+ }
173
+
174
+ /**
175
+ * Single-layer pixmap export + preview, isolated from the buggy
176
+ * `generator.getPixmap` (generator-core's version omits the
177
+ * `includeAdjustors/Children/ClipBase/Clipped` flags and passes a stray
178
+ * `settings.thread` field). `getPixmap` here is a faithful port of
179
+ * LightAi's `LayerManager.getPixmap` (index.ts:364-482): it calls the plugin's
180
+ * own `Layer/getLayerPixmap.jsx` over the progress channel and collects the
181
+ * bounds + pixmap + ICC profile messages Photoshop streams back.
182
+ *
183
+ * Whole-document export is handled separately by generator-core's
184
+ * `getDocumentPixmap` (to be wired up later); this module only deals with
185
+ * explicit layer specs, which is what `Layer/getLayerPixmap.jsx` requires.
186
+ *
187
+ * Encoding (raw RGBA -> PNG) goes through `sharp`, externalized from the bundle
188
+ * and resolved from node_modules at runtime inside Photoshop's Node.
189
+ */
190
+ /**
191
+ * The Image module surface a Plugin reaches through `plugin.modules.image`
192
+ * (RFC 0003). `ImageModule implements` this; the SDK re-exports it via
193
+ * src/contract.ts. `settings` is widened to `Record<string, unknown>` so the
194
+ * plugin contract does not drag the generator-core `GetPixmapSettings` namespace
195
+ * into the SDK; `buffer` is `Uint8Array` (not `Buffer`) so the SDK stays
196
+ * Node-free.
197
+ */
198
+ interface ImageModuleApi {
199
+ exportImage(options: {
200
+ documentId?: number;
201
+ layerSpec: LayerSpec;
202
+ settings?: Record<string, unknown>;
203
+ }): Promise<ImageResult>;
204
+ getPreview(options: {
205
+ documentId?: number;
206
+ layerSpec: number;
207
+ }): Promise<ImageResult>;
208
+ exportDocument(options: {
209
+ documentId?: number;
210
+ settings?: Record<string, unknown>;
211
+ }): Promise<ImageResult>;
212
+ }
213
+ /**
214
+ * Result of an image export / preview: PNG bytes plus geometry metadata. The
215
+ * bytes are typed as `Uint8Array` (the implementation returns a `Buffer`, which
216
+ * is a `Uint8Array` subtype) so this type can cross into the Node-free SDK
217
+ * contract unchanged.
218
+ */
219
+ interface ImageResult {
220
+ buffer: Uint8Array;
221
+ bounds: PsBounds;
222
+ width: number;
223
+ height: number;
224
+ }
225
+
226
+ /**
227
+ * Plugin-facing COS contract (RFC 0008). The minimal slice modules and plugins
228
+ * reach through `plugin.cos`: upload in-memory bytes or a local file and get back
229
+ * a ready-to-use signed URL. Params use `Uint8Array`/path strings (never `Buffer`
230
+ * or the COS SDK's own types) so the SDK's re-export of this interface stays
231
+ * Node-free, mirroring how `ImageModuleApi` is exposed.
232
+ */
233
+ interface CosServiceApi {
234
+ /** Upload raw bytes, returning a signed URL. `name` labels the object key. */
235
+ uploadObject(data: Uint8Array, name?: string): Promise<string>;
236
+ /** Upload a local file by path, returning a signed URL. */
237
+ uploadFile(dir: string, name?: string): Promise<string>;
238
+ }
239
+
240
+ declare class PsLayer {
241
+ id: number;
242
+ index: number;
243
+ name: string;
244
+ type: number;
245
+ visible: boolean;
246
+ bounds: PsBounds;
247
+ rect: PsRect;
248
+ clip: boolean;
249
+ children?: PsLayer[];
250
+ constructor(init: Partial<PsLayer>);
251
+ }
252
+ /**
253
+ * The Layer module surface a Plugin reaches through `plugin.modules.layer`
254
+ * (RFC 0003). `LayerModule implements` this, so the plugin contract tracks the
255
+ * implementation by compiler force; the SDK re-exports it via src/contract.ts.
256
+ */
257
+ interface LayerModuleApi {
258
+ getLayerInfo(options?: {
259
+ id?: number;
260
+ index?: number;
261
+ getChildren?: boolean;
262
+ getGeneratorSettings?: boolean;
263
+ }): Promise<PsLayer>;
264
+ getLayerInfoByID(layerID: number, options?: {
265
+ getChildren: boolean;
266
+ }): Promise<PsLayer>;
267
+ getLayerInfoByIndex(layerIndex: number, options?: {
268
+ getChildren: boolean;
269
+ }): Promise<PsLayer>;
270
+ }
271
+
272
+ /**
273
+ * The plugin contract a Plugin depends on (RFC 0003) — a deliberately narrowed
274
+ * view of the server's `PsBridgeHost`. Exposes only what a Plugin needs: the JSX
275
+ * seam and the feature-module accessors (all typed by the generator's own
276
+ * contract interfaces), plus the listen-only event stream. Excludes
277
+ * broadcast/emit/server/the raw generator handle — a Plugin manages its own
278
+ * clients through BasePlugin.broadcast/send.
279
+ *
280
+ * This is the one hand-written piece of the plugin surface that stays in the
281
+ * SDK: it is a *curation* of the host, not a mirror of it. Its member types are
282
+ * imported (type-only) from the generator contract so they can never drift from
283
+ * the implementation. The server's `PsBridgeHost implements PluginHost`;
284
+ * external Plugins only ever see this interface, so they depend on the SDK alone
285
+ * at runtime, never on the server package.
286
+ */
287
+ interface PluginHost {
288
+ readonly jsx: JsxRunnerApi;
289
+ /** Feature modules, reached by short key (e.g. `plugin.modules.layer`). */
290
+ readonly modules: {
291
+ layer: LayerModuleApi;
292
+ document: DocumentModuleApi;
293
+ action: ActionModuleApi;
294
+ image: ImageModuleApi;
295
+ };
296
+ /** Typed, listen-only Photoshop event stream (lazy subscribe). */
297
+ readonly events: PhotoshopEvents;
298
+ /**
299
+ * Optional object-storage upload service (RFC 0008). Present only when the host
300
+ * has COS configured via the environment; undefined otherwise. A plugin guards
301
+ * on it: `if (this.plugin.cos) await this.plugin.cos.uploadObject(bytes)`.
302
+ */
303
+ readonly cos?: CosServiceApi;
304
+ }
305
+
306
+ /**
307
+ * Per-plugin client push bus (RFC 0004). BasePlugin.broadcast/send delegate to
308
+ * this; the server's per-plugin assembler attaches a concrete adapter backed by
309
+ * the plugin's own ClientStore after construction. Defined in the SDK so
310
+ * BasePlugin can be implemented without depending on the server.
311
+ */
312
+ interface PluginClientBus {
313
+ /** Push an Event to every online client of this plugin. */
314
+ broadcast(type: string, data: unknown): void;
315
+ /** Push an Event to one client of this plugin (no-op if not connected). */
316
+ send(clientId: string, type: string, data: unknown): void;
317
+ }
318
+
319
+ /**
320
+ * Base class for plugins (ADR 0009 / RFC 0003). A plugin is the orchestration
321
+ * layer above feature modules: it composes one or more module calls (reached
322
+ * through `this.modules`, e.g. `this.modules.layer`) and exposes
323
+ * its own `@ws`/`@api` handlers, bootstrapped the same way as modules.
324
+ *
325
+ * Dependency direction is strictly downward: a plugin may depend on modules,
326
+ * never on sibling plugins and never in reverse (modules must not reach
327
+ * plugins). `plugin` is the abstract PluginHost interface — never the
328
+ * concrete server plugin — so a plugin depends only on this SDK subpath.
329
+ *
330
+ * `@ws`/`@api` names are written in full by the developer (e.g.
331
+ * `@ws("LayerOps:merge")`) — `bootstrap` does not inject or derive a namespace.
332
+ * Use a `Domain:action` prefix to keep names from colliding across modules and
333
+ * plugins (ADR 0006 convention); the assembler's `registerMethod` is a silent
334
+ * `Map.set`, so prefix uniqueness is the developer's responsibility.
335
+ *
336
+ * Client push: `broadcast`/`send` reach only this plugin's own online clients,
337
+ * via the PluginClientBus the server attaches after construction. A subclass
338
+ * may override `onConnect`/`onDisconnect` to react to its clients coming and
339
+ * going; the defaults are no-ops.
340
+ */
341
+ declare abstract class BasePlugin {
342
+ /** Stable URL identity of this plugin (read from the class's `static id`). */
343
+ readonly id: string;
344
+ /** The abstract plugin contract (never the concrete server plugin). */
345
+ protected readonly plugin: PluginHost;
346
+ private bus;
347
+ private _photoshop;
348
+ constructor(id: string, plugin: PluginHost);
349
+ /** Feature modules, reached by short key (shortcut for `this.plugin.modules`). */
350
+ protected get modules(): PluginHost["modules"];
351
+ /**
352
+ * The jsx runner scoped to this plugin's own `jsx/` dir (shortcut for
353
+ * `this.plugin.jsx`, RFC 0005). `jsx.execute("x")` runs `<pluginRoot>/jsx/x.jsx`;
354
+ * `jsx.executeBuiltin("Document/getDocumentInfo")` reaches the host's built-in
355
+ * tree.
356
+ */
357
+ protected get jsx(): JsxRunnerApi;
358
+ /**
359
+ * Typed, listen-only Photoshop event stream (shortcut for `this.plugin.events`).
360
+ * Subscriptions are lazy on the server side: the first `on`/`once` for an event
361
+ * subscribes upstream, and the last `off` unsubscribes.
362
+ */
363
+ protected get events(): PhotoshopEvents;
364
+ /**
365
+ * Photoshop DOM proxy, a typed object wrapper over `this.jsx`. Read and write
366
+ * the live document through `this.photoshop.app` / `this.photoshop.activeDocument`
367
+ * (e.g. `await this.photoshop.activeDocument.name`) instead of hand-writing
368
+ * ExtendScript. Lazily built once and backed by this plugin's own jsx runner;
369
+ * drop to `this.jsx.run(...)` for anything the proxy does not cover.
370
+ */
371
+ protected get photoshop(): PsPhotoshopProxy;
372
+ /**
373
+ * Attach the per-plugin client bus. Called by the server's assembler after
374
+ * construction and before `listen`, so `broadcast`/`send` are live by the time
375
+ * any handler can fire. Not part of the public Plugin authoring API.
376
+ */
377
+ _attachBus(bus: PluginClientBus): void;
378
+ /** Push an Event to every online client of this plugin. */
379
+ broadcast(type: string, data: unknown): void;
380
+ /** Push an Event to one client of this plugin (no-op if not connected). */
381
+ send(clientId: string, type: string, data: unknown): void;
382
+ /** Called after a client handshake registers with this plugin. Default no-op. */
383
+ onConnect(_clientId: string): void;
384
+ /** Called after a client socket is removed from this plugin. Default no-op. */
385
+ onDisconnect(_clientId: string): void;
386
+ }
387
+ /**
388
+ * Whether `S` is a class that extends BasePlugin. Uses the global brand rather
389
+ * than `instanceof` so it works when `S` came from a separately-bundled copy of
390
+ * this SDK (external plugins). The brand is inherited via the prototype chain,
391
+ * so direct and indirect subclasses both qualify.
392
+ */
393
+ declare function isBasePluginClass(S: unknown): boolean;
394
+
395
+ /**
396
+ * Minimal HTTP vocabulary for the @api decorator (ADR 0006 / RFC 0003). The SDK
397
+ * plugin subpath is platform-neutral and must not depend on fastify, so it
398
+ * declares its own string union; the server adapts it to fastify's HTTPMethods
399
+ * at the assembly boundary.
400
+ */
401
+ type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
402
+ /**
403
+ * A WS Request handler as seen by the plugin devkit (ADR 0006 open-ended
404
+ * contract). params/result are `unknown` at this layer; the SDK re-applies
405
+ * strong types for declared methods. `ctx` is opaque here — the server supplies
406
+ * a typed HandlerContext, and a handler accepting `unknown` accepts it.
407
+ */
408
+ type MethodHandler = (params: unknown, ctx: unknown) => Promise<unknown> | unknown;
409
+ /**
410
+ * An HTTP route handler as seen by the plugin devkit. request/reply are opaque
411
+ * (platform-neutral SDK); the server casts to fastify's request/reply at the
412
+ * assembly boundary.
413
+ */
414
+ type ApiHandler = (request: unknown, reply: unknown) => Promise<unknown> | unknown;
415
+ /** An HTTP route a module or plugin exposes via @api (ADR 0006). */
416
+ interface ApiRouteSpec {
417
+ method: HttpMethod | HttpMethod[];
418
+ url: string;
419
+ handler: ApiHandler;
420
+ }
421
+ /**
422
+ * Abstract assembly target (ADR 0006 / RFC 0003): the second-stage bootstrap
423
+ * registers scanned @ws/@api metadata against this. The server provides two
424
+ * concrete implementations — the global Registry (modules + builtins) and a
425
+ * per-plugin scoped assembler (RFC 0004). The SDK only depends on this shape,
426
+ * keeping the dependency arrow server -> sdk / plugin -> sdk acyclic.
427
+ */
428
+ interface AssemblyTarget {
429
+ registerMethod(name: string, handler: MethodHandler): void;
430
+ registerApi(route: ApiRouteSpec): void;
431
+ }
432
+
433
+ /** Mark a method as a WS Request handler registered under `name` (ADR 0006). */
434
+ declare function ws(name: string): (_value: unknown, context: ClassMethodDecoratorContext) => void;
435
+ /**
436
+ * Mark a method as an HTTP route handler. `@api("/path")` defaults to GET;
437
+ * `@api({ method, url })` selects the verb(s) (ADR 0006). The route URL is
438
+ * registered verbatim for modules (under `/{path}`) and prefixed with the
439
+ * plugin id for plugins (under `/{pluginId}/{path}`, RFC 0004) — the
440
+ * decorator only collects metadata; the assembly target decides the final URL.
441
+ */
442
+ declare function api(pathOrRoute: string | {
443
+ method?: HttpMethod | HttpMethod[];
444
+ url: string;
445
+ }): (_value: unknown, context: ClassMethodDecoratorContext) => void;
446
+ /**
447
+ * Second stage of decorator registration (ADR 0006 / 0009): scan a module or
448
+ * plugin instance's collected metadata and register each decorated method
449
+ * against the assembly target, bound to the instance so handlers can reach
450
+ * `this`. Walks the metadata prototype chain so inherited handlers are included.
451
+ * The `@ws`/`@api` name is registered verbatim — developers write the full
452
+ * `Domain:action` name; no namespace is injected. For a plugin, the target is
453
+ * the per-plugin scoped assembler (RFC 0004); for a module, the global Registry.
454
+ */
455
+ declare function bootstrap(instance: object, target: AssemblyTarget): void;
456
+
457
+ export { type ActionModuleApi, type ApiHandler, type ApiRouteSpec, type AssemblyTarget, BasePlugin, type Bounds, type CosServiceApi, type DocumentModuleApi, type HttpMethod, type ImageChangedEvent, type ImageChangedLayer, type ImageModuleApi, type ImageResult, type JsxRunnerApi, type LayerModuleApi, LayerSpec, type MethodHandler, type PhotoshopEventListener, type PhotoshopEventMap, type PhotoshopEvents, type PluginClientBus, type PluginHost, type PsBounds, type PsDocument, PsLayer, PsPhotoshopProxy, type PsRect, api, bootstrap, isBasePluginClass, ws };