@igorchugurov/public-api-sdk 1.1.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 +35 -1
- package/dist/{client-BV5AAKYo.d.mts → client-DS5xnLAo.d.mts} +9 -0
- package/dist/{client-BV5AAKYo.d.ts → client-DS5xnLAo.d.ts} +9 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +141 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +141 -1
- 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 +156 -1
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +156 -1
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
package/dist/server.d.mts
CHANGED
package/dist/server.d.ts
CHANGED
package/dist/server.js
CHANGED
|
@@ -34,7 +34,8 @@ var __export = (target, all) => {
|
|
|
34
34
|
var slug_exports = {};
|
|
35
35
|
__export(slug_exports, {
|
|
36
36
|
generateSlug: () => generateSlug,
|
|
37
|
-
generateUniqueSlugForInstance: () => generateUniqueSlugForInstance
|
|
37
|
+
generateUniqueSlugForInstance: () => generateUniqueSlugForInstance,
|
|
38
|
+
validateSlug: () => validateSlug
|
|
38
39
|
});
|
|
39
40
|
function generateSlug(name) {
|
|
40
41
|
if (!name || typeof name !== "string") {
|
|
@@ -42,6 +43,16 @@ function generateSlug(name) {
|
|
|
42
43
|
}
|
|
43
44
|
return name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").substring(0, 100);
|
|
44
45
|
}
|
|
46
|
+
function validateSlug(slug) {
|
|
47
|
+
if (!slug || typeof slug !== "string") {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
if (slug.length === 0 || slug.length > 100) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
54
|
+
return slugRegex.test(slug);
|
|
55
|
+
}
|
|
45
56
|
function generateRandomSuffix() {
|
|
46
57
|
return Math.random().toString(36).substring(2, 6);
|
|
47
58
|
}
|
|
@@ -584,6 +595,9 @@ function flattenInstance(instance, fields, relationsAsIds = false) {
|
|
|
584
595
|
return result;
|
|
585
596
|
}
|
|
586
597
|
|
|
598
|
+
// src/client.ts
|
|
599
|
+
init_slug();
|
|
600
|
+
|
|
587
601
|
// src/errors.ts
|
|
588
602
|
var SDKError = class extends Error {
|
|
589
603
|
constructor(code, message, statusCode, details) {
|
|
@@ -612,6 +626,19 @@ var PermissionDeniedError = class extends SDKError {
|
|
|
612
626
|
);
|
|
613
627
|
}
|
|
614
628
|
};
|
|
629
|
+
var ValidationError = class extends SDKError {
|
|
630
|
+
constructor(field, message) {
|
|
631
|
+
super(
|
|
632
|
+
"VALIDATION_ERROR",
|
|
633
|
+
`Validation failed for ${field}: ${message}`,
|
|
634
|
+
400,
|
|
635
|
+
{
|
|
636
|
+
field,
|
|
637
|
+
message
|
|
638
|
+
}
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
};
|
|
615
642
|
function handleSupabaseError(error) {
|
|
616
643
|
if (error.code === "PGRST116") {
|
|
617
644
|
throw new NotFoundError("Resource");
|
|
@@ -794,6 +821,134 @@ var _PublicAPIClient = class _PublicAPIClient extends BasePublicAPIClient {
|
|
|
794
821
|
handleSupabaseError(error);
|
|
795
822
|
}
|
|
796
823
|
}
|
|
824
|
+
/**
|
|
825
|
+
* Получить один экземпляр по slug
|
|
826
|
+
*/
|
|
827
|
+
async getInstanceBySlug(entityDefinitionId, slug, params) {
|
|
828
|
+
var _a;
|
|
829
|
+
try {
|
|
830
|
+
if (!validateSlug(slug)) {
|
|
831
|
+
throw new ValidationError(
|
|
832
|
+
"slug",
|
|
833
|
+
"Slug must contain only lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen"
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
const fields = await this.getFields(entityDefinitionId);
|
|
837
|
+
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();
|
|
838
|
+
if (instanceError || !instance) {
|
|
839
|
+
handleInstanceError(
|
|
840
|
+
instanceError || new Error("Instance not found"),
|
|
841
|
+
slug
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
const transformedInstance = transformEntityInstance(instance);
|
|
845
|
+
if (transformedInstance.entityDefinitionId !== entityDefinitionId) {
|
|
846
|
+
throw new NotFoundError("Entity instance", slug);
|
|
847
|
+
}
|
|
848
|
+
const relationFields = fields.filter(
|
|
849
|
+
(f) => f.dbType === "manyToMany" || f.dbType === "manyToOne" || f.dbType === "oneToMany" || f.dbType === "oneToOne"
|
|
850
|
+
);
|
|
851
|
+
const relations = {};
|
|
852
|
+
if (relationFields.length > 0) {
|
|
853
|
+
const relationFieldIds = relationFields.map((f) => f.id).filter((id) => Boolean(id));
|
|
854
|
+
if (relationFieldIds.length > 0) {
|
|
855
|
+
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);
|
|
856
|
+
if (relationsError) {
|
|
857
|
+
throw new SDKError(
|
|
858
|
+
"RELATIONS_LOAD_ERROR",
|
|
859
|
+
`Failed to load relations for instance with slug ${slug}: ${relationsError.message}`,
|
|
860
|
+
500,
|
|
861
|
+
relationsError
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
if (allRelations && allRelations.length > 0) {
|
|
865
|
+
const targetInstanceIds = [
|
|
866
|
+
...new Set(allRelations.map((r) => r.target_instance_id))
|
|
867
|
+
];
|
|
868
|
+
const { data: relatedInstances, error: instancesError } = await this.supabase.from("entity_instance").select("*").in("id", targetInstanceIds);
|
|
869
|
+
if (instancesError) {
|
|
870
|
+
throw new SDKError(
|
|
871
|
+
"RELATED_INSTANCES_LOAD_ERROR",
|
|
872
|
+
`Failed to load related instances for instance with slug ${slug}: ${instancesError.message}`,
|
|
873
|
+
500,
|
|
874
|
+
instancesError
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
if (relatedInstances) {
|
|
878
|
+
const relatedInstancesMap = new Map(
|
|
879
|
+
relatedInstances.map((inst) => [
|
|
880
|
+
inst.id,
|
|
881
|
+
transformEntityInstance(inst)
|
|
882
|
+
])
|
|
883
|
+
);
|
|
884
|
+
for (const relation of allRelations) {
|
|
885
|
+
const relationField = relationFields.find(
|
|
886
|
+
(f) => f.id === relation.relation_field_id
|
|
887
|
+
);
|
|
888
|
+
if (relationField) {
|
|
889
|
+
const relatedInstance = relatedInstancesMap.get(
|
|
890
|
+
relation.target_instance_id
|
|
891
|
+
);
|
|
892
|
+
if (relatedInstance) {
|
|
893
|
+
if (!relations[relationField.name]) {
|
|
894
|
+
relations[relationField.name] = [];
|
|
895
|
+
}
|
|
896
|
+
relations[relationField.name].push(
|
|
897
|
+
relatedInstance
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
const fileFields = fields.filter(
|
|
907
|
+
(f) => f.type === "files" || f.type === "images"
|
|
908
|
+
);
|
|
909
|
+
if (fileFields.length > 0) {
|
|
910
|
+
const { data: allFiles, error: filesError } = await this.supabase.from("entity_file").select("id, field_id").eq("entity_instance_id", transformedInstance.id);
|
|
911
|
+
if (filesError) {
|
|
912
|
+
throw new SDKError(
|
|
913
|
+
"FILES_LOAD_ERROR",
|
|
914
|
+
`Failed to load files for instance with slug ${slug}: ${filesError.message}`,
|
|
915
|
+
500,
|
|
916
|
+
filesError
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
if (allFiles) {
|
|
920
|
+
const filesByFieldId = /* @__PURE__ */ new Map();
|
|
921
|
+
allFiles.forEach((file) => {
|
|
922
|
+
if (file.field_id) {
|
|
923
|
+
if (!filesByFieldId.has(file.field_id)) {
|
|
924
|
+
filesByFieldId.set(file.field_id, []);
|
|
925
|
+
}
|
|
926
|
+
filesByFieldId.get(file.field_id).push(file.id);
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
fileFields.forEach((field) => {
|
|
930
|
+
const fileIds = filesByFieldId.get(field.id) || [];
|
|
931
|
+
if (fileIds.length > 0 || !transformedInstance.data[field.name]) {
|
|
932
|
+
transformedInstance.data[field.name] = fileIds;
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
const instanceWithRelations = __spreadProps(__spreadValues({}, transformedInstance), {
|
|
938
|
+
relations: Object.keys(relations).length > 0 ? relations : void 0
|
|
939
|
+
});
|
|
940
|
+
return flattenInstance(
|
|
941
|
+
instanceWithRelations,
|
|
942
|
+
fields.map((f) => ({ name: f.name, dbType: f.dbType })),
|
|
943
|
+
(_a = params == null ? void 0 : params.relationsAsIds) != null ? _a : false
|
|
944
|
+
);
|
|
945
|
+
} catch (error) {
|
|
946
|
+
if (error instanceof NotFoundError || error instanceof PermissionDeniedError || error instanceof ValidationError || error instanceof SDKError) {
|
|
947
|
+
throw error;
|
|
948
|
+
}
|
|
949
|
+
handleSupabaseError(error);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
797
952
|
/**
|
|
798
953
|
* Получить список экземпляров
|
|
799
954
|
* Поддерживает поиск, фильтры (JSONB и relation), пагинацию
|