@superblocksteam/ai-service-templates 2.0.12-next.8 → 2.0.12

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/templates.js +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superblocksteam/ai-service-templates",
3
- "version": "2.0.12-next.8",
3
+ "version": "2.0.12",
4
4
  "type": "module",
5
5
  "dependencies": {},
6
6
  "exports": {
package/templates.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // This file is auto-generated by the build script. Do not edit directly.
2
2
  export const templates = {
3
3
  "api-builder-to-yaml": {
4
+ "README.md": "# Background\n\nThis utility transforms Superblocks API definitions, written in TypeScript, into our API YAML format (and back). It is much easier to have an LLM write TypeScript than it is to generate our custom API format.\n\nTo do this, we have integration builder classes that can transform to YAML (via JSON) in `toJSON` and from JSON to class definitions (`class MySQL extends Integration { ... }`).\n\n## Architecture\n\nThe conversion process is done through module-level shimming\n\n### Module Loader Hook (`bootstrap.mjs` & `shim-loader.mjs`)\nIn order to encourage the LLM to think cohesively with the rest of the Superblocks framework, we tell it that integration builder classes are part of `@superblocksteam/library`. When generating TypeScript for APIs, the LLM duly writes import statements that require relevant integration builder classes from the library.\n\nHowever, these classes are not actually part of the user-facing library. The Module Loader Hook and this directory's TypeScript configuration allow a shim to stand in place of the user-facing library. The shim provides the implementation of the integration builder types.\n- **`bootstrap.mjs`**: This script is intended to be loaded via Node.js's `--import` flag. It registers the custom module loader hook (`shim-loader.mjs`).\n- **`shim-loader.mjs`**: This loader intercepts all `import` resolutions. When it encounters the import for `@superblocksteam/library`, it redirects the resolution to our shim (`./src/superblocks-library-shim/index.ts`).\n\n### Integration Builder Classes\n\nAll of our integrations/blocks must have the following interface:\n\n```typescript\ninterface Codec {\n // This is what we want in the YAML\n toJSON(): Promise<JsonValue>;\n\n // This returns the source code of the class representation, which gets written to disk\n toSDK(entities: string[]): Promise<string>;\n}\n```\n\nAnd we further require a small shim to handle creating an instance from JSON:\n\n```typescript\nexport abstract class Block implements Codec {\n protected name: string;\n\n constructor(name: string) {\n this.name = name;\n }\n\n public abstract toJSON(): Promise<JsonValue>;\n public abstract toSDK(entities: string[]): Promise<string>;\n\n public static fromJSON(json: any, entities: string[]): Block {\n // this calls the static method on the particular integration\n }\n}\n```\n\nIn order to have Clark properly write code for an API, every integration needs to represented by a class that implements this interface. Time for an exercise. Let's assume we want to support a new super-popular, super-hyped, non-HTTP AI service that speaks in Capn Proto (think Protobufs, but good [also this is a bad example, but I was struggling to think of anything else after a long workday]). In the API editor for the visual product, you can define a schema and data that follows that schema. Our class representation might look like:\n\n```typescript\n// Integration is an implementor of Block/Codec\nclass CapnProto extends Integration {\n schema: string;\n data: Record<string, any>\n\n constructor(readonly name: string, readonly integration: string, config: { schema: string, data: Record<string, any> }) {\n this.schema = config.schema;\n this.data = config.data;\n }\n\n // this goes to YAML\n toJSON() {\n return {\n name: this.name,\n step: {\n integration: this.integration,\n proto: {\n schema: this.schema,\n data: this.data,\n }\n }\n }\n }\n\n toSDK(): string {\n return `new CapnProto(\"${this.name}\", \"${this.integration}\", { schema: \"${this.schema}\", data: ${signatureV2(toJSBody(this.data), entities)} } })`;\n }\n\n static fromJSON(json: object) {\n return new CapnProto(...) // exercise left to reader\n }\n}\n```\n\nWith this implementation, we can:\n\n- Write the expected YAML output (`toJSON`) when Clark writes new source code\n- Write class source code from existing YAML API definitions (`fromJSON`, `toSDK`) during reprompts or a debug loop (or just anytime you need to go from YAML -> JS)\n\n#### Why?\n\nThis allows us to execute an API definition file without modification, since it transparently swaps the implementation for our builder version. Why would we want to do this? You can imagine a future where we do not actually have a YAML format for our files, but user's will be writing their API's and Workflows in an API much like this, with classes backing the definitions. This would be our code-first approach, much like our new code-first approach to applications.\n\n## Example Usage\n\nGiven an API definition file, `my-api.ts`:\n\n```typescript\nimport { Conditional, JavaScript } from \"@superblocksteam/library\";\n\nexport default new Conditional(\"test\", {\n if: {\n when: () => true,\n then: [new JavaScript(\"test\", { fn: () => 1 })],\n },\n});\n```\n\nThe tool will produce the following YAML output:\n\n```yaml\nname: test\nconditional:\n if:\n condition: (() => true)()\n blocks:\n - name: test\n step:\n integration: javascript\n javascript:\n body: return 1\n elseIf: []\n```\n\n## Runtime Execution\n\n1. **Invocation**: Our AI service invokes the bootstrap script using Node.js, shimming the library.\n ```bash\n node --import ./bootstrap.mjs ./path/to/conversion-script.js\n ```\n1. **Execution**: The conversion script imports the target API definition file (e.g., `my-api.ts`).\n1. **Shimming**: The `shim-loader` intercepts the `@superblocksteam/library` import and provides the shim implementation.\n1. **JSON Representation**: The `my-api.ts` file executes, constructing an `Api` object and its `Block`s\n1. **YAML Conversion**: The resulting JSON is then converted to YAML for output.\n",
4
5
  "bootstrap.mjs": "import { register } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\n\nregister(\"./shim-loader.mjs\", pathToFileURL(\"./\"));\n",
5
6
  "package.json": "{\n \"type\": \"module\",\n \"scripts\": {\n \"build\": \"tsc\"\n },\n \"dependencies\": {\n \"acorn\": \"^8.14.0\",\n \"prettier\": \"^3.5.3\",\n \"yaml\": \"^2.7.1\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^22.15.18\",\n \"@typescript-eslint/parser\": \"^8.34.0\",\n \"eslint\": \"^9.26.0\",\n \"typescript\": \"^5.8.3\",\n \"vitest\": \"^3.2.0\",\n \"vite\": \"^6.2.3\"\n }\n}\n",
6
7
  "shim-loader.mjs": "import path from \"path\";\nimport { pathToFileURL } from \"url\";\n\nconst overrideMap = {\n \"@superblocksteam/library\": \"./dist/superblocks-library-shim/index.js\",\n};\n\nexport async function resolve(specifier, context, nextResolve) {\n if (overrideMap[specifier]) {\n const fullPath = path.resolve(overrideMap[specifier]);\n return {\n shortCircuit: true,\n url: pathToFileURL(fullPath).href,\n };\n }\n\n return nextResolve(specifier, context);\n}\n",