@malevich-studio/strapi-sdk-typescript 1.0.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/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const generate_strapi_types_1 = require("./generate-strapi-types");
5
+ (async () => {
6
+ await (0, generate_strapi_types_1.generateStrapiTypes)();
7
+ })();
@@ -0,0 +1,5 @@
1
+ import 'dotenv/config';
2
+ /**
3
+ * Main function to fetch Strapi (v5) data and generate the d.ts file
4
+ */
5
+ export declare function generateStrapiTypes(): Promise<void>;
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.generateStrapiTypes = generateStrapiTypes;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ require("dotenv/config");
43
+ const main_1 = __importDefault(require("./main"));
44
+ var ConfigTypeKind;
45
+ (function (ConfigTypeKind) {
46
+ ConfigTypeKind["CollectionType"] = "collectionType";
47
+ ConfigTypeKind["SingleType"] = "singleType";
48
+ })(ConfigTypeKind || (ConfigTypeKind = {}));
49
+ var AttributeType;
50
+ (function (AttributeType) {
51
+ AttributeType["BigInteger"] = "biginteger";
52
+ AttributeType["Blocks"] = "blocks";
53
+ AttributeType["Boolean"] = "boolean";
54
+ AttributeType["Component"] = "component";
55
+ AttributeType["DateTime"] = "datetime";
56
+ AttributeType["Decimal"] = "decimal";
57
+ AttributeType["Email"] = "email";
58
+ AttributeType["Enumeration"] = "enumeration";
59
+ AttributeType["Integer"] = "integer";
60
+ AttributeType["Json"] = "json";
61
+ AttributeType["Media"] = "media";
62
+ AttributeType["Password"] = "password";
63
+ AttributeType["Relation"] = "relation";
64
+ AttributeType["String"] = "string";
65
+ AttributeType["Text"] = "text";
66
+ // RichText = 'richtext',
67
+ })(AttributeType || (AttributeType = {}));
68
+ var AttributeRelation;
69
+ (function (AttributeRelation) {
70
+ AttributeRelation["MorphToMany"] = "morphToMany";
71
+ AttributeRelation["ManyToOne"] = "manyToOne";
72
+ AttributeRelation["ManyToMany"] = "manyToMany";
73
+ AttributeRelation["OneToMany"] = "oneToMany";
74
+ AttributeRelation["OneToOne"] = "oneToOne";
75
+ })(AttributeRelation || (AttributeRelation = {}));
76
+ function getComponentType(attribute) {
77
+ const componentTypeName = getContentTypeName(attribute.component);
78
+ return attribute.repeatable ? `${componentTypeName}[]` : componentTypeName;
79
+ }
80
+ function getRelationType(attribute) {
81
+ const ContentTypeName = getContentTypeName(attribute.target);
82
+ switch (attribute.relation) {
83
+ case AttributeRelation.ManyToMany:
84
+ case AttributeRelation.OneToMany:
85
+ case AttributeRelation.MorphToMany:
86
+ return `${ContentTypeName}[]`;
87
+ case AttributeRelation.ManyToOne:
88
+ case AttributeRelation.OneToOne:
89
+ default:
90
+ return ContentTypeName;
91
+ }
92
+ }
93
+ /**
94
+ * Maps basic Strapi types to TypeScript types
95
+ */
96
+ function getAttributeType(attribute) {
97
+ switch (attribute.type) {
98
+ case AttributeType.String:
99
+ case AttributeType.Text:
100
+ case AttributeType.Password:
101
+ case AttributeType.Email:
102
+ return 'string';
103
+ case AttributeType.Integer:
104
+ case AttributeType.BigInteger:
105
+ case AttributeType.Decimal:
106
+ return 'number';
107
+ case AttributeType.Boolean:
108
+ return 'boolean';
109
+ case AttributeType.Relation:
110
+ return getRelationType(attribute);
111
+ case AttributeType.Component:
112
+ return getComponentType(attribute);
113
+ case AttributeType.DateTime:
114
+ return 'string'; // Usually, date/time fields come as strings from the API
115
+ case AttributeType.Json:
116
+ return 'object'; // Could be Record<string, unknown> if you want stricter typing
117
+ case AttributeType.Media:
118
+ // Media fields can be objects or arrays of objects representing files
119
+ // You can type them more specifically if needed
120
+ return 'any';
121
+ case AttributeType.Enumeration:
122
+ return `'${attribute.enum.join('\' | \'')}'`;
123
+ default:
124
+ // For unrecognized types, we check further below
125
+ break;
126
+ }
127
+ }
128
+ /**
129
+ * Returns the TypeScript interface name from a component UID
130
+ * e.g. "default.test-component" => "DefaultTestComponent"
131
+ */
132
+ function getComponentName(uid) {
133
+ return uid
134
+ .split(/[\.\-]/)
135
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
136
+ .join('');
137
+ }
138
+ /**
139
+ * Returns the TypeScript interface name from a content type UID
140
+ * e.g. "api::article.article" => "Article"
141
+ */
142
+ function getContentTypeName(uid) {
143
+ // Usually, UIDs look like "api::<api-name>.<model-name>"
144
+ // We'll split at "::" and then take the part after the dot.
145
+ const namePart = uid.split('::')[1] || uid;
146
+ const modelName = namePart.split('.')[1] || namePart;
147
+ // Convert to PascalCase
148
+ return modelName
149
+ .split('-')
150
+ .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
151
+ .join('');
152
+ }
153
+ /**
154
+ * Generates a TS interface from a content type or component definition
155
+ */
156
+ function generateTypeCode(name, attributes) {
157
+ const lines = [];
158
+ lines.push(`export type ${name} = {`);
159
+ // Strapi's default fields:
160
+ lines.push(` id?: number;`);
161
+ lines.push(` createdAt?: string;`);
162
+ lines.push(` updatedAt?: string;`);
163
+ for (const attributeName in attributes) {
164
+ const attribute = attributes[attributeName];
165
+ const typeName = getAttributeType(attribute);
166
+ const isRequired = attribute.required ? '' : '?';
167
+ lines.push(` ${attributeName}${isRequired}: ${typeName};`);
168
+ }
169
+ lines.push(`}`);
170
+ return lines.join('\n');
171
+ }
172
+ function generateQueryTypeCode(name, attributes) {
173
+ const fields = [];
174
+ const sort = [];
175
+ const filters = [];
176
+ filters.push(`export type ${name}Filters = Filters<{`);
177
+ for (const attributeName in attributes) {
178
+ const attribute = attributes[attributeName];
179
+ fields.push(`'${attributeName}'`);
180
+ if ([
181
+ AttributeType.String,
182
+ AttributeType.Text,
183
+ AttributeType.Password,
184
+ AttributeType.Email,
185
+ AttributeType.Integer,
186
+ AttributeType.BigInteger,
187
+ AttributeType.Decimal,
188
+ AttributeType.Boolean,
189
+ AttributeType.DateTime,
190
+ AttributeType.Enumeration,
191
+ ].includes(attribute.type)) {
192
+ sort.push(`'${attributeName}'`);
193
+ sort.push(`'${attributeName}:asc'`);
194
+ sort.push(`'${attributeName}:desc'`);
195
+ }
196
+ const type = getAttributeType(attribute);
197
+ switch (attribute.type) {
198
+ case AttributeType.Relation:
199
+ filters.push(` ${attributeName}?: ${getContentTypeName(attribute.target)}Filters;`);
200
+ break;
201
+ case AttributeType.Component:
202
+ case AttributeType.Media:
203
+ case AttributeType.Json:
204
+ break;
205
+ default:
206
+ filters.push(` ${attributeName}?: FilterValue<${type}>;`);
207
+ }
208
+ }
209
+ filters.push(`}>`);
210
+ filters.push('');
211
+ const lines = [];
212
+ lines.push(...filters);
213
+ lines.push(`export type ${name}Query = Query<`);
214
+ lines.push(` ${fields.join(' | ')},`);
215
+ lines.push(` ${sort.join(' | ')},`);
216
+ lines.push(` ${name}Filters`);
217
+ lines.push(`>`);
218
+ return lines.join('\n');
219
+ }
220
+ /**
221
+ * Main function to fetch Strapi (v5) data and generate the d.ts file
222
+ */
223
+ async function generateStrapiTypes() {
224
+ const strapi = new main_1.default(process.env.STRAPI_URL, process.env.STRAPI_TOKEN);
225
+ const contentTypes = (await strapi.request('content-type-builder/content-types')).data;
226
+ const components = (await strapi.request('content-type-builder/components')).data;
227
+ const allInterfaces = [];
228
+ for (const component of components) {
229
+ const componentName = getComponentName(component.uid);
230
+ const code = generateTypeCode(componentName, component.schema.attributes);
231
+ allInterfaces.push(code);
232
+ }
233
+ for (const contentType of contentTypes) {
234
+ if (!contentType.uid.startsWith('api::')) {
235
+ continue;
236
+ }
237
+ const modelName = getContentTypeName(contentType.uid);
238
+ allInterfaces.push(generateTypeCode(modelName, contentType.schema.attributes));
239
+ allInterfaces.push(generateQueryTypeCode(modelName, contentType.schema.attributes));
240
+ }
241
+ const output = allInterfaces.join('\n\n');
242
+ const outPath = path.join(process.cwd(), 'strapi-types.d.ts');
243
+ fs.writeFileSync(outPath, output, 'utf-8');
244
+ console.log(`✅ "strapi-types.d.ts" has been successfully generated!`);
245
+ }
package/dist/main.d.ts ADDED
@@ -0,0 +1,68 @@
1
+ type Response<T> = {
2
+ data: T[];
3
+ meta: {
4
+ pagination: {
5
+ total?: number;
6
+ } & ({
7
+ page: number;
8
+ pageSize: number;
9
+ pageCount?: number;
10
+ } | {
11
+ start: number;
12
+ limit: number;
13
+ });
14
+ };
15
+ };
16
+ export type Filters<T> = {
17
+ $or?: Filters<T>[];
18
+ $and?: Filters<T>[];
19
+ $not?: Filters<T>[];
20
+ } | T;
21
+ export type FilterValue<T> = {
22
+ $eq?: T;
23
+ $eqi?: string;
24
+ $ne?: T;
25
+ $nei?: string;
26
+ $lt?: T;
27
+ $lte?: T;
28
+ $gt?: T;
29
+ $gte?: T;
30
+ $in?: T[];
31
+ $notIn?: T[];
32
+ $contains?: string;
33
+ $notContains?: string;
34
+ $containsi?: string;
35
+ $notContainsi?: string;
36
+ $between?: [T, T];
37
+ $startsWith?: string;
38
+ $startsWithi?: string;
39
+ $endsWith?: string;
40
+ $endsWithi?: string;
41
+ } | T;
42
+ export type Query<Fields, Sort, Filters, Populate> = {
43
+ populate?: Populate;
44
+ fields?: Fields[] | '*';
45
+ filters?: Filters;
46
+ locale?: string;
47
+ status?: 'published' | 'draft';
48
+ sort?: Sort[] | Sort;
49
+ pagination?: {
50
+ withCount?: boolean;
51
+ } & ({
52
+ page?: number;
53
+ pageSize?: number;
54
+ } | {
55
+ start?: number;
56
+ limit?: number;
57
+ });
58
+ };
59
+ export default class Strapi {
60
+ private readonly url;
61
+ private readonly token;
62
+ constructor(url: string, token: string);
63
+ request<T>(endpoint: string, data?: object, params?: RequestInit): Promise<Response<T>>;
64
+ getDocuments<T, Q extends object>(endpoint: string, data?: Q, params?: RequestInit): Promise<Response<T>>;
65
+ getDocument<T, Q extends object>(endpoint: string, data?: Q, params?: RequestInit): Promise<Response<T>>;
66
+ create<T>(endpoint: string, data?: object, params?: RequestInit): Promise<Response<T>>;
67
+ }
68
+ export {};
package/dist/main.js ADDED
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const qs_1 = __importDefault(require("qs"));
7
+ class Strapi {
8
+ constructor(url, token) {
9
+ this.url = url;
10
+ this.token = token;
11
+ }
12
+ async request(endpoint, data = {}, params = {}) {
13
+ const baseUrl = `${this.url}/api/`;
14
+ const queryString = params.method === 'GET' ? qs_1.default.stringify(data) : '';
15
+ const url = queryString ? `${baseUrl}${endpoint}?${queryString}` : `${baseUrl}${endpoint}`;
16
+ console.log({
17
+ headers: {
18
+ Authorization: `Bearer ${this.token}`,
19
+ 'Content-Type': 'application/json',
20
+ },
21
+ cache: 'no-store',
22
+ ...(params.method && params.method !== 'GET' ? {
23
+ body: JSON.stringify({
24
+ data,
25
+ })
26
+ } : {}),
27
+ ...params
28
+ });
29
+ const response = await fetch(url, {
30
+ headers: {
31
+ Authorization: `Bearer ${this.token}`,
32
+ 'Content-Type': 'application/json',
33
+ },
34
+ cache: 'no-store',
35
+ ...(params.method && params.method !== 'GET' ? {
36
+ body: JSON.stringify({
37
+ data,
38
+ })
39
+ } : {}),
40
+ ...params
41
+ });
42
+ console.log(response);
43
+ if (!response.ok) {
44
+ throw new Error(`Помилка запиту до Strapi: ${response.status} ${response.statusText}`);
45
+ }
46
+ return (await response.json());
47
+ }
48
+ async getDocuments(endpoint, data, params = {}) {
49
+ return await this.request(endpoint, data, {
50
+ method: 'GET',
51
+ ...params,
52
+ });
53
+ }
54
+ async getDocument(endpoint, data, params = {}) {
55
+ return await this.request(endpoint, data, {
56
+ method: 'GET',
57
+ ...params,
58
+ });
59
+ }
60
+ async create(endpoint, data = {}, params = {}) {
61
+ return await this.request(endpoint, data, {
62
+ method: 'POST',
63
+ ...params,
64
+ });
65
+ }
66
+ }
67
+ exports.default = Strapi;
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@malevich-studio/strapi-sdk-typescript",
3
+ "version": "1.0.0",
4
+ "main": "dist/index.js",
5
+ "bin": {
6
+ "generate-strapi-types": "dist/cli.js"
7
+ },
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "dependencies": {
16
+ "dotenv": "^16.4.7",
17
+ "lodash": "^4.17.21",
18
+ "typescript": "^5.7.3"
19
+ },
20
+ "author": "",
21
+ "license": "ISC",
22
+ "description": ""
23
+ }