@ironflow/browser 0.31.0 → 0.33.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 +17 -13
- package/README.md +289 -45
- package/dist/client.d.ts +49 -49
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +329 -97
- package/dist/client.js.map +1 -1
- package/dist/config-client.d.ts +3 -14
- package/dist/config-client.d.ts.map +1 -1
- package/dist/config-client.js +3 -24
- package/dist/config-client.js.map +1 -1
- package/dist/config.d.ts +4 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/kv.d.ts.map +1 -1
- package/dist/kv.js +2 -4
- package/dist/kv.js.map +1 -1
- package/dist/subscription.d.ts +1 -0
- package/dist/subscription.d.ts.map +1 -1
- package/dist/subscription.js +64 -13
- package/dist/subscription.js.map +1 -1
- package/dist/transport/connectrpc.d.ts +1 -0
- package/dist/transport/connectrpc.d.ts.map +1 -1
- package/dist/transport/connectrpc.js +51 -30
- package/dist/transport/connectrpc.js.map +1 -1
- package/dist/transport/types.d.ts +4 -4
- package/dist/transport/types.d.ts.map +1 -1
- package/dist/transport/websocket.d.ts +7 -0
- package/dist/transport/websocket.d.ts.map +1 -1
- package/dist/transport/websocket.js +158 -37
- package/dist/transport/websocket.js.map +1 -1
- package/dist/websocket-auth.d.ts +11 -0
- package/dist/websocket-auth.d.ts.map +1 -0
- package/dist/websocket-auth.js +29 -0
- package/dist/websocket-auth.js.map +1 -0
- package/package.json +2 -2
package/dist/client.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Singleton client for browser-based real-time interactions with Ironflow.
|
|
5
5
|
*/
|
|
6
|
-
import { NotConfiguredError, IronflowError, RunFailedError, RunCancelledError, UnauthenticatedError, EnterpriseRequiredError, UnauthorizedError, ValidationError, createLogger, createNoopLogger, DEFAULT_TIMEOUTS, HEADERS, TriggerResponseSchema, TriggerSyncResponseSchema, RunResponseSchema, ListRunsResponseSchema, RunStatusSchema, ErrorResponseSchema, safeJsonParse, patterns, peelProjectionEnvelope, webhookVerifyConfigToWire, webhookGraceToWire, webhookSourceFromWire, webhookDeliveryFromWire, } from "@ironflow/core";
|
|
7
|
-
import { mergeConfig } from "./config.js";
|
|
6
|
+
import { NotConfiguredError, IronflowError, RunFailedError, RunCancelledError, UnauthenticatedError, EnterpriseRequiredError, UnauthorizedError, ValidationError, createLogger, createNoopLogger, DEFAULT_TIMEOUTS, HEADERS, TriggerResponseSchema, TriggerSyncResponseSchema, RunResponseSchema, ListRunsResponseSchema, RunStatusSchema, ErrorResponseSchema, safeJsonParse, patterns, peelProjectionEnvelope, webhookVerifyConfigToWire, webhookGraceToWire, webhookSourceFromWire, webhookDeliveryFromWire, registeredFunctionFromWire, functionHistoryEntryFromWire, storedEventFromWire, runStepFromWire, consumerGroupFromWire, } from "@ironflow/core";
|
|
7
|
+
import { mergeConfig, warnAboutLongLivedBrowserKey } from "./config.js";
|
|
8
8
|
import { SubscriptionManager, } from "./subscription.js";
|
|
9
9
|
import { createWebSocketTransport } from "./transport/websocket.js";
|
|
10
10
|
import { createConnectRPCTransport } from "./transport/connectrpc.js";
|
|
@@ -12,6 +12,18 @@ import { filterWaitStreamFrames } from "./projection-stream.js";
|
|
|
12
12
|
import { BrowserKVClient } from "./kv.js";
|
|
13
13
|
import { BrowserConfigClient } from "./config-client.js";
|
|
14
14
|
import { createAgentsNamespace } from "./agents/index.js";
|
|
15
|
+
function auditEventFromWire(raw) {
|
|
16
|
+
return {
|
|
17
|
+
id: String(raw.id ?? ""),
|
|
18
|
+
runId: String(raw.run_id ?? raw.runId ?? ""),
|
|
19
|
+
functionId: String(raw.function_id ?? raw.functionId ?? ""),
|
|
20
|
+
stepId: (raw.step_id ?? raw.stepId),
|
|
21
|
+
eventType: String(raw.event_type ?? raw.eventType ?? ""),
|
|
22
|
+
payload: raw.payload ?? {},
|
|
23
|
+
metadata: raw.metadata,
|
|
24
|
+
createdAt: String(raw.created_at ?? raw.createdAt ?? ""),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
15
27
|
/**
|
|
16
28
|
* Key for the offline queue's replay entry point.
|
|
17
29
|
*
|
|
@@ -71,6 +83,7 @@ class IronflowClient {
|
|
|
71
83
|
*/
|
|
72
84
|
configure(options = {}) {
|
|
73
85
|
this.config = mergeConfig(options);
|
|
86
|
+
warnAboutLongLivedBrowserKey(this.config.auth);
|
|
74
87
|
// Set up logger
|
|
75
88
|
if (this.config.logger === false) {
|
|
76
89
|
this.logger = createNoopLogger();
|
|
@@ -326,6 +339,21 @@ class IronflowClient {
|
|
|
326
339
|
const response = await this.request(RunResponseSchema, "POST", "/ironflow.v1.IronflowService/GetRun", { id: runId });
|
|
327
340
|
return this.mapRunResponse(response);
|
|
328
341
|
}
|
|
342
|
+
/** Get the durable steps recorded for a run. */
|
|
343
|
+
async getRunSteps(runId) {
|
|
344
|
+
this.ensureConfigured();
|
|
345
|
+
const response = await this.restRequest("GET", `/api/v1/runs/${encodeURIComponent(runId)}/steps`);
|
|
346
|
+
return {
|
|
347
|
+
steps: (response.steps ?? []).map(runStepFromWire),
|
|
348
|
+
count: response.count ?? 0,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
/** Get the entity stream IDs touched by a run. */
|
|
352
|
+
async getRunStreams(runId) {
|
|
353
|
+
this.ensureConfigured();
|
|
354
|
+
const response = await this.restRequest("GET", `/api/v1/runs/${encodeURIComponent(runId)}/streams`);
|
|
355
|
+
return { entityIds: response.entity_ids ?? [] };
|
|
356
|
+
}
|
|
329
357
|
/**
|
|
330
358
|
* List runs with filtering
|
|
331
359
|
*/
|
|
@@ -671,9 +699,61 @@ class IronflowClient {
|
|
|
671
699
|
clearTimeout(timeoutId);
|
|
672
700
|
}
|
|
673
701
|
}
|
|
674
|
-
/**
|
|
675
|
-
|
|
676
|
-
|
|
702
|
+
/** Get a registered function by ID. */
|
|
703
|
+
async getFunction(functionId) {
|
|
704
|
+
this.ensureConfigured();
|
|
705
|
+
const response = await this.streamRequest("/ironflow.v1.IronflowService/GetFunction", { id: functionId });
|
|
706
|
+
return registeredFunctionFromWire(response);
|
|
707
|
+
}
|
|
708
|
+
/** Change a function's lifecycle status. */
|
|
709
|
+
async updateFunctionStatus(functionId, status) {
|
|
710
|
+
this.ensureConfigured();
|
|
711
|
+
const response = await this.streamRequest("/ironflow.v1.IronflowService/UpdateFunctionStatus", {
|
|
712
|
+
id: functionId,
|
|
713
|
+
status: `FUNCTION_STATUS_${status.toUpperCase()}`,
|
|
714
|
+
});
|
|
715
|
+
return registeredFunctionFromWire(response);
|
|
716
|
+
}
|
|
717
|
+
/** Permanently delete a registered function. */
|
|
718
|
+
async deleteFunction(functionId) {
|
|
719
|
+
this.ensureConfigured();
|
|
720
|
+
await this.streamRequest("/ironflow.v1.IronflowService/DeleteFunction", { id: functionId });
|
|
721
|
+
}
|
|
722
|
+
/** List immutable configuration snapshots, newest first. */
|
|
723
|
+
async listFunctionHistory(functionId, options = {}) {
|
|
724
|
+
this.ensureConfigured();
|
|
725
|
+
const response = await this.streamRequest("/ironflow.v1.IronflowService/ListFunctionHistory", {
|
|
726
|
+
functionId,
|
|
727
|
+
...(options.limit !== undefined ? { limit: options.limit } : {}),
|
|
728
|
+
...(options.fromVersion !== undefined
|
|
729
|
+
? { fromVersion: String(options.fromVersion) }
|
|
730
|
+
: {}),
|
|
731
|
+
});
|
|
732
|
+
return {
|
|
733
|
+
entries: (response.entries ?? []).map(functionHistoryEntryFromWire),
|
|
734
|
+
hasMore: response.hasMore ?? false,
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
/** Get one historical configuration snapshot by entity version. */
|
|
738
|
+
async getFunctionAtVersion(functionId, version) {
|
|
739
|
+
this.ensureConfigured();
|
|
740
|
+
const response = await this.streamRequest("/ironflow.v1.IronflowService/GetFunctionAtVersion", {
|
|
741
|
+
functionId,
|
|
742
|
+
version: String(version),
|
|
743
|
+
});
|
|
744
|
+
return functionHistoryEntryFromWire(response.entry);
|
|
745
|
+
}
|
|
746
|
+
/** Restore a function's configuration from a historical version. */
|
|
747
|
+
async rollbackFunction(functionId, version, changeReason) {
|
|
748
|
+
this.ensureConfigured();
|
|
749
|
+
const response = await this.streamRequest("/ironflow.v1.IronflowService/RollbackFunction", {
|
|
750
|
+
functionId,
|
|
751
|
+
version: String(version),
|
|
752
|
+
...(changeReason ? { changeReason } : {}),
|
|
753
|
+
});
|
|
754
|
+
return registeredFunctionFromWire(response.function);
|
|
755
|
+
}
|
|
756
|
+
/** List registered functions. */
|
|
677
757
|
async listFunctions() {
|
|
678
758
|
this.ensureConfigured();
|
|
679
759
|
const url = `${this.config.serverUrl}/api/v1/functions`;
|
|
@@ -804,6 +884,104 @@ class IronflowClient {
|
|
|
804
884
|
eventId: response.eventId,
|
|
805
885
|
};
|
|
806
886
|
}
|
|
887
|
+
/**
|
|
888
|
+
* Publish a message to a developer pub/sub topic.
|
|
889
|
+
* Unlike emit(), this does not trigger workflow functions.
|
|
890
|
+
*/
|
|
891
|
+
async publish(topic, data, options) {
|
|
892
|
+
this.ensureConfigured();
|
|
893
|
+
const response = await this.streamRequest("/ironflow.v1.PubSubService/Publish", {
|
|
894
|
+
topic,
|
|
895
|
+
data: data ?? {},
|
|
896
|
+
...(options?.idempotencyKey
|
|
897
|
+
? { idempotencyKey: options.idempotencyKey }
|
|
898
|
+
: {}),
|
|
899
|
+
});
|
|
900
|
+
return {
|
|
901
|
+
eventId: response.eventId,
|
|
902
|
+
sequence: Number(response.sequence) || 0,
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
/** Trigger multiple events in one request. */
|
|
906
|
+
async triggerBatch(events) {
|
|
907
|
+
this.ensureConfigured();
|
|
908
|
+
const response = await this.streamRequest("/ironflow.v1.IronflowService/TriggerBatch", {
|
|
909
|
+
events: events.map((event) => ({
|
|
910
|
+
event: event.event,
|
|
911
|
+
data: event.data,
|
|
912
|
+
...(event.version !== undefined ? { version: event.version } : {}),
|
|
913
|
+
...(event.idempotencyKey
|
|
914
|
+
? { idempotencyKey: event.idempotencyKey }
|
|
915
|
+
: {}),
|
|
916
|
+
...(event.metadata ? { metadata: event.metadata } : {}),
|
|
917
|
+
})),
|
|
918
|
+
});
|
|
919
|
+
return (response.results ?? []).map((result) => ({
|
|
920
|
+
runIds: result.runIds ?? [],
|
|
921
|
+
eventId: result.eventId,
|
|
922
|
+
}));
|
|
923
|
+
}
|
|
924
|
+
/** Read a keyset-paginated page from the event log. */
|
|
925
|
+
async listEvents(options = {}) {
|
|
926
|
+
this.ensureConfigured();
|
|
927
|
+
const query = new URLSearchParams();
|
|
928
|
+
if (options.name)
|
|
929
|
+
query.set("name", options.name);
|
|
930
|
+
if (options.names?.length)
|
|
931
|
+
query.set("names", options.names.join(","));
|
|
932
|
+
if (options.sources?.length)
|
|
933
|
+
query.set("source", options.sources.join(","));
|
|
934
|
+
if (options.search)
|
|
935
|
+
query.set("search", options.search);
|
|
936
|
+
if (options.since)
|
|
937
|
+
query.set("since", options.since);
|
|
938
|
+
if (options.until)
|
|
939
|
+
query.set("until", options.until);
|
|
940
|
+
if (options.limit !== undefined)
|
|
941
|
+
query.set("limit", String(options.limit));
|
|
942
|
+
if (options.cursor)
|
|
943
|
+
query.set("cursor", options.cursor);
|
|
944
|
+
if (options.before)
|
|
945
|
+
query.set("before", options.before);
|
|
946
|
+
const suffix = query.size ? `?${query}` : "";
|
|
947
|
+
const response = await this.restRequest("GET", `/api/v1/events${suffix}`);
|
|
948
|
+
return {
|
|
949
|
+
events: (response.events ?? []).map(storedEventFromWire),
|
|
950
|
+
count: response.count ?? 0,
|
|
951
|
+
limit: response.limit ?? options.limit ?? 20,
|
|
952
|
+
nextCursor: response.next_cursor,
|
|
953
|
+
prevCursor: response.prev_cursor,
|
|
954
|
+
hasNext: response.has_next ?? false,
|
|
955
|
+
hasPrev: response.has_prev ?? false,
|
|
956
|
+
approxTotal: response.approx_total,
|
|
957
|
+
approxTotalCapped: response.approx_total_capped ?? false,
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
/** Get one persisted event by ID. */
|
|
961
|
+
async getEvent(eventId) {
|
|
962
|
+
this.ensureConfigured();
|
|
963
|
+
const response = await this.restRequest("GET", `/api/v1/events/${encodeURIComponent(eventId)}`);
|
|
964
|
+
return storedEventFromWire(response);
|
|
965
|
+
}
|
|
966
|
+
/** List event names and counts for filter UIs. */
|
|
967
|
+
async listEventNames(options = {}) {
|
|
968
|
+
this.ensureConfigured();
|
|
969
|
+
const query = new URLSearchParams();
|
|
970
|
+
if (options.sources?.length)
|
|
971
|
+
query.set("source", options.sources.join(","));
|
|
972
|
+
if (options.since)
|
|
973
|
+
query.set("since", options.since);
|
|
974
|
+
if (options.until)
|
|
975
|
+
query.set("until", options.until);
|
|
976
|
+
const suffix = query.size ? `?${query}` : "";
|
|
977
|
+
const response = await this.restRequest("GET", `/api/v1/events/names${suffix}`);
|
|
978
|
+
return {
|
|
979
|
+
names: response.names ?? [],
|
|
980
|
+
scanned: response.scanned ?? 0,
|
|
981
|
+
truncated: response.truncated ?? false,
|
|
982
|
+
scanCap: response.scan_cap ?? 0,
|
|
983
|
+
};
|
|
984
|
+
}
|
|
807
985
|
/**
|
|
808
986
|
* Emit an event synchronously — waits for the triggered run to complete and returns the result.
|
|
809
987
|
*
|
|
@@ -857,6 +1035,84 @@ class IronflowClient {
|
|
|
857
1035
|
});
|
|
858
1036
|
return sub;
|
|
859
1037
|
}
|
|
1038
|
+
/** Consumer-group lifecycle management. */
|
|
1039
|
+
consumerGroups = {
|
|
1040
|
+
create: async (config) => {
|
|
1041
|
+
this.ensureConfigured();
|
|
1042
|
+
const response = await this.streamRequest("/ironflow.v1.PubSubService/CreateConsumerGroup", {
|
|
1043
|
+
name: config.name,
|
|
1044
|
+
pattern: config.pattern,
|
|
1045
|
+
namespace: config.namespace ?? "default",
|
|
1046
|
+
...(config.filterExpr !== undefined ? { filter_expr: config.filterExpr } : {}),
|
|
1047
|
+
...(config.ackMode ? { ack_mode: `ACK_MODE_${config.ackMode.toUpperCase()}` } : {}),
|
|
1048
|
+
...(config.backpressure
|
|
1049
|
+
? { backpressure: `BACKPRESSURE_MODE_${config.backpressure.toUpperCase()}` }
|
|
1050
|
+
: {}),
|
|
1051
|
+
...(config.maxInflight !== undefined ? { max_inflight: config.maxInflight } : {}),
|
|
1052
|
+
...(config.maxRedeliveries !== undefined
|
|
1053
|
+
? { max_redeliveries: config.maxRedeliveries }
|
|
1054
|
+
: {}),
|
|
1055
|
+
...(config.redeliverDelayMs !== undefined
|
|
1056
|
+
? { redeliver_delay_ms: config.redeliverDelayMs }
|
|
1057
|
+
: {}),
|
|
1058
|
+
...(config.metadata !== undefined ? { metadata: config.metadata } : {}),
|
|
1059
|
+
});
|
|
1060
|
+
return consumerGroupFromWire(response);
|
|
1061
|
+
},
|
|
1062
|
+
get: async (name, namespace = "default") => {
|
|
1063
|
+
this.ensureConfigured();
|
|
1064
|
+
const response = await this.streamRequest("/ironflow.v1.PubSubService/GetConsumerGroup", { name, namespace });
|
|
1065
|
+
return consumerGroupFromWire(response);
|
|
1066
|
+
},
|
|
1067
|
+
list: async (namespace) => {
|
|
1068
|
+
this.ensureConfigured();
|
|
1069
|
+
const groups = [];
|
|
1070
|
+
let cursor;
|
|
1071
|
+
do {
|
|
1072
|
+
const response = await this.streamRequest("/ironflow.v1.PubSubService/ListConsumerGroups", {
|
|
1073
|
+
...(namespace ? { namespace } : {}),
|
|
1074
|
+
limit: 100,
|
|
1075
|
+
...(cursor ? { cursor } : {}),
|
|
1076
|
+
});
|
|
1077
|
+
groups.push(...(response.groups ?? []).map(consumerGroupFromWire));
|
|
1078
|
+
cursor = response.nextCursor || response.next_cursor || undefined;
|
|
1079
|
+
} while (cursor);
|
|
1080
|
+
return groups;
|
|
1081
|
+
},
|
|
1082
|
+
update: async (name, input, namespace = "default") => {
|
|
1083
|
+
this.ensureConfigured();
|
|
1084
|
+
const group = { name, namespace };
|
|
1085
|
+
const paths = [];
|
|
1086
|
+
const set = (path, value) => {
|
|
1087
|
+
if (value !== undefined) {
|
|
1088
|
+
group[path] = value;
|
|
1089
|
+
paths.push(path);
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
set("pattern", input.pattern);
|
|
1093
|
+
set("filter_expr", input.filterExpr);
|
|
1094
|
+
set("ack_mode", input.ackMode ? `ACK_MODE_${input.ackMode.toUpperCase()}` : undefined);
|
|
1095
|
+
set("backpressure", input.backpressure
|
|
1096
|
+
? `BACKPRESSURE_MODE_${input.backpressure.toUpperCase()}`
|
|
1097
|
+
: undefined);
|
|
1098
|
+
set("max_inflight", input.maxInflight);
|
|
1099
|
+
set("max_redeliveries", input.maxRedeliveries);
|
|
1100
|
+
set("redeliver_delay_ms", input.redeliverDelayMs);
|
|
1101
|
+
set("metadata", input.metadata);
|
|
1102
|
+
set("status", input.status
|
|
1103
|
+
? `CONSUMER_GROUP_STATUS_${input.status.toUpperCase()}`
|
|
1104
|
+
: undefined);
|
|
1105
|
+
if (paths.length === 0) {
|
|
1106
|
+
throw new ValidationError("Consumer group update requires at least one field");
|
|
1107
|
+
}
|
|
1108
|
+
const response = await this.streamRequest("/ironflow.v1.PubSubService/UpdateConsumerGroup", { group, update_mask: { paths } });
|
|
1109
|
+
return consumerGroupFromWire(response);
|
|
1110
|
+
},
|
|
1111
|
+
delete: async (name, namespace = "default") => {
|
|
1112
|
+
this.ensureConfigured();
|
|
1113
|
+
await this.streamRequest("/ironflow.v1.PubSubService/DeleteConsumerGroup", { name, namespace });
|
|
1114
|
+
},
|
|
1115
|
+
};
|
|
860
1116
|
// ============================================================================
|
|
861
1117
|
// Entity Streams
|
|
862
1118
|
// ============================================================================
|
|
@@ -995,6 +1251,29 @@ class IronflowClient {
|
|
|
995
1251
|
createdAt: response.createdAt ?? "",
|
|
996
1252
|
};
|
|
997
1253
|
},
|
|
1254
|
+
/** List entity streams visible in the current environment. */
|
|
1255
|
+
listStreams: async () => {
|
|
1256
|
+
this.ensureConfigured();
|
|
1257
|
+
const response = await this.restRequest("GET", "/api/v1/streams");
|
|
1258
|
+
return (response.streams ?? []).map((stream) => ({
|
|
1259
|
+
entityId: String(stream.entity_id ?? stream.entityId ?? ""),
|
|
1260
|
+
entityType: String(stream.entity_type ?? stream.entityType ?? ""),
|
|
1261
|
+
version: Number(stream.version ?? 0),
|
|
1262
|
+
eventCount: Number(stream.event_count ?? stream.eventCount ?? 0),
|
|
1263
|
+
lastEventAt: String(stream.last_event_at ?? stream.lastEventAt ?? stream.updated_at ?? ""),
|
|
1264
|
+
}));
|
|
1265
|
+
},
|
|
1266
|
+
/** Get the unified event history for an entity. */
|
|
1267
|
+
getEntityHistory: async (entityId) => {
|
|
1268
|
+
this.ensureConfigured();
|
|
1269
|
+
const response = await this.restRequest("GET", `/api/v1/streams/${encodeURIComponent(entityId)}/history`);
|
|
1270
|
+
return (response.entries ?? []).map((entry) => ({
|
|
1271
|
+
eventName: String(entry.event_name ?? entry.eventName ?? ""),
|
|
1272
|
+
data: entry.event_data ?? entry.data,
|
|
1273
|
+
version: Number(entry.entity_version ?? entry.version ?? 0),
|
|
1274
|
+
timestamp: String(entry.timestamp ?? ""),
|
|
1275
|
+
}));
|
|
1276
|
+
},
|
|
998
1277
|
/**
|
|
999
1278
|
* Subscribe to real-time events for an entity stream
|
|
1000
1279
|
*
|
|
@@ -1156,46 +1435,6 @@ class IronflowClient {
|
|
|
1156
1435
|
clearTimeout(timeoutId);
|
|
1157
1436
|
}
|
|
1158
1437
|
}
|
|
1159
|
-
/**
|
|
1160
|
-
* Trigger a rebuild of a projection
|
|
1161
|
-
*
|
|
1162
|
-
* @example
|
|
1163
|
-
* ```typescript
|
|
1164
|
-
* const result = await ironflow.rebuildProjection('order-stats');
|
|
1165
|
-
* ```
|
|
1166
|
-
*/
|
|
1167
|
-
async rebuildProjection(name, options) {
|
|
1168
|
-
this.ensureConfigured();
|
|
1169
|
-
const url = `${this.config.serverUrl}/api/v1/projections/${encodeURIComponent(name)}/rebuild`;
|
|
1170
|
-
const timeout = this.config.timeout ?? DEFAULT_TIMEOUTS.CLIENT;
|
|
1171
|
-
const controller = new AbortController();
|
|
1172
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
1173
|
-
try {
|
|
1174
|
-
const headers = {
|
|
1175
|
-
"Content-Type": "application/json",
|
|
1176
|
-
[HEADERS.ENVIRONMENT]: this.config.environment,
|
|
1177
|
-
};
|
|
1178
|
-
this.applyAuthHeader(headers);
|
|
1179
|
-
const response = await fetch(url, {
|
|
1180
|
-
method: "POST",
|
|
1181
|
-
headers,
|
|
1182
|
-
body: JSON.stringify({
|
|
1183
|
-
partition: options?.partition,
|
|
1184
|
-
from_event_id: options?.fromEventId,
|
|
1185
|
-
dry_run: options?.dryRun,
|
|
1186
|
-
}),
|
|
1187
|
-
signal: controller.signal,
|
|
1188
|
-
});
|
|
1189
|
-
if (!response.ok) {
|
|
1190
|
-
const error = safeJsonParse(await response.text());
|
|
1191
|
-
throw new IronflowError(error?.message || `Rebuild projection failed: ${response.status}`, { code: error?.code || "REBUILD_PROJECTION_FAILED" });
|
|
1192
|
-
}
|
|
1193
|
-
return response.json();
|
|
1194
|
-
}
|
|
1195
|
-
finally {
|
|
1196
|
-
clearTimeout(timeoutId);
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
1438
|
/**
|
|
1200
1439
|
* Wait until the named projection has processed events up to `minSeq`,
|
|
1201
1440
|
* or the timeout elapses. Read-your-writes primitive for CQRS UIs:
|
|
@@ -1461,11 +1700,8 @@ class IronflowClient {
|
|
|
1461
1700
|
* @example
|
|
1462
1701
|
* ```typescript
|
|
1463
1702
|
* const cfg = ironflow.configManager();
|
|
1464
|
-
* await cfg.set("app", { featureX: true, maxRetries: 3 });
|
|
1465
1703
|
* const { data, revision } = await cfg.get("app");
|
|
1466
|
-
* await cfg.patch("app", { maxRetries: 5 });
|
|
1467
1704
|
* const configs = await cfg.list();
|
|
1468
|
-
* await cfg.delete("app");
|
|
1469
1705
|
*
|
|
1470
1706
|
* // Watch for changes
|
|
1471
1707
|
* const sub = await cfg.watch("app", {
|
|
@@ -1652,6 +1888,12 @@ class IronflowClient {
|
|
|
1652
1888
|
this.ensureConfigured();
|
|
1653
1889
|
await this.restRequest("DELETE", `/api/v1/roles/${encodeURIComponent(roleId)}/policies/${encodeURIComponent(policyId)}`);
|
|
1654
1890
|
},
|
|
1891
|
+
/** List policies assigned to a role. */
|
|
1892
|
+
listPolicies: async (roleId) => {
|
|
1893
|
+
this.ensureConfigured();
|
|
1894
|
+
const response = await this.restRequest("GET", `/api/v1/roles/${encodeURIComponent(roleId)}/policies`);
|
|
1895
|
+
return response.policies ?? [];
|
|
1896
|
+
},
|
|
1655
1897
|
};
|
|
1656
1898
|
/**
|
|
1657
1899
|
* Policy management operations (enterprise-only)
|
|
@@ -1816,6 +2058,32 @@ class IronflowClient {
|
|
|
1816
2058
|
nextCursor: response.next_cursor,
|
|
1817
2059
|
};
|
|
1818
2060
|
}
|
|
2061
|
+
/** Query the environment-wide audit stream. */
|
|
2062
|
+
async listAuditEvents(options = {}) {
|
|
2063
|
+
this.ensureConfigured();
|
|
2064
|
+
const query = new URLSearchParams();
|
|
2065
|
+
if (options.runId)
|
|
2066
|
+
query.set("run_id", options.runId);
|
|
2067
|
+
if (options.functionId)
|
|
2068
|
+
query.set("function_id", options.functionId);
|
|
2069
|
+
if (options.eventType)
|
|
2070
|
+
query.set("event_type", options.eventType);
|
|
2071
|
+
if (options.fromTimestamp)
|
|
2072
|
+
query.set("from", options.fromTimestamp);
|
|
2073
|
+
if (options.toTimestamp)
|
|
2074
|
+
query.set("to", options.toTimestamp);
|
|
2075
|
+
if (options.limit !== undefined)
|
|
2076
|
+
query.set("limit", String(options.limit));
|
|
2077
|
+
if (options.cursor)
|
|
2078
|
+
query.set("cursor", options.cursor);
|
|
2079
|
+
const suffix = query.size ? `?${query}` : "";
|
|
2080
|
+
const response = await this.restRequest("GET", `/api/v1/audit${suffix}`);
|
|
2081
|
+
return {
|
|
2082
|
+
events: (response.events ?? []).map(auditEventFromWire),
|
|
2083
|
+
totalCount: response.total_count ?? 0,
|
|
2084
|
+
nextCursor: response.next_cursor || undefined,
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
1819
2087
|
// ============================================================================
|
|
1820
2088
|
// Webhook Management
|
|
1821
2089
|
// ============================================================================
|
|
@@ -1963,55 +2231,6 @@ class IronflowClient {
|
|
|
1963
2231
|
};
|
|
1964
2232
|
},
|
|
1965
2233
|
};
|
|
1966
|
-
// ============================================================================
|
|
1967
|
-
// User Management
|
|
1968
|
-
// ============================================================================
|
|
1969
|
-
/**
|
|
1970
|
-
* User management operations
|
|
1971
|
-
*
|
|
1972
|
-
* @example
|
|
1973
|
-
* ```typescript
|
|
1974
|
-
* // List all users
|
|
1975
|
-
* const users = await ironflow.users.list();
|
|
1976
|
-
*
|
|
1977
|
-
* // Create a user
|
|
1978
|
-
* const user = await ironflow.users.create({
|
|
1979
|
-
* email: "alice@example.com",
|
|
1980
|
-
* password: "secret",
|
|
1981
|
-
* roles: ["admin"],
|
|
1982
|
-
* });
|
|
1983
|
-
*
|
|
1984
|
-
* // Update a user
|
|
1985
|
-
* await ironflow.users.update(user.id, { name: "Alice" });
|
|
1986
|
-
* ```
|
|
1987
|
-
*/
|
|
1988
|
-
users = {
|
|
1989
|
-
/** Create a new user (admin only) */
|
|
1990
|
-
create: async (input) => {
|
|
1991
|
-
this.ensureConfigured();
|
|
1992
|
-
return this.restRequest("POST", "/api/v1/users", input);
|
|
1993
|
-
},
|
|
1994
|
-
/** List all users in the current organization (admin only) */
|
|
1995
|
-
list: async () => {
|
|
1996
|
-
this.ensureConfigured();
|
|
1997
|
-
return this.restRequest("GET", "/api/v1/users");
|
|
1998
|
-
},
|
|
1999
|
-
/** Get a user by ID */
|
|
2000
|
-
get: async (id) => {
|
|
2001
|
-
this.ensureConfigured();
|
|
2002
|
-
return this.restRequest("GET", `/api/v1/users/${encodeURIComponent(id)}`);
|
|
2003
|
-
},
|
|
2004
|
-
/** Update a user's profile (admin only) */
|
|
2005
|
-
update: async (id, input) => {
|
|
2006
|
-
this.ensureConfigured();
|
|
2007
|
-
return this.restRequest("PATCH", `/api/v1/users/${encodeURIComponent(id)}`, input);
|
|
2008
|
-
},
|
|
2009
|
-
/** Delete a user (admin only) */
|
|
2010
|
-
delete: async (id) => {
|
|
2011
|
-
this.ensureConfigured();
|
|
2012
|
-
await this.restRequest("DELETE", `/api/v1/users/${encodeURIComponent(id)}`);
|
|
2013
|
-
},
|
|
2014
|
-
};
|
|
2015
2234
|
/**
|
|
2016
2235
|
* Tenant management operations (enterprise-only)
|
|
2017
2236
|
*
|
|
@@ -2027,6 +2246,19 @@ class IronflowClient {
|
|
|
2027
2246
|
this.ensureConfigured();
|
|
2028
2247
|
return this.restRequest("GET", "/api/v1/tenants");
|
|
2029
2248
|
},
|
|
2249
|
+
/** Provision an organization, environment, and initial administrator key. */
|
|
2250
|
+
provision: async (input) => {
|
|
2251
|
+
this.ensureConfigured();
|
|
2252
|
+
const response = await this.restRequest("POST", "/api/v1/tenants/provision", {
|
|
2253
|
+
org_name: input.orgName,
|
|
2254
|
+
env_name: input.envName ?? "production",
|
|
2255
|
+
});
|
|
2256
|
+
return {
|
|
2257
|
+
org: response.org,
|
|
2258
|
+
environment: response.environment,
|
|
2259
|
+
apiKey: { key: response.api_key.key, roles: response.api_key.roles ?? [] },
|
|
2260
|
+
};
|
|
2261
|
+
},
|
|
2030
2262
|
};
|
|
2031
2263
|
// ============================================================================
|
|
2032
2264
|
// Pattern Helpers (static)
|