@facetlayer/prism-framework 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +20 -0
- package/CHANGELOG +28 -0
- package/CLAUDE.md +44 -0
- package/README.md +47 -0
- package/build.mts +8 -0
- package/dist/call-command.d.ts +13 -0
- package/dist/call-command.d.ts.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +475 -0
- package/dist/config/ConfigFile.d.ts +7 -0
- package/dist/config/ConfigFile.d.ts.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/loadConfig.d.ts +11 -0
- package/dist/config/loadConfig.d.ts.map +1 -0
- package/dist/generate-api-clients.d.ts +6 -0
- package/dist/generate-api-clients.d.ts.map +1 -0
- package/dist/getPorts.d.ts +10 -0
- package/dist/getPorts.d.ts.map +1 -0
- package/dist/list-endpoints-command.d.ts +5 -0
- package/dist/list-endpoints-command.d.ts.map +1 -0
- package/dist/loadEnv.d.ts +12 -0
- package/dist/loadEnv.d.ts.map +1 -0
- package/docs/endpoint-tools.md +116 -0
- package/docs/env-files.md +64 -0
- package/docs/generate-api-clients-config.md +84 -0
- package/docs/getting-started.md +86 -0
- package/package.json +43 -0
- package/src/call-command.ts +147 -0
- package/src/cli.ts +163 -0
- package/src/config/ConfigFile.ts +7 -0
- package/src/config/index.ts +3 -0
- package/src/config/loadConfig.ts +58 -0
- package/src/generate-api-clients.ts +203 -0
- package/src/getPorts.ts +39 -0
- package/src/list-endpoints-command.ts +34 -0
- package/src/loadEnv.ts +59 -0
- package/test/call-command.test.ts +96 -0
- package/test/generate-api-clients.test.ts +33 -0
- package/test/generate-api-clients.test.ts.disabled +75 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert OpenAPI path parameter format {param} to Express format :param
|
|
3
|
+
*/
|
|
4
|
+
export declare function convertToExpressPath(openApiPath: string): string;
|
|
5
|
+
export declare function generateApiClients(baseUrl: string, outputFiles: string[]): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=generate-api-clients.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-api-clients.d.ts","sourceRoot":"","sources":["../src/generate-api-clients.ts"],"names":[],"mappings":"AAqCA;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE;AA2DD,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqG9F"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gets the port from a directory's .env file
|
|
3
|
+
* @param options - Configuration options
|
|
4
|
+
* @param options.dir - Directory to search for .env file (defaults to current working directory)
|
|
5
|
+
* @returns The port number
|
|
6
|
+
*/
|
|
7
|
+
export declare function getPort(options?: {
|
|
8
|
+
dir?: string;
|
|
9
|
+
}): number;
|
|
10
|
+
//# sourceMappingURL=getPorts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getPorts.d.ts","sourceRoot":"","sources":["../src/getPorts.ts"],"names":[],"mappings":"AAsBA;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAS1D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-endpoints-command.d.ts","sourceRoot":"","sources":["../src/list-endpoints-command.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BlE"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface EnvConfig {
|
|
2
|
+
port: number;
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Load and parse the .env file from the project directory
|
|
7
|
+
* @param cwd - Current working directory to search from
|
|
8
|
+
* @returns Configuration object with port and baseUrl
|
|
9
|
+
* @throws Error if .env file is missing or PRISM_API_PORT is not defined
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadEnv(cwd: string): EnvConfig;
|
|
12
|
+
//# sourceMappingURL=loadEnv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadEnv.d.ts","sourceRoot":"","sources":["../src/loadEnv.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CA2C9C"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: endpoint-tools
|
|
3
|
+
description: CLI tools for listing and calling API endpoints on a running Prism server
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Endpoint Tools
|
|
7
|
+
|
|
8
|
+
The `prism` CLI provides commands for interacting with your API endpoints. These tools connect to a running Prism API server.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
Both commands require:
|
|
13
|
+
- A running Prism API server
|
|
14
|
+
- A `.env` file with `PRISM_API_PORT` set to your server's port
|
|
15
|
+
|
|
16
|
+
## prism list-endpoints
|
|
17
|
+
|
|
18
|
+
Lists all available endpoints from a running server.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
prism list-endpoints
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Output:
|
|
25
|
+
```
|
|
26
|
+
Using API server at: http://localhost:4003
|
|
27
|
+
|
|
28
|
+
Available endpoints:
|
|
29
|
+
|
|
30
|
+
GET /users
|
|
31
|
+
List all users
|
|
32
|
+
POST /users
|
|
33
|
+
Create a new user
|
|
34
|
+
GET /users/:id
|
|
35
|
+
Get user by ID
|
|
36
|
+
DELETE /users/:id
|
|
37
|
+
Delete a user
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## prism call
|
|
41
|
+
|
|
42
|
+
Calls an endpoint on a running server.
|
|
43
|
+
|
|
44
|
+
### Basic Usage
|
|
45
|
+
|
|
46
|
+
**Important:** Do not use the `/api` prefix in endpoint paths. Use the path directly as defined in the endpoint.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# GET request
|
|
50
|
+
prism call /users
|
|
51
|
+
|
|
52
|
+
# Explicit method
|
|
53
|
+
prism call GET /users
|
|
54
|
+
|
|
55
|
+
# POST with data
|
|
56
|
+
prism call POST /users --name "John Doe" --email "john@example.com"
|
|
57
|
+
|
|
58
|
+
# Other methods
|
|
59
|
+
prism call PUT /users/123 --name "Jane"
|
|
60
|
+
prism call PATCH /users/123 --status active
|
|
61
|
+
prism call DELETE /users/123
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Passing Data
|
|
65
|
+
|
|
66
|
+
Named arguments become the request body:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
prism call POST /users --name "John" --age 30 --active true
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Sends:
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"name": "John",
|
|
76
|
+
"age": 30,
|
|
77
|
+
"active": true
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### JSON Objects
|
|
82
|
+
|
|
83
|
+
Arguments that look like JSON are parsed automatically:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
prism call POST /config --settings '{"timeout": 30, "retries": 3}'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Output
|
|
90
|
+
|
|
91
|
+
The command prints the response status and body:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
Response status: 200
|
|
95
|
+
Response: {"id":"123","name":"John","email":"john@example.com"}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Troubleshooting
|
|
99
|
+
|
|
100
|
+
### "Failed to connect"
|
|
101
|
+
|
|
102
|
+
1. **Check the server is running** - Start your API server
|
|
103
|
+
2. **Verify PRISM_API_PORT** - Ensure `.env` contains `PRISM_API_PORT` matching your server port
|
|
104
|
+
3. **Check dotenv is loaded** - Your server must load the `.env` file:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { config } from 'dotenv';
|
|
108
|
+
config({ path: '.env' });
|
|
109
|
+
|
|
110
|
+
const PORT = parseInt(process.env.PRISM_API_PORT, 10);
|
|
111
|
+
await startServer({ app, port: PORT });
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### "Endpoint not found"
|
|
115
|
+
|
|
116
|
+
Use `prism list-endpoints` to see available endpoints and verify the path.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: env-files
|
|
3
|
+
description: Recommended strategy for environment variable configuration in Prism Framework projects
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Environment Files Strategy
|
|
7
|
+
|
|
8
|
+
The typical env variables needed for a Prism app are:
|
|
9
|
+
|
|
10
|
+
### Backend
|
|
11
|
+
|
|
12
|
+
Next to the API / backend code there should be a .env file with:
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
| name | example value | description |
|
|
16
|
+
| ---- | ------------- | ----------- |
|
|
17
|
+
| PRISM_API_PORT | `<port number>` | The port for the web server |
|
|
18
|
+
| DATABASE_DIR | data | The relative path to a folder that has SQlite databases |
|
|
19
|
+
| WEB_BASE_URL | `http://localhost:<number>` | The URL for the web server |
|
|
20
|
+
| ALLOW_LOCALHOST | `true` | Whether to allow localhost CORS origins for local development |
|
|
21
|
+
|
|
22
|
+
### Frontend
|
|
23
|
+
|
|
24
|
+
#### Next.js
|
|
25
|
+
|
|
26
|
+
| name | example value | description |
|
|
27
|
+
| ---- | ------------- | ----------- |
|
|
28
|
+
| PORT | `<port number>` | The port for the web server. Should match WEB_BASE_URL from the backend. |
|
|
29
|
+
| NEXT_PUBLIC_API_URL | `http://localhost:<number>` | The URL for the API server. Should match PRISM_API_PORT from the backend. |
|
|
30
|
+
|
|
31
|
+
Remember that if a variable is used in the frontend code, it needs a prefix of `NEXT_PUBLIC_`.
|
|
32
|
+
|
|
33
|
+
Next.js doesn't load the .env file by default so it's recommended to have this script in package.json:
|
|
34
|
+
|
|
35
|
+
"scripts": {
|
|
36
|
+
"dev": "dotenv -e .env next dev",
|
|
37
|
+
...
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
#### Vite
|
|
41
|
+
|
|
42
|
+
| name | example value | description |
|
|
43
|
+
| ---- | ------------- | ----------- |
|
|
44
|
+
| VITE_API_URL | `http://localhost:<number>` | The URL for the API server. Only needed if not using the Vite proxy approach. |
|
|
45
|
+
|
|
46
|
+
Vite uses the `VITE_` prefix instead of `NEXT_PUBLIC_` for client-exposed variables. Vite loads `.env` files automatically — no extra setup needed.
|
|
47
|
+
|
|
48
|
+
Access in code:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
const apiUrl = import.meta.env.VITE_API_URL;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
See the `vite-setup` doc in `@facetlayer/prism-framework-ui` for more details.
|
|
55
|
+
|
|
56
|
+
# Port assignment
|
|
57
|
+
|
|
58
|
+
It's recommended to use the `@facetlayer/port-assignment` tool if you need to assign new unique port numbers.
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
|
|
62
|
+
npx @facetlayer/port-assignment claim --name <project name>
|
|
63
|
+
|
|
64
|
+
Run `npx @facetlayer/port-assigment list-docs` for more documentation.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: generate-api-clients-config
|
|
3
|
+
description: Configuration file setup for automatic API client type generation
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Generate API Clients Configuration
|
|
7
|
+
|
|
8
|
+
The `prism generate-api-clients` command generates TypeScript types from your API's OpenAPI schema. You can configure output targets using a `.prism.qc` config file.
|
|
9
|
+
|
|
10
|
+
## Config File Location
|
|
11
|
+
|
|
12
|
+
Create a `.prism.qc` file in your project root (where you run `prism` commands from).
|
|
13
|
+
|
|
14
|
+
## Basic Syntax
|
|
15
|
+
|
|
16
|
+
The config uses the QC format. Each target is defined with a `generate-api-client` command:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
generate-api-client
|
|
20
|
+
output-file=<path-to-output-file>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The `output-file` path is relative to the project root.
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
### Single Output File
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
generate-api-client
|
|
31
|
+
output-file=./src/api-types.ts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Multiple Output Files
|
|
35
|
+
|
|
36
|
+
You can define multiple targets to generate the same types to multiple locations:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
generate-api-client
|
|
40
|
+
output-file=./ui/src/lib/api-types.ts
|
|
41
|
+
|
|
42
|
+
generate-api-client
|
|
43
|
+
output-file=./mobile/src/api-types.ts
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Typical Project Layouts
|
|
47
|
+
|
|
48
|
+
**Monorepo with separate UI package:**
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
generate-api-client
|
|
52
|
+
output-file=./ui/src/lib/api-types.ts
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Monorepo with web directory:**
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
generate-api-client
|
|
59
|
+
output-file=./web/src/client/api-types.ts
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
Once configured, run:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
prism generate-api-clients
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The command will:
|
|
71
|
+
1. Read the API server URL from your `.env` file (`PRISM_API_PORT`)
|
|
72
|
+
2. Fetch the OpenAPI schema from `http://localhost:<port>/openapi.json`
|
|
73
|
+
3. Generate TypeScript types and write them to all configured output files
|
|
74
|
+
|
|
75
|
+
You can also override the config by specifying `--out` directly:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
prism generate-api-clients --out ./custom/path/types.ts
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Requirements
|
|
82
|
+
|
|
83
|
+
- The API server must be running and serving `/openapi.json`
|
|
84
|
+
- A `.env` file with `PRISM_API_PORT` must exist in the project root
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: getting-started
|
|
3
|
+
description: Guide for setting up a new Prism Framework project with type-safe API and frontend client generation
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Prism Framework Getting Started Guide
|
|
7
|
+
|
|
8
|
+
## Intro
|
|
9
|
+
|
|
10
|
+
"Prism framework" is a set of Node.js libraries for a full-stack app that can target multiple platforms.
|
|
11
|
+
|
|
12
|
+
This includes the following NPM libraries:
|
|
13
|
+
|
|
14
|
+
### `@facetlayer/prism-framework`
|
|
15
|
+
- Base library with tooling for various development and testing tasks
|
|
16
|
+
- Should be installed at the top level in the devDependencies section
|
|
17
|
+
- First place to look for documentation (`prism list-docs`)
|
|
18
|
+
|
|
19
|
+
### `@facetlayer/prism-framework-api`
|
|
20
|
+
- Backend API framework
|
|
21
|
+
- Can be hosted on HTTP with Express.js
|
|
22
|
+
- Also supports other launch methods such as IPC for Electron
|
|
23
|
+
|
|
24
|
+
### `@facetlayer/prism-framework-ui`
|
|
25
|
+
- Helpers for React-based frontend web apps
|
|
26
|
+
|
|
27
|
+
### `@facetlayer/prism-framework-desktop`
|
|
28
|
+
- Helper framework for Electron based desktop apps
|
|
29
|
+
|
|
30
|
+
## Getting started
|
|
31
|
+
|
|
32
|
+
- Install `@facetlayer/prism-framework`
|
|
33
|
+
- Start using the `prism` CLI tool, especially:
|
|
34
|
+
- `prism list-docs` - List available documentation files.
|
|
35
|
+
- `prism get-doc <name>` - Read a documentation file.
|
|
36
|
+
|
|
37
|
+
## Example Project Setup
|
|
38
|
+
|
|
39
|
+
Some ways to set up the repo for a Prism project:
|
|
40
|
+
|
|
41
|
+
### Option 1: API in separate directory
|
|
42
|
+
|
|
43
|
+
- `./api` - Backend API
|
|
44
|
+
- `./api/package.json` - Contains prism-framework-api
|
|
45
|
+
- `./api/src/` - Backend service implementation
|
|
46
|
+
- `./web` or `./ui` - Frontend web app
|
|
47
|
+
- `./web/package.json` - Contains Next.js or Vite, and prism-framework-ui
|
|
48
|
+
- `./web/src` - Frontend implementation
|
|
49
|
+
|
|
50
|
+
### Option 2: API in top level directory
|
|
51
|
+
|
|
52
|
+
- `./package.json` - Contains prism-framework-api
|
|
53
|
+
- `./src` - Backend API source code
|
|
54
|
+
- `./web` or `./ui` - Frontend web app
|
|
55
|
+
- `./web/package.json` - Contains Next.js or Vite, and prism-framework-ui
|
|
56
|
+
- `./web/src`
|
|
57
|
+
|
|
58
|
+
### Frontend framework choice
|
|
59
|
+
|
|
60
|
+
- **Vite + React** - Recommended for local tools, GUIs, and apps that don't need SSR. Lighter weight and faster dev server. See the `vite-setup` doc in `@facetlayer/prism-framework-ui`.
|
|
61
|
+
- **Next.js** - Recommended for production web apps that need SSR, routing, or deployment to platforms like Vercel. See the `nextjs-setup` doc in `@facetlayer/prism-framework-ui`.
|
|
62
|
+
|
|
63
|
+
## Packages
|
|
64
|
+
|
|
65
|
+
- For package management use `pnpm`
|
|
66
|
+
- Make sure to set up a `pnpm-workspace.yaml` file in the top level
|
|
67
|
+
|
|
68
|
+
### Top level tools
|
|
69
|
+
|
|
70
|
+
The top level of the project should have these dependencies:
|
|
71
|
+
|
|
72
|
+
`pnpm add typescript dotenv @facetlayer/prism-framework`
|
|
73
|
+
|
|
74
|
+
## Local service management
|
|
75
|
+
|
|
76
|
+
Prism projects usually use the `candle` tool (from @facetlayer/candle) to run local services.
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
|
|
80
|
+
Browse documentation:
|
|
81
|
+
`candle list-docs`
|
|
82
|
+
|
|
83
|
+
Set up services in the .candle.json file:
|
|
84
|
+
`candle add-service api "node --watch src/_main/api.ts" --root ./api
|
|
85
|
+
`candle add-service web "pnpm dev" --root ./web
|
|
86
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@facetlayer/prism-framework",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Base library and CLI tools for the Prism app framework",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/cli.js",
|
|
7
|
+
"types": "dist/cli.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"prism": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "node build.mts build",
|
|
13
|
+
"prepublishOnly": "node build.mts validate && node build.mts build",
|
|
14
|
+
"typecheck": "tsc -p .",
|
|
15
|
+
"test": "vitest run --testTimeout=30000",
|
|
16
|
+
"local:install": "pnpm build && npm i -g ."
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"framework",
|
|
20
|
+
"testing",
|
|
21
|
+
"tools",
|
|
22
|
+
"typescript"
|
|
23
|
+
],
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"packageManager": "pnpm@10.15.1",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@facetlayer/doc-files-helper": "0.1.2",
|
|
29
|
+
"@facetlayer/prism-framework-api": "0.2.0",
|
|
30
|
+
"@facetlayer/qc": "^0.1.0",
|
|
31
|
+
"dotenv": "^16.4.7",
|
|
32
|
+
"uuid": "^11.1.0",
|
|
33
|
+
"yargs": "18.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@facetlayer/build-config-nodejs": "^0.3.0",
|
|
37
|
+
"@types/node": "^22.10.2",
|
|
38
|
+
"@types/uuid": "^10.0.0",
|
|
39
|
+
"@types/yargs": "17.0.34",
|
|
40
|
+
"typescript": "^5.7.2",
|
|
41
|
+
"vitest": "^3.0.0"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const EVERY_METHOD = new Set(['GET','POST','PUT','PATCH',"DELETE"]);
|
|
4
|
+
|
|
5
|
+
export interface CallEndpointLooseOptions {
|
|
6
|
+
baseUrl: string
|
|
7
|
+
positionalArgs: string[]
|
|
8
|
+
namedArgs: Record<string,any>
|
|
9
|
+
quiet?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface CallEndpointOptions {
|
|
13
|
+
baseUrl: string
|
|
14
|
+
method: string
|
|
15
|
+
path: string
|
|
16
|
+
requestBody: null | Record<string, any>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseValue(value: any): any {
|
|
20
|
+
if (typeof value === 'string') {
|
|
21
|
+
// Special case: If the string value looks like {..} or [..], then
|
|
22
|
+
// parse it as JSON. This way we can use object-based endpoints
|
|
23
|
+
// using CLI parameters.
|
|
24
|
+
const trimmed = value.trim();
|
|
25
|
+
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
26
|
+
(trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(trimmed);
|
|
29
|
+
} catch {
|
|
30
|
+
// If JSON parsing fails, keep the original string
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
35
|
+
// Recursively parse nested objects (yargs creates these from dot notation)
|
|
36
|
+
const result: Record<string, any> = {};
|
|
37
|
+
for (const [k, v] of Object.entries(value)) {
|
|
38
|
+
result[k] = parseValue(v);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function parseNamedArgs(namedArgs: Record<string, any>): Record<string, any> {
|
|
46
|
+
const result: Record<string, any> = {};
|
|
47
|
+
for (const [key, value] of Object.entries(namedArgs)) {
|
|
48
|
+
result[key] = parseValue(value);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function parseOptions(looseOptions: CallEndpointLooseOptions): CallEndpointOptions {
|
|
54
|
+
const result: CallEndpointOptions = {
|
|
55
|
+
baseUrl: looseOptions.baseUrl,
|
|
56
|
+
method: 'GET',
|
|
57
|
+
path: '/',
|
|
58
|
+
requestBody: parseNamedArgs(looseOptions.namedArgs),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Look at the positional args and figure out what they mean.
|
|
62
|
+
for (const positional of looseOptions.positionalArgs) {
|
|
63
|
+
if (positional.startsWith('/')) {
|
|
64
|
+
result.path = positional;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (EVERY_METHOD.has(positional.toUpperCase())) {
|
|
69
|
+
result.method = positional.toUpperCase();
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (positional.startsWith("http:") || positional.startsWith("https:")) {
|
|
74
|
+
result.baseUrl = positional;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
throw new Error("unrecognized positional arg:" + positional);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return result;
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Make an HTTP request to the local Prism API server
|
|
87
|
+
*/
|
|
88
|
+
export async function callEndpoint(looseOptions: CallEndpointLooseOptions) {
|
|
89
|
+
const options = parseOptions(looseOptions);
|
|
90
|
+
const url = `${options.baseUrl}${options.path}`;
|
|
91
|
+
|
|
92
|
+
const requestOptions: RequestInit = {
|
|
93
|
+
method: options.method,
|
|
94
|
+
headers: {
|
|
95
|
+
'Content-Type': 'application/json',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Add body for methods that support it
|
|
100
|
+
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(options.method)
|
|
101
|
+
&& options.requestBody
|
|
102
|
+
&& Object.keys(options.requestBody).length > 0) {
|
|
103
|
+
requestOptions.body = JSON.stringify(options.requestBody);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const response = await fetch(url, requestOptions);
|
|
108
|
+
|
|
109
|
+
if (!looseOptions.quiet)
|
|
110
|
+
console.log('Response status: ' + response.status);
|
|
111
|
+
|
|
112
|
+
// Get the response text first
|
|
113
|
+
const responseText = await response.text();
|
|
114
|
+
|
|
115
|
+
if (!looseOptions.quiet)
|
|
116
|
+
console.log('Response: ', responseText);
|
|
117
|
+
|
|
118
|
+
// Try to parse as JSON
|
|
119
|
+
let responseData;
|
|
120
|
+
try {
|
|
121
|
+
responseData = responseText ? JSON.parse(responseText) : null;
|
|
122
|
+
} catch {
|
|
123
|
+
// If not JSON, return as text
|
|
124
|
+
responseData = responseText;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!response.ok) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`HTTP ${response.status} ${response.statusText}\n` +
|
|
130
|
+
`Response: ${JSON.stringify(responseData, null, 2)}`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return responseData;
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (error instanceof Error) {
|
|
137
|
+
if (error.message.includes('fetch failed') || error.message.includes('ECONNREFUSED')) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Failed to connect to ${url}\n\n` +
|
|
140
|
+
'Make sure your Prism API server is running.\n' +
|
|
141
|
+
`The server should be listening on the port specified in .env (PRISM_API_PORT)`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|