@feltdb/core 0.2.0 → 0.3.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/LICENSE +19 -0
- package/dist/analytics-backend.d.ts +39 -0
- package/dist/analytics-backend.d.ts.map +1 -0
- package/dist/analytics-backend.js +152 -0
- package/dist/application-contract.d.ts +49 -0
- package/dist/application-contract.d.ts.map +1 -0
- package/dist/application-contract.js +13 -0
- package/dist/application-manifest.d.ts +305 -0
- package/dist/application-manifest.d.ts.map +1 -0
- package/dist/application-manifest.js +26 -0
- package/dist/artifact.d.ts +73 -0
- package/dist/artifact.d.ts.map +1 -0
- package/dist/artifact.js +21 -0
- package/dist/authorization.d.ts +99 -0
- package/dist/authorization.d.ts.map +1 -0
- package/dist/authorization.js +15 -0
- package/dist/bundle.d.ts +55 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +13 -0
- package/dist/collection.d.ts +28 -0
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +83 -0
- package/dist/db.d.ts +18 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +20 -8
- package/dist/distributed-indexing.d.ts +170 -0
- package/dist/distributed-indexing.d.ts.map +1 -0
- package/dist/distributed-indexing.js +260 -0
- package/dist/flowspec.d.ts +4 -0
- package/dist/flowspec.d.ts.map +1 -1
- package/dist/flowspec.js +8 -0
- package/dist/http-client.d.ts +35 -0
- package/dist/http-client.d.ts.map +1 -0
- package/dist/http-client.js +70 -0
- package/dist/identity.d.ts +40 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +10 -0
- package/dist/index-analytics.d.ts +121 -0
- package/dist/index-analytics.d.ts.map +1 -0
- package/dist/index-analytics.js +279 -0
- package/dist/index-backend.d.ts +26 -0
- package/dist/index-backend.d.ts.map +1 -0
- package/dist/index-backend.js +115 -0
- package/dist/index-dashboard.d.ts +114 -0
- package/dist/index-dashboard.d.ts.map +1 -0
- package/dist/index-dashboard.js +256 -0
- package/dist/index-manager.d.ts +18 -0
- package/dist/index-manager.d.ts.map +1 -0
- package/dist/index-manager.js +333 -0
- package/dist/index-monitoring.d.ts +133 -0
- package/dist/index-monitoring.d.ts.map +1 -0
- package/dist/index-monitoring.js +218 -0
- package/dist/index-recovery.d.ts +57 -0
- package/dist/index-recovery.d.ts.map +1 -0
- package/dist/index-recovery.js +117 -0
- package/dist/index-store.d.ts +62 -0
- package/dist/index-store.d.ts.map +1 -0
- package/dist/index-store.js +141 -0
- package/dist/index-types.d.ts +70 -0
- package/dist/index-types.d.ts.map +1 -0
- package/dist/index-types.js +6 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -0
- package/dist/observe.d.ts +51 -0
- package/dist/observe.d.ts.map +1 -0
- package/dist/observe.js +14 -0
- package/dist/protocol.d.ts +25 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +18 -0
- package/dist/provider.d.ts +61 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +17 -0
- package/dist/query-planner.d.ts +69 -0
- package/dist/query-planner.d.ts.map +1 -0
- package/dist/query-planner.js +184 -0
- package/dist/release.d.ts +60 -0
- package/dist/release.d.ts.map +1 -0
- package/dist/release.js +18 -0
- package/dist/sharding.d.ts +159 -0
- package/dist/sharding.d.ts.map +1 -0
- package/dist/sharding.js +317 -0
- package/dist/state-contract.d.ts +173 -0
- package/dist/state-contract.d.ts.map +1 -0
- package/dist/state-contract.js +24 -0
- package/dist/sync-contract.d.ts +101 -0
- package/dist/sync-contract.d.ts.map +1 -0
- package/dist/sync-contract.js +90 -0
- package/dist/worker.d.ts +90 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +26 -0
- package/dist/workload.d.ts +102 -0
- package/dist/workload.d.ts.map +1 -0
- package/dist/workload.js +23 -0
- package/package.json +17 -5
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
class StatusSignal {
|
|
2
|
+
constructor(value = 'offline') {
|
|
3
|
+
this.value = value;
|
|
4
|
+
this.listeners = new Set();
|
|
5
|
+
}
|
|
6
|
+
subscribe(listener) { this.listeners.add(listener); listener(this.value); return () => this.listeners.delete(listener); }
|
|
7
|
+
set(value) { this.value = value; for (const listener of this.listeners)
|
|
8
|
+
listener(value); }
|
|
9
|
+
get() { return this.value; }
|
|
10
|
+
}
|
|
11
|
+
export class SyncController {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.status = new StatusSignal();
|
|
15
|
+
this.fetcher = config.fetcher || fetch;
|
|
16
|
+
this.restore();
|
|
17
|
+
}
|
|
18
|
+
key() { return `feltdb:sync:v1:${this.config.device.application_id}:${this.config.device.device_id}`; }
|
|
19
|
+
restore() { if (typeof localStorage === 'undefined')
|
|
20
|
+
return; try {
|
|
21
|
+
const value = JSON.parse(localStorage.getItem(this.key()) || '{}');
|
|
22
|
+
this.sessionId = value.sessionId;
|
|
23
|
+
this.cursor = value.cursor;
|
|
24
|
+
this.error = value.error;
|
|
25
|
+
}
|
|
26
|
+
catch { } }
|
|
27
|
+
persist() { if (typeof localStorage !== 'undefined')
|
|
28
|
+
localStorage.setItem(this.key(), JSON.stringify({ sessionId: this.sessionId, cursor: this.cursor, error: this.error, outbox: this.readOutbox() })); }
|
|
29
|
+
readOutbox() { if (typeof localStorage === 'undefined')
|
|
30
|
+
return []; try {
|
|
31
|
+
return JSON.parse(localStorage.getItem(`${this.key()}:outbox`) || '[]');
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return [];
|
|
35
|
+
} }
|
|
36
|
+
writeOutbox(value) { if (typeof localStorage !== 'undefined')
|
|
37
|
+
localStorage.setItem(`${this.key()}:outbox`, JSON.stringify(value)); }
|
|
38
|
+
async request(path, body) { const response = await this.fetcher(`${this.config.endpoint || ''}/v1/sync/${path}`, { method: 'POST', credentials: 'include', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) }); const value = await response.json().catch(() => ({})); if (!response.ok)
|
|
39
|
+
throw new Error(value.error || `sync ${path} failed (${response.status})`); return value; }
|
|
40
|
+
async start() { this.status.set('connecting'); try {
|
|
41
|
+
if (!this.sessionId) {
|
|
42
|
+
const scope = { resources: this.config.scope.resources || this.config.scope.collections?.map(v => `flow://${this.config.device.application_id}/${v}/*`) || [], fields: this.config.scope.fields || {} };
|
|
43
|
+
const session = await this.request('session', { device: { ...this.config.device, created_at: Math.floor(Date.now() / 1000) }, scope, offline_grant: this.config.offlineGrant });
|
|
44
|
+
this.sessionId = session.session_id;
|
|
45
|
+
this.cursor = session.cursor;
|
|
46
|
+
this.persist();
|
|
47
|
+
}
|
|
48
|
+
await this.flush();
|
|
49
|
+
this.timer = setInterval(() => void this.flush(), 5000);
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
this.error = String(error);
|
|
54
|
+
this.status.set('degraded');
|
|
55
|
+
this.persist();
|
|
56
|
+
throw error;
|
|
57
|
+
} }
|
|
58
|
+
async stop() { if (this.timer)
|
|
59
|
+
clearInterval(this.timer); if (this.sessionId)
|
|
60
|
+
await this.request('close', { session_id: this.sessionId }); this.status.set('offline'); }
|
|
61
|
+
enqueue(operation) { const outbox = this.readOutbox(); if (!outbox.some(v => v.operation_id === operation.operation_id)) {
|
|
62
|
+
outbox.push(operation);
|
|
63
|
+
this.writeOutbox(outbox);
|
|
64
|
+
} return operation.operation_id; }
|
|
65
|
+
async flush() { if (!this.sessionId || !this.cursor)
|
|
66
|
+
return; this.status.set('syncing'); try {
|
|
67
|
+
const pending = this.readOutbox();
|
|
68
|
+
if (pending.length) {
|
|
69
|
+
const result = await this.request('push', { session_id: this.sessionId, operations: pending });
|
|
70
|
+
const terminal = new Set([...result.acknowledgements.map(v => v.operation_id), ...result.rejections.filter(v => !v.retryable).map(v => v.operation_id)]);
|
|
71
|
+
this.writeOutbox(pending.filter(v => !terminal.has(v.operation_id)));
|
|
72
|
+
this.cursor = result.cursor;
|
|
73
|
+
}
|
|
74
|
+
const pulled = await this.request('pull', { session_id: this.sessionId, cursor: this.cursor, limit: 250 });
|
|
75
|
+
this.cursor = pulled.cursor;
|
|
76
|
+
await this.request('ack', { session_id: this.sessionId, cursor: this.cursor });
|
|
77
|
+
this.error = undefined;
|
|
78
|
+
this.status.set('synced');
|
|
79
|
+
this.persist();
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
this.error = String(error);
|
|
83
|
+
this.status.set(typeof navigator !== 'undefined' && !navigator.onLine ? 'offline' : 'degraded');
|
|
84
|
+
this.persist();
|
|
85
|
+
} }
|
|
86
|
+
pending() { return this.readOutbox(); }
|
|
87
|
+
conflicts() { return this.readOutbox().filter((v) => v.status === 'CONFLICTED'); }
|
|
88
|
+
lastCursor() { return this.cursor; }
|
|
89
|
+
lastError() { return this.error; }
|
|
90
|
+
}
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { SignedGrant, AuthorizationSubject } from './authorization.js';
|
|
2
|
+
export type WorkerLifecycle = 'REGISTERED' | 'READY' | 'BUSY' | 'DRAINING' | 'OFFLINE' | 'RECOVERING';
|
|
3
|
+
export type WorkerHealth = 'HEALTHY' | 'DEGRADED' | 'UNHEALTHY' | 'UNKNOWN';
|
|
4
|
+
export interface WorkerRegistration {
|
|
5
|
+
tenant_id: string;
|
|
6
|
+
application_id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
runtime: 'wasm' | 'process' | 'container' | 'browser' | 'remote' | 'hosted' | 'edge' | 'customer';
|
|
9
|
+
version: string;
|
|
10
|
+
capabilities: string[];
|
|
11
|
+
workload_kinds?: string[];
|
|
12
|
+
resource_profile?: Record<string, number>;
|
|
13
|
+
labels?: Record<string, string>;
|
|
14
|
+
capacity: {
|
|
15
|
+
concurrency: number;
|
|
16
|
+
active?: number;
|
|
17
|
+
};
|
|
18
|
+
authorization_grant: SignedGrant;
|
|
19
|
+
registered_by: AuthorizationSubject;
|
|
20
|
+
registered_at?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface Worker {
|
|
23
|
+
worker_id: string;
|
|
24
|
+
tenant_id: string;
|
|
25
|
+
application_id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
runtime: string;
|
|
28
|
+
version: string;
|
|
29
|
+
declared_capabilities: string[];
|
|
30
|
+
granted_capabilities: string[];
|
|
31
|
+
workload_kinds: string[];
|
|
32
|
+
labels: Record<string, string>;
|
|
33
|
+
capacity: {
|
|
34
|
+
concurrency: number;
|
|
35
|
+
active: number;
|
|
36
|
+
};
|
|
37
|
+
health: WorkerHealth;
|
|
38
|
+
lifecycle: WorkerLifecycle;
|
|
39
|
+
last_seen: number;
|
|
40
|
+
capability_version: number;
|
|
41
|
+
claims: string[];
|
|
42
|
+
}
|
|
43
|
+
export interface WorkerPool {
|
|
44
|
+
pool_id: string;
|
|
45
|
+
tenant_id: string;
|
|
46
|
+
application_id?: string;
|
|
47
|
+
name: string;
|
|
48
|
+
capabilities: string[];
|
|
49
|
+
required_labels?: Record<string, string>;
|
|
50
|
+
worker_ids: string[];
|
|
51
|
+
max_concurrency?: number;
|
|
52
|
+
created_at: number;
|
|
53
|
+
}
|
|
54
|
+
declare class MeshTransport {
|
|
55
|
+
protected baseUrl: string;
|
|
56
|
+
protected fetcher: typeof fetch;
|
|
57
|
+
constructor(baseUrl?: string, fetcher?: typeof fetch);
|
|
58
|
+
protected json<T>(path: string, init?: RequestInit): Promise<T>;
|
|
59
|
+
}
|
|
60
|
+
export declare class WorkerClient extends MeshTransport {
|
|
61
|
+
register(input: WorkerRegistration): Promise<Worker>;
|
|
62
|
+
list(applicationId: string): Promise<{
|
|
63
|
+
workers: Worker[];
|
|
64
|
+
}>;
|
|
65
|
+
inspect(id: string): Promise<Worker>;
|
|
66
|
+
heartbeat(id: string, heartbeat: unknown, grant: SignedGrant): Promise<Worker>;
|
|
67
|
+
drain(id: string, grant: SignedGrant): Promise<Worker>;
|
|
68
|
+
recover(id: string, grant: SignedGrant): Promise<Worker>;
|
|
69
|
+
reconcile(id: string, reconciliation: unknown, grant: SignedGrant): Promise<unknown>;
|
|
70
|
+
}
|
|
71
|
+
export declare class WorkerPoolClient extends MeshTransport {
|
|
72
|
+
list(applicationId: string): Promise<{
|
|
73
|
+
pools: WorkerPool[];
|
|
74
|
+
}>;
|
|
75
|
+
create(pool: WorkerPool, grant: SignedGrant): Promise<WorkerPool>;
|
|
76
|
+
inspect(id: string): Promise<WorkerPool>;
|
|
77
|
+
}
|
|
78
|
+
export declare class MeshClient extends MeshTransport {
|
|
79
|
+
status(applicationId: string): Promise<{
|
|
80
|
+
workers: Worker[];
|
|
81
|
+
pools: WorkerPool[];
|
|
82
|
+
ready: number;
|
|
83
|
+
busy: number;
|
|
84
|
+
}>;
|
|
85
|
+
eligibleWorkers(workloadId: string): Promise<{
|
|
86
|
+
eligible_workers: string[];
|
|
87
|
+
}>;
|
|
88
|
+
}
|
|
89
|
+
export {};
|
|
90
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class MeshTransport {
|
|
2
|
+
constructor(baseUrl = '', fetcher = fetch) {
|
|
3
|
+
this.baseUrl = baseUrl;
|
|
4
|
+
this.fetcher = fetcher;
|
|
5
|
+
}
|
|
6
|
+
async json(path, init) { const response = await this.fetcher(`${this.baseUrl}${path}`, { credentials: 'include', headers: { 'content-type': 'application/json', ...(init?.headers || {}) }, ...init }); const value = response.status === 204 ? undefined : await response.json(); if (!response.ok)
|
|
7
|
+
throw new Error(value?.error || `Mesh request failed (${response.status})`); return value; }
|
|
8
|
+
}
|
|
9
|
+
export class WorkerClient extends MeshTransport {
|
|
10
|
+
register(input) { return this.json('/v1/workers/register', { method: 'POST', body: JSON.stringify({ ...input, registered_at: input.registered_at || Math.floor(Date.now() / 1000) }) }); }
|
|
11
|
+
list(applicationId) { return this.json(`/v1/workers?application_id=${encodeURIComponent(applicationId)}`); }
|
|
12
|
+
inspect(id) { return this.json(`/v1/workers/${encodeURIComponent(id)}`); }
|
|
13
|
+
heartbeat(id, heartbeat, grant) { return this.json(`/v1/workers/${encodeURIComponent(id)}/heartbeat`, { method: 'POST', body: JSON.stringify({ heartbeat, grant }) }); }
|
|
14
|
+
drain(id, grant) { return this.json(`/v1/workers/${encodeURIComponent(id)}/drain`, { method: 'POST', body: JSON.stringify({ grant }) }); }
|
|
15
|
+
recover(id, grant) { return this.json(`/v1/workers/${encodeURIComponent(id)}/recover`, { method: 'POST', body: JSON.stringify({ grant }) }); }
|
|
16
|
+
reconcile(id, reconciliation, grant) { return this.json(`/v1/workers/${encodeURIComponent(id)}/reconcile`, { method: 'POST', body: JSON.stringify({ reconciliation, grant }) }); }
|
|
17
|
+
}
|
|
18
|
+
export class WorkerPoolClient extends MeshTransport {
|
|
19
|
+
list(applicationId) { return this.json(`/v1/worker-pools?application_id=${encodeURIComponent(applicationId)}`); }
|
|
20
|
+
create(pool, grant) { return this.json('/v1/worker-pools', { method: 'POST', body: JSON.stringify({ pool, grant }) }); }
|
|
21
|
+
inspect(id) { return this.json(`/v1/worker-pools/${encodeURIComponent(id)}`); }
|
|
22
|
+
}
|
|
23
|
+
export class MeshClient extends MeshTransport {
|
|
24
|
+
async status(applicationId) { const [workers, pools] = await Promise.all([new WorkerClient(this.baseUrl, this.fetcher).list(applicationId), new WorkerPoolClient(this.baseUrl, this.fetcher).list(applicationId)]); return { workers: workers.workers, pools: pools.pools, ready: workers.workers.filter(v => v.lifecycle === 'READY').length, busy: workers.workers.filter(v => v.lifecycle === 'BUSY').length }; }
|
|
25
|
+
eligibleWorkers(workloadId) { return this.json(`/v1/workloads/${encodeURIComponent(workloadId)}/eligible-workers`); }
|
|
26
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { AuthorizationSubject, SignedGrant } from './authorization.js';
|
|
2
|
+
export type WorkloadState = 'CREATED' | 'READY' | 'CLAIMED' | 'RUNNING' | 'CHECKPOINTED' | 'RECOVERING' | 'CANCELLATION_REQUESTED' | 'SUCCEEDED' | 'FAILED' | 'CANCELLED' | 'TIMED_OUT' | 'DEAD_LETTER';
|
|
3
|
+
export interface WorkloadRetryPolicy {
|
|
4
|
+
max_attempts: number;
|
|
5
|
+
initial_delay_ms: number;
|
|
6
|
+
max_delay_ms: number;
|
|
7
|
+
backoff: 'fixed' | 'exponential';
|
|
8
|
+
retry_on: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface CreateWorkloadInput {
|
|
11
|
+
tenant_id: string;
|
|
12
|
+
application_id: string;
|
|
13
|
+
environment: string;
|
|
14
|
+
definition_id: string;
|
|
15
|
+
definition_revision: string;
|
|
16
|
+
trigger: string;
|
|
17
|
+
input: unknown;
|
|
18
|
+
requested_by: AuthorizationSubject;
|
|
19
|
+
authorization_snapshot: SignedGrant;
|
|
20
|
+
capability_snapshot: string[];
|
|
21
|
+
connection_scopes?: string[];
|
|
22
|
+
priority?: number;
|
|
23
|
+
idempotency_key: string;
|
|
24
|
+
retry_policy?: WorkloadRetryPolicy;
|
|
25
|
+
timeouts?: Record<string, number>;
|
|
26
|
+
dependencies?: Array<{
|
|
27
|
+
workload_id: string;
|
|
28
|
+
required_state?: WorkloadState;
|
|
29
|
+
}>;
|
|
30
|
+
created_at?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface Workload {
|
|
33
|
+
workload_id: string;
|
|
34
|
+
tenant_id: string;
|
|
35
|
+
application_id: string;
|
|
36
|
+
definition_id: string;
|
|
37
|
+
input: unknown;
|
|
38
|
+
input_hash: string;
|
|
39
|
+
priority: number;
|
|
40
|
+
state: WorkloadState;
|
|
41
|
+
attempt_count: number;
|
|
42
|
+
retry_count: number;
|
|
43
|
+
next_attempt_at?: number;
|
|
44
|
+
last_failure?: string;
|
|
45
|
+
current_claim?: WorkloadClaim;
|
|
46
|
+
execution_history: unknown[];
|
|
47
|
+
attempts: unknown[];
|
|
48
|
+
checkpoints: unknown[];
|
|
49
|
+
result?: WorkloadResult;
|
|
50
|
+
}
|
|
51
|
+
export interface WorkloadClaim {
|
|
52
|
+
claim_id: string;
|
|
53
|
+
workload_id: string;
|
|
54
|
+
worker_id: string;
|
|
55
|
+
lease_id: string;
|
|
56
|
+
fencing_token: number;
|
|
57
|
+
claimed_at: number;
|
|
58
|
+
expires_at: number;
|
|
59
|
+
}
|
|
60
|
+
export interface WorkloadResult {
|
|
61
|
+
result_id: string;
|
|
62
|
+
workload_id: string;
|
|
63
|
+
attempt_id: string;
|
|
64
|
+
status: string;
|
|
65
|
+
output: unknown;
|
|
66
|
+
output_hash: string;
|
|
67
|
+
artifacts: string[];
|
|
68
|
+
produced_by: string;
|
|
69
|
+
created_at: number;
|
|
70
|
+
}
|
|
71
|
+
export declare class WorkloadClient {
|
|
72
|
+
private baseUrl;
|
|
73
|
+
private fetcher;
|
|
74
|
+
constructor(baseUrl?: string, fetcher?: typeof fetch);
|
|
75
|
+
private json;
|
|
76
|
+
create(input: CreateWorkloadInput): Promise<Workload>;
|
|
77
|
+
list(applicationId: string): Promise<{
|
|
78
|
+
workloads: Workload[];
|
|
79
|
+
}>;
|
|
80
|
+
inspect(id: string): Promise<Workload>;
|
|
81
|
+
claim(id: string, input: {
|
|
82
|
+
worker_id: string;
|
|
83
|
+
lease_ms: number;
|
|
84
|
+
grant: SignedGrant;
|
|
85
|
+
}): Promise<WorkloadClaim>;
|
|
86
|
+
start(id: string, fence: unknown): Promise<void>;
|
|
87
|
+
heartbeat(id: string, input: unknown): Promise<WorkloadClaim>;
|
|
88
|
+
checkpoint(id: string, input: unknown): Promise<unknown>;
|
|
89
|
+
complete(id: string, input: unknown): Promise<WorkloadResult>;
|
|
90
|
+
fail(id: string, input: unknown): Promise<{
|
|
91
|
+
state: WorkloadState;
|
|
92
|
+
}>;
|
|
93
|
+
confirmCancelled(id: string, fence: unknown): Promise<void>;
|
|
94
|
+
executionContext(id: string, input: unknown): Promise<unknown>;
|
|
95
|
+
cancel(id: string, grant: SignedGrant): Promise<{
|
|
96
|
+
state: WorkloadState;
|
|
97
|
+
}>;
|
|
98
|
+
retry(id: string, grant: SignedGrant): Promise<void>;
|
|
99
|
+
history(id: string): Promise<unknown>;
|
|
100
|
+
result(id: string): Promise<WorkloadResult | undefined>;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=workload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workload.d.ts","sourceRoot":"","sources":["../src/workload.ts"],"names":[],"mappings":"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"}
|
package/dist/workload.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export class WorkloadClient {
|
|
2
|
+
constructor(baseUrl = '', fetcher = fetch) {
|
|
3
|
+
this.baseUrl = baseUrl;
|
|
4
|
+
this.fetcher = fetcher;
|
|
5
|
+
}
|
|
6
|
+
async json(path, init) { const response = await this.fetcher(`${this.baseUrl}${path}`, { credentials: 'include', headers: { 'content-type': 'application/json', ...(init?.headers || {}) }, ...init }); const value = response.status === 204 ? undefined : await response.json(); if (!response.ok)
|
|
7
|
+
throw new Error(value?.error || `Workload request failed (${response.status})`); return value; }
|
|
8
|
+
create(input) { return this.json('/v1/workloads', { method: 'POST', body: JSON.stringify({ ...input, created_at: input.created_at || Math.floor(Date.now() / 1000) }) }); }
|
|
9
|
+
list(applicationId) { return this.json(`/v1/workloads?application_id=${encodeURIComponent(applicationId)}`); }
|
|
10
|
+
inspect(id) { return this.json(`/v1/workloads/${encodeURIComponent(id)}`); }
|
|
11
|
+
claim(id, input) { return this.json(`/v1/workloads/${encodeURIComponent(id)}/claim`, { method: 'POST', body: JSON.stringify(input) }); }
|
|
12
|
+
start(id, fence) { return this.json(`/v1/workloads/${encodeURIComponent(id)}/start`, { method: 'POST', body: JSON.stringify(fence) }); }
|
|
13
|
+
heartbeat(id, input) { return this.json(`/v1/workloads/${encodeURIComponent(id)}/heartbeat`, { method: 'POST', body: JSON.stringify(input) }); }
|
|
14
|
+
checkpoint(id, input) { return this.json(`/v1/workloads/${encodeURIComponent(id)}/checkpoint`, { method: 'POST', body: JSON.stringify(input) }); }
|
|
15
|
+
complete(id, input) { return this.json(`/v1/workloads/${encodeURIComponent(id)}/complete`, { method: 'POST', body: JSON.stringify(input) }); }
|
|
16
|
+
fail(id, input) { return this.json(`/v1/workloads/${encodeURIComponent(id)}/fail`, { method: 'POST', body: JSON.stringify(input) }); }
|
|
17
|
+
confirmCancelled(id, fence) { return this.json(`/v1/workloads/${encodeURIComponent(id)}/cancelled`, { method: 'POST', body: JSON.stringify(fence) }); }
|
|
18
|
+
executionContext(id, input) { return this.json(`/v1/workloads/${encodeURIComponent(id)}/context`, { method: 'POST', body: JSON.stringify(input) }); }
|
|
19
|
+
cancel(id, grant) { return this.json(`/v1/workloads/${encodeURIComponent(id)}/cancel`, { method: 'POST', body: JSON.stringify({ grant }) }); }
|
|
20
|
+
retry(id, grant) { return this.json(`/v1/workloads/${encodeURIComponent(id)}/retry`, { method: 'POST', body: JSON.stringify({ grant }) }); }
|
|
21
|
+
history(id) { return this.json(`/v1/workloads/${encodeURIComponent(id)}/history`); }
|
|
22
|
+
result(id) { return this.json(`/v1/workloads/${encodeURIComponent(id)}/result`); }
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@feltdb/core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "FeltDB Core -
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "FeltDB Core - Application-facing database API with state-first reactivity",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -19,14 +19,26 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
|
-
"dist/"
|
|
22
|
+
"dist/",
|
|
23
|
+
"README.md"
|
|
23
24
|
],
|
|
24
25
|
"scripts": {
|
|
25
26
|
"build": "rm -rf dist && tsc",
|
|
26
|
-
"test": "node ../../test-runner.ts"
|
|
27
|
+
"test": "node ../../test-runner.ts",
|
|
28
|
+
"type-check": "tsc --noEmit"
|
|
27
29
|
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"feltdb",
|
|
32
|
+
"database",
|
|
33
|
+
"reactive",
|
|
34
|
+
"state-first",
|
|
35
|
+
"collections",
|
|
36
|
+
"workflows",
|
|
37
|
+
"sync"
|
|
38
|
+
],
|
|
39
|
+
"author": "FeltDB Contributors",
|
|
28
40
|
"dependencies": {
|
|
29
|
-
"@feltdb/wasm": "^0.
|
|
41
|
+
"@feltdb/wasm": "^0.3.0"
|
|
30
42
|
},
|
|
31
43
|
"devDependencies": {
|
|
32
44
|
"typescript": "^5.0.0",
|