@jondotsoy/pika 0.1.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/LICENSE +21 -0
- package/README.md +50 -0
- package/lib/esm/auth/dto/connection.d.ts +43 -0
- package/lib/esm/auth/dto/connection.js +16 -0
- package/lib/esm/auth/index.d.ts +10 -0
- package/lib/esm/auth/index.js +64 -0
- package/lib/esm/cli/__samples__/parent-child-script.d.ts +1 -0
- package/lib/esm/cli/__samples__/parent-child-script.js +9 -0
- package/lib/esm/cli/cli-event-payload.d.ts +53 -0
- package/lib/esm/cli/cli-event-payload.js +32 -0
- package/lib/esm/cli/index.d.ts +2 -0
- package/lib/esm/cli/index.js +167 -0
- package/lib/esm/cli/utils/create-server.d.ts +20 -0
- package/lib/esm/cli/utils/create-server.js +70 -0
- package/lib/esm/flow/__samples__/credentials-job.d.ts +1 -0
- package/lib/esm/flow/__samples__/credentials-job.js +9 -0
- package/lib/esm/flow/__samples__/only-app.d.ts +1 -0
- package/lib/esm/flow/__samples__/only-app.js +4 -0
- package/lib/esm/flow/__samples__/parent-child-jobs.d.ts +1 -0
- package/lib/esm/flow/__samples__/parent-child-jobs.js +10 -0
- package/lib/esm/flow/__samples__/parent-with-subjobs.d.ts +1 -0
- package/lib/esm/flow/__samples__/parent-with-subjobs.js +9 -0
- package/lib/esm/flow/__samples__/wait-job.d.ts +1 -0
- package/lib/esm/flow/__samples__/wait-job.js +6 -0
- package/lib/esm/flow/__samples__/zod-invalid-job.d.ts +1 -0
- package/lib/esm/flow/__samples__/zod-invalid-job.js +4 -0
- package/lib/esm/flow/__samples__/zod-validation-job.d.ts +1 -0
- package/lib/esm/flow/__samples__/zod-validation-job.js +6 -0
- package/lib/esm/flow/configs/env-configs.d.ts +37 -0
- package/lib/esm/flow/configs/env-configs.js +59 -0
- package/lib/esm/flow/constants/run-mode.d.ts +5 -0
- package/lib/esm/flow/constants/run-mode.js +6 -0
- package/lib/esm/flow/context-reflects/cli-http-reflect.d.ts +9 -0
- package/lib/esm/flow/context-reflects/cli-http-reflect.js +55 -0
- package/lib/esm/flow/context-reflects/dsn-reflect.d.ts +29 -0
- package/lib/esm/flow/context-reflects/dsn-reflect.js +181 -0
- package/lib/esm/flow/context-reflects/file-context-reflect.d.ts +22 -0
- package/lib/esm/flow/context-reflects/file-context-reflect.js +47 -0
- package/lib/esm/flow/context.d.ts +43 -0
- package/lib/esm/flow/context.js +104 -0
- package/lib/esm/flow/current.d.ts +4 -0
- package/lib/esm/flow/current.js +1 -0
- package/lib/esm/flow/dto/cb-app.d.ts +2 -0
- package/lib/esm/flow/dto/cb-app.js +1 -0
- package/lib/esm/flow/dto/job-id.d.ts +1 -0
- package/lib/esm/flow/dto/job-id.js +1 -0
- package/lib/esm/flow/dto/job.d.ts +18 -0
- package/lib/esm/flow/dto/job.js +9 -0
- package/lib/esm/flow/dto/manifest.d.ts +5 -0
- package/lib/esm/flow/dto/manifest.js +1 -0
- package/lib/esm/flow/index.d.ts +26 -0
- package/lib/esm/flow/index.js +46 -0
- package/lib/esm/flow/loader.d.ts +1 -0
- package/lib/esm/flow/loader.js +227 -0
- package/lib/esm/flow/util/json-rpc-transporter.d.ts +80 -0
- package/lib/esm/flow/util/json-rpc-transporter.js +175 -0
- package/lib/esm/flow/utils/bytes.d.ts +29 -0
- package/lib/esm/flow/utils/bytes.js +32 -0
- package/lib/esm/flow/utils/event-manager.d.ts +11 -0
- package/lib/esm/flow/utils/event-manager.js +41 -0
- package/lib/esm/flow/utils/file.d.ts +17 -0
- package/lib/esm/flow/utils/file.js +52 -0
- package/lib/esm/flow/utils/observable-set.d.ts +9 -0
- package/lib/esm/flow/utils/observable-set.js +31 -0
- package/lib/esm/flow/utils/queue.d.ts +32 -0
- package/lib/esm/flow/utils/queue.js +86 -0
- package/lib/esm/flow/worker_ctx.d.ts +5 -0
- package/lib/esm/flow/worker_ctx.js +2 -0
- package/lib/esm/http/index.d.ts +32 -0
- package/lib/esm/http/index.js +54 -0
- package/lib/esm/telemetry/metrics/gateway.d.ts +2 -0
- package/lib/esm/telemetry/metrics/gateway.js +6 -0
- package/lib/esm/telemetry/metrics/job-gauges.d.ts +13 -0
- package/lib/esm/telemetry/metrics/job-gauges.js +39 -0
- package/lib/esm/telemetry/metrics/job-store.d.ts +3 -0
- package/lib/esm/telemetry/metrics/job-store.js +31 -0
- package/lib/esm/telemetry/metrics/push.d.ts +1 -0
- package/lib/esm/telemetry/metrics/push.js +4 -0
- package/lib/esm/telemetry/metrics/registry.d.ts +2 -0
- package/lib/esm/telemetry/metrics/registry.js +2 -0
- package/lib/esm/telemetry/metrics/telemetry-url.d.ts +1 -0
- package/lib/esm/telemetry/metrics/telemetry-url.js +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type FileEncoding = "utf-8";
|
|
2
|
+
export declare class File {
|
|
3
|
+
readonly path: string | URL;
|
|
4
|
+
readonly options: {
|
|
5
|
+
encoding?: FileEncoding;
|
|
6
|
+
};
|
|
7
|
+
readonly encoding: FileEncoding;
|
|
8
|
+
constructor(path: string | URL, options?: {
|
|
9
|
+
encoding?: FileEncoding;
|
|
10
|
+
});
|
|
11
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
12
|
+
text(): Promise<string>;
|
|
13
|
+
json<T>(): Promise<T>;
|
|
14
|
+
exists(): Promise<boolean>;
|
|
15
|
+
write(data: string | ArrayBuffer | object): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
export class File {
|
|
4
|
+
path;
|
|
5
|
+
options;
|
|
6
|
+
encoding;
|
|
7
|
+
constructor(path, options = {}) {
|
|
8
|
+
this.path = path;
|
|
9
|
+
this.options = options;
|
|
10
|
+
this.encoding = options.encoding ?? "utf-8";
|
|
11
|
+
}
|
|
12
|
+
async arrayBuffer() {
|
|
13
|
+
const buffer = await fs.readFile(this.path);
|
|
14
|
+
return new Uint8Array(buffer).buffer;
|
|
15
|
+
}
|
|
16
|
+
async text() {
|
|
17
|
+
const buffer = await this.arrayBuffer();
|
|
18
|
+
return new TextDecoder(this.encoding).decode(buffer);
|
|
19
|
+
}
|
|
20
|
+
async json() {
|
|
21
|
+
const text = await this.text();
|
|
22
|
+
return JSON.parse(text);
|
|
23
|
+
}
|
|
24
|
+
async exists() {
|
|
25
|
+
try {
|
|
26
|
+
await fs.access(this.path);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async write(data) {
|
|
34
|
+
const encod = {
|
|
35
|
+
object: (value) => new TextEncoder(this.encoding).encode(JSON.stringify(value)).buffer,
|
|
36
|
+
string: (value) => new TextEncoder(this.encoding).encode(value).buffer,
|
|
37
|
+
buffer: (value) => value,
|
|
38
|
+
};
|
|
39
|
+
const type = data instanceof ArrayBuffer ? "buffer" : typeof data;
|
|
40
|
+
const payload = encod[typeof data]?.(data);
|
|
41
|
+
if (payload === undefined) {
|
|
42
|
+
throw new Error("Unsupported data type for writing");
|
|
43
|
+
}
|
|
44
|
+
const relativePathname = this.path instanceof URL ? this.path.pathname : this.path;
|
|
45
|
+
const abspath = path.isAbsolute(relativePathname)
|
|
46
|
+
? relativePathname
|
|
47
|
+
: path.resolve(process.cwd(), relativePathname);
|
|
48
|
+
const dir = path.dirname(abspath);
|
|
49
|
+
await fs.mkdir(dir, { recursive: true });
|
|
50
|
+
await fs.writeFile(abspath, new Uint8Array(payload), this.encoding);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class ObservableSet<T> extends Set<T> {
|
|
2
|
+
private _events;
|
|
3
|
+
addEventListener: any;
|
|
4
|
+
removeEventListener: any;
|
|
5
|
+
add(value: T): this;
|
|
6
|
+
}
|
|
7
|
+
export declare function getNextAvailable<T>(set: ObservableSet<T>, options?: {
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
}): Promise<T | null>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { EventManager } from "./event-manager.js";
|
|
2
|
+
export class ObservableSet extends Set {
|
|
3
|
+
_events = new EventManager();
|
|
4
|
+
addEventListener = this._events.addEventListener.bind(this._events);
|
|
5
|
+
removeEventListener = this._events.removeEventListener.bind(this._events);
|
|
6
|
+
add(value) {
|
|
7
|
+
const result = super.add(value);
|
|
8
|
+
this._events.emit("add", value);
|
|
9
|
+
return result;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
// Helper function to get the next available item from an ObservableSet, waiting if necessary
|
|
13
|
+
export async function getNextAvailable(set, options) {
|
|
14
|
+
let active = true;
|
|
15
|
+
while (active) {
|
|
16
|
+
for (const item of set) {
|
|
17
|
+
set.delete(item);
|
|
18
|
+
return item;
|
|
19
|
+
}
|
|
20
|
+
await new Promise((r) => {
|
|
21
|
+
const resolvePromise = () => r();
|
|
22
|
+
set.addEventListener("add", resolvePromise, { once: true });
|
|
23
|
+
options?.signal?.addEventListener("abort", () => {
|
|
24
|
+
set.removeEventListener("add", resolvePromise);
|
|
25
|
+
active = false;
|
|
26
|
+
r();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ObservableSet } from "./observable-set.js";
|
|
2
|
+
type Handler<T> = (item: T) => Promise<void>;
|
|
3
|
+
declare enum WorkerStatus {
|
|
4
|
+
Idle = "idle",
|
|
5
|
+
Processing = "processing",
|
|
6
|
+
Stopped = "stopped"
|
|
7
|
+
}
|
|
8
|
+
export declare class Worker<T> {
|
|
9
|
+
readonly observableSet: ObservableSet<T>;
|
|
10
|
+
readonly handler: Handler<T>;
|
|
11
|
+
private options?;
|
|
12
|
+
private active;
|
|
13
|
+
private _status;
|
|
14
|
+
private _process;
|
|
15
|
+
constructor(observableSet: ObservableSet<T>, handler: Handler<T>, options?: {
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
} | undefined, active?: boolean, _status?: WorkerStatus);
|
|
18
|
+
private start;
|
|
19
|
+
get status(): WorkerStatus;
|
|
20
|
+
stop(): void;
|
|
21
|
+
}
|
|
22
|
+
export declare class Queue<T> {
|
|
23
|
+
readonly options?: {} | undefined;
|
|
24
|
+
private _items;
|
|
25
|
+
private _workers;
|
|
26
|
+
constructor(options?: {} | undefined);
|
|
27
|
+
get workers(): number;
|
|
28
|
+
get size(): number;
|
|
29
|
+
enqueue(item: T): void;
|
|
30
|
+
subscribe(handler: Handler<T>): () => void;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { getNextAvailable, ObservableSet } from "./observable-set.js";
|
|
2
|
+
var SubscriptionStatus;
|
|
3
|
+
(function (SubscriptionStatus) {
|
|
4
|
+
SubscriptionStatus["Idle"] = "idle";
|
|
5
|
+
SubscriptionStatus["Processing"] = "processing";
|
|
6
|
+
})(SubscriptionStatus || (SubscriptionStatus = {}));
|
|
7
|
+
var WorkerStatus;
|
|
8
|
+
(function (WorkerStatus) {
|
|
9
|
+
WorkerStatus["Idle"] = "idle";
|
|
10
|
+
WorkerStatus["Processing"] = "processing";
|
|
11
|
+
WorkerStatus["Stopped"] = "stopped";
|
|
12
|
+
})(WorkerStatus || (WorkerStatus = {}));
|
|
13
|
+
export class Worker {
|
|
14
|
+
observableSet;
|
|
15
|
+
handler;
|
|
16
|
+
options;
|
|
17
|
+
active;
|
|
18
|
+
_status;
|
|
19
|
+
_process;
|
|
20
|
+
constructor(observableSet, handler, options, active = true, _status = WorkerStatus.Idle) {
|
|
21
|
+
this.observableSet = observableSet;
|
|
22
|
+
this.handler = handler;
|
|
23
|
+
this.options = options;
|
|
24
|
+
this.active = active;
|
|
25
|
+
this._status = _status;
|
|
26
|
+
this._process = this.start();
|
|
27
|
+
options?.signal?.addEventListener("abort", () => {
|
|
28
|
+
this.active = false;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async start() {
|
|
32
|
+
while (this.active) {
|
|
33
|
+
const item = await getNextAvailable(this.observableSet, {
|
|
34
|
+
signal: this.options?.signal,
|
|
35
|
+
});
|
|
36
|
+
if (item === null) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
this._status = WorkerStatus.Processing;
|
|
41
|
+
await this.handler(item);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error("Error processing item in worker:", error);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
this._status = WorkerStatus.Idle;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
get status() {
|
|
52
|
+
return this._status;
|
|
53
|
+
}
|
|
54
|
+
stop() {
|
|
55
|
+
this.active = false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export class Queue {
|
|
59
|
+
options;
|
|
60
|
+
_items = new ObservableSet();
|
|
61
|
+
_workers = new Set();
|
|
62
|
+
constructor(options) {
|
|
63
|
+
this.options = options;
|
|
64
|
+
}
|
|
65
|
+
get workers() {
|
|
66
|
+
return this._workers.size;
|
|
67
|
+
}
|
|
68
|
+
get size() {
|
|
69
|
+
return this._items.size;
|
|
70
|
+
}
|
|
71
|
+
enqueue(item) {
|
|
72
|
+
this._items.add(item);
|
|
73
|
+
}
|
|
74
|
+
subscribe(handler) {
|
|
75
|
+
const abortController = new AbortController();
|
|
76
|
+
const worker = new Worker(this._items, handler, {
|
|
77
|
+
signal: abortController.signal,
|
|
78
|
+
});
|
|
79
|
+
this._workers.add(worker);
|
|
80
|
+
return () => {
|
|
81
|
+
abortController.abort();
|
|
82
|
+
worker.stop();
|
|
83
|
+
this._workers.delete(worker);
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Credentials } from "../auth/index.js";
|
|
2
|
+
export type BodyParser = (res: Response) => Promise<any>;
|
|
3
|
+
export type WrapFetchOptions = {
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
bodyParser?: BodyParser;
|
|
6
|
+
credentials?: Credentials;
|
|
7
|
+
};
|
|
8
|
+
export type WrapFetchResult = {
|
|
9
|
+
status: number;
|
|
10
|
+
headers: Record<string, string>;
|
|
11
|
+
body: any;
|
|
12
|
+
items: any[];
|
|
13
|
+
};
|
|
14
|
+
export declare function wrapFetch(url: string, options?: WrapFetchOptions): Promise<WrapFetchResult>;
|
|
15
|
+
export type PaginationOptions = {
|
|
16
|
+
nextPage: (ctx: {
|
|
17
|
+
body: any;
|
|
18
|
+
}) => string | null;
|
|
19
|
+
items: (ctx: {
|
|
20
|
+
body: any;
|
|
21
|
+
}) => any[] | null;
|
|
22
|
+
};
|
|
23
|
+
export type HttpOptions = WrapFetchOptions & {
|
|
24
|
+
pagination?: PaginationOptions;
|
|
25
|
+
};
|
|
26
|
+
export type HttpResult = {
|
|
27
|
+
status: number | null;
|
|
28
|
+
headers: Record<string, string> | null;
|
|
29
|
+
body: any | null;
|
|
30
|
+
items: any[];
|
|
31
|
+
};
|
|
32
|
+
export declare function http(url: string, options?: HttpOptions): Promise<HttpResult>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { parse as parseYaml } from "yaml";
|
|
2
|
+
import { Placement } from "../auth/index.js";
|
|
3
|
+
const defaultBodyParser = async (res) => {
|
|
4
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
5
|
+
const text = await res.text();
|
|
6
|
+
if (ct.includes("application/json") || ct.includes("+json"))
|
|
7
|
+
return JSON.parse(text);
|
|
8
|
+
if (ct.includes("yaml") || ct.includes("x-yaml"))
|
|
9
|
+
return parseYaml(text);
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(text);
|
|
12
|
+
}
|
|
13
|
+
catch { }
|
|
14
|
+
try {
|
|
15
|
+
return parseYaml(text);
|
|
16
|
+
}
|
|
17
|
+
catch { }
|
|
18
|
+
return text;
|
|
19
|
+
};
|
|
20
|
+
export async function wrapFetch(url, options = {}) {
|
|
21
|
+
const { headers: reqHeaders, bodyParser = defaultBodyParser, credentials, } = options;
|
|
22
|
+
const mergedHeaders = { ...reqHeaders };
|
|
23
|
+
let fetchUrl = url;
|
|
24
|
+
if (credentials) {
|
|
25
|
+
const token = await credentials.value();
|
|
26
|
+
if (credentials.placement === Placement.query) {
|
|
27
|
+
const u = new URL(fetchUrl);
|
|
28
|
+
u.searchParams.set(credentials.parameterName ?? "token", token);
|
|
29
|
+
fetchUrl = u.toString();
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
mergedHeaders[credentials.parameterName ?? "Authorization"] = token;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const res = await fetch(fetchUrl, { headers: mergedHeaders });
|
|
36
|
+
const body = await bodyParser(res);
|
|
37
|
+
const resHeaders = Object.fromEntries(res.headers.entries());
|
|
38
|
+
const items = Array.isArray(body) ? body : [body];
|
|
39
|
+
return { status: res.status, headers: resHeaders, body, items };
|
|
40
|
+
}
|
|
41
|
+
export async function http(url, options = {}) {
|
|
42
|
+
const { pagination, ...fetchOpts } = options;
|
|
43
|
+
if (!pagination) {
|
|
44
|
+
return wrapFetch(url, fetchOpts);
|
|
45
|
+
}
|
|
46
|
+
const allItems = [];
|
|
47
|
+
let nextUrl = url;
|
|
48
|
+
while (nextUrl) {
|
|
49
|
+
const { body } = await wrapFetch(nextUrl, fetchOpts);
|
|
50
|
+
allItems.push(...(pagination.items({ body }) ?? []));
|
|
51
|
+
nextUrl = pagination.nextPage({ body });
|
|
52
|
+
}
|
|
53
|
+
return { status: null, headers: null, body: null, items: allItems };
|
|
54
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Gauge } from "prom-client";
|
|
2
|
+
/**
|
|
3
|
+
* Node Graph metrics for Grafana
|
|
4
|
+
* Docs: https://grafana.com/docs/grafana/latest/visualizations/panels-visualizations/visualizations/node-graph/
|
|
5
|
+
*
|
|
6
|
+
* Nodes frame fields: id, title, arc__success, arc__failed, detail__duration
|
|
7
|
+
* Edges frame fields: id, source, target
|
|
8
|
+
*/
|
|
9
|
+
export declare const jobDuration: Gauge<"id" | "title">;
|
|
10
|
+
export declare const jobArcSuccess: Gauge<"id">;
|
|
11
|
+
export declare const jobArcFailed: Gauge<"id">;
|
|
12
|
+
export declare const jobEdge: Gauge<"id" | "target" | "source">;
|
|
13
|
+
export declare const jobActive: Gauge<"id">;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Gauge } from "prom-client";
|
|
2
|
+
import { registry } from "./registry.js";
|
|
3
|
+
/**
|
|
4
|
+
* Node Graph metrics for Grafana
|
|
5
|
+
* Docs: https://grafana.com/docs/grafana/latest/visualizations/panels-visualizations/visualizations/node-graph/
|
|
6
|
+
*
|
|
7
|
+
* Nodes frame fields: id, title, arc__success, arc__failed, detail__duration
|
|
8
|
+
* Edges frame fields: id, source, target
|
|
9
|
+
*/
|
|
10
|
+
export const jobDuration = new Gauge({
|
|
11
|
+
name: "job_duration_seconds",
|
|
12
|
+
help: "Last execution duration of a job in seconds",
|
|
13
|
+
labelNames: ["id", "title"],
|
|
14
|
+
registers: [registry],
|
|
15
|
+
});
|
|
16
|
+
export const jobArcSuccess = new Gauge({
|
|
17
|
+
name: "job_arc_success",
|
|
18
|
+
help: "Arc ratio of successful executions (0-1) for node graph",
|
|
19
|
+
labelNames: ["id"],
|
|
20
|
+
registers: [registry],
|
|
21
|
+
});
|
|
22
|
+
export const jobArcFailed = new Gauge({
|
|
23
|
+
name: "job_arc_failed",
|
|
24
|
+
help: "Arc ratio of failed executions (0-1) for node graph",
|
|
25
|
+
labelNames: ["id"],
|
|
26
|
+
registers: [registry],
|
|
27
|
+
});
|
|
28
|
+
export const jobEdge = new Gauge({
|
|
29
|
+
name: "job_edge",
|
|
30
|
+
help: "Edge between two jobs in the node graph",
|
|
31
|
+
labelNames: ["id", "source", "target"],
|
|
32
|
+
registers: [registry],
|
|
33
|
+
});
|
|
34
|
+
export const jobActive = new Gauge({
|
|
35
|
+
name: "job_active",
|
|
36
|
+
help: "1 if the job is currently active, 0 otherwise",
|
|
37
|
+
labelNames: ["id"],
|
|
38
|
+
registers: [registry],
|
|
39
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { push } from "./push.js";
|
|
2
|
+
import { jobDuration, jobArcSuccess, jobArcFailed, jobEdge, jobActive, } from "./job-gauges.js";
|
|
3
|
+
const pendingStarts = new Map();
|
|
4
|
+
const counters = new Map();
|
|
5
|
+
export const startJob = (id) => {
|
|
6
|
+
pendingStarts.set(id, performance.now());
|
|
7
|
+
jobActive.set({ id }, 1);
|
|
8
|
+
push(id).catch(() => { });
|
|
9
|
+
};
|
|
10
|
+
export const endJob = (id, title, success) => {
|
|
11
|
+
jobActive.set({ id }, 0);
|
|
12
|
+
const startedAt = pendingStarts.get(id);
|
|
13
|
+
if (startedAt !== undefined) {
|
|
14
|
+
const duration = (performance.now() - startedAt) / 1000;
|
|
15
|
+
jobDuration.set({ id, title }, duration);
|
|
16
|
+
pendingStarts.delete(id);
|
|
17
|
+
}
|
|
18
|
+
const counts = counters.get(id) ?? { success: 0, failed: 0 };
|
|
19
|
+
if (success)
|
|
20
|
+
counts.success++;
|
|
21
|
+
else
|
|
22
|
+
counts.failed++;
|
|
23
|
+
counters.set(id, counts);
|
|
24
|
+
const total = counts.success + counts.failed;
|
|
25
|
+
jobArcSuccess.set({ id }, counts.success / total);
|
|
26
|
+
jobArcFailed.set({ id }, counts.failed / total);
|
|
27
|
+
push(id).catch(() => { });
|
|
28
|
+
};
|
|
29
|
+
export const recordEdge = (parentJobId, jobId) => {
|
|
30
|
+
jobEdge.set({ id: `${parentJobId}->${jobId}`, source: parentJobId, target: jobId }, 1);
|
|
31
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const push: (jobName: string) => Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const telemetryUrl: string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const telemetryUrl = process.env.TELEMETRY_URL;
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jondotsoy/pika",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"types": "./lib/esm/flow/index.d.ts",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./lib/esm/flow/index.d.ts",
|
|
9
|
+
"default": "./lib/esm/flow/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./loader": {
|
|
12
|
+
"types": "./lib/esm/flow/loader.d.ts",
|
|
13
|
+
"default": "./lib/esm/flow/loader.js"
|
|
14
|
+
},
|
|
15
|
+
"./http": {
|
|
16
|
+
"types": "./lib/esm/http/index.d.ts",
|
|
17
|
+
"default": "./lib/esm/http/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./auth": {
|
|
20
|
+
"types": "./lib/esm/auth/index.d.ts",
|
|
21
|
+
"default": "./lib/esm/auth/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"pika": "lib/esm/cli/index.js"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"pack": "npm pack",
|
|
29
|
+
"fmt": "prettier -w .",
|
|
30
|
+
"lint": "prettier -c .",
|
|
31
|
+
"prepack": "tsc -p tsconfig.esm.json"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@jondotsoy/flags": "3.2.1",
|
|
35
|
+
"@jondotsoy/style-text": "0.9.2",
|
|
36
|
+
"prom-client": "15.1.3",
|
|
37
|
+
"temporal-polyfill": "0.3.2",
|
|
38
|
+
"ulid": "3.0.2",
|
|
39
|
+
"yaml": "2.8.2",
|
|
40
|
+
"zod": "4.3.6"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/bun": "1.3.10",
|
|
44
|
+
"@types/node": "25.5.0",
|
|
45
|
+
"prettier": "3.8.1"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"typescript": "5.9.3"
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"lib/"
|
|
52
|
+
],
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"author": "Jonathan Delgado <hi@jon.soy> (https://jon.soy)"
|
|
55
|
+
}
|