@directus/api 35.1.0 → 36.0.0-rc.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.
Files changed (192) hide show
  1. package/dist/ai/chat/models/chat-request.js +48 -48
  2. package/dist/ai/chat/models/object-request.js +6 -6
  3. package/dist/ai/chat/models/providers.js +14 -14
  4. package/dist/ai/chat/utils/parse-json-schema-7.js +22 -22
  5. package/dist/ai/mcp/server.js +44 -6
  6. package/dist/ai/mcp/utils.js +31 -0
  7. package/dist/ai/tools/assets/index.js +3 -3
  8. package/dist/ai/tools/collections/index.js +18 -18
  9. package/dist/ai/tools/fields/index.js +18 -18
  10. package/dist/ai/tools/files/index.js +18 -18
  11. package/dist/ai/tools/flows/index.js +16 -16
  12. package/dist/ai/tools/folders/index.js +18 -18
  13. package/dist/ai/tools/items/index.js +17 -17
  14. package/dist/ai/tools/operations/index.js +16 -16
  15. package/dist/ai/tools/relations/index.js +22 -22
  16. package/dist/ai/tools/schema/index.js +3 -3
  17. package/dist/ai/tools/schema.js +159 -159
  18. package/dist/ai/tools/system/index.js +3 -3
  19. package/dist/ai/tools/trigger-flow/index.js +3 -3
  20. package/dist/app.js +33 -9
  21. package/dist/auth/drivers/ldap.js +3 -1
  22. package/dist/auth/drivers/local.js +2 -0
  23. package/dist/auth/drivers/oauth2.js +3 -1
  24. package/dist/auth/drivers/openid.js +3 -1
  25. package/dist/auth/drivers/saml.js +2 -0
  26. package/dist/auth/utils/check-local-disabled.js +16 -0
  27. package/dist/auth/utils/check-sso-enabled.js +14 -0
  28. package/dist/auth.js +8 -5
  29. package/dist/cli/commands/bootstrap/index.js +3 -0
  30. package/dist/cli/commands/cache/clear.js +6 -1
  31. package/dist/cli/commands/roles/create.js +4 -1
  32. package/dist/cli/commands/users/create.js +3 -0
  33. package/dist/constants.js +8 -1
  34. package/dist/controllers/access.js +1 -1
  35. package/dist/controllers/activity.js +2 -1
  36. package/dist/controllers/assets.js +45 -1
  37. package/dist/controllers/auth.js +13 -5
  38. package/dist/controllers/collections.js +1 -1
  39. package/dist/controllers/comments.js +1 -1
  40. package/dist/controllers/dashboards.js +1 -1
  41. package/dist/controllers/fields.js +1 -1
  42. package/dist/controllers/files.js +3 -1
  43. package/dist/controllers/flows.js +6 -5
  44. package/dist/controllers/folders.js +1 -1
  45. package/dist/controllers/graphql.js +2 -0
  46. package/dist/controllers/items.js +3 -1
  47. package/dist/controllers/license.js +119 -0
  48. package/dist/controllers/mcp/index.js +38 -0
  49. package/dist/controllers/mcp/oauth-clients.js +68 -0
  50. package/dist/controllers/mcp/oauth-consent-page.js +316 -0
  51. package/dist/controllers/mcp/oauth.js +381 -0
  52. package/dist/controllers/mcp/templates/oauth-consent.liquid +62 -0
  53. package/dist/controllers/mcp/templates/oauth-error.liquid +28 -0
  54. package/dist/controllers/notifications.js +1 -1
  55. package/dist/controllers/operations.js +1 -1
  56. package/dist/controllers/panels.js +1 -1
  57. package/dist/controllers/permissions.js +1 -1
  58. package/dist/controllers/policies.js +1 -1
  59. package/dist/controllers/presets.js +1 -1
  60. package/dist/controllers/revisions.js +3 -2
  61. package/dist/controllers/roles.js +1 -1
  62. package/dist/controllers/schema.js +2 -2
  63. package/dist/controllers/server.js +38 -9
  64. package/dist/controllers/shares.js +1 -1
  65. package/dist/controllers/translations.js +1 -1
  66. package/dist/controllers/users.js +1 -1
  67. package/dist/controllers/utils.js +2 -2
  68. package/dist/controllers/versions.js +12 -5
  69. package/dist/database/get-ast-from-query/lib/convert-wildcards.js +10 -1
  70. package/dist/database/get-ast-from-query/lib/parse-fields.js +2 -1
  71. package/dist/database/helpers/fn/dialects/mysql.js +7 -12
  72. package/dist/database/helpers/fn/dialects/oracle.js +3 -4
  73. package/dist/database/helpers/fn/dialects/postgres.js +4 -26
  74. package/dist/database/helpers/fn/json/mysql-json-path.js +22 -0
  75. package/dist/database/helpers/fn/json/parse-function.js +14 -6
  76. package/dist/database/helpers/fn/json/postgres-json-path.js +54 -0
  77. package/dist/database/migrations/20260110A-add-ai-provider-settings.js +4 -4
  78. package/dist/database/migrations/20260217A-null-item-versions.js +14 -0
  79. package/dist/database/migrations/20260312A-add-ai-translation-settings.js +18 -0
  80. package/dist/database/migrations/20260507A-add-licensing.js +22 -0
  81. package/dist/database/migrations/20260512A-add-autosave-revision-interval.js +14 -0
  82. package/dist/database/migrations/20260512B-add-mcp-oauth.js +87 -0
  83. package/dist/database/run-ast/lib/apply-query/filter/operator.js +116 -33
  84. package/dist/database/run-ast/lib/apply-query/index.js +4 -1
  85. package/dist/database/run-ast/lib/apply-query/sort.js +17 -7
  86. package/dist/database/run-ast/lib/get-db-query.js +21 -9
  87. package/dist/database/run-ast/lib/parse-current-level.js +2 -1
  88. package/dist/database/run-ast/run-ast.js +2 -1
  89. package/dist/database/run-ast/utils/get-column.js +2 -1
  90. package/dist/extensions/lib/installation/manager.js +3 -3
  91. package/dist/extensions/lib/sandbox/register/operation.js +1 -1
  92. package/dist/extensions/lib/sync/sync.js +2 -2
  93. package/dist/extensions/manager.js +5 -5
  94. package/dist/flows.js +12 -10
  95. package/dist/license/entitlements/lib/collections.js +37 -0
  96. package/dist/license/entitlements/lib/custom-llms-enabled.js +18 -0
  97. package/dist/license/entitlements/lib/custom-permission-rules-enabled.js +41 -0
  98. package/dist/license/entitlements/lib/flows.js +29 -0
  99. package/dist/license/entitlements/lib/seats.js +103 -0
  100. package/dist/license/entitlements/lib/sso-enabled.js +45 -0
  101. package/dist/license/entitlements/manager.js +256 -0
  102. package/dist/license/index.js +4 -0
  103. package/dist/license/manager.js +505 -0
  104. package/dist/license/utils/compute-license-status.js +27 -0
  105. package/dist/license/utils/get-core-grace-expires-at.js +38 -0
  106. package/dist/license/utils/get-license-key.js +23 -0
  107. package/dist/license/utils/get-license-token.js +23 -0
  108. package/dist/license/utils/handle-license-error.js +41 -0
  109. package/dist/license/utils/is-in-core-grace-period.js +11 -0
  110. package/dist/license/utils/is-sso-bypass-allowed.js +21 -0
  111. package/dist/license/utils/use-rpc.js +33 -0
  112. package/dist/middleware/cache.js +4 -1
  113. package/dist/middleware/error-handler.js +11 -0
  114. package/dist/middleware/extract-token.js +11 -2
  115. package/dist/middleware/is-admin.js +16 -0
  116. package/dist/middleware/is-locked.js +16 -0
  117. package/dist/middleware/mcp-oauth-guard.js +23 -0
  118. package/dist/middleware/request-counter.js +5 -2
  119. package/dist/packages/types/dist/index.js +117 -122
  120. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +10 -1
  121. package/dist/permissions/utils/get-unaliased-field-key.js +2 -1
  122. package/dist/request/is-denied-ip.js +2 -0
  123. package/dist/schedules/license.js +31 -0
  124. package/dist/schedules/oauth-cleanup.js +26 -0
  125. package/dist/schedules/retention.js +1 -1
  126. package/dist/schedules/telemetry.js +4 -1
  127. package/dist/schedules/tus.js +1 -1
  128. package/dist/schedules/utils/duration-to-cron.js +36 -0
  129. package/dist/services/activity.js +15 -0
  130. package/dist/services/authentication.js +12 -5
  131. package/dist/services/collections.js +41 -10
  132. package/dist/services/fields.js +6 -6
  133. package/dist/services/flows.js +12 -0
  134. package/dist/services/graphql/resolvers/system-admin.js +2 -2
  135. package/dist/services/graphql/resolvers/system-global.js +1 -1
  136. package/dist/services/graphql/resolvers/system.js +34 -18
  137. package/dist/services/graphql/schema/get-types.js +23 -2
  138. package/dist/services/graphql/schema/parse-query.js +8 -0
  139. package/dist/services/graphql/schema/read.js +12 -0
  140. package/dist/services/graphql/types/json-filter.js +30 -0
  141. package/dist/services/index.js +6 -6
  142. package/dist/services/items.js +32 -14
  143. package/dist/services/mcp-oauth/cimd.js +307 -0
  144. package/dist/services/mcp-oauth/index.js +1185 -0
  145. package/dist/services/mcp-oauth/types/error.js +22 -0
  146. package/dist/services/mcp-oauth/utils/cimd-egress.js +182 -0
  147. package/dist/services/mcp-oauth/utils/domain.js +21 -0
  148. package/dist/services/mcp-oauth/utils/loopback.js +11 -0
  149. package/dist/services/mcp-oauth/utils/redirect.js +84 -0
  150. package/dist/services/mcp-oauth/utils/registration-debug.js +131 -0
  151. package/dist/services/payload.js +2 -1
  152. package/dist/services/permissions.js +31 -9
  153. package/dist/services/revisions.js +15 -0
  154. package/dist/services/schema.js +2 -2
  155. package/dist/services/server.js +21 -4
  156. package/dist/services/settings.js +37 -3
  157. package/dist/services/users.js +13 -6
  158. package/dist/services/utils.js +6 -1
  159. package/dist/services/versions.js +138 -69
  160. package/dist/utils/calculate-field-depth.js +1 -0
  161. package/dist/utils/deep-freeze.js +24 -0
  162. package/dist/utils/extract-function-name.js +13 -0
  163. package/dist/utils/generate-translations.js +5 -5
  164. package/dist/utils/get-accountability-for-token.js +13 -1
  165. package/dist/utils/get-cache-key.js +1 -1
  166. package/dist/utils/get-history-filter-query.js +22 -0
  167. package/dist/utils/get-schema.js +2 -2
  168. package/dist/utils/get-service.js +3 -3
  169. package/dist/utils/is-admin.js +9 -0
  170. package/dist/utils/parse-oauth-scope.js +12 -0
  171. package/dist/utils/sanitize-query.js +1 -1
  172. package/dist/utils/split-field-path.js +29 -0
  173. package/dist/utils/transaction.js +2 -2
  174. package/dist/utils/translations-validation.js +2 -2
  175. package/dist/utils/validate-diff.js +7 -3
  176. package/dist/utils/validate-query.js +35 -4
  177. package/dist/utils/validate-user-count-integrity.js +28 -5
  178. package/dist/utils/verify-session-jwt.js +5 -2
  179. package/dist/utils/versioning/handle-version.js +130 -48
  180. package/dist/utils/versioning/remove-circular.js +17 -0
  181. package/dist/websocket/authenticate.js +2 -1
  182. package/dist/websocket/collab/collab.js +1 -1
  183. package/dist/websocket/collab/room.js +1 -1
  184. package/dist/websocket/controllers/base.js +12 -0
  185. package/dist/websocket/controllers/graphql.js +1 -1
  186. package/dist/websocket/handlers/subscribe.js +1 -1
  187. package/dist/websocket/messages.js +64 -64
  188. package/dist/websocket/utils/items.js +2 -2
  189. package/license +90 -80
  190. package/package.json +33 -33
  191. package/dist/controllers/mcp.js +0 -31
  192. package/dist/utils/job-queue.js +0 -24
@@ -1,5 +1,7 @@
1
+ import { getOperation } from "../get-operation.js";
2
+ import { parseJsonPath } from "../../../../helpers/fn/json/parse-function.js";
1
3
  import { getColumn } from "../../../utils/get-column.js";
2
- import { getHelpers } from "../../../../helpers/index.js";
4
+ import { getFunctions, getHelpers } from "../../../../helpers/index.js";
3
5
  import { InvalidQueryError } from "@directus/errors";
4
6
  import { getOutputTypeForFunction } from "@directus/utils";
5
7
 
@@ -14,9 +16,51 @@ function castToNumber(value) {
14
16
  if (Number.isNaN(num)) throw new InvalidQueryError({ reason: `Invalid numeric value` });
15
17
  return num;
16
18
  }
19
+ /**
20
+ * Splits "table.column" into ["table", "column"], respecting dots inside parentheses.
21
+ * Handles keys like "table.json(col,path.with.dots)" correctly.
22
+ */
23
+ function splitTableColumn(key) {
24
+ let depth = 0;
25
+ for (let i = 0; i < key.length; i++) if (key[i] === "(") depth++;
26
+ else if (key[i] === ")") depth--;
27
+ else if (key[i] === "." && depth === 0) return [key.substring(0, i), key.substring(i + 1)];
28
+ return ["", key];
29
+ }
17
30
  function applyOperator(knex, dbQuery, schema, key, operator, compareValue, logical = "and", originalCollectionName) {
18
31
  const helpers = getHelpers(knex);
19
- const [table, column] = key.split(".");
32
+ const [table, column] = splitTableColumn(key);
33
+ if (operator === "_json") {
34
+ if (!Object.entries(compareValue).length) return;
35
+ const applyJsonConditions = (group, filterObj, innerLogical) => {
36
+ for (const [jsonPath, innerFilter] of Object.entries(filterObj)) {
37
+ if (jsonPath === "_or" || jsonPath === "_and") {
38
+ const subLogical = jsonPath === "_or" ? "or" : "and";
39
+ group[innerLogical].where((subGroup) => {
40
+ for (const subFilter of innerFilter) applyJsonConditions(subGroup, subFilter, subLogical);
41
+ });
42
+ continue;
43
+ }
44
+ const normalizedPath = parseJsonPath(jsonPath);
45
+ const innerValue = innerFilter[Object.keys(innerFilter)[0]];
46
+ const castNumeric = typeof innerValue === "number" || Array.isArray(innerValue) && innerValue.length > 0 && typeof innerValue[0] === "number";
47
+ const jsonExtractionRaw = getFunctions(knex, schema).json(table, column, {
48
+ type: "json",
49
+ jsonPath: normalizedPath,
50
+ originalCollectionName,
51
+ relationalCountOptions: void 0,
52
+ jsonReturnType: castNumeric ? "numeric" : "text"
53
+ });
54
+ const innerOp = getOperation(Object.keys(innerFilter)[0], Object.values(innerFilter)[0]);
55
+ if (!innerOp) continue;
56
+ applyOperatorToRaw(group, helpers, jsonExtractionRaw, innerOp.operator, innerOp.value, innerLogical);
57
+ }
58
+ };
59
+ dbQuery[logical].where((group) => {
60
+ applyJsonConditions(group, compareValue, "and");
61
+ });
62
+ return;
63
+ }
20
64
  const selectionRaw = getColumn(knex, table, column, false, schema, { originalCollectionName });
21
65
  if (operator === "_null" && compareValue !== false || operator === "_nnull" && compareValue === false || operator === "_eq" && compareValue === null) {
22
66
  dbQuery[logical].whereNull(selectionRaw);
@@ -26,12 +70,18 @@ function applyOperator(knex, dbQuery, schema, key, operator, compareValue, logic
26
70
  dbQuery[logical].whereNotNull(selectionRaw);
27
71
  return;
28
72
  }
29
- if (operator === "_empty" && compareValue !== false || operator === "_nempty" && compareValue === false) dbQuery[logical].andWhere((query) => {
30
- query.whereNull(key).orWhere(key, "=", "");
31
- });
32
- if (operator === "_nempty" && compareValue !== false || operator === "_empty" && compareValue === false) dbQuery[logical].andWhere((query) => {
33
- query.whereNotNull(key).andWhere(key, "!=", "");
34
- });
73
+ if (operator === "_empty" && compareValue !== false || operator === "_nempty" && compareValue === false) {
74
+ dbQuery[logical].andWhere((query) => {
75
+ query.whereNull(selectionRaw).orWhere(selectionRaw, "=", "");
76
+ });
77
+ return;
78
+ }
79
+ if (operator === "_nempty" && compareValue !== false || operator === "_empty" && compareValue === false) {
80
+ dbQuery[logical].andWhere((query) => {
81
+ query.whereNotNull(selectionRaw).andWhere(selectionRaw, "!=", "");
82
+ });
83
+ return;
84
+ }
35
85
  if (compareValue === void 0) return;
36
86
  if (Array.isArray(compareValue)) compareValue = compareValue.filter((val) => val !== void 0);
37
87
  if (column.includes("(") && column.includes(")")) {
@@ -43,7 +93,7 @@ function applyOperator(knex, dbQuery, schema, key, operator, compareValue, logic
43
93
  "decimal"
44
94
  ].includes(type)) compareValue = castToNumber(compareValue);
45
95
  }
46
- const [collection, field] = key.split(".");
96
+ const [collection, field] = splitTableColumn(key);
47
97
  const mappedCollection = originalCollectionName || collection;
48
98
  if (mappedCollection in schema.collections && field in schema.collections[mappedCollection].fields) {
49
99
  const type = schema.collections[mappedCollection].fields[field].type;
@@ -60,47 +110,80 @@ function applyOperator(knex, dbQuery, schema, key, operator, compareValue, logic
60
110
  "decimal"
61
111
  ].includes(type)) compareValue = castToNumber(compareValue);
62
112
  }
63
- if (operator === "_eq") dbQuery[logical].where(selectionRaw, "=", compareValue);
64
- if (operator === "_neq") dbQuery[logical].whereNot(selectionRaw, compareValue);
65
- if (operator === "_ieq") dbQuery[logical].whereRaw(`LOWER(??) = ?`, [selectionRaw, `${compareValue.toLowerCase()}`]);
66
- if (operator === "_nieq") dbQuery[logical].whereRaw(`LOWER(??) <> ?`, [selectionRaw, `${compareValue.toLowerCase()}`]);
67
- if (operator === "_contains") dbQuery[logical].where(selectionRaw, "like", `%${compareValue}%`);
68
- if (operator === "_ncontains") dbQuery[logical].whereNot(selectionRaw, "like", `%${compareValue}%`);
69
- if (operator === "_icontains") dbQuery[logical].whereRaw(`LOWER(??) LIKE ?`, [selectionRaw, `%${compareValue.toLowerCase()}%`]);
70
- if (operator === "_nicontains") dbQuery[logical].whereRaw(`LOWER(??) NOT LIKE ?`, [selectionRaw, `%${compareValue.toLowerCase()}%`]);
71
- if (operator === "_starts_with") dbQuery[logical].where(key, "like", `${compareValue}%`);
72
- if (operator === "_nstarts_with") dbQuery[logical].whereNot(key, "like", `${compareValue}%`);
73
- if (operator === "_istarts_with") dbQuery[logical].whereRaw(`LOWER(??) LIKE ?`, [selectionRaw, `${compareValue.toLowerCase()}%`]);
74
- if (operator === "_nistarts_with") dbQuery[logical].whereRaw(`LOWER(??) NOT LIKE ?`, [selectionRaw, `${compareValue.toLowerCase()}%`]);
75
- if (operator === "_ends_with") dbQuery[logical].where(key, "like", `%${compareValue}`);
76
- if (operator === "_nends_with") dbQuery[logical].whereNot(key, "like", `%${compareValue}`);
77
- if (operator === "_iends_with") dbQuery[logical].whereRaw(`LOWER(??) LIKE ?`, [selectionRaw, `%${compareValue.toLowerCase()}`]);
78
- if (operator === "_niends_with") dbQuery[logical].whereRaw(`LOWER(??) NOT LIKE ?`, [selectionRaw, `%${compareValue.toLowerCase()}`]);
79
- if (operator === "_gt") dbQuery[logical].where(selectionRaw, ">", compareValue);
80
- if (operator === "_gte") dbQuery[logical].where(selectionRaw, ">=", compareValue);
81
- if (operator === "_lt") dbQuery[logical].where(selectionRaw, "<", compareValue);
82
- if (operator === "_lte") dbQuery[logical].where(selectionRaw, "<=", compareValue);
113
+ applyOperatorToRaw(dbQuery, helpers, selectionRaw, operator, compareValue, logical, key);
114
+ }
115
+ function applyOperatorToRaw(dbQuery, helpers, raw, operator, compareValue, logical, key) {
116
+ if (operator === "_null" && compareValue !== false || operator === "_nnull" && compareValue === false || operator === "_eq" && compareValue === null) {
117
+ dbQuery[logical].whereNull(raw);
118
+ return;
119
+ }
120
+ if (operator === "_nnull" && compareValue !== false || operator === "_null" && compareValue === false || operator === "_neq" && compareValue === null) {
121
+ dbQuery[logical].whereNotNull(raw);
122
+ return;
123
+ }
124
+ if (operator === "_empty" && compareValue !== false || operator === "_nempty" && compareValue === false) {
125
+ dbQuery[logical].andWhere((query) => {
126
+ query.whereNull(raw).orWhere(raw, "=", "");
127
+ });
128
+ return;
129
+ }
130
+ if (operator === "_nempty" && compareValue !== false || operator === "_empty" && compareValue === false) {
131
+ dbQuery[logical].andWhere((query) => {
132
+ query.whereNotNull(raw).andWhere(raw, "!=", "");
133
+ });
134
+ return;
135
+ }
136
+ if (compareValue === void 0) return;
137
+ if (Array.isArray(compareValue)) compareValue = compareValue.filter((val) => val !== void 0);
138
+ if (operator === "_eq") dbQuery[logical].where(raw, "=", compareValue);
139
+ if (operator === "_neq") dbQuery[logical].whereNot(raw, compareValue);
140
+ if (operator === "_ieq") dbQuery[logical].whereRaw(`LOWER(??) = ?`, [raw, `${compareValue.toLowerCase()}`]);
141
+ if (operator === "_nieq") dbQuery[logical].whereRaw(`LOWER(??) <> ?`, [raw, `${compareValue.toLowerCase()}`]);
142
+ if (operator === "_contains") dbQuery[logical].where(raw, "like", `%${compareValue}%`);
143
+ if (operator === "_ncontains") dbQuery[logical].whereNot(raw, "like", `%${compareValue}%`);
144
+ if (operator === "_icontains") dbQuery[logical].whereRaw(`LOWER(??) LIKE ?`, [raw, `%${compareValue.toLowerCase()}%`]);
145
+ if (operator === "_nicontains") dbQuery[logical].whereRaw(`LOWER(??) NOT LIKE ?`, [raw, `%${compareValue.toLowerCase()}%`]);
146
+ if (operator === "_starts_with") dbQuery[logical].where(raw, "like", `${compareValue}%`);
147
+ if (operator === "_nstarts_with") dbQuery[logical].whereNot(raw, "like", `${compareValue}%`);
148
+ if (operator === "_istarts_with") dbQuery[logical].whereRaw(`LOWER(??) LIKE ?`, [raw, `${compareValue.toLowerCase()}%`]);
149
+ if (operator === "_nistarts_with") dbQuery[logical].whereRaw(`LOWER(??) NOT LIKE ?`, [raw, `${compareValue.toLowerCase()}%`]);
150
+ if (operator === "_ends_with") dbQuery[logical].where(raw, "like", `%${compareValue}`);
151
+ if (operator === "_nends_with") dbQuery[logical].whereNot(raw, "like", `%${compareValue}`);
152
+ if (operator === "_iends_with") dbQuery[logical].whereRaw(`LOWER(??) LIKE ?`, [raw, `%${compareValue.toLowerCase()}`]);
153
+ if (operator === "_niends_with") dbQuery[logical].whereRaw(`LOWER(??) NOT LIKE ?`, [raw, `%${compareValue.toLowerCase()}`]);
154
+ if (operator === "_gt") dbQuery[logical].where(raw, ">", compareValue);
155
+ if (operator === "_gte") dbQuery[logical].where(raw, ">=", compareValue);
156
+ if (operator === "_lt") dbQuery[logical].where(raw, "<", compareValue);
157
+ if (operator === "_lte") dbQuery[logical].where(raw, "<=", compareValue);
83
158
  if (operator === "_in") {
84
159
  let value = compareValue;
85
160
  if (typeof value === "string") value = value.split(",");
86
- dbQuery[logical].whereIn(selectionRaw, value);
161
+ if (value.length === 0) dbQuery[logical].whereIn(raw, []);
162
+ else {
163
+ const placeholders = value.map(() => "?").join(", ");
164
+ dbQuery[logical].whereRaw(`?? in (${placeholders})`, [raw, ...value]);
165
+ }
87
166
  }
88
167
  if (operator === "_nin") {
89
168
  let value = compareValue;
90
169
  if (typeof value === "string") value = value.split(",");
91
- dbQuery[logical].whereNotIn(selectionRaw, value);
170
+ if (value.length === 0) dbQuery[logical].whereNotIn(raw, []);
171
+ else {
172
+ const placeholders = value.map(() => "?").join(", ");
173
+ dbQuery[logical].whereRaw(`?? not in (${placeholders})`, [raw, ...value]);
174
+ }
92
175
  }
93
176
  if (operator === "_between") {
94
177
  let value = compareValue;
95
178
  if (typeof value === "string") value = value.split(",");
96
179
  if (value.length !== 2) return;
97
- dbQuery[logical].whereBetween(selectionRaw, value);
180
+ dbQuery[logical].whereBetween(raw, value);
98
181
  }
99
182
  if (operator === "_nbetween") {
100
183
  let value = compareValue;
101
184
  if (typeof value === "string") value = value.split(",");
102
185
  if (value.length !== 2) return;
103
- dbQuery[logical].whereNotBetween(selectionRaw, value);
186
+ dbQuery[logical].whereNotBetween(raw, value);
104
187
  }
105
188
  if (operator == "_intersects") dbQuery[logical].whereRaw(helpers.st.intersects(key, compareValue));
106
189
  if (operator == "_nintersects") dbQuery[logical].whereRaw(helpers.st.nintersects(key, compareValue));
@@ -20,7 +20,10 @@ function applyQuery(knex, collection, dbQuery, query, schema, cases, permissions
20
20
  if (query.offset) applyOffset(knex, dbQuery, query.offset);
21
21
  if (query.page && query.limit && query.limit !== -1) applyOffset(knex, dbQuery, query.limit * (query.page - 1));
22
22
  if (query.sort && !options?.isInnerQuery && !options?.hasMultiRelationalSort) {
23
- const sortResult = applySort(knex, schema, dbQuery, query.sort, query.aggregate, collection, aliasMap);
23
+ const sortResult = applySort(knex, schema, dbQuery, query.sort, collection, aliasMap, {
24
+ aggregate: query.aggregate,
25
+ fieldAliasMap: { ...query.alias ?? {} }
26
+ });
24
27
  if (!hasJoins) hasJoins = sortResult.hasJoins;
25
28
  }
26
29
  const filter = joinFilterWithCases(query.filter, cases);
@@ -1,15 +1,18 @@
1
1
  import { getColumnPath } from "../../../../utils/get-column-path.js";
2
2
  import { addJoin } from "./add-join.js";
3
3
  import { getColumn } from "../../utils/get-column.js";
4
+ import { extractFunctionName } from "../../../../utils/extract-function-name.js";
5
+ import { splitFieldPath } from "../../../../utils/split-field-path.js";
4
6
  import { getRelationInfo } from "@directus/utils";
5
7
 
6
8
  //#region src/database/run-ast/lib/apply-query/sort.ts
7
- function applySort(knex, schema, rootQuery, sort, aggregate, collection, aliasMap, returnRecords = false) {
9
+ function applySort(knex, schema, rootQuery, sort, collection, aliasMap, options) {
10
+ const { aggregate, returnRecords = false, fieldAliasMap } = options ?? {};
8
11
  const relations = schema.relations;
9
12
  let hasJoins = false;
10
13
  let hasMultiRelationalSort = false;
11
14
  const sortRecords = sort.map((sortField) => {
12
- const column = sortField.split(".");
15
+ const column = splitFieldPath(sortField);
13
16
  let order = "asc";
14
17
  if (sortField.startsWith("-")) order = "desc";
15
18
  if (column[0].startsWith("-")) column[0] = column[0].substring(1);
@@ -30,11 +33,17 @@ function applySort(knex, schema, rootQuery, sort, aggregate, collection, aliasMa
30
33
  };
31
34
  }
32
35
  if (column.length === 1) {
33
- const pathRoot = column[0].split(":")[0];
36
+ const rawField = column[0];
37
+ const resolvedField = fieldAliasMap?.[rawField] ?? rawField;
38
+ if (extractFunctionName(resolvedField) === "json") return {
39
+ order,
40
+ column: returnRecords ? resolvedField : getColumn(knex, collection, resolvedField, false, schema, { jsonReturnType: "text" })
41
+ };
42
+ const pathRoot = resolvedField.split(":")[0];
34
43
  const { relation, relationType } = getRelationInfo(relations, collection, pathRoot);
35
44
  if (!relation || ["m2o", "a2o"].includes(relationType ?? "")) return {
36
45
  order,
37
- column: returnRecords ? column[0] : getColumn(knex, collection, column[0], false, schema)
46
+ column: returnRecords ? resolvedField : getColumn(knex, collection, resolvedField, false, schema)
38
47
  };
39
48
  }
40
49
  const { hasMultiRelational, isJoinAdded } = addJoin({
@@ -45,19 +54,20 @@ function applySort(knex, schema, rootQuery, sort, aggregate, collection, aliasMa
45
54
  schema,
46
55
  knex
47
56
  });
48
- const { columnPath } = getColumnPath({
57
+ const { columnPath, targetCollection } = getColumnPath({
49
58
  path: column,
50
59
  collection,
51
60
  aliasMap,
52
61
  relations,
53
62
  schema
54
63
  });
55
- const [alias, field] = columnPath.split(".");
64
+ const [alias, ...rest] = splitFieldPath(columnPath);
65
+ const field = rest.join(".");
56
66
  if (!hasJoins) hasJoins = isJoinAdded;
57
67
  if (!hasMultiRelationalSort) hasMultiRelationalSort = hasMultiRelational;
58
68
  return {
59
69
  order,
60
- column: returnRecords ? columnPath : getColumn(knex, alias, field, false, schema)
70
+ column: returnRecords ? columnPath : getColumn(knex, alias, field, false, schema, { originalCollectionName: targetCollection })
61
71
  };
62
72
  });
63
73
  if (returnRecords) return {
@@ -1,7 +1,10 @@
1
1
  import { generateQueryAlias } from "../utils/generate-alias.js";
2
2
  import { applyCaseWhen } from "../utils/apply-case-when.js";
3
+ import { applyFunctionToColumnName } from "../utils/apply-function-to-column-name.js";
3
4
  import { getColumn } from "../utils/get-column.js";
4
5
  import { applyLimit } from "./apply-query/pagination.js";
6
+ import { extractFunctionName } from "../../../utils/extract-function-name.js";
7
+ import { splitFieldPath } from "../../../utils/split-field-path.js";
5
8
  import { applySort } from "./apply-query/sort.js";
6
9
  import applyQuery from "./apply-query/index.js";
7
10
  import { getHelpers } from "../../helpers/index.js";
@@ -43,7 +46,13 @@ function getDBQuery({ table, fieldNodes, o2mNodes, query, cases, permissions, pe
43
46
  const innerQuerySortRecords = [];
44
47
  let hasMultiRelationalSort;
45
48
  if (queryCopy.sort) {
46
- const sortResult = applySort(knex, schema, dbQuery, queryCopy.sort, queryCopy.aggregate, table, aliasMap, true);
49
+ const fieldAliasMap = { ...queryCopy.alias ?? {} };
50
+ for (const node of fieldNodes) if (node.type === "functionField" && extractFunctionName(node.name) === "json") fieldAliasMap[applyFunctionToColumnName(node.fieldKey)] = node.name;
51
+ const sortResult = applySort(knex, schema, dbQuery, queryCopy.sort, table, aliasMap, {
52
+ aggregate: queryCopy.aggregate,
53
+ returnRecords: true,
54
+ fieldAliasMap
55
+ });
47
56
  if (sortResult) {
48
57
  sortRecords = sortResult.sortRecords;
49
58
  hasMultiRelationalSort = sortResult.hasMultiRelationalSort;
@@ -85,16 +94,18 @@ function getDBQuery({ table, fieldNodes, o2mNodes, query, cases, permissions, pe
85
94
  if (orderByString.length !== 0) orderByString += ", ";
86
95
  const sortAlias = generateQueryAlias(table, queryCopy, `sort_${index}_${sortRecord.column}_${sortRecord.order}`);
87
96
  let orderByColumn;
88
- if (sortRecord.column.includes(".")) {
89
- const [alias, field] = sortRecord.column.split(".");
97
+ const colParts = splitFieldPath(sortRecord.column);
98
+ if (colParts.length > 1) {
99
+ const [alias, ...rest] = colParts;
100
+ const field = rest.join(".");
90
101
  const originalCollectionName = getCollectionFromAlias(alias, aliasMap);
91
102
  dbQuery.select(getColumn(knex, alias, field, sortAlias, schema, { originalCollectionName }));
92
103
  orderByString += `?? ${sortRecord.order}`;
93
104
  orderByColumn = getColumn(knex, alias, field, false, schema, { originalCollectionName });
94
105
  } else {
95
- dbQuery.select(getColumn(knex, table, sortRecord.column, sortAlias, schema));
106
+ dbQuery.select(getColumn(knex, table, sortRecord.column, sortAlias, schema, { jsonReturnType: "text" }));
96
107
  orderByString += `?? ${sortRecord.order}`;
97
- orderByColumn = getColumn(knex, table, sortRecord.column, false, schema);
108
+ orderByColumn = getColumn(knex, table, sortRecord.column, false, schema, { jsonReturnType: "text" });
98
109
  }
99
110
  orderByFields.push(orderByColumn);
100
111
  innerQuerySortRecords.push({
@@ -111,10 +122,11 @@ function getDBQuery({ table, fieldNodes, o2mNodes, query, cases, permissions, pe
111
122
  dbQuery.orderByRaw(orderByString, orderByFields);
112
123
  } else {
113
124
  sortRecords.map((sortRecord) => {
114
- if (sortRecord.column.includes(".")) {
115
- const [alias, field] = sortRecord.column.split(".");
116
- sortRecord.column = getColumn(knex, alias, field, false, schema, { originalCollectionName: getCollectionFromAlias(alias, aliasMap) });
117
- } else sortRecord.column = getColumn(knex, table, sortRecord.column, false, schema);
125
+ const colParts = splitFieldPath(sortRecord.column);
126
+ if (colParts.length > 1) {
127
+ const [alias, ...rest] = colParts;
128
+ sortRecord.column = getColumn(knex, alias, rest.join("."), false, schema, { originalCollectionName: getCollectionFromAlias(alias, aliasMap) });
129
+ } else sortRecord.column = getColumn(knex, table, sortRecord.column, false, schema, { jsonReturnType: "text" });
118
130
  });
119
131
  dbQuery.orderBy(sortRecords);
120
132
  }
@@ -1,4 +1,5 @@
1
1
  import { parseJsonFunction } from "../../helpers/fn/json/parse-function.js";
2
+ import { extractFunctionName } from "../../../utils/extract-function-name.js";
2
3
  import { parseFilterKey } from "../../../utils/parse-filter-key.js";
3
4
 
4
5
  //#region src/database/run-ast/lib/parse-current-level.ts
@@ -10,7 +11,7 @@ async function parseCurrentLevel(schema, collection, children, query) {
10
11
  for (const child of children) {
11
12
  if (child.type === "field" || child.type === "functionField") {
12
13
  let fieldName;
13
- if (child.type == "functionField" && child.name.startsWith("json")) fieldName = parseJsonFunction(child.name).field;
14
+ if (child.type === "functionField" && extractFunctionName(child.name) === "json") fieldName = parseJsonFunction(child.name).field;
14
15
  else fieldName = parseFilterKey(child.name).fieldName;
15
16
  if (columnsInCollection.includes(fieldName)) columnsToSelectInternal.push(child.fieldKey);
16
17
  continue;
@@ -1,4 +1,5 @@
1
1
  import { applyFunctionToColumnName } from "./utils/apply-function-to-column-name.js";
2
+ import { extractFunctionName } from "../../utils/extract-function-name.js";
2
3
  import database_default from "../index.js";
3
4
  import { fetchPolicies } from "../../permissions/lib/fetch-policies.js";
4
5
  import { fetchPermissions } from "../../permissions/lib/fetch-permissions.js";
@@ -57,7 +58,7 @@ async function runAst(originalAST, schema, accountability, options) {
57
58
  schema
58
59
  });
59
60
  const aliasMap = { ...query.alias };
60
- for (const child of children) if (child.type === "functionField" && child.name.startsWith("json(")) {
61
+ for (const child of children) if (child.type === "functionField" && extractFunctionName(child.name) === "json") {
61
62
  const alias = applyFunctionToColumnName(child.fieldKey);
62
63
  aliasMap[alias] = child.name;
63
64
  }
@@ -42,7 +42,8 @@ function getColumn(knex, table, column, alias = applyFunctionToColumnName(column
42
42
  permissions: options.permissions
43
43
  } : void 0,
44
44
  originalCollectionName: options?.originalCollectionName,
45
- jsonPath
45
+ jsonPath,
46
+ ...options?.jsonReturnType && { jsonReturnType: options.jsonReturnType }
46
47
  });
47
48
  if (alias) return knex.raw(result + " AS ??", [alias]);
48
49
  return result;
@@ -10,7 +10,7 @@ import { Readable } from "node:stream";
10
10
  import { download } from "@directus/extensions-registry";
11
11
  import { EXTENSION_PKG_KEY, ExtensionManifest } from "@directus/extensions";
12
12
  import DriverLocal from "@directus/storage-driver-local";
13
- import Queue from "p-queue";
13
+ import PQueue from "p-queue";
14
14
  import { extract } from "tar";
15
15
 
16
16
  //#region src/extensions/lib/installation/manager.ts
@@ -50,7 +50,7 @@ var InstallationManager = class {
50
50
  if (!(await ExtensionManifest.parseAsync(packageFile))[EXTENSION_PKG_KEY]?.type) throw new Error(`Extension type not found in package.json`);
51
51
  if (env["EXTENSIONS_LOCATION"]) {
52
52
  const remoteDisk = (await getStorage()).location(env["EXTENSIONS_LOCATION"]);
53
- const queue = new Queue({ concurrency: 1e3 });
53
+ const queue = new PQueue({ concurrency: 1e3 });
54
54
  for await (const filepath of tmpStorage.list(extractedPath)) {
55
55
  const readStream = await tmpStorage.read(filepath);
56
56
  const remotePath = join(env["EXTENSIONS_PATH"], ".registry", versionId, filepath.substring(7));
@@ -74,7 +74,7 @@ var InstallationManager = class {
74
74
  async uninstall(folder) {
75
75
  if (env["EXTENSIONS_LOCATION"]) {
76
76
  const remoteDisk = (await getStorage()).location(env["EXTENSIONS_LOCATION"]);
77
- const queue = new Queue({ concurrency: 1e3 });
77
+ const queue = new PQueue({ concurrency: 1e3 });
78
78
  const prefix = join(env["EXTENSIONS_PATH"], ".registry", folder);
79
79
  for await (const filepath of remoteDisk.list(prefix)) queue.add(() => remoteDisk.delete(filepath));
80
80
  await queue.onIdle();
@@ -1,5 +1,5 @@
1
- import { callReference } from "./call-reference.js";
2
1
  import { getFlowManager } from "../../../../flows.js";
2
+ import { callReference } from "./call-reference.js";
3
3
 
4
4
  //#region src/extensions/lib/sandbox/register/operation.ts
5
5
  function registerOperationGenerator() {
@@ -14,7 +14,7 @@ import { normalizePath } from "@directus/utils";
14
14
  import { dirname, join, relative, resolve, sep } from "node:path";
15
15
  import { pipeline } from "node:stream/promises";
16
16
  import { createWriteStream } from "node:fs";
17
- import Queue from "p-queue";
17
+ import PQueue from "p-queue";
18
18
  import mid from "node-machine-id";
19
19
 
20
20
  //#region src/extensions/lib/sync/sync.ts
@@ -50,7 +50,7 @@ async function syncExtensions(options) {
50
50
  return;
51
51
  }
52
52
  }
53
- const queue = new Queue({ concurrency: 1e3 });
53
+ const queue = new PQueue({ concurrency: 1e3 });
54
54
  const fileTracker = new SyncFileTracker();
55
55
  const hasLocalFiles = await fileTracker.readLocalFiles(localExtensionsPath) > 0;
56
56
  for await (const filepath of disk.list(remoteExtensionsPath)) {
@@ -4,12 +4,12 @@ import { useLogger } from "../logger/index.js";
4
4
  import { getExtensionsPath } from "./lib/get-extensions-path.js";
5
5
  import database_default from "../database/index.js";
6
6
  import emitter_default, { Emitter } from "../emitter.js";
7
+ import { scheduleSynchronizedJob, validateCron } from "../utils/schedule.js";
7
8
  import { getSchema } from "../utils/get-schema.js";
9
+ import { getFlowManager } from "../flows.js";
8
10
  import { deleteFromRequireCache } from "../utils/delete-from-require-cache.js";
9
11
  import getModuleDefault from "../utils/get-module-default.js";
10
12
  import { importFileUrl } from "../utils/import-file-url.js";
11
- import { JobQueue } from "../utils/job-queue.js";
12
- import { scheduleSynchronizedJob, validateCron } from "../utils/schedule.js";
13
13
  import { getExtensionsSettings } from "./lib/get-extensions-settings.js";
14
14
  import { getExtensions } from "./lib/get-extensions.js";
15
15
  import { getSharedDepsMapping } from "./lib/get-shared-deps-mapping.js";
@@ -19,7 +19,6 @@ import { instantiateSandboxSdk } from "./lib/sandbox/sdk/instantiate.js";
19
19
  import { syncExtensions } from "./lib/sync/sync.js";
20
20
  import { wrapEmbeds } from "./lib/wrap-embeds.js";
21
21
  import { services_exports } from "../services/index.js";
22
- import { getFlowManager } from "../flows.js";
23
22
  import { readFile, readdir } from "node:fs/promises";
24
23
  import path from "path";
25
24
  import { useEnv } from "@directus/env";
@@ -39,6 +38,7 @@ import nodeResolveDefault from "@rollup/plugin-node-resolve";
39
38
  import virtualDefault from "@rollup/plugin-virtual";
40
39
  import chokidar from "chokidar";
41
40
  import ivm from "isolated-vm";
41
+ import PQueue from "p-queue";
42
42
  import { rolldown } from "rolldown";
43
43
  import { rollup } from "rollup";
44
44
 
@@ -97,7 +97,7 @@ var ExtensionManager = class {
97
97
  * Used to prevent race conditions when reloading extensions. Forces each reload to happen in
98
98
  * sequence.
99
99
  */
100
- reloadQueue = new JobQueue();
100
+ reloadQueue = new PQueue({ concurrency: 1 });
101
101
  /**
102
102
  * Used to prevent race condition when reading extension data while reloading extensions
103
103
  */
@@ -248,7 +248,7 @@ var ExtensionManager = class {
248
248
  resolve$1 = res;
249
249
  reject = rej;
250
250
  });
251
- this.reloadQueue.enqueue(async () => {
251
+ this.reloadQueue.add(async () => {
252
252
  if (this.isLoaded) {
253
253
  const prevExtensions = clone(this.extensions);
254
254
  await this.unload();
package/dist/flows.js CHANGED
@@ -5,22 +5,22 @@ import database_default from "./database/index.js";
5
5
  import { fetchPolicies } from "./permissions/lib/fetch-policies.js";
6
6
  import { fetchPermissions } from "./permissions/lib/fetch-permissions.js";
7
7
  import emitter_default from "./emitter.js";
8
- import { getSchema } from "./utils/get-schema.js";
8
+ import { scheduleSynchronizedJob, validateCron } from "./utils/schedule.js";
9
9
  import { ActivityService } from "./services/activity.js";
10
10
  import { getService } from "./utils/get-service.js";
11
- import { JobQueue } from "./utils/job-queue.js";
12
- import { scheduleSynchronizedJob, validateCron } from "./utils/schedule.js";
11
+ import { getSchema } from "./utils/get-schema.js";
12
+ import { FlowsService } from "./services/flows.js";
13
13
  import { RevisionsService } from "./services/revisions.js";
14
- import { services_exports } from "./services/index.js";
15
14
  import { constructFlowTree } from "./utils/construct-flow-tree.js";
16
15
  import { redactObject } from "./utils/redact-object.js";
17
- import { FlowsService } from "./services/flows.js";
16
+ import { services_exports } from "./services/index.js";
18
17
  import { useEnv } from "@directus/env";
19
18
  import { ForbiddenError } from "@directus/errors";
20
19
  import { applyOptionsData, deepMap, getRedactedString, isValidJSON, parseJSON, toArray } from "@directus/utils";
21
20
  import { pick } from "lodash-es";
22
21
  import { Action } from "@directus/constants";
23
22
  import { isSystemCollection } from "@directus/system-data";
23
+ import PQueue from "p-queue";
24
24
  import { get as get$1 } from "micromustache";
25
25
 
26
26
  //#region src/flows.ts
@@ -41,15 +41,15 @@ var FlowManager = class {
41
41
  triggerHandlers = [];
42
42
  operationFlowHandlers = {};
43
43
  webhookFlowHandlers = {};
44
- reloadQueue;
44
+ reloadQueue = new PQueue({ concurrency: 1 });
45
45
  envs;
46
46
  constructor() {
47
47
  const env = useEnv();
48
- const logger = useLogger();
49
- this.reloadQueue = new JobQueue();
50
48
  this.envs = env["FLOWS_ENV_ALLOW_LIST"] ? pick(env, toArray(env["FLOWS_ENV_ALLOW_LIST"])) : {};
51
- useBus().subscribe("flows", (event) => {
52
- if (event["type"] === "reload") this.reloadQueue.enqueue(async () => {
49
+ const messenger = useBus();
50
+ const logger = useLogger();
51
+ messenger.subscribe("flows", (event) => {
52
+ if (event.type === "reload") this.reloadQueue.add(async () => {
53
53
  if (this.isLoaded) {
54
54
  await this.unload();
55
55
  await this.load();
@@ -70,6 +70,7 @@ var FlowManager = class {
70
70
  this.operations.delete(id);
71
71
  }
72
72
  async runOperationFlow(id, data, context) {
73
+ if (this.reloadQueue.pending > 0) await this.reloadQueue.onIdle();
73
74
  const logger = useLogger();
74
75
  if (!(id in this.operationFlowHandlers)) {
75
76
  logger.warn(`Couldn't find operation triggered flow with id "${id}"`);
@@ -79,6 +80,7 @@ var FlowManager = class {
79
80
  return handler(data, context);
80
81
  }
81
82
  async runWebhookFlow(id, data, context) {
83
+ if (this.reloadQueue.pending > 0) await this.reloadQueue.onIdle();
82
84
  const logger = useLogger();
83
85
  if (!(id in this.webhookFlowHandlers)) {
84
86
  logger.warn(`Couldn't find webhook or manual triggered flow with id "${id}"`);
@@ -0,0 +1,37 @@
1
+ import database_default from "../../../database/index.js";
2
+ import { getSchema } from "../../../utils/get-schema.js";
3
+ import "../../../services/index.js";
4
+ import { CollectionsService } from "../../../services/collections.js";
5
+ import { useEnv } from "@directus/env";
6
+ import { isSystemCollection } from "@directus/system-data";
7
+
8
+ //#region src/license/entitlements/lib/collections.ts
9
+ async function getActiveCollections(opts) {
10
+ const env = useEnv();
11
+ const knex = opts?.knex ?? database_default();
12
+ return (await new CollectionsService({
13
+ schema: await getSchema({ database: knex }),
14
+ knex
15
+ }).readByQuery()).filter((collection) => {
16
+ const isFolder = collection.schema === null;
17
+ const isDBOnly = collection.meta === null;
18
+ const isDisabled = collection.meta?.status !== "active";
19
+ const isEnvExcluded = env["DB_EXCLUDE_TABLES"].includes(collection.collection);
20
+ return !isFolder && !isSystemCollection(collection.collection) && !isDBOnly && !isDisabled && !isEnvExcluded;
21
+ }).map((collection) => collection.collection);
22
+ }
23
+ async function countActiveCollections(opts) {
24
+ return (await getActiveCollections(opts)).length;
25
+ }
26
+ async function resolveCollections(collections, ctx) {
27
+ const collectionsService = new CollectionsService({
28
+ schema: await getSchema(),
29
+ accountability: ctx?.accountability
30
+ });
31
+ await Promise.allSettled(collections.map((collection) => {
32
+ return collectionsService.updateOne(collection, { meta: { status: "inactive" } });
33
+ }));
34
+ }
35
+
36
+ //#endregion
37
+ export { countActiveCollections, getActiveCollections, resolveCollections };
@@ -0,0 +1,18 @@
1
+ import { CUSTOM_LLM_FIELDS } from "../../../constants.js";
2
+ import database_default from "../../../database/index.js";
3
+ import { getSchema } from "../../../utils/get-schema.js";
4
+ import { SettingsService } from "../../../services/settings.js";
5
+ import "../../../services/index.js";
6
+
7
+ //#region src/license/entitlements/lib/custom-llms-enabled.ts
8
+ async function checkCustomLLM(opts) {
9
+ const knex = opts?.knex ?? database_default();
10
+ const data = await new SettingsService({
11
+ schema: await getSchema({ database: knex }),
12
+ knex
13
+ }).readSingleton({ fields: [...CUSTOM_LLM_FIELDS] });
14
+ return !CUSTOM_LLM_FIELDS.find((key) => data[key] !== null);
15
+ }
16
+
17
+ //#endregion
18
+ export { checkCustomLLM };