nuabase 1.0.0 → 1.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.
package/README.md CHANGED
@@ -1,26 +1,25 @@
1
- # Nuabase TypeScript client
1
+ ## Nuabase TypeScript SDK
2
2
 
3
- Nuabase turns LLM prompts into type-safe functions in under five lines.
3
+ Nuabase turns LLM prompts into type-safe functions in under five lines of code. Set up your free account now at [Nuabase](https://nuabase.com).
4
+
5
+ With Nuabase, you write a prompt, specify the output schema, and get back a well-typed function. You can invoke it with input data, like any other code in your application, and receive data that matches the exact schema you specified.
4
6
 
5
7
  Example use-cases:
6
- * Enrich a list of food items with nutrition facts
7
- * Tag sales leads with more details about the company
8
- * Convert free-form instructions into precise order forms—directly inside your application.
9
8
 
10
- You can describe the transformation once, and get back a well-typed function, and call it like any other code in your application.
9
+ - Provide free-form text input for faster filling of complex HTML forms
10
+ - Enrich a list of food items with nutrition facts
11
+ - Tag sales leads with more details about the company
11
12
 
12
- You don't have to do any custom custom orchestration, or run polling, or schema validation.
13
+ Behind the scenes, Nuabase runs the LLM transformation as async jobs, and once ready, call your server API with the results using Webhooks. It can also stream the result directly to your front-end using SSE. You can also see all requests and responses as they happen in the [Nuabase Console](https://console.nuabase.com). All outputs are guaranteed to match the schema you specified.
13
14
 
14
- Behind the scenes, Nuabase runs the async jobs, SSE streaming, webhooks, logging, caching, cost attribution, and strict JSON-schema enforcement. You also get an observability dashboard for every request so you always know what the LLM produced.
15
+ One major reason I built Nuabase is the need for granular, row-level caching. For example, let's say you want to classify your bank transaction entries, and map them to your chart of accounts. With Nuabase's row-level caching, it will only send new entries to the LLM. Any specific entry that it has seen before will be returned from the cache. This also means you can make LLM requests multiple times with identical values, and after the first time, they will return immediately without needing to go through the LLM.
15
16
 
16
- ## At a Glance
17
+ ## Usage at a glance
17
18
 
18
19
  **1. Input.** Start with the data you want to send to the LLM:
19
20
 
20
21
  ```ts
21
- const leads = [
22
- { id: 'lead-101', notes: 'Growth-stage SaaS, ~80 employees, wants a demo.' },
23
- ];
22
+ const leads = [{ id: 'lead-101', notes: 'Growth-stage SaaS, ~80 employees, wants a demo.' }];
24
23
  ```
25
24
 
26
25
  **2. Desired shape.** Describe the structure you expect back:
@@ -35,10 +34,12 @@ const LeadInsights = z.object({
35
34
  **3. Declare the prompt.** Turn it into a typed function:
36
35
 
37
36
  ```ts
38
- const classifyLeads = new Nua().map({
39
- name: 'insights',
40
- output: LeadInsights,
37
+ const classifyLeads = new Nua().createArrayFn({
41
38
  prompt: 'Classify each lead with industry and company_size bucket.',
39
+ output: {
40
+ name: 'leadInsights',
41
+ schema: LeadInsights,
42
+ },
42
43
  });
43
44
  ```
44
45
 
@@ -78,11 +79,13 @@ const LeadInsights = z.object({
78
79
  recommended_follow_up: z.enum(['Call', 'Email', 'Event']),
79
80
  });
80
81
 
81
- const enrichLeads = nua.map({
82
- name: 'leadInsights',
83
- output: LeadInsights,
82
+ const enrichLeads = nua.createArrayFn({
84
83
  prompt:
85
84
  'Summarize each inbound lead by extracting company_name, industry, company_size bucket (SMB, Mid-market, Enterprise), and the recommended_follow_up channel (Call, Email, or Event) based on their notes.',
85
+ output: {
86
+ name: 'leadInsights',
87
+ schema: LeadInsights,
88
+ },
86
89
  });
87
90
 
88
91
  const rows = [
@@ -2,6 +2,16 @@
2
2
 
3
3
  var zod = require('zod');
4
4
 
5
+ function resolveConfigValue(config, key, envVar) {
6
+ if (config[key]) return config[key];
7
+ const pe = globalThis.process;
8
+ const isNode = !!pe?.versions?.node;
9
+ if (isNode) {
10
+ return pe?.env?.[envVar];
11
+ }
12
+ return void 0;
13
+ }
14
+
5
15
  async function parseErrorResponse(response, urlPath) {
6
16
  const fallbackMessage = `Request to ${urlPath} failed with status ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
7
17
  let responseBody = "";
@@ -47,25 +57,29 @@ function getErrorMessageFromException(error) {
47
57
 
48
58
  class NuabaseAPIClient {
49
59
  constructor(config = {}) {
50
- this.apiKey = config.apiKey || process.env.NUABASE_API_KEY || "";
51
- this.baseUrl = config.baseUrl || process.env.NUABASE_API_URL || "https://api.nuabase.com";
60
+ this.apiKey = resolveConfigValue(config, "apiKey", "NUABASE_API_KEY") || "";
61
+ this.baseUrl = resolveConfigValue(config, "baseUrl", "NUABASE_API_URL") || "https://api.nuabase.com";
52
62
  if (!this.apiKey) {
53
63
  throw new Error(
54
64
  "API key is required. Provide it via config.apiKey or NUABASE_API_KEY environment variable."
55
65
  );
56
66
  }
57
67
  }
58
- async request(urlPath, params) {
68
+ async request(method, urlPath, params) {
59
69
  const url = `${this.baseUrl}/${urlPath}`;
60
70
  let response;
71
+ const hasBody = method !== "GET" && params !== void 0;
72
+ const headers = {
73
+ Authorization: `Bearer ${this.apiKey}`
74
+ };
75
+ if (hasBody) {
76
+ headers["Content-Type"] = "application/json";
77
+ }
61
78
  try {
62
79
  response = await fetch(url, {
63
- method: "POST",
64
- headers: {
65
- "Content-Type": "application/json",
66
- Authorization: `Bearer ${this.apiKey}`
67
- },
68
- body: JSON.stringify(params)
80
+ method,
81
+ headers,
82
+ body: hasBody ? JSON.stringify(params) : void 0
69
83
  });
70
84
  } catch (e) {
71
85
  return { error: `Error calling Nuabase API: ${getErrorMessageFromException(e)}` };
@@ -83,22 +97,46 @@ class NuabaseAPIClient {
83
97
  }
84
98
  }
85
99
 
86
- const zs_MapResponseRecord = (primaryKeyName, outputKey, outputSchema) => {
100
+ const zs_NuaQueuedResponse = zod.z.object({
101
+ id: zod.z.string(),
102
+ sseUrl: zod.z.string().min(1)
103
+ }).strict();
104
+
105
+ const validateArrayRequestParams = (data, primaryKeyName) => {
106
+ if (!Array.isArray(data)) {
107
+ throw new Error("`data` must be an array of objects.");
108
+ }
109
+ if (!primaryKeyName || typeof primaryKeyName !== "string") {
110
+ throw new Error("`primaryKeyName` must be a non-empty string.");
111
+ }
112
+ data.forEach((item, index) => {
113
+ if (item === null || typeof item !== "object" || Array.isArray(item)) {
114
+ throw new Error(`data[${index}] must be a non-null object.`);
115
+ }
116
+ if (!(primaryKeyName in item)) {
117
+ throw new Error(`data[${index}] must contain property '${primaryKeyName}'.`);
118
+ }
119
+ });
120
+ };
121
+
122
+ const zs_ResponseRecord = (primaryKeyName, outputKey, outputSchema) => {
87
123
  const primaryKeySchema = zod.z.any();
88
- return zod.z.object({
124
+ const shape = {
89
125
  [primaryKeyName]: primaryKeySchema,
90
126
  [outputKey]: outputSchema
91
- }).strict();
92
- };
93
- const zs_MapSuccessDataResult = (primaryKeyName, outputKey, outputSchema) => {
94
- const recordSchema = zs_MapResponseRecord(primaryKeyName, outputKey, outputSchema);
95
- return zod.z.array(recordSchema);
127
+ };
128
+ return zod.z.object(shape).strict();
96
129
  };
97
- const zs_MapRequestApiResponse_Success = (primaryKeyName, outputKey, outputSchema) => {
98
- const dataSchema = zs_MapSuccessDataResult(primaryKeyName, outputKey, outputSchema);
130
+ const createNuaApiResponseCastArray = (primaryKeyName, outputKey, outputSchema) => {
131
+ const recordSchema = zs_ResponseRecord(
132
+ primaryKeyName,
133
+ outputKey,
134
+ outputSchema
135
+ );
136
+ const dataSchema = zod.z.array(recordSchema);
99
137
  return zod.z.object({
100
138
  llmRequestId: zod.z.string(),
101
- kind: zod.z.literal("map"),
139
+ kind: zod.z.literal("cast/array"),
102
140
  data: dataSchema,
103
141
  cacheHits: zod.z.number(),
104
142
  rowsWithNoResults: zod.z.array(zod.z.string()),
@@ -106,72 +144,309 @@ const zs_MapRequestApiResponse_Success = (primaryKeyName, outputKey, outputSchem
106
144
  isError: zod.z.optional(zod.z.literal(false))
107
145
  }).strict();
108
146
  };
109
- class Nua {
110
- constructor(config) {
111
- this.apiClient = new NuabaseAPIClient(config);
147
+ const zs_NuaApiResponse_CastArray = createNuaApiResponseCastArray;
148
+
149
+ const zs_ArrayFnDef = zod.z.object({
150
+ prompt: zod.z.string(),
151
+ output: zod.z.object({
152
+ name: zod.z.string(),
153
+ schema: zod.z.custom(
154
+ (value) => value instanceof zod.z.ZodType,
155
+ "Expected a Zod schema"
156
+ )
157
+ })
158
+ });
159
+ const validateArrayFnDef = (fnDef) => {
160
+ const parsed = zs_ArrayFnDef.safeParse(fnDef);
161
+ if (!parsed.success) {
162
+ const message = zod.z.prettifyError(parsed.error);
163
+ throw new TypeError(`Invalid array function definition: ${message}`);
112
164
  }
113
- // Some of the checks here mirror type validation, but since our code could be run from
114
- // untyped JavaScript, we still want runtime checks
115
- static validateMapRequestParams(data, primaryKeyName) {
116
- if (!Array.isArray(data)) {
117
- throw new Error("`data` must be an array of objects.");
118
- }
119
- if (!primaryKeyName || typeof primaryKeyName !== "string") {
120
- throw new Error("`primaryKeyName` must be a non-empty string.");
165
+ return parsed.data;
166
+ };
167
+
168
+ function addOriginalInputRows(inputRows, outputRows, primaryKeyName) {
169
+ const lookup = /* @__PURE__ */ new Map();
170
+ for (const row of inputRows) {
171
+ lookup.set(row[primaryKeyName], row);
172
+ }
173
+ return outputRows.map((row) => {
174
+ const primaryKeyValue = row[primaryKeyName];
175
+ const sourceRow = lookup.get(primaryKeyValue);
176
+ if (!sourceRow) {
177
+ throw new Error(
178
+ `Unable to locate source row for primary key "${String(primaryKeyValue)}" in cast array response.`
179
+ );
121
180
  }
122
- data.forEach((item, index) => {
123
- if (item === null || typeof item !== "object" || Array.isArray(item)) {
124
- throw new Error(`data[${index}] must be a non-null object.`);
125
- }
126
- if (!(primaryKeyName in item)) {
127
- throw new Error(`data[${index}] must contain property '${primaryKeyName}'.`);
128
- }
129
- });
130
- }
131
- map(fnDef) {
132
- const client = this.apiClient;
133
- const outputJsonSchema = zod.z.toJSONSchema(fnDef.output);
134
- const makeRequest = async (path, data, primaryKeyName) => {
135
- Nua.validateMapRequestParams(data, primaryKeyName);
136
- const response = await client.request(path, {
137
- input: {
138
- prompt: fnDef.prompt,
139
- data,
140
- primaryKey: primaryKeyName
141
- },
142
- output: {
143
- schema: outputJsonSchema,
144
- name: fnDef.name
145
- }
146
- });
147
- if (response && typeof response === "object" && "error" in response)
148
- return { error: String(response.error), isError: true };
149
- const outputKey = fnDef.name;
150
- const successSchema = zs_MapRequestApiResponse_Success(primaryKeyName, outputKey, fnDef.output);
151
- const parsedResponse = successSchema.safeParse(response);
152
- if (!parsedResponse.success) {
153
- const emsg = zod.z.prettifyError(parsedResponse.error);
154
- return { error: `Invalid data received from Nuabase API: ${emsg}`, isError: true };
155
- }
156
- return {
157
- isSuccess: true,
158
- llmRequestId: parsedResponse.data.llmRequestId,
159
- kind: parsedResponse.data.kind,
160
- data: parsedResponse.data.data,
161
- rowsWithNoResults: parsedResponse.data.rowsWithNoResults,
162
- cacheHits: parsedResponse.data.cacheHits
163
- };
181
+ return {
182
+ ...row,
183
+ sourceRow
164
184
  };
165
- const baseInvoker = async (data, primaryKeyName) => {
166
- return await makeRequest("map", data, primaryKeyName);
185
+ });
186
+ }
187
+ const toCastArrayApiRequest = (prompt, data, primaryKeyName, outputName, outputSchema) => ({
188
+ input: {
189
+ prompt,
190
+ data,
191
+ primaryKey: primaryKeyName
192
+ },
193
+ output: {
194
+ schema: outputSchema,
195
+ name: outputName
196
+ }
197
+ });
198
+ const createArrayFn = (client, inputFnDef) => {
199
+ const fnDef = validateArrayFnDef(inputFnDef);
200
+ const outputJsonSchema = zod.z.toJSONSchema(fnDef.output.schema);
201
+ const toNuabaseError = (response) => {
202
+ if (response && typeof response === "object" && "error" in response)
203
+ return { error: String(response.error), isError: true };
204
+ return void 0;
205
+ };
206
+ const baseFn = async (data, primaryKeyName) => {
207
+ validateArrayRequestParams(data, primaryKeyName);
208
+ const response = await client.request(
209
+ "POST",
210
+ "cast/array",
211
+ toCastArrayApiRequest(
212
+ fnDef.prompt,
213
+ data,
214
+ primaryKeyName,
215
+ fnDef.output.name,
216
+ outputJsonSchema
217
+ )
218
+ );
219
+ const error = toNuabaseError(response);
220
+ if (error) return error;
221
+ const parsedResponse = zs_NuaQueuedResponse.safeParse(response);
222
+ if (!parsedResponse.success) {
223
+ const emsg = zod.z.prettifyError(parsedResponse.error);
224
+ return { error: `Invalid queued response received from Nuabase API: ${emsg}`, isError: true };
225
+ }
226
+ return parsedResponse.data;
227
+ };
228
+ const nowFn = async (data, primaryKeyName) => {
229
+ validateArrayRequestParams(data, primaryKeyName);
230
+ const response = await client.request(
231
+ "POST",
232
+ "cast/array/now",
233
+ toCastArrayApiRequest(
234
+ fnDef.prompt,
235
+ data,
236
+ primaryKeyName,
237
+ fnDef.output.name,
238
+ outputJsonSchema
239
+ )
240
+ );
241
+ const error = toNuabaseError(response);
242
+ if (error) return error;
243
+ const outputKey = fnDef.output.name;
244
+ const successSchema = zs_NuaApiResponse_CastArray(primaryKeyName, outputKey, fnDef.output.schema);
245
+ const parsedResponse = successSchema.safeParse(response);
246
+ if (!parsedResponse.success) {
247
+ const emsg = zod.z.prettifyError(parsedResponse.error);
248
+ return { error: `Invalid data received from Nuabase API: ${emsg}`, isError: true };
249
+ }
250
+ return {
251
+ isSuccess: true,
252
+ llmRequestId: parsedResponse.data.llmRequestId,
253
+ kind: parsedResponse.data.kind,
254
+ data: addOriginalInputRows(
255
+ data,
256
+ parsedResponse.data.data,
257
+ primaryKeyName
258
+ ),
259
+ rowsWithNoResults: parsedResponse.data.rowsWithNoResults,
260
+ cacheHits: parsedResponse.data.cacheHits
167
261
  };
168
- const nowInvoker = async (data, primaryKeyName) => {
169
- return await makeRequest("map/now", data, primaryKeyName);
262
+ };
263
+ return Object.assign(baseFn, {
264
+ now: nowFn
265
+ });
266
+ };
267
+
268
+ const createNuaApiResponseCastValue = (outputKey, outputSchema) => {
269
+ return zod.z.object({
270
+ llmRequestId: zod.z.string(),
271
+ kind: zod.z.literal("cast/value"),
272
+ data: outputSchema,
273
+ isCacheHit: zod.z.boolean(),
274
+ isSuccess: zod.z.literal(true),
275
+ isError: zod.z.optional(zod.z.literal(false))
276
+ }).strict();
277
+ };
278
+ const zs_NuaApiResponse_CastValue = createNuaApiResponseCastValue;
279
+
280
+ const zs_ValueFnDef = zod.z.object({
281
+ prompt: zod.z.string(),
282
+ output: zod.z.object({
283
+ name: zod.z.string(),
284
+ schema: zod.z.custom(
285
+ (value) => value instanceof zod.z.ZodType,
286
+ "Expected a Zod schema for `output.schema`"
287
+ )
288
+ })
289
+ });
290
+ const validateValueFnDef = (fnDef) => {
291
+ const parsed = zs_ValueFnDef.safeParse(fnDef);
292
+ if (!parsed.success) {
293
+ const message = zod.z.prettifyError(parsed.error);
294
+ throw new TypeError(`Invalid value function definition: ${message}`);
295
+ }
296
+ return parsed.data;
297
+ };
298
+
299
+ const toCastValueApiRequest = (prompt, data, outputName, outputSchema) => ({
300
+ input: {
301
+ prompt,
302
+ data
303
+ },
304
+ output: {
305
+ schema: outputSchema,
306
+ name: outputName
307
+ }
308
+ });
309
+ const createValueFn = (client, inputFnDef) => {
310
+ const fnDef = validateValueFnDef(inputFnDef);
311
+ const outputJsonSchema = zod.z.toJSONSchema(fnDef.output.schema);
312
+ const toNuabaseError = (response) => {
313
+ if (response && typeof response === "object" && "error" in response)
314
+ return { error: String(response.error), isError: true };
315
+ return void 0;
316
+ };
317
+ const sendRequest = async (path, data) => client.request(
318
+ "POST",
319
+ path,
320
+ toCastValueApiRequest(fnDef.prompt, data, fnDef.output.name, outputJsonSchema)
321
+ );
322
+ const baseFn = async (data) => {
323
+ const response = await sendRequest("cast/value", data);
324
+ const error = toNuabaseError(response);
325
+ if (error) return error;
326
+ const parsedResponse = zs_NuaQueuedResponse.safeParse(response);
327
+ if (!parsedResponse.success) {
328
+ const emsg = zod.z.prettifyError(parsedResponse.error);
329
+ return { error: `Invalid queued response received from Nuabase API: ${emsg}`, isError: true };
330
+ }
331
+ return parsedResponse.data;
332
+ };
333
+ const nowFn = async (data) => {
334
+ const response = await sendRequest("cast/value/now", data);
335
+ const error = toNuabaseError(response);
336
+ if (error) return error;
337
+ const outputKey = fnDef.output.name;
338
+ const successSchema = zs_NuaApiResponse_CastValue(
339
+ outputKey,
340
+ fnDef.output.schema
341
+ );
342
+ const parsedResponse = successSchema.safeParse(response);
343
+ if (!parsedResponse.success) {
344
+ const emsg = zod.z.prettifyError(parsedResponse.error);
345
+ return { error: `Invalid data received from Nuabase API: ${emsg}`, isError: true };
346
+ }
347
+ return {
348
+ isSuccess: true,
349
+ llmRequestId: parsedResponse.data.llmRequestId,
350
+ kind: parsedResponse.data.kind,
351
+ data: parsedResponse.data.data,
352
+ isCacheHit: parsedResponse.data.isCacheHit
170
353
  };
171
- const fnReturnValue = Object.assign(baseInvoker, {
172
- now: nowInvoker
173
- });
174
- return fnReturnValue;
354
+ };
355
+ return Object.assign(baseFn, {
356
+ now: nowFn
357
+ });
358
+ };
359
+
360
+ const errorParseFailed = {
361
+ kind: "internal-error",
362
+ message: "Unable to parse stored LLM result"
363
+ };
364
+ const dateFromIso = zod.z.preprocess(
365
+ (value) => {
366
+ if (value == null) return null;
367
+ if (value instanceof Date) return value;
368
+ if (typeof value === "string") {
369
+ const parsed = new Date(value);
370
+ if (!Number.isNaN(parsed.getTime())) return parsed;
371
+ }
372
+ return zod.z.NEVER;
373
+ },
374
+ zod.z.union([zod.z.date(), zod.z.null()])
375
+ );
376
+ const mandatoryDateFromIso = zod.z.preprocess((value) => {
377
+ if (value instanceof Date) return value;
378
+ if (typeof value === "string") {
379
+ const parsed = new Date(value);
380
+ if (!Number.isNaN(parsed.getTime())) return parsed;
381
+ }
382
+ return zod.z.NEVER;
383
+ }, zod.z.date());
384
+ const apiResponseSchema = zod.z.object({
385
+ id: zod.z.string(),
386
+ requestType: zod.z.string(),
387
+ llmStatus: zod.z.union([
388
+ zod.z.literal("pending"),
389
+ zod.z.literal("processing"),
390
+ zod.z.literal("success"),
391
+ zod.z.literal("failed")
392
+ ]),
393
+ sseStatus: zod.z.union([
394
+ zod.z.literal("n/a"),
395
+ zod.z.literal("pending"),
396
+ zod.z.literal("sent"),
397
+ zod.z.literal("failed")
398
+ ]),
399
+ webhookStatus: zod.z.union([
400
+ zod.z.literal("n/a"),
401
+ zod.z.literal("pending"),
402
+ zod.z.literal("sent"),
403
+ zod.z.literal("failed")
404
+ ]),
405
+ input: zod.z.object({
406
+ prompt: zod.z.union([zod.z.string(), zod.z.null()]),
407
+ data: zod.z.union([zod.z.string(), zod.z.null()]),
408
+ primaryKey: zod.z.union([zod.z.string(), zod.z.null()])
409
+ }),
410
+ output: zod.z.object({
411
+ name: zod.z.string(),
412
+ schema: zod.z.string(),
413
+ effectiveSchema: zod.z.string()
414
+ }),
415
+ result: zod.z.union([zod.z.record(zod.z.string(), zod.z.unknown()), zod.z.null()]).optional(),
416
+ error: zod.z.union([zod.z.string(), zod.z.null()]),
417
+ fullPrompt: zod.z.string(),
418
+ model: zod.z.string(),
419
+ provider: zod.z.string(),
420
+ startedAt: dateFromIso,
421
+ finishedAt: dateFromIso,
422
+ createdAt: mandatoryDateFromIso,
423
+ updatedAt: mandatoryDateFromIso
424
+ });
425
+ const isNuabaseError = (value) => Boolean(value && typeof value === "object" && "isError" in value && value.isError);
426
+ async function getRequest(client, llmRequestId) {
427
+ const response = await client.request("GET", `requests/${encodeURIComponent(llmRequestId)}`);
428
+ if (isNuabaseError(response)) {
429
+ return response;
430
+ }
431
+ const parsedResponse = apiResponseSchema.safeParse(response);
432
+ if (!parsedResponse.success) {
433
+ return errorParseFailed;
434
+ }
435
+ return parsedResponse.data;
436
+ }
437
+
438
+ class Nua {
439
+ constructor(config) {
440
+ this.apiClient = new NuabaseAPIClient(config);
441
+ }
442
+ createArrayFn(fnDef) {
443
+ return createArrayFn(this.apiClient, fnDef);
444
+ }
445
+ createFn(fnDef) {
446
+ return createValueFn(this.apiClient, fnDef);
447
+ }
448
+ async getRequest(llmRequestId) {
449
+ return getRequest(this.apiClient, llmRequestId);
175
450
  }
176
451
  }
177
452
 
@@ -1,5 +1,15 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ function resolveConfigValue(config, key, envVar) {
4
+ if (config[key]) return config[key];
5
+ const pe = globalThis.process;
6
+ const isNode = !!pe?.versions?.node;
7
+ if (isNode) {
8
+ return pe?.env?.[envVar];
9
+ }
10
+ return void 0;
11
+ }
12
+
3
13
  async function parseErrorResponse(response, urlPath) {
4
14
  const fallbackMessage = `Request to ${urlPath} failed with status ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
5
15
  let responseBody = "";
@@ -45,25 +55,29 @@ function getErrorMessageFromException(error) {
45
55
 
46
56
  class NuabaseAPIClient {
47
57
  constructor(config = {}) {
48
- this.apiKey = config.apiKey || process.env.NUABASE_API_KEY || "";
49
- this.baseUrl = config.baseUrl || process.env.NUABASE_API_URL || "https://api.nuabase.com";
58
+ this.apiKey = resolveConfigValue(config, "apiKey", "NUABASE_API_KEY") || "";
59
+ this.baseUrl = resolveConfigValue(config, "baseUrl", "NUABASE_API_URL") || "https://api.nuabase.com";
50
60
  if (!this.apiKey) {
51
61
  throw new Error(
52
62
  "API key is required. Provide it via config.apiKey or NUABASE_API_KEY environment variable."
53
63
  );
54
64
  }
55
65
  }
56
- async request(urlPath, params) {
66
+ async request(method, urlPath, params) {
57
67
  const url = `${this.baseUrl}/${urlPath}`;
58
68
  let response;
69
+ const hasBody = method !== "GET" && params !== void 0;
70
+ const headers = {
71
+ Authorization: `Bearer ${this.apiKey}`
72
+ };
73
+ if (hasBody) {
74
+ headers["Content-Type"] = "application/json";
75
+ }
59
76
  try {
60
77
  response = await fetch(url, {
61
- method: "POST",
62
- headers: {
63
- "Content-Type": "application/json",
64
- Authorization: `Bearer ${this.apiKey}`
65
- },
66
- body: JSON.stringify(params)
78
+ method,
79
+ headers,
80
+ body: hasBody ? JSON.stringify(params) : void 0
67
81
  });
68
82
  } catch (e) {
69
83
  return { error: `Error calling Nuabase API: ${getErrorMessageFromException(e)}` };
@@ -81,22 +95,46 @@ class NuabaseAPIClient {
81
95
  }
82
96
  }
83
97
 
84
- const zs_MapResponseRecord = (primaryKeyName, outputKey, outputSchema) => {
98
+ const zs_NuaQueuedResponse = z.object({
99
+ id: z.string(),
100
+ sseUrl: z.string().min(1)
101
+ }).strict();
102
+
103
+ const validateArrayRequestParams = (data, primaryKeyName) => {
104
+ if (!Array.isArray(data)) {
105
+ throw new Error("`data` must be an array of objects.");
106
+ }
107
+ if (!primaryKeyName || typeof primaryKeyName !== "string") {
108
+ throw new Error("`primaryKeyName` must be a non-empty string.");
109
+ }
110
+ data.forEach((item, index) => {
111
+ if (item === null || typeof item !== "object" || Array.isArray(item)) {
112
+ throw new Error(`data[${index}] must be a non-null object.`);
113
+ }
114
+ if (!(primaryKeyName in item)) {
115
+ throw new Error(`data[${index}] must contain property '${primaryKeyName}'.`);
116
+ }
117
+ });
118
+ };
119
+
120
+ const zs_ResponseRecord = (primaryKeyName, outputKey, outputSchema) => {
85
121
  const primaryKeySchema = z.any();
86
- return z.object({
122
+ const shape = {
87
123
  [primaryKeyName]: primaryKeySchema,
88
124
  [outputKey]: outputSchema
89
- }).strict();
90
- };
91
- const zs_MapSuccessDataResult = (primaryKeyName, outputKey, outputSchema) => {
92
- const recordSchema = zs_MapResponseRecord(primaryKeyName, outputKey, outputSchema);
93
- return z.array(recordSchema);
125
+ };
126
+ return z.object(shape).strict();
94
127
  };
95
- const zs_MapRequestApiResponse_Success = (primaryKeyName, outputKey, outputSchema) => {
96
- const dataSchema = zs_MapSuccessDataResult(primaryKeyName, outputKey, outputSchema);
128
+ const createNuaApiResponseCastArray = (primaryKeyName, outputKey, outputSchema) => {
129
+ const recordSchema = zs_ResponseRecord(
130
+ primaryKeyName,
131
+ outputKey,
132
+ outputSchema
133
+ );
134
+ const dataSchema = z.array(recordSchema);
97
135
  return z.object({
98
136
  llmRequestId: z.string(),
99
- kind: z.literal("map"),
137
+ kind: z.literal("cast/array"),
100
138
  data: dataSchema,
101
139
  cacheHits: z.number(),
102
140
  rowsWithNoResults: z.array(z.string()),
@@ -104,72 +142,309 @@ const zs_MapRequestApiResponse_Success = (primaryKeyName, outputKey, outputSchem
104
142
  isError: z.optional(z.literal(false))
105
143
  }).strict();
106
144
  };
107
- class Nua {
108
- constructor(config) {
109
- this.apiClient = new NuabaseAPIClient(config);
145
+ const zs_NuaApiResponse_CastArray = createNuaApiResponseCastArray;
146
+
147
+ const zs_ArrayFnDef = z.object({
148
+ prompt: z.string(),
149
+ output: z.object({
150
+ name: z.string(),
151
+ schema: z.custom(
152
+ (value) => value instanceof z.ZodType,
153
+ "Expected a Zod schema"
154
+ )
155
+ })
156
+ });
157
+ const validateArrayFnDef = (fnDef) => {
158
+ const parsed = zs_ArrayFnDef.safeParse(fnDef);
159
+ if (!parsed.success) {
160
+ const message = z.prettifyError(parsed.error);
161
+ throw new TypeError(`Invalid array function definition: ${message}`);
110
162
  }
111
- // Some of the checks here mirror type validation, but since our code could be run from
112
- // untyped JavaScript, we still want runtime checks
113
- static validateMapRequestParams(data, primaryKeyName) {
114
- if (!Array.isArray(data)) {
115
- throw new Error("`data` must be an array of objects.");
116
- }
117
- if (!primaryKeyName || typeof primaryKeyName !== "string") {
118
- throw new Error("`primaryKeyName` must be a non-empty string.");
163
+ return parsed.data;
164
+ };
165
+
166
+ function addOriginalInputRows(inputRows, outputRows, primaryKeyName) {
167
+ const lookup = /* @__PURE__ */ new Map();
168
+ for (const row of inputRows) {
169
+ lookup.set(row[primaryKeyName], row);
170
+ }
171
+ return outputRows.map((row) => {
172
+ const primaryKeyValue = row[primaryKeyName];
173
+ const sourceRow = lookup.get(primaryKeyValue);
174
+ if (!sourceRow) {
175
+ throw new Error(
176
+ `Unable to locate source row for primary key "${String(primaryKeyValue)}" in cast array response.`
177
+ );
119
178
  }
120
- data.forEach((item, index) => {
121
- if (item === null || typeof item !== "object" || Array.isArray(item)) {
122
- throw new Error(`data[${index}] must be a non-null object.`);
123
- }
124
- if (!(primaryKeyName in item)) {
125
- throw new Error(`data[${index}] must contain property '${primaryKeyName}'.`);
126
- }
127
- });
128
- }
129
- map(fnDef) {
130
- const client = this.apiClient;
131
- const outputJsonSchema = z.toJSONSchema(fnDef.output);
132
- const makeRequest = async (path, data, primaryKeyName) => {
133
- Nua.validateMapRequestParams(data, primaryKeyName);
134
- const response = await client.request(path, {
135
- input: {
136
- prompt: fnDef.prompt,
137
- data,
138
- primaryKey: primaryKeyName
139
- },
140
- output: {
141
- schema: outputJsonSchema,
142
- name: fnDef.name
143
- }
144
- });
145
- if (response && typeof response === "object" && "error" in response)
146
- return { error: String(response.error), isError: true };
147
- const outputKey = fnDef.name;
148
- const successSchema = zs_MapRequestApiResponse_Success(primaryKeyName, outputKey, fnDef.output);
149
- const parsedResponse = successSchema.safeParse(response);
150
- if (!parsedResponse.success) {
151
- const emsg = z.prettifyError(parsedResponse.error);
152
- return { error: `Invalid data received from Nuabase API: ${emsg}`, isError: true };
153
- }
154
- return {
155
- isSuccess: true,
156
- llmRequestId: parsedResponse.data.llmRequestId,
157
- kind: parsedResponse.data.kind,
158
- data: parsedResponse.data.data,
159
- rowsWithNoResults: parsedResponse.data.rowsWithNoResults,
160
- cacheHits: parsedResponse.data.cacheHits
161
- };
179
+ return {
180
+ ...row,
181
+ sourceRow
162
182
  };
163
- const baseInvoker = async (data, primaryKeyName) => {
164
- return await makeRequest("map", data, primaryKeyName);
183
+ });
184
+ }
185
+ const toCastArrayApiRequest = (prompt, data, primaryKeyName, outputName, outputSchema) => ({
186
+ input: {
187
+ prompt,
188
+ data,
189
+ primaryKey: primaryKeyName
190
+ },
191
+ output: {
192
+ schema: outputSchema,
193
+ name: outputName
194
+ }
195
+ });
196
+ const createArrayFn = (client, inputFnDef) => {
197
+ const fnDef = validateArrayFnDef(inputFnDef);
198
+ const outputJsonSchema = z.toJSONSchema(fnDef.output.schema);
199
+ const toNuabaseError = (response) => {
200
+ if (response && typeof response === "object" && "error" in response)
201
+ return { error: String(response.error), isError: true };
202
+ return void 0;
203
+ };
204
+ const baseFn = async (data, primaryKeyName) => {
205
+ validateArrayRequestParams(data, primaryKeyName);
206
+ const response = await client.request(
207
+ "POST",
208
+ "cast/array",
209
+ toCastArrayApiRequest(
210
+ fnDef.prompt,
211
+ data,
212
+ primaryKeyName,
213
+ fnDef.output.name,
214
+ outputJsonSchema
215
+ )
216
+ );
217
+ const error = toNuabaseError(response);
218
+ if (error) return error;
219
+ const parsedResponse = zs_NuaQueuedResponse.safeParse(response);
220
+ if (!parsedResponse.success) {
221
+ const emsg = z.prettifyError(parsedResponse.error);
222
+ return { error: `Invalid queued response received from Nuabase API: ${emsg}`, isError: true };
223
+ }
224
+ return parsedResponse.data;
225
+ };
226
+ const nowFn = async (data, primaryKeyName) => {
227
+ validateArrayRequestParams(data, primaryKeyName);
228
+ const response = await client.request(
229
+ "POST",
230
+ "cast/array/now",
231
+ toCastArrayApiRequest(
232
+ fnDef.prompt,
233
+ data,
234
+ primaryKeyName,
235
+ fnDef.output.name,
236
+ outputJsonSchema
237
+ )
238
+ );
239
+ const error = toNuabaseError(response);
240
+ if (error) return error;
241
+ const outputKey = fnDef.output.name;
242
+ const successSchema = zs_NuaApiResponse_CastArray(primaryKeyName, outputKey, fnDef.output.schema);
243
+ const parsedResponse = successSchema.safeParse(response);
244
+ if (!parsedResponse.success) {
245
+ const emsg = z.prettifyError(parsedResponse.error);
246
+ return { error: `Invalid data received from Nuabase API: ${emsg}`, isError: true };
247
+ }
248
+ return {
249
+ isSuccess: true,
250
+ llmRequestId: parsedResponse.data.llmRequestId,
251
+ kind: parsedResponse.data.kind,
252
+ data: addOriginalInputRows(
253
+ data,
254
+ parsedResponse.data.data,
255
+ primaryKeyName
256
+ ),
257
+ rowsWithNoResults: parsedResponse.data.rowsWithNoResults,
258
+ cacheHits: parsedResponse.data.cacheHits
165
259
  };
166
- const nowInvoker = async (data, primaryKeyName) => {
167
- return await makeRequest("map/now", data, primaryKeyName);
260
+ };
261
+ return Object.assign(baseFn, {
262
+ now: nowFn
263
+ });
264
+ };
265
+
266
+ const createNuaApiResponseCastValue = (outputKey, outputSchema) => {
267
+ return z.object({
268
+ llmRequestId: z.string(),
269
+ kind: z.literal("cast/value"),
270
+ data: outputSchema,
271
+ isCacheHit: z.boolean(),
272
+ isSuccess: z.literal(true),
273
+ isError: z.optional(z.literal(false))
274
+ }).strict();
275
+ };
276
+ const zs_NuaApiResponse_CastValue = createNuaApiResponseCastValue;
277
+
278
+ const zs_ValueFnDef = z.object({
279
+ prompt: z.string(),
280
+ output: z.object({
281
+ name: z.string(),
282
+ schema: z.custom(
283
+ (value) => value instanceof z.ZodType,
284
+ "Expected a Zod schema for `output.schema`"
285
+ )
286
+ })
287
+ });
288
+ const validateValueFnDef = (fnDef) => {
289
+ const parsed = zs_ValueFnDef.safeParse(fnDef);
290
+ if (!parsed.success) {
291
+ const message = z.prettifyError(parsed.error);
292
+ throw new TypeError(`Invalid value function definition: ${message}`);
293
+ }
294
+ return parsed.data;
295
+ };
296
+
297
+ const toCastValueApiRequest = (prompt, data, outputName, outputSchema) => ({
298
+ input: {
299
+ prompt,
300
+ data
301
+ },
302
+ output: {
303
+ schema: outputSchema,
304
+ name: outputName
305
+ }
306
+ });
307
+ const createValueFn = (client, inputFnDef) => {
308
+ const fnDef = validateValueFnDef(inputFnDef);
309
+ const outputJsonSchema = z.toJSONSchema(fnDef.output.schema);
310
+ const toNuabaseError = (response) => {
311
+ if (response && typeof response === "object" && "error" in response)
312
+ return { error: String(response.error), isError: true };
313
+ return void 0;
314
+ };
315
+ const sendRequest = async (path, data) => client.request(
316
+ "POST",
317
+ path,
318
+ toCastValueApiRequest(fnDef.prompt, data, fnDef.output.name, outputJsonSchema)
319
+ );
320
+ const baseFn = async (data) => {
321
+ const response = await sendRequest("cast/value", data);
322
+ const error = toNuabaseError(response);
323
+ if (error) return error;
324
+ const parsedResponse = zs_NuaQueuedResponse.safeParse(response);
325
+ if (!parsedResponse.success) {
326
+ const emsg = z.prettifyError(parsedResponse.error);
327
+ return { error: `Invalid queued response received from Nuabase API: ${emsg}`, isError: true };
328
+ }
329
+ return parsedResponse.data;
330
+ };
331
+ const nowFn = async (data) => {
332
+ const response = await sendRequest("cast/value/now", data);
333
+ const error = toNuabaseError(response);
334
+ if (error) return error;
335
+ const outputKey = fnDef.output.name;
336
+ const successSchema = zs_NuaApiResponse_CastValue(
337
+ outputKey,
338
+ fnDef.output.schema
339
+ );
340
+ const parsedResponse = successSchema.safeParse(response);
341
+ if (!parsedResponse.success) {
342
+ const emsg = z.prettifyError(parsedResponse.error);
343
+ return { error: `Invalid data received from Nuabase API: ${emsg}`, isError: true };
344
+ }
345
+ return {
346
+ isSuccess: true,
347
+ llmRequestId: parsedResponse.data.llmRequestId,
348
+ kind: parsedResponse.data.kind,
349
+ data: parsedResponse.data.data,
350
+ isCacheHit: parsedResponse.data.isCacheHit
168
351
  };
169
- const fnReturnValue = Object.assign(baseInvoker, {
170
- now: nowInvoker
171
- });
172
- return fnReturnValue;
352
+ };
353
+ return Object.assign(baseFn, {
354
+ now: nowFn
355
+ });
356
+ };
357
+
358
+ const errorParseFailed = {
359
+ kind: "internal-error",
360
+ message: "Unable to parse stored LLM result"
361
+ };
362
+ const dateFromIso = z.preprocess(
363
+ (value) => {
364
+ if (value == null) return null;
365
+ if (value instanceof Date) return value;
366
+ if (typeof value === "string") {
367
+ const parsed = new Date(value);
368
+ if (!Number.isNaN(parsed.getTime())) return parsed;
369
+ }
370
+ return z.NEVER;
371
+ },
372
+ z.union([z.date(), z.null()])
373
+ );
374
+ const mandatoryDateFromIso = z.preprocess((value) => {
375
+ if (value instanceof Date) return value;
376
+ if (typeof value === "string") {
377
+ const parsed = new Date(value);
378
+ if (!Number.isNaN(parsed.getTime())) return parsed;
379
+ }
380
+ return z.NEVER;
381
+ }, z.date());
382
+ const apiResponseSchema = z.object({
383
+ id: z.string(),
384
+ requestType: z.string(),
385
+ llmStatus: z.union([
386
+ z.literal("pending"),
387
+ z.literal("processing"),
388
+ z.literal("success"),
389
+ z.literal("failed")
390
+ ]),
391
+ sseStatus: z.union([
392
+ z.literal("n/a"),
393
+ z.literal("pending"),
394
+ z.literal("sent"),
395
+ z.literal("failed")
396
+ ]),
397
+ webhookStatus: z.union([
398
+ z.literal("n/a"),
399
+ z.literal("pending"),
400
+ z.literal("sent"),
401
+ z.literal("failed")
402
+ ]),
403
+ input: z.object({
404
+ prompt: z.union([z.string(), z.null()]),
405
+ data: z.union([z.string(), z.null()]),
406
+ primaryKey: z.union([z.string(), z.null()])
407
+ }),
408
+ output: z.object({
409
+ name: z.string(),
410
+ schema: z.string(),
411
+ effectiveSchema: z.string()
412
+ }),
413
+ result: z.union([z.record(z.string(), z.unknown()), z.null()]).optional(),
414
+ error: z.union([z.string(), z.null()]),
415
+ fullPrompt: z.string(),
416
+ model: z.string(),
417
+ provider: z.string(),
418
+ startedAt: dateFromIso,
419
+ finishedAt: dateFromIso,
420
+ createdAt: mandatoryDateFromIso,
421
+ updatedAt: mandatoryDateFromIso
422
+ });
423
+ const isNuabaseError = (value) => Boolean(value && typeof value === "object" && "isError" in value && value.isError);
424
+ async function getRequest(client, llmRequestId) {
425
+ const response = await client.request("GET", `requests/${encodeURIComponent(llmRequestId)}`);
426
+ if (isNuabaseError(response)) {
427
+ return response;
428
+ }
429
+ const parsedResponse = apiResponseSchema.safeParse(response);
430
+ if (!parsedResponse.success) {
431
+ return errorParseFailed;
432
+ }
433
+ return parsedResponse.data;
434
+ }
435
+
436
+ class Nua {
437
+ constructor(config) {
438
+ this.apiClient = new NuabaseAPIClient(config);
439
+ }
440
+ createArrayFn(fnDef) {
441
+ return createArrayFn(this.apiClient, fnDef);
442
+ }
443
+ createFn(fnDef) {
444
+ return createValueFn(this.apiClient, fnDef);
445
+ }
446
+ async getRequest(llmRequestId) {
447
+ return getRequest(this.apiClient, llmRequestId);
173
448
  }
174
449
  }
175
450
 
@@ -0,0 +1,12 @@
1
+ import z from 'zod';
2
+ export declare const processedSchema: z.ZodObject<{
3
+ processed_id: z.ZodString;
4
+ }, z.core.$strip>;
5
+ export declare const sseResponseSchema: z.ZodObject<{
6
+ isSuccess: z.ZodLiteral<true>;
7
+ llmRequestId: z.ZodString;
8
+ kind: z.ZodLiteral<"cast/value">;
9
+ data: z.ZodUnknown;
10
+ isCacheHit: z.ZodOptional<z.ZodBoolean>;
11
+ }, z.core.$loose>;
12
+ export declare const waitForSuccessfulSseMessage: (sseUrl: string, timeoutMs?: number) => Promise<Record<string, unknown>>;
@@ -7,5 +7,5 @@ export declare class NuabaseAPIClient {
7
7
  private readonly apiKey;
8
8
  private readonly baseUrl;
9
9
  constructor(config?: NuabaseConfig);
10
- request(urlPath: string, params: object): Promise<NuabaseError | unknown>;
10
+ request(method: 'GET' | 'POST', urlPath: string, params?: object): Promise<NuabaseError | unknown>;
11
11
  }
@@ -0,0 +1 @@
1
+ export declare function resolveConfigValue<T extends Record<string, any>>(config: T, key: keyof T, envVar: string): string | undefined;
@@ -0,0 +1,4 @@
1
+ import { NuabaseAPIClient } from '../../lib/api-client';
2
+ import { ArrayFn, ArrayFnDef } from './types';
3
+ import { z } from 'zod';
4
+ export declare const createArrayFn: <OutputName extends string, OutputZodSchema extends z.ZodTypeAny>(client: NuabaseAPIClient, inputFnDef: ArrayFnDef<OutputName, OutputZodSchema>) => ArrayFn<OutputZodSchema, OutputName>;
@@ -0,0 +1 @@
1
+ export declare const validateArrayRequestParams: (data: unknown, primaryKeyName: unknown) => void;
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod';
2
+ export type UserDataPKValue = string | number;
3
+ export type PrimaryKeyedInputRecord<PrimaryKeyName extends string> = Record<PrimaryKeyName, UserDataPKValue> & Record<string, unknown>;
4
+ type ResponseRow<OutputZodSchema extends z.ZodTypeAny, OutputName extends string, PrimaryKeyName extends string, InputRecord extends PrimaryKeyedInputRecord<PrimaryKeyName>> = Pick<InputRecord, PrimaryKeyName> & Record<OutputName, z.infer<OutputZodSchema>>;
5
+ type NuaApiResponseShape<OutputZodSchema extends z.ZodTypeAny, OutputName extends string, PrimaryKeyName extends string, InputRecord extends PrimaryKeyedInputRecord<PrimaryKeyName>> = {
6
+ llmRequestId: string;
7
+ kind: 'cast/array';
8
+ data: Array<ResponseRow<OutputZodSchema, OutputName, PrimaryKeyName, InputRecord>>;
9
+ cacheHits: number;
10
+ rowsWithNoResults: UserDataPKValue[];
11
+ isSuccess: true;
12
+ isError?: false;
13
+ };
14
+ type NuaApiResponseCastArrayBuilder = <OutputZodSchema extends z.ZodTypeAny, OutputName extends string, PrimaryKeyName extends string, InputRecord extends PrimaryKeyedInputRecord<PrimaryKeyName>>(primaryKeyName: PrimaryKeyName, outputKey: OutputName, outputSchema: OutputZodSchema) => z.ZodType<NuaApiResponseShape<OutputZodSchema, OutputName, PrimaryKeyName, InputRecord>>;
15
+ export declare const zs_NuaApiResponse_CastArray: NuaApiResponseCastArrayBuilder;
16
+ export type NuaApiResponse_CastArray<OutputZodSchema extends z.ZodTypeAny, OutputName extends string, PrimaryKeyName extends string, InputRecord extends PrimaryKeyedInputRecord<PrimaryKeyName>> = z.infer<ReturnType<typeof zs_NuaApiResponse_CastArray<OutputZodSchema, OutputName, PrimaryKeyName, InputRecord>>>;
17
+ export {};
@@ -0,0 +1,33 @@
1
+ import { NuabaseError } from '../../lib/error-response';
2
+ import { NuaQueuedResponse } from '../common/queued-response';
3
+ import type { NuaApiResponse_CastArray, PrimaryKeyedInputRecord } from './response-schema';
4
+ import { z } from 'zod';
5
+ export type ArrayFnDef<OutputName extends string, OutputZodSchema extends z.ZodTypeAny> = {
6
+ prompt: string;
7
+ output: {
8
+ name: OutputName;
9
+ schema: OutputZodSchema;
10
+ };
11
+ };
12
+ export declare const validateArrayFnDef: <OutputName extends string, OutputZodSchema extends z.ZodTypeAny>(fnDef: ArrayFnDef<OutputName, OutputZodSchema>) => ArrayFnDef<OutputName, OutputZodSchema>;
13
+ type CastArraySuccessFromApi<OutputZodSchema extends z.ZodTypeAny, OutputName extends string, PrimaryKeyName extends string, InputRecord extends PrimaryKeyedInputRecord<PrimaryKeyName>> = NuaApiResponse_CastArray<OutputZodSchema, OutputName, PrimaryKeyName, InputRecord>;
14
+ type CastArraySourceRowAugmentation<PrimaryKeyName extends string, InputRecord extends PrimaryKeyedInputRecord<PrimaryKeyName>> = {
15
+ /**
16
+ * SDK-added field containing the full input row for convenience; not returned by the API.
17
+ */
18
+ sourceRow: InputRecord;
19
+ };
20
+ type CastArraySuccessRowWithSource<OutputZodSchema extends z.ZodTypeAny, OutputName extends string, PrimaryKeyName extends string, InputRecord extends PrimaryKeyedInputRecord<PrimaryKeyName>> = CastArraySuccessFromApi<OutputZodSchema, OutputName, PrimaryKeyName, InputRecord>['data'][number] & CastArraySourceRowAugmentation<PrimaryKeyName, InputRecord>;
21
+ export type CastArraySuccessWithSource<OutputZodSchema extends z.ZodTypeAny, OutputName extends string, PrimaryKeyName extends string, InputRecord extends PrimaryKeyedInputRecord<PrimaryKeyName>> = Omit<CastArraySuccessFromApi<OutputZodSchema, OutputName, PrimaryKeyName, InputRecord>, 'data'> & {
22
+ /**
23
+ * Each row mirrors the API payload and includes the SDK-provided `sourceRow`.
24
+ */
25
+ data: CastArraySuccessRowWithSource<OutputZodSchema, OutputName, PrimaryKeyName, InputRecord>[];
26
+ };
27
+ export type ArrayFnResult<OutputZodSchema extends z.ZodTypeAny, OutputName extends string, PrimaryKeyName extends string, InputRecord extends PrimaryKeyedInputRecord<PrimaryKeyName>> = NuabaseError | CastArraySuccessWithSource<OutputZodSchema, OutputName, PrimaryKeyName, InputRecord>;
28
+ export type ArrayFnQueuedResult = NuabaseError | NuaQueuedResponse;
29
+ export type ArrayFn<OutputZodSchema extends z.ZodTypeAny, OutputName extends string> = {
30
+ <PrimaryKeyName extends string, InputRecord extends PrimaryKeyedInputRecord<PrimaryKeyName>>(data: InputRecord[], primaryKeyName: PrimaryKeyName): Promise<ArrayFnQueuedResult>;
31
+ now: <PrimaryKeyName extends string, InputRecord extends PrimaryKeyedInputRecord<PrimaryKeyName>>(data: InputRecord[], primaryKeyName: PrimaryKeyName) => Promise<ArrayFnResult<OutputZodSchema, OutputName, PrimaryKeyName, InputRecord>>;
32
+ };
33
+ export {};
@@ -0,0 +1,4 @@
1
+ import { NuabaseAPIClient } from '../../lib/api-client';
2
+ import { ValueFn, ValueFnDef } from './types';
3
+ import { z } from 'zod';
4
+ export declare const createValueFn: <OutputName extends string, OutputZodSchema extends z.ZodTypeAny>(client: NuabaseAPIClient, inputFnDef: ValueFnDef<OutputName, OutputZodSchema>) => ValueFn<OutputZodSchema, OutputName>;
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod';
2
+ type NuaApiResponseShape<OutputZodSchema extends z.ZodTypeAny> = {
3
+ llmRequestId: string;
4
+ kind: 'cast/value';
5
+ data: z.infer<OutputZodSchema>;
6
+ isCacheHit: boolean;
7
+ isSuccess: true;
8
+ isError?: false;
9
+ };
10
+ type NuaApiResponseCastValueBuilder = <OutputZodSchema extends z.ZodTypeAny, OutputName extends string>(outputKey: OutputName, outputSchema: OutputZodSchema) => z.ZodType<NuaApiResponseShape<OutputZodSchema>>;
11
+ export declare const zs_NuaApiResponse_CastValue: NuaApiResponseCastValueBuilder;
12
+ export type NuaApiResponse_CastValue<OutputZodSchema extends z.ZodTypeAny, OutputName extends string> = z.infer<ReturnType<typeof zs_NuaApiResponse_CastValue<OutputZodSchema, OutputName>>>;
13
+ export {};
@@ -0,0 +1,18 @@
1
+ import { NuabaseError } from '../../lib/error-response';
2
+ import { NuaQueuedResponse } from '../common/queued-response';
3
+ import { NuaApiResponse_CastValue } from './response-schema';
4
+ import { z } from 'zod';
5
+ export type ValueFnDef<OutputName extends string, OutputZodSchema extends z.ZodTypeAny> = {
6
+ prompt: string;
7
+ output: {
8
+ name: OutputName;
9
+ schema: OutputZodSchema;
10
+ };
11
+ };
12
+ export declare const validateValueFnDef: <OutputName extends string, OutputZodSchema extends z.ZodTypeAny>(fnDef: ValueFnDef<OutputName, OutputZodSchema>) => ValueFnDef<OutputName, OutputZodSchema>;
13
+ export type ValueFnResult<OutputZodSchema extends z.ZodTypeAny, OutputName extends string> = NuabaseError | NuaApiResponse_CastValue<OutputZodSchema, OutputName>;
14
+ export type ValueFnQueuedResult = NuabaseError | NuaQueuedResponse;
15
+ export type ValueFn<OutputZodSchema extends z.ZodTypeAny, OutputName extends string> = {
16
+ (data: unknown): Promise<ValueFnQueuedResult>;
17
+ now: (data: unknown) => Promise<ValueFnResult<OutputZodSchema, OutputName>>;
18
+ };
@@ -0,0 +1,6 @@
1
+ import { z } from 'zod';
2
+ export declare const zs_NuaQueuedResponse: z.ZodObject<{
3
+ id: z.ZodString;
4
+ sseUrl: z.ZodString;
5
+ }, z.core.$strict>;
6
+ export type NuaQueuedResponse = z.infer<typeof zs_NuaQueuedResponse>;
@@ -0,0 +1,12 @@
1
+ import { NuabaseAPIClient, NuabaseConfig } from '../lib/api-client';
2
+ import { ArrayFn, ArrayFnDef } from './cast_array/types';
3
+ import { ValueFn, ValueFnDef } from './cast_value/types';
4
+ import { GetRequestResult } from './get-request';
5
+ import { z } from 'zod';
6
+ export declare class Nua {
7
+ apiClient: NuabaseAPIClient;
8
+ constructor(config?: NuabaseConfig);
9
+ createArrayFn<OutputName extends string, OutputZodSchema extends z.ZodTypeAny>(fnDef: ArrayFnDef<OutputName, OutputZodSchema>): ArrayFn<OutputZodSchema, OutputName>;
10
+ createFn<OutputName extends string, OutputZodSchema extends z.ZodTypeAny>(fnDef: ValueFnDef<OutputName, OutputZodSchema>): ValueFn<OutputZodSchema, OutputName>;
11
+ getRequest(llmRequestId: string): Promise<GetRequestResult>;
12
+ }
@@ -0,0 +1,6 @@
1
+ import { NuabaseAPIClient } from '../../lib/api-client';
2
+ import { NuabaseError } from '../../lib/error-response';
3
+ import { ApiResponseLlmRequest, NuaInternalError } from './types';
4
+ export type GetRequestResult = ApiResponseLlmRequest | NuaInternalError | NuabaseError;
5
+ export declare function getRequest(client: NuabaseAPIClient, llmRequestId: string): Promise<GetRequestResult>;
6
+ export type { ApiResponseLlmRequest, LlmRequestStatus, NuaInternalError, SseRequestStatus, WebhookRequestStatus, } from './types';
@@ -0,0 +1,34 @@
1
+ export type LlmRequestType = string;
2
+ export type LlmRequestStatus = 'pending' | 'processing' | 'success' | 'failed';
3
+ export type SseRequestStatus = 'n/a' | 'pending' | 'sent' | 'failed';
4
+ export type WebhookRequestStatus = 'n/a' | 'pending' | 'sent' | 'failed';
5
+ export type NuaInternalError = {
6
+ kind: 'internal-error';
7
+ message: string;
8
+ };
9
+ export type ApiResponseLlmRequest = {
10
+ id: string;
11
+ requestType: LlmRequestType;
12
+ llmStatus: LlmRequestStatus;
13
+ sseStatus: SseRequestStatus;
14
+ webhookStatus: WebhookRequestStatus;
15
+ input: {
16
+ prompt: string | null;
17
+ data: string | null;
18
+ primaryKey: string | null;
19
+ };
20
+ output: {
21
+ name: string;
22
+ schema: string;
23
+ effectiveSchema: string;
24
+ };
25
+ result?: Record<string, unknown> | null;
26
+ error: string | null;
27
+ fullPrompt: string;
28
+ model: string;
29
+ provider: string;
30
+ startedAt: Date | null;
31
+ finishedAt: Date | null;
32
+ createdAt: Date;
33
+ updatedAt: Date;
34
+ };
@@ -0,0 +1,5 @@
1
+ export { Nua } from './core';
2
+ export { createArrayFn } from './cast_array/factory';
3
+ export { validateArrayRequestParams } from './cast_array/request-validation';
4
+ export type { ArrayFn, ArrayFnDef } from './cast_array/types';
5
+ export type { ApiResponseLlmRequest, GetRequestResult, LlmRequestStatus, NuaInternalError, SseRequestStatus, WebhookRequestStatus, } from './get-request';
@@ -1,33 +1,3 @@
1
- import { NuabaseAPIClient, NuabaseConfig } from './lib/api-client';
2
- import { NuabaseError } from './lib/error-response';
3
- import { z } from 'zod';
4
- type MappingFnDef<OutputName extends string, OutputZodSchema extends z.ZodTypeAny> = {
5
- name: OutputName;
6
- prompt: string;
7
- output: OutputZodSchema;
8
- };
9
- type MapResponseRecord<OutputZodSchema extends z.ZodTypeAny, OutputName extends string, InputRecord extends Record<string, unknown>, PrimaryKeyName extends keyof InputRecord & string> = {
10
- [Key in PrimaryKeyName | OutputName]: Key extends PrimaryKeyName ? InputRecord[PrimaryKeyName] : z.infer<OutputZodSchema>;
11
- };
12
- type MapSuccessDataResult<OutputZodSchema extends z.ZodTypeAny, OutputName extends string, InputRecord extends Record<string, unknown>, PrimaryKeyName extends keyof InputRecord & string> = Array<MapResponseRecord<OutputZodSchema, OutputName, InputRecord, PrimaryKeyName>>;
13
- type MapRequestApiResponse_Success<OutputZodSchema extends z.ZodTypeAny, OutputName extends string, InputRecord extends Record<string, unknown>, PrimaryKeyName extends keyof InputRecord & string> = {
14
- data: MapSuccessDataResult<OutputZodSchema, OutputName, InputRecord, PrimaryKeyName>;
15
- cacheHits: number;
16
- llmRequestId: string;
17
- kind: 'map';
18
- rowsWithNoResults: string[];
19
- isSuccess: true;
20
- isError?: false;
21
- };
22
- type MapResult<OutputZodSchema extends z.ZodTypeAny, OutputName extends string, InputRecord extends Record<string, unknown>, PrimaryKeyName extends keyof InputRecord & string> = NuabaseError | MapRequestApiResponse_Success<OutputZodSchema, OutputName, InputRecord, PrimaryKeyName>;
23
- type MapInvoker<OutputZodSchema extends z.ZodTypeAny, OutputName extends string> = {
24
- <InputRecord extends Record<string, unknown>, PrimaryKeyName extends keyof InputRecord & string>(data: InputRecord[], primaryKeyName: PrimaryKeyName): Promise<MapResult<OutputZodSchema, OutputName, InputRecord, PrimaryKeyName>>;
25
- now: <InputRecord extends Record<string, unknown>, PrimaryKeyName extends keyof InputRecord & string>(data: InputRecord[], primaryKeyName: PrimaryKeyName) => Promise<MapResult<OutputZodSchema, OutputName, InputRecord, PrimaryKeyName>>;
26
- };
27
- export declare class Nua {
28
- apiClient: NuabaseAPIClient;
29
- constructor(config?: NuabaseConfig);
30
- static validateMapRequestParams(data: unknown, primaryKeyName: unknown): void;
31
- map<OutputName extends string, OutputZodSchema extends z.ZodTypeAny>(fnDef: MappingFnDef<OutputName, OutputZodSchema>): MapInvoker<OutputZodSchema, OutputName>;
32
- }
33
- export {};
1
+ export { Nua } from './nua/index';
2
+ export { createArrayFn, validateArrayRequestParams } from './nua/index';
3
+ export type { ArrayFn, ArrayFnDef } from './nua/index';
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "nuabase",
3
3
  "description": "Nuabase TypeScript client to create typed functions from LLM prompts",
4
4
  "packageManager": "yarn@4.10.3+sha512.c38cafb5c7bb273f3926d04e55e1d8c9dfa7d9c3ea1f36a4868fa028b9e5f72298f0b7f401ad5eb921749eb012eb1c3bb74bf7503df3ee43fd600d14a018266f",
5
- "version": "1.0.0",
5
+ "version": "1.1.0",
6
6
  "type": "module",
7
7
  "author": "Jasim A Basheer <jasim@nuabase.com>",
8
8
  "engines": {
@@ -69,6 +69,7 @@
69
69
  "chalk": "^5.6.2",
70
70
  "esbuild": "^0.25.9",
71
71
  "eslint": "^9.35.0",
72
+ "eventsource": "^4.0.0",
72
73
  "fs-extra": "^11.3.1",
73
74
  "jest": "^30.1.3",
74
75
  "knip": "^5.63.1",