@dyrected/core 2.10.0 → 2.10.1

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/dist/index.cjs CHANGED
@@ -2300,8 +2300,10 @@ var index_exports = {};
2300
2300
  __export(index_exports, {
2301
2301
  BUILTIN_JEXL_HELPERS: () => BUILTIN_JEXL_HELPERS,
2302
2302
  ConfigValidationError: () => ConfigValidationError,
2303
+ FieldConditionBuilder: () => FieldConditionBuilder,
2303
2304
  LIFECYCLE_EVENTS_COLLECTION: () => LIFECYCLE_EVENTS_COLLECTION,
2304
2305
  LIFECYCLE_EVENT_NAMES: () => LIFECYCLE_EVENT_NAMES,
2306
+ MatchBuilder: () => MatchBuilder,
2305
2307
  WORKFLOW_HISTORY_COLLECTION: () => WORKFLOW_HISTORY_COLLECTION,
2306
2308
  abs: () => abs,
2307
2309
  addDays: () => addDays,
@@ -2383,6 +2385,7 @@ __export(index_exports, {
2383
2385
  floor: () => floor,
2384
2386
  formatConfigDiagnostics: () => formatConfigDiagnostics,
2385
2387
  formatDate: () => formatDate,
2388
+ formatJexlValue: () => formatJexlValue,
2386
2389
  generateDefaultDetailSchema: () => generateDefaultDetailSchema,
2387
2390
  generateOpenApi: () => generateOpenApi,
2388
2391
  get: () => get,
@@ -2434,6 +2437,7 @@ __export(index_exports, {
2434
2437
  validateDeclarativeAccessExpression: () => validateDeclarativeAccessExpression,
2435
2438
  validateDeclarativeHookExpression: () => validateDeclarativeHookExpression,
2436
2439
  validatePreviewUrlExpression: () => validatePreviewUrlExpression,
2440
+ when: () => when,
2437
2441
  wordCount: () => wordCount,
2438
2442
  workflowCapabilities: () => workflowCapabilities
2439
2443
  });
@@ -3276,6 +3280,11 @@ function registerJexlHelpers(jexlInstance = import_jexl.default) {
3276
3280
  try {
3277
3281
  jexlInstance.addBinaryOp("and", 20, (left, right) => Boolean(left && right));
3278
3282
  jexlInstance.addBinaryOp("or", 10, (left, right) => Boolean(left || right));
3283
+ jexlInstance.addBinaryOp("in", 30, (left, right) => {
3284
+ if (Array.isArray(right)) return right.includes(left);
3285
+ if (typeof right === "string") return right.includes(String(left ?? ""));
3286
+ return false;
3287
+ });
3279
3288
  } catch {
3280
3289
  }
3281
3290
  }
@@ -6078,6 +6087,346 @@ function isDetailItemVisible(item, doc, user) {
6078
6087
  return true;
6079
6088
  }
6080
6089
 
6090
+ // src/utils/conditions.ts
6091
+ function formatJexlValue(v) {
6092
+ if (v === null || v === void 0) return "null";
6093
+ if (typeof v === "boolean" || typeof v === "number") return String(v);
6094
+ if (typeof v === "string") {
6095
+ if (v.startsWith("'") && v.endsWith("'") || v.startsWith('"') && v.endsWith('"') || v.includes("+") || v.includes("?") || v.includes("(") || v.includes(" ")) {
6096
+ return v;
6097
+ }
6098
+ if (v.startsWith("/")) {
6099
+ return `'${v}'`;
6100
+ }
6101
+ if (v === "null" || v === "true" || v === "false") {
6102
+ return v;
6103
+ }
6104
+ if (/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(v)) {
6105
+ return v;
6106
+ }
6107
+ return `'${v}'`;
6108
+ }
6109
+ return String(v);
6110
+ }
6111
+ var MatchBuilder = class {
6112
+ branches = [];
6113
+ /**
6114
+ * Adds a conditional branch (`condition ? value : ...`).
6115
+ */
6116
+ case(condition, value) {
6117
+ this.branches.push({ condition, value });
6118
+ return this;
6119
+ }
6120
+ /**
6121
+ * Final fallback value when no cases match.
6122
+ */
6123
+ otherwise(fallback = "null") {
6124
+ if (this.branches.length === 0) return formatJexlValue(fallback);
6125
+ let expr = formatJexlValue(fallback);
6126
+ for (let i = this.branches.length - 1; i >= 0; i--) {
6127
+ const b = this.branches[i];
6128
+ expr = `${b.condition} ? ${formatJexlValue(b.value)} : ${expr}`;
6129
+ }
6130
+ return expr;
6131
+ }
6132
+ };
6133
+ var FieldConditionBuilder = class {
6134
+ constructor(field) {
6135
+ this.field = field;
6136
+ }
6137
+ field;
6138
+ /**
6139
+ * Generates equality condition (`field == value`).
6140
+ */
6141
+ equals(value) {
6142
+ return `${this.field} == ${JSON.stringify(value)}`;
6143
+ }
6144
+ /**
6145
+ * Generates inequality condition (`field != value`).
6146
+ */
6147
+ notEquals(value) {
6148
+ return `${this.field} != ${JSON.stringify(value)}`;
6149
+ }
6150
+ /**
6151
+ * Generates set membership condition (`field in [val1, val2]`).
6152
+ */
6153
+ in(...values) {
6154
+ const flat = values.flat();
6155
+ return `${this.field} in [${flat.map((v) => JSON.stringify(v)).join(", ")}]`;
6156
+ }
6157
+ /**
6158
+ * Generates inverted set membership condition (`!(field in [val1, val2])`).
6159
+ */
6160
+ notIn(...values) {
6161
+ const flat = values.flat();
6162
+ return `!(${this.field} in [${flat.map((v) => JSON.stringify(v)).join(", ")}])`;
6163
+ }
6164
+ /**
6165
+ * Generates boolean true check (`field == true`).
6166
+ */
6167
+ isTrue() {
6168
+ return `${this.field} == true`;
6169
+ }
6170
+ /**
6171
+ * Generates boolean false check (`field != true`).
6172
+ */
6173
+ isFalse() {
6174
+ return `${this.field} != true`;
6175
+ }
6176
+ /**
6177
+ * Checks that the field is neither null/undefined nor an empty string.
6178
+ */
6179
+ notEmpty() {
6180
+ return `${this.field} != null && ${this.field} != ''`;
6181
+ }
6182
+ /**
6183
+ * Checks that the field is null, undefined, or an empty string.
6184
+ */
6185
+ isEmpty() {
6186
+ return `${this.field} == null || ${this.field} == ''`;
6187
+ }
6188
+ /**
6189
+ * Numerical greater than (`field > num`).
6190
+ */
6191
+ greaterThan(num) {
6192
+ return `${this.field} > ${typeof num === "number" ? num : num}`;
6193
+ }
6194
+ /**
6195
+ * Numerical greater than or equal (`field >= num`).
6196
+ */
6197
+ greaterThanOrEqual(num) {
6198
+ return `${this.field} >= ${typeof num === "number" ? num : num}`;
6199
+ }
6200
+ /**
6201
+ * Numerical less than (`field < num`).
6202
+ */
6203
+ lessThan(num) {
6204
+ return `${this.field} < ${typeof num === "number" ? num : num}`;
6205
+ }
6206
+ /**
6207
+ * Numerical less than or equal (`field <= num`).
6208
+ */
6209
+ lessThanOrEqual(num) {
6210
+ return `${this.field} <= ${typeof num === "number" ? num : num}`;
6211
+ }
6212
+ /**
6213
+ * Numerical range check (`field >= min && field <= max`).
6214
+ */
6215
+ between(min2, max2) {
6216
+ return `${this.field} >= ${min2} && ${this.field} <= ${max2}`;
6217
+ }
6218
+ /**
6219
+ * String prefix check using JEXL `startsWith(field, prefix)`.
6220
+ */
6221
+ startsWith(prefix) {
6222
+ return `startsWith(${this.field}, ${JSON.stringify(prefix)})`;
6223
+ }
6224
+ /**
6225
+ * String suffix check using JEXL `endsWith(field, suffix)`.
6226
+ */
6227
+ endsWith(suffix) {
6228
+ return `endsWith(${this.field}, ${JSON.stringify(suffix)})`;
6229
+ }
6230
+ /**
6231
+ * Substring or array item check using JEXL `includes(field, item)`.
6232
+ */
6233
+ contains(val) {
6234
+ return `includes(${this.field}, ${JSON.stringify(val)})`;
6235
+ }
6236
+ /**
6237
+ * Checks if array/string length exceeds a threshold.
6238
+ */
6239
+ hasLengthGreaterThan(length2) {
6240
+ return `length(${this.field}) > ${length2}`;
6241
+ }
6242
+ /**
6243
+ * Checks if array/string length is at least a threshold.
6244
+ */
6245
+ hasLengthAtLeast(length2) {
6246
+ return `length(${this.field}) >= ${length2}`;
6247
+ }
6248
+ };
6249
+ function createWhen() {
6250
+ const whenFn = ((fieldName) => new FieldConditionBuilder(fieldName));
6251
+ whenFn.user = (userProp) => new FieldConditionBuilder(`user.${userProp}`);
6252
+ whenFn.sibling = (siblingProp) => new FieldConditionBuilder(`siblingData.${siblingProp}`);
6253
+ whenFn.concat = (...parts) => {
6254
+ if (parts.length === 0) return "''";
6255
+ return parts.map((p) => formatJexlValue(p)).join(" + ");
6256
+ };
6257
+ whenFn.slugify = (field) => `slugify(${field})`;
6258
+ whenFn.lower = (field) => `lower(${field})`;
6259
+ whenFn.upper = (field) => `upper(${field})`;
6260
+ whenFn.trim = (field) => `trim(${field})`;
6261
+ whenFn.access = {
6262
+ isOwner: (ownerField = "author") => {
6263
+ return `${ownerField} == user.id || ${ownerField}.id == user.id`;
6264
+ },
6265
+ isAdmin: () => {
6266
+ return "user.role == 'admin' || (user.roles != null && 'admin' in user.roles)";
6267
+ },
6268
+ hasRole: (...roles) => {
6269
+ return whenFn.userRole(...roles);
6270
+ },
6271
+ isPublishedOrAdmin: (statusField = "status") => {
6272
+ return `${statusField} == 'published' || user.role == 'admin' || (user.roles != null && 'admin' in user.roles)`;
6273
+ }
6274
+ };
6275
+ whenFn.then = (condition, ifTrue, ifFalse = "null") => {
6276
+ return `${condition} ? ${formatJexlValue(ifTrue)} : ${formatJexlValue(ifFalse)}`;
6277
+ };
6278
+ whenFn.match = () => new MatchBuilder();
6279
+ whenFn.cases = (...branches) => {
6280
+ const builder = new MatchBuilder();
6281
+ let fallback = "null";
6282
+ for (const b of branches) {
6283
+ if (Array.isArray(b)) {
6284
+ builder.case(b[0], b[1]);
6285
+ } else {
6286
+ fallback = b;
6287
+ }
6288
+ }
6289
+ return builder.otherwise(fallback);
6290
+ };
6291
+ whenFn.all = (...conditions) => {
6292
+ const valid = conditions.filter(Boolean);
6293
+ if (valid.length === 0) return "true";
6294
+ if (valid.length === 1) return valid[0];
6295
+ return valid.map((c) => c.includes(" ") ? `(${c})` : c).join(" && ");
6296
+ };
6297
+ whenFn.and = whenFn.all;
6298
+ whenFn.any = (...conditions) => {
6299
+ const valid = conditions.filter(Boolean);
6300
+ if (valid.length === 0) return "true";
6301
+ if (valid.length === 1) return valid[0];
6302
+ return valid.map((c) => c.includes(" ") ? `(${c})` : c).join(" || ");
6303
+ };
6304
+ whenFn.or = whenFn.any;
6305
+ whenFn.not = (condition) => {
6306
+ if (!condition) return "false";
6307
+ return `!(${condition})`;
6308
+ };
6309
+ whenFn.variant = (...variants) => {
6310
+ const flat = variants.flat();
6311
+ if (flat.length === 0) return "true";
6312
+ if (flat.length === 1) return `variant == '${flat[0]}'`;
6313
+ return `variant in [${flat.map((v) => `'${v}'`).join(", ")}]`;
6314
+ };
6315
+ whenFn.notVariant = (...variants) => {
6316
+ const flat = variants.flat();
6317
+ if (flat.length === 0) return "false";
6318
+ if (flat.length === 1) return `variant != '${flat[0]}'`;
6319
+ return `!(variant in [${flat.map((v) => `'${v}'`).join(", ")}])`;
6320
+ };
6321
+ whenFn.block = (...blocks) => {
6322
+ const flat = blocks.flat();
6323
+ if (flat.length === 0) return "true";
6324
+ if (flat.length === 1) return `block == '${flat[0]}'`;
6325
+ return `block in [${flat.map((b) => `'${b}'`).join(", ")}]`;
6326
+ };
6327
+ whenFn.notBlock = (...blocks) => {
6328
+ const flat = blocks.flat();
6329
+ if (flat.length === 0) return "false";
6330
+ if (flat.length === 1) return `block != '${flat[0]}'`;
6331
+ return `!(block in [${flat.map((b) => `'${b}'`).join(", ")}])`;
6332
+ };
6333
+ whenFn.fieldEquals = (field, value) => {
6334
+ return `${field} == ${JSON.stringify(value)}`;
6335
+ };
6336
+ whenFn.fieldNotEquals = (field, value) => {
6337
+ return `${field} != ${JSON.stringify(value)}`;
6338
+ };
6339
+ whenFn.fieldIn = (field, ...values) => {
6340
+ const flat = values.flat();
6341
+ return `${field} in [${flat.map((v) => JSON.stringify(v)).join(", ")}]`;
6342
+ };
6343
+ whenFn.fieldNotIn = (field, ...values) => {
6344
+ const flat = values.flat();
6345
+ return `!(${field} in [${flat.map((v) => JSON.stringify(v)).join(", ")}])`;
6346
+ };
6347
+ whenFn.fieldNotEmpty = (field) => {
6348
+ return `${field} != null && ${field} != ''`;
6349
+ };
6350
+ whenFn.fieldEmpty = (field) => {
6351
+ return `${field} == null || ${field} == ''`;
6352
+ };
6353
+ whenFn.fieldIsTrue = (field) => {
6354
+ return `${field} == true`;
6355
+ };
6356
+ whenFn.fieldIsFalse = (field) => {
6357
+ return `${field} != true`;
6358
+ };
6359
+ whenFn.fieldGreaterThan = (field, value) => {
6360
+ return `${field} > ${value}`;
6361
+ };
6362
+ whenFn.fieldGreaterThanOrEqual = (field, value) => {
6363
+ return `${field} >= ${value}`;
6364
+ };
6365
+ whenFn.fieldLessThan = (field, value) => {
6366
+ return `${field} < ${value}`;
6367
+ };
6368
+ whenFn.fieldLessThanOrEqual = (field, value) => {
6369
+ return `${field} <= ${value}`;
6370
+ };
6371
+ whenFn.fieldBetween = (field, min2, max2) => {
6372
+ return `${field} >= ${min2} && ${field} <= ${max2}`;
6373
+ };
6374
+ whenFn.fieldStartsWith = (field, prefix) => {
6375
+ return `startsWith(${field}, ${JSON.stringify(prefix)})`;
6376
+ };
6377
+ whenFn.fieldEndsWith = (field, suffix) => {
6378
+ return `endsWith(${field}, ${JSON.stringify(suffix)})`;
6379
+ };
6380
+ whenFn.fieldContains = (field, item) => {
6381
+ return `includes(${field}, ${JSON.stringify(item)})`;
6382
+ };
6383
+ whenFn.arrayNotEmpty = (field) => {
6384
+ return `length(${field}) > 0`;
6385
+ };
6386
+ whenFn.arrayEmpty = (field) => {
6387
+ return `length(${field}) == 0`;
6388
+ };
6389
+ whenFn.arrayCountGreaterThan = (field, count2) => {
6390
+ return `length(${field}) > ${count2}`;
6391
+ };
6392
+ whenFn.arrayCountAtLeast = (field, count2) => {
6393
+ return `length(${field}) >= ${count2}`;
6394
+ };
6395
+ whenFn.arrayCountLessThan = (field, count2) => {
6396
+ return `length(${field}) < ${count2}`;
6397
+ };
6398
+ whenFn.userRole = (...roles) => {
6399
+ const flat = roles.flat();
6400
+ if (flat.length === 1) {
6401
+ return `user.role == '${flat[0]}' || (user.roles != null && '${flat[0]}' in user.roles)`;
6402
+ }
6403
+ const roleArray = `[${flat.map((r) => `'${r}'`).join(", ")}]`;
6404
+ return `user.role in ${roleArray} || (user.roles != null && includes(${roleArray}, user.role))`;
6405
+ };
6406
+ whenFn.userEmailDomain = (domain) => {
6407
+ const clean = domain.startsWith("@") ? domain : `@${domain}`;
6408
+ return `endsWith(user.email, '${clean}')`;
6409
+ };
6410
+ whenFn.userAttributeEquals = (attr, value) => {
6411
+ return `user.${attr} == ${JSON.stringify(value)}`;
6412
+ };
6413
+ whenFn.isNewDocument = () => {
6414
+ return "id == null";
6415
+ };
6416
+ whenFn.isExistingDocument = () => {
6417
+ return "id != null";
6418
+ };
6419
+ whenFn.statusEquals = (status) => {
6420
+ return `status == '${status}'`;
6421
+ };
6422
+ whenFn.statusIn = (...statuses) => {
6423
+ const flat = statuses.flat();
6424
+ return `status in [${flat.map((s) => `'${s}'`).join(", ")}]`;
6425
+ };
6426
+ return whenFn;
6427
+ }
6428
+ var when = createWhen();
6429
+
6081
6430
  // src/index.ts
6082
6431
  function defineCollection(config) {
6083
6432
  return config;
@@ -6129,8 +6478,10 @@ function defineTab(args) {
6129
6478
  0 && (module.exports = {
6130
6479
  BUILTIN_JEXL_HELPERS,
6131
6480
  ConfigValidationError,
6481
+ FieldConditionBuilder,
6132
6482
  LIFECYCLE_EVENTS_COLLECTION,
6133
6483
  LIFECYCLE_EVENT_NAMES,
6484
+ MatchBuilder,
6134
6485
  WORKFLOW_HISTORY_COLLECTION,
6135
6486
  abs,
6136
6487
  addDays,
@@ -6212,6 +6563,7 @@ function defineTab(args) {
6212
6563
  floor,
6213
6564
  formatConfigDiagnostics,
6214
6565
  formatDate,
6566
+ formatJexlValue,
6215
6567
  generateDefaultDetailSchema,
6216
6568
  generateOpenApi,
6217
6569
  get,
@@ -6263,6 +6615,7 @@ function defineTab(args) {
6263
6615
  validateDeclarativeAccessExpression,
6264
6616
  validateDeclarativeHookExpression,
6265
6617
  validatePreviewUrlExpression,
6618
+ when,
6266
6619
  wordCount,
6267
6620
  workflowCapabilities
6268
6621
  });