├── .github └── workflows │ ├── release.yaml │ └── test.yaml ├── .gitignore ├── .prettierrc.json ├── LICENSE ├── MAINTAINERS.md ├── README.md ├── examples ├── aws │ ├── s3 │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ └── sqs │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js ├── blob-storage │ └── backblaze │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js ├── common-patterns │ ├── outbound-http │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ └── routing-requests │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js ├── github │ └── octokit-rest │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js ├── orm │ └── drizzle │ │ ├── .gitignore │ │ ├── README.md │ │ ├── migrations.sql │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js ├── serverless_db │ ├── neondb │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ └── planetscale │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js ├── spin-host-apis │ ├── spin-kv │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── spin-llm │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── spin-mqtt │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── spin-mysql │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── spin-postgres │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── spin-redis │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── spin-sqlite │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ └── spin-variables │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js └── upstash │ ├── qstash │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── spin.toml │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── webpack.config.js │ └── vector │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── spin.toml │ ├── src │ └── index.ts │ ├── tsconfig.json │ └── webpack.config.js ├── package-lock.json ├── package.json ├── packages ├── build-tools │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── crates │ │ └── wit-tools │ │ │ ├── Cargo.toml │ │ │ ├── README.md │ │ │ ├── src │ │ │ └── lib.rs │ │ │ └── wit │ │ │ └── world.wit │ ├── package-lock.json │ ├── package.json │ ├── plugins │ │ └── webpack │ │ │ └── index.js │ ├── src │ │ ├── build.ts │ │ ├── cli.ts │ │ ├── index.ts │ │ ├── precompile.js │ │ ├── utils.ts │ │ └── wasiDepsParser.ts │ ├── test │ │ └── wasiDepsTest.spec.ts │ ├── tsconfig.json │ └── wit │ │ └── world.wit ├── http-trigger │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── wit │ │ └── wasi-http@0.2.3.wit ├── spin-kv │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ ├── types │ │ └── fermyon-spin-key-value.d.ts │ └── wit │ │ └── world.wit ├── spin-llm │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ ├── types │ │ └── fermyon-spin-llm.d.ts │ └── wit │ │ └── world.wit ├── spin-mqtt │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ ├── types │ │ └── fermyon-spin-mqtt.d.ts │ └── wit │ │ └── world.wit ├── spin-mysql │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── rdbmsHelper.ts │ │ └── types │ │ │ └── rdbms.d.ts │ ├── tsconfig.json │ ├── types │ │ └── fermyon-spin-mysql.d.ts │ └── wit │ │ └── world.wit ├── spin-postgres │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── rdbmsHelper.ts │ │ └── types │ │ │ └── rdbms.d.ts │ ├── tsconfig.json │ ├── types │ │ └── fermyon-spin-postgres.d.ts │ └── wit │ │ └── world.wit ├── spin-postgres3 │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ ├── types │ │ └── spin-postgres-postgres.d.ts │ └── wit │ │ └── world.wit ├── spin-redis │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ ├── types │ │ └── fermyon-spin-redis.d.ts │ └── wit │ │ └── world.wit ├── spin-sqlite │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ ├── types │ │ └── fermyon-spin-sqlite.d.ts │ └── wit │ │ └── world.wit ├── spin-trigger-redis │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── wit │ │ ├── redis-types.wit │ │ └── world.wit └── spin-variables │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ └── index.ts │ ├── tsconfig.json │ ├── types │ └── fermyon-spin-variables.d.ts │ └── wit │ └── world.wit ├── templates ├── http-js │ ├── content │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.js.tmpl │ │ └── webpack.config.js │ └── metadata │ │ ├── snippets │ │ └── component.txt │ │ └── spin-template.toml ├── http-ts │ ├── content │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.ts.tmpl │ │ ├── tsconfig.json │ │ └── webpack.config.js │ └── metadata │ │ ├── snippets │ │ └── component.txt │ │ └── spin-template.toml ├── redis-js │ ├── content │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── spin.toml │ │ ├── src │ │ │ └── index.js │ │ └── webpack.config.js │ └── metadata │ │ ├── snippets │ │ ├── application-trigger.txt │ │ └── component.txt │ │ └── spin-template.toml └── redis-ts │ ├── content │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── spin.toml │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── webpack.config.js │ └── metadata │ ├── snippets │ ├── application-trigger.txt │ └── component.txt │ └── spin-template.toml ├── test ├── test-app │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── spin.toml │ ├── src │ │ ├── helpers.ts │ │ ├── index.ts │ │ └── test.ts │ ├── tsconfig.json │ └── webpack.config.js └── test.sh └── typedoc.jsonc /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | workflow_dispatch: 9 | 10 | env: 11 | RUST_VERSION: 1.84 12 | 13 | jobs: 14 | lint_and_test: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Install Rust toolchain 21 | shell: bash 22 | run: | 23 | rustup toolchain install ${{ env.RUST_VERSION }} --no-self-update 24 | rustup default ${{ env.RUST_VERSION }} 25 | 26 | - name: 'Install Wasm Rust target' 27 | run: rustup target add wasm32-wasip1 wasm32-unknown-unknown --toolchain ${{ env.RUST_VERSION }} 28 | 29 | - name: Use Node.js 22 30 | uses: actions/setup-node@v3 31 | with: 32 | node-version: 22 33 | 34 | # - name: Install dependencies 35 | # shell: bash 36 | # run: npm install --ws 37 | 38 | # - name: Run prettier check 39 | # shell: bash 40 | # run: npm run fmt-check 41 | 42 | - name: Setup `spin` 43 | uses: fermyon/actions/spin/setup@v1 44 | with: 45 | github_token: ${{ secrets.GITHUB_TOKEN }} 46 | 47 | - name: Run Test 48 | shell: bash 49 | run: | 50 | cd test 51 | ./test.sh 52 | 53 | test_template: 54 | runs-on: ${{ matrix.os }} 55 | strategy: 56 | matrix: 57 | os: [ubuntu-latest, macos-latest, windows-latest] 58 | router: ['hono', 'itty', 'none'] 59 | 60 | steps: 61 | - uses: actions/checkout@v3 62 | 63 | - name: Use Node.js 22 64 | uses: actions/setup-node@v3 65 | with: 66 | node-version: 22 67 | 68 | - name: Setup `spin` 69 | uses: fermyon/actions/spin/setup@v1 70 | with: 71 | github_token: ${{ secrets.GITHUB_TOKEN }} 72 | 73 | - name: Install templates 74 | run: spin templates install --dir . 75 | 76 | - name: Create new project 77 | run: spin new -t http-ts --value http-router=${{ matrix.router }} -a test-project 78 | 79 | - name: Add new component to project 80 | run: | 81 | cd test-project 82 | spin add -t http-ts --value http-router=${{ matrix.router }} -a new-component 83 | cd new-component 84 | npm install 85 | 86 | - name: Build the application 87 | run: | 88 | cd test-project 89 | spin build 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | lib/ 3 | obj/ 4 | target/ 5 | .spin/ 6 | combined-wit/ 7 | dist.js 8 | dist/ 9 | build/ 10 | docs/ 11 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "arrowParens": "avoid" 5 | } -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # MAINTAINERS 2 | 3 | ## Current Maintainers 4 | 5 | | GitHub Username | Name | 6 | | --- | --- | 7 | | Joel Dice | dicej | 8 | | Karthik Ganeshram | karthik2804 | 9 | | Radu Matei | radu-matei | 10 | | Thorsten Hans | ThorstenHans | 11 | | Till Schneidereit | tschneidereit | 12 | | Vaughn Dice | vdice | 13 | 14 | ## Emeritus Maintainers 15 | 16 | None 17 | -------------------------------------------------------------------------------- /examples/aws/s3/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/aws/s3/README.md: -------------------------------------------------------------------------------- 1 | # AWS S3 Integration 2 | 3 | This example showcases how to connect to and send messages using Amazon S3 with the AWS SDK. 4 | 5 | ## Prerequisites 6 | - `spin >=2.6.0` 7 | 8 | ## Setup the Example 9 | 10 | 1. **Create an AWS Account** 11 | - If you don't have an AWS account, create one at [AWS](https://aws.amazon.com/). 12 | 13 | 2. **Create an S3 bucket** 14 | - Go to the [Amazon S3 Console](https://console.aws.amazon.com/s3/). 15 | - Create a new bucket and add files to it. Note down the bucket name. 16 | 17 | 3. **Get AWS Credentials** 18 | - Create or obtain your AWS credentials (Access Key ID, Secret Access Key, and a Session Token). 19 | 20 | 4. **Configure the Code** 21 | - Copy the region, access key ID, secret access key, session token, and bucket name into the code at `src/index.ts`. 22 | 23 | ```typescript 24 | const client = new S3Client({ 25 | region: "us-west-2", 26 | credentials: { 27 | accessKeyId: "<>", 28 | secretAccessKey: "<>", 29 | sessionToken: "<>" 30 | }, 31 | }); 32 | ``` 33 | *note*: The example assumes that the bucket is in the `us-west-2` region. If the object is in some other region modify the configuration of the client and also edit the `allowed_outbound_hosts` in the `spin.toml`. 34 | 35 | ## Building and Running the Example 36 | 37 | ```bash 38 | spin build 39 | spin up 40 | ``` 41 | 42 | ### Testing the different endpoints 43 | 44 | - `curl -v http://127.0.0.1:3000/list/` to list the objects stored in the given bucket. 45 | - `curl -v http://127.0.0.1:3000/stream//` to stream the object in the given bucket. -------------------------------------------------------------------------------- /examples/aws/s3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "s3", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/s3.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "itty-router": "^5.0.18", 25 | "@aws-sdk/client-s3": "^3.600.0" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/aws/s3/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "s3" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "s3" 12 | 13 | [component.s3] 14 | source = "dist/s3.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["https://*.s3.us-west-2.amazonaws.com"] 17 | [component.s3.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/aws/s3/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { S3Client, ListObjectsV2Command, GetObjectCommand } from '@aws-sdk/client-s3'; 4 | 5 | const client = new S3Client({ 6 | region: 'us-west-2', 7 | credentials: { 8 | accessKeyId: '<>', 9 | secretAccessKey: '<>', 10 | sessionToken: '<>', 11 | }, 12 | }); 13 | let router = AutoRouter(); 14 | 15 | // Route ordering matters, the first route that matches will be used 16 | // Any route that does not return will be treated as a middleware 17 | // Any unmatched route will return a 404 18 | router 19 | .get("/list/:bucket", async ({ bucket }) => { 20 | let command = new ListObjectsV2Command({ Bucket: bucket }); 21 | try { 22 | let data = await client.send(command); 23 | return new Response(JSON.stringify(data.Contents, null, 2)); 24 | } catch (e: any) { 25 | return new Response(`error : ${e.message}`, { status: 500 }); 26 | } 27 | }) 28 | .get("/stream/:bucket/:file", async ({ bucket, file }) => { 29 | let command = new GetObjectCommand({ Bucket: bucket, Key: file }); 30 | try { 31 | const data = await client.send(command); 32 | return new Response(data.Body as ReadableStream, { 33 | status: 200, 34 | }); 35 | } catch (e: any) { 36 | return new Response(`error : ${e.message}`, { status: 500 }); 37 | } 38 | }) 39 | 40 | //@ts-ignore 41 | addEventListener('fetch', async (event: FetchEvent) => { 42 | event.respondWith(router.fetch(event.request)); 43 | }); 44 | -------------------------------------------------------------------------------- /examples/aws/s3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/aws/s3/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/aws/sqs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/aws/sqs/README.md: -------------------------------------------------------------------------------- 1 | # AWS SQS Integration 2 | 3 | This example showcases how to connect to and send messages using Amazon SQS with the AWS SDK. 4 | 5 | ## Prerequisites 6 | - `spin >=2.6.0` 7 | 8 | ## Setup the Example 9 | 10 | 1. **Create an AWS Account** 11 | - If you don't have an AWS account, create one at [AWS](https://aws.amazon.com/). 12 | 13 | 2. **Create an SQS Queue** 14 | - Go to the [Amazon SQS Console](https://console.aws.amazon.com/sqs/). 15 | - Create a new SQS queue and note down the Queue URL. 16 | 17 | 3. **Get AWS Credentials** 18 | - Create or obtain your AWS credentials (Access Key ID, Secret Access Key, and optionally, a Session Token). 19 | 20 | 4. **Configure the Code** 21 | - Copy the region, access key ID, secret access key, session token, and queue URL into the code at `src/index.ts`. 22 | 23 | ```typescript 24 | const client = new SQSClient({ 25 | region: "", 26 | credentials: { 27 | accessKeyId: "", 28 | secretAccessKey: "", 29 | sessionToken: "" 30 | }, 31 | }); 32 | 33 | const params = { 34 | MessageBody: 'This is a test message with SQSClient.', 35 | QueueUrl: '' 36 | }; 37 | 38 | ## Building and Running the Example 39 | 40 | ```bash 41 | spin build 42 | spin up 43 | ``` 44 | 45 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 46 | -------------------------------------------------------------------------------- /examples/aws/sqs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sqs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/sqs.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "itty-router": "^5.0.18", 25 | "@aws-sdk/client-sqs": "^3.600.0" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/aws/sqs/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "sqs" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "sqs" 12 | 13 | [component.sqs] 14 | source = "dist/sqs.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["https://*.amazonaws.com"] 17 | [component.sqs.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/aws/sqs/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs'; 4 | 5 | const client = new SQSClient({ 6 | region: '<>', 7 | credentials: { 8 | accessKeyId: '<>', 9 | secretAccessKey: '<>', 10 | sessionToken: '<>', 11 | }, 12 | }); 13 | 14 | const params = { 15 | MessageBody: 'This is a test message with SQSClient.', 16 | QueueUrl: '<>', 17 | }; 18 | 19 | let router = AutoRouter(); 20 | 21 | // Route ordering matters, the first route that matches will be used 22 | // Any route that does not return will be treated as a middleware 23 | // Any unmatched route will return a 404 24 | router 25 | .get("/", async () => { 26 | let command = new SendMessageCommand(params); 27 | try { 28 | await client.send(command); 29 | return new Response('success'); 30 | } catch (e: any) { 31 | return new Response(`error : ${e.message}`, { status: 500 }); 32 | } 33 | }) 34 | 35 | //@ts-ignore 36 | addEventListener('fetch', async (event: FetchEvent) => { 37 | event.respondWith(router.fetch(event.request)); 38 | }); 39 | -------------------------------------------------------------------------------- /examples/aws/sqs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/aws/sqs/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/blob-storage/backblaze/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/blob-storage/backblaze/README.md: -------------------------------------------------------------------------------- 1 | # Backblaze B2 Integration 2 | 3 | This example showcases how to connect to and send messages using Backblaze B2 with the AWS SDK. 4 | 5 | ## Prerequisites 6 | - `spin >=2.6.0` 7 | 8 | ## Setup the Example 9 | 10 | 1. **Create an AWS Account** 11 | - If you don't have an Backblaze account, create one at [Backblaze](https://www.backblaze.com/). 12 | 13 | 2. **Get Backblaze Credentials** 14 | - Create or obtain your Backblaze credentials (Access Key ID, Secret Access Key). 15 | 16 | 3. **Configure the Code** 17 | - Copy the region, access key ID, secret access key, into the code at `src/index.ts`. 18 | 19 | ```typescript 20 | const s3 = new S3Client({ 21 | endpoint: '', 22 | region: '<>', 23 | credentials: { 24 | accessKeyId: '<>', 25 | secretAccessKey: '<>', 26 | }, 27 | }); 28 | ``` 29 | 30 | ## Building and Running the Example 31 | 32 | ```bash 33 | spin build 34 | spin up 35 | ``` 36 | 37 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 38 | -------------------------------------------------------------------------------- /examples/blob-storage/backblaze/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backblaze", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/backblaze.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "itty-router": "^5.0.18", 25 | "@aws-sdk/client-s3": "^3.600.0", 26 | "uuid": "^10.0.0" 27 | } 28 | } -------------------------------------------------------------------------------- /examples/blob-storage/backblaze/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "backblaze" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "backblaze" 12 | 13 | [component.backblaze] 14 | source = "dist/backblaze.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["https://*.backblazeb2.com"] 17 | [component.backblaze.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/blob-storage/backblaze/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { 4 | CreateBucketCommand, 5 | PutObjectCommand, 6 | S3Client, 7 | } from '@aws-sdk/client-s3'; 8 | import { v4 as uuid } from 'uuid'; 9 | 10 | const s3 = new S3Client({ 11 | endpoint: '', 12 | region: '<>', 13 | credentials: { 14 | accessKeyId: '<>', 15 | secretAccessKey: '<>', 16 | }, 17 | }); 18 | let router = AutoRouter(); 19 | 20 | // Route ordering matters, the first route that matches will be used 21 | // Any route that does not return will be treated as a middleware 22 | // Any unmatched route will return a 404 23 | router 24 | .get("/", async () => { 25 | try { 26 | let bucketName = 'spin-sdk-bucket-' + uuid(); 27 | let keyName = 'hello_world.txt'; 28 | await s3.send(new CreateBucketCommand({ Bucket: bucketName })); 29 | 30 | await s3.send( 31 | new PutObjectCommand({ 32 | Bucket: bucketName, 33 | Key: keyName, 34 | Body: 'Hello World!', 35 | }), 36 | ); 37 | return new Response(`Successfully uploaded data to ${bucketName}/${keyName}`); 38 | } catch (e: any) { 39 | return new Response(`error: ${e}`, { status: 500 }); 40 | } 41 | }) 42 | 43 | //@ts-ignore 44 | addEventListener('fetch', async (event: FetchEvent) => { 45 | event.respondWith(router.fetch(event.request)); 46 | }); 47 | -------------------------------------------------------------------------------- /examples/blob-storage/backblaze/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/blob-storage/backblaze/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/common-patterns/outbound-http/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | dist.js -------------------------------------------------------------------------------- /examples/common-patterns/outbound-http/README.md: -------------------------------------------------------------------------------- 1 | # Using Outbound HTTP 2 | 3 | This example showcases making an outbound HTTP request and returning the response. 4 | 5 | ## Configuring `allowed_outbound_hosts` in `spin.toml` 6 | 7 | The `allowed_outbound_hosts` needs to include the address of the host to which the guest tried to call. In the case of this example, the host is `https://random-data-api.fermyon.app/` which is added in the `spin.toml` 8 | 9 | ## Building and Running the Example 10 | 11 | ```bash 12 | spin build 13 | spin up 14 | ``` 15 | 16 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 17 | -------------------------------------------------------------------------------- /examples/common-patterns/outbound-http/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "outbound-http", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/outbound-http.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "itty-router": "^5.0.18" 25 | } 26 | } -------------------------------------------------------------------------------- /examples/common-patterns/outbound-http/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "outbound-http" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "outbound-http" 12 | 13 | [component.outbound-http] 14 | source = "dist/outbound-http.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["https://random-data-api.fermyon.app/"] 17 | [component.outbound-http.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/common-patterns/outbound-http/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | 4 | let router = AutoRouter(); 5 | 6 | // Route ordering matters, the first route that matches will be used 7 | // Any route that does not return will be treated as a middleware 8 | // Any unmatched route will return a 404 9 | router 10 | .get("*", getDataFromAPI) 11 | 12 | async function getDataFromAPI(_request: Request) { 13 | let response = await fetch( 14 | 'https://random-data-api.fermyon.app/physics/json', 15 | ); 16 | return response; 17 | } 18 | 19 | //@ts-ignore 20 | addEventListener('fetch', async (event: FetchEvent) => { 21 | event.respondWith(router.fetch(event.request)); 22 | }); 23 | -------------------------------------------------------------------------------- /examples/common-patterns/outbound-http/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/common-patterns/outbound-http/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/common-patterns/routing-requests/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | dist.js -------------------------------------------------------------------------------- /examples/common-patterns/routing-requests/README.md: -------------------------------------------------------------------------------- 1 | # Using the Router 2 | 3 | This example showcases utilizing the `itty-router` to respond to requests on different paths. 4 | 5 | ## Building and Running the Example 6 | 7 | ```bash 8 | spin build 9 | spin up 10 | ``` 11 | 12 | ## Testing Endpoints 13 | 14 | To test the different endpoints registered using the `itty-router`, run the following commands: 15 | 16 | ### Sending a `GET` request to `/` 17 | 18 | ```console 19 | curl localhost:3000/ 20 | # Hello, Fermyon 21 | ``` 22 | 23 | ### Sending a `GET` request to `/hello/:name` 24 | 25 | ```console 26 | # Passing "Spin" for the route parameter :name 27 | curl localhost:3000/hello/Spin 28 | # Hello, Spin 29 | ``` 30 | 31 | ### Sending a `GET` request to `/bye/:name` 32 | 33 | ```console 34 | # Passing "Spin" for the route parameter :name 35 | curl localhost:3000/bye/Spin 36 | # {"message": "Goodbye, Spin"} 37 | ``` 38 | 39 | ### Sending a `GET` request to `/reverse-header-value` 40 | 41 | ```console 42 | # Specifying the x-spin-demo header and printing response headers 43 | curl -i -H 'x-spin-demo:hello spin' localhost:3000/reverse-header-value 44 | # HTTP/1.1 200 OK 45 | # x-spin-demo: nips olleh 46 | # content-length: 0 47 | # date: Mon, 17 Feb 2025 13:52:52 GMT 48 | ``` 49 | 50 | ### Sending a `POST` request to `/items` 51 | 52 | ```console 53 | # Passing a JSON payload 54 | curl -XPOST -H 'content-type:application/json' -d '{ "message": "hello"}' localhost:3000/items 55 | # Item processed 56 | ``` 57 | -------------------------------------------------------------------------------- /examples/common-patterns/routing-requests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "routing-requests", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/routing-requests.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "itty-router": "^5.0.18" 25 | } 26 | } -------------------------------------------------------------------------------- /examples/common-patterns/routing-requests/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "routing-requests" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "routing-requests" 12 | 13 | [component.routing-requests] 14 | source = "dist/routing-requests.wasm" 15 | exclude_files = ["**/node_modules"] 16 | 17 | [component.routing-requests.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/common-patterns/routing-requests/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | 4 | let router = AutoRouter(); 5 | 6 | // Route ordering matters, the first route that matches will be used 7 | // Any route that does not return will be treated as a middleware 8 | // Any unmatched route will return a 404 9 | router 10 | .get('/', () => new Response('Hello, Fermyon')) 11 | .get('/hello/:name', ({ name }) => `Hello, ${name}!`) 12 | .get('/bye/:name', ({ name }) => sayGoodBye(name)) 13 | .get('/reverse-header-value', req => reverseHeaderValue(req)) 14 | .post('/items', async (req) => processItem(await req.arrayBuffer())); 15 | 16 | 17 | //@ts-ignore 18 | addEventListener('fetch', async (event: FetchEvent) => { 19 | event.respondWith(router.fetch(event.request)); 20 | }); 21 | 22 | const sayGoodBye = (name: string): Response => { 23 | const payload = { 24 | message: `Goodbye, ${name}` 25 | }; 26 | return new Response(JSON.stringify(payload), { 27 | status: 200, 28 | headers: { 29 | 'content-type': 'application/json' 30 | } 31 | }); 32 | } 33 | 34 | const reverseHeaderValue = (req: Request): Response => { 35 | const headerValue = req.headers.get('x-spin-demo'); 36 | if (!headerValue) { 37 | return new Response('Bad Request', { status: 400 }); 38 | } 39 | return new Response(undefined, { 40 | status: 200, headers: { 41 | 'x-spin-demo': headerValue.split('').reverse().join('') 42 | } 43 | }); 44 | } 45 | 46 | const processItem = (requestBody: ArrayBuffer): Response => { 47 | const decoder = new TextDecoder(); 48 | let payload; 49 | try { 50 | payload = JSON.parse(decoder.decode(requestBody)); 51 | } 52 | catch (error) { 53 | return new Response('Bad Request', { status: 400 }); 54 | } 55 | console.log(`Processing item ${JSON.stringify(payload)}`); 56 | return new Response('Item processed', { status: 200 }); 57 | }; 58 | -------------------------------------------------------------------------------- /examples/common-patterns/routing-requests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/common-patterns/routing-requests/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/github/octokit-rest/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | dist.js -------------------------------------------------------------------------------- /examples/github/octokit-rest/README.md: -------------------------------------------------------------------------------- 1 | # GitHub API Integration 2 | 3 | This example showcases how to connect to the GitHub API and list public repositories for a specified organization using the Octokit library. 4 | 5 | ## Install Dependencies 6 | Install the necessary npm packages: 7 | 8 | ```bash 9 | npm install 10 | ``` 11 | 12 | ## Building and Running the Example 13 | 14 | ```bash 15 | spin build 16 | spin up 17 | ``` 18 | 19 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 20 | -------------------------------------------------------------------------------- /examples/github/octokit-rest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "octokit-rest", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/octokit-rest.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@fermyon/knitwit": "0.3.0", 15 | "mkdirp": "^3.0.1", 16 | "ts-loader": "^9.4.1", 17 | "typescript": "^4.8.4", 18 | "webpack": "^5.74.0", 19 | "webpack-cli": "^4.10.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "@octokit/rest": "^21.1.0", 25 | "itty-router": "^5.0.18" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/github/octokit-rest/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "octokit-rest" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "octokit-rest" 12 | 13 | [component.octokit-rest] 14 | source = "dist/octokit-rest.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["https://api.github.com"] 17 | [component.octokit-rest.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/github/octokit-rest/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { Octokit } from '@octokit/rest'; 4 | 5 | const octokit = new Octokit(); 6 | let router = AutoRouter(); 7 | 8 | // Route ordering matters, the first route that matches will be used 9 | // Any route that does not return will be treated as a middleware 10 | // Any unmatched route will return a 404 11 | router 12 | .get("/", async () => { 13 | let data = await octokit.rest.repos.listForOrg({ 14 | org: 'fermyon', 15 | type: 'public', 16 | }); 17 | return new Response(JSON.stringify(data, null, 2), { 18 | headers: { 19 | "content-type": "application/json" 20 | } 21 | }) 22 | }) 23 | 24 | //@ts-ignore 25 | addEventListener('fetch', async (event: FetchEvent) => { 26 | event.respondWith(router.fetch(event.request)); 27 | }); 28 | -------------------------------------------------------------------------------- /examples/github/octokit-rest/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/github/octokit-rest/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/orm/drizzle/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | dist.js -------------------------------------------------------------------------------- /examples/orm/drizzle/README.md: -------------------------------------------------------------------------------- 1 | # Drizzle ORM Integration 2 | 3 | This example showcases how to use [Drizzle ORM](https://orm.drizzle.team/) to generate database queries and execute it against Spin's SQLite database. 4 | 5 | ## Building and Running the Example 6 | 7 | ```bash 8 | spin build 9 | spin up --sqlite @migrations.sql 10 | ``` 11 | 12 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 13 | -------------------------------------------------------------------------------- /examples/orm/drizzle/migrations.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, age INTEGER); 2 | INSERT OR REPLACE INTO users (id, name, age) VALUES (1, 'Dan', 30); 3 | INSERT OR REPLACE INTO users (id, name, age) VALUES (2, 'Alice', 25); -------------------------------------------------------------------------------- /examples/orm/drizzle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "drizzle", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/drizzle.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "@spinframework/spin-sqlite": "1.0.0", 25 | "itty-router": "^5.0.18", 26 | "drizzle-orm": "^0.31.2" 27 | } 28 | } -------------------------------------------------------------------------------- /examples/orm/drizzle/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "drizzle" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "drizzle" 12 | 13 | [component.drizzle] 14 | source = "dist/drizzle.wasm" 15 | exclude_files = ["**/node_modules"] 16 | sqlite_databases = ["default"] 17 | [component.drizzle.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/orm/drizzle/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { Sqlite, ParameterValue } from '@spinframework/spin-sqlite'; 3 | import { AutoRouter } from 'itty-router'; 4 | import { eq } from 'drizzle-orm'; 5 | import { 6 | sqliteTable, 7 | text, 8 | integer, 9 | QueryBuilder, 10 | } from 'drizzle-orm/sqlite-core'; 11 | 12 | const users = sqliteTable('users', { 13 | id: integer('id').primaryKey(), 14 | name: text('name').notNull(), 15 | age: integer('age'), 16 | }); 17 | 18 | // Need this as BigInt does not implement to JSON by default. 19 | (BigInt.prototype as any).toJSON = function () { 20 | return this.toString(); 21 | }; 22 | 23 | let router = AutoRouter(); 24 | 25 | // Route ordering matters, the first route that matches will be used 26 | // Any route that does not return will be treated as a middleware 27 | // Any unmatched route will return a 404 28 | router 29 | .get("/", () => { 30 | // Use drizzle to generate the query. 31 | const qb = new QueryBuilder(); 32 | const query = qb.select().from(users).where(eq(users.name, 'Dan')); 33 | const { sql, params } = query.toSQL(); 34 | 35 | // Use the generated query and parameters to execute against Spin's SQLite database. 36 | let sqlite = Sqlite.openDefault(); 37 | try { 38 | let result = sqlite.execute(sql, params as ParameterValue[]); 39 | return new Response(JSON.stringify(result, null, 2), { status: 200 }); 40 | } catch (e: any) { 41 | return new Response('Error: ' + JSON.stringify(e), { status: 500 }); 42 | } 43 | }) 44 | 45 | //@ts-ignore 46 | addEventListener('fetch', async (event: FetchEvent) => { 47 | event.respondWith(router.fetch(event.request)); 48 | }); 49 | -------------------------------------------------------------------------------- /examples/orm/drizzle/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/orm/drizzle/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/serverless_db/neondb/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/serverless_db/neondb/README.md: -------------------------------------------------------------------------------- 1 | # Neon Database Integration 2 | 3 | This example demonstrates how to connect to Neon Database using the Neon Serverless SDK, execute a SQL query, and handle responses using the Spin SDK. 4 | 5 | ## Setup the Example 6 | 7 | 1. **Setup Neon Database** 8 | - Ensure you have access to a Neon Database instance from [Neon](https://neon.tech/). 9 | 10 | 2. **Configure the Code** 11 | - Copy the Neon Database endpoint into the code at `src/index.ts`. 12 | 13 | ```js 14 | const sql = neon(''); 15 | ``` 16 | 17 | ## Building and Running the Example 18 | 19 | ```bash 20 | spin build 21 | spin up 22 | ``` 23 | 24 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 25 | -------------------------------------------------------------------------------- /examples/serverless_db/neondb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "neondb", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/neondb.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "itty-router": "^5.0.18", 25 | "@neondatabase/serverless": "^0.9.3" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/serverless_db/neondb/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "neondb" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "neondb" 12 | 13 | [component.neondb] 14 | source = "dist/neondb.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["https://*.aws.neon.tech"] 17 | [component.neondb.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/serverless_db/neondb/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { neon } from '@neondatabase/serverless'; 4 | 5 | let router = AutoRouter(); 6 | 7 | // Route ordering matters, the first route that matches will be used 8 | // Any route that does not return will be treated as a middleware 9 | // Any unmatched route will return a 404 10 | router 11 | .get("/", async () => { 12 | try { 13 | const sql = neon(''); 14 | const posts = await sql('SELECT * FROM posts'); 15 | return new Response(JSON.stringify(posts, null, 2), { headers: { 'Content-Type': 'application/json' } }); 16 | } catch (e: any) { 17 | return new Response(`error: ${e}`, { status: 500 }); 18 | } 19 | }) 20 | 21 | //@ts-ignore 22 | addEventListener('fetch', async (event: FetchEvent) => { 23 | event.respondWith(router.fetch(event.request)); 24 | }); 25 | -------------------------------------------------------------------------------- /examples/serverless_db/neondb/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/serverless_db/neondb/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/serverless_db/planetscale/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/serverless_db/planetscale/README.md: -------------------------------------------------------------------------------- 1 | # Planetscale Database Integration 2 | 3 | This example demonstrates how to connect to Neon Database using the Planetscale Database SDK, execute a SQL query, and handle responses using the Spin SDK. 4 | 5 | ## Setup the Example 6 | 7 | 1. **Setup Neon Database** 8 | - Ensure you have access to a Database instance from [Planetscale](https://planetscale.com/). 9 | 10 | 2. **Configure the Code** 11 | - Copy the Planetscale host, username and password into the code at `src/index.ts`. 12 | 13 | ```js 14 | const config = { 15 | host: '', 16 | username: '', 17 | password: '' 18 | } 19 | ``` 20 | 21 | ## Building and Running the Example 22 | 23 | ```bash 24 | spin build 25 | spin up 26 | ``` 27 | 28 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 29 | -------------------------------------------------------------------------------- /examples/serverless_db/planetscale/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "planetscale", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/planetscale.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "itty-router": "^5.0.18", 25 | "@planetscale/database": "^1.18.0" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/serverless_db/planetscale/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "planetscale" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "planetscale" 12 | 13 | [component.planetscale] 14 | source = "dist/planetscale.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["https://aws.connect.psdb.cloud"] 17 | [component.planetscale.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/serverless_db/planetscale/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { connect } from '@planetscale/database'; 4 | 5 | const config = { 6 | host: '', 7 | username: '', 8 | password: '', 9 | }; 10 | 11 | let router = AutoRouter(); 12 | 13 | // Route ordering matters, the first route that matches will be used 14 | // Any route that does not return will be treated as a middleware 15 | // Any unmatched route will return a 404 16 | router 17 | .get("/", async () => { 18 | try { 19 | const conn = connect(config); 20 | const results = await conn.execute('SHOW TABLES'); 21 | return new Response(JSON.stringify(results, null, 2)); 22 | } catch (e: any) { 23 | return new Response(`error: ${e}`, { status: 500 }); 24 | } 25 | }) 26 | 27 | //@ts-ignore 28 | addEventListener('fetch', async (event: FetchEvent) => { 29 | event.respondWith(router.fetch(event.request)); 30 | }); 31 | -------------------------------------------------------------------------------- /examples/serverless_db/planetscale/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/serverless_db/planetscale/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-kv/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-kv/README.md: -------------------------------------------------------------------------------- 1 | # Using Spin KV 2 | 3 | This is a simple example showcasing using Spin key-value storage with TypeScript. 4 | 5 | ### Building and Running 6 | 7 | To build the app run the following commands: 8 | 9 | ```bash 10 | $ spin build --up 11 | ``` 12 | 13 | The mapping between the request method and KV operation is as follows: 14 | ```bash 15 | GET -> kv.get(request.url) 16 | POST -> kv.set(request.uri, body) 17 | HEAD -> kv.exists(request.uri) 18 | DELETE -> kv.delete(request.uri) 19 | ``` 20 | 21 | The application can now receive requests on `http://localhost:3000`: 22 | 23 | ```bash 24 | $ curl -i -X POST -d "ok" localhost:3000/test 25 | HTTP/1.1 200 OK 26 | content-length: 0 27 | date: Tue, 25 Apr 2023 14:25:43 GMT 28 | 29 | $ curl -i -X GET localhost:3000/test 30 | HTTP/1.1 200 OK 31 | content-length: 3 32 | date: Tue, 25 Apr 2023 14:25:54 GMT 33 | 34 | ok! 35 | 36 | $ curl -i -X DELETE localhost:3000/test 37 | HTTP/1.1 200 OK 38 | content-length: 0 39 | date: Tue, 25 Apr 2023 14:26:30 GMT 40 | 41 | $ curl -i -X GET localhost:3000/test 42 | HTTP/1.1 404 Not Found 43 | content-length: 0 44 | date: Tue, 25 Apr 2023 14:31:53 GMT 45 | ``` 46 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-kv/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spin-kv", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/spin-kv.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "@spinframework/spin-kv": "1.0.0", 25 | "itty-router": "^5.0.18" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-kv/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "spin-kv" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "spin-kv" 12 | 13 | [component.spin-kv] 14 | source = "dist/spin-kv.wasm" 15 | exclude_files = ["**/node_modules"] 16 | key_value_stores = ["default"] 17 | [component.spin-kv.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-kv/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { openDefault } from '@spinframework/spin-kv'; 4 | 5 | const decoder = new TextDecoder(); 6 | 7 | let router = AutoRouter(); 8 | 9 | // Route ordering matters, the first route that matches will be used 10 | // Any route that does not return will be treated as a middleware 11 | // Any unmatched route will return a 404 12 | router 13 | .get("*", async (req: Request) => { 14 | let store = openDefault(); 15 | let status = 200; 16 | let body = "Not Found"; 17 | 18 | switch (req.method) { 19 | case 'POST': 20 | store.set(req.url, (await req.text()) || new Uint8Array().buffer); 21 | break; 22 | case 'GET': 23 | let val; 24 | val = store.get(req.url); 25 | if (!val) { 26 | status = 404; 27 | } else { 28 | body = decoder.decode(val); 29 | } 30 | break; 31 | case 'DELETE': 32 | store.delete(req.url); 33 | break; 34 | case 'HEAD': 35 | if (!store.exists(req.url)) { 36 | status = 404; 37 | } 38 | break; 39 | default: 40 | } 41 | 42 | return new Response(body, { status }); 43 | }) 44 | 45 | //@ts-ignore 46 | addEventListener('fetch', async (event: FetchEvent) => { 47 | event.respondWith(router.fetch(event.request)); 48 | }); 49 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-kv/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-kv/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-llm/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-llm/README.md: -------------------------------------------------------------------------------- 1 | # Using Spin LLM 2 | 3 | This is a simple example showcasing using Spin LLM. 4 | 5 | ### Building and Running 6 | 7 | As a prerequisite, you will need to download the models and place them in a structure as described in https://developer.fermyon.com/spin/v2/serverless-ai-api-guide#file-structure 8 | 9 | To build the app run the following commands: 10 | 11 | ```bash 12 | $ spin build 13 | $ spin up 14 | ``` 15 | 16 | Make a request using `curl`: 17 | 18 | ```bash 19 | curl localhost:3000 20 | ``` -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-llm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spin-llm", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/spin-llm.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "@spinframework/spin-llm": "1.0.0", 25 | "itty-router": "^5.0.18" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-llm/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "spin-llm" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "spin-llm" 12 | 13 | [component.spin-llm] 14 | source = "dist/spin-llm.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_models = ["llama2-chat"] 17 | [component.spin-llm.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-llm/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { infer } from '@spinframework/spin-llm'; 4 | 5 | 6 | let router = AutoRouter(); 7 | 8 | // Route ordering matters, the first route that matches will be used 9 | // Any route that does not return will be treated as a middleware 10 | // Any unmatched route will return a 404 11 | router 12 | .get("/", () => { 13 | try { 14 | let result = infer(Llm.InferencingModels.Llama2Chat, 'tell me a joke'); 15 | return new Response(JSON.stringify(result)); 16 | } catch (e: any) { 17 | return new Response(e.message, { status: 500 }); 18 | } 19 | }) 20 | 21 | //@ts-ignore 22 | addEventListener('fetch', async (event: FetchEvent) => { 23 | event.respondWith(router.fetch(event.request)); 24 | }); 25 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-llm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-llm/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mqtt/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mqtt/README.md: -------------------------------------------------------------------------------- 1 | # Using Spin Outbound MQTT 2 | 3 | This example showcases using outbound MQTT with the Spin SDK. 4 | 5 | ## Building and Running the Example 6 | 7 | For this example, configure the app with the address, username password and topics in `src/index.ts`: 8 | 9 | ```js 10 | const config = { 11 | address: "", 12 | username: "", 13 | password: "", 14 | KeepAlive: 60, 15 | } 16 | 17 | const topic = ""; 18 | ``` 19 | 20 | Build and run the app: 21 | 22 | ```bash 23 | spin build 24 | spin up 25 | ``` 26 | 27 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 28 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mqtt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spin-mqtt", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/spin-mqtt.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "@spinframework/spin-mqtt": "1.0.0", 25 | "itty-router": "^5.0.18" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mqtt/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "spin-mqtt" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "spin-mqtt" 12 | 13 | [component.spin-mqtt] 14 | source = "dist/spin-mqtt.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["mqtt://*:*"] 17 | [component.spin-mqtt.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mqtt/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { Mqtt, QoS } from '@spinframework/spin-mqtt'; 4 | 5 | const encoder = new TextEncoder(); 6 | 7 | const config = { 8 | address: '', 9 | username: '', 10 | password: '', 11 | KeepAlive: 60, 12 | }; 13 | 14 | const topic = ''; 15 | 16 | let router = AutoRouter(); 17 | 18 | // Route ordering matters, the first route that matches will be used 19 | // Any route that does not return will be treated as a middleware 20 | // Any unmatched route will return a 404 21 | router 22 | .get("/", () => { 23 | let conn = Mqtt.open( 24 | config.address, 25 | config.username, 26 | config.password, 27 | config.KeepAlive, 28 | ); 29 | conn.publish(topic, encoder.encode('message'), QoS.AtMostOnce); 30 | 31 | return new Response('Message published successfully'); 32 | }) 33 | 34 | //@ts-ignore 35 | addEventListener('fetch', async (event: FetchEvent) => { 36 | event.respondWith(router.fetch(event.request)); 37 | }); 38 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mqtt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mqtt/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mysql/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mysql/README.md: -------------------------------------------------------------------------------- 1 | # Using Spin Outbound MySQL 2 | 3 | This example showcases using outbound MySQL with the Spin SDK. 4 | 5 | ## Building and Running the Example 6 | 7 | Setup the MySQL database instance running at `127.0.0.1`. Run the following commands after connecting to it: 8 | 9 | ```bash 10 | create database spin_dev; 11 | use spin_dev; 12 | create table test(id int, val int); 13 | insert into test values (4,4); 14 | ``` 15 | 16 | Build and run the app: 17 | 18 | ```bash 19 | spin build 20 | spin up 21 | ``` 22 | 23 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 24 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mysql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spin-mysql", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/spin-mysql.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "@spinframework/spin-mysql": "1.0.0", 25 | "itty-router": "^5.0.18" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mysql/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "spin-mysql" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "spin-mysql" 12 | 13 | [component.spin-mysql] 14 | source = "dist/spin-mysql.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["mysql://localhost"] 17 | [component.spin-mysql.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mysql/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { open } from '@spinframework/spin-mysql'; 4 | 5 | // Connects as the root user without a password 6 | const DB_URL = 'mysql://root:@127.0.0.1/spin_dev'; 7 | 8 | let router = AutoRouter(); 9 | 10 | // Route ordering matters, the first route that matches will be used 11 | // Any route that does not return will be treated as a middleware 12 | // Any unmatched route will return a 404 13 | router 14 | .get("/", () => { 15 | let conn = open(DB_URL); 16 | conn.execute('delete from test where id=?', [4]); 17 | conn.execute('insert into test values (4,5)', []); 18 | let ret = conn.query('select * from test', []); 19 | 20 | return new Response(JSON.stringify(ret, null, 2)); 21 | }) 22 | 23 | //@ts-ignore 24 | addEventListener('fetch', async (event: FetchEvent) => { 25 | event.respondWith(router.fetch(event.request)); 26 | }); 27 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mysql/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-mysql/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-postgres/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-postgres/README.md: -------------------------------------------------------------------------------- 1 | # Using Spin Outbound PostgreSQL 2 | 3 | This example showcases using outbound PostgreSQL with the Spin SDK. 4 | 5 | ## Building and Running the Example 6 | 7 | Setup the PostgreSQL database instance running at `127.0.0.1`. Run the following commands after connecting to it: 8 | 9 | ```bash 10 | create database spin_dev; 11 | \c spin_dev; 12 | create table test(id int, val int); 13 | insert into test values (4,4); 14 | ``` 15 | 16 | Build and run the app: 17 | 18 | ```bash 19 | spin build 20 | spin up 21 | ``` 22 | 23 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 24 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-postgres/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spin-postgres", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/spin-postgres.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "@spinframework/spin-postgres": "1.0.0", 25 | "itty-router": "^5.0.18" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-postgres/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "spin-postgres" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "spin-postgres" 12 | 13 | [component.spin-postgres] 14 | source = "dist/spin-postgres.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["postgres://localhost"] 17 | [component.spin-postgres.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-postgres/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { open } from '@spinframework/spin-postgres'; 4 | 5 | const DB_URL = 'host=localhost user=postgres dbname=spin_dev'; 6 | 7 | let router = AutoRouter(); 8 | 9 | // Route ordering matters, the first route that matches will be used 10 | // Any route that does not return will be treated as a middleware 11 | // Any unmatched route will return a 404 12 | router 13 | .get("/", () => { 14 | let conn = open(DB_URL); 15 | conn.execute('delete from test where id=4', []); 16 | conn.execute('insert into test values (4,5)', []); 17 | let ret = conn.query('select * from test', []); 18 | 19 | return new Response(JSON.stringify(ret, null, 2)); 20 | }) 21 | 22 | //@ts-ignore 23 | addEventListener('fetch', async (event: FetchEvent) => { 24 | event.respondWith(router.fetch(event.request)); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-postgres/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-postgres/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-redis/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-redis/README.md: -------------------------------------------------------------------------------- 1 | # Using Spin Outbound Redis 2 | 3 | This example showcases using outbound Redis using the Spin SDK. 4 | 5 | ## Prerequisites 6 | 7 | - A Redis server is running at `localhost:6379` 8 | 9 | ## Building and Running the Example 10 | 11 | ```bash 12 | spin build 13 | spin up 14 | ``` 15 | 16 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-redis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spin-redis", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/spin-redis.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "@spinframework/spin-redis": "1.0.0", 25 | "itty-router": "^5.0.18" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-redis/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "spin-redis" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "spin-redis" 12 | 13 | [component.spin-redis] 14 | source = "dist/spin-redis.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["redis://localhost:6379"] 17 | [component.spin-redis.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-redis/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { open } from '@spinframework/spin-redis'; 4 | 5 | const encoder = new TextEncoder(); 6 | const redisAddress = 'redis://localhost:6379/'; 7 | 8 | let router = AutoRouter(); 9 | 10 | // Route ordering matters, the first route that matches will be used 11 | // Any route that does not return will be treated as a middleware 12 | // Any unmatched route will return a 404 13 | router 14 | .get("/", () => { 15 | try { 16 | let db = Redis.open(redisAddress); 17 | db.set('test', encoder.encode('Hello world')); 18 | let val = db.get('test'); 19 | 20 | if (!val) { 21 | return new Response(null, { status: 404 }); 22 | } 23 | return new Response(val); 24 | } catch (e: any) { 25 | return new Response(`Error: ${JSON.stringify(e.payload)}`, { status: 500 }); 26 | } 27 | }) 28 | 29 | //@ts-ignore 30 | addEventListener('fetch', async (event: FetchEvent) => { 31 | event.respondWith(router.fetch(event.request)); 32 | }); 33 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-redis/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-redis/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-sqlite/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-sqlite/README.md: -------------------------------------------------------------------------------- 1 | # Using Spin SQLite 2 | 3 | This is a simple example showcasing using Spin SQLite. 4 | 5 | Make a request using `curl`: 6 | 7 | ```bash 8 | curl localhost:3000 9 | ``` -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-sqlite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spin-sqlite", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/spin-sqlite.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "@spinframework/spin-sqlite": "1.0.0", 25 | "itty-router": "^5.0.18" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-sqlite/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "spin-sqlite" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "spin-sqlite" 12 | 13 | [component.spin-sqlite] 14 | source = "dist/spin-sqlite.wasm" 15 | exclude_files = ["**/node_modules"] 16 | sqlite_databases = ["default"] 17 | [component.spin-sqlite.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-sqlite/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { openDefault } from '@fermyon/spin-sqlite'; 4 | 5 | let router = AutoRouter(); 6 | 7 | // Route ordering matters, the first route that matches will be used 8 | // Any route that does not return will be treated as a middleware 9 | // Any unmatched route will return a 404 10 | router 11 | .get("/", () => { 12 | try { 13 | let conn = openDefault(); 14 | let result = conn.execute('SELECT * FROM todos WHERE id > (?);', [1]); 15 | return new Response(JSON.stringify(result)); 16 | } catch (e: any) { 17 | console.log('Error: ' + JSON.stringify(e.payload)); 18 | return new Response('Error: ' + JSON.stringify(e), { status: 500 }); 19 | } 20 | }) 21 | 22 | //@ts-ignore 23 | addEventListener('fetch', async (event: FetchEvent) => { 24 | event.respondWith(router.fetch(event.request)); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-sqlite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-sqlite/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-variables/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-variables/README.md: -------------------------------------------------------------------------------- 1 | # Using Spin Variables 2 | 3 | This example showcases how to read Spin Variables. 4 | 5 | ## Building and Running the Example 6 | 7 | ```bash 8 | spin build 9 | spin up 10 | ``` 11 | 12 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 13 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-variables/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spin-variables", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/spin-variables.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "@spinframework/spin-variables": "1.0.0", 25 | "itty-router": "^5.0.18" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-variables/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "spin-variables" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "spin-variables" 12 | 13 | [component.spin-variables] 14 | source = "dist/spin-variables.wasm" 15 | exclude_files = ["**/node_modules"] 16 | [component.spin-variables.variables] 17 | my_variable = "Hello, World!" 18 | [component.spin-variables.build] 19 | command = ["npm install", "npm run build"] 20 | watch = ["src/**/*.ts"] 21 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-variables/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { get } from '@spinframework/spin-variables'; 4 | 5 | let router = AutoRouter(); 6 | 7 | // Route ordering matters, the first route that matches will be used 8 | // Any route that does not return will be treated as a middleware 9 | // Any unmatched route will return a 404 10 | router 11 | .get("/", () => { 12 | let val = get('my_variable'); 13 | if (!val) { 14 | return new Response(null, { status: 404 }); 15 | } 16 | return new Response(val); 17 | }) 18 | 19 | //@ts-ignore 20 | addEventListener('fetch', async (event: FetchEvent) => { 21 | event.respondWith(router.fetch(event.request)); 22 | }); 23 | -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-variables/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/spin-host-apis/spin-variables/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/upstash/qstash/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | dist.js -------------------------------------------------------------------------------- /examples/upstash/qstash/README.md: -------------------------------------------------------------------------------- 1 | # Upstash QStash Integration 2 | 3 | This example demonstrates how to connect to Upstash QStash, publish a JSON message with a delay, and handle an HTTP request using the Spin SDK. 4 | 5 | ## Setup the Example 6 | 7 | 1. **Create an Upstash Account** 8 | - If you don't have an Upstash account, create one at [Upstash](https://upstash.com/). 9 | 10 | 2. **Get QStash Token** 11 | - Go to the Upstash dashboard and obtain your QStash token. 12 | 13 | 3. **Configure the Code** 14 | - Copy the API URL and token into the code at `src/index.ts`. 15 | ```js 16 | const client = new Client({ token: "" }); 17 | 18 | ... 19 | 20 | const resp = await client.publishJSON({ 21 | url: "", 22 | body: { hello: "world" }, 23 | delay: 2, 24 | }); 25 | ``` 26 | 27 | ## Building and Running the Example 28 | 29 | ```bash 30 | spin build 31 | spin up 32 | ``` 33 | 34 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 35 | -------------------------------------------------------------------------------- /examples/upstash/qstash/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qstash", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/qstash.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "itty-router": "^5.0.18", 25 | "@upstash/qstash": "^2.5.5" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/upstash/qstash/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "qstash" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "qstash" 12 | 13 | [component.qstash] 14 | source = "dist/qstash.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["https://qstash.upstash.io:443"] 17 | [component.qstash.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/upstash/qstash/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { Client } from '@upstash/qstash'; 4 | 5 | const client = new Client({ token: '' }); 6 | let router = AutoRouter(); 7 | 8 | // Route ordering matters, the first route that matches will be used 9 | // Any route that does not return will be treated as a middleware 10 | // Any unmatched route will return a 404 11 | router 12 | .get("/", async () => { 13 | let resp = await client.publishJSON({ 14 | url: '<>', 15 | body: { hello: 'world' }, 16 | delay: 2, 17 | }); 18 | 19 | return new Response(JSON.stringify(resp, null, 2), { status: 200, headers: { "Content-Type": "application/json" } }); 20 | }) 21 | 22 | //@ts-ignore 23 | addEventListener('fetch', async (event: FetchEvent) => { 24 | event.respondWith(router.fetch(event.request)); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/upstash/qstash/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/upstash/qstash/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /examples/upstash/vector/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | dist.js -------------------------------------------------------------------------------- /examples/upstash/vector/README.md: -------------------------------------------------------------------------------- 1 | # Upstash Vector Integration 2 | 3 | This example showcases how to connect to Upstash Vector, upsert vectors, and perform a query using the Upstash SDK. 4 | 5 | ## Setup the Example 6 | 7 | 1. **Create an Upstash Account** 8 | - If you don't have an Upstash account, create one at [Upstash](https://upstash.com/). 9 | 10 | 2. **Create a Vector Index** 11 | - Go to the Upstash dashboard and create a new vector index. 12 | - Note down the index URL and token. 13 | 14 | 3. **Configure the Code** 15 | - Copy the index URL and token into the code at `src/index.ts`. 16 | 17 | ```ts 18 | const index = new Index({ 19 | url: "", 20 | token: "" 21 | }) 22 | ``` 23 | 24 | ## Building and Running the Example 25 | 26 | ```bash 27 | spin build 28 | spin up 29 | ``` 30 | 31 | Use e.g. `curl -v http://127.0.0.1:3000/` to test the endpoint. 32 | -------------------------------------------------------------------------------- /examples/upstash/vector/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vector", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/vector.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0", 19 | "@fermyon/knitwit": "0.3.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "1.0.0", 23 | "@spinframework/wasi-http-proxy": "1.0.0", 24 | "itty-router": "^5.0.18", 25 | "@upstash/vector": "^1.1.3" 26 | } 27 | } -------------------------------------------------------------------------------- /examples/upstash/vector/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "vector" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "vector" 12 | 13 | [component.vector] 14 | source = "dist/vector.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["https://*.upstash.io"] 17 | [component.vector.build] 18 | command = ["npm install", "npm run build"] 19 | watch = ["src/**/*.ts"] 20 | -------------------------------------------------------------------------------- /examples/upstash/vector/src/index.ts: -------------------------------------------------------------------------------- 1 | // https://itty.dev/itty-router/routers/autorouter 2 | import { AutoRouter } from 'itty-router'; 3 | import { Index } from '@upstash/vector'; 4 | 5 | const index = new Index({ 6 | url: '', 7 | token: '', 8 | }); 9 | 10 | let router = AutoRouter(); 11 | 12 | // Route ordering matters, the first route that matches will be used 13 | // Any route that does not return will be treated as a middleware 14 | // Any unmatched route will return a 404 15 | router 16 | .get("/", async () => { 17 | try { 18 | await index.upsert({ 19 | id: '1', 20 | vector: [0.6, 0.8], 21 | metadata: { field: 'value' }, 22 | }); 23 | await index.upsert({ 24 | id: '2', 25 | vector: [0.6, 0.6], 26 | metadata: { field: 'value' }, 27 | }); 28 | 29 | let data = await index.query({ 30 | vector: [0.6, 0.7], 31 | topK: 3, 32 | includeMetadata: true, 33 | }); 34 | return new Response(JSON.stringify(data, null, 2), { status: 200, headers: { "Content-Type": "application/json" } }); 35 | } catch (e: any) { 36 | return new Response(`error: ${e}`, { status: 500 }); 37 | } 38 | }) 39 | 40 | //@ts-ignore 41 | addEventListener('fetch', async (event: FetchEvent) => { 42 | event.respondWith(router.fetch(event.request)); 43 | }); 44 | -------------------------------------------------------------------------------- /examples/upstash/vector/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/upstash/vector/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spin-js-sdk", 3 | "version": "1.0.0", 4 | "description": "This package is currently used only for generating documentation. It is not a library and does not contain any code.", 5 | "main": "index.js", 6 | "private": true, 7 | "scripts": { 8 | "docs": "typedoc", 9 | "test": "cd test && ./test.sh" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/spinframework/spin-js-sdk.git" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "Apache-2.0 WITH LLVM-exception", 18 | "type": "module", 19 | "bugs": { 20 | "url": "https://github.com/spinframework/spin-js-sdk/issues" 21 | }, 22 | "homepage": "https://github.com/spinframework/spin-js-sdk#readme", 23 | "devDependencies": { 24 | "typedoc": "^0.28.2", 25 | "typedoc-plugin-missing-exports": "^4.0.0" 26 | } 27 | } -------------------------------------------------------------------------------- /packages/build-tools/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace.package] 2 | version = "0.1.0" 3 | edition = "2021" 4 | 5 | [workspace] 6 | members = ["crates/wit-tools"] 7 | resolver = "2" 8 | 9 | [workspace.dependencies] 10 | anyhow = "1.0.86" 11 | wit-bindgen = "0.27.0" 12 | wit-bindgen-core = "0.27.0" 13 | wit-component = "0.219.1" 14 | wit-parser = "0.219.1" 15 | -------------------------------------------------------------------------------- /packages/build-tools/Makefile: -------------------------------------------------------------------------------- 1 | CARGO_BUILD_TARGET = wasm32-wasip1 2 | WASM_FILE = target/wasm32-wasip1/release/wit_tools.wasm 3 | OUTPUT_DIR = obj 4 | OUTPUT_WASM = $(OUTPUT_DIR)/wit_tools.wasm 5 | LIB_DIR = lib 6 | 7 | all: build 8 | 9 | build: 10 | cargo build --release --target $(CARGO_BUILD_TARGET) 11 | mkdir -p $(OUTPUT_DIR) 12 | npx @bytecodealliance/jco new $(WASM_FILE) -o $(OUTPUT_WASM) --wasi-reactor 13 | npx @bytecodealliance/jco transpile -q --name wit_tools $(OUTPUT_WASM) -o $(LIB_DIR) -- -O1 14 | 15 | clean: 16 | cargo clean 17 | rm -rf $(OUTPUT_DIR) 18 | rm -rf $(LIB_DIR) 19 | 20 | .PHONY: all build clean -------------------------------------------------------------------------------- /packages/build-tools/crates/wit-tools/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wit-tools" 3 | version.workspace = true 4 | edition.workspace = true 5 | description = "package containing build tools to help with managing wit dependencies" 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | anyhow = { workspace = true } 12 | id-arena = "2.2.1" 13 | wit-bindgen = { workspace = true } 14 | wit-bindgen-core = { workspace = true } 15 | wit-component = { workspace = true } 16 | wit-parser = { workspace = true } 17 | -------------------------------------------------------------------------------- /packages/build-tools/crates/wit-tools/README.md: -------------------------------------------------------------------------------- 1 | # `wit-tools` 2 | 3 | This crate provides useful functionality for mananging wit dependencies in projects. It currently provides the functionality of: 4 | - Merging multiple `wit` packages and outputting a combined world. 5 | - Getting all the imports of a given world. 6 | 7 | This crate can be compiled as a webassembly component and then consumed in language that do not have direct support for `wasm-tools` but has a wasm runtime capable of running components. (e.g) Can be used in Javascript programs using JCO. 8 | 9 | ## Developing 10 | 11 | The `wit` world that the library implements is defined in the [`wit` directory](./wit). 12 | 13 | ### Building 14 | 15 | The component can be built by running 16 | 17 | ```bash 18 | cargo build --release 19 | ``` 20 | -------------------------------------------------------------------------------- /packages/build-tools/crates/wit-tools/wit/world.wit: -------------------------------------------------------------------------------- 1 | package spinframework:js-build; 2 | 3 | world tools{ 4 | record target-world { 5 | package-name: string, 6 | world-name: string, 7 | } 8 | 9 | export merge-wit: func(wit-paths: list, worlds: list, output-world: option, output-package: option) -> result; 10 | export get-wit-imports: func(wit-path: list, worlds: list) -> result, string>; 11 | } -------------------------------------------------------------------------------- /packages/build-tools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/build-tools", 3 | "version": "1.0.2", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "make && rm -rf dist && tsc && chmod +x dist/* && cp src/*.js dist/", 8 | "fmt": "prettier --write \"src/**/*.{ts,tsx,js,jsx}\"", 9 | "test": "npm run build && mocha --require ts-node/register test/**/*.spec.ts" 10 | }, 11 | "bin": { 12 | "j2w": "dist/index.js" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "Apache-2.0 WITH LLVM-exception", 17 | "type": "module", 18 | "devDependencies": { 19 | "@types/chai": "^5.0.1", 20 | "@types/mocha": "^10.0.10", 21 | "@types/node": "^22.13.1", 22 | "@types/sinon": "^17.0.3", 23 | "@types/yargs": "^17.0.33", 24 | "chai": "^5.2.0", 25 | "mocha": "^11.1.0", 26 | "prettier": "^3.5.0", 27 | "sinon": "^19.0.2", 28 | "ts-node": "^10.9.2", 29 | "typescript": "^5.7.3" 30 | }, 31 | "dependencies": { 32 | "@bytecodealliance/componentize-js": "^0.18.1", 33 | "@bytecodealliance/jco": "^1.10.2", 34 | "yargs": "^17.7.2", 35 | "acorn-walk": "^8.3.4", 36 | "acron": "^1.0.5", 37 | "magic-string": "^0.30.17", 38 | "regexpu-core": "^6.2.0" 39 | }, 40 | "files": [ 41 | "lib", 42 | "bin", 43 | "plugins", 44 | "dist", 45 | "wit" 46 | ], 47 | "config": { 48 | "witDependencies": [ 49 | { 50 | "witPath": "./wit", 51 | "package": "spinframework:wasi-cli-environment@0.2.3", 52 | "world": "wasi-cli" 53 | } 54 | ] 55 | } 56 | } -------------------------------------------------------------------------------- /packages/build-tools/plugins/webpack/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | getPackagesWithWasiDeps, 3 | processWasiDeps, 4 | } from '../../dist/wasiDepsParser.js'; 5 | 6 | // The plugin is used to automatically add the wit imports to the webpack 7 | // externals. This is required because the bindings for wit imports are not 8 | // generated until we are componentizing and webpack needs to consider them 9 | // externals (available at runtime). 10 | class SpinSdkPlugin { 11 | constructor() { 12 | this.externals = {}; 13 | } 14 | 15 | static async init() { 16 | const { getWitImports } = await import('../../lib/wit_tools.js'); 17 | let plugin = new SpinSdkPlugin(); 18 | 19 | // Get the list of wit dependencies from other packages as defined in the package.json. 20 | let wasiDeps = getPackagesWithWasiDeps(process.cwd(), new Set(), true); 21 | let { witPaths, targetWorlds } = processWasiDeps(wasiDeps); 22 | 23 | // Get the list of wit imports from the world 24 | let imports = getWitImports(witPaths, targetWorlds); 25 | 26 | // Convert the imports into a format that can be used to define the webpack 27 | // externals that can be applied. 28 | imports.map(i => { 29 | plugin.externals[i] = i; 30 | }); 31 | 32 | return plugin; 33 | } 34 | 35 | apply(compiler) { 36 | if ( 37 | compiler.options.externals && 38 | typeof compiler.options.externals === 'object' 39 | ) { 40 | this.externals = Object.assign( 41 | {}, 42 | compiler.options.externals, 43 | this.externals, 44 | ); 45 | } 46 | compiler.options.externals = this.externals; 47 | } 48 | } 49 | 50 | export default SpinSdkPlugin; 51 | -------------------------------------------------------------------------------- /packages/build-tools/src/build.ts: -------------------------------------------------------------------------------- 1 | import { 2 | calculateChecksum, 3 | fileExists, 4 | getExistingBuildData, 5 | } from './utils.js'; 6 | 7 | export function getBuildDataPath(src: string): string { 8 | return `${src}.buildData.json`; 9 | } 10 | 11 | export async function ShouldComponentize( 12 | src: string, outputPath: string, componentizeVersion: string, runtimeArgs: string, 13 | ) { 14 | const sourceChecksum = await calculateChecksum(src); 15 | const existingBuildData = await getExistingBuildData(getBuildDataPath(src)); 16 | 17 | if ( 18 | existingBuildData?.version == componentizeVersion && 19 | existingBuildData?.checksum === sourceChecksum && 20 | existingBuildData?.runtimeArgs === runtimeArgs && 21 | (await fileExists(outputPath)) 22 | ) { 23 | return false; 24 | } 25 | 26 | return true; 27 | } 28 | -------------------------------------------------------------------------------- /packages/build-tools/src/cli.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs'; 2 | import { hideBin } from 'yargs/helpers'; 3 | 4 | export interface CliArgs { 5 | input: string; 6 | output: string; 7 | aot?: boolean; 8 | debug?: boolean; 9 | } 10 | 11 | export function getCliArgs(): CliArgs { 12 | let args = yargs(hideBin(process.argv)) 13 | .option('input', { 14 | alias: 'i', 15 | describe: 'Path to the input file', 16 | demandOption: true, 17 | }) 18 | .option('output', { 19 | alias: 'o', 20 | describe: 'Path to the output file', 21 | default: 'component.wasm', 22 | }) 23 | .option('aot', { 24 | describe: 'Enable Ahead of Time compilation', 25 | type: 'boolean', 26 | }) 27 | .option('debug', { 28 | alias: 'd', 29 | describe: 'Enable JavaScript debugging', 30 | type: 'boolean', 31 | }) 32 | .argv as CliArgs; 33 | 34 | return args; 35 | } 36 | -------------------------------------------------------------------------------- /packages/build-tools/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { readFile } from 'fs/promises'; 2 | import { createHash } from 'node:crypto'; 3 | import { access, writeFile } from 'node:fs/promises'; 4 | 5 | 6 | // Function to calculate file checksum 7 | export async function calculateChecksum(filePath: string) { 8 | try { 9 | const fileBuffer = await readFile(filePath); 10 | const hash = createHash('sha256'); 11 | hash.update(fileBuffer); 12 | return hash.digest('hex'); 13 | } catch (error) { 14 | console.error(`Error calculating checksum for file ${filePath}:`, error); 15 | throw error; 16 | } 17 | } 18 | 19 | // Function to check if a file exists 20 | export async function fileExists(filePath: string) { 21 | try { 22 | await access(filePath); 23 | return true; 24 | } catch { 25 | return false; 26 | } 27 | } 28 | 29 | export async function getExistingBuildData(buildDataPath: string) { 30 | try { 31 | if (await fileExists(buildDataPath)) { 32 | const buildData = await readFile(buildDataPath, 'utf8'); 33 | return JSON.parse(buildData); 34 | } 35 | return null; 36 | } catch (error) { 37 | console.error( 38 | `Error reading existing checksum file at ${buildDataPath}:`, 39 | error, 40 | ); 41 | throw error; 42 | } 43 | } 44 | 45 | export async function saveBuildData( 46 | buildDataPath: string, checksum: string, version: string, runtimeArgs: string, 47 | ) { 48 | try { 49 | const checksumData = { 50 | version, 51 | checksum, 52 | runtimeArgs, 53 | }; 54 | await writeFile(buildDataPath, JSON.stringify(checksumData, null, 2)); 55 | } catch (error) { 56 | console.error(`Error saving checksum file at ${buildDataPath}:`, error); 57 | throw error; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /packages/build-tools/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | "declaration": true, 8 | "strict": true, 9 | "skipLibCheck": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "outDir": "./dist", 12 | "rootDir": "./src" 13 | }, 14 | "include": [ 15 | "src/**/*" 16 | ], 17 | "exclude": [ 18 | "node_modules", 19 | "dist" 20 | ] 21 | } -------------------------------------------------------------------------------- /packages/http-trigger/README.md: -------------------------------------------------------------------------------- 1 | # Wasi HTTP Proxy 2 | 3 | This package enables adding the `wasi:http` trigger export to apps built using the `@spinframework/build-tools`. This repository does not contain any code, it just contains the `wit` files describing the `wasi:http` package. -------------------------------------------------------------------------------- /packages/http-trigger/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/wasi-http-proxy", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@spinframework/wasi-http-proxy", 9 | "version": "1.0.0", 10 | "license": " Apache-2.0 WITH LLVM-exception", 11 | "devDependencies": { 12 | "typescript": "^5.7.3" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.8.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 18 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/http-trigger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/wasi-http-proxy", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "type": "module", 8 | "scripts": { 9 | "build": "tsc" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": " Apache-2.0 WITH LLVM-exception", 14 | "devDependencies": { 15 | "typescript": "^5.7.3" 16 | }, 17 | "files": [ 18 | "dist", 19 | "wit" 20 | ], 21 | "config": { 22 | "witDependencies": [ 23 | { 24 | "witPath": "./wit", 25 | "package": "spinframework:http-trigger@0.2.3", 26 | "world": "http-trigger" 27 | } 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /packages/http-trigger/src/index.ts: -------------------------------------------------------------------------------- 1 | // This package does not contain any code. 2 | // It exists purely as a package that exports the wasi http trigger. 3 | -------------------------------------------------------------------------------- /packages/http-trigger/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "lib": [ 6 | "ES2020" 7 | ], 8 | "moduleResolution": "node", 9 | "declaration": true, 10 | "outDir": "dist", 11 | "strict": true, 12 | "esModuleInterop": true, 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | "dist" 17 | ] 18 | } -------------------------------------------------------------------------------- /packages/spin-kv/README.md: -------------------------------------------------------------------------------- 1 | # Spin KV 2 | 3 | This package provides bindings that enable using the Spin Key-Value interface in apps built using the `@spinframework/build-tools`. -------------------------------------------------------------------------------- /packages/spin-kv/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-kv", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@spinframework/spin-kv", 9 | "version": "1.0.0", 10 | "license": " Apache-2.0 WITH LLVM-exception", 11 | "devDependencies": { 12 | "typescript": "^5.7.3" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.8.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 18 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/spin-kv/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-kv", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "type": "module", 8 | "scripts": { 9 | "build": "tsc && cp -r types/* dist/" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": " Apache-2.0 WITH LLVM-exception", 14 | "devDependencies": { 15 | "typescript": "^5.7.3" 16 | }, 17 | "files": [ 18 | "dist", 19 | "wit" 20 | ], 21 | "config": { 22 | "witDependencies": [ 23 | { 24 | "witPath": "./wit", 25 | "package": "fermyon:spin@2.0.0", 26 | "world": "spin-kv" 27 | } 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /packages/spin-kv/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "lib": [ 6 | "ES2020", 7 | "WebWorker" 8 | ], 9 | "moduleResolution": "node", 10 | "declaration": true, 11 | "outDir": "dist", 12 | "strict": true, 13 | "esModuleInterop": true, 14 | }, 15 | "exclude": [ 16 | "node_modules", 17 | "dist" 18 | ] 19 | } -------------------------------------------------------------------------------- /packages/spin-kv/types/fermyon-spin-key-value.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'fermyon:spin/key-value@2.0.0' { 2 | /** 3 | * The set of errors which may be raised by functions in this interface 4 | */ 5 | export type Error = 6 | | ErrorStoreTableFull 7 | | ErrorNoSuchStore 8 | | ErrorAccessDenied 9 | | ErrorOther; 10 | /** 11 | * Too many stores have been opened simultaneously. Closing one or more 12 | * stores prior to retrying may address this. 13 | */ 14 | export interface ErrorStoreTableFull { 15 | tag: 'store-table-full'; 16 | } 17 | /** 18 | * The host does not recognize the store label requested. 19 | */ 20 | export interface ErrorNoSuchStore { 21 | tag: 'no-such-store'; 22 | } 23 | /** 24 | * The requesting component does not have access to the specified store 25 | * (which may or may not exist). 26 | */ 27 | export interface ErrorAccessDenied { 28 | tag: 'access-denied'; 29 | } 30 | /** 31 | * Some implementation-specific error has occurred (e.g. I/O) 32 | */ 33 | export interface ErrorOther { 34 | tag: 'other'; 35 | val: string; 36 | } 37 | 38 | export class Store { 39 | /** 40 | * Open the store with the specified label. 41 | * 42 | * `label` must refer to a store allowed in the spin.toml manifest. 43 | * 44 | * `error::no-such-store` will be raised if the `label` is not recognized. 45 | */ 46 | static open(label: string): Store; 47 | /** 48 | * Get the value associated with the specified `key` 49 | * 50 | * Returns `ok(none)` if the key does not exist. 51 | */ 52 | get(key: string): Uint8Array | undefined; 53 | /** 54 | * Set the `value` associated with the specified `key` overwriting any existing value. 55 | */ 56 | set(key: string, value: Uint8Array): void; 57 | /** 58 | * Delete the tuple with the specified `key` 59 | * 60 | * No error is raised if a tuple did not previously exist for `key`. 61 | */ 62 | delete(key: string): void; 63 | /** 64 | * Return whether a tuple exists for the specified `key` 65 | */ 66 | exists(key: string): boolean; 67 | /** 68 | * Return a list of all the keys 69 | */ 70 | getKeys(): Array; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/spin-kv/wit/world.wit: -------------------------------------------------------------------------------- 1 | package fermyon:spin@2.0.0; 2 | 3 | interface key-value { 4 | /// An open key-value store 5 | resource store { 6 | /// Open the store with the specified label. 7 | /// 8 | /// `label` must refer to a store allowed in the spin.toml manifest. 9 | /// 10 | /// `error::no-such-store` will be raised if the `label` is not recognized. 11 | open: static func(label: string) -> result; 12 | 13 | /// Get the value associated with the specified `key` 14 | /// 15 | /// Returns `ok(none)` if the key does not exist. 16 | get: func(key: string) -> result>, error>; 17 | 18 | /// Set the `value` associated with the specified `key` overwriting any existing value. 19 | set: func(key: string, value: list) -> result<_, error>; 20 | 21 | /// Delete the tuple with the specified `key` 22 | /// 23 | /// No error is raised if a tuple did not previously exist for `key`. 24 | delete: func(key: string) -> result<_, error>; 25 | 26 | /// Return whether a tuple exists for the specified `key` 27 | exists: func(key: string) -> result; 28 | 29 | /// Return a list of all the keys 30 | get-keys: func() -> result, error>; 31 | } 32 | 33 | /// The set of errors which may be raised by functions in this interface 34 | variant error { 35 | /// Too many stores have been opened simultaneously. Closing one or more 36 | /// stores prior to retrying may address this. 37 | store-table-full, 38 | 39 | /// The host does not recognize the store label requested. 40 | no-such-store, 41 | 42 | /// The requesting component does not have access to the specified store 43 | /// (which may or may not exist). 44 | access-denied, 45 | 46 | /// Some implementation-specific error has occurred (e.g. I/O) 47 | other(string) 48 | } 49 | } 50 | 51 | world spin-kv { 52 | import key-value; 53 | } -------------------------------------------------------------------------------- /packages/spin-llm/README.md: -------------------------------------------------------------------------------- 1 | # Spin LLM 2 | 3 | This package provides bindings that enable using the Spin LLM interface in apps built using the `@spinframework/build-tools`. -------------------------------------------------------------------------------- /packages/spin-llm/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-llm", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@spinframework/spin-llm", 9 | "version": "1.0.0", 10 | "license": " Apache-2.0 WITH LLVM-exception", 11 | "devDependencies": { 12 | "typescript": "^5.7.3" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.8.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 18 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/spin-llm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-llm", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "type": "module", 8 | "scripts": { 9 | "build": "tsc && cp types/fermyon-spin-llm.d.ts dist/" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": " Apache-2.0 WITH LLVM-exception", 14 | "devDependencies": { 15 | "typescript": "^5.7.3" 16 | }, 17 | "files": [ 18 | "dist", 19 | "wit" 20 | ], 21 | "config": { 22 | "witDependencies": [ 23 | { 24 | "witPath": "./wit", 25 | "package": "fermyon:spin@2.0.0", 26 | "world": "spin-llm" 27 | } 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /packages/spin-llm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "lib": [ 6 | "ES2020" 7 | ], 8 | "moduleResolution": "node", 9 | "declaration": true, 10 | "outDir": "dist", 11 | "strict": true, 12 | "esModuleInterop": true, 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | "dist" 17 | ] 18 | } -------------------------------------------------------------------------------- /packages/spin-mqtt/README.md: -------------------------------------------------------------------------------- 1 | # Spin MQTT 2 | 3 | This package provides bindings that enable using the Spin MQTT interface in apps built using the `@spinframework/build-tools`. -------------------------------------------------------------------------------- /packages/spin-mqtt/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-mqtt", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@spinframework/spin-mqtt", 9 | "version": "1.0.0", 10 | "license": " Apache-2.0 WITH LLVM-exception", 11 | "devDependencies": { 12 | "typescript": "^5.7.3" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.8.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 18 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/spin-mqtt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-mqtt", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "type": "module", 8 | "scripts": { 9 | "build": "tsc && cp -r types/* dist/" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": " Apache-2.0 WITH LLVM-exception", 14 | "devDependencies": { 15 | "typescript": "^5.7.3" 16 | }, 17 | "files": [ 18 | "dist", 19 | "wit" 20 | ], 21 | "config": { 22 | "witDependencies": [ 23 | { 24 | "witPath": "./wit", 25 | "package": "fermyon:spin@2.0.0", 26 | "world": "spin-mqtt" 27 | } 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /packages/spin-mqtt/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as spinMqtt from 'fermyon:spin/mqtt@2.0.0'; 2 | 3 | /** 4 | * Enum representing the Quality of Service (QoS) levels for MQTT. 5 | * @enum {string} 6 | */ 7 | export enum QoS { 8 | /** Messages are delivered at most once. */ 9 | AtMostOnce = 'at-most-once', 10 | /** Messages are delivered at least once. */ 11 | AtLeastOnce = 'at-least-once', 12 | /** Messages are delivered exactly once. */ 13 | ExactlyOnce = 'exactly-once', 14 | } 15 | 16 | /** 17 | * Interface representing an MQTT connection with a method for publishing messages. 18 | * @interface MqttConnection 19 | */ 20 | export interface MqttConnection { 21 | /** 22 | * Publishes a message to the specified MQTT topic. 23 | * @param topic - The topic to publish the message to. 24 | * @param payload - The message payload as a Uint8Array. 25 | * @param qos - The Quality of Service level for message delivery. 26 | */ 27 | publish: (topic: string, payload: Uint8Array, qos: QoS) => void; 28 | } 29 | 30 | /** 31 | * Opens an MQTT connection with the specified parameters. 32 | * @param {string} address - The address of the MQTT broker. 33 | * @param {string} username - The username for the MQTT connection. 34 | * @param {string} password - The password for the MQTT connection. 35 | * @param {bigint} keepAliveIntervalInSecs - The keep-alive interval in seconds. 36 | * @returns {MqttConnection} The MQTT connection object. 37 | */ 38 | export function open( 39 | address: string, 40 | username: string, 41 | password: string, 42 | keepAliveIntervalInSecs: bigint, 43 | ): MqttConnection { 44 | return spinMqtt.Connection.open( 45 | address, 46 | username, 47 | password, 48 | keepAliveIntervalInSecs, 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /packages/spin-mqtt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "lib": [ 6 | "ES2020", 7 | "WebWorker" 8 | ], 9 | "moduleResolution": "node", 10 | "declaration": true, 11 | "outDir": "dist", 12 | "strict": true, 13 | "esModuleInterop": true, 14 | }, 15 | "exclude": [ 16 | "node_modules", 17 | "dist" 18 | ] 19 | } -------------------------------------------------------------------------------- /packages/spin-mqtt/types/fermyon-spin-mqtt.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'fermyon:spin/mqtt@2.0.0' { 2 | /** 3 | * Errors related to interacting with Mqtt 4 | */ 5 | export type Error = 6 | | ErrorInvalidAddress 7 | | ErrorTooManyConnections 8 | | ErrorConnectionFailed 9 | | ErrorOther; 10 | /** 11 | * An invalid address string 12 | */ 13 | export interface ErrorInvalidAddress { 14 | tag: 'invalid-address'; 15 | } 16 | /** 17 | * There are too many open connections 18 | */ 19 | export interface ErrorTooManyConnections { 20 | tag: 'too-many-connections'; 21 | } 22 | /** 23 | * Connection failure e.g. address not allowed. 24 | */ 25 | export interface ErrorConnectionFailed { 26 | tag: 'connection-failed'; 27 | val: string; 28 | } 29 | /** 30 | * Some other error occurred 31 | */ 32 | export interface ErrorOther { 33 | tag: 'other'; 34 | val: string; 35 | } 36 | /** 37 | * QoS for publishing Mqtt messages 38 | * # Variants 39 | * 40 | * ## `"at-most-once"` 41 | * 42 | * ## `"at-least-once"` 43 | * 44 | * ## `"exactly-once"` 45 | */ 46 | export type Qos = 'at-most-once' | 'at-least-once' | 'exactly-once'; 47 | /** 48 | * The message payload. 49 | */ 50 | export type Payload = Uint8Array; 51 | 52 | export class Connection { 53 | /** 54 | * Open a connection to the Mqtt instance at `address`. 55 | */ 56 | static open( 57 | address: string, 58 | username: string, 59 | password: string, 60 | keepAliveIntervalInSecs: bigint, 61 | ): Connection; 62 | /** 63 | * Publish an Mqtt message to the specified `topic`. 64 | */ 65 | publish(topic: string, payload: Payload, qos: Qos): void; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /packages/spin-mqtt/wit/world.wit: -------------------------------------------------------------------------------- 1 | package fermyon:spin@2.0.0; 2 | 3 | interface mqtt { 4 | /// Errors related to interacting with Mqtt 5 | variant error { 6 | /// An invalid address string 7 | invalid-address, 8 | /// There are too many open connections 9 | too-many-connections, 10 | /// Connection failure e.g. address not allowed. 11 | connection-failed(string), 12 | /// Some other error occurred 13 | other(string), 14 | } 15 | 16 | /// QoS for publishing Mqtt messages 17 | enum qos { 18 | at-most-once, 19 | at-least-once, 20 | exactly-once, 21 | } 22 | 23 | resource connection { 24 | /// Open a connection to the Mqtt instance at `address`. 25 | open: static func(address: string, username: string, password: string, keep-alive-interval-in-secs: u64) -> result; 26 | 27 | /// Publish an Mqtt message to the specified `topic`. 28 | publish: func(topic: string, payload: payload, qos: qos) -> result<_, error>; 29 | } 30 | 31 | /// The message payload. 32 | type payload = list; 33 | } 34 | 35 | world spin-mqtt { 36 | import mqtt; 37 | } -------------------------------------------------------------------------------- /packages/spin-mysql/README.md: -------------------------------------------------------------------------------- 1 | # Spin MySQL 2 | 3 | This package provides bindings that enable using the Spin MySQL interface in apps built using the `@spinframework/build-tools`. -------------------------------------------------------------------------------- /packages/spin-mysql/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-mysql", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@spinframework/spin-mysql", 9 | "version": "1.0.0", 10 | "license": " Apache-2.0 WITH LLVM-exception", 11 | "devDependencies": { 12 | "typescript": "^5.7.3" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.8.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 18 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/spin-mysql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-mysql", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "type": "module", 8 | "scripts": { 9 | "build": "tsc && cp -r types/* dist/ && cp -r src/types dist/" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": " Apache-2.0 WITH LLVM-exception", 14 | "devDependencies": { 15 | "typescript": "^5.7.3" 16 | }, 17 | "files": [ 18 | "dist", 19 | "wit" 20 | ], 21 | "config": { 22 | "witDependencies": [ 23 | { 24 | "witPath": "./wit", 25 | "package": "fermyon:spin@2.0.0", 26 | "world": "spin-mysql" 27 | } 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /packages/spin-mysql/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as spinMysql from 'fermyon:spin/mysql@2.0.0'; 2 | import { 3 | RdbmsParameterValue, 4 | RdbmsRow, 5 | RdbmsRowSet, 6 | SpinRdbmsRowSet, 7 | } from './types/rdbms'; 8 | import { convertRdbmsToWitTypes } from './rdbmsHelper.js'; 9 | 10 | /** 11 | * Interface representing a MySQL connection with methods for querying and executing statements. 12 | * @interface MysqlConnection 13 | */ 14 | export interface MysqlConnection { 15 | query: (statement: string, params: RdbmsParameterValue[]) => RdbmsRowSet; 16 | execute: (statement: string, params: RdbmsParameterValue[]) => void; 17 | } 18 | 19 | function createMysqlConnection( 20 | connection: spinMysql.Connection, 21 | ): MysqlConnection { 22 | return { 23 | query: (statement: string, params: RdbmsParameterValue[]) => { 24 | let santizedParams = convertRdbmsToWitTypes(params); 25 | let ret = connection.query(statement, santizedParams) as SpinRdbmsRowSet; 26 | let results: RdbmsRowSet = { 27 | columns: ret.columns, 28 | rows: [], 29 | }; 30 | ret.rows.map((k: RdbmsRow, rowIndex: number) => { 31 | results.rows.push({}); 32 | k.map((val, valIndex: number) => { 33 | results.rows[rowIndex][results.columns[valIndex].name] = 34 | val.tag == 'db-null' || val.tag == 'unsupported' ? null : val.val; 35 | }); 36 | }); 37 | return results; 38 | }, 39 | execute: (statement: string, params: RdbmsParameterValue[]) => { 40 | let santizedParams = convertRdbmsToWitTypes(params); 41 | connection.execute(statement, santizedParams); 42 | }, 43 | }; 44 | } 45 | 46 | /** 47 | * Opens a MySQL connection to the specified address. 48 | * @param {string} address - The address of the MySQL server. 49 | * @returns {MysqlConnection} The MySQL connection object. 50 | */ 51 | export function open(address: string): MysqlConnection { 52 | return createMysqlConnection(spinMysql.Connection.open(address)); 53 | } 54 | -------------------------------------------------------------------------------- /packages/spin-mysql/src/rdbmsHelper.ts: -------------------------------------------------------------------------------- 1 | import { RdbmsParameterValue, SpinRdbmsParameterValue } from './types/rdbms'; 2 | 3 | export function convertRdbmsToWitTypes( 4 | parameters: RdbmsParameterValue[], 5 | ): SpinRdbmsParameterValue[] { 6 | let sanitized: SpinRdbmsParameterValue[] = []; 7 | for (let k of parameters) { 8 | if (typeof k === 'object') { 9 | sanitized.push(k as SpinRdbmsParameterValue); 10 | continue; 11 | } 12 | if (typeof k === 'string') { 13 | sanitized.push({ tag: 'str', val: k }); 14 | continue; 15 | } 16 | if (typeof k === null) { 17 | sanitized.push({ tag: 'db-null' }); 18 | continue; 19 | } 20 | if (typeof k === 'boolean') { 21 | sanitized.push({ tag: 'boolean', val: k }); 22 | continue; 23 | } 24 | if (typeof k === 'bigint') { 25 | sanitized.push({ tag: 'int64', val: k }); 26 | continue; 27 | } 28 | if (typeof k === 'number') { 29 | isFloat(k) 30 | ? sanitized.push({ tag: 'floating64', val: k }) 31 | : sanitized.push({ tag: 'int32', val: k }); 32 | continue; 33 | } 34 | if ((k as any) instanceof Uint8Array) { 35 | sanitized.push({ tag: 'binary', val: k }); 36 | continue; 37 | } 38 | } 39 | return sanitized; 40 | } 41 | 42 | function isFloat(number: number) { 43 | return number % 1 !== 0; 44 | } 45 | -------------------------------------------------------------------------------- /packages/spin-mysql/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "lib": [ 6 | "ES2020", 7 | "WebWorker" 8 | ], 9 | "moduleResolution": "node", 10 | "declaration": true, 11 | "outDir": "dist", 12 | "strict": true, 13 | "esModuleInterop": true, 14 | }, 15 | "exclude": [ 16 | "node_modules", 17 | "dist" 18 | ] 19 | } -------------------------------------------------------------------------------- /packages/spin-mysql/wit/world.wit: -------------------------------------------------------------------------------- 1 | package fermyon:spin@2.0.0; 2 | 3 | interface mysql { 4 | variant error { 5 | connection-failed(string), 6 | bad-parameter(string), 7 | query-failed(string), 8 | value-conversion-failed(string), 9 | other(string) 10 | } 11 | 12 | /// Data types for a database column 13 | enum db-data-type { 14 | boolean, 15 | int8, 16 | int16, 17 | int32, 18 | int64, 19 | uint8, 20 | uint16, 21 | uint32, 22 | uint64, 23 | floating32, 24 | floating64, 25 | str, 26 | binary, 27 | other, 28 | } 29 | 30 | /// Database values 31 | variant db-value { 32 | boolean(bool), 33 | int8(s8), 34 | int16(s16), 35 | int32(s32), 36 | int64(s64), 37 | uint8(u8), 38 | uint16(u16), 39 | uint32(u32), 40 | uint64(u64), 41 | floating32(f32), 42 | floating64(f64), 43 | str(string), 44 | binary(list), 45 | db-null, 46 | unsupported, 47 | } 48 | 49 | /// Values used in parameterized queries 50 | variant parameter-value { 51 | boolean(bool), 52 | int8(s8), 53 | int16(s16), 54 | int32(s32), 55 | int64(s64), 56 | uint8(u8), 57 | uint16(u16), 58 | uint32(u32), 59 | uint64(u64), 60 | floating32(f32), 61 | floating64(f64), 62 | str(string), 63 | binary(list), 64 | db-null, 65 | } 66 | 67 | /// A database column 68 | record column { 69 | name: string, 70 | data-type: db-data-type, 71 | } 72 | 73 | /// A database row 74 | type row = list; 75 | 76 | /// A set of database rows 77 | record row-set { 78 | columns: list, 79 | rows: list, 80 | } 81 | 82 | /// A connection to a MySQL database. 83 | resource connection { 84 | /// Open a connection to the MySQL instance at `address`. 85 | open: static func(address: string) -> result; 86 | 87 | /// query the database: select 88 | query: func(statement: string, params: list) -> result; 89 | 90 | /// execute command to the database: insert, update, delete 91 | execute: func(statement: string, params: list) -> result<_, error>; 92 | } 93 | } 94 | 95 | world spin-mysql { 96 | import mysql; 97 | } -------------------------------------------------------------------------------- /packages/spin-postgres/README.md: -------------------------------------------------------------------------------- 1 | # Spin Postgres 2 | 3 | This package provides bindings that enable using the Spin Postgres interface in apps built using the `@spinframework/build-tools`. -------------------------------------------------------------------------------- /packages/spin-postgres/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-postgres", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@spinframework/spin-postgres", 9 | "version": "1.0.0", 10 | "license": " Apache-2.0 WITH LLVM-exception", 11 | "devDependencies": { 12 | "typescript": "^5.7.3" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.8.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 18 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/spin-postgres/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-postgres", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "type": "module", 8 | "scripts": { 9 | "build": "tsc && cp -r types/* dist/ && cp -r src/types dist/" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": " Apache-2.0 WITH LLVM-exception", 14 | "devDependencies": { 15 | "typescript": "^5.7.3" 16 | }, 17 | "files": [ 18 | "dist", 19 | "wit" 20 | ], 21 | "config": { 22 | "witDependencies": [ 23 | { 24 | "witPath": "./wit", 25 | "package": "fermyon:spin@2.0.0", 26 | "world": "spin-postgres" 27 | } 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /packages/spin-postgres/src/rdbmsHelper.ts: -------------------------------------------------------------------------------- 1 | import { RdbmsParameterValue, SpinRdbmsParameterValue } from './types/rdbms'; 2 | 3 | export function convertRdbmsToWitTypes( 4 | parameters: RdbmsParameterValue[], 5 | ): SpinRdbmsParameterValue[] { 6 | let sanitized: SpinRdbmsParameterValue[] = []; 7 | for (let k of parameters) { 8 | if (typeof k === 'object') { 9 | sanitized.push(k as SpinRdbmsParameterValue); 10 | continue; 11 | } 12 | if (typeof k === 'string') { 13 | sanitized.push({ tag: 'str', val: k }); 14 | continue; 15 | } 16 | if (typeof k === null) { 17 | sanitized.push({ tag: 'db-null' }); 18 | continue; 19 | } 20 | if (typeof k === 'boolean') { 21 | sanitized.push({ tag: 'boolean', val: k }); 22 | continue; 23 | } 24 | if (typeof k === 'bigint') { 25 | sanitized.push({ tag: 'int64', val: k }); 26 | continue; 27 | } 28 | if (typeof k === 'number') { 29 | isFloat(k) 30 | ? sanitized.push({ tag: 'floating64', val: k }) 31 | : sanitized.push({ tag: 'int32', val: k }); 32 | continue; 33 | } 34 | if ((k as any) instanceof Uint8Array) { 35 | sanitized.push({ tag: 'binary', val: k }); 36 | continue; 37 | } 38 | } 39 | return sanitized; 40 | } 41 | 42 | function isFloat(number: number) { 43 | return number % 1 !== 0; 44 | } 45 | -------------------------------------------------------------------------------- /packages/spin-postgres/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "lib": [ 6 | "ES2020", 7 | "WebWorker" 8 | ], 9 | "moduleResolution": "node", 10 | "declaration": true, 11 | "outDir": "dist", 12 | "strict": true, 13 | "esModuleInterop": true, 14 | }, 15 | "exclude": [ 16 | "node_modules", 17 | "dist" 18 | ] 19 | } -------------------------------------------------------------------------------- /packages/spin-postgres/wit/world.wit: -------------------------------------------------------------------------------- 1 | package fermyon:spin@2.0.0; 2 | 3 | interface postgres { 4 | /// Errors related to interacting with a database. 5 | variant error { 6 | connection-failed(string), 7 | bad-parameter(string), 8 | query-failed(string), 9 | value-conversion-failed(string), 10 | other(string) 11 | } 12 | 13 | /// Data types for a database column 14 | enum db-data-type { 15 | boolean, 16 | int8, 17 | int16, 18 | int32, 19 | int64, 20 | uint8, 21 | uint16, 22 | uint32, 23 | uint64, 24 | floating32, 25 | floating64, 26 | str, 27 | binary, 28 | other, 29 | } 30 | 31 | /// Database values 32 | variant db-value { 33 | boolean(bool), 34 | int8(s8), 35 | int16(s16), 36 | int32(s32), 37 | int64(s64), 38 | uint8(u8), 39 | uint16(u16), 40 | uint32(u32), 41 | uint64(u64), 42 | floating32(f32), 43 | floating64(f64), 44 | str(string), 45 | binary(list), 46 | db-null, 47 | unsupported, 48 | } 49 | 50 | /// Values used in parameterized queries 51 | variant parameter-value { 52 | boolean(bool), 53 | int8(s8), 54 | int16(s16), 55 | int32(s32), 56 | int64(s64), 57 | uint8(u8), 58 | uint16(u16), 59 | uint32(u32), 60 | uint64(u64), 61 | floating32(f32), 62 | floating64(f64), 63 | str(string), 64 | binary(list), 65 | db-null, 66 | } 67 | 68 | /// A database column 69 | record column { 70 | name: string, 71 | data-type: db-data-type, 72 | } 73 | 74 | /// A database row 75 | type row = list; 76 | 77 | /// A set of database rows 78 | record row-set { 79 | columns: list, 80 | rows: list, 81 | } 82 | 83 | 84 | /// A connection to a postgres database. 85 | resource connection { 86 | /// Open a connection to the Postgres instance at `address`. 87 | open: static func(address: string) -> result; 88 | 89 | /// Query the database. 90 | query: func(statement: string, params: list) -> result; 91 | 92 | /// Execute command to the database. 93 | execute: func(statement: string, params: list) -> result; 94 | } 95 | } 96 | 97 | world spin-postgres { 98 | import postgres; 99 | } -------------------------------------------------------------------------------- /packages/spin-postgres3/README.md: -------------------------------------------------------------------------------- 1 | # Spin Postgres v3 2 | 3 | This package provides bindings that enable using the Spin Postgres v3 interface in apps built using the `@spinframework/build-tools`. -------------------------------------------------------------------------------- /packages/spin-postgres3/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-postgres3", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@spinframework/spin-postgres3", 9 | "version": "1.0.0", 10 | "license": " Apache-2.0 WITH LLVM-exception", 11 | "devDependencies": { 12 | "typescript": "^5.7.3" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.8.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 18 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/spin-postgres3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-postgres3", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "type": "module", 8 | "scripts": { 9 | "build": "tsc && cp -r types/* dist/" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": " Apache-2.0 WITH LLVM-exception", 14 | "devDependencies": { 15 | "typescript": "^5.7.3" 16 | }, 17 | "files": [ 18 | "dist", 19 | "wit" 20 | ], 21 | "config": { 22 | "witDependencies": [ 23 | { 24 | "witPath": "./wit", 25 | "package": "spin:postgres@3.0.0", 26 | "world": "spin-postgres3" 27 | } 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /packages/spin-postgres3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "lib": [ 6 | "ES2020", 7 | "WebWorker" 8 | ], 9 | "moduleResolution": "node", 10 | "declaration": true, 11 | "outDir": "dist", 12 | "strict": true, 13 | "esModuleInterop": true, 14 | }, 15 | "exclude": [ 16 | "node_modules", 17 | "dist" 18 | ] 19 | } -------------------------------------------------------------------------------- /packages/spin-redis/README.md: -------------------------------------------------------------------------------- 1 | # Spin Redis 2 | 3 | This package provides bindings that enables using the Spin redis interface in apps built using the `@spinframework/build-tools`. -------------------------------------------------------------------------------- /packages/spin-redis/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-redis", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@spinframework/spin-redis", 9 | "version": "1.0.0", 10 | "license": " Apache-2.0 WITH LLVM-exception", 11 | "devDependencies": { 12 | "typescript": "^5.7.3" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.8.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 18 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/spin-redis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-redis", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "type": "module", 8 | "scripts": { 9 | "build": "tsc && cp types/fermyon-spin-redis.d.ts dist/" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": " Apache-2.0 WITH LLVM-exception", 14 | "devDependencies": { 15 | "typescript": "^5.7.3" 16 | }, 17 | "files": [ 18 | "dist", 19 | "wit" 20 | ], 21 | "config": { 22 | "witDependencies": [ 23 | { 24 | "witPath": "./wit", 25 | "package": "fermyon:spin@2.0.0", 26 | "world": "spin-redis" 27 | } 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /packages/spin-redis/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "lib": [ 6 | "ES2020" 7 | ], 8 | "moduleResolution": "node", 9 | "declaration": true, 10 | "outDir": "dist", 11 | "strict": true, 12 | "esModuleInterop": true, 13 | "typeRoots": [ 14 | "./src/@types", 15 | "./node_modules/@types" 16 | ] 17 | }, 18 | "include": [ 19 | "src/**/*.ts", 20 | "src/**/*.d.ts" 21 | ], 22 | "exclude": [ 23 | "node_modules", 24 | "dist" 25 | ] 26 | } -------------------------------------------------------------------------------- /packages/spin-sqlite/README.md: -------------------------------------------------------------------------------- 1 | # Spin SQLite 2 | 3 | This package provides bindings that enable using the Spin SQLite interface in apps built using the `@spinframework/build-tools`. -------------------------------------------------------------------------------- /packages/spin-sqlite/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-sqlite", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@spinframework/spin-sqlite", 9 | "version": "1.0.0", 10 | "license": " Apache-2.0 WITH LLVM-exception", 11 | "devDependencies": { 12 | "typescript": "^5.7.3" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.8.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 18 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/spin-sqlite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-sqlite", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "type": "module", 8 | "scripts": { 9 | "build": "tsc && cp -r types/* dist/" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": " Apache-2.0 WITH LLVM-exception", 14 | "devDependencies": { 15 | "typescript": "^5.7.3" 16 | }, 17 | "files": [ 18 | "dist", 19 | "wit" 20 | ], 21 | "config": { 22 | "witDependencies": [ 23 | { 24 | "witPath": "./wit", 25 | "package": "fermyon:spin@2.0.0", 26 | "world": "spin-sqlite" 27 | } 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /packages/spin-sqlite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "lib": [ 6 | "ES2020", 7 | "WebWorker" 8 | ], 9 | "moduleResolution": "node", 10 | "declaration": true, 11 | "outDir": "dist", 12 | "strict": true, 13 | "esModuleInterop": true, 14 | }, 15 | "exclude": [ 16 | "node_modules", 17 | "dist" 18 | ] 19 | } -------------------------------------------------------------------------------- /packages/spin-sqlite/wit/world.wit: -------------------------------------------------------------------------------- 1 | package fermyon:spin@2.0.0; 2 | 3 | interface sqlite { 4 | /// A handle to an open sqlite instance 5 | resource connection { 6 | /// Open a connection to a named database instance. 7 | /// 8 | /// If `database` is "default", the default instance is opened. 9 | /// 10 | /// `error::no-such-database` will be raised if the `name` is not recognized. 11 | open: static func(database: string) -> result; 12 | 13 | /// Execute a statement returning back data if there is any 14 | execute: func(statement: string, parameters: list) -> result; 15 | } 16 | 17 | /// The set of errors which may be raised by functions in this interface 18 | variant error { 19 | /// The host does not recognize the database name requested. 20 | no-such-database, 21 | /// The requesting component does not have access to the specified database (which may or may not exist). 22 | access-denied, 23 | /// The provided connection is not valid 24 | invalid-connection, 25 | /// The database has reached its capacity 26 | database-full, 27 | /// Some implementation-specific error has occurred (e.g. I/O) 28 | io(string) 29 | } 30 | 31 | /// A result of a query 32 | record query-result { 33 | /// The names of the columns retrieved in the query 34 | columns: list, 35 | /// the row results each containing the values for all the columns for a given row 36 | rows: list, 37 | } 38 | 39 | /// A set of values for each of the columns in a query-result 40 | record row-result { 41 | values: list 42 | } 43 | 44 | /// A single column's result from a database query 45 | variant value { 46 | integer(s64), 47 | real(f64), 48 | text(string), 49 | blob(list), 50 | null 51 | } 52 | } 53 | 54 | world spin-sqlite { 55 | import sqlite; 56 | } -------------------------------------------------------------------------------- /packages/spin-trigger-redis/README.md: -------------------------------------------------------------------------------- 1 | # Spin Redis Trigger 2 | 3 | This package enables adding the `fermyon:spin/incoming-redis` trigger export to apps built using the `@spinframework/build-tools`. -------------------------------------------------------------------------------- /packages/spin-trigger-redis/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-trigger-redis", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@spinframework/spin-trigger-redis", 9 | "version": "1.0.0", 10 | "license": " Apache-2.0 WITH LLVM-exception", 11 | "devDependencies": { 12 | "typescript": "^5.7.3" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.8.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 18 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/spin-trigger-redis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-trigger-redis", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "type": "module", 8 | "scripts": { 9 | "build": "tsc" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": " Apache-2.0 WITH LLVM-exception", 14 | "devDependencies": { 15 | "typescript": "^5.7.3" 16 | }, 17 | "files": [ 18 | "dist", 19 | "wit" 20 | ], 21 | "config": { 22 | "witDependencies": [ 23 | { 24 | "witPath": "./wit", 25 | "package": "fermyon:spin", 26 | "world": "spin-trigger-redis" 27 | } 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /packages/spin-trigger-redis/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Abstract class for handling Redis messages. 3 | */ 4 | export abstract class RedisHandler { 5 | /** 6 | * Handles a Redis message. 7 | * @param msg - The message data as a Uint8Array. 8 | * @returns A Promise that resolves when the message has been handled. 9 | */ 10 | abstract handleMessage(msg: Uint8Array): Promise; 11 | } 12 | -------------------------------------------------------------------------------- /packages/spin-trigger-redis/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "lib": [ 6 | "ES2020", 7 | "WebWorker" 8 | ], 9 | "moduleResolution": "node", 10 | "declaration": true, 11 | "outDir": "dist", 12 | "strict": true, 13 | "esModuleInterop": true, 14 | }, 15 | "exclude": [ 16 | "node_modules", 17 | "dist" 18 | ] 19 | } -------------------------------------------------------------------------------- /packages/spin-trigger-redis/wit/redis-types.wit: -------------------------------------------------------------------------------- 1 | interface redis-types { 2 | // General purpose error. 3 | enum error { 4 | success, 5 | error, 6 | } 7 | 8 | /// The message payload. 9 | type payload = list; 10 | 11 | /// A parameter type for the general-purpose `execute` function. 12 | variant redis-parameter { 13 | int64(s64), 14 | binary(payload) 15 | } 16 | 17 | /// A return type for the general-purpose `execute` function. 18 | variant redis-result { 19 | nil, 20 | status(string), 21 | int64(s64), 22 | binary(payload) 23 | } 24 | } -------------------------------------------------------------------------------- /packages/spin-trigger-redis/wit/world.wit: -------------------------------------------------------------------------------- 1 | package fermyon:spin; 2 | 3 | interface inbound-redis { 4 | use redis-types.{payload, error}; 5 | 6 | // The entrypoint for a Redis handler. 7 | handle-message: func(message: payload) -> result<_, error>; 8 | } 9 | 10 | world spin-trigger-redis { 11 | export inbound-redis; 12 | } -------------------------------------------------------------------------------- /packages/spin-variables/README.md: -------------------------------------------------------------------------------- 1 | # Spin-KV 2 | 3 | This package provides bindings that enables using the Spin Key Value interface in apps built using the `@spinframework/build-tools`. -------------------------------------------------------------------------------- /packages/spin-variables/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-variables", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@spinframework/spin-variables", 9 | "version": "1.0.0", 10 | "license": " Apache-2.0 WITH LLVM-exception", 11 | "devDependencies": { 12 | "typescript": "^5.7.3" 13 | } 14 | }, 15 | "node_modules/typescript": { 16 | "version": "5.8.2", 17 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 18 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 19 | "dev": true, 20 | "license": "Apache-2.0", 21 | "bin": { 22 | "tsc": "bin/tsc", 23 | "tsserver": "bin/tsserver" 24 | }, 25 | "engines": { 26 | "node": ">=14.17" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/spin-variables/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@spinframework/spin-variables", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "type": "module", 8 | "scripts": { 9 | "build": "tsc" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": " Apache-2.0 WITH LLVM-exception", 14 | "devDependencies": { 15 | "typescript": "^5.7.3" 16 | }, 17 | "files": [ 18 | "dist", 19 | "wit" 20 | ], 21 | "config": { 22 | "witDependencies": [ 23 | { 24 | "witPath": "./wit", 25 | "package": "fermyon:spin@2.0.0", 26 | "world": "spin-variables" 27 | } 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /packages/spin-variables/src/index.ts: -------------------------------------------------------------------------------- 1 | import { get as spinGet } from 'fermyon:spin/variables@2.0.0'; 2 | 3 | /** 4 | * Gets the value of a variable if it exists, otherwise returns null. 5 | * @param {string} key - The key of the variable to retrieve. 6 | * @returns {string | null} The value of the variable, or null if it does not exist or an error occurs. 7 | */ 8 | export function get(key: string): string | null { 9 | try { 10 | return spinGet(key); 11 | } catch (e) { 12 | return null; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/spin-variables/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "lib": [ 6 | "ES2020" 7 | ], 8 | "moduleResolution": "node", 9 | "declaration": true, 10 | "outDir": "dist", 11 | "strict": true, 12 | "esModuleInterop": true, 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | "dist" 17 | ] 18 | } -------------------------------------------------------------------------------- /packages/spin-variables/types/fermyon-spin-variables.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'fermyon:spin/variables@2.0.0' { 2 | /** 3 | * Get an application variable value for the current component. 4 | * 5 | * The name must match one defined in in the component manifest. 6 | */ 7 | export function get(name: string): string; 8 | } 9 | -------------------------------------------------------------------------------- /packages/spin-variables/wit/world.wit: -------------------------------------------------------------------------------- 1 | package fermyon:spin@2.0.0; 2 | 3 | interface variables { 4 | /// Get an application variable value for the current component. 5 | /// 6 | /// The name must match one defined in in the component manifest. 7 | get: func(name: string) -> result; 8 | 9 | /// The set of errors which may be raised by functions in this interface. 10 | variant error { 11 | /// The provided variable name is invalid. 12 | invalid-name(string), 13 | /// The provided variable is undefined. 14 | undefined(string), 15 | /// A variables provider specific error has occurred. 16 | provider(string), 17 | /// Some implementation-specific error has occurred. 18 | other(string), 19 | } 20 | } 21 | 22 | world spin-variables{ 23 | import variables; 24 | } -------------------------------------------------------------------------------- /templates/http-js/content/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /templates/http-js/content/README.md: -------------------------------------------------------------------------------- 1 | # `http-js` Template 2 | 3 | A starter template for building JavaScript HTTP applications with Spin. 4 | 5 | ## Getting Started 6 | 7 | Build the App 8 | 9 | ```bash 10 | spin build 11 | ``` 12 | 13 | ## Run the App 14 | 15 | ```bash 16 | spin up 17 | ``` 18 | 19 | ## Using Spin Interfaces 20 | 21 | To use additional Spin interfaces, install the corresponding packages: 22 | 23 | | Interface | Package | 24 | |---------------|---------------------------------| 25 | | Key-Value | `@spinframework/spin-kv` | 26 | | LLM | `@spinframework/spin-llm` | 27 | | MQTT | `@spinframework/spin-mqtt` | 28 | | MySQL | `@spinframework/spin-mysql` | 29 | | PostgreSQL | `@spinframework/spin-postgres` | 30 | | Redis | `@spinframework/spin-redis` | 31 | | SQLite | `@spinframework/spin-sqlite` | 32 | | Variables | `@spinframework/spin-variables` | -------------------------------------------------------------------------------- /templates/http-js/content/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{project-name | kebab_case}}", 3 | "version": "1.0.0", 4 | "description": "{{project-description}}", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/{{ project-name | kebab_case }}.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "webpack": "^5.74.0", 16 | "webpack-cli": "^4.10.0" 17 | }, 18 | "dependencies": { 19 | {%- case http-router -%} 20 | {% when "hono" %} 21 | "hono": "^4.7.4", 22 | {% when "itty" %} 23 | "itty-router": "^5.0.18", 24 | {%- endcase %} 25 | "@spinframework/build-tools": "^1.0.1", 26 | "@spinframework/wasi-http-proxy": "^1.0.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /templates/http-js/content/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["{{authors}}"] 5 | description = "{{project-description}}" 6 | name = "{{project-name}}" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "{{http-path}}" 11 | component = "{{project-name | kebab_case}}" 12 | 13 | [component.{{project-name | kebab_case}}] 14 | source = "dist/{{project-name | kebab_case}}.wasm" 15 | exclude_files = ["**/node_modules"] 16 | [component.{{project-name | kebab_case}}.build] 17 | command = ["npm install", "npm run build"] 18 | watch = ["src/**/*.js"] -------------------------------------------------------------------------------- /templates/http-js/content/src/index.js.tmpl: -------------------------------------------------------------------------------- 1 | {%- case http-router -%} 2 | {% when "hono" %} 3 | // For Hono documentation refer to https://hono.dev/docs/ 4 | import { Hono } from 'hono'; 5 | import { logger } from 'hono/logger'; 6 | 7 | let app = new Hono(); 8 | 9 | // Logging to stdout via built-in middleware 10 | app.use(logger()) 11 | 12 | // Example of a custom middleware to set HTTP response header 13 | app.use(async (c, next) => { 14 | c.header('server', 'Spin CLI') 15 | await next(); 16 | }) 17 | 18 | app.get('/', (c) => c.text('Hello, Spin!')); 19 | app.get('/hello/:name', (c) => { 20 | return c.json({ message: `Hello, ${c.req.param('name')}` }) 21 | }); 22 | 23 | app.fire(); 24 | {% when "itty" %} 25 | // For AutoRouter documentation refer to https://itty.dev/itty-router/routers/autorouter 26 | import { AutoRouter } from 'itty-router'; 27 | 28 | let router = AutoRouter(); 29 | 30 | // Route ordering matters, the first route that matches will be used 31 | // Any route that does not return will be treated as a middleware 32 | // Any unmatched route will return a 404 33 | router 34 | .get('/', () => new Response('Hello, Spin!')) 35 | .get('/hello/:name', ({ name }) => `Hello, ${name}!`) 36 | 37 | addEventListener('fetch', (event) => { 38 | event.respondWith(router.fetch(event.request)); 39 | }); 40 | {% else %} 41 | function handle(_request) { 42 | return new Response('Hello, Spin!', { 43 | status: 200, 44 | headers: { 45 | 'content-type': 'text/plain' 46 | } 47 | }) 48 | } 49 | 50 | addEventListener('fetch', (event) => { 51 | event.respondWith(handle(event.request)); 52 | }); 53 | {%- endcase %} 54 | -------------------------------------------------------------------------------- /templates/http-js/content/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.js', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | resolve: { 14 | extensions: ['.js'], 15 | }, 16 | output: { 17 | path: path.resolve(process.cwd(), './build'), 18 | filename: 'bundle.js', 19 | module: true, 20 | library: { 21 | type: "module", 22 | } 23 | }, 24 | plugins: [ 25 | SpinPlugin 26 | ], 27 | optimization: { 28 | minimize: false 29 | }, 30 | }; 31 | } 32 | export default config -------------------------------------------------------------------------------- /templates/http-js/metadata/snippets/component.txt: -------------------------------------------------------------------------------- 1 | [[trigger.http]] 2 | route = "{{http-path}}" 3 | component = "{{project-name | kebab_case}}" 4 | 5 | [component.{{project-name | kebab_case}}] 6 | source = "{{ output-path }}/dist/{{project-name | kebab_case}}.wasm" 7 | allowed_outbound_hosts = [] 8 | 9 | [component.{{project-name | kebab_case}}.build] 10 | command = "npm install && npm run build" 11 | workdir = "{{ output-path }}" 12 | -------------------------------------------------------------------------------- /templates/http-js/metadata/spin-template.toml: -------------------------------------------------------------------------------- 1 | manifest_version = "1" 2 | id = "http-js" 3 | description = "HTTP request handler using JavaScript" 4 | 5 | [add_component] 6 | skip_files = ["spin.toml"] 7 | [add_component.snippets] 8 | component = "component.txt" 9 | 10 | [parameters] 11 | project-description = { type = "string", prompt = "Description", default = "" } 12 | http-path = { type = "string", prompt = "HTTP path", default = "/...", pattern = "^/\\S*$" } 13 | http-router = { type = "string", prompt = "HTTP Router", default = "itty", allowed_values = [ 14 | "itty", 15 | "hono", 16 | "none", 17 | ] } 18 | -------------------------------------------------------------------------------- /templates/http-ts/content/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /templates/http-ts/content/README.md: -------------------------------------------------------------------------------- 1 | # `http-ts` Template 2 | 3 | A starter template for building TypeScript HTTP applications with Spin. 4 | 5 | ## Getting Started 6 | 7 | Build the App 8 | 9 | ```bash 10 | spin build 11 | ``` 12 | 13 | ## Run the App 14 | 15 | ```bash 16 | spin up 17 | ``` 18 | 19 | ## Using Spin Interfaces 20 | 21 | To use additional Spin interfaces, install the corresponding packages: 22 | 23 | | Interface | Package | 24 | |---------------|---------------------------------| 25 | | Key-Value | `@spinframework/spin-kv` | 26 | | LLM | `@spinframework/spin-llm` | 27 | | MQTT | `@spinframework/spin-mqtt` | 28 | | MySQL | `@spinframework/spin-mysql` | 29 | | PostgreSQL | `@spinframework/spin-postgres` | 30 | | Redis | `@spinframework/spin-redis` | 31 | | SQLite | `@spinframework/spin-sqlite` | 32 | | Variables | `@spinframework/spin-variables` | -------------------------------------------------------------------------------- /templates/http-ts/content/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{project-name | kebab_case}}", 3 | "version": "1.0.0", 4 | "description": "{{project-description}}", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/{{ project-name | kebab_case }}.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0" 19 | }, 20 | "dependencies": { 21 | {%- case http-router -%} 22 | {% when "hono" %} 23 | "hono": "^4.7.4", 24 | {% when "itty" %} 25 | "itty-router": "^5.0.18", 26 | {%- endcase %} 27 | "@spinframework/build-tools": "^1.0.1", 28 | "@spinframework/wasi-http-proxy": "^1.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /templates/http-ts/content/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["{{authors}}"] 5 | description = "{{project-description}}" 6 | name = "{{project-name}}" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "{{http-path}}" 11 | component = "{{project-name | kebab_case}}" 12 | 13 | [component.{{project-name | kebab_case}}] 14 | source = "dist/{{project-name | kebab_case}}.wasm" 15 | exclude_files = ["**/node_modules"] 16 | [component.{{project-name | kebab_case}}.build] 17 | command = ["npm install", "npm run build"] 18 | watch = ["src/**/*.ts"] -------------------------------------------------------------------------------- /templates/http-ts/content/src/index.ts.tmpl: -------------------------------------------------------------------------------- 1 | {%- case http-router -%} 2 | {% when "hono" %} 3 | // For Hono documentation refer to https://hono.dev/docs/ 4 | import { Hono } from 'hono'; 5 | import type { Context, Next } from 'hono' 6 | import { logger } from 'hono/logger'; 7 | 8 | let app = new Hono(); 9 | 10 | // Logging to stdout via built-in middleware 11 | app.use(logger()) 12 | 13 | // Example of a custom middleware to set HTTP response header 14 | app.use(async (c: Context, next: Next) => { 15 | c.header('server', 'Spin CLI') 16 | await next(); 17 | }) 18 | 19 | app.get('/', (c: Context) => c.text('Hello, Spin!')); 20 | app.get('/hello/:name', (c: Context) => { 21 | return c.json({ message: `Hello, ${c.req.param('name')}!` }) 22 | }); 23 | 24 | app.fire(); 25 | {% when "itty" %} 26 | // For AutoRouter documentation refer to https://itty.dev/itty-router/routers/autorouter 27 | import { AutoRouter } from 'itty-router'; 28 | 29 | let router = AutoRouter(); 30 | 31 | // Route ordering matters, the first route that matches will be used 32 | // Any route that does not return will be treated as a middleware 33 | // Any unmatched route will return a 404 34 | router 35 | .get('/', () => new Response('Hello, Spin!')) 36 | .get('/hello/:name', ({ name }) => `Hello, ${name}!`) 37 | 38 | //@ts-ignore 39 | addEventListener('fetch', (event: FetchEvent) => { 40 | event.respondWith(router.fetch(event.request)); 41 | }); 42 | {% else %} 43 | function handle(_request: Request): Response { 44 | return new Response('Hello, Spin!', { 45 | status: 200, 46 | headers: { 47 | 'content-type': 'text/plain' 48 | } 49 | }) 50 | } 51 | 52 | //@ts-ignore 53 | addEventListener('fetch', (event: FetchEvent) => { 54 | event.respondWith(handle(event.request)); 55 | }); 56 | {%- endcase %} 57 | -------------------------------------------------------------------------------- /templates/http-ts/content/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | }, 18 | "include": [ 19 | "src/**/*" 20 | ] 21 | } -------------------------------------------------------------------------------- /templates/http-ts/content/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /templates/http-ts/metadata/snippets/component.txt: -------------------------------------------------------------------------------- 1 | [[trigger.http]] 2 | route = "{{http-path}}" 3 | component = "{{project-name | kebab_case}}" 4 | 5 | [component.{{project-name | kebab_case}}] 6 | source = "{{ output-path }}/dist/{{project-name | kebab_case}}.wasm" 7 | allowed_outbound_hosts = [] 8 | 9 | [component.{{project-name | kebab_case}}.build] 10 | command = "npm install && npm run build" 11 | workdir = "{{ output-path }}" -------------------------------------------------------------------------------- /templates/http-ts/metadata/spin-template.toml: -------------------------------------------------------------------------------- 1 | manifest_version = "1" 2 | id = "http-ts" 3 | description = "HTTP request handler using TypeScript" 4 | 5 | [add_component] 6 | skip_files = ["spin.toml"] 7 | [add_component.snippets] 8 | component = "component.txt" 9 | 10 | [parameters] 11 | project-description = { type = "string", prompt = "Description", default = "" } 12 | http-path = { type = "string", prompt = "HTTP path", default = "/...", pattern = "^/\\S*$" } 13 | http-router = { type = "string", prompt = "HTTP Router", default = "itty", allowed_values = [ 14 | "itty", 15 | "hono", 16 | "none", 17 | ] } 18 | -------------------------------------------------------------------------------- /templates/redis-js/content/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /templates/redis-js/content/README.md: -------------------------------------------------------------------------------- 1 | # `redis-js` Template 2 | 3 | A starter template for building TypeScript redis applications with Spin. 4 | 5 | ## Getting Started 6 | 7 | Build the App 8 | 9 | ```bash 10 | spin build 11 | ``` 12 | 13 | ## Run the App 14 | 15 | ```bash 16 | spin up 17 | ``` 18 | 19 | ## Using Spin Interfaces 20 | 21 | To use additional Spin interfaces, install the corresponding packages: 22 | 23 | | Interface | Package | 24 | |---------------|---------------------------------| 25 | | Key-Value | `@spinframework/spin-kv` | 26 | | LLM | `@spinframework/spin-llm` | 27 | | MQTT | `@spinframework/spin-mqtt` | 28 | | MySQL | `@spinframework/spin-mysql` | 29 | | PostgreSQL | `@spinframework/spin-postgres` | 30 | | Redis | `@spinframework/spin-redis` | 31 | | SQLite | `@spinframework/spin-sqlite` | 32 | | Variables | `@spinframework/spin-variables` | -------------------------------------------------------------------------------- /templates/redis-js/content/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{project-name | kebab_case}}", 3 | "version": "1.0.0", 4 | "description": "{{project-description}}", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/{{ project-name | kebab_case }}.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "postinstall": "knitwit --out-dir build/wit/knitwit --out-world combined" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "mkdirp": "^3.0.1", 16 | "webpack": "^5.74.0", 17 | "webpack-cli": "^4.10.0" 18 | }, 19 | "dependencies": { 20 | "@spinframework/build-tools": "^1.0.1", 21 | "@spinframework/spin-trigger-redis": "^1.0.0" 22 | } 23 | } -------------------------------------------------------------------------------- /templates/redis-js/content/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | name = "{{project-name | kebab_case}}" 5 | version = "0.1.0" 6 | authors = ["{{authors}}"] 7 | description = "{{project-description}}" 8 | 9 | [application.trigger.redis] 10 | address = "{{redis-address}}" 11 | 12 | [[trigger.redis]] 13 | channel = "{{redis-channel}}" 14 | component = "{{project-name | kebab_case}}" 15 | 16 | [component.{{project-name | kebab_case}}] 17 | source = "dist/{{project-name | kebab_case}}.wasm" 18 | exclude_files = ["**/node_modules"] 19 | [component.{{project-name | kebab_case}}.build] 20 | command = "npm install && npm run build" 21 | watch = ["src/**/*.ts", "package.json"] 22 | -------------------------------------------------------------------------------- /templates/redis-js/content/src/index.js: -------------------------------------------------------------------------------- 1 | import { RedisHandler } from "@fermyon/spin-sdk"; 2 | 3 | const decoder = new TextDecoder(); 4 | 5 | export class MyRedisHandler extends RedisHandler { 6 | async handleMessage(msg) { 7 | console.log("Received message:", decoder.decode(msg)); 8 | } 9 | } 10 | 11 | export const inboundRedis = new MyRedisHandler(); -------------------------------------------------------------------------------- /templates/redis-js/content/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.js', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | resolve: { 14 | extensions: ['.js'], 15 | }, 16 | output: { 17 | path: path.resolve(process.cwd(), './build'), 18 | filename: 'bundle.js', 19 | module: true, 20 | library: { 21 | type: "module", 22 | } 23 | }, 24 | plugins: [ 25 | SpinPlugin 26 | ], 27 | optimization: { 28 | minimize: false 29 | }, 30 | }; 31 | } 32 | export default config -------------------------------------------------------------------------------- /templates/redis-js/metadata/snippets/application-trigger.txt: -------------------------------------------------------------------------------- 1 | [application.trigger.redis] 2 | address = "{{redis-address}}" -------------------------------------------------------------------------------- /templates/redis-js/metadata/snippets/component.txt: -------------------------------------------------------------------------------- 1 | [[trigger.redis]] 2 | channel = "{{redis-channel}}" 3 | component = "{{project-name | kebab_case}}" 4 | 5 | [component.{{project-name | kebab_case}}] 6 | source = "{{ output-path }}/dist/{{project-name | kebab_case}}.wasm" 7 | allowed_outbound_hosts = [] 8 | [component.{{project-name | kebab_case}}.build] 9 | command = "npm install && npm run build" 10 | workdir = "{{ output-path }}" -------------------------------------------------------------------------------- /templates/redis-js/metadata/spin-template.toml: -------------------------------------------------------------------------------- 1 | manifest_version = "1" 2 | id = "redis-js" 3 | description = "Redis message handler using JavaScript" 4 | tags = ["redis", "js"] 5 | 6 | [add_component] 7 | skip_files = ["spin.toml"] 8 | [add_component.snippets] 9 | component = "component.txt" 10 | application_trigger = "application-trigger.txt" 11 | [add_component.conditions.address_exists] 12 | condition = { manifest_entry_exists = "application.trigger.redis" } 13 | skip_parameters = ["redis-address"] 14 | skip_snippets = ["application_trigger"] 15 | 16 | [parameters] 17 | project-description = { type = "string", prompt = "Description", default = "" } 18 | redis-address = { type = "string", prompt = "Redis address", default = "redis://localhost:6379" } 19 | redis-channel = { type = "string", prompt = "Redis channel" } 20 | -------------------------------------------------------------------------------- /templates/redis-ts/content/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | dist.js 6 | build/ -------------------------------------------------------------------------------- /templates/redis-ts/content/README.md: -------------------------------------------------------------------------------- 1 | # `redis-ts` Template 2 | 3 | A starter template for building TypeScript edis applications with Spin. 4 | 5 | ## Getting Started 6 | 7 | Build the App 8 | 9 | ```bash 10 | spin build 11 | ``` 12 | 13 | ## Run the App 14 | 15 | ```bash 16 | spin up 17 | ``` 18 | 19 | ## Using Spin Interfaces 20 | 21 | To use additional Spin interfaces, install the corresponding packages: 22 | 23 | | Interface | Package | 24 | |---------------|---------------------------------| 25 | | Key-Value | `@spinframework/spin-kv` | 26 | | LLM | `@spinframework/spin-llm` | 27 | | MQTT | `@spinframework/spin-mqtt` | 28 | | MySQL | `@spinframework/spin-mysql` | 29 | | PostgreSQL | `@spinframework/spin-postgres` | 30 | | Redis | `@spinframework/spin-redis` | 31 | | SQLite | `@spinframework/spin-sqlite` | 32 | | Variables | `@spinframework/spin-variables` | -------------------------------------------------------------------------------- /templates/redis-ts/content/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{project-name | kebab_case}}", 3 | "version": "1.0.0", 4 | "description": "{{project-description}}", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/{{ project-name | kebab_case }}.wasm", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "postinstall": "knitwit --out-dir build/wit/knitwit --out-world combined" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "mkdirp": "^3.0.1", 16 | "ts-loader": "^9.4.1", 17 | "typescript": "^4.8.4", 18 | "webpack": "^5.74.0", 19 | "webpack-cli": "^4.10.0" 20 | }, 21 | "dependencies": { 22 | "@spinframework/build-tools": "^1.0.1", 23 | "@spinframework/spin-trigger-redis": "^1.0.0" 24 | } 25 | } -------------------------------------------------------------------------------- /templates/redis-ts/content/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | name = "{{project-name | kebab_case}}" 5 | version = "0.1.0" 6 | authors = ["{{authors}}"] 7 | description = "{{project-description}}" 8 | 9 | [application.trigger.redis] 10 | address = "{{redis-address}}" 11 | 12 | [[trigger.redis]] 13 | channel = "{{redis-channel}}" 14 | component = "{{project-name | kebab_case}}" 15 | 16 | [component.{{project-name | kebab_case}}] 17 | source = "dist/{{project-name | kebab_case}}.wasm" 18 | exclude_files = ["**/node_modules"] 19 | [component.{{project-name | kebab_case}}.build] 20 | command = "npm install && npm run build" 21 | watch = ["src/**/*.ts", "package.json"] 22 | -------------------------------------------------------------------------------- /templates/redis-ts/content/src/index.ts: -------------------------------------------------------------------------------- 1 | import { RedisHandler } from "@fermyon/spin-sdk"; 2 | 3 | const decoder = new TextDecoder(); 4 | 5 | export class MyRedisHandler extends RedisHandler { 6 | async handleMessage(msg: Uint8Array): Promise { 7 | console.log("Received message:", decoder.decode(msg)); 8 | } 9 | } 10 | 11 | export const inboundRedis = new MyRedisHandler() -------------------------------------------------------------------------------- /templates/redis-ts/content/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | }, 18 | "include": [ 19 | "src/**/*" 20 | ] 21 | } -------------------------------------------------------------------------------- /templates/redis-ts/content/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | performance: { 40 | hints: false, 41 | } 42 | }; 43 | } 44 | export default config -------------------------------------------------------------------------------- /templates/redis-ts/metadata/snippets/application-trigger.txt: -------------------------------------------------------------------------------- 1 | [application.trigger.redis] 2 | address = "{{redis-address}}" -------------------------------------------------------------------------------- /templates/redis-ts/metadata/snippets/component.txt: -------------------------------------------------------------------------------- 1 | [[trigger.redis]] 2 | channel = "{{redis-channel}}" 3 | component = "{{project-name | kebab_case}}" 4 | 5 | [component.{{project-name | kebab_case}}] 6 | source = "{{ output-path }}/dist/{{project-name | kebab_case}}.wasm" 7 | allowed_outbound_hosts = [] 8 | [component.{{project-name | kebab_case}}.build] 9 | command = "npm install && npm run build" 10 | workdir = "{{ output-path }}" -------------------------------------------------------------------------------- /templates/redis-ts/metadata/spin-template.toml: -------------------------------------------------------------------------------- 1 | manifest_version = "1" 2 | id = "redis-ts" 3 | description = "Redis message handler using TypeScript" 4 | tags = ["redis", "ts"] 5 | 6 | [add_component] 7 | skip_files = ["spin.toml"] 8 | [add_component.snippets] 9 | component = "component.txt" 10 | application_trigger = "application-trigger.txt" 11 | [add_component.conditions.address_exists] 12 | condition = { manifest_entry_exists = "application.trigger.redis" } 13 | skip_parameters = ["redis-address"] 14 | skip_snippets = ["application_trigger"] 15 | 16 | [parameters] 17 | project-description = { type = "string", prompt = "Description", default = "" } 18 | redis-address = { type = "string", prompt = "Redis address", default = "redis://localhost:6379" } 19 | redis-channel = { type = "string", prompt = "Redis channel" } 20 | -------------------------------------------------------------------------------- /test/test-app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | target 4 | .spin/ 5 | build/ -------------------------------------------------------------------------------- /test/test-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-app", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "", 6 | "description": "", 7 | "keywords": [], 8 | "license": "Apache-2.0", 9 | "scripts": { 10 | "build": "npx webpack && mkdirp dist && j2w -i build/bundle.js -o dist/test-app.wasm", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "devDependencies": { 14 | "mkdirp": "^3.0.1", 15 | "ts-loader": "^9.4.1", 16 | "typescript": "^4.8.4", 17 | "webpack": "^5.74.0", 18 | "webpack-cli": "^4.10.0" 19 | }, 20 | "dependencies": { 21 | "@spinframework/build-tools": "file:../../packages/build-tools", 22 | "@spinframework/spin-kv": "file:../../packages/spin-kv", 23 | "@spinframework/spin-variables": "file:../../packages/spin-variables", 24 | "@spinframework/wasi-http-proxy": "file:../../packages/http-trigger", 25 | "itty-router": "^5.0.18" 26 | }, 27 | "config": { 28 | "wasiDep": { 29 | "wellKnownWorlds": [ 30 | "http-trigger@0.2.3" 31 | ] 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /test/test-app/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | authors = ["karthik2804 "] 5 | description = "" 6 | name = "test-app" 7 | version = "0.1.0" 8 | 9 | [[trigger.http]] 10 | route = "/..." 11 | component = "test-app" 12 | 13 | [component.test-app] 14 | source = "dist/test-app.wasm" 15 | exclude_files = ["**/node_modules"] 16 | allowed_outbound_hosts = ["http://localhost:3000"] 17 | key_value_stores = ["default"] 18 | [component.test-app.build] 19 | command = ["npm install", "npm run build"] 20 | watch = ["src/**/*.ts"] 21 | [component.test-app.variables] 22 | test = "try" 23 | -------------------------------------------------------------------------------- /test/test-app/src/helpers.ts: -------------------------------------------------------------------------------- 1 | const decoder = new TextDecoder(); 2 | const encoder = new TextEncoder(); 3 | 4 | export function isEqualBytes( 5 | bytes1: Uint8Array, 6 | bytes2: Uint8Array 7 | 8 | ): boolean { 9 | 10 | if (bytes1.length !== bytes2.length) { 11 | return false; 12 | } 13 | 14 | for (let i = 0; i < bytes1.length; i++) { 15 | if (bytes1[i] !== bytes2[i]) { 16 | return false; 17 | } 18 | } 19 | 20 | return true; 21 | 22 | } 23 | 24 | export function sleep(ms: number) { 25 | return new Promise((resolve) => setTimeout(resolve, ms)); 26 | } 27 | 28 | export async function readStreamChunks(stream: ReadableStream) { 29 | const reader = stream.getReader(); 30 | const chunks = []; 31 | 32 | while (true) { 33 | const { done, value } = await reader.read(); 34 | if (done) break; 35 | chunks.push(decoder.decode(value)); 36 | } 37 | 38 | return chunks; 39 | } 40 | 41 | export async function pushStreamChunks(stream: WritableStream, chunks: string[]) { 42 | const writer = stream.getWriter(); 43 | 44 | for (const chunk of chunks) { 45 | await writer.write(encoder.encode(chunk)); 46 | await sleep(20); 47 | } 48 | 49 | writer.close(); 50 | } -------------------------------------------------------------------------------- /test/test-app/src/index.ts: -------------------------------------------------------------------------------- 1 | import { AutoRouter } from "itty-router" 2 | import { headersTest, health, kvTest, kvTestUint8Array, outboundHttp, statusTest, stream, streamTest, testFunctionality } from "./test"; 3 | 4 | let router = AutoRouter() 5 | 6 | router.get("/health", health) 7 | router.get("/stream", stream) 8 | router.get("/statusTest", statusTest) 9 | router.get("/headersTest", headersTest) 10 | router.get("/outboundHttp", outboundHttp) 11 | router.get("/kvTest", kvTest) 12 | router.get("/kvTestUint8Array", kvTestUint8Array) 13 | router.get("/streamTest", streamTest) 14 | router.get("/testFunctionality", testFunctionality) 15 | 16 | //@ts-ignore 17 | addEventListener('fetch', async (event: FetchEvent) => { 18 | event.respondWith(router.fetch(event.request)); 19 | }); -------------------------------------------------------------------------------- /test/test-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "target": "es2020", 7 | "jsx": "react", 8 | "skipLibCheck": true, 9 | "lib": [ 10 | "ES2020", 11 | "WebWorker" 12 | ], 13 | "allowJs": true, 14 | "strict": true, 15 | "noImplicitReturns": true, 16 | "moduleResolution": "node" 17 | } 18 | } -------------------------------------------------------------------------------- /test/test-app/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import SpinSdkPlugin from "@spinframework/build-tools/plugins/webpack/index.js"; 3 | 4 | const config = async () => { 5 | let SpinPlugin = await SpinSdkPlugin.init() 6 | return { 7 | mode: 'production', 8 | stats: 'errors-only', 9 | entry: './src/index.ts', 10 | experiments: { 11 | outputModule: true, 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.tsx?$/, 17 | use: 'ts-loader', 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | resolve: { 23 | extensions: ['.tsx', '.ts', '.js'], 24 | }, 25 | output: { 26 | path: path.resolve(process.cwd(), './build'), 27 | filename: 'bundle.js', 28 | module: true, 29 | library: { 30 | type: "module", 31 | } 32 | }, 33 | plugins: [ 34 | SpinPlugin 35 | ], 36 | optimization: { 37 | minimize: false 38 | }, 39 | }; 40 | } 41 | export default config -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | set -euo pipefail 2 | 3 | # Build the npm package 4 | cd .. 5 | for d in packages/*; do 6 | echo "Building $d" 7 | cd $d 8 | npm install 9 | npm run build 10 | cd - 11 | done 12 | cd test 13 | 14 | isFailed=false 15 | # Build test app 16 | echo "Building the test app" 17 | cd test-app 18 | npm install 19 | spin build 20 | echo "built the test app successfully" 21 | 22 | 23 | # Start the spin app in the background 24 | echo "Starting Spin app" 25 | spin up & 26 | 27 | # wait for app to be up and running 28 | echo "Waiting for Spin app to be ready" 29 | timeout 60s bash -c 'until curl --silent -f http://localhost:3000/health > /dev/null; do sleep 2; done' 30 | 31 | # start the test 32 | echo "Starting test\n" 33 | curl -f http://localhost:3000/testFunctionality || isFailed=true 34 | echo "\n\nTest completed" 35 | 36 | # kill the spin app 37 | echo "Stopping Spin" 38 | killall spin 39 | 40 | 41 | if [ "$isFailed" = true ] ; then 42 | echo "Some tests failed" 43 | exit 1 44 | fi 45 | -------------------------------------------------------------------------------- /typedoc.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": [ 3 | "packages/*" 4 | ], 5 | "entryPointStrategy": "packages", 6 | "includeVersion": false, 7 | "plugin": [ 8 | "typedoc-plugin-missing-exports" 9 | ], 10 | "readme": "none", 11 | "packageOptions": { 12 | "includeVersion": true, 13 | "entryPoints": [ 14 | "src/index.ts" 15 | ] 16 | } 17 | } --------------------------------------------------------------------------------