helloruntime 0.0.1
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/cli/config.d.ts +17 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +22 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/deploy.d.ts +4 -0
- package/dist/cli/deploy.d.ts.map +1 -0
- package/dist/cli/deploy.js +69 -0
- package/dist/cli/deploy.js.map +1 -0
- package/dist/cli/destroy.d.ts +5 -0
- package/dist/cli/destroy.d.ts.map +1 -0
- package/dist/cli/destroy.js +41 -0
- package/dist/cli/destroy.js.map +1 -0
- package/dist/cli/http.d.ts +3 -0
- package/dist/cli/http.d.ts.map +1 -0
- package/dist/cli/http.js +26 -0
- package/dist/cli/http.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +76 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/packager.d.ts +7 -0
- package/dist/cli/packager.d.ts.map +1 -0
- package/dist/cli/packager.js +49 -0
- package/dist/cli/packager.js.map +1 -0
- package/dist/cli/project-config.d.ts +3 -0
- package/dist/cli/project-config.d.ts.map +1 -0
- package/dist/cli/project-config.js +20 -0
- package/dist/cli/project-config.js.map +1 -0
- package/dist/cli/triggers.d.ts +15 -0
- package/dist/cli/triggers.d.ts.map +1 -0
- package/dist/cli/triggers.js +68 -0
- package/dist/cli/triggers.js.map +1 -0
- package/dist/sdk/cron.d.ts +12 -0
- package/dist/sdk/cron.d.ts.map +1 -0
- package/dist/sdk/cron.js +10 -0
- package/dist/sdk/cron.js.map +1 -0
- package/dist/sdk/db.d.ts +14 -0
- package/dist/sdk/db.d.ts.map +1 -0
- package/dist/sdk/db.js +44 -0
- package/dist/sdk/db.js.map +1 -0
- package/dist/sdk/index.d.ts +10 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +11 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/jobs.d.ts +12 -0
- package/dist/sdk/jobs.d.ts.map +1 -0
- package/dist/sdk/jobs.js +11 -0
- package/dist/sdk/jobs.js.map +1 -0
- package/dist/sdk/registry.d.ts +2 -0
- package/dist/sdk/registry.d.ts.map +1 -0
- package/dist/sdk/registry.js +5 -0
- package/dist/sdk/registry.js.map +1 -0
- package/dist/sdk/runtime/express.d.ts +6 -0
- package/dist/sdk/runtime/express.d.ts.map +1 -0
- package/dist/sdk/runtime/express.js +56 -0
- package/dist/sdk/runtime/express.js.map +1 -0
- package/dist/sdk/runtime/handler.d.ts +13 -0
- package/dist/sdk/runtime/handler.d.ts.map +1 -0
- package/dist/sdk/runtime/handler.js +85 -0
- package/dist/sdk/runtime/handler.js.map +1 -0
- package/dist/sdk/runtime/hono.d.ts +10 -0
- package/dist/sdk/runtime/hono.d.ts.map +1 -0
- package/dist/sdk/runtime/hono.js +16 -0
- package/dist/sdk/runtime/hono.js.map +1 -0
- package/dist/sdk/runtime/register.d.ts +9 -0
- package/dist/sdk/runtime/register.d.ts.map +1 -0
- package/dist/sdk/runtime/register.js +32 -0
- package/dist/sdk/runtime/register.js.map +1 -0
- package/dist/sdk/runtime/verify.d.ts +20 -0
- package/dist/sdk/runtime/verify.d.ts.map +1 -0
- package/dist/sdk/runtime/verify.js +29 -0
- package/dist/sdk/runtime/verify.js.map +1 -0
- package/dist/sdk/send-job.d.ts +15 -0
- package/dist/sdk/send-job.d.ts.map +1 -0
- package/dist/sdk/send-job.js +36 -0
- package/dist/sdk/send-job.js.map +1 -0
- package/helloruntime.schema.json +48 -0
- package/package.json +50 -0
package/dist/sdk/db.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { DEFAULT_DB_POOL_MAX, HELLORUNTIME_ENV } from "@helloruntime/protocol";
|
|
2
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
3
|
+
import pg from "pg";
|
|
4
|
+
// Drizzle over node-postgres on the injected POOLED endpoint. Small pool (the app
|
|
5
|
+
// runs at maxScale=1 with bounded concurrency). Do NOT use `.prepare()` — the
|
|
6
|
+
// pooled endpoint runs in transaction mode where persistent prepared statements
|
|
7
|
+
// are unreliable.
|
|
8
|
+
let instance = null;
|
|
9
|
+
export function getDb() {
|
|
10
|
+
if (instance)
|
|
11
|
+
return instance;
|
|
12
|
+
const url = process.env[HELLORUNTIME_ENV.DATABASE_URL];
|
|
13
|
+
if (!url) {
|
|
14
|
+
throw new Error(`${HELLORUNTIME_ENV.DATABASE_URL} is not set — is this running inside a Helloruntime app?`);
|
|
15
|
+
}
|
|
16
|
+
const max = Number(process.env[HELLORUNTIME_ENV.DB_POOL_MAX] ?? DEFAULT_DB_POOL_MAX);
|
|
17
|
+
const pool = new pg.Pool({ connectionString: url, max: Number.isFinite(max) ? max : DEFAULT_DB_POOL_MAX });
|
|
18
|
+
instance = drizzle(pool);
|
|
19
|
+
return instance;
|
|
20
|
+
}
|
|
21
|
+
/** Lazily-initialized Drizzle handle. Use with your own schema: `db.select().from(table)`. */
|
|
22
|
+
export const db = new Proxy({}, {
|
|
23
|
+
get(_target, property) {
|
|
24
|
+
return Reflect.get(getDb(), property);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Create a Drizzle database bound to your schema, over the injected pooled
|
|
29
|
+
* DATABASE_URL. The recommended entry point:
|
|
30
|
+
*
|
|
31
|
+
* import { createDatabase } from "helloruntime";
|
|
32
|
+
* import * as schema from "./schema";
|
|
33
|
+
* export const db = createDatabase(schema);
|
|
34
|
+
*/
|
|
35
|
+
export function createDatabase(schema) {
|
|
36
|
+
const url = process.env[HELLORUNTIME_ENV.DATABASE_URL];
|
|
37
|
+
if (!url) {
|
|
38
|
+
throw new Error(`${HELLORUNTIME_ENV.DATABASE_URL} is not set — is this running inside a Helloruntime app?`);
|
|
39
|
+
}
|
|
40
|
+
const max = Number(process.env[HELLORUNTIME_ENV.DB_POOL_MAX] ?? DEFAULT_DB_POOL_MAX);
|
|
41
|
+
const pool = new pg.Pool({ connectionString: url, max: Number.isFinite(max) ? max : DEFAULT_DB_POOL_MAX });
|
|
42
|
+
return drizzle(pool, { schema });
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/sdk/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAuB,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,kFAAkF;AAClF,8EAA8E;AAC9E,gFAAgF;AAChF,kBAAkB;AAClB,IAAI,QAAQ,GAA0B,IAAI,CAAC;AAE3C,MAAM,UAAU,KAAK;IACnB,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;IACvD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,GAAG,gBAAgB,CAAC,YAAY,0DAA0D,CAAC,CAAC;IAC9G,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,WAAW,CAAC,IAAI,mBAAmB,CAAC,CAAC;IACrF,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,gBAAgB,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC3G,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,8FAA8F;AAC9F,MAAM,CAAC,MAAM,EAAE,GAAmB,IAAI,KAAK,CAAC,EAAoB,EAAE;IAChE,GAAG,CAAC,OAAO,EAAE,QAAQ;QACnB,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAY,EAAE,QAAQ,CAAC,CAAC;IAClD,CAAC;CACF,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAe;IAEf,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;IACvD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,GAAG,gBAAgB,CAAC,YAAY,0DAA0D,CAAC,CAAC;IAC9G,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,WAAW,CAAC,IAAI,mBAAmB,CAAC,CAAC;IACrF,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,gBAAgB,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC3G,OAAO,OAAO,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { db, getDb, createDatabase } from "./db.js";
|
|
2
|
+
export { defineJob, type JobDefinition } from "./jobs.js";
|
|
3
|
+
export { defineCron, type CronDefinition } from "./cron.js";
|
|
4
|
+
export { sendJob, type SendJobOptions, type SendJobResult } from "./send-job.js";
|
|
5
|
+
export { createHelloruntimeHandler, type HelloruntimeHandlerOptions } from "./runtime/handler.js";
|
|
6
|
+
export { helloruntimeExpress } from "./runtime/express.js";
|
|
7
|
+
export { helloruntimeHono } from "./runtime/hono.js";
|
|
8
|
+
export { registerHelloruntime } from "./runtime/register.js";
|
|
9
|
+
export type { JobContext, CronContext, JobHandler, CronHandler } from "@helloruntime/protocol";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAGjF,OAAO,EAAE,yBAAyB,EAAE,KAAK,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClG,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAE7D,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// The public `helloruntime` SDK surface: import { db, defineCron, defineJob, sendJob } from "helloruntime".
|
|
2
|
+
export { db, getDb, createDatabase } from "./db.js";
|
|
3
|
+
export { defineJob } from "./jobs.js";
|
|
4
|
+
export { defineCron } from "./cron.js";
|
|
5
|
+
export { sendJob } from "./send-job.js";
|
|
6
|
+
// Runtime: serve platform job/cron dispatch from inside the app's own HTTP server.
|
|
7
|
+
export { createHelloruntimeHandler } from "./runtime/handler.js";
|
|
8
|
+
export { helloruntimeExpress } from "./runtime/express.js";
|
|
9
|
+
export { helloruntimeHono } from "./runtime/hono.js";
|
|
10
|
+
export { registerHelloruntime } from "./runtime/register.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"AAAA,4GAA4G;AAC5G,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,SAAS,EAAsB,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAuB,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,OAAO,EAA2C,MAAM,eAAe,CAAC;AAEjF,mFAAmF;AACnF,OAAO,EAAE,yBAAyB,EAAmC,MAAM,sBAAsB,CAAC;AAClG,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type JobHandler } from "@helloruntime/protocol";
|
|
2
|
+
export interface JobDefinition<T = unknown> {
|
|
3
|
+
name: string;
|
|
4
|
+
handler: JobHandler<T>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Register a job handler. The call is an import side-effect that fills the runtime
|
|
8
|
+
* registry; mount the SDK handler (helloruntimeExpress / helloruntimeHono / createHelloruntimeHandler)
|
|
9
|
+
* in your server to serve `_helloruntime/jobs/<name>`. Enqueue work with `sendJob(name, payload)`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function defineJob<T = unknown>(name: string, handler: JobHandler<T>): JobDefinition<T>;
|
|
12
|
+
//# sourceMappingURL=jobs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../src/sdk/jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAG7F"}
|
package/dist/sdk/jobs.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { registerJob } from "@helloruntime/protocol";
|
|
2
|
+
/**
|
|
3
|
+
* Register a job handler. The call is an import side-effect that fills the runtime
|
|
4
|
+
* registry; mount the SDK handler (helloruntimeExpress / helloruntimeHono / createHelloruntimeHandler)
|
|
5
|
+
* in your server to serve `_helloruntime/jobs/<name>`. Enqueue work with `sendJob(name, payload)`.
|
|
6
|
+
*/
|
|
7
|
+
export function defineJob(name, handler) {
|
|
8
|
+
registerJob(name, handler);
|
|
9
|
+
return { name, handler };
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=jobs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../src/sdk/jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAmB,MAAM,wBAAwB,CAAC;AAOtE;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAc,IAAY,EAAE,OAAsB;IACzE,WAAW,CAAI,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/sdk/registry.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// The `lakebed/_supervisor` subpath. The supervisor reads the populated registry
|
|
2
|
+
// after importing the user entry. (The registry itself lives in @lakebed/protocol
|
|
3
|
+
// so both the SDK and the supervisor depend only downward on protocol.)
|
|
4
|
+
export { getRegistry, snapshotRegistration } from "@lakebed/protocol";
|
|
5
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/sdk/registry.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,kFAAkF;AAClF,wEAAwE;AACxE,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAwB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { type HelloruntimeHandlerOptions } from "./handler.js";
|
|
3
|
+
type NextFn = (err?: unknown) => void;
|
|
4
|
+
export declare function helloruntimeExpress(options?: HelloruntimeHandlerOptions): (req: IncomingMessage, res: ServerResponse, next: NextFn) => void;
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=express.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/express.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,EAA6B,KAAK,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAK1F,KAAK,MAAM,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAKtC,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA+B,IAInC,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,MAAM,KAAG,IAAI,CAgBtG"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createHelloruntimeHandler } from "./handler.js";
|
|
2
|
+
import { registerHelloruntime } from "./register.js";
|
|
3
|
+
const HELLORUNTIME_PREFIX = "/_helloruntime/";
|
|
4
|
+
// Express middleware: terminates `/_helloruntime/*` dispatch (verifying HMAC + running
|
|
5
|
+
// the registered job/cron) and calls next() for everything else. Mount it BEFORE
|
|
6
|
+
// any body-parser — the HMAC covers the exact raw bytes, which a parser consumes.
|
|
7
|
+
export function helloruntimeExpress(options = {}) {
|
|
8
|
+
const handle = createHelloruntimeHandler(options);
|
|
9
|
+
setImmediate(() => void registerHelloruntime()); // after the entry module's defineJob/defineCron run
|
|
10
|
+
return function helloruntimeMiddleware(req, res, next) {
|
|
11
|
+
if (!req.url?.startsWith(HELLORUNTIME_PREFIX)) {
|
|
12
|
+
next();
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
readBody(req)
|
|
16
|
+
.then(async (body) => {
|
|
17
|
+
const response = await handle(toWebRequest(req, body));
|
|
18
|
+
if (!response) {
|
|
19
|
+
next();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
await writeResponse(res, response);
|
|
23
|
+
})
|
|
24
|
+
.catch((err) => next(err));
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function readBody(req) {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
const chunks = [];
|
|
30
|
+
req.on("data", (c) => chunks.push(Buffer.from(c)));
|
|
31
|
+
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
32
|
+
req.on("error", reject);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function toWebRequest(req, body) {
|
|
36
|
+
const host = req.headers.host ?? "app.internal";
|
|
37
|
+
const headers = new Headers();
|
|
38
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
39
|
+
if (Array.isArray(value))
|
|
40
|
+
for (const v of value)
|
|
41
|
+
headers.append(key, v);
|
|
42
|
+
else if (value != null)
|
|
43
|
+
headers.set(key, value);
|
|
44
|
+
}
|
|
45
|
+
return new Request(`http://${host}${req.url}`, {
|
|
46
|
+
method: req.method ?? "GET",
|
|
47
|
+
headers,
|
|
48
|
+
body: body.length ? body : undefined
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async function writeResponse(res, response) {
|
|
52
|
+
res.statusCode = response.status;
|
|
53
|
+
response.headers.forEach((value, key) => res.setHeader(key, value));
|
|
54
|
+
res.end(Buffer.from(await response.arrayBuffer()));
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=express.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.js","sourceRoot":"","sources":["../../../src/sdk/runtime/express.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type RuntimeRegistry } from "@helloruntime/protocol";
|
|
2
|
+
export interface HelloruntimeHandlerOptions {
|
|
3
|
+
/** The populated job/cron registry. Defaults to the global registry defineJob/defineCron fill. */
|
|
4
|
+
registry?: RuntimeRegistry;
|
|
5
|
+
/** Per-app HMAC secret; defaults to HELLORUNTIME_DISPATCH_SECRET. Verification fails closed if absent. */
|
|
6
|
+
dispatchSecret?: string;
|
|
7
|
+
/** Max time a handler may run (default 60 min, the safety ceiling). */
|
|
8
|
+
handlerTimeoutMs?: number;
|
|
9
|
+
/** Current unix seconds (injectable for tests). */
|
|
10
|
+
now?: () => number;
|
|
11
|
+
}
|
|
12
|
+
export declare function createHelloruntimeHandler(options?: HelloruntimeHandlerOptions): (request: Request) => Promise<Response | null>;
|
|
13
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAC;AAIhC,MAAM,WAAW,0BAA0B;IACzC,kGAAkG;IAClG,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,0GAA0G;IAC1G,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AA0BD,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,0BAA+B,GACvC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA+DhD"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { getRegistry, INTERNAL_CRON_PATH_PREFIX, INTERNAL_JOB_PATH_PREFIX, HELLORUNTIME_ENV } from "@helloruntime/protocol";
|
|
2
|
+
import { verifyDispatchRequest } from "./verify.js";
|
|
3
|
+
const DEFAULT_TIMEOUT_MS = 60 * 60 * 1000;
|
|
4
|
+
function json(status, body) {
|
|
5
|
+
return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
|
|
6
|
+
}
|
|
7
|
+
async function runWithTimeout(fn, ms) {
|
|
8
|
+
let timer;
|
|
9
|
+
try {
|
|
10
|
+
await Promise.race([
|
|
11
|
+
Promise.resolve().then(fn),
|
|
12
|
+
new Promise((_, reject) => {
|
|
13
|
+
timer = setTimeout(() => reject(new Error(`handler timed out after ${ms}ms`)), ms);
|
|
14
|
+
})
|
|
15
|
+
]);
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
if (timer)
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// The platform's job/cron dispatch handler, as a plain WHATWG fetch function.
|
|
23
|
+
// Returns a Response for a verified `/_helloruntime/jobs|cron/:name` POST, or `null`
|
|
24
|
+
// for any other request so the host app serves its own routes. This is what the
|
|
25
|
+
// Express/Hono shims wrap; raw `node:http` servers can call it directly.
|
|
26
|
+
export function createHelloruntimeHandler(options = {}) {
|
|
27
|
+
const registry = options.registry ?? getRegistry();
|
|
28
|
+
const timeoutMs = options.handlerTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
29
|
+
const now = options.now ?? (() => Math.floor(Date.now() / 1000));
|
|
30
|
+
return async function handleDispatch(request) {
|
|
31
|
+
const path = new URL(request.url).pathname;
|
|
32
|
+
const isJob = path.startsWith(INTERNAL_JOB_PATH_PREFIX);
|
|
33
|
+
const isCron = path.startsWith(INTERNAL_CRON_PATH_PREFIX);
|
|
34
|
+
if (!isJob && !isCron)
|
|
35
|
+
return null; // not ours → host app handles it
|
|
36
|
+
if (request.method !== "POST")
|
|
37
|
+
return json(405, { error: "method not allowed" });
|
|
38
|
+
const secret = options.dispatchSecret ?? process.env[HELLORUNTIME_ENV.DISPATCH_SECRET];
|
|
39
|
+
const rawBody = await request.text();
|
|
40
|
+
const verified = verifyDispatchRequest({
|
|
41
|
+
method: "POST",
|
|
42
|
+
path,
|
|
43
|
+
headers: request.headers,
|
|
44
|
+
rawBody,
|
|
45
|
+
secret,
|
|
46
|
+
nowSeconds: now()
|
|
47
|
+
});
|
|
48
|
+
if (!verified.ok)
|
|
49
|
+
return json(verified.status, { error: verified.reason });
|
|
50
|
+
const { envelope } = verified;
|
|
51
|
+
if (envelope.kind !== (isJob ? "job" : "cron"))
|
|
52
|
+
return json(400, { error: "kind mismatch" });
|
|
53
|
+
const prefix = isJob ? INTERNAL_JOB_PATH_PREFIX : INTERNAL_CRON_PATH_PREFIX;
|
|
54
|
+
const name = decodeURIComponent(path.slice(prefix.length));
|
|
55
|
+
try {
|
|
56
|
+
if (isJob) {
|
|
57
|
+
const handler = registry.jobs.get(name);
|
|
58
|
+
if (!handler)
|
|
59
|
+
return json(404, { error: `No job registered named "${name}"` });
|
|
60
|
+
await runWithTimeout(() => handler(envelope.payload, {
|
|
61
|
+
name,
|
|
62
|
+
jobId: envelope.jobId,
|
|
63
|
+
idempotencyKey: envelope.idempotencyKey,
|
|
64
|
+
attempt: envelope.attempt
|
|
65
|
+
}), timeoutMs);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const entry = registry.crons.get(name);
|
|
69
|
+
if (!entry)
|
|
70
|
+
return json(404, { error: `No cron registered named "${name}"` });
|
|
71
|
+
await runWithTimeout(() => entry.handler({
|
|
72
|
+
name,
|
|
73
|
+
schedule: entry.schedule,
|
|
74
|
+
idempotencyKey: envelope.idempotencyKey,
|
|
75
|
+
scheduledAt: new Date(envelope.enqueuedAt)
|
|
76
|
+
}), timeoutMs);
|
|
77
|
+
}
|
|
78
|
+
return json(200, { ok: true });
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return json(500, { error: error instanceof Error ? error.message : String(error) });
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/sdk/runtime/handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type HelloruntimeHandlerOptions } from "./handler.js";
|
|
2
|
+
interface HonoContextLike {
|
|
3
|
+
req: {
|
|
4
|
+
raw: Request;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
type HonoNext = () => Promise<void>;
|
|
8
|
+
export declare function helloruntimeHono(options?: HelloruntimeHandlerOptions): (c: HonoContextLike, next: HonoNext) => Promise<Response | void>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=hono.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAK1F,UAAU,eAAe;IACvB,GAAG,EAAE;QAAE,GAAG,EAAE,OAAO,CAAA;KAAE,CAAC;CACvB;AACD,KAAK,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAKpC,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,0BAA+B,IAI1B,GAAG,eAAe,EAAE,MAAM,QAAQ,KAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAK3G"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createHelloruntimeHandler } from "./handler.js";
|
|
2
|
+
import { registerHelloruntime } from "./register.js";
|
|
3
|
+
// Hono middleware: terminates `/_helloruntime/*` dispatch, falls through otherwise.
|
|
4
|
+
// `app.use(helloruntimeHono())`. The handler inspects the path before touching the body,
|
|
5
|
+
// so non-dispatch requests pass through with their body intact.
|
|
6
|
+
export function helloruntimeHono(options = {}) {
|
|
7
|
+
const handle = createHelloruntimeHandler(options);
|
|
8
|
+
setImmediate(() => void registerHelloruntime());
|
|
9
|
+
return async function helloruntimeMiddleware(c, next) {
|
|
10
|
+
const response = await handle(c.req.raw);
|
|
11
|
+
if (response)
|
|
12
|
+
return response;
|
|
13
|
+
await next();
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=hono.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../../../src/sdk/runtime/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAmC,MAAM,cAAc,CAAC;AAC1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AASrD,oFAAoF;AACpF,yFAAyF;AACzF,gEAAgE;AAChE,MAAM,UAAU,gBAAgB,CAAC,UAAsC,EAAE;IACvE,MAAM,MAAM,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAClD,YAAY,CAAC,GAAG,EAAE,CAAC,KAAK,oBAAoB,EAAE,CAAC,CAAC;IAEhD,OAAO,KAAK,UAAU,sBAAsB,CAAC,CAAkB,EAAE,IAAc;QAC7E,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST the app's full job/cron snapshot to the control plane (runtime token auth).
|
|
3
|
+
* The platform treats it as desired-state and reconciles cron schedules. Fires at
|
|
4
|
+
* most once per process; best-effort — never wedges boot. The framework shims call
|
|
5
|
+
* this automatically; raw servers (or other languages) can call it themselves (or
|
|
6
|
+
* POST the same JSON to RUNTIME_REGISTER_PATH).
|
|
7
|
+
*/
|
|
8
|
+
export declare function registerHelloruntime(): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/register.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAoB1D"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { HELLORUNTIME_ENV, RUNTIME_REGISTER_PATH, snapshotRegistration } from "@helloruntime/protocol";
|
|
2
|
+
let registered = false;
|
|
3
|
+
/**
|
|
4
|
+
* POST the app's full job/cron snapshot to the control plane (runtime token auth).
|
|
5
|
+
* The platform treats it as desired-state and reconciles cron schedules. Fires at
|
|
6
|
+
* most once per process; best-effort — never wedges boot. The framework shims call
|
|
7
|
+
* this automatically; raw servers (or other languages) can call it themselves (or
|
|
8
|
+
* POST the same JSON to RUNTIME_REGISTER_PATH).
|
|
9
|
+
*/
|
|
10
|
+
export async function registerHelloruntime() {
|
|
11
|
+
if (registered)
|
|
12
|
+
return;
|
|
13
|
+
registered = true;
|
|
14
|
+
const apiUrl = process.env[HELLORUNTIME_ENV.API_URL];
|
|
15
|
+
const token = process.env[HELLORUNTIME_ENV.TOKEN];
|
|
16
|
+
if (!apiUrl || !token)
|
|
17
|
+
return;
|
|
18
|
+
try {
|
|
19
|
+
const response = await fetch(new URL(RUNTIME_REGISTER_PATH, apiUrl), {
|
|
20
|
+
method: "POST",
|
|
21
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
|
|
22
|
+
body: JSON.stringify(snapshotRegistration())
|
|
23
|
+
});
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
console.warn(`helloruntime: self-register returned ${response.status} (continuing)`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.warn("helloruntime: self-register failed (continuing):", error);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../src/sdk/runtime/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAEvG,IAAI,UAAU,GAAG,KAAK,CAAC;AAEvB;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,IAAI,UAAU;QAAE,OAAO;IACvB,UAAU,GAAG,IAAI,CAAC;IAElB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK;QAAE,OAAO;IAE9B,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,qBAAqB,EAAE,MAAM,CAAC,EAAE;YACnE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;YACjF,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,oBAAoB,EAAE,CAAC;SAC7C,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,wCAAwC,QAAQ,CAAC,MAAM,eAAe,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,kDAAkD,EAAE,KAAK,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type DispatchEnvelope } from "@helloruntime/protocol";
|
|
2
|
+
export interface IncomingDispatch {
|
|
3
|
+
method: string;
|
|
4
|
+
path: string;
|
|
5
|
+
headers: Headers;
|
|
6
|
+
rawBody: string;
|
|
7
|
+
/** HELLORUNTIME_DISPATCH_SECRET injected into the app. */
|
|
8
|
+
secret: string | undefined;
|
|
9
|
+
nowSeconds: number;
|
|
10
|
+
}
|
|
11
|
+
export type DispatchVerification = {
|
|
12
|
+
ok: true;
|
|
13
|
+
envelope: DispatchEnvelope;
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
status: 400 | 401;
|
|
17
|
+
reason: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function verifyDispatchRequest(req: IncomingDispatch): DispatchVerification;
|
|
20
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAOhC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,oBAAoB,GAC5B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,gBAAgB,CAAA;CAAE,GACxC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG,oBAAoB,CA4BjF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { dispatchEnvelopeSchema, DISPATCH_HEADERS, verifyDispatch } from "@helloruntime/protocol";
|
|
2
|
+
export function verifyDispatchRequest(req) {
|
|
3
|
+
// Parse + validate the envelope first; the idempotencyKey it carries is what the
|
|
4
|
+
// signature was computed over.
|
|
5
|
+
let envelope;
|
|
6
|
+
try {
|
|
7
|
+
envelope = dispatchEnvelopeSchema.parse(JSON.parse(req.rawBody));
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return { ok: false, status: 400, reason: "invalid-envelope" };
|
|
11
|
+
}
|
|
12
|
+
const result = verifyDispatch({
|
|
13
|
+
secret: req.secret,
|
|
14
|
+
method: req.method,
|
|
15
|
+
path: req.path,
|
|
16
|
+
rawBody: req.rawBody,
|
|
17
|
+
signatureHeader: req.headers.get(DISPATCH_HEADERS.signature),
|
|
18
|
+
timestampHeader: req.headers.get(DISPATCH_HEADERS.timestamp),
|
|
19
|
+
idempotencyKey: envelope.idempotencyKey,
|
|
20
|
+
nowSeconds: req.nowSeconds
|
|
21
|
+
});
|
|
22
|
+
if (!result.ok) {
|
|
23
|
+
// All verification failures (incl. fail-closed missing-secret) reject as 401.
|
|
24
|
+
// The dispatcher maps 401/403 to dead-letter + alert.
|
|
25
|
+
return { ok: false, status: 401, reason: result.reason };
|
|
26
|
+
}
|
|
27
|
+
return { ok: true, envelope };
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/sdk/runtime/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,EAEf,MAAM,wBAAwB,CAAC;AAqBhC,MAAM,UAAU,qBAAqB,CAAC,GAAqB;IACzD,iFAAiF;IACjF,+BAA+B;IAC/B,IAAI,QAA0B,CAAC;IAC/B,IAAI,CAAC;QACH,QAAQ,GAAG,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAChE,CAAC;IAED,MAAM,MAAM,GAAG,cAAc,CAAC;QAC5B,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,eAAe,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,SAAS,CAAC;QAC5D,eAAe,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,SAAS,CAAC;QAC5D,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,UAAU,EAAE,GAAG,CAAC,UAAU;KAC3B,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,8EAA8E;QAC9E,sDAAsD;QACtD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAC3D,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface SendJobOptions {
|
|
2
|
+
/** Producer-side dedupe key: identical keys enqueue the job at most once. */
|
|
3
|
+
dedupeKey?: string;
|
|
4
|
+
/** Earliest time the job may be dispatched. */
|
|
5
|
+
notBefore?: Date;
|
|
6
|
+
}
|
|
7
|
+
export interface SendJobResult {
|
|
8
|
+
jobId: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Enqueue a job with the durable platform dispatcher. The app is inferred from
|
|
12
|
+
* the injected runtime token (HELLORUNTIME_TOKEN) — never from the URL.
|
|
13
|
+
*/
|
|
14
|
+
export declare function sendJob(name: string, payload: unknown, options?: SendJobOptions): Promise<SendJobResult>;
|
|
15
|
+
//# sourceMappingURL=send-job.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-job.d.ts","sourceRoot":"","sources":["../../src/sdk/send-job.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,aAAa,CAAC,CA+BxB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { HELLORUNTIME_ENV, SENDJOB_DEDUPE_HEADER, SENDJOB_PATH } from "@helloruntime/protocol";
|
|
2
|
+
/**
|
|
3
|
+
* Enqueue a job with the durable platform dispatcher. The app is inferred from
|
|
4
|
+
* the injected runtime token (HELLORUNTIME_TOKEN) — never from the URL.
|
|
5
|
+
*/
|
|
6
|
+
export async function sendJob(name, payload, options = {}) {
|
|
7
|
+
const apiUrl = process.env[HELLORUNTIME_ENV.API_URL];
|
|
8
|
+
const token = process.env[HELLORUNTIME_ENV.TOKEN];
|
|
9
|
+
if (!apiUrl)
|
|
10
|
+
throw new Error(`${HELLORUNTIME_ENV.API_URL} is not set`);
|
|
11
|
+
if (!token)
|
|
12
|
+
throw new Error(`${HELLORUNTIME_ENV.TOKEN} is not set`);
|
|
13
|
+
const body = {
|
|
14
|
+
name,
|
|
15
|
+
payload,
|
|
16
|
+
dedupeKey: options.dedupeKey,
|
|
17
|
+
notBefore: options.notBefore?.toISOString()
|
|
18
|
+
};
|
|
19
|
+
const headers = {
|
|
20
|
+
"content-type": "application/json",
|
|
21
|
+
authorization: `Bearer ${token}`
|
|
22
|
+
};
|
|
23
|
+
if (options.dedupeKey)
|
|
24
|
+
headers[SENDJOB_DEDUPE_HEADER] = options.dedupeKey;
|
|
25
|
+
const response = await fetch(new URL(SENDJOB_PATH, apiUrl), {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers,
|
|
28
|
+
body: JSON.stringify(body)
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
const detail = await response.text().catch(() => "");
|
|
32
|
+
throw new Error(`sendJob failed: ${response.status} ${detail}`);
|
|
33
|
+
}
|
|
34
|
+
return (await response.json());
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=send-job.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-job.js","sourceRoot":"","sources":["../../src/sdk/send-job.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,YAAY,EAAuB,MAAM,wBAAwB,CAAC;AAapH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,IAAY,EACZ,OAAgB,EAChB,UAA0B,EAAE;IAE5B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,gBAAgB,CAAC,OAAO,aAAa,CAAC,CAAC;IACvE,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,gBAAgB,CAAC,KAAK,aAAa,CAAC,CAAC;IAEpE,MAAM,IAAI,GAAmB;QAC3B,IAAI;QACJ,OAAO;QACP,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,WAAW,EAAE;KAC5C,CAAC;IAEF,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;QAClC,aAAa,EAAE,UAAU,KAAK,EAAE;KACjC,CAAC;IACF,IAAI,OAAO,CAAC,SAAS;QAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAE1E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE;QAC1D,MAAM,EAAE,MAAM;QACd,OAAO;QACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAkB,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://helloruntime.dev/schema/helloruntime.json",
|
|
4
|
+
"title": "Helloruntime app config",
|
|
5
|
+
"description": "Build + deploy knobs for a Helloruntime app (helloruntime.json). All fields are optional. Helloruntime builds the Dockerfile your source ships (./Dockerfile, or build.dockerfile).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"$schema": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "JSON Schema reference for editor autocomplete/validation."
|
|
12
|
+
},
|
|
13
|
+
"build": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"additionalProperties": false,
|
|
16
|
+
"description": "How the source becomes an image.",
|
|
17
|
+
"properties": {
|
|
18
|
+
"dockerfile": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "Path to the Dockerfile to build, relative to the project root. Defaults to ./Dockerfile (required)."
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"deploy": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"additionalProperties": false,
|
|
27
|
+
"description": "Deploy-time behavior.",
|
|
28
|
+
"properties": {
|
|
29
|
+
"release": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "One-shot command run in the built image before cutover (e.g. database migrations). The deploy fails if it exits non-zero."
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"database": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"description": "Managed database.",
|
|
39
|
+
"properties": {
|
|
40
|
+
"enabled": {
|
|
41
|
+
"type": "boolean",
|
|
42
|
+
"default": true,
|
|
43
|
+
"description": "Provision a managed Postgres and inject DATABASE_URL. Set false to opt out."
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|