@womp/kakapo-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/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # @womp/kakapo-sdk
2
+
3
+ Typed Node.js and browser SDK for manipulating a Kakapo scene through its JSON-RPC WebSocket.
4
+
5
+ ```bash
6
+ yarn install
7
+ yarn build
8
+ ```
9
+
10
+ ```ts
11
+ import { KakapoAPI } from "@womp/kakapo-sdk";
12
+
13
+ const kakapo = new KakapoAPI();
14
+ await kakapo.connect();
15
+
16
+ const union = await kakapo.editScene((scene) => {
17
+ const root = scene.createNode({
18
+ kind: "union",
19
+ parentId: 0,
20
+ name: "Agent Model",
21
+ });
22
+
23
+ const box = scene.createNode({
24
+ kind: "primitive",
25
+ parentId: root.id,
26
+ name: "Body",
27
+ properties: {
28
+ primitive: "box",
29
+ transform: { scale: { x: 10, y: 4, z: 6 } },
30
+ },
31
+ });
32
+
33
+ const red = scene.createMaterial({
34
+ color: { x: 1, y: 0.05, z: 0.05 },
35
+ roughness: 0.4,
36
+ });
37
+
38
+ const body = scene.node(box.id, "primitive");
39
+ body.materialId = red.id;
40
+ body.position = { x: 0, y: 4, z: 0 };
41
+ body.round = 0.2;
42
+ body.save();
43
+ return root;
44
+ });
45
+
46
+ console.log(await kakapo.getNodeBoundingBox(union.id));
47
+
48
+ kakapo.disconnect();
49
+ ```
50
+
51
+ The transport uses the browser's native `WebSocket` when bundled for the web and loads `ws`
52
+ dynamically in Node.js.
53
+
54
+ All public inputs are checked at runtime as well as by TypeScript. Invalid calls throw structured
55
+ `KakapoValidationError` objects with a stable `code`, offending `path`, expected rule, and a
56
+ correction `hint` intended for automated agents.
57
+
58
+ `editScene()` refreshes once, exposes synchronous JSON reads and mutations, then validates and sends
59
+ one RFC 6902 patch after the callback succeeds. Callback or commit failures discard the draft and
60
+ refresh the authoritative scene. `node(id, kind)` returns a typed editable handle; `save()` applies
61
+ its staged properties synchronously to the transaction draft.
62
+
63
+ Engine-backed reads such as bounding boxes and screenshots remain asynchronous. Run them before the
64
+ first draft mutation or after `editScene()` commits.
65
+
66
+ Each `editScene()` refreshes before creating its private draft. Top-level edits are serialized per
67
+ client. Calling a scene-mutating method through raw `rpc()` deliberately marks the cache stale.
68
+
69
+ Mesh, field, and decal names are syntax-checked, but their existence cannot be enumerated by the
70
+ current engine RPC surface. Font family/weight/style combinations are checked against `listFonts()`.
71
+
72
+ ## Verification
73
+
74
+ ```bash
75
+ yarn test # fake WebSocket protocol and API behavior
76
+ yarn test:live # launches ../kakapo/kakapo_app.exe and runs named live tests per API method
77
+ yarn test:all # runs both suites
78
+ ```
79
+
80
+ The live suite starts Kakapo once, runs methods sequentially, and reports failures as names such as
81
+ `live:setNodeParent` or `live:createNode:mesh`. Mutations refresh the authoritative engine scene
82
+ before asserting their result. See [TEST_MATRIX.md](./TEST_MATRIX.md) for the exact unit/live mapping.
package/dist/api.d.ts ADDED
@@ -0,0 +1,151 @@
1
+ import { type NodeHandle } from "./node-handle.js";
2
+ import type { BoundingBox, CaptureScreenshotOptions, CreateNodeInput, DeleteNodeOptions, FindNodeOptions, FontFamily, JsonPatchOperation, JsonValue, KakapoAPIOptions, KakapoNode, NodeKind, ListNodeOptions, Material, MaterialData, NodeUpdate, OpenScadScript, OpenScadValidationResult, PendingResources, PrimitiveOperation, Scene, ScreenshotImage, SvgPath, TokenMessage, Transform, Vec3 } from "./types.js";
3
+ export interface SceneEdit {
4
+ getScene(): Scene;
5
+ listNodes(options?: ListNodeOptions): KakapoNode[];
6
+ findNodes(options: FindNodeOptions): KakapoNode[];
7
+ getNode(id: number): KakapoNode;
8
+ node(id: number): NodeHandle;
9
+ node<K extends NodeKind>(id: number, kind: K): NodeHandle<Extract<KakapoNode, {
10
+ kind: K;
11
+ }>>;
12
+ getNodeName(id: number): string;
13
+ getNodeParent(id: number): KakapoNode | null;
14
+ getNodeChildren(id: number): KakapoNode[];
15
+ applyScenePatch(operations: JsonPatchOperation[]): Scene;
16
+ createNode(input: CreateNodeInput): KakapoNode;
17
+ cloneNode(id: number, options?: {
18
+ parentId?: number;
19
+ name?: string;
20
+ index?: number;
21
+ }): KakapoNode;
22
+ updateNode(id: number, changes: NodeUpdate): KakapoNode;
23
+ deleteNode(id: number, options?: DeleteNodeOptions): number[];
24
+ setNodeParent(id: number, parentId: number, index?: number): KakapoNode;
25
+ setNodeName(id: number, name: string): KakapoNode;
26
+ setNodeTransform(id: number, transform: Partial<Transform>): KakapoNode;
27
+ setNodePosition(id: number, position: Vec3): KakapoNode;
28
+ setNodeRotation(id: number, rotation: Vec3): KakapoNode;
29
+ setNodeScale(id: number, scale: Vec3): KakapoNode;
30
+ setNodeVisible(id: number, visible: boolean): KakapoNode;
31
+ setNodePickable(id: number, pickable: boolean): KakapoNode;
32
+ setNodeMaterial(id: number, materialId: number | null): KakapoNode;
33
+ setNodeOperation(id: number, operation: PrimitiveOperation): KakapoNode;
34
+ setTextContent(id: number, text: string): KakapoNode;
35
+ setTextFont(id: number, fontFamily: string, weight?: number, italic?: boolean): KakapoNode;
36
+ setSvgPaths(id: number, paths: SvgPath[]): KakapoNode;
37
+ setMeshSource(id: number, mesh: string, meshIndex?: number): KakapoNode;
38
+ setFieldSource(id: number, field: string): KakapoNode;
39
+ setDecalSource(id: number, image: string): KakapoNode;
40
+ setSocketTag(id: number, tag: string): KakapoNode;
41
+ setOpenScadScript(id: number, scriptId: number, params?: Record<string, JsonValue>): KakapoNode;
42
+ listMaterials(): Material[];
43
+ getMaterial(id: number): Material;
44
+ createMaterial(data?: Partial<MaterialData>, shaderIds?: number[]): Material;
45
+ updateMaterial(id: number, data: Partial<MaterialData>, shaderIds?: number[]): Material;
46
+ deleteMaterial(id: number): void;
47
+ listOpenScadScripts(): OpenScadScript[];
48
+ getOpenScadScript(id: number): OpenScadScript;
49
+ createOpenScadScript(name: string, source: string): OpenScadScript;
50
+ updateOpenScadScript(id: number, changes: {
51
+ name?: string;
52
+ source?: string;
53
+ }): OpenScadScript;
54
+ deleteOpenScadScript(id: number): void;
55
+ }
56
+ export declare class KakapoAPI {
57
+ private readonly transport;
58
+ private rawScene?;
59
+ private cacheValid;
60
+ private fonts?;
61
+ private transactionTail;
62
+ private draftScene?;
63
+ private draftDirty;
64
+ private rendererSyncRequired;
65
+ constructor(options?: KakapoAPIOptions);
66
+ get isConnected(): boolean;
67
+ connect(): Promise<void>;
68
+ disconnect(): void;
69
+ ping(): Promise<"pong">;
70
+ rpc<T = unknown>(method: string, params?: JsonValue[]): Promise<T>;
71
+ newToken(): string;
72
+ waitForToken(token: string, timeoutMs?: number): Promise<TokenMessage>;
73
+ refreshScene(): Promise<Scene>;
74
+ getScene(): Scene;
75
+ editScene<T>(callback: (scene: SceneEdit) => T | Promise<T>): Promise<T>;
76
+ private applyScenePatch;
77
+ listNodes(options?: ListNodeOptions): KakapoNode[];
78
+ findNodes(options: FindNodeOptions): KakapoNode[];
79
+ getNode(id: number): KakapoNode;
80
+ node(id: number): NodeHandle;
81
+ node<K extends NodeKind>(id: number, kind: K): NodeHandle<Extract<KakapoNode, {
82
+ kind: K;
83
+ }>>;
84
+ getNodeName(id: number): string;
85
+ getNodeParent(id: number): KakapoNode | null;
86
+ getNodeChildren(id: number): KakapoNode[];
87
+ getNodeBoundingBox(id: number): Promise<BoundingBox>;
88
+ getNodesBoundingBox(ids: number[]): Promise<BoundingBox>;
89
+ captureScreenshot(options: CaptureScreenshotOptions): Promise<ScreenshotImage>;
90
+ private ensureRendererReady;
91
+ private captureRendererFrame;
92
+ private screenshotDirection;
93
+ private createNode;
94
+ private cloneNode;
95
+ private updateNode;
96
+ private deleteNode;
97
+ private setNodeParent;
98
+ private setNodeName;
99
+ private setNodeTransform;
100
+ private setNodePosition;
101
+ private setNodeRotation;
102
+ private setNodeScale;
103
+ private setNodeVisible;
104
+ private setNodePickable;
105
+ private setNodeMaterial;
106
+ private setNodeOperation;
107
+ private setTextContent;
108
+ private setTextFont;
109
+ private setSvgPaths;
110
+ private setMeshSource;
111
+ private setFieldSource;
112
+ private setDecalSource;
113
+ private setSocketTag;
114
+ private setOpenScadScript;
115
+ listMaterials(): Material[];
116
+ getMaterial(id: number): Material;
117
+ private createMaterial;
118
+ private updateMaterial;
119
+ private deleteMaterial;
120
+ listOpenScadScripts(): OpenScadScript[];
121
+ getOpenScadScript(id: number): OpenScadScript;
122
+ private createOpenScadScript;
123
+ private updateOpenScadScript;
124
+ private deleteOpenScadScript;
125
+ validateOpenScad(source: string, params?: Record<string, JsonValue>): Promise<OpenScadValidationResult>;
126
+ listFonts(): Promise<FontFamily[]>;
127
+ reloadFonts(): Promise<void>;
128
+ reloadMeshes(): Promise<void>;
129
+ reloadFields(): Promise<void>;
130
+ reloadDecals(): Promise<void>;
131
+ reloadTextures(): Promise<unknown>;
132
+ getPendingResources(): Promise<PendingResources>;
133
+ private call;
134
+ private sendPatch;
135
+ private saveNode;
136
+ private requireScene;
137
+ private requireDraft;
138
+ private assertEngineReadAllowed;
139
+ private createSceneEdit;
140
+ private treeArrays;
141
+ private extWithName;
142
+ private extWithNode;
143
+ private mergeNode;
144
+ private mergeMaterialData;
145
+ private expectKind;
146
+ private unsupported;
147
+ private rootError;
148
+ private materialMissing;
149
+ private scriptMissing;
150
+ private assertOpenScadValid;
151
+ }