@bpmnkit/api 0.0.8

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.
Files changed (48) hide show
  1. package/README.md +150 -0
  2. package/dist/generated/admin-resources.d.ts +199 -0
  3. package/dist/generated/admin-resources.js +381 -0
  4. package/dist/generated/admin-types.d.ts +283 -0
  5. package/dist/generated/admin-types.js +4 -0
  6. package/dist/generated/resources.d.ts +1519 -0
  7. package/dist/generated/resources.js +2650 -0
  8. package/dist/generated/types.d.ts +11946 -0
  9. package/dist/generated/types.js +4 -0
  10. package/dist/index.d.ts +11 -0
  11. package/dist/index.js +10 -0
  12. package/dist/runtime/auth.d.ts +31 -0
  13. package/dist/runtime/auth.js +136 -0
  14. package/dist/runtime/cache.d.ts +13 -0
  15. package/dist/runtime/cache.js +48 -0
  16. package/dist/runtime/cache.test.d.ts +2 -0
  17. package/dist/runtime/cache.test.js +38 -0
  18. package/dist/runtime/client.d.ts +25 -0
  19. package/dist/runtime/client.js +33 -0
  20. package/dist/runtime/config.d.ts +15 -0
  21. package/dist/runtime/config.js +371 -0
  22. package/dist/runtime/errors.d.ts +53 -0
  23. package/dist/runtime/errors.js +101 -0
  24. package/dist/runtime/errors.test.d.ts +2 -0
  25. package/dist/runtime/errors.test.js +40 -0
  26. package/dist/runtime/events.d.ts +12 -0
  27. package/dist/runtime/events.js +48 -0
  28. package/dist/runtime/events.test.d.ts +2 -0
  29. package/dist/runtime/events.test.js +57 -0
  30. package/dist/runtime/http.d.ts +15 -0
  31. package/dist/runtime/http.js +210 -0
  32. package/dist/runtime/logger.d.ts +9 -0
  33. package/dist/runtime/logger.js +34 -0
  34. package/dist/runtime/relations.d.ts +43 -0
  35. package/dist/runtime/relations.js +54 -0
  36. package/dist/runtime/retry.d.ts +14 -0
  37. package/dist/runtime/retry.js +41 -0
  38. package/dist/runtime/retry.test.d.ts +2 -0
  39. package/dist/runtime/retry.test.js +46 -0
  40. package/dist/runtime/token-cache.d.ts +42 -0
  41. package/dist/runtime/token-cache.js +104 -0
  42. package/dist/runtime/types.d.ts +191 -0
  43. package/dist/runtime/types.js +2 -0
  44. package/dist/runtime/yaml.d.ts +14 -0
  45. package/dist/runtime/yaml.js +234 -0
  46. package/dist/runtime/yaml.test.d.ts +2 -0
  47. package/dist/runtime/yaml.test.js +93 -0
  48. package/package.json +31 -0
@@ -0,0 +1,191 @@
1
+ export type LogLevel = "debug" | "info" | "warn" | "error" | "none";
2
+ export interface LoggerConfig {
3
+ level?: LogLevel;
4
+ /** Custom sink. Defaults to console. */
5
+ sink?: (level: LogLevel, message: string, data?: unknown) => void;
6
+ }
7
+ export interface RetryConfig {
8
+ /** Maximum number of attempts (including the first). Default: 3. */
9
+ maxAttempts?: number;
10
+ /** Initial delay in ms. Default: 100. */
11
+ initialDelay?: number;
12
+ /** Maximum delay cap in ms. Default: 30_000. */
13
+ maxDelay?: number;
14
+ /** Multiplier applied to delay after each failure. Default: 2. */
15
+ backoffFactor?: number;
16
+ /** HTTP status codes that trigger a retry. Default: [429, 500, 502, 503, 504]. */
17
+ retryOn?: number[];
18
+ }
19
+ export interface CacheConfig {
20
+ /** Enable response caching for eventually-consistent GET/POST-search endpoints. Default: false. */
21
+ enabled?: boolean;
22
+ /** Time-to-live in ms. Default: 30_000. */
23
+ ttl?: number;
24
+ /** Maximum number of cached entries. Default: 500. */
25
+ maxSize?: number;
26
+ }
27
+ /** A cached OAuth2 access token. */
28
+ export interface CachedToken {
29
+ accessToken: string;
30
+ /** Unix timestamp (ms) when the token expires. */
31
+ expiresAt: number;
32
+ }
33
+ /**
34
+ * Interface for a persistent OAuth2 token store.
35
+ * Implement this to store tokens in Redis, a database, or any custom backend.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const redisStore: TokenStore = {
40
+ * async get(key) {
41
+ * const raw = await redis.get(`token:${key}`);
42
+ * return raw ? JSON.parse(raw) : null;
43
+ * },
44
+ * async set(key, token) {
45
+ * const ttl = Math.floor((token.expiresAt - Date.now()) / 1000);
46
+ * await redis.set(`token:${key}`, JSON.stringify(token), "EX", ttl);
47
+ * },
48
+ * };
49
+ * ```
50
+ */
51
+ export interface TokenStore {
52
+ get(key: string): Promise<CachedToken | null>;
53
+ set(key: string, token: CachedToken): Promise<void>;
54
+ }
55
+ export interface TokenCacheConfig {
56
+ /**
57
+ * Disable the persistent token cache entirely.
58
+ * The token will only be held in memory for the lifetime of the client instance.
59
+ * Default: false.
60
+ */
61
+ disabled?: boolean;
62
+ /**
63
+ * Absolute path to the token cache JSON file.
64
+ * Defaults to `{osConfigDir}/camunda-api/token-cache.json`.
65
+ */
66
+ filePath?: string;
67
+ /**
68
+ * Custom token store. When provided, overrides the default file-based cache.
69
+ * Use this to store tokens in Redis, a database, or any other backend.
70
+ */
71
+ store?: TokenStore;
72
+ }
73
+ export type AuthConfig = {
74
+ type: "bearer";
75
+ token: string;
76
+ } | {
77
+ type: "oauth2";
78
+ clientId: string;
79
+ clientSecret: string;
80
+ /** Token endpoint URL. */
81
+ tokenUrl: string;
82
+ scope?: string;
83
+ /** OAuth2 audience parameter. Required by Camunda Cloud (default: "zeebe.camunda.io"). */
84
+ audience?: string;
85
+ /**
86
+ * Persistent token cache configuration.
87
+ * Enabled by default — tokens survive process restarts.
88
+ */
89
+ tokenCache?: TokenCacheConfig;
90
+ } | {
91
+ type: "basic";
92
+ username: string;
93
+ password: string;
94
+ } | {
95
+ type: "none";
96
+ };
97
+ export interface CamundaClientConfig {
98
+ /** Base URL of the Camunda cluster, e.g. http://localhost:8080/v2 */
99
+ baseUrl: string;
100
+ auth: AuthConfig;
101
+ /**
102
+ * Path to a YAML config file. Fields from the file are merged with lower
103
+ * priority than values passed directly to the constructor.
104
+ */
105
+ configFile?: string;
106
+ retry?: RetryConfig;
107
+ cache?: CacheConfig;
108
+ logger?: LoggerConfig;
109
+ /** Request timeout in ms. Default: 30_000. */
110
+ timeout?: number;
111
+ }
112
+ /**
113
+ * All fields optional — missing values are resolved from the config file
114
+ * or environment variables. Useful when config is fully provided via env/file.
115
+ */
116
+ export type CamundaClientInput = {
117
+ baseUrl?: string;
118
+ auth?: AuthConfig;
119
+ configFile?: string;
120
+ retry?: RetryConfig;
121
+ cache?: CacheConfig;
122
+ logger?: LoggerConfig;
123
+ timeout?: number;
124
+ };
125
+ export interface RequestEvent {
126
+ method: string;
127
+ url: string;
128
+ headers: Record<string, string>;
129
+ body?: unknown;
130
+ }
131
+ export interface ResponseEvent {
132
+ method: string;
133
+ url: string;
134
+ status: number;
135
+ durationMs: number;
136
+ cached: boolean;
137
+ }
138
+ export interface RawResponseEvent {
139
+ method: string;
140
+ url: string;
141
+ status: number;
142
+ headers: Record<string, string>;
143
+ body: string;
144
+ requestHeaders: Record<string, string>;
145
+ requestBody?: string;
146
+ }
147
+ export interface ErrorEvent {
148
+ method: string;
149
+ url: string;
150
+ error: Error;
151
+ }
152
+ export interface RetryEvent {
153
+ method: string;
154
+ url: string;
155
+ attempt: number;
156
+ maxAttempts: number;
157
+ delayMs: number;
158
+ reason: string;
159
+ }
160
+ export interface TokenRefreshEvent {
161
+ tokenUrl: string;
162
+ }
163
+ export interface CacheEvent {
164
+ url: string;
165
+ }
166
+ export type ClientEventMap = {
167
+ request: RequestEvent;
168
+ response: ResponseEvent;
169
+ rawResponse: RawResponseEvent;
170
+ error: ErrorEvent;
171
+ retry: RetryEvent;
172
+ tokenRefresh: TokenRefreshEvent;
173
+ cacheHit: CacheEvent;
174
+ cacheMiss: CacheEvent;
175
+ };
176
+ export interface RequestOptions {
177
+ method: string;
178
+ path: string;
179
+ pathParams?: Record<string, string | number>;
180
+ query?: Record<string, unknown>;
181
+ body?: unknown;
182
+ /** Whether this response may be served from cache. */
183
+ cacheable?: boolean;
184
+ /** Override timeout for this request. */
185
+ timeout?: number;
186
+ /** Custom Accept header. Defaults to "application/json". */
187
+ accept?: string;
188
+ /** How to parse the response body. Defaults to "json". */
189
+ responseType?: "json" | "text";
190
+ }
191
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Minimal YAML parser for Camunda client config files.
3
+ * Handles block mappings, block sequences, inline arrays, all scalar types,
4
+ * quoted strings, and comments. No external dependencies.
5
+ */
6
+ type Scalar = string | number | boolean | null;
7
+ export interface YamlObject {
8
+ [key: string]: Scalar | YamlObject | YamlArray;
9
+ }
10
+ interface YamlArray extends Array<Scalar | YamlObject | YamlArray> {
11
+ }
12
+ export declare function parseYaml(text: string): YamlObject;
13
+ export {};
14
+ //# sourceMappingURL=yaml.d.ts.map
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Minimal YAML parser for Camunda client config files.
3
+ * Handles block mappings, block sequences, inline arrays, all scalar types,
4
+ * quoted strings, and comments. No external dependencies.
5
+ */
6
+ export function parseYaml(text) {
7
+ const lines = buildLines(text);
8
+ const [value] = parseMapping(lines, 0, 0);
9
+ return value;
10
+ }
11
+ // ─── Preprocessing ────────────────────────────────────────────────────────────
12
+ function buildLines(text) {
13
+ const result = [];
14
+ for (const raw of text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n")) {
15
+ let indent = 0;
16
+ for (const ch of raw) {
17
+ if (ch === " ")
18
+ indent++;
19
+ else if (ch === "\t")
20
+ indent += 2;
21
+ else
22
+ break;
23
+ }
24
+ const content = stripInlineComment(raw.trim());
25
+ if (content !== "")
26
+ result.push({ indent, content });
27
+ }
28
+ return result;
29
+ }
30
+ function stripInlineComment(s) {
31
+ let inSingle = false;
32
+ let inDouble = false;
33
+ for (let i = 0; i < s.length; i++) {
34
+ const ch = s[i];
35
+ if (ch === "'" && !inDouble) {
36
+ inSingle = !inSingle;
37
+ continue;
38
+ }
39
+ if (ch === '"' && !inSingle) {
40
+ inDouble = !inDouble;
41
+ continue;
42
+ }
43
+ if (ch === "#" && !inSingle && !inDouble && i > 0 && s[i - 1] === " ") {
44
+ return s.slice(0, i).trimEnd();
45
+ }
46
+ }
47
+ return s;
48
+ }
49
+ // ─── Recursive parser ─────────────────────────────────────────────────────────
50
+ /** Parse a mapping block. Returns [object, nextPos]. */
51
+ function parseMapping(lines, startPos, baseIndent) {
52
+ const obj = {};
53
+ let pos = startPos;
54
+ while (pos < lines.length) {
55
+ const line = lines[pos];
56
+ if (!line || line.indent < baseIndent)
57
+ break;
58
+ if (line.indent > baseIndent) {
59
+ // Indented line where we don't expect it — skip safely
60
+ pos++;
61
+ continue;
62
+ }
63
+ const [key, rest] = splitMappingKey(line.content);
64
+ if (key === null) {
65
+ pos++;
66
+ continue;
67
+ }
68
+ pos++;
69
+ if (rest === null) {
70
+ // Value is on following lines
71
+ const next = lines[pos];
72
+ if (!next || next.indent <= baseIndent) {
73
+ obj[key] = null;
74
+ continue;
75
+ }
76
+ const childIndent = next.indent;
77
+ if (next.content.startsWith("- ") || next.content === "-") {
78
+ const [seq, nextPos] = parseSequence(lines, pos, childIndent);
79
+ obj[key] = seq;
80
+ pos = nextPos;
81
+ }
82
+ else {
83
+ const [map, nextPos] = parseMapping(lines, pos, childIndent);
84
+ obj[key] = map;
85
+ pos = nextPos;
86
+ }
87
+ }
88
+ else if (rest.startsWith("[")) {
89
+ obj[key] = parseInlineArray(rest);
90
+ }
91
+ else {
92
+ obj[key] = parseScalar(rest);
93
+ }
94
+ }
95
+ return [obj, pos];
96
+ }
97
+ /** Parse a sequence block. Returns [array, nextPos]. */
98
+ function parseSequence(lines, startPos, baseIndent) {
99
+ const arr = [];
100
+ let pos = startPos;
101
+ while (pos < lines.length) {
102
+ const line = lines[pos];
103
+ if (!line || line.indent < baseIndent)
104
+ break;
105
+ if (!line.content.startsWith("- ") && line.content !== "-")
106
+ break;
107
+ const itemText = line.content.slice(1).trimStart();
108
+ pos++;
109
+ if (itemText === "") {
110
+ const next = lines[pos];
111
+ if (!next || next.indent <= baseIndent) {
112
+ arr.push(null);
113
+ continue;
114
+ }
115
+ // Could be a nested mapping or sequence
116
+ if (next.content.startsWith("- ") || next.content === "-") {
117
+ const [child, nextPos] = parseSequence(lines, pos, next.indent);
118
+ arr.push(child);
119
+ pos = nextPos;
120
+ }
121
+ else {
122
+ const [child, nextPos] = parseMapping(lines, pos, next.indent);
123
+ arr.push(child);
124
+ pos = nextPos;
125
+ }
126
+ }
127
+ else if (itemText.startsWith("[")) {
128
+ arr.push(parseInlineArray(itemText));
129
+ }
130
+ else {
131
+ arr.push(parseScalar(itemText));
132
+ }
133
+ }
134
+ return [arr, pos];
135
+ }
136
+ function parseInlineArray(text) {
137
+ let inner = text.trim();
138
+ if (inner.startsWith("["))
139
+ inner = inner.slice(1);
140
+ if (inner.endsWith("]"))
141
+ inner = inner.slice(0, -1);
142
+ if (!inner.trim())
143
+ return [];
144
+ return splitByComma(inner).map((item) => parseScalar(item.trim()));
145
+ }
146
+ // ─── Key extraction ───────────────────────────────────────────────────────────
147
+ /**
148
+ * Split `key: value rest` → [key, "value rest"] or [key, null] for blank value.
149
+ * Returns [null, null] if the line is not a mapping entry.
150
+ */
151
+ function splitMappingKey(content) {
152
+ // Quoted key
153
+ if (content.startsWith('"') || content.startsWith("'")) {
154
+ const q = content[0];
155
+ const close = content.indexOf(q, 1);
156
+ if (close >= 0 && content[close + 1] === ":") {
157
+ const key = content.slice(1, close);
158
+ const after = content.slice(close + 2).trimStart();
159
+ return [key, after || null];
160
+ }
161
+ return [null, null];
162
+ }
163
+ // Plain key — find `:` that is not part of `://`
164
+ for (let i = 0; i < content.length; i++) {
165
+ if (content[i] !== ":")
166
+ continue;
167
+ const next = content[i + 1];
168
+ if (next === "/" || next === ":")
169
+ continue; // skip :// and ::
170
+ if (next !== " " && next !== undefined)
171
+ continue; // must be `: ` or end of string
172
+ const key = content.slice(0, i).trim();
173
+ if (!key)
174
+ continue;
175
+ const after = content.slice(i + 1).trimStart();
176
+ return [key, after || null];
177
+ }
178
+ return [null, null];
179
+ }
180
+ // ─── Scalar parsing ───────────────────────────────────────────────────────────
181
+ function parseScalar(s) {
182
+ const str = s.trim();
183
+ if (str === "" || str === "null" || str === "~")
184
+ return null;
185
+ if (str === "true" || str === "yes" || str === "on")
186
+ return true;
187
+ if (str === "false" || str === "no" || str === "off")
188
+ return false;
189
+ if (str.startsWith('"') && str.endsWith('"') && str.length >= 2) {
190
+ return str
191
+ .slice(1, -1)
192
+ .replace(/\\n/g, "\n")
193
+ .replace(/\\t/g, "\t")
194
+ .replace(/\\"/g, '"')
195
+ .replace(/\\\\/g, "\\");
196
+ }
197
+ if (str.startsWith("'") && str.endsWith("'") && str.length >= 2) {
198
+ return str.slice(1, -1).replace(/''/g, "'");
199
+ }
200
+ if (/^-?\d+$/.test(str))
201
+ return Number.parseInt(str, 10);
202
+ if (/^-?\d*\.?\d+([eE][+-]?\d+)?$/.test(str))
203
+ return Number.parseFloat(str);
204
+ return str;
205
+ }
206
+ function splitByComma(text) {
207
+ const parts = [];
208
+ let depth = 0;
209
+ let current = "";
210
+ let inSingle = false;
211
+ let inDouble = false;
212
+ for (const ch of text) {
213
+ if (ch === "'" && !inDouble)
214
+ inSingle = !inSingle;
215
+ else if (ch === '"' && !inSingle)
216
+ inDouble = !inDouble;
217
+ else if (!inSingle && !inDouble) {
218
+ if (ch === "[" || ch === "{")
219
+ depth++;
220
+ else if (ch === "]" || ch === "}")
221
+ depth--;
222
+ else if (ch === "," && depth === 0) {
223
+ parts.push(current.trim());
224
+ current = "";
225
+ continue;
226
+ }
227
+ }
228
+ current += ch;
229
+ }
230
+ if (current.trim())
231
+ parts.push(current.trim());
232
+ return parts;
233
+ }
234
+ //# sourceMappingURL=yaml.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=yaml.test.d.ts.map
@@ -0,0 +1,93 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { parseYaml } from "./yaml.js";
3
+ describe("parseYaml", () => {
4
+ it("parses basic key-value pairs", () => {
5
+ const result = parseYaml("baseUrl: http://localhost:8080/v2\ntimeout: 30000");
6
+ expect(result.baseUrl).toBe("http://localhost:8080/v2");
7
+ expect(result.timeout).toBe(30000);
8
+ });
9
+ it("parses nested mappings", () => {
10
+ const result = parseYaml(`
11
+ auth:
12
+ type: oauth2
13
+ clientId: my-client
14
+ tokenUrl: https://example.com/token
15
+ `);
16
+ expect(result.auth).toEqual({
17
+ type: "oauth2",
18
+ clientId: "my-client",
19
+ tokenUrl: "https://example.com/token",
20
+ });
21
+ });
22
+ it("parses inline arrays", () => {
23
+ const result = parseYaml("retryOn: [429, 500, 503]");
24
+ expect(result.retryOn).toEqual([429, 500, 503]);
25
+ });
26
+ it("parses block sequences", () => {
27
+ const result = parseYaml(`
28
+ items:
29
+ - 429
30
+ - 500
31
+ - 503
32
+ `);
33
+ expect(result.items).toEqual([429, 500, 503]);
34
+ });
35
+ it("parses booleans", () => {
36
+ const result = parseYaml("enabled: true\ndisabled: false");
37
+ expect(result.enabled).toBe(true);
38
+ expect(result.disabled).toBe(false);
39
+ });
40
+ it("parses null values", () => {
41
+ const result = parseYaml("value: null");
42
+ expect(result.value).toBeNull();
43
+ });
44
+ it("parses double-quoted strings with escapes", () => {
45
+ const result = parseYaml('key: "hello\\nworld"');
46
+ expect(result.key).toBe("hello\nworld");
47
+ });
48
+ it("parses single-quoted strings", () => {
49
+ const result = parseYaml("key: 'it''s fine'");
50
+ expect(result.key).toBe("it's fine");
51
+ });
52
+ it("strips inline comments", () => {
53
+ const result = parseYaml("baseUrl: http://localhost # comment\ntimeout: 5000 # ms");
54
+ expect(result.baseUrl).toBe("http://localhost");
55
+ expect(result.timeout).toBe(5000);
56
+ });
57
+ it("handles URLs with :// correctly (not treated as key: value)", () => {
58
+ const result = parseYaml("tokenUrl: https://login.example.com/oauth/token");
59
+ expect(result.tokenUrl).toBe("https://login.example.com/oauth/token");
60
+ });
61
+ it("parses a realistic config file", () => {
62
+ const yaml = `
63
+ baseUrl: http://localhost:8080/v2
64
+ auth:
65
+ type: oauth2
66
+ clientId: my-client
67
+ clientSecret: my-secret
68
+ tokenUrl: https://auth.example.com/token
69
+ tokenCache:
70
+ disabled: false
71
+ filePath: /tmp/token-cache.json
72
+ retry:
73
+ maxAttempts: 3
74
+ initialDelay: 100
75
+ retryOn: [429, 500, 502, 503, 504]
76
+ cache:
77
+ enabled: true
78
+ ttl: 30000
79
+ logger:
80
+ level: info
81
+ timeout: 10000
82
+ `;
83
+ const result = parseYaml(yaml);
84
+ expect(result.baseUrl).toBe("http://localhost:8080/v2");
85
+ expect(result.auth.type).toBe("oauth2");
86
+ expect(result.auth.clientId).toBe("my-client");
87
+ expect(result.retry.maxAttempts).toBe(3);
88
+ expect(result.retry.retryOn).toEqual([429, 500, 502, 503, 504]);
89
+ expect(result.cache.enabled).toBe(true);
90
+ expect(result.timeout).toBe(10000);
91
+ });
92
+ });
93
+ //# sourceMappingURL=yaml.test.js.map
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@bpmnkit/api",
3
+ "version": "0.0.8",
4
+ "private": false,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/index.js",
9
+ "types": "./dist/index.d.ts"
10
+ }
11
+ },
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "files": ["dist/**/*.js", "dist/**/*.d.ts"],
15
+ "scripts": {
16
+ "generate": "node scripts/generate.mjs",
17
+ "generate:admin": "node scripts/generate.mjs --api admin",
18
+ "build": "node scripts/generate.mjs && node scripts/generate.mjs --api admin && tsc",
19
+ "typecheck": "tsc --noEmit",
20
+ "check": "biome check .",
21
+ "test": "vitest run"
22
+ },
23
+ "dependencies": {},
24
+ "description": "TypeScript client for the Camunda 8 REST API — 180 typed operations, OAuth2, retries, and caching",
25
+ "keywords": ["camunda", "zeebe", "bpmn", "api", "client", "rest", "typescript"],
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/bpmnkit/monorepo"
30
+ }
31
+ }