@promakeai/orm 1.0.6 → 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/README.md +155 -185
- package/dist/adapters/RestAdapter.d.ts +94 -0
- package/dist/index.d.ts +12 -13
- package/dist/index.js +374 -192
- package/dist/schema/index.d.ts +1 -3
- package/dist/schema/schemaHelpers.d.ts +5 -1
- package/dist/schema/validator.d.ts +2 -0
- package/dist/types.d.ts +16 -15
- package/package.json +1 -1
- package/src/adapters/RestAdapter.ts +483 -0
- package/src/index.ts +23 -26
- package/src/schema/index.ts +1 -4
- package/src/schema/schemaHelpers.ts +8 -1
- package/src/schema/validator.ts +33 -1
- package/src/types.ts +21 -17
- package/src/utils/jsonConverter.ts +124 -117
- package/src/utils/translationQuery.ts +62 -62
- package/src/schema/defineSchema.ts +0 -164
- package/src/schema/fieldBuilder.ts +0 -293
package/src/schema/validator.ts
CHANGED
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
* Validates schema definitions for correctness.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { SchemaDefinition, TableDefinition } from "../types";
|
|
7
|
+
import type { SchemaDefinition, TableDefinition, PermissionRole, PermissionAction } from "../types";
|
|
8
8
|
import { getRefTarget } from "./schemaHelpers";
|
|
9
9
|
|
|
10
|
+
const VALID_ROLES: PermissionRole[] = ["anon", "user", "admin"];
|
|
11
|
+
const VALID_ACTIONS: PermissionAction[] = ["create", "read", "update", "delete"];
|
|
12
|
+
|
|
10
13
|
/**
|
|
11
14
|
* Validation error with context
|
|
12
15
|
*/
|
|
@@ -28,6 +31,8 @@ export const ValidationErrorCode = {
|
|
|
28
31
|
DUPLICATE_TABLE: "DUPLICATE_TABLE",
|
|
29
32
|
RESERVED_FIELD_NAME: "RESERVED_FIELD_NAME",
|
|
30
33
|
SELF_REFERENCE_ON_REQUIRED: "SELF_REFERENCE_ON_REQUIRED",
|
|
34
|
+
INVALID_PERMISSION_ROLE: "INVALID_PERMISSION_ROLE",
|
|
35
|
+
INVALID_PERMISSION_ACTION: "INVALID_PERMISSION_ACTION",
|
|
31
36
|
} as const;
|
|
32
37
|
|
|
33
38
|
/**
|
|
@@ -113,6 +118,33 @@ export function validateSchema(schema: SchemaDefinition): ValidationError[] {
|
|
|
113
118
|
});
|
|
114
119
|
}
|
|
115
120
|
}
|
|
121
|
+
|
|
122
|
+
// Validate $permissions if present
|
|
123
|
+
if (table.permissions) {
|
|
124
|
+
for (const [role, actions] of Object.entries(table.permissions)) {
|
|
125
|
+
if (!VALID_ROLES.includes(role as PermissionRole)) {
|
|
126
|
+
errors.push({
|
|
127
|
+
table: tableName,
|
|
128
|
+
field: "$permissions",
|
|
129
|
+
message: `Invalid permission role: "${role}". Valid roles: ${VALID_ROLES.join(", ")}`,
|
|
130
|
+
code: ValidationErrorCode.INVALID_PERMISSION_ROLE,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (Array.isArray(actions)) {
|
|
135
|
+
for (const action of actions) {
|
|
136
|
+
if (!VALID_ACTIONS.includes(action as PermissionAction)) {
|
|
137
|
+
errors.push({
|
|
138
|
+
table: tableName,
|
|
139
|
+
field: "$permissions",
|
|
140
|
+
message: `Invalid permission action: "${action}". Valid actions: ${VALID_ACTIONS.join(", ")}`,
|
|
141
|
+
code: ValidationErrorCode.INVALID_PERMISSION_ACTION,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
116
148
|
}
|
|
117
149
|
|
|
118
150
|
return errors;
|
package/src/types.ts
CHANGED
|
@@ -46,7 +46,7 @@ export interface FieldReference {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* Complete field definition
|
|
49
|
+
* Complete field definition (normalized from JSON schema)
|
|
50
50
|
*/
|
|
51
51
|
export interface FieldDefinition {
|
|
52
52
|
type: FieldType;
|
|
@@ -76,6 +76,25 @@ export interface FieldDefinition {
|
|
|
76
76
|
match?: string; // RegExp pattern as string
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
// ==================== Permission Types ====================
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Permission roles for table access control
|
|
83
|
+
*/
|
|
84
|
+
export type PermissionRole = "anon" | "user" | "admin";
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Permission actions
|
|
88
|
+
*/
|
|
89
|
+
export type PermissionAction = "create" | "read" | "update" | "delete";
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Table-level permissions mapping roles to allowed actions.
|
|
93
|
+
* If not defined on a table, no restrictions apply (backward compatible).
|
|
94
|
+
* Enforced by the backend, not by the ORM.
|
|
95
|
+
*/
|
|
96
|
+
export type TablePermissions = Partial<Record<PermissionRole, PermissionAction[]>>;
|
|
97
|
+
|
|
79
98
|
// ==================== Table & Schema Types ====================
|
|
80
99
|
|
|
81
100
|
/**
|
|
@@ -84,6 +103,7 @@ export interface FieldDefinition {
|
|
|
84
103
|
export interface TableDefinition {
|
|
85
104
|
name: string;
|
|
86
105
|
fields: Record<string, FieldDefinition>;
|
|
106
|
+
permissions?: TablePermissions;
|
|
87
107
|
}
|
|
88
108
|
|
|
89
109
|
/**
|
|
@@ -103,22 +123,6 @@ export interface SchemaDefinition {
|
|
|
103
123
|
tables: Record<string, TableDefinition>;
|
|
104
124
|
}
|
|
105
125
|
|
|
106
|
-
/**
|
|
107
|
-
* Interface for FieldBuilder-like objects (for type checking)
|
|
108
|
-
*/
|
|
109
|
-
export interface FieldBuilderLike {
|
|
110
|
-
build(): FieldDefinition;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Raw schema input before processing (from user DSL)
|
|
115
|
-
*/
|
|
116
|
-
export interface SchemaInput {
|
|
117
|
-
name?: string;
|
|
118
|
-
languages: string[] | LanguageConfig;
|
|
119
|
-
tables: Record<string, Record<string, FieldBuilderLike>>;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
126
|
// ==================== JSON Schema Types (AI Agent Friendly) ====================
|
|
123
127
|
|
|
124
128
|
/**
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Enables AI agents to work with JSON while maintaining type-safe internal representation.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {
|
|
8
|
+
import type {
|
|
9
9
|
JSONSchemaDefinition,
|
|
10
10
|
JSONFieldDefinition,
|
|
11
11
|
JSONFieldType,
|
|
@@ -13,23 +13,24 @@ import type {
|
|
|
13
13
|
FieldDefinition,
|
|
14
14
|
FieldType,
|
|
15
15
|
TableDefinition,
|
|
16
|
-
LanguageConfig,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
16
|
+
LanguageConfig,
|
|
17
|
+
TablePermissions,
|
|
18
|
+
} from "../types";
|
|
19
|
+
|
|
20
|
+
const LANGUAGE_NAME_TO_CODE: Record<string, string> = {
|
|
21
|
+
english: "en",
|
|
22
|
+
turkish: "tr",
|
|
23
|
+
german: "de",
|
|
24
|
+
french: "fr",
|
|
25
|
+
spanish: "es",
|
|
26
|
+
russian: "ru",
|
|
27
|
+
arabic: "ar",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export interface ParseJSONSchemaResult {
|
|
31
|
+
schema: SchemaDefinition;
|
|
32
|
+
warnings: string[];
|
|
33
|
+
}
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* Convert JSON schema to internal SchemaDefinition
|
|
@@ -52,119 +53,125 @@ export interface ParseJSONSchemaResult {
|
|
|
52
53
|
* const schema = parseJSONSchema(jsonSchema);
|
|
53
54
|
* ```
|
|
54
55
|
*/
|
|
55
|
-
export function parseJSONSchema(json: JSONSchemaDefinition): SchemaDefinition {
|
|
56
|
-
return parseJSONSchemaWithWarnings(json).schema;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function parseJSONSchemaWithWarnings(
|
|
60
|
-
json: JSONSchemaDefinition
|
|
61
|
-
): ParseJSONSchemaResult {
|
|
62
|
-
const warnings: string[] = [];
|
|
63
|
-
const supported = (json.languages || ["en"]).map((lang) => normalizeLangToken(lang));
|
|
64
|
-
const defaultLanguage = resolveDefaultLanguage(
|
|
65
|
-
json.defaultLanguage,
|
|
66
|
-
supported,
|
|
67
|
-
warnings
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
const languages: LanguageConfig = {
|
|
71
|
-
default: defaultLanguage,
|
|
72
|
-
supported,
|
|
73
|
-
};
|
|
56
|
+
export function parseJSONSchema(json: JSONSchemaDefinition): SchemaDefinition {
|
|
57
|
+
return parseJSONSchemaWithWarnings(json).schema;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function parseJSONSchemaWithWarnings(
|
|
61
|
+
json: JSONSchemaDefinition
|
|
62
|
+
): ParseJSONSchemaResult {
|
|
63
|
+
const warnings: string[] = [];
|
|
64
|
+
const supported = (json.languages || ["en"]).map((lang) => normalizeLangToken(lang));
|
|
65
|
+
const defaultLanguage = resolveDefaultLanguage(
|
|
66
|
+
json.defaultLanguage,
|
|
67
|
+
supported,
|
|
68
|
+
warnings
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const languages: LanguageConfig = {
|
|
72
|
+
default: defaultLanguage,
|
|
73
|
+
supported,
|
|
74
|
+
};
|
|
74
75
|
|
|
75
76
|
const tables: Record<string, TableDefinition> = {};
|
|
76
77
|
|
|
77
78
|
for (const [tableName, jsonTable] of Object.entries(json.tables)) {
|
|
78
79
|
const fields: Record<string, FieldDefinition> = {};
|
|
80
|
+
let permissions: TablePermissions | undefined;
|
|
79
81
|
|
|
80
82
|
for (const [fieldName, jsonField] of Object.entries(jsonTable)) {
|
|
81
|
-
|
|
83
|
+
if (fieldName === "$permissions") {
|
|
84
|
+
permissions = jsonField as unknown as TablePermissions;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
fields[fieldName] = convertJSONField(jsonField as JSONFieldDefinition);
|
|
82
88
|
}
|
|
83
89
|
|
|
84
90
|
tables[tableName] = {
|
|
85
91
|
name: tableName,
|
|
86
92
|
fields,
|
|
93
|
+
...(permissions ? { permissions } : {}),
|
|
87
94
|
};
|
|
88
95
|
}
|
|
89
96
|
|
|
90
|
-
return {
|
|
91
|
-
schema: {
|
|
92
|
-
name: json.name,
|
|
93
|
-
languages,
|
|
94
|
-
tables,
|
|
95
|
-
},
|
|
96
|
-
warnings,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function normalizeLangToken(lang: string): string {
|
|
101
|
-
return String(lang).trim().toLowerCase();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function resolveDefaultLanguage(
|
|
105
|
-
input: string | undefined,
|
|
106
|
-
supported: string[],
|
|
107
|
-
warnings: string[]
|
|
108
|
-
): string {
|
|
109
|
-
const fallback = supported[0] || "en";
|
|
110
|
-
if (!input) return fallback;
|
|
111
|
-
|
|
112
|
-
const normalizedInput = normalizeLangToken(input);
|
|
113
|
-
const directMatch = supported.find(
|
|
114
|
-
(lang) => normalizeLangToken(lang) === normalizedInput
|
|
115
|
-
);
|
|
116
|
-
if (directMatch) return directMatch;
|
|
117
|
-
|
|
118
|
-
const languageNameMatch = resolveLanguageNameToSupportedCode(
|
|
119
|
-
normalizedInput,
|
|
120
|
-
supported
|
|
121
|
-
);
|
|
122
|
-
if (languageNameMatch) {
|
|
123
|
-
warnings.push(
|
|
124
|
-
`Normalized defaultLanguage "${input}" to "${languageNameMatch}".`
|
|
125
|
-
);
|
|
126
|
-
return languageNameMatch;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
throw new Error(
|
|
130
|
-
`Invalid defaultLanguage "${input}". Expected one of: ${supported.join(", ")}`
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function resolveLanguageNameToSupportedCode(
|
|
135
|
-
normalizedInput: string,
|
|
136
|
-
supported: string[]
|
|
137
|
-
): string | null {
|
|
138
|
-
const mapped = LANGUAGE_NAME_TO_CODE[normalizedInput];
|
|
139
|
-
if (mapped) {
|
|
140
|
-
const supportedMatch = supported.find(
|
|
141
|
-
(lang) => normalizeLangToken(lang) === mapped
|
|
142
|
-
);
|
|
143
|
-
if (supportedMatch) return supportedMatch;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
for (const code of supported) {
|
|
147
|
-
const languageName = getLanguageDisplayName(code);
|
|
148
|
-
if (languageName && normalizeLangToken(languageName) === normalizedInput) {
|
|
149
|
-
return code;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function getLanguageDisplayName(code: string): string | null {
|
|
157
|
-
try {
|
|
158
|
-
if (typeof Intl === "undefined" || !("DisplayNames" in Intl)) {
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
const displayNames = new Intl.DisplayNames(["en"], { type: "language" });
|
|
162
|
-
const label = displayNames.of(code);
|
|
163
|
-
return label || null;
|
|
164
|
-
} catch {
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
97
|
+
return {
|
|
98
|
+
schema: {
|
|
99
|
+
name: json.name,
|
|
100
|
+
languages,
|
|
101
|
+
tables,
|
|
102
|
+
},
|
|
103
|
+
warnings,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function normalizeLangToken(lang: string): string {
|
|
108
|
+
return String(lang).trim().toLowerCase();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function resolveDefaultLanguage(
|
|
112
|
+
input: string | undefined,
|
|
113
|
+
supported: string[],
|
|
114
|
+
warnings: string[]
|
|
115
|
+
): string {
|
|
116
|
+
const fallback = supported[0] || "en";
|
|
117
|
+
if (!input) return fallback;
|
|
118
|
+
|
|
119
|
+
const normalizedInput = normalizeLangToken(input);
|
|
120
|
+
const directMatch = supported.find(
|
|
121
|
+
(lang) => normalizeLangToken(lang) === normalizedInput
|
|
122
|
+
);
|
|
123
|
+
if (directMatch) return directMatch;
|
|
124
|
+
|
|
125
|
+
const languageNameMatch = resolveLanguageNameToSupportedCode(
|
|
126
|
+
normalizedInput,
|
|
127
|
+
supported
|
|
128
|
+
);
|
|
129
|
+
if (languageNameMatch) {
|
|
130
|
+
warnings.push(
|
|
131
|
+
`Normalized defaultLanguage "${input}" to "${languageNameMatch}".`
|
|
132
|
+
);
|
|
133
|
+
return languageNameMatch;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
throw new Error(
|
|
137
|
+
`Invalid defaultLanguage "${input}". Expected one of: ${supported.join(", ")}`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function resolveLanguageNameToSupportedCode(
|
|
142
|
+
normalizedInput: string,
|
|
143
|
+
supported: string[]
|
|
144
|
+
): string | null {
|
|
145
|
+
const mapped = LANGUAGE_NAME_TO_CODE[normalizedInput];
|
|
146
|
+
if (mapped) {
|
|
147
|
+
const supportedMatch = supported.find(
|
|
148
|
+
(lang) => normalizeLangToken(lang) === mapped
|
|
149
|
+
);
|
|
150
|
+
if (supportedMatch) return supportedMatch;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
for (const code of supported) {
|
|
154
|
+
const languageName = getLanguageDisplayName(code);
|
|
155
|
+
if (languageName && normalizeLangToken(languageName) === normalizedInput) {
|
|
156
|
+
return code;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function getLanguageDisplayName(code: string): string | null {
|
|
164
|
+
try {
|
|
165
|
+
if (typeof Intl === "undefined" || !("DisplayNames" in Intl)) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
const displayNames = new Intl.DisplayNames(["en"], { type: "language" });
|
|
169
|
+
const label = displayNames.of(code);
|
|
170
|
+
return label || null;
|
|
171
|
+
} catch {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
168
175
|
|
|
169
176
|
/**
|
|
170
177
|
* Normalize JSON-native type syntax to internal FieldType + properties
|
|
@@ -225,7 +232,7 @@ function convertJSONField(jsonField: JSONFieldDefinition): FieldDefinition {
|
|
|
225
232
|
type,
|
|
226
233
|
nullable: jsonField.nullable ?? !jsonField.required,
|
|
227
234
|
unique: jsonField.unique ?? false,
|
|
228
|
-
primary: jsonField.primary ??
|
|
235
|
+
primary: jsonField.primary ?? (type === "id"),
|
|
229
236
|
default: jsonField.default,
|
|
230
237
|
translatable: jsonField.translatable ?? false,
|
|
231
238
|
properties,
|
|
@@ -44,21 +44,21 @@ function buildAliasedWhereClause(
|
|
|
44
44
|
/**
|
|
45
45
|
* Options for translation queries
|
|
46
46
|
*/
|
|
47
|
-
export interface TranslationQueryOptions {
|
|
48
|
-
table: string;
|
|
49
|
-
schema: SchemaDefinition;
|
|
50
|
-
lang: string;
|
|
51
|
-
fallbackLang?: string;
|
|
52
|
-
/**
|
|
53
|
-
* Main-table fields that can be safely used as cache fallback in COALESCE.
|
|
54
|
-
* If omitted, all translatable fields are assumed available in main table.
|
|
55
|
-
*/
|
|
56
|
-
mainFallbackFields?: string[];
|
|
57
|
-
where?: Record<string, unknown>;
|
|
58
|
-
orderBy?: OrderByOption[];
|
|
59
|
-
limit?: number;
|
|
60
|
-
offset?: number;
|
|
61
|
-
}
|
|
47
|
+
export interface TranslationQueryOptions {
|
|
48
|
+
table: string;
|
|
49
|
+
schema: SchemaDefinition;
|
|
50
|
+
lang: string;
|
|
51
|
+
fallbackLang?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Main-table fields that can be safely used as cache fallback in COALESCE.
|
|
54
|
+
* If omitted, all translatable fields are assumed available in main table.
|
|
55
|
+
*/
|
|
56
|
+
mainFallbackFields?: string[];
|
|
57
|
+
where?: Record<string, unknown>;
|
|
58
|
+
orderBy?: OrderByOption[];
|
|
59
|
+
limit?: number;
|
|
60
|
+
offset?: number;
|
|
61
|
+
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* Query result with SQL and parameters
|
|
@@ -78,13 +78,13 @@ export function buildTranslationQuery(
|
|
|
78
78
|
table,
|
|
79
79
|
schema,
|
|
80
80
|
lang,
|
|
81
|
-
fallbackLang = schema.languages.default,
|
|
82
|
-
mainFallbackFields,
|
|
83
|
-
where,
|
|
84
|
-
orderBy,
|
|
85
|
-
limit,
|
|
86
|
-
offset,
|
|
87
|
-
} = options;
|
|
81
|
+
fallbackLang = schema.languages.default,
|
|
82
|
+
mainFallbackFields,
|
|
83
|
+
where,
|
|
84
|
+
orderBy,
|
|
85
|
+
limit,
|
|
86
|
+
offset,
|
|
87
|
+
} = options;
|
|
88
88
|
|
|
89
89
|
const tableSchema = schema.tables[table];
|
|
90
90
|
if (!tableSchema) {
|
|
@@ -100,32 +100,32 @@ export function buildTranslationQuery(
|
|
|
100
100
|
const transTable = toTranslationTableName(table);
|
|
101
101
|
const fkName = toTranslationFKName(table);
|
|
102
102
|
|
|
103
|
-
const mainAlias = "m";
|
|
104
|
-
const transAlias = "t";
|
|
105
|
-
const fallbackAlias = "fb";
|
|
106
|
-
const mainFallbackSet =
|
|
107
|
-
mainFallbackFields !== undefined
|
|
108
|
-
? new Set(mainFallbackFields)
|
|
109
|
-
: null;
|
|
103
|
+
const mainAlias = "m";
|
|
104
|
+
const transAlias = "t";
|
|
105
|
+
const fallbackAlias = "fb";
|
|
106
|
+
const mainFallbackSet =
|
|
107
|
+
mainFallbackFields !== undefined
|
|
108
|
+
? new Set(mainFallbackFields)
|
|
109
|
+
: null;
|
|
110
110
|
|
|
111
111
|
// Build SELECT fields
|
|
112
112
|
const selectFields: string[] = [];
|
|
113
113
|
|
|
114
|
-
for (const [fieldName, field] of Object.entries(tableSchema.fields)) {
|
|
115
|
-
if (field.translatable) {
|
|
116
|
-
if (mainFallbackSet && !mainFallbackSet.has(fieldName)) {
|
|
117
|
-
selectFields.push(
|
|
118
|
-
`COALESCE(${transAlias}.${fieldName}, ${fallbackAlias}.${fieldName}) as ${fieldName}`
|
|
119
|
-
);
|
|
120
|
-
} else {
|
|
121
|
-
selectFields.push(
|
|
122
|
-
`COALESCE(${transAlias}.${fieldName}, ${fallbackAlias}.${fieldName}, ${mainAlias}.${fieldName}) as ${fieldName}`
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
} else {
|
|
126
|
-
selectFields.push(`${mainAlias}.${fieldName}`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
114
|
+
for (const [fieldName, field] of Object.entries(tableSchema.fields)) {
|
|
115
|
+
if (field.translatable) {
|
|
116
|
+
if (mainFallbackSet && !mainFallbackSet.has(fieldName)) {
|
|
117
|
+
selectFields.push(
|
|
118
|
+
`COALESCE(${transAlias}.${fieldName}, ${fallbackAlias}.${fieldName}) as ${fieldName}`
|
|
119
|
+
);
|
|
120
|
+
} else {
|
|
121
|
+
selectFields.push(
|
|
122
|
+
`COALESCE(${transAlias}.${fieldName}, ${fallbackAlias}.${fieldName}, ${mainAlias}.${fieldName}) as ${fieldName}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
selectFields.push(`${mainAlias}.${fieldName}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
129
|
|
|
130
130
|
let sql = `SELECT ${selectFields.join(", ")}
|
|
131
131
|
FROM ${table} ${mainAlias}
|
|
@@ -170,24 +170,24 @@ LEFT JOIN ${transTable} ${fallbackAlias}
|
|
|
170
170
|
/**
|
|
171
171
|
* Build query for single record by ID with translations
|
|
172
172
|
*/
|
|
173
|
-
export function buildTranslationQueryById(
|
|
174
|
-
table: string,
|
|
175
|
-
schema: SchemaDefinition,
|
|
176
|
-
id: number | string,
|
|
177
|
-
lang: string,
|
|
178
|
-
fallbackLang?: string,
|
|
179
|
-
mainFallbackFields?: string[]
|
|
180
|
-
): TranslationQueryResult {
|
|
181
|
-
return buildTranslationQuery({
|
|
182
|
-
table,
|
|
183
|
-
schema,
|
|
184
|
-
lang,
|
|
185
|
-
fallbackLang,
|
|
186
|
-
mainFallbackFields,
|
|
187
|
-
where: { id },
|
|
188
|
-
limit: 1,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
173
|
+
export function buildTranslationQueryById(
|
|
174
|
+
table: string,
|
|
175
|
+
schema: SchemaDefinition,
|
|
176
|
+
id: number | string,
|
|
177
|
+
lang: string,
|
|
178
|
+
fallbackLang?: string,
|
|
179
|
+
mainFallbackFields?: string[]
|
|
180
|
+
): TranslationQueryResult {
|
|
181
|
+
return buildTranslationQuery({
|
|
182
|
+
table,
|
|
183
|
+
schema,
|
|
184
|
+
lang,
|
|
185
|
+
fallbackLang,
|
|
186
|
+
mainFallbackFields,
|
|
187
|
+
where: { id },
|
|
188
|
+
limit: 1,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
191
|
|
|
192
192
|
/**
|
|
193
193
|
* Build simple query without translations
|