@rytass/bpm-core-client 0.1.3 → 0.1.5
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/CHANGELOG.md +28 -0
- package/LICENSE +21 -0
- package/README.md +69 -3
- package/package.json +5 -4
- package/src/index.js +7 -0
- package/src/index.js.map +1 -0
- package/src/lib/api-auth-client.d.ts +73 -0
- package/src/lib/api-auth-client.js +121 -0
- package/src/lib/api-auth-client.js.map +1 -0
- package/src/lib/form/form-api.d.ts +48 -0
- package/src/lib/form/form-api.js +228 -0
- package/src/lib/form/form-api.js.map +1 -0
- package/src/lib/form/form-rendering.d.ts +52 -0
- package/src/lib/form/form-rendering.js +341 -0
- package/src/lib/form/form-rendering.js.map +1 -0
- package/src/lib/form/index.js +6 -0
- package/src/lib/form/index.js.map +1 -0
- package/src/lib/graphql-client.d.ts +45 -0
- package/src/lib/graphql-client.js +92 -0
- package/src/lib/graphql-client.js.map +1 -0
- package/src/lib/member-api.d.ts +49 -0
- package/src/lib/member-api.js +72 -0
- package/src/lib/member-api.js.map +1 -0
- package/src/lib/organization/index.js +5 -0
- package/src/lib/organization/index.js.map +1 -0
- package/src/lib/organization/organization-api.d.ts +168 -0
- package/src/lib/organization/organization-api.js +394 -0
- package/src/lib/organization/organization-api.js.map +1 -0
- package/src/lib/template/index.js +5 -0
- package/src/lib/template/index.js.map +1 -0
- package/src/lib/template/template-api.d.ts +150 -0
- package/src/lib/template/template-api.js +407 -0
- package/src/lib/template/template-api.js.map +1 -0
- package/src/lib/workflow/index.js +5 -0
- package/src/lib/workflow/index.js.map +1 -0
- package/src/lib/workflow/workflow-api.d.ts +376 -0
- package/src/lib/workflow/workflow-api.js +867 -0
- package/src/lib/workflow/workflow-api.js.map +1 -0
- package/eslint.config.mjs +0 -19
- package/jest.config.cts +0 -10
- package/project.json +0 -66
- package/src/lib/api-auth-client.ts +0 -169
- package/src/lib/form/form-api.spec.ts +0 -81
- package/src/lib/form/form-api.ts +0 -388
- package/src/lib/form/form-rendering.ts +0 -580
- package/src/lib/graphql-client.spec.ts +0 -65
- package/src/lib/graphql-client.ts +0 -114
- package/src/lib/member-api.ts +0 -129
- package/src/lib/organization/organization-api.spec.ts +0 -90
- package/src/lib/organization/organization-api.ts +0 -710
- package/src/lib/template/template-api.spec.ts +0 -88
- package/src/lib/template/template-api.ts +0 -772
- package/src/lib/workflow/workflow-api.spec.ts +0 -132
- package/src/lib/workflow/workflow-api.ts +0 -1749
- package/tsconfig.json +0 -27
- package/tsconfig.lib.json +0 -16
- package/tsconfig.spec.json +0 -16
- /package/src/{index.ts → index.d.ts} +0 -0
- /package/src/lib/form/{index.ts → index.d.ts} +0 -0
- /package/src/lib/organization/{index.ts → index.d.ts} +0 -0
- /package/src/lib/template/{index.ts → index.d.ts} +0 -0
- /package/src/lib/workflow/{index.ts → index.d.ts} +0 -0
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
const LOCAL_GRAPHQL_ENDPOINT = 'http://localhost:17603/graphql';
|
|
2
|
-
const SAME_ORIGIN_GRAPHQL_ENDPOINT = '/graphql';
|
|
3
|
-
|
|
4
|
-
interface GraphQlError {
|
|
5
|
-
readonly message: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
interface GraphQlResponse<TData> {
|
|
9
|
-
readonly data?: TData;
|
|
10
|
-
readonly errors?: readonly GraphQlError[];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Sends a single GraphQL operation to the BPM host and returns the typed data.
|
|
15
|
-
*
|
|
16
|
-
* Uses the standard Fetch API with `credentials: 'include'`, so any
|
|
17
|
-
* HTTP-only session cookie issued by the host's `POST /auth/login` endpoint
|
|
18
|
-
* flows back automatically on subsequent calls.
|
|
19
|
-
*
|
|
20
|
-
* The endpoint URL is resolved by {@link readGraphQlEndpoint} — set
|
|
21
|
-
* `NEXT_PUBLIC_API_URL` to override the default localhost / same-origin
|
|
22
|
-
* resolution.
|
|
23
|
-
*
|
|
24
|
-
* Throws an `Error` when the HTTP response is non-2xx, when the GraphQL
|
|
25
|
-
* payload carries an `errors` array, or when the response has no `data`
|
|
26
|
-
* field.
|
|
27
|
-
*
|
|
28
|
-
* @typeParam TData - Shape of the `data` field for the operation.
|
|
29
|
-
* @param query - GraphQL operation document (query or mutation source).
|
|
30
|
-
* @param variables - Variables passed to the GraphQL operation.
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* ```ts
|
|
34
|
-
* interface PingData { readonly memberCount: number; }
|
|
35
|
-
* const data = await requestGraphQl<PingData>('query Ping { memberCount }');
|
|
36
|
-
* console.log(data.memberCount);
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
export async function requestGraphQl<TData>(
|
|
40
|
-
query: string,
|
|
41
|
-
variables?: Readonly<Record<string, unknown>>,
|
|
42
|
-
): Promise<TData> {
|
|
43
|
-
const response = await fetch(readGraphQlEndpoint(), {
|
|
44
|
-
body: JSON.stringify({ query, variables }),
|
|
45
|
-
credentials: 'include',
|
|
46
|
-
headers: buildGraphQlHeaders(),
|
|
47
|
-
method: 'POST',
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
if (!response.ok) {
|
|
51
|
-
throw new Error(`GraphQL request failed with HTTP ${response.status}`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const payload = (await response.json()) as GraphQlResponse<TData>;
|
|
55
|
-
|
|
56
|
-
if (payload.errors?.length) {
|
|
57
|
-
throw new Error(payload.errors.map((error) => error.message).join('; '));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (!payload.data) {
|
|
61
|
-
throw new Error('GraphQL response did not include data');
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return payload.data;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Resolves the GraphQL endpoint URL used by {@link requestGraphQl}.
|
|
69
|
-
*
|
|
70
|
-
* Resolution order:
|
|
71
|
-
* 1. `process.env.NEXT_PUBLIC_API_URL` if non-empty.
|
|
72
|
-
* 2. `http://localhost:17603/graphql` when the browser is on
|
|
73
|
-
* `localhost` / `127.0.0.1`.
|
|
74
|
-
* 3. Same-origin `/graphql` for any other hostname (and for non-browser
|
|
75
|
-
* runtimes such as Node SSR).
|
|
76
|
-
*/
|
|
77
|
-
export function readGraphQlEndpoint(): string {
|
|
78
|
-
return (
|
|
79
|
-
readOptionalPublicEnvValue(process.env.NEXT_PUBLIC_API_URL) ??
|
|
80
|
-
resolveDefaultGraphQlEndpoint(readBrowserHostname())
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Pure helper used by {@link readGraphQlEndpoint}.
|
|
86
|
-
*
|
|
87
|
-
* Exposed mainly for tests and for hosts that want to derive their own
|
|
88
|
-
* endpoint URLs without coupling to environment variables.
|
|
89
|
-
*/
|
|
90
|
-
export function resolveDefaultGraphQlEndpoint(
|
|
91
|
-
hostname: string | null,
|
|
92
|
-
): string {
|
|
93
|
-
return isLocalHost(hostname)
|
|
94
|
-
? LOCAL_GRAPHQL_ENDPOINT
|
|
95
|
-
: SAME_ORIGIN_GRAPHQL_ENDPOINT;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function buildGraphQlHeaders(): Readonly<Record<string, string>> {
|
|
99
|
-
return {
|
|
100
|
-
'Content-Type': 'application/json',
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function readOptionalPublicEnvValue(value: string | undefined): string | null {
|
|
105
|
-
return value?.trim() || null;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function readBrowserHostname(): string | null {
|
|
109
|
-
return typeof window === 'undefined' ? null : window.location.hostname;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function isLocalHost(hostname: string | null): boolean {
|
|
113
|
-
return hostname === 'localhost' || hostname === '127.0.0.1';
|
|
114
|
-
}
|
package/src/lib/member-api.ts
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { requestGraphQl } from './graphql-client';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Member profile record exposed by the BPM identity GraphQL surface.
|
|
5
|
-
*
|
|
6
|
-
* `customFieldsJson` is a stringified JSON blob — BPM does not impose a
|
|
7
|
-
* schema on host-side member metadata. Consumers parse it according to
|
|
8
|
-
* their own host conventions.
|
|
9
|
-
*/
|
|
10
|
-
export interface MemberProfileRecord {
|
|
11
|
-
readonly customFieldsJson: string;
|
|
12
|
-
readonly email: string;
|
|
13
|
-
readonly memberId: string;
|
|
14
|
-
readonly name: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* A paginated slice of the host member directory, returned by
|
|
19
|
-
* {@link listMemberDirectoryPage}.
|
|
20
|
-
*/
|
|
21
|
-
export interface MemberDirectoryPage {
|
|
22
|
-
readonly members: readonly MemberProfileRecord[];
|
|
23
|
-
readonly totalCount: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface MembersQueryData {
|
|
27
|
-
readonly members: readonly MemberProfileRecord[];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface SearchMembersQueryData {
|
|
31
|
-
readonly searchMembers: readonly MemberProfileRecord[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface MemberDirectoryPageQueryData extends SearchMembersQueryData {
|
|
35
|
-
readonly memberCount: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Resolves a set of `memberId`s into full member profile records by hitting
|
|
40
|
-
* BPM's `members(memberIds: [String!]!)` GraphQL query, which in turn calls
|
|
41
|
-
* the host-provided `BPMMemberResolver.resolveMany`.
|
|
42
|
-
*
|
|
43
|
-
* Returns an empty array when given an empty input — does not make a
|
|
44
|
-
* network call in that case.
|
|
45
|
-
*/
|
|
46
|
-
export async function resolveMembers(
|
|
47
|
-
memberIds: readonly string[],
|
|
48
|
-
): Promise<readonly MemberProfileRecord[]> {
|
|
49
|
-
if (!memberIds.length) {
|
|
50
|
-
return [];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const data = await requestGraphQl<MembersQueryData>(
|
|
54
|
-
`query AdminMembers($memberIds: [String!]!) {
|
|
55
|
-
members(memberIds: $memberIds) {
|
|
56
|
-
customFieldsJson
|
|
57
|
-
email
|
|
58
|
-
memberId
|
|
59
|
-
name
|
|
60
|
-
}
|
|
61
|
-
}`,
|
|
62
|
-
{ memberIds },
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
return data.members;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Free-text search over the host's member directory through BPM's
|
|
70
|
-
* `searchMembers(searchText: String!)` query. The matching strategy is
|
|
71
|
-
* defined by the host's `BPMMemberResolver.search` implementation.
|
|
72
|
-
*/
|
|
73
|
-
export async function searchMembers(
|
|
74
|
-
searchText: string,
|
|
75
|
-
): Promise<readonly MemberProfileRecord[]> {
|
|
76
|
-
const data = await requestGraphQl<SearchMembersQueryData>(
|
|
77
|
-
`query AdminSearchMembers($searchText: String!) {
|
|
78
|
-
searchMembers(searchText: $searchText) {
|
|
79
|
-
customFieldsJson
|
|
80
|
-
email
|
|
81
|
-
memberId
|
|
82
|
-
name
|
|
83
|
-
}
|
|
84
|
-
}`,
|
|
85
|
-
{ searchText },
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
return data.searchMembers;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Reads a single page of the member directory (search + total count in a
|
|
93
|
-
* single GraphQL roundtrip).
|
|
94
|
-
*
|
|
95
|
-
* @param page - 1-based page index.
|
|
96
|
-
* @param pageSize - Items per page.
|
|
97
|
-
* @param searchText - Optional free-text filter; empty string returns all.
|
|
98
|
-
*/
|
|
99
|
-
export async function listMemberDirectoryPage({
|
|
100
|
-
page,
|
|
101
|
-
pageSize,
|
|
102
|
-
searchText = '',
|
|
103
|
-
}: {
|
|
104
|
-
readonly page: number;
|
|
105
|
-
readonly pageSize: number;
|
|
106
|
-
readonly searchText?: string;
|
|
107
|
-
}): Promise<MemberDirectoryPage> {
|
|
108
|
-
const data = await requestGraphQl<MemberDirectoryPageQueryData>(
|
|
109
|
-
`query AdminMemberDirectoryPage(
|
|
110
|
-
$page: Int
|
|
111
|
-
$pageSize: Int
|
|
112
|
-
$searchText: String!
|
|
113
|
-
) {
|
|
114
|
-
searchMembers(page: $page, pageSize: $pageSize, searchText: $searchText) {
|
|
115
|
-
customFieldsJson
|
|
116
|
-
email
|
|
117
|
-
memberId
|
|
118
|
-
name
|
|
119
|
-
}
|
|
120
|
-
memberCount(searchText: $searchText)
|
|
121
|
-
}`,
|
|
122
|
-
{ page, pageSize, searchText },
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
return {
|
|
126
|
-
members: data.searchMembers,
|
|
127
|
-
totalCount: data.memberCount,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { createOrgUnit, deleteOrgUnit } from './organization-api';
|
|
2
|
-
|
|
3
|
-
interface CapturedRequest {
|
|
4
|
-
readonly query: string;
|
|
5
|
-
readonly variables?: Readonly<Record<string, unknown>>;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function installFetchMock<TData>(payload: TData): {
|
|
9
|
-
readonly capture: () => CapturedRequest;
|
|
10
|
-
readonly restore: () => void;
|
|
11
|
-
} {
|
|
12
|
-
const originalFetch = global.fetch;
|
|
13
|
-
const fetchMock = jest.fn<
|
|
14
|
-
Promise<Response>,
|
|
15
|
-
[RequestInfo | URL, RequestInit?]
|
|
16
|
-
>();
|
|
17
|
-
fetchMock.mockImplementation(
|
|
18
|
-
async () =>
|
|
19
|
-
new Response(JSON.stringify({ data: payload }), {
|
|
20
|
-
headers: { 'Content-Type': 'application/json' },
|
|
21
|
-
status: 200,
|
|
22
|
-
}),
|
|
23
|
-
);
|
|
24
|
-
(global as { fetch: typeof fetch }).fetch =
|
|
25
|
-
fetchMock as unknown as typeof fetch;
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
capture: (): CapturedRequest => {
|
|
29
|
-
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
30
|
-
const init = fetchMock.mock.calls[0]?.[1];
|
|
31
|
-
return JSON.parse(String(init?.body ?? '{}')) as CapturedRequest;
|
|
32
|
-
},
|
|
33
|
-
restore: (): void => {
|
|
34
|
-
(global as { fetch: typeof fetch }).fetch = originalFetch;
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
describe('@rytass/bpm-core-client/organization', () => {
|
|
40
|
-
describe('createOrgUnit', () => {
|
|
41
|
-
it('forwards the input under the GraphQL "input" variable', async (): Promise<void> => {
|
|
42
|
-
const input = {
|
|
43
|
-
code: 'ENG',
|
|
44
|
-
metadataJson: '{}',
|
|
45
|
-
name: 'Engineering',
|
|
46
|
-
parentId: null,
|
|
47
|
-
type: 'DEPARTMENT' as const,
|
|
48
|
-
};
|
|
49
|
-
const harness = installFetchMock({
|
|
50
|
-
createOrgUnit: {
|
|
51
|
-
code: 'ENG',
|
|
52
|
-
createdAt: '2026-05-26T00:00:00.000Z',
|
|
53
|
-
deletedAt: null,
|
|
54
|
-
id: 'unit-1',
|
|
55
|
-
name: 'Engineering',
|
|
56
|
-
parentId: null,
|
|
57
|
-
path: 'unit-1',
|
|
58
|
-
type: 'DEPARTMENT',
|
|
59
|
-
updatedAt: '2026-05-26T00:00:00.000Z',
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
try {
|
|
63
|
-
const result = await createOrgUnit(input);
|
|
64
|
-
const request = harness.capture();
|
|
65
|
-
|
|
66
|
-
expect(request.query).toContain('mutation AdminCreateOrgUnit');
|
|
67
|
-
expect(request.variables).toEqual({ input });
|
|
68
|
-
expect(result.id).toBe('unit-1');
|
|
69
|
-
} finally {
|
|
70
|
-
harness.restore();
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe('deleteOrgUnit', () => {
|
|
76
|
-
it('passes the id and returns the boolean payload', async (): Promise<void> => {
|
|
77
|
-
const harness = installFetchMock({ deleteOrgUnit: true });
|
|
78
|
-
try {
|
|
79
|
-
const result = await deleteOrgUnit('unit-1');
|
|
80
|
-
const request = harness.capture();
|
|
81
|
-
|
|
82
|
-
expect(request.query).toContain('deleteOrgUnit');
|
|
83
|
-
expect(request.variables).toEqual({ id: 'unit-1' });
|
|
84
|
-
expect(result).toBe(true);
|
|
85
|
-
} finally {
|
|
86
|
-
harness.restore();
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
});
|