@igorchugurov/public-api-sdk 1.1.0 → 1.3.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/LICENSE +1 -0
- package/README.md +56 -1
- package/dist/{client-BV5AAKYo.d.mts → client-D3jtlTId.d.mts} +18 -0
- package/dist/{client-BV5AAKYo.d.ts → client-D3jtlTId.d.ts} +18 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +183 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +183 -26
- 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 +198 -26
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +198 -26
- package/dist/server.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -32,7 +32,8 @@ var __export = (target, all) => {
|
|
|
32
32
|
var slug_exports = {};
|
|
33
33
|
__export(slug_exports, {
|
|
34
34
|
generateSlug: () => generateSlug,
|
|
35
|
-
generateUniqueSlugForInstance: () => generateUniqueSlugForInstance
|
|
35
|
+
generateUniqueSlugForInstance: () => generateUniqueSlugForInstance,
|
|
36
|
+
validateSlug: () => validateSlug
|
|
36
37
|
});
|
|
37
38
|
function generateSlug(name) {
|
|
38
39
|
if (!name || typeof name !== "string") {
|
|
@@ -40,6 +41,16 @@ function generateSlug(name) {
|
|
|
40
41
|
}
|
|
41
42
|
return name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").substring(0, 100);
|
|
42
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
|
+
}
|
|
43
54
|
function generateRandomSuffix() {
|
|
44
55
|
return Math.random().toString(36).substring(2, 6);
|
|
45
56
|
}
|
|
@@ -752,8 +763,25 @@ function flattenInstance(instance, fields, relationsAsIds = false) {
|
|
|
752
763
|
}
|
|
753
764
|
return result;
|
|
754
765
|
}
|
|
766
|
+
function transformEntityFile(row) {
|
|
767
|
+
return {
|
|
768
|
+
id: row.id,
|
|
769
|
+
entityInstanceId: row.entity_instance_id,
|
|
770
|
+
fieldId: row.field_id,
|
|
771
|
+
fileUrl: row.file_url,
|
|
772
|
+
filePath: row.file_path,
|
|
773
|
+
fileName: row.file_name,
|
|
774
|
+
fileSize: row.file_size,
|
|
775
|
+
fileType: row.file_type,
|
|
776
|
+
storageBucket: row.storage_bucket,
|
|
777
|
+
uploadedBy: row.uploaded_by,
|
|
778
|
+
createdAt: row.created_at,
|
|
779
|
+
updatedAt: row.updated_at
|
|
780
|
+
};
|
|
781
|
+
}
|
|
755
782
|
|
|
756
783
|
// src/client.ts
|
|
784
|
+
init_slug();
|
|
757
785
|
var _PublicAPIClient = class _PublicAPIClient extends BasePublicAPIClient {
|
|
758
786
|
constructor(supabase, projectId, mode, options = {}) {
|
|
759
787
|
super(supabase, projectId, options);
|
|
@@ -783,6 +811,42 @@ var _PublicAPIClient = class _PublicAPIClient extends BasePublicAPIClient {
|
|
|
783
811
|
getMode() {
|
|
784
812
|
return this.mode;
|
|
785
813
|
}
|
|
814
|
+
/**
|
|
815
|
+
* Загружает файлы для экземпляра и возвращает их как полные объекты EntityFile,
|
|
816
|
+
* сгруппированные по именам полей
|
|
817
|
+
*/
|
|
818
|
+
async loadFilesForInstance(instanceId, fileFields) {
|
|
819
|
+
if (fileFields.length === 0) {
|
|
820
|
+
return /* @__PURE__ */ new Map();
|
|
821
|
+
}
|
|
822
|
+
const { data: allFiles, error: filesError } = await this.supabase.from("entity_file").select("*").eq("entity_instance_id", instanceId);
|
|
823
|
+
if (filesError) {
|
|
824
|
+
throw new SDKError(
|
|
825
|
+
"FILES_LOAD_ERROR",
|
|
826
|
+
`Failed to load files for instance ${instanceId}: ${filesError.message}`,
|
|
827
|
+
500,
|
|
828
|
+
filesError
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
if (!allFiles || allFiles.length === 0) {
|
|
832
|
+
return /* @__PURE__ */ new Map();
|
|
833
|
+
}
|
|
834
|
+
const filesMap = /* @__PURE__ */ new Map();
|
|
835
|
+
const fieldIdToName = new Map(
|
|
836
|
+
fileFields.map((f) => [f.id, f.name])
|
|
837
|
+
);
|
|
838
|
+
allFiles.forEach((fileRow) => {
|
|
839
|
+
if (!fileRow.field_id) return;
|
|
840
|
+
const fieldName = fieldIdToName.get(fileRow.field_id);
|
|
841
|
+
if (!fieldName) return;
|
|
842
|
+
const entityFile = transformEntityFile(fileRow);
|
|
843
|
+
if (!filesMap.has(fieldName)) {
|
|
844
|
+
filesMap.set(fieldName, []);
|
|
845
|
+
}
|
|
846
|
+
filesMap.get(fieldName).push(entityFile);
|
|
847
|
+
});
|
|
848
|
+
return filesMap;
|
|
849
|
+
}
|
|
786
850
|
/**
|
|
787
851
|
* Получить один экземпляр
|
|
788
852
|
*/
|
|
@@ -859,34 +923,127 @@ var _PublicAPIClient = class _PublicAPIClient extends BasePublicAPIClient {
|
|
|
859
923
|
}
|
|
860
924
|
}
|
|
861
925
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
filesError
|
|
873
|
-
);
|
|
926
|
+
if ((params == null ? void 0 : params.loadFiles) === true) {
|
|
927
|
+
const fileFields = fields.filter(
|
|
928
|
+
(f) => f.type === "files" || f.type === "images"
|
|
929
|
+
);
|
|
930
|
+
if (fileFields.length > 0) {
|
|
931
|
+
const filesMap = await this.loadFilesForInstance(id, fileFields);
|
|
932
|
+
fileFields.forEach((field) => {
|
|
933
|
+
const files = filesMap.get(field.name) || [];
|
|
934
|
+
transformedInstance.data[field.name] = files;
|
|
935
|
+
});
|
|
874
936
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
937
|
+
}
|
|
938
|
+
const instanceWithRelations = __spreadProps(__spreadValues({}, transformedInstance), {
|
|
939
|
+
relations: Object.keys(relations).length > 0 ? relations : void 0
|
|
940
|
+
});
|
|
941
|
+
return flattenInstance(
|
|
942
|
+
instanceWithRelations,
|
|
943
|
+
fields.map((f) => ({ name: f.name, dbType: f.dbType })),
|
|
944
|
+
(_a = params == null ? void 0 : params.relationsAsIds) != null ? _a : false
|
|
945
|
+
);
|
|
946
|
+
} catch (error) {
|
|
947
|
+
if (error instanceof NotFoundError || error instanceof PermissionDeniedError || error instanceof SDKError) {
|
|
948
|
+
throw error;
|
|
949
|
+
}
|
|
950
|
+
handleSupabaseError(error);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Получить один экземпляр по slug
|
|
955
|
+
*/
|
|
956
|
+
async getInstanceBySlug(entityDefinitionId, slug, params) {
|
|
957
|
+
var _a;
|
|
958
|
+
try {
|
|
959
|
+
if (!validateSlug(slug)) {
|
|
960
|
+
throw new ValidationError(
|
|
961
|
+
"slug",
|
|
962
|
+
"Slug must contain only lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen"
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
const fields = await this.getFields(entityDefinitionId);
|
|
966
|
+
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();
|
|
967
|
+
if (instanceError || !instance) {
|
|
968
|
+
handleInstanceError(
|
|
969
|
+
instanceError || new Error("Instance not found"),
|
|
970
|
+
slug
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
const transformedInstance = transformEntityInstance(instance);
|
|
974
|
+
if (transformedInstance.entityDefinitionId !== entityDefinitionId) {
|
|
975
|
+
throw new NotFoundError("Entity instance", slug);
|
|
976
|
+
}
|
|
977
|
+
const relationFields = fields.filter(
|
|
978
|
+
(f) => f.dbType === "manyToMany" || f.dbType === "manyToOne" || f.dbType === "oneToMany" || f.dbType === "oneToOne"
|
|
979
|
+
);
|
|
980
|
+
const relations = {};
|
|
981
|
+
if (relationFields.length > 0) {
|
|
982
|
+
const relationFieldIds = relationFields.map((f) => f.id).filter((id) => Boolean(id));
|
|
983
|
+
if (relationFieldIds.length > 0) {
|
|
984
|
+
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);
|
|
985
|
+
if (relationsError) {
|
|
986
|
+
throw new SDKError(
|
|
987
|
+
"RELATIONS_LOAD_ERROR",
|
|
988
|
+
`Failed to load relations for instance with slug ${slug}: ${relationsError.message}`,
|
|
989
|
+
500,
|
|
990
|
+
relationsError
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
if (allRelations && allRelations.length > 0) {
|
|
994
|
+
const targetInstanceIds = [
|
|
995
|
+
...new Set(allRelations.map((r) => r.target_instance_id))
|
|
996
|
+
];
|
|
997
|
+
const { data: relatedInstances, error: instancesError } = await this.supabase.from("entity_instance").select("*").in("id", targetInstanceIds);
|
|
998
|
+
if (instancesError) {
|
|
999
|
+
throw new SDKError(
|
|
1000
|
+
"RELATED_INSTANCES_LOAD_ERROR",
|
|
1001
|
+
`Failed to load related instances for instance with slug ${slug}: ${instancesError.message}`,
|
|
1002
|
+
500,
|
|
1003
|
+
instancesError
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
if (relatedInstances) {
|
|
1007
|
+
const relatedInstancesMap = new Map(
|
|
1008
|
+
relatedInstances.map((inst) => [
|
|
1009
|
+
inst.id,
|
|
1010
|
+
transformEntityInstance(inst)
|
|
1011
|
+
])
|
|
1012
|
+
);
|
|
1013
|
+
for (const relation of allRelations) {
|
|
1014
|
+
const relationField = relationFields.find(
|
|
1015
|
+
(f) => f.id === relation.relation_field_id
|
|
1016
|
+
);
|
|
1017
|
+
if (relationField) {
|
|
1018
|
+
const relatedInstance = relatedInstancesMap.get(
|
|
1019
|
+
relation.target_instance_id
|
|
1020
|
+
);
|
|
1021
|
+
if (relatedInstance) {
|
|
1022
|
+
if (!relations[relationField.name]) {
|
|
1023
|
+
relations[relationField.name] = [];
|
|
1024
|
+
}
|
|
1025
|
+
relations[relationField.name].push(
|
|
1026
|
+
relatedInstance
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
881
1030
|
}
|
|
882
|
-
filesByFieldId.get(file.field_id).push(file.id);
|
|
883
1031
|
}
|
|
884
|
-
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
if ((params == null ? void 0 : params.loadFiles) === true) {
|
|
1036
|
+
const fileFields = fields.filter(
|
|
1037
|
+
(f) => f.type === "files" || f.type === "images"
|
|
1038
|
+
);
|
|
1039
|
+
if (fileFields.length > 0) {
|
|
1040
|
+
const filesMap = await this.loadFilesForInstance(
|
|
1041
|
+
transformedInstance.id,
|
|
1042
|
+
fileFields
|
|
1043
|
+
);
|
|
885
1044
|
fileFields.forEach((field) => {
|
|
886
|
-
const
|
|
887
|
-
|
|
888
|
-
transformedInstance.data[field.name] = fileIds;
|
|
889
|
-
}
|
|
1045
|
+
const files = filesMap.get(field.name) || [];
|
|
1046
|
+
transformedInstance.data[field.name] = files;
|
|
890
1047
|
});
|
|
891
1048
|
}
|
|
892
1049
|
}
|
|
@@ -899,7 +1056,7 @@ var _PublicAPIClient = class _PublicAPIClient extends BasePublicAPIClient {
|
|
|
899
1056
|
(_a = params == null ? void 0 : params.relationsAsIds) != null ? _a : false
|
|
900
1057
|
);
|
|
901
1058
|
} catch (error) {
|
|
902
|
-
if (error instanceof NotFoundError || error instanceof PermissionDeniedError || error instanceof SDKError) {
|
|
1059
|
+
if (error instanceof NotFoundError || error instanceof PermissionDeniedError || error instanceof ValidationError || error instanceof SDKError) {
|
|
903
1060
|
throw error;
|
|
904
1061
|
}
|
|
905
1062
|
handleSupabaseError(error);
|