@nirmos/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +62 -0
  2. package/dist/cjs/client.js +61 -0
  3. package/dist/cjs/client.js.map +1 -0
  4. package/dist/cjs/core/errors.js +143 -0
  5. package/dist/cjs/core/errors.js.map +1 -0
  6. package/dist/cjs/core/metadata.js +104 -0
  7. package/dist/cjs/core/metadata.js.map +1 -0
  8. package/dist/cjs/core/transport.js +276 -0
  9. package/dist/cjs/core/transport.js.map +1 -0
  10. package/dist/cjs/core/types.js +3 -0
  11. package/dist/cjs/core/types.js.map +1 -0
  12. package/dist/cjs/gateway/chat-stream.js +262 -0
  13. package/dist/cjs/gateway/chat-stream.js.map +1 -0
  14. package/dist/cjs/gateway/chat.js +102 -0
  15. package/dist/cjs/gateway/chat.js.map +1 -0
  16. package/dist/cjs/gateway/embeddings.js +33 -0
  17. package/dist/cjs/gateway/embeddings.js.map +1 -0
  18. package/dist/cjs/gateway/gateway.js +18 -0
  19. package/dist/cjs/gateway/gateway.js.map +1 -0
  20. package/dist/cjs/gateway/images.js +36 -0
  21. package/dist/cjs/gateway/images.js.map +1 -0
  22. package/dist/cjs/gateway/mappers.js +184 -0
  23. package/dist/cjs/gateway/mappers.js.map +1 -0
  24. package/dist/cjs/gateway/types.js +3 -0
  25. package/dist/cjs/gateway/types.js.map +1 -0
  26. package/dist/cjs/index.js +25 -0
  27. package/dist/cjs/index.js.map +1 -0
  28. package/dist/cjs/package.json +3 -0
  29. package/dist/cjs/prompts/cache.js +48 -0
  30. package/dist/cjs/prompts/cache.js.map +1 -0
  31. package/dist/cjs/prompts/mappers.js +242 -0
  32. package/dist/cjs/prompts/mappers.js.map +1 -0
  33. package/dist/cjs/prompts/prompts.js +121 -0
  34. package/dist/cjs/prompts/prompts.js.map +1 -0
  35. package/dist/cjs/prompts/render.js +86 -0
  36. package/dist/cjs/prompts/render.js.map +1 -0
  37. package/dist/cjs/prompts/types.js +3 -0
  38. package/dist/cjs/prompts/types.js.map +1 -0
  39. package/dist/cjs/prompts/versions.js +51 -0
  40. package/dist/cjs/prompts/versions.js.map +1 -0
  41. package/dist/esm/client.d.ts +28 -0
  42. package/dist/esm/client.d.ts.map +1 -0
  43. package/dist/esm/client.js +58 -0
  44. package/dist/esm/client.js.map +1 -0
  45. package/dist/esm/core/errors.d.ts +72 -0
  46. package/dist/esm/core/errors.d.ts.map +1 -0
  47. package/dist/esm/core/errors.js +125 -0
  48. package/dist/esm/core/errors.js.map +1 -0
  49. package/dist/esm/core/metadata.d.ts +11 -0
  50. package/dist/esm/core/metadata.d.ts.map +1 -0
  51. package/dist/esm/core/metadata.js +95 -0
  52. package/dist/esm/core/metadata.js.map +1 -0
  53. package/dist/esm/core/transport.d.ts +28 -0
  54. package/dist/esm/core/transport.d.ts.map +1 -0
  55. package/dist/esm/core/transport.js +272 -0
  56. package/dist/esm/core/transport.js.map +1 -0
  57. package/dist/esm/core/types.d.ts +43 -0
  58. package/dist/esm/core/types.d.ts.map +1 -0
  59. package/dist/esm/core/types.js +2 -0
  60. package/dist/esm/core/types.js.map +1 -0
  61. package/dist/esm/gateway/chat-stream.d.ts +9 -0
  62. package/dist/esm/gateway/chat-stream.d.ts.map +1 -0
  63. package/dist/esm/gateway/chat-stream.js +258 -0
  64. package/dist/esm/gateway/chat-stream.js.map +1 -0
  65. package/dist/esm/gateway/chat.d.ts +12 -0
  66. package/dist/esm/gateway/chat.d.ts.map +1 -0
  67. package/dist/esm/gateway/chat.js +98 -0
  68. package/dist/esm/gateway/chat.js.map +1 -0
  69. package/dist/esm/gateway/embeddings.d.ts +9 -0
  70. package/dist/esm/gateway/embeddings.d.ts.map +1 -0
  71. package/dist/esm/gateway/embeddings.js +29 -0
  72. package/dist/esm/gateway/embeddings.js.map +1 -0
  73. package/dist/esm/gateway/gateway.d.ts +12 -0
  74. package/dist/esm/gateway/gateway.d.ts.map +1 -0
  75. package/dist/esm/gateway/gateway.js +14 -0
  76. package/dist/esm/gateway/gateway.js.map +1 -0
  77. package/dist/esm/gateway/images.d.ts +9 -0
  78. package/dist/esm/gateway/images.d.ts.map +1 -0
  79. package/dist/esm/gateway/images.js +32 -0
  80. package/dist/esm/gateway/images.js.map +1 -0
  81. package/dist/esm/gateway/mappers.d.ts +11 -0
  82. package/dist/esm/gateway/mappers.d.ts.map +1 -0
  83. package/dist/esm/gateway/mappers.js +174 -0
  84. package/dist/esm/gateway/mappers.js.map +1 -0
  85. package/dist/esm/gateway/types.d.ts +168 -0
  86. package/dist/esm/gateway/types.d.ts.map +1 -0
  87. package/dist/esm/gateway/types.js +2 -0
  88. package/dist/esm/gateway/types.js.map +1 -0
  89. package/dist/esm/index.d.ts +8 -0
  90. package/dist/esm/index.d.ts.map +1 -0
  91. package/dist/esm/index.js +4 -0
  92. package/dist/esm/index.js.map +1 -0
  93. package/dist/esm/prompts/cache.d.ts +13 -0
  94. package/dist/esm/prompts/cache.d.ts.map +1 -0
  95. package/dist/esm/prompts/cache.js +44 -0
  96. package/dist/esm/prompts/cache.js.map +1 -0
  97. package/dist/esm/prompts/mappers.d.ts +8 -0
  98. package/dist/esm/prompts/mappers.d.ts.map +1 -0
  99. package/dist/esm/prompts/mappers.js +235 -0
  100. package/dist/esm/prompts/mappers.js.map +1 -0
  101. package/dist/esm/prompts/prompts.d.ts +17 -0
  102. package/dist/esm/prompts/prompts.d.ts.map +1 -0
  103. package/dist/esm/prompts/prompts.js +117 -0
  104. package/dist/esm/prompts/prompts.js.map +1 -0
  105. package/dist/esm/prompts/render.d.ts +3 -0
  106. package/dist/esm/prompts/render.d.ts.map +1 -0
  107. package/dist/esm/prompts/render.js +83 -0
  108. package/dist/esm/prompts/render.js.map +1 -0
  109. package/dist/esm/prompts/types.d.ts +119 -0
  110. package/dist/esm/prompts/types.d.ts.map +1 -0
  111. package/dist/esm/prompts/types.js +2 -0
  112. package/dist/esm/prompts/types.js.map +1 -0
  113. package/dist/esm/prompts/versions.d.ts +12 -0
  114. package/dist/esm/prompts/versions.d.ts.map +1 -0
  115. package/dist/esm/prompts/versions.js +47 -0
  116. package/dist/esm/prompts/versions.js.map +1 -0
  117. package/package.json +50 -0
  118. package/src/client.ts +89 -0
  119. package/src/core/errors.ts +160 -0
  120. package/src/core/metadata.ts +141 -0
  121. package/src/core/transport.ts +331 -0
  122. package/src/core/types.ts +51 -0
  123. package/src/gateway/chat-stream.ts +307 -0
  124. package/src/gateway/chat.ts +134 -0
  125. package/src/gateway/embeddings.ts +44 -0
  126. package/src/gateway/gateway.ts +17 -0
  127. package/src/gateway/images.ts +41 -0
  128. package/src/gateway/mappers.ts +234 -0
  129. package/src/gateway/types.ts +206 -0
  130. package/src/index.ts +83 -0
  131. package/src/prompts/cache.ts +50 -0
  132. package/src/prompts/mappers.ts +318 -0
  133. package/src/prompts/prompts.ts +188 -0
  134. package/src/prompts/render.ts +117 -0
  135. package/src/prompts/types.ts +148 -0
  136. package/src/prompts/versions.ts +80 -0
package/src/index.ts ADDED
@@ -0,0 +1,83 @@
1
+ export { Nirmos, NirmosClient } from "./client.js";
2
+ export type { NirmosOptions } from "./client.js";
3
+
4
+ export {
5
+ APIError,
6
+ AuthenticationError,
7
+ ConfigurationError,
8
+ ConflictError,
9
+ NetworkError,
10
+ NirmosError,
11
+ NotFoundError,
12
+ PermissionDeniedError,
13
+ PromptRenderError,
14
+ RateLimitError,
15
+ RequestAbortedError,
16
+ ServerError,
17
+ StreamError,
18
+ TimeoutError,
19
+ ValidationError,
20
+ } from "./core/errors.js";
21
+
22
+ export type {
23
+ HeadersInput,
24
+ JsonPrimitive,
25
+ JsonValue,
26
+ Page,
27
+ RequestMetadata,
28
+ RequestOptions,
29
+ Usage,
30
+ } from "./core/types.js";
31
+
32
+ export { ChatStream } from "./gateway/chat-stream.js";
33
+ export type {
34
+ AudioContentPart,
35
+ ChatChoice,
36
+ ChatCompletion,
37
+ ChatContentPart,
38
+ ChatCreateParams,
39
+ ChatMessage,
40
+ ChatRole,
41
+ ChatStreamEvent,
42
+ Embedding,
43
+ EmbeddingCreateParams,
44
+ EmbeddingResponse,
45
+ FinishReason,
46
+ FunctionTool,
47
+ FunctionToolCall,
48
+ GeneratedImage,
49
+ ImageContentPart,
50
+ ImageGenerateParams,
51
+ ImageGeneration,
52
+ ImageSize,
53
+ PromptReference,
54
+ ResponseFormat,
55
+ RoutingStrategy,
56
+ TextContentPart,
57
+ ToolChoice,
58
+ } from "./gateway/types.js";
59
+
60
+ export type {
61
+ ChatPromptContent,
62
+ Prompt,
63
+ PromptCacheConfig,
64
+ PromptCacheMode,
65
+ PromptContent,
66
+ PromptCreateParams,
67
+ PromptIdentifier,
68
+ PromptRenderOptions,
69
+ PromptRenderReference,
70
+ PromptRetrieveOptions,
71
+ PromptStatus,
72
+ PromptUpdateParams,
73
+ PromptVariable,
74
+ PromptVariableType,
75
+ PromptVariableValue,
76
+ PromptVariableValues,
77
+ PromptVersion,
78
+ PromptVersionCreateParams,
79
+ PromptVersionListOptions,
80
+ PromptVersionPage,
81
+ RenderedPrompt,
82
+ TextPromptContent,
83
+ } from "./prompts/types.js";
@@ -0,0 +1,50 @@
1
+ export interface CacheEntry<T> {
2
+ value: T;
3
+ expiresAt: number;
4
+ }
5
+
6
+ export class MemoryCache<T> {
7
+ readonly #entries = new Map<string, CacheEntry<T>>();
8
+ readonly #maxEntries: number;
9
+ readonly #ttlMs: number;
10
+
11
+ constructor(maxEntries: number, ttlMs: number) {
12
+ this.#maxEntries = maxEntries;
13
+ this.#ttlMs = ttlMs;
14
+ }
15
+
16
+ get(key: string): T | undefined {
17
+ const entry = this.#entries.get(key);
18
+ if (!entry) return undefined;
19
+ if (entry.expiresAt <= Date.now()) {
20
+ this.#entries.delete(key);
21
+ return undefined;
22
+ }
23
+ this.#entries.delete(key);
24
+ this.#entries.set(key, entry);
25
+ return entry.value;
26
+ }
27
+
28
+ set(key: string, value: T): void {
29
+ this.#entries.delete(key);
30
+ this.#entries.set(key, {
31
+ value,
32
+ expiresAt: Date.now() + this.#ttlMs,
33
+ });
34
+ while (this.#entries.size > this.#maxEntries) {
35
+ const oldest = this.#entries.keys().next().value as string | undefined;
36
+ if (oldest === undefined) break;
37
+ this.#entries.delete(oldest);
38
+ }
39
+ }
40
+
41
+ deleteMatching(predicate: (key: string) => boolean): void {
42
+ for (const key of this.#entries.keys()) {
43
+ if (predicate(key)) this.#entries.delete(key);
44
+ }
45
+ }
46
+
47
+ clear(): void {
48
+ this.#entries.clear();
49
+ }
50
+ }
@@ -0,0 +1,318 @@
1
+ import {
2
+ asUnknownRecord,
3
+ compactMetadata,
4
+ extractMetadata,
5
+ readArray,
6
+ readBoolean,
7
+ readNumber,
8
+ readString,
9
+ } from "../core/metadata.js";
10
+ import type { JsonValue, Page } from "../core/types.js";
11
+ import { normalizeMessage, toWireMessage } from "../gateway/mappers.js";
12
+ import type {
13
+ Prompt,
14
+ PromptContent,
15
+ PromptCreateParams,
16
+ PromptVariable,
17
+ PromptVersion,
18
+ PromptVersionCreateParams,
19
+ } from "./types.js";
20
+
21
+ export function toWirePromptCreate(
22
+ params: PromptCreateParams,
23
+ ): Record<string, unknown> {
24
+ return {
25
+ name: params.name,
26
+ slug: params.slug,
27
+ description: params.description,
28
+ content: toWireContent(params.content),
29
+ variables: params.variables?.map(toWireVariable),
30
+ metadata: params.metadata,
31
+ tags: params.tags,
32
+ folder: params.folder,
33
+ commit_message: params.commitMessage,
34
+ activate: params.activate,
35
+ };
36
+ }
37
+
38
+ export function toWireVersionCreate(
39
+ params: PromptVersionCreateParams,
40
+ ): Record<string, unknown> {
41
+ return {
42
+ content: toWireContent(params.content),
43
+ variables: params.variables?.map(toWireVariable),
44
+ metadata: params.metadata,
45
+ commit_message: params.commitMessage,
46
+ activate: params.activate,
47
+ };
48
+ }
49
+
50
+ export function normalizePrompt(payload: unknown, response: Response): Prompt {
51
+ const root = unwrap(payload);
52
+ const promptRecord = asUnknownRecord(root.prompt ?? root);
53
+ const versionCandidate =
54
+ root.version ??
55
+ root.active_version ??
56
+ root.activeVersion ??
57
+ root.latest_version ??
58
+ root.latestVersion ??
59
+ promptRecord.version ??
60
+ promptRecord.active_version ??
61
+ promptRecord.activeVersion ??
62
+ promptRecord.latest_version ??
63
+ promptRecord.latestVersion;
64
+ const versionRecord = isRecord(versionCandidate)
65
+ ? asUnknownRecord(versionCandidate)
66
+ : promptRecord;
67
+ const content = normalizeContent(
68
+ versionRecord.content ?? promptRecord.content,
69
+ versionRecord.messages ?? promptRecord.messages,
70
+ );
71
+ const variables = normalizeVariables(
72
+ versionRecord.variables ?? promptRecord.variables,
73
+ versionRecord.messages ?? promptRecord.messages,
74
+ );
75
+
76
+ return {
77
+ id: readString(promptRecord, "id") ?? "",
78
+ name: readString(promptRecord, "name") ?? "",
79
+ slug: readString(promptRecord, "slug") ?? "",
80
+ description: readString(promptRecord, "description"),
81
+ status:
82
+ readString(promptRecord, "status") ??
83
+ readString(versionRecord, "status") ??
84
+ "draft",
85
+ version: normalizeVersionRecord(versionRecord, content, variables),
86
+ content,
87
+ variables,
88
+ metadata: normalizeJsonRecord(promptRecord.metadata),
89
+ tags: normalizeTags(promptRecord.tags),
90
+ folder: readString(promptRecord, "folder", "folder_path", "folderPath"),
91
+ createdAt: normalizeDate(promptRecord.created_at ?? promptRecord.createdAt),
92
+ updatedAt: normalizeDate(promptRecord.updated_at ?? promptRecord.updatedAt),
93
+ requestMetadata: compactMetadata(extractMetadata(response, payload)),
94
+ };
95
+ }
96
+
97
+ export function normalizeVersion(payload: unknown): PromptVersion {
98
+ const root = unwrap(payload);
99
+ const versionRecord = isRecord(root.version)
100
+ ? asUnknownRecord(root.version)
101
+ : root;
102
+ const content = normalizeContent(
103
+ versionRecord.content,
104
+ versionRecord.messages,
105
+ );
106
+ const variables = normalizeVariables(
107
+ versionRecord.variables,
108
+ versionRecord.messages,
109
+ );
110
+ return normalizeVersionRecord(versionRecord, content, variables);
111
+ }
112
+
113
+ export function normalizeVersionPage(payload: unknown): Page<PromptVersion> {
114
+ const root = asUnknownRecord(payload);
115
+ const unwrapped = asUnknownRecord(root.data ?? root);
116
+ const entries = Array.isArray(root.data)
117
+ ? root.data
118
+ : readArray(unwrapped, "data", "versions");
119
+ return {
120
+ data: entries.map((entry) => normalizeVersion(entry)),
121
+ hasMore:
122
+ readBoolean(root, "has_more", "hasMore") ??
123
+ readBoolean(unwrapped, "has_more", "hasMore") ??
124
+ false,
125
+ nextCursor:
126
+ readString(root, "next_cursor", "nextCursor") ??
127
+ readString(unwrapped, "next_cursor", "nextCursor"),
128
+ };
129
+ }
130
+
131
+ function normalizeVersionRecord(
132
+ version: Record<string, unknown>,
133
+ content: PromptContent,
134
+ variables: readonly PromptVariable[],
135
+ ): PromptVersion {
136
+ const status = readString(version, "status") ?? "draft";
137
+ return {
138
+ id: readString(version, "id") ?? "",
139
+ number: readNumber(version, "number", "version") ?? 0,
140
+ status,
141
+ active:
142
+ readBoolean(version, "active", "is_active", "isActive") ??
143
+ (status === "active" || status === "published"),
144
+ content,
145
+ variables,
146
+ metadata: normalizeJsonRecord(version.metadata),
147
+ commitMessage: readString(
148
+ version,
149
+ "commit_message",
150
+ "commitMessage",
151
+ "message",
152
+ ),
153
+ createdAt: normalizeDate(version.created_at ?? version.createdAt),
154
+ updatedAt: normalizeDate(version.updated_at ?? version.updatedAt),
155
+ };
156
+ }
157
+
158
+ function normalizeContent(
159
+ value: unknown,
160
+ messagesValue: unknown,
161
+ ): PromptContent {
162
+ const content = asUnknownRecord(value);
163
+ const type = readString(content, "type");
164
+ const messages = Array.isArray(messagesValue)
165
+ ? messagesValue
166
+ : readArray(content, "messages");
167
+
168
+ if (type === "chat" || messages.length > 0) {
169
+ return {
170
+ type: "chat",
171
+ messages: messages.map(normalizePromptMessage),
172
+ };
173
+ }
174
+ if (type === "text") {
175
+ return {
176
+ type: "text",
177
+ text: readString(content, "text") ?? "",
178
+ role: (readString(content, "role") ?? "user") as
179
+ "system" | "developer" | "user" | "assistant",
180
+ };
181
+ }
182
+ if (typeof value === "string") {
183
+ return { type: "text", text: value, role: "user" };
184
+ }
185
+ return {
186
+ type: "text",
187
+ text: readString(content, "text") ?? "",
188
+ role: "user",
189
+ };
190
+ }
191
+
192
+ function normalizePromptMessage(value: unknown) {
193
+ const message = asUnknownRecord(value);
194
+ const blocks = readArray(message, "content_blocks", "contentBlocks");
195
+ if (blocks.length === 0) return normalizeMessage(message);
196
+
197
+ const text = blocks
198
+ .map((entry) => {
199
+ const block = asUnknownRecord(entry);
200
+ return readString(block, "text") ?? "";
201
+ })
202
+ .join("");
203
+ return normalizeMessage({ ...message, content: text });
204
+ }
205
+
206
+ function normalizeVariables(
207
+ value: unknown,
208
+ messagesValue: unknown,
209
+ ): readonly PromptVariable[] {
210
+ const direct = Array.isArray(value) ? value : [];
211
+ if (direct.length > 0) return direct.map(normalizeVariable);
212
+
213
+ const variables = new Map<string, PromptVariable>();
214
+ for (const rawMessage of Array.isArray(messagesValue) ? messagesValue : []) {
215
+ const message = asUnknownRecord(rawMessage);
216
+ for (const rawBlock of readArray(
217
+ message,
218
+ "content_blocks",
219
+ "contentBlocks",
220
+ )) {
221
+ const block = asUnknownRecord(rawBlock);
222
+ const metadata = asUnknownRecord(block.metadata);
223
+ for (const rawVariable of readArray(metadata, "variables")) {
224
+ const variable = normalizeVariable(rawVariable);
225
+ if (variable.name) variables.set(variable.name, variable);
226
+ }
227
+ }
228
+ }
229
+ return [...variables.values()];
230
+ }
231
+
232
+ function normalizeVariable(value: unknown): PromptVariable {
233
+ const variable = asUnknownRecord(value);
234
+ return {
235
+ name: readString(variable, "name") ?? "",
236
+ type: (readString(variable, "type") ?? "string") as PromptVariable["type"],
237
+ required: readBoolean(variable, "required"),
238
+ description: readString(variable, "description"),
239
+ default: variable.default as JsonValue | undefined,
240
+ values: readArray(variable, "values", "enum").filter(
241
+ (entry): entry is string => typeof entry === "string",
242
+ ),
243
+ min: readNumber(variable, "min"),
244
+ max: readNumber(variable, "max"),
245
+ minLength: readNumber(variable, "min_length", "minLength"),
246
+ maxLength: readNumber(variable, "max_length", "maxLength"),
247
+ itemType: readString(
248
+ variable,
249
+ "item_type",
250
+ "itemType",
251
+ ) as PromptVariable["itemType"],
252
+ };
253
+ }
254
+
255
+ function toWireContent(content: PromptContent): Record<string, unknown> {
256
+ if (content.type === "text") {
257
+ return { type: "text", text: content.text, role: content.role };
258
+ }
259
+ return {
260
+ type: "chat",
261
+ messages: content.messages.map(toWireMessage),
262
+ };
263
+ }
264
+
265
+ function toWireVariable(variable: PromptVariable): Record<string, unknown> {
266
+ return {
267
+ name: variable.name,
268
+ type: variable.type,
269
+ required: variable.required,
270
+ description: variable.description,
271
+ default: variable.default,
272
+ enum: variable.values,
273
+ min: variable.min,
274
+ max: variable.max,
275
+ min_length: variable.minLength,
276
+ max_length: variable.maxLength,
277
+ item_type: variable.itemType,
278
+ };
279
+ }
280
+
281
+ function normalizeJsonRecord(
282
+ value: unknown,
283
+ ): Readonly<Record<string, JsonValue>> {
284
+ const record = asUnknownRecord(value);
285
+ return record as Readonly<Record<string, JsonValue>>;
286
+ }
287
+
288
+ function normalizeTags(value: unknown): readonly string[] {
289
+ if (!Array.isArray(value)) return [];
290
+ return value
291
+ .map((tag) =>
292
+ typeof tag === "string"
293
+ ? tag
294
+ : readString(asUnknownRecord(tag), "name", "slug"),
295
+ )
296
+ .filter((tag): tag is string => tag !== undefined);
297
+ }
298
+
299
+ function normalizeDate(value: unknown): string | undefined {
300
+ if (typeof value === "string") return value;
301
+ if (typeof value === "number") {
302
+ const milliseconds = value < 1_000_000_000_000 ? value * 1_000 : value;
303
+ return new Date(milliseconds).toISOString();
304
+ }
305
+ return undefined;
306
+ }
307
+
308
+ function unwrap(payload: unknown): Record<string, unknown> {
309
+ const root = asUnknownRecord(payload);
310
+ if (root.success === true && root.data !== undefined) {
311
+ return asUnknownRecord(root.data);
312
+ }
313
+ return root;
314
+ }
315
+
316
+ function isRecord(value: unknown): value is Record<string, unknown> {
317
+ return value !== null && typeof value === "object" && !Array.isArray(value);
318
+ }
@@ -0,0 +1,188 @@
1
+ import type { HttpTransport } from "../core/transport.js";
2
+ import type { RequestOptions } from "../core/types.js";
3
+ import { MemoryCache } from "./cache.js";
4
+ import { normalizePrompt, toWirePromptCreate } from "./mappers.js";
5
+ import { renderPrompt } from "./render.js";
6
+ import type {
7
+ Prompt,
8
+ PromptCacheConfig,
9
+ PromptCreateParams,
10
+ PromptIdentifier,
11
+ PromptRenderOptions,
12
+ PromptRenderReference,
13
+ PromptRetrieveOptions,
14
+ PromptUpdateParams,
15
+ PromptVariableValues,
16
+ RenderedPrompt,
17
+ } from "./types.js";
18
+ import { PromptVersions } from "./versions.js";
19
+
20
+ export class Prompts {
21
+ readonly versions: PromptVersions;
22
+ readonly #transport: HttpTransport;
23
+ readonly #cache?: MemoryCache<Prompt>;
24
+ readonly #inflight = new Map<string, Promise<Prompt>>();
25
+
26
+ constructor(
27
+ transport: HttpTransport,
28
+ config: PromptCacheConfig | false = {},
29
+ ) {
30
+ this.#transport = transport;
31
+ if (config !== false && config.enabled !== false) {
32
+ this.#cache = new MemoryCache(
33
+ positiveInteger(config.maxEntries, 100),
34
+ nonNegativeNumber(config.ttlMs, 60_000),
35
+ );
36
+ }
37
+ this.versions = new PromptVersions(transport, () => this.clearCache());
38
+ }
39
+
40
+ async create(
41
+ params: PromptCreateParams,
42
+ options?: RequestOptions,
43
+ ): Promise<Prompt> {
44
+ const { data, response } = await this.#transport.request<unknown>({
45
+ method: "POST",
46
+ path: "/v1/prompts",
47
+ body: toWirePromptCreate(params),
48
+ options,
49
+ });
50
+ this.clearCache();
51
+ return normalizePrompt(data, response);
52
+ }
53
+
54
+ async retrieve(
55
+ identifier: PromptIdentifier,
56
+ options: PromptRetrieveOptions = {},
57
+ ): Promise<Prompt> {
58
+ const {
59
+ version = "active",
60
+ cache = "default",
61
+ ...requestOptions
62
+ } = options;
63
+ const key = cacheKey(identifier, version);
64
+
65
+ if (cache === "default") {
66
+ const cached = this.#cache?.get(key);
67
+ if (cached) return cached;
68
+ const pending = this.#inflight.get(key);
69
+ if (pending) return pending;
70
+ }
71
+
72
+ const pending = this.#retrieveFromApi(
73
+ identifier,
74
+ version,
75
+ requestOptions,
76
+ ).then((prompt) => {
77
+ if (cache !== "no-store") this.#cache?.set(key, prompt);
78
+ return prompt;
79
+ });
80
+
81
+ if (cache === "default") this.#inflight.set(key, pending);
82
+ try {
83
+ return await pending;
84
+ } finally {
85
+ this.#inflight.delete(key);
86
+ }
87
+ }
88
+
89
+ async retrieveActive(
90
+ identifier: PromptIdentifier,
91
+ options: Omit<PromptRetrieveOptions, "version"> = {},
92
+ ): Promise<Prompt> {
93
+ return this.retrieve(identifier, { ...options, version: "active" });
94
+ }
95
+
96
+ async retrieveLatest(
97
+ identifier: PromptIdentifier,
98
+ options: Omit<PromptRetrieveOptions, "version"> = {},
99
+ ): Promise<Prompt> {
100
+ return this.retrieve(identifier, { ...options, version: "latest" });
101
+ }
102
+
103
+ async update(
104
+ identifier: PromptIdentifier,
105
+ params: PromptUpdateParams,
106
+ options?: RequestOptions,
107
+ ): Promise<Prompt> {
108
+ const { data, response } = await this.#transport.request<unknown>({
109
+ method: "PATCH",
110
+ path: `/v1/prompts/${encodeURIComponent(identifier)}`,
111
+ body: {
112
+ name: params.name,
113
+ slug: params.slug,
114
+ description: params.description,
115
+ metadata: params.metadata,
116
+ tags: params.tags,
117
+ folder: params.folder,
118
+ status: params.status,
119
+ },
120
+ options,
121
+ });
122
+ this.clearCache();
123
+ return normalizePrompt(data, response);
124
+ }
125
+
126
+ async render(
127
+ promptOrReference: Prompt | PromptRenderReference,
128
+ values: PromptVariableValues = {},
129
+ options: PromptRenderOptions = {},
130
+ ): Promise<RenderedPrompt> {
131
+ const prompt =
132
+ "content" in promptOrReference
133
+ ? promptOrReference
134
+ : await this.retrieve(promptOrReference.id, {
135
+ version: promptOrReference.version,
136
+ cache: promptOrReference.cache,
137
+ });
138
+ return renderPrompt(prompt, values, options);
139
+ }
140
+
141
+ clearCache(identifier?: PromptIdentifier): void {
142
+ if (!identifier) {
143
+ this.#cache?.clear();
144
+ return;
145
+ }
146
+ const encoded = encodeURIComponent(identifier);
147
+ this.#cache?.deleteMatching(
148
+ (key) => key.startsWith(`${encoded}:`) || key.includes(`:${encoded}:`),
149
+ );
150
+ }
151
+
152
+ async #retrieveFromApi(
153
+ identifier: PromptIdentifier,
154
+ version: number | "active" | "latest",
155
+ options: RequestOptions,
156
+ ): Promise<Prompt> {
157
+ const encoded = encodeURIComponent(identifier);
158
+ const { data, response } = await this.#transport.request<unknown>({
159
+ method: "GET",
160
+ path: `/v1/prompts/${encoded}`,
161
+ query: version === "active" ? undefined : { version },
162
+ options,
163
+ });
164
+ return normalizePrompt(data, response);
165
+ }
166
+ }
167
+
168
+ function cacheKey(
169
+ identifier: PromptIdentifier,
170
+ version: number | "active" | "latest",
171
+ ): string {
172
+ return `${encodeURIComponent(identifier)}:${version}`;
173
+ }
174
+
175
+ function positiveInteger(value: number | undefined, fallback: number): number {
176
+ return value !== undefined && Number.isInteger(value) && value > 0
177
+ ? value
178
+ : fallback;
179
+ }
180
+
181
+ function nonNegativeNumber(
182
+ value: number | undefined,
183
+ fallback: number,
184
+ ): number {
185
+ return value !== undefined && Number.isFinite(value) && value >= 0
186
+ ? value
187
+ : fallback;
188
+ }