@promptlycms/prompts 0.0.1 → 0.1.0-canary.1ccc7de
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +182 -28
- package/dist/chunk-GZIUWN6H.js +284 -0
- package/dist/cli.js +140 -0
- package/dist/index.cjs +422 -0
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +107 -0
- package/dist/schema.cjs +518 -0
- package/dist/schema.d.cts +10 -0
- package/dist/schema.d.ts +10 -0
- package/dist/schema.js +214 -0
- package/dist/types-C9TUf4ZY.d.cts +118 -0
- package/dist/types-C9TUf4ZY.d.ts +118 -0
- package/package.json +56 -7
package/README.md
CHANGED
|
@@ -1,45 +1,199 @@
|
|
|
1
1
|
# @promptlycms/prompts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TypeScript SDK for the [Promptly CMS](https://promptlycms.com) API. Fetch prompts at runtime, get typed template variables via codegen, and integrate with the [Vercel AI SDK](https://sdk.vercel.ai).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install @promptlycms/prompts
|
|
9
|
+
# or
|
|
10
|
+
bun add @promptlycms/prompts
|
|
11
|
+
```
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
Peer dependencies: `zod@^4`, `ai@^4 || ^5 || ^6`, `typescript@^5`
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@promptlycms/prompts`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
15
|
+
## Quick start
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
### 1. Set your API key
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
```bash
|
|
20
|
+
# .env
|
|
21
|
+
PROMPTLY_API_KEY=pk_live_...
|
|
22
|
+
```
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
### 2. Generate types (optional but recommended)
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
```bash
|
|
27
|
+
npx promptly generate
|
|
28
|
+
```
|
|
23
29
|
|
|
24
|
-
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
30
|
+
This fetches all your prompts from the API and generates a `promptly-env.d.ts` file in your project root. This gives you typed autocomplete on `userMessage()` variables for every prompt ID.
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
```bash
|
|
33
|
+
# Custom output path
|
|
34
|
+
npx promptly generate --output ./types/promptly-env.d.ts
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
36
|
+
# Pass API key directly
|
|
37
|
+
npx promptly generate --api-key pk_live_...
|
|
38
|
+
```
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
### 3. Create a client
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
42
|
+
```typescript
|
|
43
|
+
import { createPromptClient } from '@promptlycms/prompts';
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
const promptly = createPromptClient({
|
|
46
|
+
apiKey: process.env.PROMPTLY_API_KEY,
|
|
47
|
+
});
|
|
48
|
+
```
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
## Fetching prompts
|
|
51
|
+
|
|
52
|
+
### Single prompt
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const result = await promptly.get('JPxlUpstuhXB5OwOtKPpj');
|
|
56
|
+
|
|
57
|
+
// Access prompt metadata
|
|
58
|
+
result.promptId; // 'JPxlUpstuhXB5OwOtKPpj'
|
|
59
|
+
result.promptName; // 'Review Prompt'
|
|
60
|
+
result.systemMessage; // 'You are a helpful assistant.'
|
|
61
|
+
result.temperature; // 0.7
|
|
62
|
+
|
|
63
|
+
// Interpolate template variables (typed if you ran codegen)
|
|
64
|
+
const message = result.userMessage({
|
|
65
|
+
pickupLocation: 'London',
|
|
66
|
+
items: 'sofa',
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Get the raw template string
|
|
70
|
+
const template = String(result.userMessage);
|
|
71
|
+
// => 'Help with ${pickupLocation} moving ${items}.'
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Fetch a specific version:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const result = await promptly.get('JPxlUpstuhXB5OwOtKPpj', {
|
|
78
|
+
version: '2.0.0',
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Batch fetch
|
|
83
|
+
|
|
84
|
+
Fetch multiple prompts in parallel with typed results per position:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import type { PromptRequest } from '@promptlycms/prompts';
|
|
88
|
+
|
|
89
|
+
const [reviewPrompt, welcomePrompt] = await promptly.getPrompts([
|
|
90
|
+
{ promptId: 'JPxlUpstuhXB5OwOtKPpj' },
|
|
91
|
+
{ promptId: 'abc123', version: '2.0.0' },
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
// Each result is typed to its own prompt's variables
|
|
95
|
+
reviewPrompt.userMessage({ pickupLocation: 'London', items: 'sofa' });
|
|
96
|
+
welcomePrompt.userMessage({ email: 'a@b.com', subject: 'Hi' });
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## AI SDK integration
|
|
100
|
+
|
|
101
|
+
`aiParams()` returns an object you can spread directly into Vercel AI SDK functions:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { generateText } from 'ai';
|
|
105
|
+
import { anthropic } from '@ai-sdk/anthropic';
|
|
106
|
+
|
|
107
|
+
const params = await promptly.aiParams('my-prompt', {
|
|
108
|
+
variables: { name: 'Alice', task: 'coding' },
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const { text } = await generateText({
|
|
112
|
+
model: anthropic('claude-sonnet-4-5-20250929'),
|
|
113
|
+
...params,
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
If the prompt has a structured output schema defined in the CMS, `params.output` is automatically populated with a Zod schema wrapped in `Output.object()`.
|
|
118
|
+
|
|
119
|
+
## Type generation
|
|
120
|
+
|
|
121
|
+
Running `npx promptly generate` creates a `promptly-env.d.ts` file that uses [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) to narrow types:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// Auto-generated by @promptlycms/prompts — do not edit
|
|
125
|
+
import '@promptlycms/prompts';
|
|
126
|
+
|
|
127
|
+
declare module '@promptlycms/prompts' {
|
|
128
|
+
interface PromptVariableMap {
|
|
129
|
+
'JPxlUpstuhXB5OwOtKPpj': {
|
|
130
|
+
pickupLocation: string;
|
|
131
|
+
items: string;
|
|
132
|
+
};
|
|
133
|
+
'abc123': {
|
|
134
|
+
email: string;
|
|
135
|
+
subject: string;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
With this file present, `get()` and `getPrompts()` return typed `userMessage` functions with autocomplete. Unknown prompt IDs fall back to `Record<string, string>`.
|
|
142
|
+
|
|
143
|
+
Add the generated file to version control so types are available without running codegen in CI:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# .gitignore — do NOT ignore promptly-env.d.ts
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Re-run `npx promptly generate` whenever you add, remove, or rename template variables in the CMS.
|
|
150
|
+
|
|
151
|
+
## Error handling
|
|
152
|
+
|
|
153
|
+
All API errors throw `PromptlyError`:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { PromptlyError } from '@promptlycms/prompts';
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
await promptly.get('nonexistent');
|
|
160
|
+
} catch (err) {
|
|
161
|
+
if (err instanceof PromptlyError) {
|
|
162
|
+
err.code; // 'NOT_FOUND' | 'INVALID_KEY' | 'USAGE_LIMIT_EXCEEDED' | ...
|
|
163
|
+
err.status; // HTTP status code
|
|
164
|
+
err.message; // Human-readable error message
|
|
165
|
+
err.usage; // Usage data (on 429s)
|
|
166
|
+
err.upgradeUrl; // Upgrade link (on 429s)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## API reference
|
|
172
|
+
|
|
173
|
+
### `createPromptClient(config)`
|
|
174
|
+
|
|
175
|
+
| Option | Type | Required | Description |
|
|
176
|
+
|-----------|----------|----------|----------------------------------------------------|
|
|
177
|
+
| `apiKey` | `string` | Yes | Your Promptly CMS API key |
|
|
178
|
+
| `baseUrl` | `string` | No | API base URL (default: `https://api.promptlycms.com`) |
|
|
179
|
+
|
|
180
|
+
Returns a `PromptClient` with `get()`, `getPrompts()`, and `aiParams()` methods.
|
|
181
|
+
|
|
182
|
+
### `client.get(promptId, options?)`
|
|
183
|
+
|
|
184
|
+
Fetch a single prompt. Returns `PromptResult` with typed `userMessage` when codegen types are present.
|
|
185
|
+
|
|
186
|
+
### `client.getPrompts(entries)`
|
|
187
|
+
|
|
188
|
+
Fetch multiple prompts in parallel. Accepts `PromptRequest[]` and returns a typed tuple matching the input order.
|
|
189
|
+
|
|
190
|
+
### `client.aiParams(promptId, options?)`
|
|
191
|
+
|
|
192
|
+
Fetch a prompt and return params ready to spread into AI SDK functions (`system`, `prompt`, `temperature`, and optionally `output`).
|
|
193
|
+
|
|
194
|
+
### CLI: `npx promptly generate`
|
|
195
|
+
|
|
196
|
+
| Flag | Alias | Description |
|
|
197
|
+
|-------------|-------|------------------------------------------------------|
|
|
198
|
+
| `--api-key` | | API key (defaults to `PROMPTLY_API_KEY` env var) |
|
|
199
|
+
| `--output` | `-o` | Output path (default: `./promptly-env.d.ts`) |
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// src/schema/builder.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var resolveTypeString = (typeStr) => {
|
|
4
|
+
const builder = TYPE_BUILDERS.get(typeStr);
|
|
5
|
+
if (builder) {
|
|
6
|
+
return builder({
|
|
7
|
+
id: "",
|
|
8
|
+
name: "",
|
|
9
|
+
type: typeStr,
|
|
10
|
+
validations: [],
|
|
11
|
+
params: {}
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return z.string();
|
|
15
|
+
};
|
|
16
|
+
var buildCoercible = (params, coerced, standard) => {
|
|
17
|
+
if (params.coerce) {
|
|
18
|
+
return coerced;
|
|
19
|
+
}
|
|
20
|
+
return standard;
|
|
21
|
+
};
|
|
22
|
+
var TYPE_BUILDERS = /* @__PURE__ */ new Map([
|
|
23
|
+
["string", (f) => buildCoercible(f.params, z.coerce.string(), z.string())],
|
|
24
|
+
["number", (f) => buildCoercible(f.params, z.coerce.number(), z.number())],
|
|
25
|
+
["boolean", (f) => buildCoercible(f.params, z.coerce.boolean(), z.boolean())],
|
|
26
|
+
["date", (f) => buildCoercible(f.params, z.coerce.date(), z.date())],
|
|
27
|
+
["bigint", (f) => buildCoercible(f.params, z.coerce.bigint(), z.bigint())],
|
|
28
|
+
["null", () => z.null()],
|
|
29
|
+
["undefined", () => z.undefined()],
|
|
30
|
+
["void", () => z.void()],
|
|
31
|
+
["any", () => z.any()],
|
|
32
|
+
["unknown", () => z.unknown()],
|
|
33
|
+
["never", () => z.never()],
|
|
34
|
+
["nan", () => z.nan()],
|
|
35
|
+
["symbol", () => z.symbol()],
|
|
36
|
+
[
|
|
37
|
+
"enum",
|
|
38
|
+
(f) => {
|
|
39
|
+
const values = f.params.enumValues ?? [];
|
|
40
|
+
return z.enum(values);
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
[
|
|
44
|
+
"literal",
|
|
45
|
+
(f) => {
|
|
46
|
+
const value = f.params.enumValues?.[0] ?? "";
|
|
47
|
+
return z.literal(value);
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
[
|
|
51
|
+
"array",
|
|
52
|
+
(f) => {
|
|
53
|
+
if (f.params.isTuple && f.params.tupleTypes) {
|
|
54
|
+
const types = f.params.tupleTypes.map(resolveTypeString);
|
|
55
|
+
return z.tuple(types);
|
|
56
|
+
}
|
|
57
|
+
const elementSchema = f.params.elementType ? resolveTypeString(f.params.elementType) : z.unknown();
|
|
58
|
+
return z.array(elementSchema);
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
"object",
|
|
63
|
+
(f) => {
|
|
64
|
+
const schema = z.object({});
|
|
65
|
+
if (f.params.isStrict) {
|
|
66
|
+
return schema.strict();
|
|
67
|
+
}
|
|
68
|
+
if (f.params.isPassthrough) {
|
|
69
|
+
return schema.passthrough();
|
|
70
|
+
}
|
|
71
|
+
return schema;
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
[
|
|
75
|
+
"record",
|
|
76
|
+
(f) => {
|
|
77
|
+
const keySchema = f.params.keyType ? resolveTypeString(f.params.keyType) : z.string();
|
|
78
|
+
const valueSchema = f.params.valueType ? resolveTypeString(f.params.valueType) : z.unknown();
|
|
79
|
+
return z.record(keySchema, valueSchema);
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
"map",
|
|
84
|
+
(f) => {
|
|
85
|
+
const keySchema = f.params.keyType ? resolveTypeString(f.params.keyType) : z.string();
|
|
86
|
+
const valueSchema = f.params.valueType ? resolveTypeString(f.params.valueType) : z.unknown();
|
|
87
|
+
return z.map(keySchema, valueSchema);
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
[
|
|
91
|
+
"set",
|
|
92
|
+
(f) => {
|
|
93
|
+
const elementSchema = f.params.elementType ? resolveTypeString(f.params.elementType) : z.unknown();
|
|
94
|
+
return z.set(elementSchema);
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
"union",
|
|
99
|
+
(f) => {
|
|
100
|
+
if (f.params.isDiscriminatedUnion && f.params.discriminatedUnion) {
|
|
101
|
+
const { discriminator, cases } = f.params.discriminatedUnion;
|
|
102
|
+
const schemas = Object.values(cases).map(
|
|
103
|
+
(c) => z.object({
|
|
104
|
+
[discriminator]: z.literal(c.value),
|
|
105
|
+
...Object.fromEntries(
|
|
106
|
+
c.fields.map((field) => [field.name, buildFieldSchema(field)])
|
|
107
|
+
)
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
return z.discriminatedUnion(
|
|
111
|
+
discriminator,
|
|
112
|
+
schemas
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
const types = (f.params.unionTypes ?? []).map(resolveTypeString);
|
|
116
|
+
return z.union(types);
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
[
|
|
120
|
+
"intersection",
|
|
121
|
+
(f) => {
|
|
122
|
+
const types = (f.params.unionTypes ?? []).map(resolveTypeString);
|
|
123
|
+
return z.intersection(types[0] ?? z.unknown(), types[1] ?? z.unknown());
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
]);
|
|
127
|
+
var coerceDefaultValue = (value, fieldType) => {
|
|
128
|
+
const coercers = /* @__PURE__ */ new Map([
|
|
129
|
+
["number", (v) => Number(v)],
|
|
130
|
+
["boolean", (v) => v === "true"],
|
|
131
|
+
["bigint", (v) => BigInt(v)]
|
|
132
|
+
]);
|
|
133
|
+
const coercer = coercers.get(fieldType);
|
|
134
|
+
if (coercer) {
|
|
135
|
+
return coercer(value);
|
|
136
|
+
}
|
|
137
|
+
return value;
|
|
138
|
+
};
|
|
139
|
+
var applySizeValidation = (schema, type, value, message) => {
|
|
140
|
+
const num = Number(value);
|
|
141
|
+
if (schema instanceof z.ZodString) {
|
|
142
|
+
const methods = {
|
|
143
|
+
min: (n, m) => schema.min(n, m),
|
|
144
|
+
max: (n, m) => schema.max(n, m),
|
|
145
|
+
length: (n, m) => schema.length(n, m)
|
|
146
|
+
};
|
|
147
|
+
return methods[type]?.(num, message) ?? schema;
|
|
148
|
+
}
|
|
149
|
+
if (schema instanceof z.ZodNumber) {
|
|
150
|
+
const methods = {
|
|
151
|
+
min: (n, m) => schema.min(n, m),
|
|
152
|
+
max: (n, m) => schema.max(n, m)
|
|
153
|
+
};
|
|
154
|
+
return methods[type]?.(num, message) ?? schema;
|
|
155
|
+
}
|
|
156
|
+
if (schema instanceof z.ZodArray) {
|
|
157
|
+
const methods = {
|
|
158
|
+
min: (n) => schema.min(n),
|
|
159
|
+
max: (n) => schema.max(n),
|
|
160
|
+
length: (n) => schema.length(n)
|
|
161
|
+
};
|
|
162
|
+
return methods[type]?.(num) ?? schema;
|
|
163
|
+
}
|
|
164
|
+
return schema;
|
|
165
|
+
};
|
|
166
|
+
var VALIDATION_APPLICATORS = /* @__PURE__ */ new Map([
|
|
167
|
+
// Size validations
|
|
168
|
+
["min", (s, r) => applySizeValidation(s, "min", r.value, r.message)],
|
|
169
|
+
["max", (s, r) => applySizeValidation(s, "max", r.value, r.message)],
|
|
170
|
+
["length", (s, r) => applySizeValidation(s, "length", r.value, r.message)],
|
|
171
|
+
// String validations
|
|
172
|
+
["email", (s, r) => s instanceof z.ZodString ? s.email(r.message) : s],
|
|
173
|
+
["url", (s, r) => s instanceof z.ZodString ? s.url(r.message) : s],
|
|
174
|
+
["uuid", (s, r) => s instanceof z.ZodString ? s.uuid(r.message) : s],
|
|
175
|
+
["cuid", (s, r) => s instanceof z.ZodString ? s.cuid(r.message) : s],
|
|
176
|
+
["cuid2", (s, r) => s instanceof z.ZodString ? s.cuid2(r.message) : s],
|
|
177
|
+
["ulid", (s, r) => s instanceof z.ZodString ? s.ulid(r.message) : s],
|
|
178
|
+
[
|
|
179
|
+
"regex",
|
|
180
|
+
(s, r) => s instanceof z.ZodString ? s.regex(new RegExp(r.value), r.message) : s
|
|
181
|
+
],
|
|
182
|
+
[
|
|
183
|
+
"startsWith",
|
|
184
|
+
(s, r) => s instanceof z.ZodString ? s.startsWith(r.value, r.message) : s
|
|
185
|
+
],
|
|
186
|
+
[
|
|
187
|
+
"endsWith",
|
|
188
|
+
(s, r) => s instanceof z.ZodString ? s.endsWith(r.value, r.message) : s
|
|
189
|
+
],
|
|
190
|
+
[
|
|
191
|
+
"datetime",
|
|
192
|
+
(s, _r, f) => {
|
|
193
|
+
if (!(s instanceof z.ZodString)) {
|
|
194
|
+
return s;
|
|
195
|
+
}
|
|
196
|
+
const opts = f.params.stringOptions?.datetime;
|
|
197
|
+
return s.datetime(opts);
|
|
198
|
+
}
|
|
199
|
+
],
|
|
200
|
+
[
|
|
201
|
+
"ip",
|
|
202
|
+
(s, _r, f) => {
|
|
203
|
+
if (!(s instanceof z.ZodString)) {
|
|
204
|
+
return s;
|
|
205
|
+
}
|
|
206
|
+
const version = f.params.stringOptions?.ip?.version;
|
|
207
|
+
if (version === "v6") {
|
|
208
|
+
return s.ipv6();
|
|
209
|
+
}
|
|
210
|
+
return s.ipv4();
|
|
211
|
+
}
|
|
212
|
+
],
|
|
213
|
+
// String transforms
|
|
214
|
+
["trim", (s) => s instanceof z.ZodString ? s.trim() : s],
|
|
215
|
+
["toLowerCase", (s) => s instanceof z.ZodString ? s.toLowerCase() : s],
|
|
216
|
+
["toUpperCase", (s) => s instanceof z.ZodString ? s.toUpperCase() : s],
|
|
217
|
+
// Number validations
|
|
218
|
+
["int", (s, r) => s instanceof z.ZodNumber ? s.int(r.message) : s],
|
|
219
|
+
[
|
|
220
|
+
"positive",
|
|
221
|
+
(s, r) => s instanceof z.ZodNumber ? s.positive(r.message) : s
|
|
222
|
+
],
|
|
223
|
+
[
|
|
224
|
+
"negative",
|
|
225
|
+
(s, r) => s instanceof z.ZodNumber ? s.negative(r.message) : s
|
|
226
|
+
],
|
|
227
|
+
[
|
|
228
|
+
"multipleOf",
|
|
229
|
+
(s, r) => s instanceof z.ZodNumber ? s.multipleOf(Number(r.value), r.message) : s
|
|
230
|
+
],
|
|
231
|
+
["finite", (s, r) => s instanceof z.ZodNumber ? s.finite(r.message) : s],
|
|
232
|
+
["safe", (s, r) => s instanceof z.ZodNumber ? s.safe(r.message) : s],
|
|
233
|
+
// Collection
|
|
234
|
+
[
|
|
235
|
+
"nonempty",
|
|
236
|
+
(s) => {
|
|
237
|
+
if (s instanceof z.ZodString) {
|
|
238
|
+
return s.min(1);
|
|
239
|
+
}
|
|
240
|
+
if (s instanceof z.ZodArray) {
|
|
241
|
+
return s.nonempty();
|
|
242
|
+
}
|
|
243
|
+
return s;
|
|
244
|
+
}
|
|
245
|
+
],
|
|
246
|
+
// Wrapping modifiers
|
|
247
|
+
["optional", (s) => s.optional()],
|
|
248
|
+
["nullable", (s) => s.nullable()],
|
|
249
|
+
["nullish", (s) => s.nullish()],
|
|
250
|
+
// Default & catch
|
|
251
|
+
["default", (s, r, f) => s.default(coerceDefaultValue(r.value, f.type))],
|
|
252
|
+
["catch", (s, r, f) => s.catch(coerceDefaultValue(r.value, f.type))],
|
|
253
|
+
// Readonly
|
|
254
|
+
["readonly", (s) => s.readonly()]
|
|
255
|
+
]);
|
|
256
|
+
var buildFieldSchema = (field) => {
|
|
257
|
+
const builder = TYPE_BUILDERS.get(field.type);
|
|
258
|
+
if (!builder) {
|
|
259
|
+
return z.unknown();
|
|
260
|
+
}
|
|
261
|
+
let schema = builder(field);
|
|
262
|
+
for (const rule of field.validations) {
|
|
263
|
+
const applicator = VALIDATION_APPLICATORS.get(rule.type);
|
|
264
|
+
if (applicator) {
|
|
265
|
+
schema = applicator(schema, rule, field);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (field.params.description) {
|
|
269
|
+
schema = schema.describe(field.params.description);
|
|
270
|
+
}
|
|
271
|
+
return schema;
|
|
272
|
+
};
|
|
273
|
+
var buildZodSchema = (fields) => {
|
|
274
|
+
const shape = {};
|
|
275
|
+
for (const field of fields) {
|
|
276
|
+
shape[field.name] = buildFieldSchema(field);
|
|
277
|
+
}
|
|
278
|
+
return z.object(shape);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
export {
|
|
282
|
+
buildFieldSchema,
|
|
283
|
+
buildZodSchema
|
|
284
|
+
};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/index.ts
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
import { resolve } from "path";
|
|
6
|
+
import { defineCommand, runMain } from "citty";
|
|
7
|
+
|
|
8
|
+
// src/cli/generate.ts
|
|
9
|
+
import { writeFile } from "fs/promises";
|
|
10
|
+
var DEFAULT_BASE_URL = "https://api.promptlycms.com";
|
|
11
|
+
var extractTemplateVariables = (text) => {
|
|
12
|
+
const matches = text.matchAll(/\$\{(\w+)\}/g);
|
|
13
|
+
const vars = /* @__PURE__ */ new Set();
|
|
14
|
+
for (const match of matches) {
|
|
15
|
+
const captured = match[1];
|
|
16
|
+
if (captured) {
|
|
17
|
+
vars.add(captured);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return [...vars];
|
|
21
|
+
};
|
|
22
|
+
var fetchAllPrompts = async (apiKey, baseUrl) => {
|
|
23
|
+
const url = new URL("/prompts", baseUrl ?? DEFAULT_BASE_URL);
|
|
24
|
+
const response = await fetch(url.toString(), {
|
|
25
|
+
headers: {
|
|
26
|
+
Authorization: `Bearer ${apiKey}`
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Failed to fetch prompts: ${response.status} ${response.statusText}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return response.json();
|
|
35
|
+
};
|
|
36
|
+
var generateTypeDeclaration = (prompts) => {
|
|
37
|
+
const lines = [
|
|
38
|
+
"// Auto-generated by @promptlycms/prompts \u2014 do not edit",
|
|
39
|
+
"import '@promptlycms/prompts';",
|
|
40
|
+
"",
|
|
41
|
+
"declare module '@promptlycms/prompts' {",
|
|
42
|
+
" interface PromptVariableMap {"
|
|
43
|
+
];
|
|
44
|
+
for (const prompt of prompts) {
|
|
45
|
+
const variables = extractTemplateVariables(prompt.userMessage);
|
|
46
|
+
if (variables.length === 0) {
|
|
47
|
+
lines.push(` '${prompt.promptId}': Record<string, never>;`);
|
|
48
|
+
} else {
|
|
49
|
+
lines.push(` '${prompt.promptId}': {`);
|
|
50
|
+
for (const v of variables) {
|
|
51
|
+
lines.push(` ${v}: string;`);
|
|
52
|
+
}
|
|
53
|
+
lines.push(" };");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
lines.push(" }");
|
|
57
|
+
lines.push("}");
|
|
58
|
+
lines.push("");
|
|
59
|
+
return lines.join("\n");
|
|
60
|
+
};
|
|
61
|
+
var generate = async (apiKey, outputPath, baseUrl) => {
|
|
62
|
+
const prompts = await fetchAllPrompts(apiKey, baseUrl);
|
|
63
|
+
if (prompts.length === 0) {
|
|
64
|
+
console.log(" No prompts found for this API key.");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
console.log(` Found ${prompts.length} prompt(s)`);
|
|
68
|
+
const content = generateTypeDeclaration(prompts);
|
|
69
|
+
await writeFile(outputPath, content, "utf-8");
|
|
70
|
+
console.log(` Generated ${outputPath}`);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// src/cli/index.ts
|
|
74
|
+
var DEFAULT_DTS_OUTPUT = "./promptly-env.d.ts";
|
|
75
|
+
var loadEnvFile = () => {
|
|
76
|
+
try {
|
|
77
|
+
const envPath = resolve(process.cwd(), ".env");
|
|
78
|
+
const content = readFileSync(envPath, "utf-8");
|
|
79
|
+
for (const line of content.split("\n")) {
|
|
80
|
+
const trimmed = line.trim();
|
|
81
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const eqIndex = trimmed.indexOf("=");
|
|
85
|
+
if (eqIndex === -1) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
89
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
90
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
91
|
+
value = value.slice(1, -1);
|
|
92
|
+
}
|
|
93
|
+
if (!(key in process.env)) {
|
|
94
|
+
process.env[key] = value;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
loadEnvFile();
|
|
101
|
+
var generateCommand = defineCommand({
|
|
102
|
+
meta: {
|
|
103
|
+
name: "generate",
|
|
104
|
+
description: "Generate typed TypeScript declarations from Promptly CMS prompts"
|
|
105
|
+
},
|
|
106
|
+
args: {
|
|
107
|
+
output: {
|
|
108
|
+
type: "string",
|
|
109
|
+
description: "Output path for .d.ts file (default: ./promptly-env.d.ts)",
|
|
110
|
+
alias: "o"
|
|
111
|
+
},
|
|
112
|
+
"api-key": {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "API key (defaults to PROMPTLY_API_KEY env var)"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
run: async ({ args }) => {
|
|
118
|
+
console.log("@promptlycms/prompts \u2014 generating...\n");
|
|
119
|
+
const apiKey = args["api-key"] ?? process.env.PROMPTLY_API_KEY;
|
|
120
|
+
if (!apiKey) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
"No API key provided. Set PROMPTLY_API_KEY or pass --api-key."
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
const outputPath = args.output ?? DEFAULT_DTS_OUTPUT;
|
|
126
|
+
await generate(apiKey, outputPath);
|
|
127
|
+
console.log("\nDone!");
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
var main = defineCommand({
|
|
131
|
+
meta: {
|
|
132
|
+
name: "promptly",
|
|
133
|
+
version: "0.1.0",
|
|
134
|
+
description: "CLI for @promptlycms/prompts"
|
|
135
|
+
},
|
|
136
|
+
subCommands: {
|
|
137
|
+
generate: generateCommand
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
runMain(main);
|