@newhomestar/sdk 0.4.5 → 0.4.6
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/dist/index.d.ts +14 -53
- package/dist/index.js +36 -208
- package/package.json +45 -45
- package/dist/parseSpec.d.ts +0 -76
- package/dist/parseSpec.js +0 -101
- package/dist/workerSchema.d.ts +0 -46
- package/dist/workerSchema.js +0 -38
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
import { z,
|
|
2
|
-
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
1
|
+
import { z, ZodTypeAny } from "zod";
|
|
3
2
|
export interface ActionDef<I extends ZodTypeAny, O extends ZodTypeAny> {
|
|
4
3
|
name: string;
|
|
5
4
|
input: I;
|
|
6
5
|
output: O;
|
|
7
6
|
handler: (input: z.infer<I>, ctx: ActionCtx) => Promise<z.infer<O>>;
|
|
8
|
-
fga?: {
|
|
9
|
-
resourceType: string;
|
|
10
|
-
relation: string;
|
|
11
|
-
resourceIdKey?: string;
|
|
12
|
-
policy?: string;
|
|
13
|
-
};
|
|
14
|
-
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
15
|
-
path?: string;
|
|
16
7
|
}
|
|
17
8
|
export interface ActionCtx {
|
|
18
9
|
jobId: string;
|
|
@@ -22,58 +13,28 @@ export declare function action<I extends ZodTypeAny, O extends ZodTypeAny>(cfg:
|
|
|
22
13
|
name?: string;
|
|
23
14
|
input: I;
|
|
24
15
|
output: O;
|
|
25
|
-
fga?: {
|
|
26
|
-
resourceType: string;
|
|
27
|
-
relation: string;
|
|
28
|
-
resourceIdKey?: string;
|
|
29
|
-
policy?: string;
|
|
30
|
-
};
|
|
31
|
-
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
32
|
-
path?: string;
|
|
33
16
|
handler: (input: z.infer<I>, ctx: ActionCtx) => Promise<z.infer<O>>;
|
|
34
17
|
}): ActionDef<I, O>;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
18
|
+
export interface WorkerDef {
|
|
19
|
+
name: string;
|
|
20
|
+
queue: string;
|
|
21
|
+
actions: Record<string, ActionDef<any, any>>;
|
|
22
|
+
envSpec?: {
|
|
23
|
+
name: string;
|
|
24
|
+
secret: boolean;
|
|
25
|
+
default?: string;
|
|
26
|
+
}[];
|
|
27
|
+
}
|
|
44
28
|
export declare function defineWorker<T extends WorkerDef>(def: T): T;
|
|
45
29
|
/** Enqueue an async action and receive `{ job_id }` */
|
|
46
30
|
export declare function enqueue<P extends object>(actionPath: `${string}.${string}`, payload: P): Promise<{
|
|
47
31
|
job_id: string;
|
|
48
32
|
}>;
|
|
49
|
-
/**
|
|
50
|
-
* Create an oRPC router from a WorkerDef, mapping each action to an oRPC procedure
|
|
51
|
-
*/
|
|
52
|
-
export declare function createORPCRouter<T extends WorkerDef>(def: T): Record<string, any>;
|
|
53
|
-
export interface ORPCServerOptions {
|
|
54
|
-
port?: number;
|
|
55
|
-
plugins?: any[];
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Run an official oRPC server - fully compliant with oRPC standards!
|
|
59
|
-
*/
|
|
60
|
-
export declare function runORPCServer<T extends WorkerDef>(def: T, options?: ORPCServerOptions): import("http").Server<typeof IncomingMessage, typeof ServerResponse>;
|
|
61
33
|
export declare function runWorker(def: WorkerDef): Promise<void>;
|
|
62
|
-
export
|
|
63
|
-
openapi: string;
|
|
64
|
-
info: {
|
|
65
|
-
title: string;
|
|
66
|
-
version: string;
|
|
67
|
-
description: string;
|
|
68
|
-
};
|
|
69
|
-
paths: any;
|
|
70
|
-
}>;
|
|
34
|
+
export type { ZodTypeAny as SchemaAny, ZodTypeAny };
|
|
71
35
|
/**
|
|
72
|
-
*
|
|
36
|
+
* Spin up an HTTP server exposing each action under POST /<worker>/<action>
|
|
73
37
|
*/
|
|
74
|
-
export declare function runHttpServer
|
|
38
|
+
export declare function runHttpServer(def: WorkerDef, opts?: {
|
|
75
39
|
port?: number;
|
|
76
40
|
}): void;
|
|
77
|
-
export type { ZodTypeAny as SchemaAny, ZodTypeAny };
|
|
78
|
-
export { parseNovaSpec } from "./parseSpec.js";
|
|
79
|
-
export type { NovaSpec } from "./parseSpec.js";
|
package/dist/index.js
CHANGED
|
@@ -1,54 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import { createServer } from "node:http";
|
|
14
|
-
// Full oRPC imports now working with ESM
|
|
15
|
-
import { os } from "@orpc/server";
|
|
16
|
-
import { RPCHandler } from "@orpc/server/node";
|
|
17
|
-
import { CORSPlugin } from "@orpc/server/plugins";
|
|
18
|
-
import { OpenAPIHandler } from "@orpc/openapi/fetch";
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.action = action;
|
|
7
|
+
exports.defineWorker = defineWorker;
|
|
8
|
+
exports.enqueue = enqueue;
|
|
9
|
+
exports.runWorker = runWorker;
|
|
10
|
+
exports.runHttpServer = runHttpServer;
|
|
11
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
12
|
+
const supabase_js_1 = require("@supabase/supabase-js");
|
|
19
13
|
if (!process.env.RUNTIME_SUPABASE_URL) {
|
|
20
14
|
// local dev – read .env.local
|
|
21
|
-
|
|
15
|
+
dotenv_1.default.config({ path: ".env.local", override: true });
|
|
22
16
|
}
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
name: cfg.name ?? "unnamed",
|
|
26
|
-
method: cfg.method ?? 'POST',
|
|
27
|
-
path: cfg.path,
|
|
28
|
-
...cfg
|
|
29
|
-
};
|
|
17
|
+
function action(cfg) {
|
|
18
|
+
return { name: cfg.name ?? "unnamed", ...cfg };
|
|
30
19
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Register a worker definition - SAME API as before
|
|
35
|
-
*/
|
|
36
|
-
export function defineWorker(def) {
|
|
37
|
-
// Runtime validation of the worker definition
|
|
38
|
-
WorkerDefSchema.parse(def);
|
|
39
|
-
return def;
|
|
20
|
+
function defineWorker(def) {
|
|
21
|
+
return def; // identity – type info retained for CLI
|
|
40
22
|
}
|
|
41
|
-
/*──────────────────────* Client‑side enqueue()
|
|
23
|
+
/*──────────────────────* Client‑side enqueue() *──────────────────*/
|
|
24
|
+
// CLIENT_SUPABASE_… vars exist in gateways / other services
|
|
42
25
|
const CLIENT_SUPABASE_URL = process.env.CLIENT_SUPABASE_PUBLIC_URL;
|
|
43
26
|
const CLIENT_SUPABASE_KEY = process.env.CLIENT_SUPABASE_SERVICE_ROLE_KEY;
|
|
44
27
|
let clientSupabase;
|
|
45
28
|
function getClient() {
|
|
46
29
|
if (!CLIENT_SUPABASE_URL || !CLIENT_SUPABASE_KEY)
|
|
47
30
|
throw new Error("CLIENT_SUPABASE_* env vars not set");
|
|
48
|
-
return (clientSupabase
|
|
31
|
+
return (clientSupabase ?? (clientSupabase = (0, supabase_js_1.createClient)(CLIENT_SUPABASE_URL, CLIENT_SUPABASE_KEY)));
|
|
49
32
|
}
|
|
50
33
|
/** Enqueue an async action and receive `{ job_id }` */
|
|
51
|
-
|
|
34
|
+
async function enqueue(actionPath, payload) {
|
|
52
35
|
const [pipeline, action] = actionPath.split(".");
|
|
53
36
|
const { data, error } = await getClient().rpc("nova_enqueue", {
|
|
54
37
|
pipeline_name: pipeline,
|
|
@@ -59,70 +42,13 @@ export async function enqueue(actionPath, payload) {
|
|
|
59
42
|
throw error;
|
|
60
43
|
return data;
|
|
61
44
|
}
|
|
62
|
-
/*────────────────
|
|
63
|
-
/**
|
|
64
|
-
* Create an oRPC router from a WorkerDef, mapping each action to an oRPC procedure
|
|
65
|
-
*/
|
|
66
|
-
export function createORPCRouter(def) {
|
|
67
|
-
const procedures = {};
|
|
68
|
-
for (const [actionName, actionDef] of Object.entries(def.actions)) {
|
|
69
|
-
// Create oRPC procedure - convert Nova action to oRPC procedure
|
|
70
|
-
const procedure = os
|
|
71
|
-
.input(actionDef.input)
|
|
72
|
-
.output(actionDef.output)
|
|
73
|
-
.handler(async ({ input, context }) => {
|
|
74
|
-
const ctx = {
|
|
75
|
-
jobId: context?.jobId || `orpc-${Date.now()}`,
|
|
76
|
-
progress: (percent, meta) => {
|
|
77
|
-
console.log(`[${actionName}] Progress: ${percent}%`, meta);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
return await actionDef.handler(input, ctx);
|
|
81
|
-
})
|
|
82
|
-
.callable(); // Make the procedure callable like a regular function
|
|
83
|
-
procedures[actionName] = procedure;
|
|
84
|
-
}
|
|
85
|
-
return procedures;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Run an official oRPC server - fully compliant with oRPC standards!
|
|
89
|
-
*/
|
|
90
|
-
export function runORPCServer(def, options = {}) {
|
|
91
|
-
const router = createORPCRouter(def);
|
|
92
|
-
console.log(`[nova] Starting official oRPC server "${def.name}"`);
|
|
93
|
-
// Use official oRPC serving pattern
|
|
94
|
-
const handler = new RPCHandler(router, {
|
|
95
|
-
plugins: [new CORSPlugin(), ...(options.plugins || [])]
|
|
96
|
-
});
|
|
97
|
-
const server = createServer(async (req, res) => {
|
|
98
|
-
const result = await handler.handle(req, res, {
|
|
99
|
-
context: {
|
|
100
|
-
headers: req.headers,
|
|
101
|
-
jobId: `orpc-${Date.now()}`,
|
|
102
|
-
// Add any other Nova-specific context
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
if (!result.matched) {
|
|
106
|
-
res.statusCode = 404;
|
|
107
|
-
res.end('No procedure matched');
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
const port = options.port ?? (process.env.PORT ? parseInt(process.env.PORT) : 8000);
|
|
111
|
-
server.listen(port, () => {
|
|
112
|
-
console.log(`[nova] Official oRPC server listening on http://localhost:${port}`);
|
|
113
|
-
console.log(`[nova] Available procedures: ${Object.keys(def.actions).join(', ')}`);
|
|
114
|
-
console.log(`[nova] 🔥 RPC protocol: ACTIVE (not HTTP fallback)`);
|
|
115
|
-
console.log(`[nova] 🌐 CORS: Enabled via CORSPlugin`);
|
|
116
|
-
});
|
|
117
|
-
return server;
|
|
118
|
-
}
|
|
119
|
-
/*──────────────── Runtime harness (Supabase RPC) - UNCHANGED ───────────────*/
|
|
45
|
+
/*──────────────── Runtime harness (Supabase RPC) ───────────────*/
|
|
120
46
|
const RUNTIME_SUPABASE_URL = process.env.RUNTIME_SUPABASE_URL;
|
|
121
47
|
const RUNTIME_SUPABASE_KEY = process.env.RUNTIME_SUPABASE_SERVICE_ROLE_KEY;
|
|
122
48
|
const runtime = RUNTIME_SUPABASE_URL && RUNTIME_SUPABASE_KEY
|
|
123
|
-
? createClient(RUNTIME_SUPABASE_URL, RUNTIME_SUPABASE_KEY)
|
|
49
|
+
? (0, supabase_js_1.createClient)(RUNTIME_SUPABASE_URL, RUNTIME_SUPABASE_KEY)
|
|
124
50
|
: undefined;
|
|
125
|
-
|
|
51
|
+
async function runWorker(def) {
|
|
126
52
|
if (!runtime)
|
|
127
53
|
throw new Error("RUNTIME_SUPABASE_* env vars not configured");
|
|
128
54
|
console.log(`[nova] worker '${def.name}' polling ${def.queue}`);
|
|
@@ -151,47 +77,6 @@ export async function runWorker(def) {
|
|
|
151
77
|
await nack(msg.msg_id, def.queue);
|
|
152
78
|
continue;
|
|
153
79
|
}
|
|
154
|
-
// FGA enforcement (unchanged from original)
|
|
155
|
-
const hints = act.fga ? (Array.isArray(act.fga) ? act.fga : [act.fga]) : [];
|
|
156
|
-
if (hints.length) {
|
|
157
|
-
const apiEndpoint = process.env.OPENFGA_API_ENDPOINT;
|
|
158
|
-
const storeId = process.env.OPENFGA_STORE_ID;
|
|
159
|
-
const authToken = process.env.OPENFGA_AUTH_TOKEN;
|
|
160
|
-
if (!apiEndpoint || !storeId || !authToken) {
|
|
161
|
-
throw new Error('Missing OPENFGA_API_ENDPOINT, OPENFGA_STORE_ID, or OPENFGA_AUTH_TOKEN for FGA enforcement');
|
|
162
|
-
}
|
|
163
|
-
const fgaClient = new OpenFgaClient({ apiUrl: apiEndpoint, storeId });
|
|
164
|
-
let authorized = true;
|
|
165
|
-
for (const hint of hints) {
|
|
166
|
-
const key = hint.resourceIdKey;
|
|
167
|
-
if (!key) {
|
|
168
|
-
throw new Error(`Missing resourceIdKey for FGA hint on action '${actName}'`);
|
|
169
|
-
}
|
|
170
|
-
const id = payload[key];
|
|
171
|
-
if (!id) {
|
|
172
|
-
throw new Error(`Payload field '${key}' required for FGA hint on action '${actName}'`);
|
|
173
|
-
}
|
|
174
|
-
const tupleKey = {
|
|
175
|
-
user: `user:${user_id}`,
|
|
176
|
-
relation: hint.relation,
|
|
177
|
-
object: `${hint.resourceType}:${id}`
|
|
178
|
-
};
|
|
179
|
-
const result = await fgaClient.check({
|
|
180
|
-
user: tupleKey.user,
|
|
181
|
-
relation: tupleKey.relation,
|
|
182
|
-
object: tupleKey.object
|
|
183
|
-
});
|
|
184
|
-
if (!result.allowed) {
|
|
185
|
-
authorized = false;
|
|
186
|
-
break;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
if (!authorized) {
|
|
190
|
-
await runtime.from("jobs").update({ status: "failed", error: "Unauthorized" }).eq("id", jobId);
|
|
191
|
-
await ack(msg.msg_id, def.queue);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
80
|
try {
|
|
196
81
|
const parsedInput = act.input.parse(payload);
|
|
197
82
|
const ctx = {
|
|
@@ -218,64 +103,20 @@ async function nack(id, q) {
|
|
|
218
103
|
await runtime.schema("pgmq_public").rpc("nack", { queue_name: q, message_id: id });
|
|
219
104
|
}
|
|
220
105
|
function delay(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// For now, return a basic spec structure
|
|
225
|
-
return {
|
|
226
|
-
openapi: "3.0.0",
|
|
227
|
-
info: {
|
|
228
|
-
title: `${def.name} API`,
|
|
229
|
-
version: "1.0.0",
|
|
230
|
-
description: `OpenAPI specification for Nova worker: ${def.name}`
|
|
231
|
-
},
|
|
232
|
-
paths: Object.entries(def.actions).reduce((paths, [actionName, actionDef]) => {
|
|
233
|
-
const method = (actionDef.method || 'POST').toLowerCase();
|
|
234
|
-
const path = actionDef.path || `/${actionName}`;
|
|
235
|
-
paths[path] = {
|
|
236
|
-
[method]: {
|
|
237
|
-
operationId: actionName,
|
|
238
|
-
summary: `Execute ${actionName} action`,
|
|
239
|
-
requestBody: {
|
|
240
|
-
content: {
|
|
241
|
-
'application/json': {
|
|
242
|
-
schema: { type: 'object' } // Would be generated from Zod schema
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
responses: {
|
|
247
|
-
'200': {
|
|
248
|
-
description: 'Success',
|
|
249
|
-
content: {
|
|
250
|
-
'application/json': {
|
|
251
|
-
schema: { type: 'object' } // Would be generated from Zod schema
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
return paths;
|
|
259
|
-
}, {})
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
/*──────────────── HTTP Server Harness (Enhanced) ───────────────*/
|
|
263
|
-
import express from "express";
|
|
264
|
-
import bodyParser from "body-parser";
|
|
106
|
+
//──────────────────── HTTP Server Harness ─────────────────────
|
|
107
|
+
const express_1 = __importDefault(require("express"));
|
|
108
|
+
const body_parser_1 = __importDefault(require("body-parser"));
|
|
265
109
|
/**
|
|
266
|
-
*
|
|
110
|
+
* Spin up an HTTP server exposing each action under POST /<worker>/<action>
|
|
267
111
|
*/
|
|
268
|
-
|
|
269
|
-
const app =
|
|
270
|
-
app.use(
|
|
112
|
+
function runHttpServer(def, opts = {}) {
|
|
113
|
+
const app = (0, express_1.default)();
|
|
114
|
+
app.use(body_parser_1.default.json());
|
|
271
115
|
for (const [actionName, act] of Object.entries(def.actions)) {
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
// unified handler: parse JSON body or default to empty object
|
|
275
|
-
const handler = async (req, res) => {
|
|
116
|
+
const route = `/${def.name}/${actionName}`;
|
|
117
|
+
app.post(route, async (req, res) => {
|
|
276
118
|
try {
|
|
277
|
-
const
|
|
278
|
-
const input = act.input.parse(payload);
|
|
119
|
+
const input = act.input.parse(req.body);
|
|
279
120
|
const ctx = { jobId: `http-${Date.now()}`, progress: () => { } };
|
|
280
121
|
const out = await act.handler(input, ctx);
|
|
281
122
|
act.output.parse(out);
|
|
@@ -284,23 +125,10 @@ export function runHttpServer(def, opts = {}) {
|
|
|
284
125
|
catch (err) {
|
|
285
126
|
res.status(400).json({ error: err.message });
|
|
286
127
|
}
|
|
287
|
-
};
|
|
288
|
-
// Register route with specified HTTP method
|
|
289
|
-
app[method](route, handler);
|
|
290
|
-
// Special case: expose health checks under GET /health for liveness checks
|
|
291
|
-
if (actionName === 'health' && method !== 'get') {
|
|
292
|
-
app.get('/health', handler);
|
|
293
|
-
}
|
|
128
|
+
});
|
|
294
129
|
}
|
|
295
|
-
const port = opts.port ?? (process.env.PORT ? parseInt(process.env.PORT) :
|
|
130
|
+
const port = opts.port ?? (process.env.PORT ? parseInt(process.env.PORT) : 3000);
|
|
296
131
|
app.listen(port, () => {
|
|
297
132
|
console.log(`[nova] HTTP server listening on http://localhost:${port}`);
|
|
298
|
-
Object.entries(def.actions).forEach(([actionName, actionDef]) => {
|
|
299
|
-
const method = (actionDef.method || 'POST').toUpperCase();
|
|
300
|
-
const path = actionDef.path || `/${def.name}/${actionName}`;
|
|
301
|
-
console.log(`[nova] ${method} ${path} -> ${actionName}`);
|
|
302
|
-
});
|
|
303
133
|
});
|
|
304
134
|
}
|
|
305
|
-
// YAML spec parsing utility
|
|
306
|
-
export { parseNovaSpec } from "./parseSpec.js";
|
package/package.json
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@newhomestar/sdk",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "Type-safe SDK for building Nova pipelines (workers & functions)",
|
|
5
|
-
"homepage": "https://github.com/newhomestar/nova-node-sdk#readme",
|
|
6
|
-
"bugs": {
|
|
7
|
-
"url": "https://github.com/newhomestar/nova-node-sdk/issues"
|
|
8
|
-
},
|
|
9
|
-
"repository": {
|
|
10
|
-
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/newhomestar/nova-node-sdk.git"
|
|
12
|
-
},
|
|
13
|
-
"license": "ISC",
|
|
14
|
-
"author": "Christian Gomez",
|
|
15
|
-
"type": "module",
|
|
16
|
-
"main": "dist/index.js",
|
|
17
|
-
"types": "dist/index.d.ts",
|
|
18
|
-
"exports": {
|
|
19
|
-
".": {
|
|
20
|
-
"import": "./dist/index.js",
|
|
21
|
-
"types": "./dist/index.d.ts"
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
"files": [
|
|
25
|
-
"dist"
|
|
26
|
-
],
|
|
27
|
-
"scripts": {
|
|
28
|
-
"build": "tsc"
|
|
29
|
-
},
|
|
30
|
-
"dependencies": {
|
|
31
|
-
"@openfga/sdk": "^0.9.0",
|
|
32
|
-
"@orpc/openapi": "1.7.4",
|
|
33
|
-
"@orpc/server": "1.7.4",
|
|
34
|
-
"@supabase/supabase-js": "^2.39.0",
|
|
35
|
-
"body-parser": "^1.20.2",
|
|
36
|
-
"dotenv": "^16.4.3",
|
|
37
|
-
"express": "^4.18.2",
|
|
38
|
-
"yaml": "^2.7.1",
|
|
39
|
-
"zod": "^4.0.5"
|
|
40
|
-
},
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@types/node": "^20.11.17",
|
|
43
|
-
"typescript": "^5.4.4"
|
|
44
|
-
}
|
|
45
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@newhomestar/sdk",
|
|
3
|
+
"version": "0.4.6",
|
|
4
|
+
"description": "Type-safe SDK for building Nova pipelines (workers & functions)",
|
|
5
|
+
"homepage": "https://github.com/newhomestar/nova-node-sdk#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/newhomestar/nova-node-sdk/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/newhomestar/nova-node-sdk.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"author": "Christian Gomez",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "dist/index.js",
|
|
17
|
+
"types": "dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"import": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@openfga/sdk": "^0.9.0",
|
|
32
|
+
"@orpc/openapi": "1.7.4",
|
|
33
|
+
"@orpc/server": "1.7.4",
|
|
34
|
+
"@supabase/supabase-js": "^2.39.0",
|
|
35
|
+
"body-parser": "^1.20.2",
|
|
36
|
+
"dotenv": "^16.4.3",
|
|
37
|
+
"express": "^4.18.2",
|
|
38
|
+
"yaml": "^2.7.1",
|
|
39
|
+
"zod": "^4.0.5"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^20.11.17",
|
|
43
|
+
"typescript": "^5.4.4"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/dist/parseSpec.d.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
export declare const NovaSpecSchema: z.ZodObject<{
|
|
3
|
-
apiVersion: z.ZodString;
|
|
4
|
-
kind: z.ZodString;
|
|
5
|
-
metadata: z.ZodObject<{
|
|
6
|
-
name: z.ZodString;
|
|
7
|
-
displayName: z.ZodOptional<z.ZodString>;
|
|
8
|
-
description: z.ZodOptional<z.ZodString>;
|
|
9
|
-
icon: z.ZodOptional<z.ZodString>;
|
|
10
|
-
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
11
|
-
}, z.core.$strip>;
|
|
12
|
-
spec: z.ZodObject<{
|
|
13
|
-
runtime: z.ZodObject<{
|
|
14
|
-
type: z.ZodString;
|
|
15
|
-
image: z.ZodString;
|
|
16
|
-
resources: z.ZodObject<{
|
|
17
|
-
cpu: z.ZodString;
|
|
18
|
-
memory: z.ZodString;
|
|
19
|
-
}, z.core.$strip>;
|
|
20
|
-
command: z.ZodArray<z.ZodString>;
|
|
21
|
-
queue: z.ZodString;
|
|
22
|
-
port: z.ZodNumber;
|
|
23
|
-
envSpec: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
24
|
-
name: z.ZodString;
|
|
25
|
-
value: z.ZodOptional<z.ZodString>;
|
|
26
|
-
secret: z.ZodOptional<z.ZodBoolean>;
|
|
27
|
-
default: z.ZodOptional<z.ZodString>;
|
|
28
|
-
}, z.core.$strip>>>;
|
|
29
|
-
}, z.core.$strip>;
|
|
30
|
-
actions: z.ZodArray<z.ZodObject<{
|
|
31
|
-
name: z.ZodString;
|
|
32
|
-
displayName: z.ZodOptional<z.ZodString>;
|
|
33
|
-
description: z.ZodOptional<z.ZodString>;
|
|
34
|
-
icon: z.ZodOptional<z.ZodString>;
|
|
35
|
-
async: z.ZodOptional<z.ZodBoolean>;
|
|
36
|
-
input: z.ZodOptional<z.ZodUnknown>;
|
|
37
|
-
output: z.ZodOptional<z.ZodUnknown>;
|
|
38
|
-
schema: z.ZodOptional<z.ZodObject<{
|
|
39
|
-
input: z.ZodString;
|
|
40
|
-
output: z.ZodString;
|
|
41
|
-
}, z.core.$strip>>;
|
|
42
|
-
fga: z.ZodOptional<z.ZodObject<{
|
|
43
|
-
resourceType: z.ZodString;
|
|
44
|
-
relation: z.ZodString;
|
|
45
|
-
}, z.core.$strip>>;
|
|
46
|
-
}, z.core.$strip>>;
|
|
47
|
-
}, z.core.$strip>;
|
|
48
|
-
build: z.ZodOptional<z.ZodObject<{
|
|
49
|
-
dockerfile: z.ZodString;
|
|
50
|
-
context: z.ZodString;
|
|
51
|
-
}, z.core.$strip>>;
|
|
52
|
-
ui: z.ZodOptional<z.ZodObject<{
|
|
53
|
-
category: z.ZodOptional<z.ZodString>;
|
|
54
|
-
color: z.ZodOptional<z.ZodString>;
|
|
55
|
-
}, z.core.$strip>>;
|
|
56
|
-
fga: z.ZodOptional<z.ZodObject<{
|
|
57
|
-
types: z.ZodArray<z.ZodObject<{
|
|
58
|
-
name: z.ZodString;
|
|
59
|
-
relations: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodObject<{
|
|
60
|
-
computedUserset: z.ZodObject<{
|
|
61
|
-
object: z.ZodString;
|
|
62
|
-
relation: z.ZodString;
|
|
63
|
-
}, z.core.$strip>;
|
|
64
|
-
}, z.core.$strip>]>>;
|
|
65
|
-
}, z.core.$strip>>;
|
|
66
|
-
}, z.core.$strip>>;
|
|
67
|
-
}, z.core.$strict>;
|
|
68
|
-
export type NovaSpec = z.infer<typeof NovaSpecSchema>;
|
|
69
|
-
/**
|
|
70
|
-
* Parse nova.yaml content and validate against the NovaSpec schema.
|
|
71
|
-
* Unknown fields are stripped; missing required fields throw an error.
|
|
72
|
-
* @param yamlContent String contents of a nova.yaml specification
|
|
73
|
-
* @returns Parsed NovaSpec object
|
|
74
|
-
* @throws Error with validation details if parsing or validation fail
|
|
75
|
-
*/
|
|
76
|
-
export declare function parseNovaSpec(yamlContent: string): NovaSpec;
|
package/dist/parseSpec.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { parse as parseYAML } from "yaml";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
// Zod schema for nova.yaml
|
|
4
|
-
export const NovaSpecSchema = z.object({
|
|
5
|
-
apiVersion: z.string(),
|
|
6
|
-
kind: z.string(),
|
|
7
|
-
metadata: z.object({
|
|
8
|
-
name: z.string(),
|
|
9
|
-
displayName: z.string().optional(),
|
|
10
|
-
description: z.string().optional(),
|
|
11
|
-
icon: z.string().optional(),
|
|
12
|
-
tags: z.array(z.string()).optional(),
|
|
13
|
-
}),
|
|
14
|
-
spec: z.object({
|
|
15
|
-
runtime: z.object({
|
|
16
|
-
type: z.string(),
|
|
17
|
-
image: z.string(),
|
|
18
|
-
resources: z.object({
|
|
19
|
-
cpu: z.string(),
|
|
20
|
-
memory: z.string(),
|
|
21
|
-
}),
|
|
22
|
-
command: z.array(z.string()),
|
|
23
|
-
queue: z.string(),
|
|
24
|
-
port: z.number(),
|
|
25
|
-
envSpec: z.array(z.object({
|
|
26
|
-
name: z.string(),
|
|
27
|
-
value: z.string().optional(),
|
|
28
|
-
secret: z.boolean().optional(),
|
|
29
|
-
default: z.string().optional(),
|
|
30
|
-
})).optional(),
|
|
31
|
-
}),
|
|
32
|
-
actions: z.array(z.object({
|
|
33
|
-
name: z.string(),
|
|
34
|
-
displayName: z.string().optional(),
|
|
35
|
-
description: z.string().optional(),
|
|
36
|
-
icon: z.string().optional(),
|
|
37
|
-
async: z.boolean().optional(),
|
|
38
|
-
input: z.unknown().optional(), // JSON schema object
|
|
39
|
-
output: z.unknown().optional(), // JSON schema object
|
|
40
|
-
schema: z.object({
|
|
41
|
-
input: z.string(),
|
|
42
|
-
output: z.string(),
|
|
43
|
-
}).optional(),
|
|
44
|
-
fga: z.object({
|
|
45
|
-
resourceType: z.string(),
|
|
46
|
-
relation: z.string(),
|
|
47
|
-
}).optional(),
|
|
48
|
-
})),
|
|
49
|
-
}),
|
|
50
|
-
build: z.object({
|
|
51
|
-
dockerfile: z.string(),
|
|
52
|
-
context: z.string(),
|
|
53
|
-
}).optional(),
|
|
54
|
-
ui: z.object({
|
|
55
|
-
category: z.string().optional(),
|
|
56
|
-
color: z.string().optional(),
|
|
57
|
-
}).optional(),
|
|
58
|
-
// OpenFGA policy hints embedded in nova.yaml
|
|
59
|
-
fga: z.object({
|
|
60
|
-
types: z.array(z.object({
|
|
61
|
-
name: z.string(),
|
|
62
|
-
relations: z.record(z.string(), z.union([
|
|
63
|
-
z.array(z.string()),
|
|
64
|
-
z.object({
|
|
65
|
-
computedUserset: z.object({
|
|
66
|
-
object: z.string(),
|
|
67
|
-
relation: z.string(),
|
|
68
|
-
}),
|
|
69
|
-
}),
|
|
70
|
-
])),
|
|
71
|
-
})),
|
|
72
|
-
}).optional(),
|
|
73
|
-
}).strict();
|
|
74
|
-
/**
|
|
75
|
-
* Parse nova.yaml content and validate against the NovaSpec schema.
|
|
76
|
-
* Unknown fields are stripped; missing required fields throw an error.
|
|
77
|
-
* @param yamlContent String contents of a nova.yaml specification
|
|
78
|
-
* @returns Parsed NovaSpec object
|
|
79
|
-
* @throws Error with validation details if parsing or validation fail
|
|
80
|
-
*/
|
|
81
|
-
export function parseNovaSpec(yamlContent) {
|
|
82
|
-
let parsed;
|
|
83
|
-
try {
|
|
84
|
-
parsed = parseYAML(yamlContent);
|
|
85
|
-
}
|
|
86
|
-
catch (e) {
|
|
87
|
-
throw new Error(`Failed to parse YAML content: ${e.message}`);
|
|
88
|
-
}
|
|
89
|
-
try {
|
|
90
|
-
return NovaSpecSchema.parse(parsed);
|
|
91
|
-
}
|
|
92
|
-
catch (e) {
|
|
93
|
-
if (e instanceof z.ZodError) {
|
|
94
|
-
const details = e.issues
|
|
95
|
-
.map((issue) => `Path '${issue.path.join(".")}': ${issue.message}`)
|
|
96
|
-
.join("; ");
|
|
97
|
-
throw new Error(`nova.yaml validation error(s): ${details}`);
|
|
98
|
-
}
|
|
99
|
-
throw e;
|
|
100
|
-
}
|
|
101
|
-
}
|
package/dist/workerSchema.d.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
/**
|
|
3
|
-
* Schema for code-level defineWorker() argument.
|
|
4
|
-
* This enforces worker metadata, actions, envSpec, and optional FGA hints.
|
|
5
|
-
*/
|
|
6
|
-
export declare const WorkerDefSchema: z.ZodObject<{
|
|
7
|
-
name: z.ZodString;
|
|
8
|
-
queue: z.ZodString;
|
|
9
|
-
envSpec: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
10
|
-
name: z.ZodString;
|
|
11
|
-
secret: z.ZodBoolean;
|
|
12
|
-
default: z.ZodOptional<z.ZodString>;
|
|
13
|
-
}, z.core.$strip>>>;
|
|
14
|
-
fga: z.ZodOptional<z.ZodObject<{
|
|
15
|
-
types: z.ZodArray<z.ZodObject<{
|
|
16
|
-
name: z.ZodString;
|
|
17
|
-
relations: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodObject<{
|
|
18
|
-
computedUserset: z.ZodObject<{
|
|
19
|
-
object: z.ZodString;
|
|
20
|
-
relation: z.ZodString;
|
|
21
|
-
}, z.core.$strip>;
|
|
22
|
-
}, z.core.$strip>]>>;
|
|
23
|
-
}, z.core.$strip>>;
|
|
24
|
-
}, z.core.$strip>>;
|
|
25
|
-
actions: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
26
|
-
name: z.ZodOptional<z.ZodString>;
|
|
27
|
-
input: z.ZodAny;
|
|
28
|
-
output: z.ZodAny;
|
|
29
|
-
method: z.ZodOptional<z.ZodEnum<{
|
|
30
|
-
GET: "GET";
|
|
31
|
-
POST: "POST";
|
|
32
|
-
PUT: "PUT";
|
|
33
|
-
DELETE: "DELETE";
|
|
34
|
-
PATCH: "PATCH";
|
|
35
|
-
}>>;
|
|
36
|
-
path: z.ZodOptional<z.ZodString>;
|
|
37
|
-
fga: z.ZodOptional<z.ZodObject<{
|
|
38
|
-
resourceType: z.ZodString;
|
|
39
|
-
relation: z.ZodString;
|
|
40
|
-
resourceIdKey: z.ZodOptional<z.ZodString>;
|
|
41
|
-
policy: z.ZodOptional<z.ZodString>;
|
|
42
|
-
}, z.core.$strip>>;
|
|
43
|
-
handler: z.ZodAny;
|
|
44
|
-
}, z.core.$strip>>;
|
|
45
|
-
}, z.core.$strip>;
|
|
46
|
-
export type WorkerDef = z.infer<typeof WorkerDefSchema>;
|
package/dist/workerSchema.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
/**
|
|
3
|
-
* Schema for code-level defineWorker() argument.
|
|
4
|
-
* This enforces worker metadata, actions, envSpec, and optional FGA hints.
|
|
5
|
-
*/
|
|
6
|
-
export const WorkerDefSchema = z.object({
|
|
7
|
-
name: z.string(),
|
|
8
|
-
queue: z.string(),
|
|
9
|
-
// Optional environment variable spec for nova.yaml generation
|
|
10
|
-
envSpec: z.array(z.object({ name: z.string(), secret: z.boolean(), default: z.string().optional() })).optional(),
|
|
11
|
-
// Optional top-level FGA policy types
|
|
12
|
-
fga: z.object({
|
|
13
|
-
types: z.array(z.object({
|
|
14
|
-
name: z.string(),
|
|
15
|
-
relations: z.record(z.string(), z.union([
|
|
16
|
-
z.array(z.string()),
|
|
17
|
-
z.object({ computedUserset: z.object({ object: z.string(), relation: z.string() }) })
|
|
18
|
-
])),
|
|
19
|
-
}))
|
|
20
|
-
}).optional(),
|
|
21
|
-
// Map of action definitions
|
|
22
|
-
actions: z.record(z.string(), z.object({
|
|
23
|
-
name: z.string().optional(),
|
|
24
|
-
input: z.any(), // Zod schema instance expected
|
|
25
|
-
output: z.any(), // Zod schema instance expected
|
|
26
|
-
// NEW: HTTP routing support for oRPC
|
|
27
|
-
method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']).optional(),
|
|
28
|
-
path: z.string().optional(),
|
|
29
|
-
// Optional per-action OpenFGA hints: resource type, relation, ID key, and optional policy caveat
|
|
30
|
-
fga: z.object({
|
|
31
|
-
resourceType: z.string(),
|
|
32
|
-
relation: z.string(),
|
|
33
|
-
resourceIdKey: z.string().optional(),
|
|
34
|
-
policy: z.string().optional(),
|
|
35
|
-
}).optional(),
|
|
36
|
-
handler: z.any(), // function with signature (input, ctx) => Promise<…>
|
|
37
|
-
})),
|
|
38
|
-
});
|