@shuffl/sdk 0.3.0 → 0.4.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 +20 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.js +78 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ const preview = await shuffl.programs.preview({
|
|
|
33
33
|
|
|
34
34
|
`users` means people in the team directory, including people who have no web login. IDs are Shuffl person IDs, not Slack IDs or authentication account IDs.
|
|
35
35
|
|
|
36
|
-
The client includes people and their profiles, teams and departments, access, identity links, programs and runs, Knowledge, invitations, People sync, onboarding and first 90 days check-ins, celebrations, Pulse, Praise, badges, communities, reminders, Home, the HR digest, HR requests, support, reports, exports, credentials and assistant conversations.
|
|
36
|
+
The client includes people and their profiles, teams and departments, access, identity links, programs and runs, Knowledge, invitations, People sync, onboarding and first 90 days check-ins, celebrations, Pulse, Praise, badges, communities, reminders, Home, the HR digest, HR requests, support, reports, exports, credentials and assistant conversations. A team key never gains company scope; company settings have their own client and key, below. See the [permission reference](https://shuffl-rebuild.vercel.app/docs/api-permissions) for every method. Input and output types come directly from the portable public oRPC contract.
|
|
37
37
|
|
|
38
38
|
Program creation requires a stable `requestKey`. Repeating the same creation request returns the existing program; using that key with different settings returns `CONFLICT`. Programs start paused unless `draft: false` is explicitly supplied. Updates require `expectedRevision`. Run requests require a preview and request key and return a durable run ID; poll `runs.get` for status. The application scheduler owns execution and delivery.
|
|
39
39
|
|
|
@@ -60,4 +60,23 @@ Management writes return `{receipt, result}`. A duplicate request returns the sa
|
|
|
60
60
|
|
|
61
61
|
Credentials with `credentials:write` can delegate only their own scopes, with bounded expiry and inherited revocation. Account sign-in, switching to another team, provider consent and payment confirmation remain explicit browser/account flows.
|
|
62
62
|
|
|
63
|
+
## Company settings
|
|
64
|
+
|
|
65
|
+
A company key (`shuffl_company_`), created by a company owner or administrator in **Company settings → API keys**, manages one company's settings and never reaches a workspace. `createShufflCompany` takes it and calls the REST operations under `/api/v1/company`:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { createShufflCompany } from '@shuffl/sdk';
|
|
69
|
+
|
|
70
|
+
const company = createShufflCompany({
|
|
71
|
+
baseUrl: process.env.SHUFFL_BASE_URL!,
|
|
72
|
+
token: process.env.SHUFFL_COMPANY_KEY!,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const { permissions } = await company.current(); // Any company key.
|
|
76
|
+
const page = await company.members.list({ limit: 100 });
|
|
77
|
+
await company.members.remove({ userId: page.items[0]!.userId });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`company.current()` returns the key's company and grants. Every other method needs its grant: `company.details`, `company.workspaces.list`, `company.members.list`, `company.emailDomains.list`, `company.slack.list`, `company.rename`, `company.workspaces.setDefault` and `company.members.remove`. `members.list` pages with `limit` and `cursor` like `users.list`. Company writes run once when called and take no `requestKey`. A company key can't call `createShuffl` methods, and a workspace key can't call these. See [company keys](https://shuffl-rebuild.vercel.app/docs/api-permissions#company-keys).
|
|
81
|
+
|
|
63
82
|
Full guide: [Use the SDK and HTTP API](https://shuffl-rebuild.vercel.app/docs/sdk).
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ContractRouterClient } from '@orpc/contract';
|
|
2
2
|
import type { ShufflContract } from '@shuffl/api-contracts';
|
|
3
|
+
import { type ShufflCompanyContract } from '@shuffl/api-contracts/company';
|
|
3
4
|
export { ORPCError } from '@orpc/client';
|
|
4
|
-
export type { Permission, PermissionDefinition, Scope, } from '@shuffl/api-contracts';
|
|
5
|
+
export type { CompanyPermission, Permission, PermissionDefinition, Scope, } from '@shuffl/api-contracts';
|
|
5
6
|
export type ShufflClient = ContractRouterClient<ShufflContract>;
|
|
7
|
+
/** The company API (#1692), for a company key (shuffl_company_). */
|
|
8
|
+
export type ShufflCompanyClient = ContractRouterClient<ShufflCompanyContract>;
|
|
6
9
|
export interface ShufflOptions {
|
|
7
10
|
token: string | (() => string | Promise<string>);
|
|
8
11
|
/** Shuffl application origin, without /api/v1/rpc. */
|
|
9
12
|
baseUrl: string;
|
|
10
13
|
fetch?: typeof globalThis.fetch;
|
|
11
14
|
}
|
|
15
|
+
/** A client for workspace operations, with a workspace key (shuffl_api_). */
|
|
12
16
|
export declare function createShuffl(options: ShufflOptions): ShufflClient;
|
|
17
|
+
/**
|
|
18
|
+
* A client for the company API, with a company key (shuffl_company_) from
|
|
19
|
+
* Company settings → API keys. It calls the REST operations under
|
|
20
|
+
* /api/v1/company: `company.members.list({ limit: 100 })`. A company key
|
|
21
|
+
* never reaches workspace operations, and a workspace key never reaches these.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createShufflCompany(options: ShufflOptions): ShufflCompanyClient;
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,95 @@
|
|
|
1
|
-
import { createORPCClient } from '@orpc/client';
|
|
1
|
+
import { createORPCClient, ORPCError } from '@orpc/client';
|
|
2
2
|
import { RPCLink } from '@orpc/client/fetch';
|
|
3
|
+
import { isContractProcedure } from '@orpc/contract';
|
|
4
|
+
import { companyContract, } from '@shuffl/api-contracts/company';
|
|
3
5
|
export { ORPCError } from '@orpc/client';
|
|
4
|
-
|
|
5
|
-
const base = new URL(
|
|
6
|
+
function origin(baseUrl) {
|
|
7
|
+
const base = new URL(baseUrl);
|
|
6
8
|
if (base.username ||
|
|
7
9
|
base.password ||
|
|
8
10
|
(base.protocol !== 'https:' &&
|
|
9
11
|
!(base.protocol === 'http:' &&
|
|
10
12
|
['localhost', '127.0.0.1', '[::1]'].includes(base.hostname))))
|
|
11
13
|
throw new Error('Use HTTPS, or HTTP on localhost for development.');
|
|
14
|
+
return base;
|
|
15
|
+
}
|
|
16
|
+
const bearer = async (token) => `Bearer ${typeof token === 'function' ? await token() : token}`;
|
|
17
|
+
/** A client for workspace operations, with a workspace key (shuffl_api_). */
|
|
18
|
+
export function createShuffl(options) {
|
|
19
|
+
const base = origin(options.baseUrl);
|
|
12
20
|
return createORPCClient(new RPCLink({
|
|
13
21
|
// Writes are not automatically retried. Callers own stable request keys.
|
|
14
22
|
fetch: async (request, init) => (options.fetch ?? globalThis.fetch)(request, {
|
|
15
23
|
...init,
|
|
16
24
|
redirect: 'error',
|
|
17
25
|
}),
|
|
18
|
-
headers: async () => ({
|
|
19
|
-
authorization: `Bearer ${typeof options.token === 'function' ? await options.token() : options.token}`,
|
|
20
|
-
}),
|
|
26
|
+
headers: async () => ({ authorization: await bearer(options.token) }),
|
|
21
27
|
url: new URL('/api/v1/rpc', base).toString(),
|
|
22
28
|
}));
|
|
23
29
|
}
|
|
30
|
+
const statusCodes = {
|
|
31
|
+
400: 'BAD_REQUEST',
|
|
32
|
+
401: 'UNAUTHORIZED',
|
|
33
|
+
403: 'FORBIDDEN',
|
|
34
|
+
404: 'NOT_FOUND',
|
|
35
|
+
405: 'METHOD_NOT_SUPPORTED',
|
|
36
|
+
409: 'CONFLICT',
|
|
37
|
+
429: 'TOO_MANY_REQUESTS',
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* A client for the company API, with a company key (shuffl_company_) from
|
|
41
|
+
* Company settings → API keys. It calls the REST operations under
|
|
42
|
+
* /api/v1/company: `company.members.list({ limit: 100 })`. A company key
|
|
43
|
+
* never reaches workspace operations, and a workspace key never reaches these.
|
|
44
|
+
*/
|
|
45
|
+
export function createShufflCompany(options) {
|
|
46
|
+
const base = origin(options.baseUrl);
|
|
47
|
+
const procedure = ({ method, path }) => async (input, call) => {
|
|
48
|
+
const url = new URL(`/api/v1${path}`, base);
|
|
49
|
+
const read = method === 'GET';
|
|
50
|
+
if (read)
|
|
51
|
+
for (const [key, value] of Object.entries(input ?? {}))
|
|
52
|
+
if (value !== undefined)
|
|
53
|
+
url.searchParams.set(key, String(value));
|
|
54
|
+
const response = await (options.fetch ?? globalThis.fetch)(url, {
|
|
55
|
+
body: read ? undefined : JSON.stringify(input ?? {}),
|
|
56
|
+
headers: {
|
|
57
|
+
accept: 'application/json',
|
|
58
|
+
authorization: await bearer(options.token),
|
|
59
|
+
...(read ? {} : { 'content-type': 'application/json' }),
|
|
60
|
+
},
|
|
61
|
+
method,
|
|
62
|
+
redirect: 'error',
|
|
63
|
+
signal: call?.signal,
|
|
64
|
+
});
|
|
65
|
+
const text = await response.text();
|
|
66
|
+
let body;
|
|
67
|
+
try {
|
|
68
|
+
body = text ? JSON.parse(text) : null;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
body = undefined;
|
|
72
|
+
}
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const error = (body ?? {});
|
|
75
|
+
throw new ORPCError(typeof error.code === 'string'
|
|
76
|
+
? error.code
|
|
77
|
+
: (statusCodes[response.status] ?? 'INTERNAL_SERVER_ERROR'), {
|
|
78
|
+
message: typeof error.message === 'string'
|
|
79
|
+
? error.message
|
|
80
|
+
: `Shuffl answered ${response.status}.`,
|
|
81
|
+
status: response.status,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (body === undefined)
|
|
85
|
+
throw new ORPCError('INTERNAL_SERVER_ERROR', {
|
|
86
|
+
message: 'Shuffl returned a response that is not JSON.',
|
|
87
|
+
status: response.status,
|
|
88
|
+
});
|
|
89
|
+
return body;
|
|
90
|
+
};
|
|
91
|
+
const build = (node) => isContractProcedure(node)
|
|
92
|
+
? procedure(node['~orpc'].route)
|
|
93
|
+
: Object.fromEntries(Object.entries(node).map(([key, value]) => [key, build(value)]));
|
|
94
|
+
return build(companyContract);
|
|
95
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"dependencies": {
|
|
3
3
|
"@orpc/client": "1.15.3",
|
|
4
4
|
"@orpc/contract": "1.15.3",
|
|
5
|
-
"@shuffl/api-contracts": "0.
|
|
5
|
+
"@shuffl/api-contracts": "0.4.0"
|
|
6
6
|
},
|
|
7
7
|
"description": "TypeScript client for the Shuffl API: people, teams, knowledge, programs and management commands.",
|
|
8
8
|
"engines": {
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"sideEffects": false,
|
|
35
35
|
"type": "module",
|
|
36
36
|
"types": "./dist/index.d.ts",
|
|
37
|
-
"version": "0.
|
|
37
|
+
"version": "0.4.0"
|
|
38
38
|
}
|