@igorchugurov/public-api-sdk 1.0.0 → 1.2.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 +127 -3
- package/dist/{client-DqqjGYgA.d.mts → client-DS5xnLAo.d.mts} +20 -0
- package/dist/{client-DqqjGYgA.d.ts → client-DS5xnLAo.d.ts} +20 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +239 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +239 -0
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +254 -0
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +254 -0
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { createBrowserClient as createBrowserClient$1 } from '@supabase/ssr';
|
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __defProps = Object.defineProperties;
|
|
5
5
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
7
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
7
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
9
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
@@ -19,6 +20,61 @@ var __spreadValues = (a, b) => {
|
|
|
19
20
|
return a;
|
|
20
21
|
};
|
|
21
22
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
23
|
+
var __esm = (fn, res) => function __init() {
|
|
24
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
25
|
+
};
|
|
26
|
+
var __export = (target, all) => {
|
|
27
|
+
for (var name in all)
|
|
28
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// src/utils/slug.ts
|
|
32
|
+
var slug_exports = {};
|
|
33
|
+
__export(slug_exports, {
|
|
34
|
+
generateSlug: () => generateSlug,
|
|
35
|
+
generateUniqueSlugForInstance: () => generateUniqueSlugForInstance,
|
|
36
|
+
validateSlug: () => validateSlug
|
|
37
|
+
});
|
|
38
|
+
function generateSlug(name) {
|
|
39
|
+
if (!name || typeof name !== "string") {
|
|
40
|
+
throw new Error("Name must be a non-empty string");
|
|
41
|
+
}
|
|
42
|
+
return name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").substring(0, 100);
|
|
43
|
+
}
|
|
44
|
+
function validateSlug(slug) {
|
|
45
|
+
if (!slug || typeof slug !== "string") {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (slug.length === 0 || slug.length > 100) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
52
|
+
return slugRegex.test(slug);
|
|
53
|
+
}
|
|
54
|
+
function generateRandomSuffix() {
|
|
55
|
+
return Math.random().toString(36).substring(2, 6);
|
|
56
|
+
}
|
|
57
|
+
async function generateUniqueSlugForInstance(name, entityDefinitionId, checkExists, excludeId) {
|
|
58
|
+
const baseSlug = generateSlug(name);
|
|
59
|
+
let slug = baseSlug;
|
|
60
|
+
let attempts = 0;
|
|
61
|
+
const maxAttempts = 100;
|
|
62
|
+
while (attempts < maxAttempts) {
|
|
63
|
+
const exists = await checkExists(slug, entityDefinitionId, excludeId);
|
|
64
|
+
if (!exists) {
|
|
65
|
+
return slug;
|
|
66
|
+
}
|
|
67
|
+
const randomSuffix = generateRandomSuffix();
|
|
68
|
+
slug = `${baseSlug}-${randomSuffix}`;
|
|
69
|
+
attempts++;
|
|
70
|
+
}
|
|
71
|
+
const timestamp = Date.now().toString(36);
|
|
72
|
+
return `${baseSlug}-${timestamp}`;
|
|
73
|
+
}
|
|
74
|
+
var init_slug = __esm({
|
|
75
|
+
"src/utils/slug.ts"() {
|
|
76
|
+
}
|
|
77
|
+
});
|
|
22
78
|
|
|
23
79
|
// src/types/entity-types.ts
|
|
24
80
|
function isFieldValue(value) {
|
|
@@ -395,6 +451,7 @@ var BasePublicAPIClient = class {
|
|
|
395
451
|
return {
|
|
396
452
|
id: row.id,
|
|
397
453
|
name: row.name,
|
|
454
|
+
slug: row.slug,
|
|
398
455
|
description: row.description,
|
|
399
456
|
tableName: row.table_name,
|
|
400
457
|
type: row.type,
|
|
@@ -517,6 +574,38 @@ var BasePublicAPIClient = class {
|
|
|
517
574
|
}
|
|
518
575
|
return config;
|
|
519
576
|
}
|
|
577
|
+
/**
|
|
578
|
+
* Получить все EntityDefinitions проекта с полями одним запросом (JOIN)
|
|
579
|
+
* Используется для загрузки всех сущностей в layout
|
|
580
|
+
*
|
|
581
|
+
* @returns Массив EntityDefinitionConfig с полями
|
|
582
|
+
*/
|
|
583
|
+
async getAllEntityDefinitions() {
|
|
584
|
+
const { data, error } = await this.supabase.from("entity_definition").select(
|
|
585
|
+
`
|
|
586
|
+
*,
|
|
587
|
+
field!field_entity_definition_id_fkey (*)
|
|
588
|
+
`
|
|
589
|
+
).eq("project_id", this.projectId).order("name");
|
|
590
|
+
if (error) {
|
|
591
|
+
throw new Error(`Failed to load entity definitions: ${error.message}`);
|
|
592
|
+
}
|
|
593
|
+
if (!data || data.length === 0) {
|
|
594
|
+
return [];
|
|
595
|
+
}
|
|
596
|
+
return data.map((row) => {
|
|
597
|
+
const entityDefinition = this.transformEntityDefinitionFromDB(row);
|
|
598
|
+
const fields = (row.field || []).map(
|
|
599
|
+
(fieldRow) => this.transformFieldFromDB(fieldRow)
|
|
600
|
+
);
|
|
601
|
+
fields.sort(
|
|
602
|
+
(a, b) => a.displayIndex - b.displayIndex
|
|
603
|
+
);
|
|
604
|
+
return __spreadProps(__spreadValues({}, entityDefinition), {
|
|
605
|
+
fields
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
}
|
|
520
609
|
/**
|
|
521
610
|
* Преобразование EntityDefinitionConfig в EntityDefinition
|
|
522
611
|
*/
|
|
@@ -524,6 +613,7 @@ var BasePublicAPIClient = class {
|
|
|
524
613
|
return {
|
|
525
614
|
id: config.id,
|
|
526
615
|
name: config.name,
|
|
616
|
+
slug: config.slug,
|
|
527
617
|
description: config.description,
|
|
528
618
|
tableName: config.tableName,
|
|
529
619
|
type: config.type,
|
|
@@ -635,6 +725,7 @@ var BasePublicAPIClient = class {
|
|
|
635
725
|
function transformEntityInstance(row) {
|
|
636
726
|
return {
|
|
637
727
|
id: row.id,
|
|
728
|
+
slug: row.slug,
|
|
638
729
|
entityDefinitionId: row.entity_definition_id,
|
|
639
730
|
projectId: row.project_id,
|
|
640
731
|
data: row.data || {},
|
|
@@ -645,6 +736,7 @@ function transformEntityInstance(row) {
|
|
|
645
736
|
function flattenInstance(instance, fields, relationsAsIds = false) {
|
|
646
737
|
const result = {
|
|
647
738
|
id: instance.id,
|
|
739
|
+
slug: instance.slug,
|
|
648
740
|
entityDefinitionId: instance.entityDefinitionId,
|
|
649
741
|
projectId: instance.projectId,
|
|
650
742
|
createdAt: instance.createdAt,
|
|
@@ -673,6 +765,7 @@ function flattenInstance(instance, fields, relationsAsIds = false) {
|
|
|
673
765
|
}
|
|
674
766
|
|
|
675
767
|
// src/client.ts
|
|
768
|
+
init_slug();
|
|
676
769
|
var _PublicAPIClient = class _PublicAPIClient extends BasePublicAPIClient {
|
|
677
770
|
constructor(supabase, projectId, mode, options = {}) {
|
|
678
771
|
super(supabase, projectId, options);
|
|
@@ -824,6 +917,134 @@ var _PublicAPIClient = class _PublicAPIClient extends BasePublicAPIClient {
|
|
|
824
917
|
handleSupabaseError(error);
|
|
825
918
|
}
|
|
826
919
|
}
|
|
920
|
+
/**
|
|
921
|
+
* Получить один экземпляр по slug
|
|
922
|
+
*/
|
|
923
|
+
async getInstanceBySlug(entityDefinitionId, slug, params) {
|
|
924
|
+
var _a;
|
|
925
|
+
try {
|
|
926
|
+
if (!validateSlug(slug)) {
|
|
927
|
+
throw new ValidationError(
|
|
928
|
+
"slug",
|
|
929
|
+
"Slug must contain only lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen"
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
const fields = await this.getFields(entityDefinitionId);
|
|
933
|
+
const { data: instance, error: instanceError } = await this.supabase.from("entity_instance").select("*").eq("slug", slug).eq("entity_definition_id", entityDefinitionId).eq("project_id", this.projectId).single();
|
|
934
|
+
if (instanceError || !instance) {
|
|
935
|
+
handleInstanceError(
|
|
936
|
+
instanceError || new Error("Instance not found"),
|
|
937
|
+
slug
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
const transformedInstance = transformEntityInstance(instance);
|
|
941
|
+
if (transformedInstance.entityDefinitionId !== entityDefinitionId) {
|
|
942
|
+
throw new NotFoundError("Entity instance", slug);
|
|
943
|
+
}
|
|
944
|
+
const relationFields = fields.filter(
|
|
945
|
+
(f) => f.dbType === "manyToMany" || f.dbType === "manyToOne" || f.dbType === "oneToMany" || f.dbType === "oneToOne"
|
|
946
|
+
);
|
|
947
|
+
const relations = {};
|
|
948
|
+
if (relationFields.length > 0) {
|
|
949
|
+
const relationFieldIds = relationFields.map((f) => f.id).filter((id) => Boolean(id));
|
|
950
|
+
if (relationFieldIds.length > 0) {
|
|
951
|
+
const { data: allRelations, error: relationsError } = await this.supabase.from("entity_relation").select("target_instance_id, relation_field_id").eq("source_instance_id", transformedInstance.id).in("relation_field_id", relationFieldIds);
|
|
952
|
+
if (relationsError) {
|
|
953
|
+
throw new SDKError(
|
|
954
|
+
"RELATIONS_LOAD_ERROR",
|
|
955
|
+
`Failed to load relations for instance with slug ${slug}: ${relationsError.message}`,
|
|
956
|
+
500,
|
|
957
|
+
relationsError
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
if (allRelations && allRelations.length > 0) {
|
|
961
|
+
const targetInstanceIds = [
|
|
962
|
+
...new Set(allRelations.map((r) => r.target_instance_id))
|
|
963
|
+
];
|
|
964
|
+
const { data: relatedInstances, error: instancesError } = await this.supabase.from("entity_instance").select("*").in("id", targetInstanceIds);
|
|
965
|
+
if (instancesError) {
|
|
966
|
+
throw new SDKError(
|
|
967
|
+
"RELATED_INSTANCES_LOAD_ERROR",
|
|
968
|
+
`Failed to load related instances for instance with slug ${slug}: ${instancesError.message}`,
|
|
969
|
+
500,
|
|
970
|
+
instancesError
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
if (relatedInstances) {
|
|
974
|
+
const relatedInstancesMap = new Map(
|
|
975
|
+
relatedInstances.map((inst) => [
|
|
976
|
+
inst.id,
|
|
977
|
+
transformEntityInstance(inst)
|
|
978
|
+
])
|
|
979
|
+
);
|
|
980
|
+
for (const relation of allRelations) {
|
|
981
|
+
const relationField = relationFields.find(
|
|
982
|
+
(f) => f.id === relation.relation_field_id
|
|
983
|
+
);
|
|
984
|
+
if (relationField) {
|
|
985
|
+
const relatedInstance = relatedInstancesMap.get(
|
|
986
|
+
relation.target_instance_id
|
|
987
|
+
);
|
|
988
|
+
if (relatedInstance) {
|
|
989
|
+
if (!relations[relationField.name]) {
|
|
990
|
+
relations[relationField.name] = [];
|
|
991
|
+
}
|
|
992
|
+
relations[relationField.name].push(
|
|
993
|
+
relatedInstance
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
const fileFields = fields.filter(
|
|
1003
|
+
(f) => f.type === "files" || f.type === "images"
|
|
1004
|
+
);
|
|
1005
|
+
if (fileFields.length > 0) {
|
|
1006
|
+
const { data: allFiles, error: filesError } = await this.supabase.from("entity_file").select("id, field_id").eq("entity_instance_id", transformedInstance.id);
|
|
1007
|
+
if (filesError) {
|
|
1008
|
+
throw new SDKError(
|
|
1009
|
+
"FILES_LOAD_ERROR",
|
|
1010
|
+
`Failed to load files for instance with slug ${slug}: ${filesError.message}`,
|
|
1011
|
+
500,
|
|
1012
|
+
filesError
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
if (allFiles) {
|
|
1016
|
+
const filesByFieldId = /* @__PURE__ */ new Map();
|
|
1017
|
+
allFiles.forEach((file) => {
|
|
1018
|
+
if (file.field_id) {
|
|
1019
|
+
if (!filesByFieldId.has(file.field_id)) {
|
|
1020
|
+
filesByFieldId.set(file.field_id, []);
|
|
1021
|
+
}
|
|
1022
|
+
filesByFieldId.get(file.field_id).push(file.id);
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
fileFields.forEach((field) => {
|
|
1026
|
+
const fileIds = filesByFieldId.get(field.id) || [];
|
|
1027
|
+
if (fileIds.length > 0 || !transformedInstance.data[field.name]) {
|
|
1028
|
+
transformedInstance.data[field.name] = fileIds;
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
const instanceWithRelations = __spreadProps(__spreadValues({}, transformedInstance), {
|
|
1034
|
+
relations: Object.keys(relations).length > 0 ? relations : void 0
|
|
1035
|
+
});
|
|
1036
|
+
return flattenInstance(
|
|
1037
|
+
instanceWithRelations,
|
|
1038
|
+
fields.map((f) => ({ name: f.name, dbType: f.dbType })),
|
|
1039
|
+
(_a = params == null ? void 0 : params.relationsAsIds) != null ? _a : false
|
|
1040
|
+
);
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
if (error instanceof NotFoundError || error instanceof PermissionDeniedError || error instanceof ValidationError || error instanceof SDKError) {
|
|
1043
|
+
throw error;
|
|
1044
|
+
}
|
|
1045
|
+
handleSupabaseError(error);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
827
1048
|
/**
|
|
828
1049
|
* Получить список экземпляров
|
|
829
1050
|
* Поддерживает поиск, фильтры (JSONB и relation), пагинацию
|
|
@@ -1212,9 +1433,27 @@ var _PublicAPIClient = class _PublicAPIClient extends BasePublicAPIClient {
|
|
|
1212
1433
|
);
|
|
1213
1434
|
}
|
|
1214
1435
|
const { data: instanceData, relations } = data;
|
|
1436
|
+
const name = instanceData.name;
|
|
1437
|
+
if (!name || typeof name !== "string") {
|
|
1438
|
+
throw new Error(
|
|
1439
|
+
"Field 'name' is required and must be a string for slug generation"
|
|
1440
|
+
);
|
|
1441
|
+
}
|
|
1442
|
+
const {
|
|
1443
|
+
generateUniqueSlugForInstance: generateUniqueSlugForInstance2
|
|
1444
|
+
} = await Promise.resolve().then(() => (init_slug(), slug_exports));
|
|
1445
|
+
const slug = await generateUniqueSlugForInstance2(
|
|
1446
|
+
name,
|
|
1447
|
+
entityDefinitionId,
|
|
1448
|
+
async (slugToCheck, entityDefIdToCheck) => {
|
|
1449
|
+
const { data: existing } = await this.supabase.from("entity_instance").select("id").eq("entity_definition_id", entityDefIdToCheck).eq("slug", slugToCheck).single();
|
|
1450
|
+
return !!existing;
|
|
1451
|
+
}
|
|
1452
|
+
);
|
|
1215
1453
|
const { data: instance, error: instanceError } = await this.supabase.from("entity_instance").insert({
|
|
1216
1454
|
entity_definition_id: entityDefinitionId,
|
|
1217
1455
|
project_id: this.projectId,
|
|
1456
|
+
slug,
|
|
1218
1457
|
data: instanceData,
|
|
1219
1458
|
created_by: (user == null ? void 0 : user.id) || null
|
|
1220
1459
|
}).select().single();
|