@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/README.md
CHANGED
|
@@ -12,6 +12,56 @@ pnpm add @igorchugurov/public-api-sdk@workspace:*
|
|
|
12
12
|
pnpm add @igorchugurov/public-api-sdk
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
## ✨ Возможности
|
|
16
|
+
|
|
17
|
+
SDK предоставляет полный набор инструментов для работы с универсальными сущностями:
|
|
18
|
+
|
|
19
|
+
### 🔄 CRUD операции
|
|
20
|
+
- **Получение списка** с расширенной фильтрацией, поиском и пагинацией
|
|
21
|
+
- **Получение одного экземпляра** с автоматической загрузкой связей и файлов
|
|
22
|
+
- **Создание** экземпляров с поддержкой relations и автоматической установкой `created_by`
|
|
23
|
+
- **Обновление** экземпляров с поддержкой частичных обновлений и relations
|
|
24
|
+
- **Удаление** экземпляров с автоматической очисткой связей
|
|
25
|
+
|
|
26
|
+
### 🔗 Работа со связями (Relations)
|
|
27
|
+
- Автоматическое определение relation-полей из конфигурации
|
|
28
|
+
- Поддержка всех типов связей: `manyToMany`, `manyToOne`, `oneToMany`, `oneToOne`
|
|
29
|
+
- Batch-загрузка связанных объектов для оптимизации производительности
|
|
30
|
+
- Фильтрация по relations с режимами `any` (OR) и `all` (AND)
|
|
31
|
+
- Опция получения relations как ID или полных объектов
|
|
32
|
+
|
|
33
|
+
### 🔍 Поиск и фильтрация
|
|
34
|
+
- **Умный поиск** по полям с флагом `searchable: true`
|
|
35
|
+
- **JSONB фильтрация** для обычных полей
|
|
36
|
+
- **Relation фильтрация** с автоматическим определением relation-полей
|
|
37
|
+
- **Гибкая сортировка** по любому полю с поддержкой `asc`/`desc`
|
|
38
|
+
|
|
39
|
+
### 📁 Работа с файлами
|
|
40
|
+
- Автоматическая загрузка файлов и изображений для полей типа `files` и `images`
|
|
41
|
+
- Batch-загрузка файлов для оптимизации запросов
|
|
42
|
+
- Поддержка множественных файлов на поле
|
|
43
|
+
|
|
44
|
+
### ⚡ Производительность
|
|
45
|
+
- **Кэширование конфигурации** EntityDefinition и Fields (TTL: 5 минут по умолчанию)
|
|
46
|
+
- **Batch-запросы** для relations и файлов
|
|
47
|
+
- **Оптимизированные RPC функции** для поиска и загрузки связей
|
|
48
|
+
- Поддержка ESM и CJS форматов
|
|
49
|
+
|
|
50
|
+
### 🎯 Типобезопасность
|
|
51
|
+
- Полная типизация всех методов и параметров
|
|
52
|
+
- Экспорт всех типов для использования в вашем коде
|
|
53
|
+
- Типизированные ошибки для удобной обработки
|
|
54
|
+
|
|
55
|
+
### 🔐 Безопасность
|
|
56
|
+
- Интеграция с Supabase RLS (Row Level Security)
|
|
57
|
+
- Автоматическая проверка прав доступа
|
|
58
|
+
- Типизированные ошибки для различных сценариев доступа
|
|
59
|
+
|
|
60
|
+
### 🎨 UI конфигурация
|
|
61
|
+
- Автоматическая генерация UI конфигурации из EntityDefinition и Fields
|
|
62
|
+
- Поддержка кастомных UI настроек
|
|
63
|
+
- Генерация конфигурации колонок таблицы из полей
|
|
64
|
+
|
|
15
65
|
## 🚀 Быстрый старт
|
|
16
66
|
|
|
17
67
|
### 1. Server Component (SSR)
|
|
@@ -102,7 +152,7 @@ const { data, pagination } = await sdk.getInstances(entityDefinitionId, {
|
|
|
102
152
|
|
|
103
153
|
#### `getInstance(entityDefinitionId, id, params?)`
|
|
104
154
|
|
|
105
|
-
Получить один
|
|
155
|
+
Получить один экземпляр по ID.
|
|
106
156
|
|
|
107
157
|
```typescript
|
|
108
158
|
const instance = await sdk.getInstance(entityDefinitionId, id, {
|
|
@@ -110,17 +160,57 @@ const instance = await sdk.getInstance(entityDefinitionId, id, {
|
|
|
110
160
|
});
|
|
111
161
|
```
|
|
112
162
|
|
|
163
|
+
**Пример:**
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const instance = await sdk.getInstance("entity-def-id", "instance-id", {
|
|
167
|
+
relationsAsIds: true, // для редактирования - нужны только ID
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### `getInstanceBySlug(entityDefinitionId, slug, params?)`
|
|
172
|
+
|
|
173
|
+
Получить один экземпляр по slug.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const instance = await sdk.getInstanceBySlug(entityDefinitionId, slug, {
|
|
177
|
+
relationsAsIds?: boolean; // default: false
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Пример:**
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
const instance = await sdk.getInstanceBySlug("entity-def-id", "my-article-slug", {
|
|
185
|
+
relationsAsIds: false, // для отображения - нужны полные объекты
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Особенности:**
|
|
190
|
+
- Валидирует формат slug перед запросом (только строчные латинские буквы, цифры и дефисы)
|
|
191
|
+
- Работает аналогично `getInstance`, но ищет по slug вместо id
|
|
192
|
+
- Поддерживает те же параметры, что и `getInstance` (relationsAsIds)
|
|
193
|
+
- Параметр `relationsAsIds`:
|
|
194
|
+
- `false` (по умолчанию) - возвращает полные объекты связанных сущностей
|
|
195
|
+
- `true` - возвращает только массивы ID связанных сущностей
|
|
196
|
+
|
|
113
197
|
#### `createInstance(entityDefinitionId, data)`
|
|
114
198
|
|
|
115
|
-
Создать новый экземпляр.
|
|
199
|
+
Создать новый экземпляр. Автоматически генерирует уникальный `slug` из поля `name`.
|
|
116
200
|
|
|
117
201
|
```typescript
|
|
118
202
|
const instance = await sdk.createInstance(entityDefinitionId, {
|
|
119
|
-
data: Record<string, unknown>;
|
|
203
|
+
data: Record<string, unknown>; // Обязательно должно содержать поле 'name' (string)
|
|
120
204
|
relations?: Record<string, string[]>;
|
|
121
205
|
});
|
|
122
206
|
```
|
|
123
207
|
|
|
208
|
+
**Особенности:**
|
|
209
|
+
- Автоматически генерирует уникальный `slug` из поля `name`
|
|
210
|
+
- Если slug уже существует, добавляет случайный суффикс
|
|
211
|
+
- Автоматически устанавливает `created_by` из текущего пользователя
|
|
212
|
+
- Поддерживает создание relations в одном запросе
|
|
213
|
+
|
|
124
214
|
#### `updateInstance(entityDefinitionId, id, data)`
|
|
125
215
|
|
|
126
216
|
Обновить экземпляр.
|
|
@@ -159,6 +249,16 @@ const config = await sdk.getEntityDefinitionWithUIConfig(entityDefinitionId);
|
|
|
159
249
|
// config: { entityDefinition, fields, uiConfig }
|
|
160
250
|
```
|
|
161
251
|
|
|
252
|
+
#### `getAllEntityDefinitions()`
|
|
253
|
+
|
|
254
|
+
Получить все EntityDefinitions проекта с полями одним запросом (JOIN). Используется для загрузки всех сущностей в layout.
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
const entityDefinitions = await sdk.getAllEntityDefinitions();
|
|
258
|
+
// Возвращает массив EntityDefinitionConfig с полями
|
|
259
|
+
// Каждый элемент содержит: { id, name, slug, fields, ... }
|
|
260
|
+
```
|
|
261
|
+
|
|
162
262
|
## 🔧 Типы
|
|
163
263
|
|
|
164
264
|
```typescript
|
|
@@ -173,6 +273,30 @@ import type {
|
|
|
173
273
|
} from '@igorchugurov/public-api-sdk';
|
|
174
274
|
```
|
|
175
275
|
|
|
276
|
+
### Slug поддержка
|
|
277
|
+
|
|
278
|
+
Все экземпляры и определения сущностей теперь содержат поле `slug`:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
// EntityInstance содержит slug
|
|
282
|
+
const instance: EntityInstanceWithFields = {
|
|
283
|
+
id: string;
|
|
284
|
+
slug: string; // URL-friendly идентификатор
|
|
285
|
+
entityDefinitionId: string;
|
|
286
|
+
// ...
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// EntityDefinition также содержит slug
|
|
290
|
+
const entityDef: EntityDefinition = {
|
|
291
|
+
id: string;
|
|
292
|
+
name: string;
|
|
293
|
+
slug: string; // URL-friendly идентификатор
|
|
294
|
+
// ...
|
|
295
|
+
};
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Slug автоматически генерируется при создании экземпляра из поля `name` и гарантирует уникальность в рамках одной EntityDefinition.
|
|
299
|
+
|
|
176
300
|
## 🛠️ Обработка ошибок
|
|
177
301
|
|
|
178
302
|
SDK использует типизированные ошибки:
|
|
@@ -109,6 +109,7 @@ type PartialUIConfig = {
|
|
|
109
109
|
interface EntityDefinition {
|
|
110
110
|
id: string;
|
|
111
111
|
name: string;
|
|
112
|
+
slug: string;
|
|
112
113
|
description?: string | null;
|
|
113
114
|
tableName: string;
|
|
114
115
|
type: "primary" | "secondary" | "tertiary";
|
|
@@ -207,6 +208,7 @@ type DbTypeToTSType = {
|
|
|
207
208
|
type EntityData<T extends Record<string, FieldValue> = Record<string, FieldValue>> = T;
|
|
208
209
|
interface EntityInstance<TData extends Record<string, FieldValue> = Record<string, FieldValue>> {
|
|
209
210
|
id: string;
|
|
211
|
+
slug: string;
|
|
210
212
|
entityDefinitionId: string;
|
|
211
213
|
projectId: string;
|
|
212
214
|
data: EntityData<TData>;
|
|
@@ -230,6 +232,7 @@ interface EntityRelation {
|
|
|
230
232
|
*/
|
|
231
233
|
type EntityInstanceWithFields<TFields extends Record<string, FieldValue> = Record<string, FieldValue>> = {
|
|
232
234
|
id: string;
|
|
235
|
+
slug: string;
|
|
233
236
|
entityDefinitionId: string;
|
|
234
237
|
projectId: string;
|
|
235
238
|
createdAt: string;
|
|
@@ -318,6 +321,7 @@ interface ProjectConfig {
|
|
|
318
321
|
interface EntityDefinitionConfig {
|
|
319
322
|
id: string;
|
|
320
323
|
name: string;
|
|
324
|
+
slug: string;
|
|
321
325
|
description?: string | null;
|
|
322
326
|
tableName: string;
|
|
323
327
|
type: "primary" | "secondary" | "tertiary";
|
|
@@ -1067,6 +1071,13 @@ declare abstract class BasePublicAPIClient {
|
|
|
1067
1071
|
* @returns EntityDefinitionConfig с полями, отсортированными по display_index
|
|
1068
1072
|
*/
|
|
1069
1073
|
getEntityDefinitionConfig(entityDefinitionId: string): Promise<EntityDefinitionConfig>;
|
|
1074
|
+
/**
|
|
1075
|
+
* Получить все EntityDefinitions проекта с полями одним запросом (JOIN)
|
|
1076
|
+
* Используется для загрузки всех сущностей в layout
|
|
1077
|
+
*
|
|
1078
|
+
* @returns Массив EntityDefinitionConfig с полями
|
|
1079
|
+
*/
|
|
1080
|
+
getAllEntityDefinitions(): Promise<EntityDefinitionConfig[]>;
|
|
1070
1081
|
/**
|
|
1071
1082
|
* Преобразование EntityDefinitionConfig в EntityDefinition
|
|
1072
1083
|
*/
|
|
@@ -1098,6 +1109,9 @@ declare abstract class BasePublicAPIClient {
|
|
|
1098
1109
|
abstract getInstance(entityDefinitionId: string, id: string, params?: {
|
|
1099
1110
|
relationsAsIds?: boolean;
|
|
1100
1111
|
}): Promise<EntityInstanceWithFields>;
|
|
1112
|
+
abstract getInstanceBySlug(entityDefinitionId: string, slug: string, params?: {
|
|
1113
|
+
relationsAsIds?: boolean;
|
|
1114
|
+
}): Promise<EntityInstanceWithFields>;
|
|
1101
1115
|
abstract createInstance(entityDefinitionId: string, data: CreateInstanceData): Promise<EntityInstanceWithFields>;
|
|
1102
1116
|
abstract updateInstance(entityDefinitionId: string, id: string, data: UpdateInstanceData): Promise<EntityInstanceWithFields>;
|
|
1103
1117
|
abstract deleteInstance(entityDefinitionId: string, id: string): Promise<void>;
|
|
@@ -1141,6 +1155,12 @@ declare class PublicAPIClient extends BasePublicAPIClient {
|
|
|
1141
1155
|
getInstance(entityDefinitionId: string, id: string, params?: {
|
|
1142
1156
|
relationsAsIds?: boolean;
|
|
1143
1157
|
}): Promise<EntityInstanceWithFields>;
|
|
1158
|
+
/**
|
|
1159
|
+
* Получить один экземпляр по slug
|
|
1160
|
+
*/
|
|
1161
|
+
getInstanceBySlug(entityDefinitionId: string, slug: string, params?: {
|
|
1162
|
+
relationsAsIds?: boolean;
|
|
1163
|
+
}): Promise<EntityInstanceWithFields>;
|
|
1144
1164
|
/**
|
|
1145
1165
|
* Получить список экземпляров
|
|
1146
1166
|
* Поддерживает поиск, фильтры (JSONB и relation), пагинацию
|
|
@@ -109,6 +109,7 @@ type PartialUIConfig = {
|
|
|
109
109
|
interface EntityDefinition {
|
|
110
110
|
id: string;
|
|
111
111
|
name: string;
|
|
112
|
+
slug: string;
|
|
112
113
|
description?: string | null;
|
|
113
114
|
tableName: string;
|
|
114
115
|
type: "primary" | "secondary" | "tertiary";
|
|
@@ -207,6 +208,7 @@ type DbTypeToTSType = {
|
|
|
207
208
|
type EntityData<T extends Record<string, FieldValue> = Record<string, FieldValue>> = T;
|
|
208
209
|
interface EntityInstance<TData extends Record<string, FieldValue> = Record<string, FieldValue>> {
|
|
209
210
|
id: string;
|
|
211
|
+
slug: string;
|
|
210
212
|
entityDefinitionId: string;
|
|
211
213
|
projectId: string;
|
|
212
214
|
data: EntityData<TData>;
|
|
@@ -230,6 +232,7 @@ interface EntityRelation {
|
|
|
230
232
|
*/
|
|
231
233
|
type EntityInstanceWithFields<TFields extends Record<string, FieldValue> = Record<string, FieldValue>> = {
|
|
232
234
|
id: string;
|
|
235
|
+
slug: string;
|
|
233
236
|
entityDefinitionId: string;
|
|
234
237
|
projectId: string;
|
|
235
238
|
createdAt: string;
|
|
@@ -318,6 +321,7 @@ interface ProjectConfig {
|
|
|
318
321
|
interface EntityDefinitionConfig {
|
|
319
322
|
id: string;
|
|
320
323
|
name: string;
|
|
324
|
+
slug: string;
|
|
321
325
|
description?: string | null;
|
|
322
326
|
tableName: string;
|
|
323
327
|
type: "primary" | "secondary" | "tertiary";
|
|
@@ -1067,6 +1071,13 @@ declare abstract class BasePublicAPIClient {
|
|
|
1067
1071
|
* @returns EntityDefinitionConfig с полями, отсортированными по display_index
|
|
1068
1072
|
*/
|
|
1069
1073
|
getEntityDefinitionConfig(entityDefinitionId: string): Promise<EntityDefinitionConfig>;
|
|
1074
|
+
/**
|
|
1075
|
+
* Получить все EntityDefinitions проекта с полями одним запросом (JOIN)
|
|
1076
|
+
* Используется для загрузки всех сущностей в layout
|
|
1077
|
+
*
|
|
1078
|
+
* @returns Массив EntityDefinitionConfig с полями
|
|
1079
|
+
*/
|
|
1080
|
+
getAllEntityDefinitions(): Promise<EntityDefinitionConfig[]>;
|
|
1070
1081
|
/**
|
|
1071
1082
|
* Преобразование EntityDefinitionConfig в EntityDefinition
|
|
1072
1083
|
*/
|
|
@@ -1098,6 +1109,9 @@ declare abstract class BasePublicAPIClient {
|
|
|
1098
1109
|
abstract getInstance(entityDefinitionId: string, id: string, params?: {
|
|
1099
1110
|
relationsAsIds?: boolean;
|
|
1100
1111
|
}): Promise<EntityInstanceWithFields>;
|
|
1112
|
+
abstract getInstanceBySlug(entityDefinitionId: string, slug: string, params?: {
|
|
1113
|
+
relationsAsIds?: boolean;
|
|
1114
|
+
}): Promise<EntityInstanceWithFields>;
|
|
1101
1115
|
abstract createInstance(entityDefinitionId: string, data: CreateInstanceData): Promise<EntityInstanceWithFields>;
|
|
1102
1116
|
abstract updateInstance(entityDefinitionId: string, id: string, data: UpdateInstanceData): Promise<EntityInstanceWithFields>;
|
|
1103
1117
|
abstract deleteInstance(entityDefinitionId: string, id: string): Promise<void>;
|
|
@@ -1141,6 +1155,12 @@ declare class PublicAPIClient extends BasePublicAPIClient {
|
|
|
1141
1155
|
getInstance(entityDefinitionId: string, id: string, params?: {
|
|
1142
1156
|
relationsAsIds?: boolean;
|
|
1143
1157
|
}): Promise<EntityInstanceWithFields>;
|
|
1158
|
+
/**
|
|
1159
|
+
* Получить один экземпляр по slug
|
|
1160
|
+
*/
|
|
1161
|
+
getInstanceBySlug(entityDefinitionId: string, slug: string, params?: {
|
|
1162
|
+
relationsAsIds?: boolean;
|
|
1163
|
+
}): Promise<EntityInstanceWithFields>;
|
|
1144
1164
|
/**
|
|
1145
1165
|
* Получить список экземпляров
|
|
1146
1166
|
* Поддерживает поиск, фильтры (JSONB и relation), пагинацию
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FieldConfig, a as FieldValue, E as EntityDefinition, b as Field, c as EntityUIConfig, C as ColumnConfig, S as SDKOptions, P as PublicAPIClient } from './client-
|
|
2
|
-
export { y as ActionConfig, A as AuthResult, g as CreateInstanceData, D as DbType, l as DbTypeToTSType, m as EntityData, e as EntityDefinitionConfig, x as EntityFile, n as EntityInstance, q as EntityInstanceWithFields, p as EntityRelation, k as FieldOption, j as FieldType, r as FilterValue, z as FormPageConfig, G as GetInstancesOptions, I as InstanceData, L as ListPageConfig, M as MessagesConfig, h as PaginationResult, w as PartialInstanceData, B as PartialUIConfig, d as ProjectConfig, Q as QueryParams, R as RelationFilterInfo, f as RelationFilterMode, o as RelationType, s as RelationsData, i as SignUpData, U as UpdateInstanceData, v as getFieldValue, u as isEntityData, t as isFieldValue } from './client-
|
|
1
|
+
import { F as FieldConfig, a as FieldValue, E as EntityDefinition, b as Field, c as EntityUIConfig, C as ColumnConfig, S as SDKOptions, P as PublicAPIClient } from './client-DS5xnLAo.mjs';
|
|
2
|
+
export { y as ActionConfig, A as AuthResult, g as CreateInstanceData, D as DbType, l as DbTypeToTSType, m as EntityData, e as EntityDefinitionConfig, x as EntityFile, n as EntityInstance, q as EntityInstanceWithFields, p as EntityRelation, k as FieldOption, j as FieldType, r as FilterValue, z as FormPageConfig, G as GetInstancesOptions, I as InstanceData, L as ListPageConfig, M as MessagesConfig, h as PaginationResult, w as PartialInstanceData, B as PartialUIConfig, d as ProjectConfig, Q as QueryParams, R as RelationFilterInfo, f as RelationFilterMode, o as RelationType, s as RelationsData, i as SignUpData, U as UpdateInstanceData, v as getFieldValue, u as isEntityData, t as isFieldValue } from './client-DS5xnLAo.mjs';
|
|
3
3
|
import '@supabase/supabase-js';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FieldConfig, a as FieldValue, E as EntityDefinition, b as Field, c as EntityUIConfig, C as ColumnConfig, S as SDKOptions, P as PublicAPIClient } from './client-
|
|
2
|
-
export { y as ActionConfig, A as AuthResult, g as CreateInstanceData, D as DbType, l as DbTypeToTSType, m as EntityData, e as EntityDefinitionConfig, x as EntityFile, n as EntityInstance, q as EntityInstanceWithFields, p as EntityRelation, k as FieldOption, j as FieldType, r as FilterValue, z as FormPageConfig, G as GetInstancesOptions, I as InstanceData, L as ListPageConfig, M as MessagesConfig, h as PaginationResult, w as PartialInstanceData, B as PartialUIConfig, d as ProjectConfig, Q as QueryParams, R as RelationFilterInfo, f as RelationFilterMode, o as RelationType, s as RelationsData, i as SignUpData, U as UpdateInstanceData, v as getFieldValue, u as isEntityData, t as isFieldValue } from './client-
|
|
1
|
+
import { F as FieldConfig, a as FieldValue, E as EntityDefinition, b as Field, c as EntityUIConfig, C as ColumnConfig, S as SDKOptions, P as PublicAPIClient } from './client-DS5xnLAo.js';
|
|
2
|
+
export { y as ActionConfig, A as AuthResult, g as CreateInstanceData, D as DbType, l as DbTypeToTSType, m as EntityData, e as EntityDefinitionConfig, x as EntityFile, n as EntityInstance, q as EntityInstanceWithFields, p as EntityRelation, k as FieldOption, j as FieldType, r as FilterValue, z as FormPageConfig, G as GetInstancesOptions, I as InstanceData, L as ListPageConfig, M as MessagesConfig, h as PaginationResult, w as PartialInstanceData, B as PartialUIConfig, d as ProjectConfig, Q as QueryParams, R as RelationFilterInfo, f as RelationFilterMode, o as RelationType, s as RelationsData, i as SignUpData, U as UpdateInstanceData, v as getFieldValue, u as isEntityData, t as isFieldValue } from './client-DS5xnLAo.js';
|
|
3
3
|
import '@supabase/supabase-js';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var ssr = require('@supabase/ssr');
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __defProps = Object.defineProperties;
|
|
7
7
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
8
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
9
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
9
10
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
11
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
@@ -21,6 +22,61 @@ var __spreadValues = (a, b) => {
|
|
|
21
22
|
return a;
|
|
22
23
|
};
|
|
23
24
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
25
|
+
var __esm = (fn, res) => function __init() {
|
|
26
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
27
|
+
};
|
|
28
|
+
var __export = (target, all) => {
|
|
29
|
+
for (var name in all)
|
|
30
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/utils/slug.ts
|
|
34
|
+
var slug_exports = {};
|
|
35
|
+
__export(slug_exports, {
|
|
36
|
+
generateSlug: () => generateSlug,
|
|
37
|
+
generateUniqueSlugForInstance: () => generateUniqueSlugForInstance,
|
|
38
|
+
validateSlug: () => validateSlug
|
|
39
|
+
});
|
|
40
|
+
function generateSlug(name) {
|
|
41
|
+
if (!name || typeof name !== "string") {
|
|
42
|
+
throw new Error("Name must be a non-empty string");
|
|
43
|
+
}
|
|
44
|
+
return name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").substring(0, 100);
|
|
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
|
+
}
|
|
56
|
+
function generateRandomSuffix() {
|
|
57
|
+
return Math.random().toString(36).substring(2, 6);
|
|
58
|
+
}
|
|
59
|
+
async function generateUniqueSlugForInstance(name, entityDefinitionId, checkExists, excludeId) {
|
|
60
|
+
const baseSlug = generateSlug(name);
|
|
61
|
+
let slug = baseSlug;
|
|
62
|
+
let attempts = 0;
|
|
63
|
+
const maxAttempts = 100;
|
|
64
|
+
while (attempts < maxAttempts) {
|
|
65
|
+
const exists = await checkExists(slug, entityDefinitionId, excludeId);
|
|
66
|
+
if (!exists) {
|
|
67
|
+
return slug;
|
|
68
|
+
}
|
|
69
|
+
const randomSuffix = generateRandomSuffix();
|
|
70
|
+
slug = `${baseSlug}-${randomSuffix}`;
|
|
71
|
+
attempts++;
|
|
72
|
+
}
|
|
73
|
+
const timestamp = Date.now().toString(36);
|
|
74
|
+
return `${baseSlug}-${timestamp}`;
|
|
75
|
+
}
|
|
76
|
+
var init_slug = __esm({
|
|
77
|
+
"src/utils/slug.ts"() {
|
|
78
|
+
}
|
|
79
|
+
});
|
|
24
80
|
|
|
25
81
|
// src/types/entity-types.ts
|
|
26
82
|
function isFieldValue(value) {
|
|
@@ -397,6 +453,7 @@ var BasePublicAPIClient = class {
|
|
|
397
453
|
return {
|
|
398
454
|
id: row.id,
|
|
399
455
|
name: row.name,
|
|
456
|
+
slug: row.slug,
|
|
400
457
|
description: row.description,
|
|
401
458
|
tableName: row.table_name,
|
|
402
459
|
type: row.type,
|
|
@@ -519,6 +576,38 @@ var BasePublicAPIClient = class {
|
|
|
519
576
|
}
|
|
520
577
|
return config;
|
|
521
578
|
}
|
|
579
|
+
/**
|
|
580
|
+
* Получить все EntityDefinitions проекта с полями одним запросом (JOIN)
|
|
581
|
+
* Используется для загрузки всех сущностей в layout
|
|
582
|
+
*
|
|
583
|
+
* @returns Массив EntityDefinitionConfig с полями
|
|
584
|
+
*/
|
|
585
|
+
async getAllEntityDefinitions() {
|
|
586
|
+
const { data, error } = await this.supabase.from("entity_definition").select(
|
|
587
|
+
`
|
|
588
|
+
*,
|
|
589
|
+
field!field_entity_definition_id_fkey (*)
|
|
590
|
+
`
|
|
591
|
+
).eq("project_id", this.projectId).order("name");
|
|
592
|
+
if (error) {
|
|
593
|
+
throw new Error(`Failed to load entity definitions: ${error.message}`);
|
|
594
|
+
}
|
|
595
|
+
if (!data || data.length === 0) {
|
|
596
|
+
return [];
|
|
597
|
+
}
|
|
598
|
+
return data.map((row) => {
|
|
599
|
+
const entityDefinition = this.transformEntityDefinitionFromDB(row);
|
|
600
|
+
const fields = (row.field || []).map(
|
|
601
|
+
(fieldRow) => this.transformFieldFromDB(fieldRow)
|
|
602
|
+
);
|
|
603
|
+
fields.sort(
|
|
604
|
+
(a, b) => a.displayIndex - b.displayIndex
|
|
605
|
+
);
|
|
606
|
+
return __spreadProps(__spreadValues({}, entityDefinition), {
|
|
607
|
+
fields
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
}
|
|
522
611
|
/**
|
|
523
612
|
* Преобразование EntityDefinitionConfig в EntityDefinition
|
|
524
613
|
*/
|
|
@@ -526,6 +615,7 @@ var BasePublicAPIClient = class {
|
|
|
526
615
|
return {
|
|
527
616
|
id: config.id,
|
|
528
617
|
name: config.name,
|
|
618
|
+
slug: config.slug,
|
|
529
619
|
description: config.description,
|
|
530
620
|
tableName: config.tableName,
|
|
531
621
|
type: config.type,
|
|
@@ -637,6 +727,7 @@ var BasePublicAPIClient = class {
|
|
|
637
727
|
function transformEntityInstance(row) {
|
|
638
728
|
return {
|
|
639
729
|
id: row.id,
|
|
730
|
+
slug: row.slug,
|
|
640
731
|
entityDefinitionId: row.entity_definition_id,
|
|
641
732
|
projectId: row.project_id,
|
|
642
733
|
data: row.data || {},
|
|
@@ -647,6 +738,7 @@ function transformEntityInstance(row) {
|
|
|
647
738
|
function flattenInstance(instance, fields, relationsAsIds = false) {
|
|
648
739
|
const result = {
|
|
649
740
|
id: instance.id,
|
|
741
|
+
slug: instance.slug,
|
|
650
742
|
entityDefinitionId: instance.entityDefinitionId,
|
|
651
743
|
projectId: instance.projectId,
|
|
652
744
|
createdAt: instance.createdAt,
|
|
@@ -675,6 +767,7 @@ function flattenInstance(instance, fields, relationsAsIds = false) {
|
|
|
675
767
|
}
|
|
676
768
|
|
|
677
769
|
// src/client.ts
|
|
770
|
+
init_slug();
|
|
678
771
|
var _PublicAPIClient = class _PublicAPIClient extends BasePublicAPIClient {
|
|
679
772
|
constructor(supabase, projectId, mode, options = {}) {
|
|
680
773
|
super(supabase, projectId, options);
|
|
@@ -826,6 +919,134 @@ var _PublicAPIClient = class _PublicAPIClient extends BasePublicAPIClient {
|
|
|
826
919
|
handleSupabaseError(error);
|
|
827
920
|
}
|
|
828
921
|
}
|
|
922
|
+
/**
|
|
923
|
+
* Получить один экземпляр по slug
|
|
924
|
+
*/
|
|
925
|
+
async getInstanceBySlug(entityDefinitionId, slug, params) {
|
|
926
|
+
var _a;
|
|
927
|
+
try {
|
|
928
|
+
if (!validateSlug(slug)) {
|
|
929
|
+
throw new ValidationError(
|
|
930
|
+
"slug",
|
|
931
|
+
"Slug must contain only lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen"
|
|
932
|
+
);
|
|
933
|
+
}
|
|
934
|
+
const fields = await this.getFields(entityDefinitionId);
|
|
935
|
+
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();
|
|
936
|
+
if (instanceError || !instance) {
|
|
937
|
+
handleInstanceError(
|
|
938
|
+
instanceError || new Error("Instance not found"),
|
|
939
|
+
slug
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
const transformedInstance = transformEntityInstance(instance);
|
|
943
|
+
if (transformedInstance.entityDefinitionId !== entityDefinitionId) {
|
|
944
|
+
throw new NotFoundError("Entity instance", slug);
|
|
945
|
+
}
|
|
946
|
+
const relationFields = fields.filter(
|
|
947
|
+
(f) => f.dbType === "manyToMany" || f.dbType === "manyToOne" || f.dbType === "oneToMany" || f.dbType === "oneToOne"
|
|
948
|
+
);
|
|
949
|
+
const relations = {};
|
|
950
|
+
if (relationFields.length > 0) {
|
|
951
|
+
const relationFieldIds = relationFields.map((f) => f.id).filter((id) => Boolean(id));
|
|
952
|
+
if (relationFieldIds.length > 0) {
|
|
953
|
+
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);
|
|
954
|
+
if (relationsError) {
|
|
955
|
+
throw new SDKError(
|
|
956
|
+
"RELATIONS_LOAD_ERROR",
|
|
957
|
+
`Failed to load relations for instance with slug ${slug}: ${relationsError.message}`,
|
|
958
|
+
500,
|
|
959
|
+
relationsError
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
if (allRelations && allRelations.length > 0) {
|
|
963
|
+
const targetInstanceIds = [
|
|
964
|
+
...new Set(allRelations.map((r) => r.target_instance_id))
|
|
965
|
+
];
|
|
966
|
+
const { data: relatedInstances, error: instancesError } = await this.supabase.from("entity_instance").select("*").in("id", targetInstanceIds);
|
|
967
|
+
if (instancesError) {
|
|
968
|
+
throw new SDKError(
|
|
969
|
+
"RELATED_INSTANCES_LOAD_ERROR",
|
|
970
|
+
`Failed to load related instances for instance with slug ${slug}: ${instancesError.message}`,
|
|
971
|
+
500,
|
|
972
|
+
instancesError
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
if (relatedInstances) {
|
|
976
|
+
const relatedInstancesMap = new Map(
|
|
977
|
+
relatedInstances.map((inst) => [
|
|
978
|
+
inst.id,
|
|
979
|
+
transformEntityInstance(inst)
|
|
980
|
+
])
|
|
981
|
+
);
|
|
982
|
+
for (const relation of allRelations) {
|
|
983
|
+
const relationField = relationFields.find(
|
|
984
|
+
(f) => f.id === relation.relation_field_id
|
|
985
|
+
);
|
|
986
|
+
if (relationField) {
|
|
987
|
+
const relatedInstance = relatedInstancesMap.get(
|
|
988
|
+
relation.target_instance_id
|
|
989
|
+
);
|
|
990
|
+
if (relatedInstance) {
|
|
991
|
+
if (!relations[relationField.name]) {
|
|
992
|
+
relations[relationField.name] = [];
|
|
993
|
+
}
|
|
994
|
+
relations[relationField.name].push(
|
|
995
|
+
relatedInstance
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
const fileFields = fields.filter(
|
|
1005
|
+
(f) => f.type === "files" || f.type === "images"
|
|
1006
|
+
);
|
|
1007
|
+
if (fileFields.length > 0) {
|
|
1008
|
+
const { data: allFiles, error: filesError } = await this.supabase.from("entity_file").select("id, field_id").eq("entity_instance_id", transformedInstance.id);
|
|
1009
|
+
if (filesError) {
|
|
1010
|
+
throw new SDKError(
|
|
1011
|
+
"FILES_LOAD_ERROR",
|
|
1012
|
+
`Failed to load files for instance with slug ${slug}: ${filesError.message}`,
|
|
1013
|
+
500,
|
|
1014
|
+
filesError
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
if (allFiles) {
|
|
1018
|
+
const filesByFieldId = /* @__PURE__ */ new Map();
|
|
1019
|
+
allFiles.forEach((file) => {
|
|
1020
|
+
if (file.field_id) {
|
|
1021
|
+
if (!filesByFieldId.has(file.field_id)) {
|
|
1022
|
+
filesByFieldId.set(file.field_id, []);
|
|
1023
|
+
}
|
|
1024
|
+
filesByFieldId.get(file.field_id).push(file.id);
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
fileFields.forEach((field) => {
|
|
1028
|
+
const fileIds = filesByFieldId.get(field.id) || [];
|
|
1029
|
+
if (fileIds.length > 0 || !transformedInstance.data[field.name]) {
|
|
1030
|
+
transformedInstance.data[field.name] = fileIds;
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
const instanceWithRelations = __spreadProps(__spreadValues({}, transformedInstance), {
|
|
1036
|
+
relations: Object.keys(relations).length > 0 ? relations : void 0
|
|
1037
|
+
});
|
|
1038
|
+
return flattenInstance(
|
|
1039
|
+
instanceWithRelations,
|
|
1040
|
+
fields.map((f) => ({ name: f.name, dbType: f.dbType })),
|
|
1041
|
+
(_a = params == null ? void 0 : params.relationsAsIds) != null ? _a : false
|
|
1042
|
+
);
|
|
1043
|
+
} catch (error) {
|
|
1044
|
+
if (error instanceof NotFoundError || error instanceof PermissionDeniedError || error instanceof ValidationError || error instanceof SDKError) {
|
|
1045
|
+
throw error;
|
|
1046
|
+
}
|
|
1047
|
+
handleSupabaseError(error);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
829
1050
|
/**
|
|
830
1051
|
* Получить список экземпляров
|
|
831
1052
|
* Поддерживает поиск, фильтры (JSONB и relation), пагинацию
|
|
@@ -1214,9 +1435,27 @@ var _PublicAPIClient = class _PublicAPIClient extends BasePublicAPIClient {
|
|
|
1214
1435
|
);
|
|
1215
1436
|
}
|
|
1216
1437
|
const { data: instanceData, relations } = data;
|
|
1438
|
+
const name = instanceData.name;
|
|
1439
|
+
if (!name || typeof name !== "string") {
|
|
1440
|
+
throw new Error(
|
|
1441
|
+
"Field 'name' is required and must be a string for slug generation"
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
const {
|
|
1445
|
+
generateUniqueSlugForInstance: generateUniqueSlugForInstance2
|
|
1446
|
+
} = await Promise.resolve().then(() => (init_slug(), slug_exports));
|
|
1447
|
+
const slug = await generateUniqueSlugForInstance2(
|
|
1448
|
+
name,
|
|
1449
|
+
entityDefinitionId,
|
|
1450
|
+
async (slugToCheck, entityDefIdToCheck) => {
|
|
1451
|
+
const { data: existing } = await this.supabase.from("entity_instance").select("id").eq("entity_definition_id", entityDefIdToCheck).eq("slug", slugToCheck).single();
|
|
1452
|
+
return !!existing;
|
|
1453
|
+
}
|
|
1454
|
+
);
|
|
1217
1455
|
const { data: instance, error: instanceError } = await this.supabase.from("entity_instance").insert({
|
|
1218
1456
|
entity_definition_id: entityDefinitionId,
|
|
1219
1457
|
project_id: this.projectId,
|
|
1458
|
+
slug,
|
|
1220
1459
|
data: instanceData,
|
|
1221
1460
|
created_by: (user == null ? void 0 : user.id) || null
|
|
1222
1461
|
}).select().single();
|