@fluxomni/api-client 0.11.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/README.md +253 -0
- package/dist/auth.d.ts +36 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +71 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +89 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +254 -0
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts +59 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +135 -0
- package/dist/errors.js.map +1 -0
- package/dist/generated/graphql.d.ts +6777 -0
- package/dist/generated/graphql.d.ts.map +1 -0
- package/dist/generated/graphql.js +13702 -0
- package/dist/generated/graphql.js.map +1 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/scalars.d.ts +5 -0
- package/dist/scalars.d.ts.map +1 -0
- package/dist/scalars.js +2 -0
- package/dist/scalars.js.map +1 -0
- package/package.json +61 -0
- package/src/auth.ts +127 -0
- package/src/client.ts +412 -0
- package/src/errors.ts +183 -0
- package/src/generated/graphql.ts +20296 -0
- package/src/index.ts +72 -0
- package/src/scalars.ts +4 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fluxomni TypeScript GraphQL API Client
|
|
3
|
+
*
|
|
4
|
+
* A thin Fluxomni GraphQL transport with:
|
|
5
|
+
* - Session-aware HTTP and WebSocket setup
|
|
6
|
+
* - Direct raw GraphQL execution for automation scripts
|
|
7
|
+
* - Generated GraphQL documents and types for typed callers
|
|
8
|
+
* - Browser and server convenience helpers without a hand-written domain SDK
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { GraphQLClient, StateDocument } from '@fluxomni/api-client';
|
|
13
|
+
*
|
|
14
|
+
* // Create client
|
|
15
|
+
* const client = new GraphQLClient({
|
|
16
|
+
* httpEndpoint: 'http://localhost:8001/api',
|
|
17
|
+
* wsEndpoint: 'ws://localhost:8001/api/subscriptions',
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* const data = await client.executeRaw<{
|
|
21
|
+
* routes: { allRoutes: Array<{ id: string; label?: string | null }> };
|
|
22
|
+
* }>(`
|
|
23
|
+
* query Routes {
|
|
24
|
+
* routes { allRoutes { id label } }
|
|
25
|
+
* }
|
|
26
|
+
* `);
|
|
27
|
+
*
|
|
28
|
+
* // Subscribe to state changes
|
|
29
|
+
* client.subscribe({ query: StateDocument }).subscribe({
|
|
30
|
+
* next: (result) => {
|
|
31
|
+
* console.log('Routes updated:', result.data);
|
|
32
|
+
* },
|
|
33
|
+
* error: (err) => console.error(err),
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @packageDocumentation
|
|
38
|
+
*/
|
|
39
|
+
export { type AuthRole, type AuthStatus, type AuthUser, createSessionFetch, type LoginOptions, type LoginResult, login, SESSION_COOKIE_NAME, } from './auth.js';
|
|
40
|
+
export { type ConnectionCallbacks, GraphQLClient, type GraphQLClientConfig, type GraphQLClientLoginOptions, type GraphQLClientLoginResult, } from './client.js';
|
|
41
|
+
export { classifyGraphQLError, ClientError, extractField, GraphQLError, type GraphQLErrorCategory, type GraphQLServerError, MissingDataError, MissingFieldError, NetworkError, } from './errors.js';
|
|
42
|
+
export * from './generated/graphql.js';
|
|
43
|
+
export type { DateTime } from './scalars.js';
|
|
44
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAGH,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,EACL,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,KAAK,mBAAmB,EACxB,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,GAC9B,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,cAAc,wBAAwB,CAAC;AACvC,YAAY,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fluxomni TypeScript GraphQL API Client
|
|
3
|
+
*
|
|
4
|
+
* A thin Fluxomni GraphQL transport with:
|
|
5
|
+
* - Session-aware HTTP and WebSocket setup
|
|
6
|
+
* - Direct raw GraphQL execution for automation scripts
|
|
7
|
+
* - Generated GraphQL documents and types for typed callers
|
|
8
|
+
* - Browser and server convenience helpers without a hand-written domain SDK
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { GraphQLClient, StateDocument } from '@fluxomni/api-client';
|
|
13
|
+
*
|
|
14
|
+
* // Create client
|
|
15
|
+
* const client = new GraphQLClient({
|
|
16
|
+
* httpEndpoint: 'http://localhost:8001/api',
|
|
17
|
+
* wsEndpoint: 'ws://localhost:8001/api/subscriptions',
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* const data = await client.executeRaw<{
|
|
21
|
+
* routes: { allRoutes: Array<{ id: string; label?: string | null }> };
|
|
22
|
+
* }>(`
|
|
23
|
+
* query Routes {
|
|
24
|
+
* routes { allRoutes { id label } }
|
|
25
|
+
* }
|
|
26
|
+
* `);
|
|
27
|
+
*
|
|
28
|
+
* // Subscribe to state changes
|
|
29
|
+
* client.subscribe({ query: StateDocument }).subscribe({
|
|
30
|
+
* next: (result) => {
|
|
31
|
+
* console.log('Routes updated:', result.data);
|
|
32
|
+
* },
|
|
33
|
+
* error: (err) => console.error(err),
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @packageDocumentation
|
|
38
|
+
*/
|
|
39
|
+
// Low-level client
|
|
40
|
+
export { createSessionFetch, login, SESSION_COOKIE_NAME, } from './auth.js';
|
|
41
|
+
export { GraphQLClient, } from './client.js';
|
|
42
|
+
// Errors
|
|
43
|
+
export { classifyGraphQLError, ClientError, extractField, GraphQLError, MissingDataError, MissingFieldError, NetworkError, } from './errors.js';
|
|
44
|
+
// Re-export all generated types for convenience
|
|
45
|
+
export * from './generated/graphql.js';
|
|
46
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,mBAAmB;AACnB,OAAO,EAIL,kBAAkB,EAGlB,KAAK,EACL,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAEL,aAAa,GAId,MAAM,aAAa,CAAC;AACrB,SAAS;AACT,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,YAAY,EACZ,YAAY,EAGZ,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,gDAAgD;AAChD,cAAc,wBAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scalars.d.ts","sourceRoot":"","sources":["../src/scalars.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC"}
|
package/dist/scalars.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scalars.js","sourceRoot":"","sources":["../src/scalars.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fluxomni/api-client",
|
|
3
|
+
"version": "0.11.0",
|
|
4
|
+
"description": "TypeScript GraphQL client for Fluxomni API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=24"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./generated": {
|
|
18
|
+
"types": "./dist/generated/graphql.d.ts",
|
|
19
|
+
"import": "./dist/generated/graphql.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"clean": "rm -rf dist",
|
|
29
|
+
"fmt": "pnpm -w exec biome format --write api/clients/typescript",
|
|
30
|
+
"lint": "pnpm -w exec biome lint api/clients/typescript && tsc --noEmit",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"examples:check": "pnpm run build && tsc --noEmit -p ../../examples/typescript/tsconfig.json",
|
|
33
|
+
"graphql:codegen": "graphql-codegen --config codegen.yml",
|
|
34
|
+
"prepublishOnly": "pnpm run clean && pnpm run build"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@apollo/client": "^3.13.0",
|
|
38
|
+
"@graphql-codegen/cli": "^6.0.1",
|
|
39
|
+
"@graphql-codegen/typed-document-node": "^6.1.0",
|
|
40
|
+
"@graphql-codegen/typescript": "^5.0.2",
|
|
41
|
+
"@graphql-codegen/typescript-operations": "^5.0.2",
|
|
42
|
+
"@graphql-tools/graphql-file-loader": "^8.1.5",
|
|
43
|
+
"@graphql-typed-document-node/core": "^3.2.0",
|
|
44
|
+
"graphql": "^16.13.1",
|
|
45
|
+
"graphql-ws": "^6.0.7",
|
|
46
|
+
"typescript": "^5.9",
|
|
47
|
+
"vitest": "^4.1.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@apollo/client": ">=3.8",
|
|
51
|
+
"graphql": ">=16.0"
|
|
52
|
+
},
|
|
53
|
+
"keywords": [
|
|
54
|
+
"fluxomni",
|
|
55
|
+
"graphql",
|
|
56
|
+
"apollo",
|
|
57
|
+
"streaming",
|
|
58
|
+
"rtmp"
|
|
59
|
+
],
|
|
60
|
+
"license": "PolyForm-Noncommercial-1.0.0"
|
|
61
|
+
}
|
package/src/auth.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
export const SESSION_COOKIE_NAME = 'fluxomni_session';
|
|
2
|
+
|
|
3
|
+
export type AuthRole = 'admin' | 'operator' | 'viewer';
|
|
4
|
+
|
|
5
|
+
export type AuthUser = {
|
|
6
|
+
id: string;
|
|
7
|
+
username: string;
|
|
8
|
+
displayName: string | null;
|
|
9
|
+
role: AuthRole;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type AuthStatus = {
|
|
13
|
+
authRequired: boolean;
|
|
14
|
+
authenticated: boolean;
|
|
15
|
+
role: AuthRole | null;
|
|
16
|
+
currentUser: AuthUser | null;
|
|
17
|
+
canAccessSettings: boolean;
|
|
18
|
+
canAccessFleet: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type LoginOptions = {
|
|
22
|
+
username: string;
|
|
23
|
+
password: string;
|
|
24
|
+
/** Base server URL, for example `http://localhost:8001`. Defaults to same-origin `/api/auth/login`. */
|
|
25
|
+
baseUrl?: string;
|
|
26
|
+
/** Full auth endpoint override. Takes precedence over `baseUrl`. */
|
|
27
|
+
authEndpoint?: string;
|
|
28
|
+
/** Custom fetch implementation. */
|
|
29
|
+
fetch?: typeof fetch;
|
|
30
|
+
/** Browser credential mode for the login request. Defaults to `same-origin`. */
|
|
31
|
+
credentials?: RequestCredentials;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type LoginResult = {
|
|
35
|
+
status: AuthStatus;
|
|
36
|
+
/** `Cookie` header value for server-side callers when the runtime exposes Set-Cookie. */
|
|
37
|
+
cookieHeader?: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export async function login(options: LoginOptions): Promise<LoginResult> {
|
|
41
|
+
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
42
|
+
if (!fetchFn) {
|
|
43
|
+
throw new Error('A fetch implementation is required for login()');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const response = await fetchFn(resolveAuthEndpoint(options), {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
credentials: options.credentials ?? 'same-origin',
|
|
49
|
+
headers: {
|
|
50
|
+
'content-type': 'application/json',
|
|
51
|
+
},
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
username: options.username,
|
|
54
|
+
password: options.password,
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new Error(await readAuthError(response));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
status: (await response.json()) as AuthStatus,
|
|
64
|
+
cookieHeader: sessionCookieHeaderFromResponse(response),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function createSessionFetch(
|
|
69
|
+
cookieHeader: string,
|
|
70
|
+
fetchFn: typeof fetch = globalThis.fetch,
|
|
71
|
+
): typeof fetch {
|
|
72
|
+
return (input, init = {}) => {
|
|
73
|
+
const headers = new Headers(init.headers);
|
|
74
|
+
if (!headers.has('cookie')) {
|
|
75
|
+
headers.set('cookie', cookieHeader);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return fetchFn(input, {
|
|
79
|
+
...init,
|
|
80
|
+
headers,
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function resolveAuthEndpoint(options: LoginOptions): string {
|
|
86
|
+
if (options.authEndpoint) {
|
|
87
|
+
return options.authEndpoint;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (options.baseUrl) {
|
|
91
|
+
return new URL('/api/auth/login', options.baseUrl).toString();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return '/api/auth/login';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function sessionCookieHeaderFromResponse(
|
|
98
|
+
response: Response,
|
|
99
|
+
): string | undefined {
|
|
100
|
+
const setCookie = response.headers.get('set-cookie');
|
|
101
|
+
if (!setCookie) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return sessionCookieHeaderFromSetCookie(setCookie);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function sessionCookieHeaderFromSetCookie(
|
|
109
|
+
setCookie: string,
|
|
110
|
+
): string | undefined {
|
|
111
|
+
const cookiePair = setCookie
|
|
112
|
+
.split(',')
|
|
113
|
+
.map((part) => part.trim())
|
|
114
|
+
.find((part) => part.startsWith(`${SESSION_COOKIE_NAME}=`))
|
|
115
|
+
?.split(';')[0];
|
|
116
|
+
|
|
117
|
+
return cookiePair && cookiePair.includes('=') ? cookiePair : undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function readAuthError(response: Response): Promise<string> {
|
|
121
|
+
try {
|
|
122
|
+
const body = (await response.json()) as { message?: string };
|
|
123
|
+
return body.message || `Authentication failed (${response.status})`;
|
|
124
|
+
} catch {
|
|
125
|
+
return `Authentication failed (${response.status})`;
|
|
126
|
+
}
|
|
127
|
+
}
|