@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 +2 -0
- package/dist/cli.js +7 -0
- package/dist/generate-strapi-types.d.ts +5 -0
- package/dist/generate-strapi-types.js +245 -0
- package/dist/main.d.ts +68 -0
- package/dist/main.js +67 -0
- package/package.json +23 -0
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -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
|
+
}
|