@autofleet/sadot 1.6.18-beta.2 → 1.6.18-beta.21

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.
Files changed (162) hide show
  1. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/decorate.cjs +11 -0
  2. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/decorate.js +10 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/decorateMetadata.cjs +8 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/decorateMetadata.js +7 -0
  5. package/dist/_virtual/rolldown_runtime.cjs +43 -0
  6. package/dist/{chunk-DJulfCLt.js → _virtual/rolldown_runtime.js} +1 -2
  7. package/dist/api/index.cjs +12 -0
  8. package/dist/api/index.js +11 -0
  9. package/dist/api/v1/definition/index.cjs +113 -0
  10. package/dist/api/v1/definition/index.js +112 -0
  11. package/dist/api/v1/definition/validations.cjs +128 -0
  12. package/dist/api/v1/definition/validations.js +125 -0
  13. package/dist/api/v1/errors.cjs +14 -0
  14. package/dist/api/v1/errors.js +13 -0
  15. package/dist/api/v1/field-policies/index.cjs +104 -0
  16. package/dist/api/v1/field-policies/index.js +103 -0
  17. package/dist/api/v1/index.cjs +16 -0
  18. package/dist/api/v1/index.js +15 -0
  19. package/dist/api/v1/validator/index.cjs +132 -0
  20. package/dist/api/v1/validator/index.js +131 -0
  21. package/dist/api/v1/validator/validations.cjs +40 -0
  22. package/dist/api/v1/validator/validations.d.cts +22 -0
  23. package/dist/api/v1/validator/validations.d.ts +22 -0
  24. package/dist/api/v1/validator/validations.js +38 -0
  25. package/dist/errors/index.cjs +62 -0
  26. package/dist/errors/index.js +55 -0
  27. package/dist/events/index.cjs +52 -0
  28. package/dist/events/index.js +50 -0
  29. package/dist/hooks/enrich.cjs +175 -0
  30. package/dist/hooks/enrich.js +175 -0
  31. package/dist/hooks/find.cjs +22 -0
  32. package/dist/hooks/find.js +22 -0
  33. package/dist/hooks/hooks.cjs +310 -0
  34. package/dist/hooks/hooks.js +302 -0
  35. package/dist/hooks/index.cjs +3 -0
  36. package/dist/hooks/index.js +5 -0
  37. package/dist/hooks/utils/updateInstanceValues.cjs +27 -0
  38. package/dist/hooks/utils/updateInstanceValues.js +27 -0
  39. package/dist/index.cjs +48 -33508
  40. package/dist/index.d.cts +13 -396
  41. package/dist/index.d.ts +14 -394
  42. package/dist/index.js +21 -33435
  43. package/dist/init-state.cjs +56 -0
  44. package/dist/init-state.d.cts +35 -0
  45. package/dist/init-state.d.ts +35 -0
  46. package/dist/init-state.js +55 -0
  47. package/dist/mat-path-state.cjs +10 -0
  48. package/dist/mat-path-state.js +9 -0
  49. package/dist/migrations/001-create-core-tables.cjs +70 -0
  50. package/dist/migrations/001-create-core-tables.js +68 -0
  51. package/dist/migrations/002-create-custom-field-entries.cjs +27 -0
  52. package/dist/migrations/002-create-custom-field-entries.js +25 -0
  53. package/dist/migrations/003-create-custom-field-model-type-map.cjs +35 -0
  54. package/dist/migrations/003-create-custom-field-model-type-map.js +33 -0
  55. package/dist/migrations/004-create-field-policy-tables.cjs +29 -0
  56. package/dist/migrations/004-create-field-policy-tables.js +27 -0
  57. package/dist/migrations/005-add-entity-type-to-field-policies.cjs +20 -0
  58. package/dist/migrations/005-add-entity-type-to-field-policies.js +18 -0
  59. package/dist/migrations/index.cjs +94 -0
  60. package/dist/migrations/index.js +95 -0
  61. package/dist/models/CustomFieldDefinition.cjs +147 -0
  62. package/dist/models/CustomFieldDefinition.d.cts +40 -0
  63. package/dist/models/CustomFieldDefinition.d.ts +40 -0
  64. package/dist/models/CustomFieldDefinition.js +146 -0
  65. package/dist/models/CustomFieldEntries.cjs +71 -0
  66. package/dist/models/CustomFieldEntries.d.cts +15 -0
  67. package/dist/models/CustomFieldEntries.d.ts +15 -0
  68. package/dist/models/CustomFieldEntries.js +70 -0
  69. package/dist/models/CustomFieldModelTypeMap.cjs +62 -0
  70. package/dist/models/CustomFieldModelTypeMap.d.cts +14 -0
  71. package/dist/models/CustomFieldModelTypeMap.d.ts +14 -0
  72. package/dist/models/CustomFieldModelTypeMap.js +61 -0
  73. package/dist/models/CustomFieldValue.cjs +109 -0
  74. package/dist/models/CustomFieldValue.d.cts +20 -0
  75. package/dist/models/CustomFieldValue.d.ts +20 -0
  76. package/dist/models/CustomFieldValue.js +108 -0
  77. package/dist/models/CustomValidator.cjs +68 -0
  78. package/dist/models/CustomValidator.d.cts +18 -0
  79. package/dist/models/CustomValidator.d.ts +18 -0
  80. package/dist/models/CustomValidator.js +67 -0
  81. package/dist/models/FieldPolicy.cjs +54 -0
  82. package/dist/models/FieldPolicy.d.cts +25 -0
  83. package/dist/models/FieldPolicy.d.ts +25 -0
  84. package/dist/models/FieldPolicy.js +53 -0
  85. package/dist/models/index.cjs +163 -0
  86. package/dist/models/index.d.ts +11 -0
  87. package/dist/models/index.js +161 -0
  88. package/dist/models/tests/AssociatedTestModel.cjs +51 -0
  89. package/dist/models/tests/AssociatedTestModel.d.ts +2 -0
  90. package/dist/models/tests/AssociatedTestModel.js +50 -0
  91. package/dist/models/tests/TestModel.cjs +43 -0
  92. package/dist/models/tests/TestModel.d.ts +2 -0
  93. package/dist/models/tests/TestModel.js +42 -0
  94. package/dist/models/tests/contextAwareModels/ContextAwareTestModel.cjs +35 -0
  95. package/dist/models/tests/contextAwareModels/ContextAwareTestModel.d.ts +2 -0
  96. package/dist/models/tests/contextAwareModels/ContextAwareTestModel.js +34 -0
  97. package/dist/models/tests/contextAwareModels/ContextTestModel.cjs +33 -0
  98. package/dist/models/tests/contextAwareModels/ContextTestModel.d.ts +1 -0
  99. package/dist/models/tests/contextAwareModels/ContextTestModel.js +32 -0
  100. package/dist/repository/definition.cjs +221 -0
  101. package/dist/repository/definition.js +212 -0
  102. package/dist/repository/entries.cjs +69 -0
  103. package/dist/repository/entries.js +67 -0
  104. package/dist/repository/field-policy.cjs +69 -0
  105. package/dist/repository/field-policy.d.cts +35 -0
  106. package/dist/repository/field-policy.d.ts +35 -0
  107. package/dist/repository/field-policy.js +59 -0
  108. package/dist/repository/utils/formatValues.cjs +14 -0
  109. package/dist/repository/utils/formatValues.js +14 -0
  110. package/dist/repository/validator.cjs +85 -0
  111. package/dist/repository/validator.js +78 -0
  112. package/dist/repository/value.cjs +77 -0
  113. package/dist/repository/value.js +76 -0
  114. package/dist/scopes/filter.cjs +56 -0
  115. package/dist/scopes/filter.d.cts +22 -0
  116. package/dist/scopes/filter.d.ts +22 -0
  117. package/dist/scopes/filter.js +54 -0
  118. package/dist/scopes/helpers/filter.helpers.cjs +152 -0
  119. package/dist/scopes/helpers/filter.helpers.d.cts +16 -0
  120. package/dist/scopes/helpers/filter.helpers.d.ts +16 -0
  121. package/dist/scopes/helpers/filter.helpers.js +147 -0
  122. package/dist/scopes/index.cjs +1 -0
  123. package/dist/scopes/index.js +3 -0
  124. package/dist/types/index.d.cts +75 -0
  125. package/dist/types/index.d.ts +76 -0
  126. package/dist/utils/constants/index.cjs +32 -0
  127. package/dist/utils/constants/index.d.cts +23 -0
  128. package/dist/utils/constants/index.d.ts +23 -0
  129. package/dist/utils/constants/index.js +29 -0
  130. package/dist/utils/db/index.cjs +15 -0
  131. package/dist/utils/db/index.js +14 -0
  132. package/dist/utils/helpers/index.cjs +23 -0
  133. package/dist/utils/helpers/index.d.cts +30 -0
  134. package/dist/utils/helpers/index.d.ts +30 -0
  135. package/dist/utils/helpers/index.js +21 -0
  136. package/dist/utils/init.cjs +115 -0
  137. package/dist/utils/init.js +111 -0
  138. package/dist/utils/logger/index.cjs +15 -0
  139. package/dist/utils/logger/index.js +12 -0
  140. package/dist/utils/scopeAttributes.cjs +12 -0
  141. package/dist/utils/scopeAttributes.js +11 -0
  142. package/dist/utils/validations/index.cjs +34 -0
  143. package/dist/utils/validations/index.js +32 -0
  144. package/dist/utils/validations/schema/custom-fields.cjs +9 -0
  145. package/dist/utils/validations/schema/custom-fields.d.cts +6 -0
  146. package/dist/utils/validations/schema/custom-fields.d.ts +6 -0
  147. package/dist/utils/validations/schema/custom-fields.js +7 -0
  148. package/dist/utils/validations/schema/validator-schema.cjs +102 -0
  149. package/dist/utils/validations/schema/validator-schema.js +99 -0
  150. package/dist/utils/validations/validators/index.cjs +45 -0
  151. package/dist/utils/validations/validators/index.js +42 -0
  152. package/dist/utils/validations/validators/multi-select-dropdown.validator.cjs +7 -0
  153. package/dist/utils/validations/validators/multi-select-dropdown.validator.js +7 -0
  154. package/dist/utils/validations/validators/multi-select-status.validator.cjs +10 -0
  155. package/dist/utils/validations/validators/multi-select-status.validator.js +10 -0
  156. package/dist/utils/validations/validators/multi-select.helpers.cjs +12 -0
  157. package/dist/utils/validations/validators/multi-select.helpers.js +10 -0
  158. package/dist/utils/validations/validators/select.validator.cjs +12 -0
  159. package/dist/utils/validations/validators/select.validator.js +10 -0
  160. package/dist/utils/validations/validators/status.validator.cjs +12 -0
  161. package/dist/utils/validations/validators/status.validator.js +10 -0
  162. package/package.json +4 -4
@@ -0,0 +1,22 @@
1
+ import { CustomFieldOptions } from "../types/index.cjs";
2
+ import { ConditionValue } from "./helpers/filter.helpers.cjs";
3
+ import { customFields } from "@autofleet/common-types";
4
+ import { Literal } from "sequelize/types/utils";
5
+
6
+ //#region src/scopes/filter.d.ts
7
+
8
+ declare const customFieldsSortScope: (name: string, options?: Pick<CustomFieldOptions, "useCustomFieldsEntries">) => ({
9
+ replacementsMap,
10
+ scopeValue: sort
11
+ }: {
12
+ replacementsMap: Record<string, string>;
13
+ scopeValue: Record<string, ConditionValue>;
14
+ }) => {
15
+ attributes: {
16
+ include: (string | Literal)[][];
17
+ };
18
+ order: Literal[];
19
+ replacements: Record<string, string>;
20
+ } | Record<string, never>;
21
+ //#endregion
22
+ export { customFieldsSortScope };
@@ -0,0 +1,22 @@
1
+ import { CustomFieldOptions } from "../types/index.js";
2
+ import { ConditionValue } from "./helpers/filter.helpers.js";
3
+ import { customFields } from "@autofleet/common-types";
4
+ import { Literal } from "sequelize/types/utils";
5
+
6
+ //#region src/scopes/filter.d.ts
7
+
8
+ declare const customFieldsSortScope: (name: string, options?: Pick<CustomFieldOptions, "useCustomFieldsEntries">) => ({
9
+ replacementsMap,
10
+ scopeValue: sort
11
+ }: {
12
+ replacementsMap: Record<string, string>;
13
+ scopeValue: Record<string, ConditionValue>;
14
+ }) => {
15
+ attributes: {
16
+ include: (string | Literal)[][];
17
+ };
18
+ order: Literal[];
19
+ replacements: Record<string, string>;
20
+ } | Record<string, never>;
21
+ //#endregion
22
+ export { customFieldsSortScope };
@@ -0,0 +1,54 @@
1
+ import { generateRandomString } from "../utils/helpers/index.js";
2
+ import { SubQueryType, formatConditionsForEntries, formatConditionsForValues, getFilterCustomFieldsSubQuery, getSortCustomFieldsSubQuery } from "./helpers/filter.helpers.js";
3
+ import { Op } from "sequelize";
4
+ import { Sequelize } from "sequelize-typescript";
5
+ import { customFields } from "@autofleet/common-types";
6
+
7
+ //#region src/scopes/filter.ts
8
+ /**
9
+ * A Sequelize scope for filtering models by custom fields.
10
+ * This scope builds a WHERE clause to be applied on the main query.
11
+ *
12
+ * @param name - The model type name used to join custom_field_definitions.
13
+ * @returns A function that takes conditions and returns the Sequelize options object.
14
+ */
15
+ const customFieldsFilterScope = (name, options) => ({ replacementsMap: replacements, scopeValue: conditions }) => {
16
+ if (!conditions || Object.keys(conditions).length === 0) return {};
17
+ const queryType = options?.useCustomFieldsEntries ? SubQueryType.ENTRIES : SubQueryType.VALUES;
18
+ const reverseReplacementsMap = new Map(Object.entries(replacements).map(([key, value]) => [value, key]));
19
+ const conditionsStrings = Object.entries(conditions).map(([key, condition]) => {
20
+ switch (queryType) {
21
+ case SubQueryType.ENTRIES: return formatConditionsForEntries(key, condition, reverseReplacementsMap);
22
+ case SubQueryType.VALUES: return formatConditionsForValues(key, condition, reverseReplacementsMap);
23
+ default: return false;
24
+ }
25
+ }).filter(Boolean);
26
+ if (conditionsStrings.length === 0) return {};
27
+ const subQuery = getFilterCustomFieldsSubQuery(queryType, name, conditionsStrings);
28
+ return {
29
+ where: { id: { [Op.in]: Sequelize.literal(`(${subQuery})`) } },
30
+ replacements
31
+ };
32
+ };
33
+ const scopeName = customFields.CUSTOM_FIELDS_FILTER_SCOPE;
34
+ const customFieldsSortScope = (name, options) => ({ replacementsMap, scopeValue: sort }) => {
35
+ if (!sort || sort.length === 0) return {};
36
+ const queryType = options?.useCustomFieldsEntries ? SubQueryType.ENTRIES : SubQueryType.VALUES;
37
+ const randomStr = generateRandomString();
38
+ const includes = Object.entries(sort).map(([key]) => {
39
+ const replacementKey = Object.keys(replacementsMap).find((randomString) => replacementsMap[randomString] === key);
40
+ return [Sequelize.literal(getSortCustomFieldsSubQuery(queryType, name, replacementKey)), randomStr];
41
+ });
42
+ const orders = Object.entries(sort).map(([, sortObject]) => {
43
+ const direction = typeof sortObject === "string" ? sortObject : Object.values(sortObject)[0];
44
+ return Sequelize.literal(`"${randomStr}" ${direction || "ASC"}`);
45
+ });
46
+ return {
47
+ attributes: { include: includes },
48
+ order: orders,
49
+ replacements: replacementsMap
50
+ };
51
+ };
52
+
53
+ //#endregion
54
+ export { customFieldsFilterScope, customFieldsSortScope };
@@ -0,0 +1,152 @@
1
+
2
+ //#region src/scopes/helpers/filter.helpers.ts
3
+ let SubQueryType = /* @__PURE__ */ function(SubQueryType$1) {
4
+ SubQueryType$1["VALUES"] = "values";
5
+ SubQueryType$1["ENTRIES"] = "entries";
6
+ return SubQueryType$1;
7
+ }({});
8
+ const isConditionStringArray = (input) => Array.isArray(input) && typeof input[0] === "string";
9
+ const isBooleanString = (input) => ["true", "false"].includes(input.toString());
10
+ const isDate = (input) => input instanceof Date || Object.prototype.toString.call(input) === "[object Date]";
11
+ const castValueToJsonb = (value, type) => `to_jsonb(${value}::${type})`;
12
+ const castValueToJsonbText = (value) => castValueToJsonb(value, "text");
13
+ const castValueToJsonbBoolean = (value) => castValueToJsonb(value, "boolean");
14
+ const castValueToJsonbNumeric = (value) => castValueToJsonb(value, "numeric");
15
+ const castIfNeeded = (columnName, conditionValue) => {
16
+ if (isDate(conditionValue)) return castValueToJsonb(columnName, "timestamp");
17
+ return columnName;
18
+ };
19
+ const AND_DELIMITER = " AND ";
20
+ const OR_DELIMITER = " OR ";
21
+ const CD_TABLE_ALIAS = "cd";
22
+ const CD_NAME_COLUMN = `${CD_TABLE_ALIAS}.name`;
23
+ const CV_TABLE_ALIAS = "cv";
24
+ const CV_VALUE_COLUMN = `${CV_TABLE_ALIAS}.value`;
25
+ const CE_TABLE_ALIAS = "ce";
26
+ const getSingleConditionWithOperator = (value, operator, replacementKey, reverseReplacementsMap) => {
27
+ let type = "text";
28
+ if (isDate(value)) type = "date";
29
+ else if (!Number.isNaN(Number(value))) type = "numeric";
30
+ else if (isBooleanString(value)) type = "boolean";
31
+ const replacedValue = reverseReplacementsMap.get(value);
32
+ return `(jsonb_extract_path_text(${CE_TABLE_ALIAS}.custom_fields, :${replacementKey})::${type}) ${operator} :${replacedValue}`;
33
+ };
34
+ const getFormattedValue = (value) => {
35
+ let formattedValue = value;
36
+ if (isBooleanString(value)) formattedValue = value === "true";
37
+ else if (!Number.isNaN(Number(value))) formattedValue = Number(value);
38
+ return formattedValue;
39
+ };
40
+ const getJSONSubQuery = (value, key) => {
41
+ const formattedValue = getFormattedValue(value);
42
+ const jsonQuery = JSON.stringify({ [key]: formattedValue });
43
+ let jsonQueryWithStringBoolean;
44
+ if (isBooleanString(value)) jsonQueryWithStringBoolean = `${CE_TABLE_ALIAS}.custom_fields @> '${JSON.stringify({ [key]: value })}'`;
45
+ return `
46
+ (
47
+ ${jsonQueryWithStringBoolean ? `${jsonQueryWithStringBoolean} OR` : ""}
48
+ ${CE_TABLE_ALIAS}.custom_fields @> '${jsonQuery}'
49
+ )
50
+ `;
51
+ };
52
+ const getFilterCustomFieldsSubQuery = (queryType, modelType, conditionsStrings) => {
53
+ switch (queryType) {
54
+ case SubQueryType.VALUES: return `
55
+ SELECT ${CV_TABLE_ALIAS}.model_id
56
+ FROM custom_field_values AS ${CV_TABLE_ALIAS}
57
+ INNER JOIN custom_field_definitions AS ${CD_TABLE_ALIAS} ON ${CV_TABLE_ALIAS}.custom_field_definition_id = ${CD_TABLE_ALIAS}.id
58
+ ${AND_DELIMITER}${CD_TABLE_ALIAS}.model_type = '${modelType}'
59
+ WHERE ${conditionsStrings.join(OR_DELIMITER)}
60
+ ${AND_DELIMITER}${CV_TABLE_ALIAS}.deleted_at IS NULL${AND_DELIMITER}${CD_TABLE_ALIAS}.deleted_at IS NULL
61
+ GROUP BY ${CV_TABLE_ALIAS}.model_id
62
+ HAVING COUNT(DISTINCT ${CV_TABLE_ALIAS}.custom_field_definition_id) = ${conditionsStrings.length}
63
+ `.replace(/\n/g, "");
64
+ case SubQueryType.ENTRIES: return `
65
+ SELECT ce.model_id
66
+ FROM custom_field_entries ce
67
+ JOIN custom_field_definitions cfd
68
+ ON ce.model_type = cfd.model_type
69
+ AND ce.entity_id = cfd.entity_id
70
+ WHERE
71
+ cfd.deleted_at IS NULL AND
72
+ ${conditionsStrings.join(AND_DELIMITER)}
73
+ `;
74
+ default: throw new Error("Invalid query type");
75
+ }
76
+ };
77
+ const getSortCustomFieldsSubQuery = (queryType, modelType, replacementKey) => {
78
+ switch (queryType) {
79
+ case SubQueryType.VALUES: return `(
80
+ SELECT value
81
+ FROM (SELECT cv.model_id, cv.value
82
+ FROM custom_field_values AS cv INNER JOIN custom_field_definitions AS cd
83
+ ON cv.custom_field_definition_id = cd.id
84
+ ${AND_DELIMITER}cd.model_type = '${modelType}'
85
+ WHERE cv.model_id = "${modelType}"."id"
86
+ ${AND_DELIMITER}cd.name = :${replacementKey}
87
+ ) AS CustomFieldAggregation
88
+ )
89
+ `;
90
+ case SubQueryType.ENTRIES: return `(
91
+ SELECT
92
+ customFields.value
93
+ FROM
94
+ custom_field_entries AS ${CE_TABLE_ALIAS},
95
+ jsonb_each_text(custom_fields) AS customFields
96
+ WHERE
97
+ customFields.key = :${replacementKey}${AND_DELIMITER}
98
+ ${CE_TABLE_ALIAS}.model_type = '${modelType}'${AND_DELIMITER}
99
+ ${CE_TABLE_ALIAS}.model_id = "${modelType}"."id"
100
+ )
101
+ `;
102
+ default: throw new Error("Invalid query type");
103
+ }
104
+ };
105
+ const formatConditionsForValues = (key, condition, reverseReplacementsMap) => {
106
+ const replacementKey = reverseReplacementsMap.get(key);
107
+ if (!replacementKey) return false;
108
+ const columnCondition = `(${CD_NAME_COLUMN} = :${replacementKey})`;
109
+ if (Array.isArray(condition)) {
110
+ if (condition.length === 0) return false;
111
+ if (isConditionStringArray(condition)) return `(${columnCondition}${AND_DELIMITER}${CV_VALUE_COLUMN} IN (${condition.flatMap((v) => {
112
+ const valRandom = reverseReplacementsMap.get(v);
113
+ if (isBooleanString(v)) return [castValueToJsonbText(`:${valRandom}`), castValueToJsonbBoolean(`:${valRandom}`)];
114
+ if (!Number.isNaN(Number(v))) return castValueToJsonbNumeric(`:${valRandom}`);
115
+ return castValueToJsonbText(`:${valRandom}`);
116
+ }).join(",")}))`;
117
+ return condition.map((c) => {
118
+ const valueAsJsonb = castValueToJsonbText(`:${reverseReplacementsMap.get(c.value)}`);
119
+ return `(${columnCondition}${AND_DELIMITER}${castIfNeeded(CV_VALUE_COLUMN, c.value)} ${c.operator} ${valueAsJsonb})`;
120
+ }).join(AND_DELIMITER);
121
+ }
122
+ if (typeof condition === "string" || typeof condition === "number") {
123
+ const conditionRep = reverseReplacementsMap.get(condition);
124
+ const valueAsJsonb = !Number.isNaN(Number(condition)) ? castValueToJsonbNumeric(`:${conditionRep}`) : castValueToJsonbText(`:${conditionRep}`);
125
+ const valueAsJsonbBoolean = isBooleanString(condition) ? `${OR_DELIMITER}${CV_VALUE_COLUMN} = ${castValueToJsonbBoolean(`:${conditionRep}`)}` : "";
126
+ return `(${columnCondition}${AND_DELIMITER}(${castIfNeeded(CV_VALUE_COLUMN, condition)} = ${valueAsJsonb}${valueAsJsonbBoolean}))`;
127
+ }
128
+ if (condition?.operator) {
129
+ const valueAsJsonb = castValueToJsonbText(`:${reverseReplacementsMap.get(condition.value)}`);
130
+ return `( ${columnCondition}${AND_DELIMITER}${castIfNeeded(CV_VALUE_COLUMN, condition.value)} ${condition.operator} ${valueAsJsonb})`;
131
+ }
132
+ return false;
133
+ };
134
+ const formatConditionsForEntries = (key, condition, reverseReplacementsMap) => {
135
+ const replacementKey = reverseReplacementsMap.get(key);
136
+ if (!replacementKey) return false;
137
+ if (Array.isArray(condition)) {
138
+ if (condition.length === 0) return false;
139
+ if (isConditionStringArray(condition)) return `( ${condition.map((value) => getJSONSubQuery(value, key)).join(`${OR_DELIMITER}\n`)})`;
140
+ return condition.map((c) => getSingleConditionWithOperator(c.value, c.operator, replacementKey, reverseReplacementsMap)).join(AND_DELIMITER);
141
+ }
142
+ if (typeof condition === "string" || typeof condition === "number") return getJSONSubQuery(condition, key);
143
+ if (condition?.operator) return getSingleConditionWithOperator(condition.value, condition.operator, replacementKey, reverseReplacementsMap);
144
+ return false;
145
+ };
146
+
147
+ //#endregion
148
+ exports.SubQueryType = SubQueryType;
149
+ exports.formatConditionsForEntries = formatConditionsForEntries;
150
+ exports.formatConditionsForValues = formatConditionsForValues;
151
+ exports.getFilterCustomFieldsSubQuery = getFilterCustomFieldsSubQuery;
152
+ exports.getSortCustomFieldsSubQuery = getSortCustomFieldsSubQuery;
@@ -0,0 +1,16 @@
1
+ import { WhereOptions } from "sequelize";
2
+
3
+ //#region src/scopes/helpers/filter.helpers.d.ts
4
+
5
+ /**
6
+ * Type representing possible condition values.
7
+ * Currently supporting strings and arrays of strings.
8
+ * More types to be added (TBA).
9
+ */
10
+ interface ConditionWithOperator {
11
+ operator: string;
12
+ value: string;
13
+ }
14
+ type ConditionValue = ConditionWithOperator | ConditionWithOperator[] | string | string[];
15
+ //#endregion
16
+ export { ConditionValue };
@@ -0,0 +1,16 @@
1
+ import { WhereOptions } from "sequelize";
2
+
3
+ //#region src/scopes/helpers/filter.helpers.d.ts
4
+
5
+ /**
6
+ * Type representing possible condition values.
7
+ * Currently supporting strings and arrays of strings.
8
+ * More types to be added (TBA).
9
+ */
10
+ interface ConditionWithOperator {
11
+ operator: string;
12
+ value: string;
13
+ }
14
+ type ConditionValue = ConditionWithOperator | ConditionWithOperator[] | string | string[];
15
+ //#endregion
16
+ export { ConditionValue };
@@ -0,0 +1,147 @@
1
+ //#region src/scopes/helpers/filter.helpers.ts
2
+ let SubQueryType = /* @__PURE__ */ function(SubQueryType$1) {
3
+ SubQueryType$1["VALUES"] = "values";
4
+ SubQueryType$1["ENTRIES"] = "entries";
5
+ return SubQueryType$1;
6
+ }({});
7
+ const isConditionStringArray = (input) => Array.isArray(input) && typeof input[0] === "string";
8
+ const isBooleanString = (input) => ["true", "false"].includes(input.toString());
9
+ const isDate = (input) => input instanceof Date || Object.prototype.toString.call(input) === "[object Date]";
10
+ const castValueToJsonb = (value, type) => `to_jsonb(${value}::${type})`;
11
+ const castValueToJsonbText = (value) => castValueToJsonb(value, "text");
12
+ const castValueToJsonbBoolean = (value) => castValueToJsonb(value, "boolean");
13
+ const castValueToJsonbNumeric = (value) => castValueToJsonb(value, "numeric");
14
+ const castIfNeeded = (columnName, conditionValue) => {
15
+ if (isDate(conditionValue)) return castValueToJsonb(columnName, "timestamp");
16
+ return columnName;
17
+ };
18
+ const AND_DELIMITER = " AND ";
19
+ const OR_DELIMITER = " OR ";
20
+ const CD_TABLE_ALIAS = "cd";
21
+ const CD_NAME_COLUMN = `${CD_TABLE_ALIAS}.name`;
22
+ const CV_TABLE_ALIAS = "cv";
23
+ const CV_VALUE_COLUMN = `${CV_TABLE_ALIAS}.value`;
24
+ const CE_TABLE_ALIAS = "ce";
25
+ const getSingleConditionWithOperator = (value, operator, replacementKey, reverseReplacementsMap) => {
26
+ let type = "text";
27
+ if (isDate(value)) type = "date";
28
+ else if (!Number.isNaN(Number(value))) type = "numeric";
29
+ else if (isBooleanString(value)) type = "boolean";
30
+ const replacedValue = reverseReplacementsMap.get(value);
31
+ return `(jsonb_extract_path_text(${CE_TABLE_ALIAS}.custom_fields, :${replacementKey})::${type}) ${operator} :${replacedValue}`;
32
+ };
33
+ const getFormattedValue = (value) => {
34
+ let formattedValue = value;
35
+ if (isBooleanString(value)) formattedValue = value === "true";
36
+ else if (!Number.isNaN(Number(value))) formattedValue = Number(value);
37
+ return formattedValue;
38
+ };
39
+ const getJSONSubQuery = (value, key) => {
40
+ const formattedValue = getFormattedValue(value);
41
+ const jsonQuery = JSON.stringify({ [key]: formattedValue });
42
+ let jsonQueryWithStringBoolean;
43
+ if (isBooleanString(value)) jsonQueryWithStringBoolean = `${CE_TABLE_ALIAS}.custom_fields @> '${JSON.stringify({ [key]: value })}'`;
44
+ return `
45
+ (
46
+ ${jsonQueryWithStringBoolean ? `${jsonQueryWithStringBoolean} OR` : ""}
47
+ ${CE_TABLE_ALIAS}.custom_fields @> '${jsonQuery}'
48
+ )
49
+ `;
50
+ };
51
+ const getFilterCustomFieldsSubQuery = (queryType, modelType, conditionsStrings) => {
52
+ switch (queryType) {
53
+ case SubQueryType.VALUES: return `
54
+ SELECT ${CV_TABLE_ALIAS}.model_id
55
+ FROM custom_field_values AS ${CV_TABLE_ALIAS}
56
+ INNER JOIN custom_field_definitions AS ${CD_TABLE_ALIAS} ON ${CV_TABLE_ALIAS}.custom_field_definition_id = ${CD_TABLE_ALIAS}.id
57
+ ${AND_DELIMITER}${CD_TABLE_ALIAS}.model_type = '${modelType}'
58
+ WHERE ${conditionsStrings.join(OR_DELIMITER)}
59
+ ${AND_DELIMITER}${CV_TABLE_ALIAS}.deleted_at IS NULL${AND_DELIMITER}${CD_TABLE_ALIAS}.deleted_at IS NULL
60
+ GROUP BY ${CV_TABLE_ALIAS}.model_id
61
+ HAVING COUNT(DISTINCT ${CV_TABLE_ALIAS}.custom_field_definition_id) = ${conditionsStrings.length}
62
+ `.replace(/\n/g, "");
63
+ case SubQueryType.ENTRIES: return `
64
+ SELECT ce.model_id
65
+ FROM custom_field_entries ce
66
+ JOIN custom_field_definitions cfd
67
+ ON ce.model_type = cfd.model_type
68
+ AND ce.entity_id = cfd.entity_id
69
+ WHERE
70
+ cfd.deleted_at IS NULL AND
71
+ ${conditionsStrings.join(AND_DELIMITER)}
72
+ `;
73
+ default: throw new Error("Invalid query type");
74
+ }
75
+ };
76
+ const getSortCustomFieldsSubQuery = (queryType, modelType, replacementKey) => {
77
+ switch (queryType) {
78
+ case SubQueryType.VALUES: return `(
79
+ SELECT value
80
+ FROM (SELECT cv.model_id, cv.value
81
+ FROM custom_field_values AS cv INNER JOIN custom_field_definitions AS cd
82
+ ON cv.custom_field_definition_id = cd.id
83
+ ${AND_DELIMITER}cd.model_type = '${modelType}'
84
+ WHERE cv.model_id = "${modelType}"."id"
85
+ ${AND_DELIMITER}cd.name = :${replacementKey}
86
+ ) AS CustomFieldAggregation
87
+ )
88
+ `;
89
+ case SubQueryType.ENTRIES: return `(
90
+ SELECT
91
+ customFields.value
92
+ FROM
93
+ custom_field_entries AS ${CE_TABLE_ALIAS},
94
+ jsonb_each_text(custom_fields) AS customFields
95
+ WHERE
96
+ customFields.key = :${replacementKey}${AND_DELIMITER}
97
+ ${CE_TABLE_ALIAS}.model_type = '${modelType}'${AND_DELIMITER}
98
+ ${CE_TABLE_ALIAS}.model_id = "${modelType}"."id"
99
+ )
100
+ `;
101
+ default: throw new Error("Invalid query type");
102
+ }
103
+ };
104
+ const formatConditionsForValues = (key, condition, reverseReplacementsMap) => {
105
+ const replacementKey = reverseReplacementsMap.get(key);
106
+ if (!replacementKey) return false;
107
+ const columnCondition = `(${CD_NAME_COLUMN} = :${replacementKey})`;
108
+ if (Array.isArray(condition)) {
109
+ if (condition.length === 0) return false;
110
+ if (isConditionStringArray(condition)) return `(${columnCondition}${AND_DELIMITER}${CV_VALUE_COLUMN} IN (${condition.flatMap((v) => {
111
+ const valRandom = reverseReplacementsMap.get(v);
112
+ if (isBooleanString(v)) return [castValueToJsonbText(`:${valRandom}`), castValueToJsonbBoolean(`:${valRandom}`)];
113
+ if (!Number.isNaN(Number(v))) return castValueToJsonbNumeric(`:${valRandom}`);
114
+ return castValueToJsonbText(`:${valRandom}`);
115
+ }).join(",")}))`;
116
+ return condition.map((c) => {
117
+ const valueAsJsonb = castValueToJsonbText(`:${reverseReplacementsMap.get(c.value)}`);
118
+ return `(${columnCondition}${AND_DELIMITER}${castIfNeeded(CV_VALUE_COLUMN, c.value)} ${c.operator} ${valueAsJsonb})`;
119
+ }).join(AND_DELIMITER);
120
+ }
121
+ if (typeof condition === "string" || typeof condition === "number") {
122
+ const conditionRep = reverseReplacementsMap.get(condition);
123
+ const valueAsJsonb = !Number.isNaN(Number(condition)) ? castValueToJsonbNumeric(`:${conditionRep}`) : castValueToJsonbText(`:${conditionRep}`);
124
+ const valueAsJsonbBoolean = isBooleanString(condition) ? `${OR_DELIMITER}${CV_VALUE_COLUMN} = ${castValueToJsonbBoolean(`:${conditionRep}`)}` : "";
125
+ return `(${columnCondition}${AND_DELIMITER}(${castIfNeeded(CV_VALUE_COLUMN, condition)} = ${valueAsJsonb}${valueAsJsonbBoolean}))`;
126
+ }
127
+ if (condition?.operator) {
128
+ const valueAsJsonb = castValueToJsonbText(`:${reverseReplacementsMap.get(condition.value)}`);
129
+ return `( ${columnCondition}${AND_DELIMITER}${castIfNeeded(CV_VALUE_COLUMN, condition.value)} ${condition.operator} ${valueAsJsonb})`;
130
+ }
131
+ return false;
132
+ };
133
+ const formatConditionsForEntries = (key, condition, reverseReplacementsMap) => {
134
+ const replacementKey = reverseReplacementsMap.get(key);
135
+ if (!replacementKey) return false;
136
+ if (Array.isArray(condition)) {
137
+ if (condition.length === 0) return false;
138
+ if (isConditionStringArray(condition)) return `( ${condition.map((value) => getJSONSubQuery(value, key)).join(`${OR_DELIMITER}\n`)})`;
139
+ return condition.map((c) => getSingleConditionWithOperator(c.value, c.operator, replacementKey, reverseReplacementsMap)).join(AND_DELIMITER);
140
+ }
141
+ if (typeof condition === "string" || typeof condition === "number") return getJSONSubQuery(condition, key);
142
+ if (condition?.operator) return getSingleConditionWithOperator(condition.value, condition.operator, replacementKey, reverseReplacementsMap);
143
+ return false;
144
+ };
145
+
146
+ //#endregion
147
+ export { SubQueryType, formatConditionsForEntries, formatConditionsForValues, getFilterCustomFieldsSubQuery, getSortCustomFieldsSubQuery };
@@ -0,0 +1 @@
1
+ const require_filter = require('./filter.cjs');
@@ -0,0 +1,3 @@
1
+ import { customFieldsFilterScope } from "./filter.js";
2
+
3
+ export { };
@@ -0,0 +1,75 @@
1
+ import { CustomFieldDefinition } from "../models/CustomFieldDefinition.cjs";
2
+ import { ModelCtor, Sequelize } from "sequelize-typescript";
3
+ import { IncludeOptions } from "sequelize";
4
+
5
+ //#region src/types/index.d.ts
6
+ type ModelFetcher = (name: string) => any;
7
+ interface ModelOptions {
8
+ /**
9
+ * Include options for the model
10
+ */
11
+ include?: (entityIds: string | string[]) => IncludeOptions[];
12
+ /**
13
+ * Custom association for the model
14
+ * @param model - The model to associate with
15
+ */
16
+ customAssociation?: (model: ModelCtor) => void;
17
+ /**
18
+ * Whether to use the entity id from the instance per scope attribute
19
+ */
20
+ useEntityIdFromInclude?: boolean;
21
+ /**
22
+ * Which attributes to include in the model
23
+ */
24
+ attributes?: string[];
25
+ }
26
+ interface Models {
27
+ name: string;
28
+ scopeAttributes: any[];
29
+ modelOptions?: ModelOptions;
30
+ hasTypeId?: boolean;
31
+ /**
32
+ * Optional async callback to determine which custom fields are required for an instance.
33
+ * Allows consumer applications to override CustomFieldDefinition.required based on custom logic.
34
+ * @param instance - The model instance being created/updated
35
+ * @param fieldDefinitions - Array of CustomFieldDefinition records
36
+ * @param options - Sequelize options including transaction
37
+ * @returns Array of field names that should be validated as required
38
+ */
39
+ getRequiredCustomFields?: (instance: any, fieldDefinitions: CustomFieldDefinition[], options: any) => Promise<string[]> | string[];
40
+ creationWebhookHandler?: (instance: any) => any;
41
+ updateWebhookHandler?: (instance: any) => any;
42
+ deletionWebhookHandler?: (instance: any) => any;
43
+ }
44
+ interface CustomFieldOptions {
45
+ models: Models[];
46
+ hasTypeId?: boolean;
47
+ databaseConfig: any;
48
+ getUser: typeof GetUserType;
49
+ sequelize?: Sequelize;
50
+ useCustomFieldsEntries?: boolean;
51
+ useValidators?: boolean;
52
+ useModelTypeMapping?: boolean;
53
+ /**
54
+ * When true, use the legacy `sync({ alter: true })` migration approach
55
+ * instead of umzug-managed migrations via the `sadot_migrations` table.
56
+ * Defaults to false (umzug is used by default).
57
+ */
58
+ runLegacyMigrations?: boolean;
59
+ getRequiredCustomFields?: (instance: any, fieldDefinitions: CustomFieldDefinition[], options: any) => Promise<string[]> | string[];
60
+ /**
61
+ * Optional predicate that returns true when materialized-path RLS is active for the current
62
+ * request. When it returns true, sadot's `userScope` on CFD/CV becomes a no-op so RLS is the
63
+ * sole filter (consumer typically decorates CFD/CV with their mat-path manager so RLS applies).
64
+ * Pass `matPathManager.isScopingRequest` if using `@autofleet/mat-path-mvp`.
65
+ */
66
+ isMatPathEnabled?: () => boolean;
67
+ /**
68
+ * Optional hook for the effective field-policy route. When provided, sadot mounts
69
+ * GET /field-policies/:entityType/effective and delegates scoped queries through this callback.
70
+ * Typically: (table, cb) => { matPathManager.setScopingMode({ [table]: SCOPING_MODE.ANCESTORS }); return matPathManager.withPaths(cb); }
71
+ */
72
+ withMatPath?: (tableName: string, callback: (transaction: any) => Promise<any>) => Promise<any>;
73
+ }
74
+ //#endregion
75
+ export { CustomFieldOptions, ModelFetcher, Models };
@@ -0,0 +1,76 @@
1
+ import { CustomFieldDefinition } from "../models/CustomFieldDefinition.js";
2
+ import "../models/CustomValidator.js";
3
+ import { IncludeOptions } from "sequelize";
4
+ import { ModelCtor, Sequelize } from "sequelize-typescript";
5
+
6
+ //#region src/types/index.d.ts
7
+ type ModelFetcher = (name: string) => any;
8
+ interface ModelOptions {
9
+ /**
10
+ * Include options for the model
11
+ */
12
+ include?: (entityIds: string | string[]) => IncludeOptions[];
13
+ /**
14
+ * Custom association for the model
15
+ * @param model - The model to associate with
16
+ */
17
+ customAssociation?: (model: ModelCtor) => void;
18
+ /**
19
+ * Whether to use the entity id from the instance per scope attribute
20
+ */
21
+ useEntityIdFromInclude?: boolean;
22
+ /**
23
+ * Which attributes to include in the model
24
+ */
25
+ attributes?: string[];
26
+ }
27
+ interface Models {
28
+ name: string;
29
+ scopeAttributes: any[];
30
+ modelOptions?: ModelOptions;
31
+ hasTypeId?: boolean;
32
+ /**
33
+ * Optional async callback to determine which custom fields are required for an instance.
34
+ * Allows consumer applications to override CustomFieldDefinition.required based on custom logic.
35
+ * @param instance - The model instance being created/updated
36
+ * @param fieldDefinitions - Array of CustomFieldDefinition records
37
+ * @param options - Sequelize options including transaction
38
+ * @returns Array of field names that should be validated as required
39
+ */
40
+ getRequiredCustomFields?: (instance: any, fieldDefinitions: CustomFieldDefinition[], options: any) => Promise<string[]> | string[];
41
+ creationWebhookHandler?: (instance: any) => any;
42
+ updateWebhookHandler?: (instance: any) => any;
43
+ deletionWebhookHandler?: (instance: any) => any;
44
+ }
45
+ interface CustomFieldOptions {
46
+ models: Models[];
47
+ hasTypeId?: boolean;
48
+ databaseConfig: any;
49
+ getUser: typeof GetUserType;
50
+ sequelize?: Sequelize;
51
+ useCustomFieldsEntries?: boolean;
52
+ useValidators?: boolean;
53
+ useModelTypeMapping?: boolean;
54
+ /**
55
+ * When true, use the legacy `sync({ alter: true })` migration approach
56
+ * instead of umzug-managed migrations via the `sadot_migrations` table.
57
+ * Defaults to false (umzug is used by default).
58
+ */
59
+ runLegacyMigrations?: boolean;
60
+ getRequiredCustomFields?: (instance: any, fieldDefinitions: CustomFieldDefinition[], options: any) => Promise<string[]> | string[];
61
+ /**
62
+ * Optional predicate that returns true when materialized-path RLS is active for the current
63
+ * request. When it returns true, sadot's `userScope` on CFD/CV becomes a no-op so RLS is the
64
+ * sole filter (consumer typically decorates CFD/CV with their mat-path manager so RLS applies).
65
+ * Pass `matPathManager.isScopingRequest` if using `@autofleet/mat-path-mvp`.
66
+ */
67
+ isMatPathEnabled?: () => boolean;
68
+ /**
69
+ * Optional hook for the effective field-policy route. When provided, sadot mounts
70
+ * GET /field-policies/:entityType/effective and delegates scoped queries through this callback.
71
+ * Typically: (table, cb) => { matPathManager.setScopingMode({ [table]: SCOPING_MODE.ANCESTORS }); return matPathManager.withPaths(cb); }
72
+ */
73
+ withMatPath?: (tableName: string, callback: (transaction: any) => Promise<any>) => Promise<any>;
74
+ }
75
+ //#endregion
76
+ export { CustomFieldOptions, ModelFetcher, Models };
@@ -0,0 +1,32 @@
1
+ const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
2
+ let _autofleet_common_types = require("@autofleet/common-types");
3
+
4
+ //#region src/utils/constants/index.ts
5
+ const supportedEntities = [
6
+ "businessModelId",
7
+ "fleetId",
8
+ "demandSourceId"
9
+ ];
10
+ /**
11
+ * Supported custom field types
12
+ */
13
+ let CustomFieldDefinitionType = /* @__PURE__ */ function(CustomFieldDefinitionType$1) {
14
+ CustomFieldDefinitionType$1["NUMBER"] = "number";
15
+ CustomFieldDefinitionType$1["BOOLEAN"] = "boolean";
16
+ CustomFieldDefinitionType$1["DATE"] = "date";
17
+ CustomFieldDefinitionType$1["DATETIME"] = "datetime";
18
+ CustomFieldDefinitionType$1["TEXT"] = "text";
19
+ CustomFieldDefinitionType$1["IMAGE"] = "image";
20
+ CustomFieldDefinitionType$1["SELECT"] = "select";
21
+ CustomFieldDefinitionType$1["STATUS"] = "status";
22
+ CustomFieldDefinitionType$1["FILE"] = "file";
23
+ CustomFieldDefinitionType$1["MULTI_SELECT_STATUS"] = "multi-select-status";
24
+ CustomFieldDefinitionType$1["MULTI_SELECT_DROPDOWN"] = "multi-select-dropdown";
25
+ return CustomFieldDefinitionType$1;
26
+ }({});
27
+ const CUSTOM_FIELDS_FILTER_SCOPE = _autofleet_common_types.customFields.CUSTOM_FIELDS_FILTER_SCOPE;
28
+
29
+ //#endregion
30
+ exports.CUSTOM_FIELDS_FILTER_SCOPE = CUSTOM_FIELDS_FILTER_SCOPE;
31
+ exports.CustomFieldDefinitionType = CustomFieldDefinitionType;
32
+ exports.supportedEntities = supportedEntities;
@@ -0,0 +1,23 @@
1
+ import { customFields } from "@autofleet/common-types";
2
+
3
+ //#region src/utils/constants/index.d.ts
4
+ declare const supportedEntities: readonly ["businessModelId", "fleetId", "demandSourceId"];
5
+ /**
6
+ * Supported custom field types
7
+ */
8
+ declare enum CustomFieldDefinitionType {
9
+ NUMBER = "number",
10
+ BOOLEAN = "boolean",
11
+ DATE = "date",
12
+ DATETIME = "datetime",
13
+ TEXT = "text",
14
+ IMAGE = "image",
15
+ SELECT = "select",
16
+ STATUS = "status",
17
+ FILE = "file",
18
+ MULTI_SELECT_STATUS = "multi-select-status",
19
+ MULTI_SELECT_DROPDOWN = "multi-select-dropdown",
20
+ }
21
+ declare const CUSTOM_FIELDS_FILTER_SCOPE: typeof customFields.CUSTOM_FIELDS_FILTER_SCOPE;
22
+ //#endregion
23
+ export { CUSTOM_FIELDS_FILTER_SCOPE, CustomFieldDefinitionType, supportedEntities };