@storyblok/management-api-client 0.1.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.
Files changed (73) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +241 -0
  3. package/dist/_virtual/rolldown_runtime.js +25 -0
  4. package/dist/client/client.d.mts +7 -0
  5. package/dist/client/client.d.ts +7 -0
  6. package/dist/client/client.js +160 -0
  7. package/dist/client/client.js.map +1 -0
  8. package/dist/client/client.mjs +159 -0
  9. package/dist/client/client.mjs.map +1 -0
  10. package/dist/client/index.js +4 -0
  11. package/dist/client/index.mjs +4 -0
  12. package/dist/client/types.d.mts +116 -0
  13. package/dist/client/types.d.ts +116 -0
  14. package/dist/client/utils.d.mts +23 -0
  15. package/dist/client/utils.d.ts +23 -0
  16. package/dist/client/utils.js +242 -0
  17. package/dist/client/utils.js.map +1 -0
  18. package/dist/client/utils.mjs +236 -0
  19. package/dist/client/utils.mjs.map +1 -0
  20. package/dist/core/auth.d.mts +21 -0
  21. package/dist/core/auth.d.ts +21 -0
  22. package/dist/core/auth.js +13 -0
  23. package/dist/core/auth.js.map +1 -0
  24. package/dist/core/auth.mjs +12 -0
  25. package/dist/core/auth.mjs.map +1 -0
  26. package/dist/core/bodySerializer.d.mts +13 -0
  27. package/dist/core/bodySerializer.d.ts +13 -0
  28. package/dist/core/bodySerializer.js +7 -0
  29. package/dist/core/bodySerializer.js.map +1 -0
  30. package/dist/core/bodySerializer.mjs +6 -0
  31. package/dist/core/bodySerializer.mjs.map +1 -0
  32. package/dist/core/params.js +12 -0
  33. package/dist/core/params.js.map +1 -0
  34. package/dist/core/params.mjs +11 -0
  35. package/dist/core/params.mjs.map +1 -0
  36. package/dist/core/pathSerializer.d.mts +14 -0
  37. package/dist/core/pathSerializer.d.ts +14 -0
  38. package/dist/core/pathSerializer.js +85 -0
  39. package/dist/core/pathSerializer.js.map +1 -0
  40. package/dist/core/pathSerializer.mjs +82 -0
  41. package/dist/core/pathSerializer.mjs.map +1 -0
  42. package/dist/core/types.d.mts +78 -0
  43. package/dist/core/types.d.ts +78 -0
  44. package/dist/index.d.mts +50 -0
  45. package/dist/index.d.ts +50 -0
  46. package/dist/index.js +84 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/index.mjs +82 -0
  49. package/dist/index.mjs.map +1 -0
  50. package/dist/sdk-registry.generated.d.mts +28 -0
  51. package/dist/sdk-registry.generated.d.ts +28 -0
  52. package/dist/sdk-registry.generated.js +26 -0
  53. package/dist/sdk-registry.generated.js.map +1 -0
  54. package/dist/sdk-registry.generated.mjs +26 -0
  55. package/dist/sdk-registry.generated.mjs.map +1 -0
  56. package/examples/index.ts +32 -0
  57. package/generate.ts +125 -0
  58. package/package.json +79 -0
  59. package/src/__tests__/integration.test.ts +143 -0
  60. package/src/__tests__/region-resolution.test.ts +89 -0
  61. package/src/client/client.ts +246 -0
  62. package/src/client/index.ts +22 -0
  63. package/src/client/types.ts +222 -0
  64. package/src/client/utils.ts +417 -0
  65. package/src/core/auth.ts +40 -0
  66. package/src/core/bodySerializer.ts +88 -0
  67. package/src/core/params.ts +151 -0
  68. package/src/core/pathSerializer.ts +179 -0
  69. package/src/core/types.ts +118 -0
  70. package/src/index.ts +129 -0
  71. package/src/shared-client.ts +13 -0
  72. package/tsconfig.json +25 -0
  73. package/tsdown.config.ts +21 -0
@@ -0,0 +1,179 @@
1
+ interface SerializeOptions<T>
2
+ extends SerializePrimitiveOptions,
3
+ SerializerOptions<T> {}
4
+
5
+ interface SerializePrimitiveOptions {
6
+ allowReserved?: boolean;
7
+ name: string;
8
+ }
9
+
10
+ export interface SerializerOptions<T> {
11
+ /**
12
+ * @default true
13
+ */
14
+ explode: boolean;
15
+ style: T;
16
+ }
17
+
18
+ export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited';
19
+ export type ArraySeparatorStyle = ArrayStyle | MatrixStyle;
20
+ type MatrixStyle = 'label' | 'matrix' | 'simple';
21
+ export type ObjectStyle = 'form' | 'deepObject';
22
+ type ObjectSeparatorStyle = ObjectStyle | MatrixStyle;
23
+
24
+ interface SerializePrimitiveParam extends SerializePrimitiveOptions {
25
+ value: string;
26
+ }
27
+
28
+ export const separatorArrayExplode = (style: ArraySeparatorStyle) => {
29
+ switch (style) {
30
+ case 'label':
31
+ return '.';
32
+ case 'matrix':
33
+ return ';';
34
+ case 'simple':
35
+ return ',';
36
+ default:
37
+ return '&';
38
+ }
39
+ };
40
+
41
+ export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => {
42
+ switch (style) {
43
+ case 'form':
44
+ return ',';
45
+ case 'pipeDelimited':
46
+ return '|';
47
+ case 'spaceDelimited':
48
+ return '%20';
49
+ default:
50
+ return ',';
51
+ }
52
+ };
53
+
54
+ export const separatorObjectExplode = (style: ObjectSeparatorStyle) => {
55
+ switch (style) {
56
+ case 'label':
57
+ return '.';
58
+ case 'matrix':
59
+ return ';';
60
+ case 'simple':
61
+ return ',';
62
+ default:
63
+ return '&';
64
+ }
65
+ };
66
+
67
+ export const serializeArrayParam = ({
68
+ allowReserved,
69
+ explode,
70
+ name,
71
+ style,
72
+ value,
73
+ }: SerializeOptions<ArraySeparatorStyle> & {
74
+ value: unknown[];
75
+ }) => {
76
+ if (!explode) {
77
+ const joinedValues = (
78
+ allowReserved ? value : value.map((v) => encodeURIComponent(v as string))
79
+ ).join(separatorArrayNoExplode(style));
80
+ switch (style) {
81
+ case 'label':
82
+ return `.${joinedValues}`;
83
+ case 'matrix':
84
+ return `;${name}=${joinedValues}`;
85
+ case 'simple':
86
+ return joinedValues;
87
+ default:
88
+ return `${name}=${joinedValues}`;
89
+ }
90
+ }
91
+
92
+ const separator = separatorArrayExplode(style);
93
+ const joinedValues = value
94
+ .map((v) => {
95
+ if (style === 'label' || style === 'simple') {
96
+ return allowReserved ? v : encodeURIComponent(v as string);
97
+ }
98
+
99
+ return serializePrimitiveParam({
100
+ allowReserved,
101
+ name,
102
+ value: v as string,
103
+ });
104
+ })
105
+ .join(separator);
106
+ return style === 'label' || style === 'matrix'
107
+ ? separator + joinedValues
108
+ : joinedValues;
109
+ };
110
+
111
+ export const serializePrimitiveParam = ({
112
+ allowReserved,
113
+ name,
114
+ value,
115
+ }: SerializePrimitiveParam) => {
116
+ if (value === undefined || value === null) {
117
+ return '';
118
+ }
119
+
120
+ if (typeof value === 'object') {
121
+ throw new Error(
122
+ 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.',
123
+ );
124
+ }
125
+
126
+ return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
127
+ };
128
+
129
+ export const serializeObjectParam = ({
130
+ allowReserved,
131
+ explode,
132
+ name,
133
+ style,
134
+ value,
135
+ valueOnly,
136
+ }: SerializeOptions<ObjectSeparatorStyle> & {
137
+ value: Record<string, unknown> | Date;
138
+ valueOnly?: boolean;
139
+ }) => {
140
+ if (value instanceof Date) {
141
+ return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
142
+ }
143
+
144
+ if (style !== 'deepObject' && !explode) {
145
+ let values: string[] = [];
146
+ Object.entries(value).forEach(([key, v]) => {
147
+ values = [
148
+ ...values,
149
+ key,
150
+ allowReserved ? (v as string) : encodeURIComponent(v as string),
151
+ ];
152
+ });
153
+ const joinedValues = values.join(',');
154
+ switch (style) {
155
+ case 'form':
156
+ return `${name}=${joinedValues}`;
157
+ case 'label':
158
+ return `.${joinedValues}`;
159
+ case 'matrix':
160
+ return `;${name}=${joinedValues}`;
161
+ default:
162
+ return joinedValues;
163
+ }
164
+ }
165
+
166
+ const separator = separatorObjectExplode(style);
167
+ const joinedValues = Object.entries(value)
168
+ .map(([key, v]) =>
169
+ serializePrimitiveParam({
170
+ allowReserved,
171
+ name: style === 'deepObject' ? `${name}[${key}]` : key,
172
+ value: v as string,
173
+ }),
174
+ )
175
+ .join(separator);
176
+ return style === 'label' || style === 'matrix'
177
+ ? separator + joinedValues
178
+ : joinedValues;
179
+ };
@@ -0,0 +1,118 @@
1
+ import type { Auth, AuthToken } from './auth';
2
+ import type {
3
+ BodySerializer,
4
+ QuerySerializer,
5
+ QuerySerializerOptions,
6
+ } from './bodySerializer';
7
+
8
+ export interface Client<
9
+ RequestFn = never,
10
+ Config = unknown,
11
+ MethodFn = never,
12
+ BuildUrlFn = never,
13
+ > {
14
+ /**
15
+ * Returns the final request URL.
16
+ */
17
+ buildUrl: BuildUrlFn;
18
+ connect: MethodFn;
19
+ delete: MethodFn;
20
+ get: MethodFn;
21
+ getConfig: () => Config;
22
+ head: MethodFn;
23
+ options: MethodFn;
24
+ patch: MethodFn;
25
+ post: MethodFn;
26
+ put: MethodFn;
27
+ request: RequestFn;
28
+ setConfig: (config: Config) => Config;
29
+ trace: MethodFn;
30
+ }
31
+
32
+ export interface Config {
33
+ /**
34
+ * Auth token or a function returning auth token. The resolved value will be
35
+ * added to the request payload as defined by its `security` array.
36
+ */
37
+ auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken;
38
+ /**
39
+ * A function for serializing request body parameter. By default,
40
+ * {@link JSON.stringify()} will be used.
41
+ */
42
+ bodySerializer?: BodySerializer | null;
43
+ /**
44
+ * An object containing any HTTP headers that you want to pre-populate your
45
+ * `Headers` object with.
46
+ *
47
+ * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more}
48
+ */
49
+ headers?:
50
+ | RequestInit['headers']
51
+ | Record<
52
+ string,
53
+ | string
54
+ | number
55
+ | boolean
56
+ | (string | number | boolean)[]
57
+ | null
58
+ | undefined
59
+ | unknown
60
+ >;
61
+ /**
62
+ * The request method.
63
+ *
64
+ * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more}
65
+ */
66
+ method?:
67
+ | 'CONNECT'
68
+ | 'DELETE'
69
+ | 'GET'
70
+ | 'HEAD'
71
+ | 'OPTIONS'
72
+ | 'PATCH'
73
+ | 'POST'
74
+ | 'PUT'
75
+ | 'TRACE';
76
+ /**
77
+ * A function for serializing request query parameters. By default, arrays
78
+ * will be exploded in form style, objects will be exploded in deepObject
79
+ * style, and reserved characters are percent-encoded.
80
+ *
81
+ * This method will have no effect if the native `paramsSerializer()` Axios
82
+ * API function is used.
83
+ *
84
+ * {@link https://swagger.io/docs/specification/serialization/#query View examples}
85
+ */
86
+ querySerializer?: QuerySerializer | QuerySerializerOptions;
87
+ /**
88
+ * A function validating request data. This is useful if you want to ensure
89
+ * the request conforms to the desired shape, so it can be safely sent to
90
+ * the server.
91
+ */
92
+ requestValidator?: (data: unknown) => Promise<unknown>;
93
+ /**
94
+ * A function transforming response data before it's returned. This is useful
95
+ * for post-processing data, e.g. converting ISO strings into Date objects.
96
+ */
97
+ responseTransformer?: (data: unknown) => Promise<unknown>;
98
+ /**
99
+ * A function validating response data. This is useful if you want to ensure
100
+ * the response conforms to the desired shape, so it can be safely passed to
101
+ * the transformers and returned to the user.
102
+ */
103
+ responseValidator?: (data: unknown) => Promise<unknown>;
104
+ }
105
+
106
+ type IsExactlyNeverOrNeverUndefined<T> = [T] extends [never]
107
+ ? true
108
+ : [T] extends [never | undefined]
109
+ ? [undefined] extends [T]
110
+ ? false
111
+ : true
112
+ : false;
113
+
114
+ export type OmitNever<T extends Record<string, unknown>> = {
115
+ [K in keyof T as IsExactlyNeverOrNeverUndefined<T[K]> extends true
116
+ ? never
117
+ : K]: T[K];
118
+ };
package/src/index.ts ADDED
@@ -0,0 +1,129 @@
1
+ // Import generated SDKs with shared client support
2
+ import { createClient } from './client';
3
+ import type { Client } from './client/types';
4
+ import { getManagementBaseUrl, type Region } from '@storyblok/region-helper';
5
+ import { sdkRegistry, SdkRegistryInstance } from './sdk-registry.generated';
6
+
7
+ type PersonalAccessToken = {
8
+ accessToken: string;
9
+ }
10
+
11
+ type OAuthToken = {
12
+ oauthToken: string;
13
+ }
14
+
15
+ export interface ManagementApiClientConfig<ThrowOnError extends boolean = false> {
16
+ token: PersonalAccessToken | OAuthToken;
17
+ region?: Region;
18
+ baseUrl?: string; // Override for custom endpoints
19
+ headers?: Record<string, string>;
20
+ throwOnError?: ThrowOnError;
21
+ }
22
+
23
+ export interface ManagementApiClient<ThrowOnError extends boolean = false> extends SdkRegistryInstance {}
24
+ export class ManagementApiClient<ThrowOnError extends boolean = false> {
25
+ protected client: Client;
26
+ protected config: ManagementApiClientConfig<ThrowOnError>;
27
+ protected sdkCache: Record<string, Promise<any>> = {};
28
+
29
+ constructor(config: ManagementApiClientConfig<ThrowOnError>) {
30
+ this.config = config;
31
+ this.client = createClientInstance(config);
32
+
33
+ Object.entries(sdkRegistry).forEach(([name, Sdk]) => {
34
+ Object.defineProperty(this, name, {
35
+ get: () => new Sdk({ client: this.client }),
36
+ enumerable: true,
37
+ configurable: true
38
+ });
39
+ });
40
+ }
41
+
42
+
43
+ /**
44
+ * @returns The client's interceptors
45
+ * @example
46
+ * client.interceptors.request.use((request, options) => {
47
+ * console.log('Request:', request);
48
+ * return request;
49
+ * });
50
+ */
51
+ public get interceptors(): Client['interceptors'] {
52
+ return this.client.interceptors;
53
+ }
54
+
55
+ /**
56
+ * @param config - The configuration to set
57
+ * @example
58
+ * client.setConfig({ region: 'eu' });
59
+ */
60
+ setConfig(config: Partial<Omit<ManagementApiClientConfig, 'token'>>): void {
61
+ const { region, baseUrl, headers } = config;
62
+
63
+ let finalBaseUrl = baseUrl;
64
+ if (region && !baseUrl) {
65
+ finalBaseUrl = getManagementBaseUrl(region, "https");
66
+ }
67
+
68
+ if (finalBaseUrl) {
69
+ this.client.setConfig({ baseUrl: finalBaseUrl });
70
+ }
71
+
72
+ if (headers) {
73
+ this.client.setConfig({
74
+ headers: {
75
+ 'Content-Type': 'application/json',
76
+ ...getAuthHeader(this.config.token),
77
+ ...headers
78
+ }
79
+ });
80
+ }
81
+ }
82
+
83
+ /**
84
+ * @param token - The token to set
85
+ * @example
86
+ * client.setToken({ accessToken: '123' });
87
+ */
88
+ setToken(token: PersonalAccessToken | OAuthToken): void {
89
+ this.config.token = token;
90
+ this.client.setConfig({
91
+ headers: {
92
+ 'Content-Type': 'application/json',
93
+ ...getAuthHeader(token),
94
+ ...this.config.headers
95
+ }
96
+ });
97
+ }
98
+ }
99
+
100
+ // Pure functions for client creation and setup
101
+ function createClientInstance<ThrowOnError extends boolean = false>(
102
+ config: ManagementApiClientConfig<ThrowOnError>
103
+ ): Client {
104
+ const { token, region = "eu", baseUrl, headers = {}, throwOnError = false } = config;
105
+
106
+ const finalBaseUrl = baseUrl || getManagementBaseUrl(region, 'https');
107
+
108
+ return createClient({
109
+ baseUrl: finalBaseUrl,
110
+ headers: {
111
+ 'Content-Type': 'application/json',
112
+ ...getAuthHeader(token),
113
+ ...headers
114
+ },
115
+ throwOnError: throwOnError ?? false
116
+ });
117
+ }
118
+
119
+ function getAuthHeader(token: PersonalAccessToken | OAuthToken): Record<string, string> {
120
+ return 'accessToken' in token ? {
121
+ 'Authorization': token.accessToken
122
+ } : {
123
+ 'Authorization': token.oauthToken
124
+ }
125
+ }
126
+
127
+ // Export client utilities
128
+ export { createClient } from './client';
129
+ export type { Client } from './client/types';
@@ -0,0 +1,13 @@
1
+ // Shared client for all generated SDKs
2
+ import { createClient, type Client, type Config } from './client';
3
+
4
+ // Create a shared client instance that can be used by all generated SDKs
5
+ export const sharedClient = createClient();
6
+
7
+ // Export types for generated SDKs
8
+ export type { Client, Config } from './client';
9
+
10
+ // Helper to create a client with custom config
11
+ export const createSharedClient = (config?: Config): Client => {
12
+ return createClient(config);
13
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
5
+ "useDefineForClassFields": true,
6
+ "module": "ESNext",
7
+
8
+ /* Bundler mode */
9
+ "moduleResolution": "bundler",
10
+ "resolveJsonModule": true,
11
+ "allowImportingTsExtensions": true,
12
+
13
+ /* Linting */
14
+ "strict": true,
15
+ "noFallthroughCasesInSwitch": true,
16
+ "noUnusedLocals": true,
17
+ "noUnusedParameters": true,
18
+ "noEmit": true,
19
+ "isolatedModules": true,
20
+ "skipLibCheck": true,
21
+ "types": ["node"]
22
+ },
23
+ "include": ["src", "examples", "dist"],
24
+ "exclude": ["node_modules", "src/**/*.cy.ts", "src/**/*.test.ts"]
25
+ }
@@ -0,0 +1,21 @@
1
+ import { defineConfig } from 'tsdown';
2
+
3
+ export default defineConfig({
4
+ entry: [
5
+ './src/index.ts',
6
+ './src/generated/**/types.gen.ts',
7
+ ],
8
+ outDir: './dist',
9
+ platform: "neutral",
10
+ format: ['esm', "commonjs"],
11
+ globalName: 'StoryblokManagementApiClient',
12
+ sourcemap: true,
13
+ clean: true,
14
+ dts: true,
15
+ unbundle: true,
16
+ external: [
17
+ // Externalize generated client duplicates to reduce bundle size
18
+ "./src/generated/*/client/*",
19
+ "./src/generated/*/core/*"
20
+ ]
21
+ });