@jskit-ai/crud-server-generator 0.1.59 → 0.1.60

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.
@@ -1,7 +1,7 @@
1
1
  export default Object.freeze({
2
2
  packageVersion: 1,
3
3
  packageId: "@jskit-ai/crud-server-generator",
4
- version: "0.1.59",
4
+ version: "0.1.60",
5
5
  kind: "generator",
6
6
  description: "CRUD server generator with routes, actions, and persistence scaffolding.",
7
7
  options: {
@@ -151,13 +151,13 @@ export default Object.freeze({
151
151
  mutations: {
152
152
  dependencies: {
153
153
  runtime: {
154
- "@jskit-ai/auth-core": "0.1.50",
155
- "@jskit-ai/crud-core": "0.1.59",
156
- "@jskit-ai/database-runtime": "0.1.51",
157
- "@jskit-ai/http-runtime": "0.1.50",
158
- "@jskit-ai/kernel": "0.1.51",
159
- "@jskit-ai/realtime": "0.1.50",
160
- "@jskit-ai/users-core": "0.1.61",
154
+ "@jskit-ai/auth-core": "0.1.51",
155
+ "@jskit-ai/crud-core": "0.1.60",
156
+ "@jskit-ai/database-runtime": "0.1.52",
157
+ "@jskit-ai/http-runtime": "0.1.51",
158
+ "@jskit-ai/kernel": "0.1.52",
159
+ "@jskit-ai/realtime": "0.1.51",
160
+ "@jskit-ai/users-core": "0.1.62",
161
161
  "@local/${option:namespace|kebab}": "file:packages/${option:namespace|kebab}",
162
162
  "typebox": "^1.0.81"
163
163
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/crud-server-generator",
3
- "version": "0.1.59",
3
+ "version": "0.1.60",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
@@ -13,11 +13,11 @@
13
13
  },
14
14
  "dependencies": {
15
15
  "@babel/parser": "^7.29.2",
16
- "@jskit-ai/crud-core": "0.1.59",
17
- "@jskit-ai/database-runtime": "0.1.51",
18
- "@jskit-ai/http-runtime": "0.1.50",
19
- "@jskit-ai/kernel": "0.1.51",
20
- "@jskit-ai/users-core": "0.1.61",
16
+ "@jskit-ai/crud-core": "0.1.60",
17
+ "@jskit-ai/database-runtime": "0.1.52",
18
+ "@jskit-ai/http-runtime": "0.1.51",
19
+ "@jskit-ai/kernel": "0.1.52",
20
+ "@jskit-ai/users-core": "0.1.62",
21
21
  "recast": "^0.23.11",
22
22
  "typebox": "^1.0.81"
23
23
  }
@@ -392,6 +392,7 @@ function resolveColumnKey(column, idColumn) {
392
392
  }
393
393
 
394
394
  const NUMERIC_CHECK_CONSTRAINT_PATTERN = /(?:`([^`]+)`|([A-Za-z_][A-Za-z0-9_]*))\s*(>=|>|<=|<)\s*(-?\d+(?:\.\d+)?)/g;
395
+ const NUMERIC_CHECK_CONSTRAINT_BETWEEN_PATTERN = /(?:`([^`]+)`|([A-Za-z_][A-Za-z0-9_]*))\s+between\s+(-?\d+(?:\.\d+)?)\s+and\s+(-?\d+(?:\.\d+)?)/gi;
395
396
 
396
397
  function normalizeNumericBoundValue(value, scale = null) {
397
398
  const parsed = Number(value);
@@ -452,6 +453,83 @@ function applyUpperBound(current = null, candidate = null) {
452
453
  return current;
453
454
  }
454
455
 
456
+ function applyNumericConstraintBound(target = {}, column = null, operator = "", rawValue = null) {
457
+ if (!column || !Number.isFinite(rawValue)) {
458
+ return;
459
+ }
460
+
461
+ if (operator === ">=" || operator === ">") {
462
+ let candidate = null;
463
+ if (operator === ">=") {
464
+ candidate = {
465
+ value: normalizeNumericBoundValue(rawValue, column.numericScale),
466
+ exclusive: false
467
+ };
468
+ } else {
469
+ const exclusiveStep = resolveNumericExclusiveStep(column);
470
+ if (exclusiveStep != null) {
471
+ candidate = {
472
+ value: normalizeNumericBoundValue(rawValue + exclusiveStep, column.numericScale),
473
+ exclusive: false
474
+ };
475
+ } else {
476
+ candidate = {
477
+ value: normalizeNumericBoundValue(rawValue, column.numericScale),
478
+ exclusive: true
479
+ };
480
+ }
481
+ }
482
+
483
+ const nextBound = applyLowerBound(
484
+ target.minimum != null || target.exclusiveMinimum != null
485
+ ? {
486
+ value: target.minimum ?? target.exclusiveMinimum,
487
+ exclusive: target.exclusiveMinimum != null
488
+ }
489
+ : null,
490
+ candidate
491
+ );
492
+ target.minimum = nextBound?.exclusive === true ? null : nextBound?.value ?? null;
493
+ target.exclusiveMinimum = nextBound?.exclusive === true ? nextBound?.value ?? null : null;
494
+ return;
495
+ }
496
+
497
+ if (operator === "<=" || operator === "<") {
498
+ let candidate = null;
499
+ if (operator === "<=") {
500
+ candidate = {
501
+ value: normalizeNumericBoundValue(rawValue, column.numericScale),
502
+ exclusive: false
503
+ };
504
+ } else {
505
+ const exclusiveStep = resolveNumericExclusiveStep(column);
506
+ if (exclusiveStep != null) {
507
+ candidate = {
508
+ value: normalizeNumericBoundValue(rawValue - exclusiveStep, column.numericScale),
509
+ exclusive: false
510
+ };
511
+ } else {
512
+ candidate = {
513
+ value: normalizeNumericBoundValue(rawValue, column.numericScale),
514
+ exclusive: true
515
+ };
516
+ }
517
+ }
518
+
519
+ const nextBound = applyUpperBound(
520
+ target.maximum != null || target.exclusiveMaximum != null
521
+ ? {
522
+ value: target.maximum ?? target.exclusiveMaximum,
523
+ exclusive: target.exclusiveMaximum != null
524
+ }
525
+ : null,
526
+ candidate
527
+ );
528
+ target.maximum = nextBound?.exclusive === true ? null : nextBound?.value ?? null;
529
+ target.exclusiveMaximum = nextBound?.exclusive === true ? nextBound?.value ?? null : null;
530
+ }
531
+ }
532
+
455
533
  function resolveColumnNumericBounds(snapshot = {}) {
456
534
  const byColumnName = new Map();
457
535
  const columns = Array.isArray(snapshot.columns) ? snapshot.columns : [];
@@ -487,6 +565,22 @@ function resolveColumnNumericBounds(snapshot = {}) {
487
565
  continue;
488
566
  }
489
567
 
568
+ let betweenMatch = null;
569
+ while ((betweenMatch = NUMERIC_CHECK_CONSTRAINT_BETWEEN_PATTERN.exec(clause)) != null) {
570
+ const columnName = String(betweenMatch[1] || betweenMatch[2] || "");
571
+ const lowerValue = Number(betweenMatch[3]);
572
+ const upperValue = Number(betweenMatch[4]);
573
+ const column = numericColumnsByName.get(columnName) || null;
574
+ if (!column || !Number.isFinite(lowerValue) || !Number.isFinite(upperValue)) {
575
+ continue;
576
+ }
577
+
578
+ const target = getColumnBounds(columnName);
579
+ applyNumericConstraintBound(target, column, ">=", lowerValue);
580
+ applyNumericConstraintBound(target, column, "<=", upperValue);
581
+ }
582
+ NUMERIC_CHECK_CONSTRAINT_BETWEEN_PATTERN.lastIndex = 0;
583
+
490
584
  let match = null;
491
585
  while ((match = NUMERIC_CHECK_CONSTRAINT_PATTERN.exec(clause)) != null) {
492
586
  const columnName = String(match[1] || match[2] || "");
@@ -498,76 +592,7 @@ function resolveColumnNumericBounds(snapshot = {}) {
498
592
  }
499
593
 
500
594
  const target = getColumnBounds(columnName);
501
- if (operator === ">=" || operator === ">") {
502
- let candidate = null;
503
- if (operator === ">=") {
504
- candidate = {
505
- value: normalizeNumericBoundValue(rawValue, column.numericScale),
506
- exclusive: false
507
- };
508
- } else {
509
- const exclusiveStep = resolveNumericExclusiveStep(column);
510
- if (exclusiveStep != null) {
511
- candidate = {
512
- value: normalizeNumericBoundValue(rawValue + exclusiveStep, column.numericScale),
513
- exclusive: false
514
- };
515
- } else {
516
- candidate = {
517
- value: normalizeNumericBoundValue(rawValue, column.numericScale),
518
- exclusive: true
519
- };
520
- }
521
- }
522
-
523
- const nextBound = applyLowerBound(
524
- target.minimum != null || target.exclusiveMinimum != null
525
- ? {
526
- value: target.minimum ?? target.exclusiveMinimum,
527
- exclusive: target.exclusiveMinimum != null
528
- }
529
- : null,
530
- candidate
531
- );
532
- target.minimum = nextBound?.exclusive === true ? null : nextBound?.value ?? null;
533
- target.exclusiveMinimum = nextBound?.exclusive === true ? nextBound?.value ?? null : null;
534
- continue;
535
- }
536
-
537
- if (operator === "<=" || operator === "<") {
538
- let candidate = null;
539
- if (operator === "<=") {
540
- candidate = {
541
- value: normalizeNumericBoundValue(rawValue, column.numericScale),
542
- exclusive: false
543
- };
544
- } else {
545
- const exclusiveStep = resolveNumericExclusiveStep(column);
546
- if (exclusiveStep != null) {
547
- candidate = {
548
- value: normalizeNumericBoundValue(rawValue - exclusiveStep, column.numericScale),
549
- exclusive: false
550
- };
551
- } else {
552
- candidate = {
553
- value: normalizeNumericBoundValue(rawValue, column.numericScale),
554
- exclusive: true
555
- };
556
- }
557
- }
558
-
559
- const nextBound = applyUpperBound(
560
- target.maximum != null || target.exclusiveMaximum != null
561
- ? {
562
- value: target.maximum ?? target.exclusiveMaximum,
563
- exclusive: target.exclusiveMaximum != null
564
- }
565
- : null,
566
- candidate
567
- );
568
- target.maximum = nextBound?.exclusive === true ? null : nextBound?.value ?? null;
569
- target.exclusiveMaximum = nextBound?.exclusive === true ? nextBound?.value ?? null : null;
570
- }
595
+ applyNumericConstraintBound(target, column, operator, rawValue);
571
596
  }
572
597
  NUMERIC_CHECK_CONSTRAINT_PATTERN.lastIndex = 0;
573
598
  }
@@ -808,13 +808,35 @@ test("resolveScaffoldColumns derives resource numeric bounds from check constrai
808
808
  enumValues: Object.freeze([])
809
809
  });
810
810
 
811
+ const severityColumn = Object.freeze({
812
+ name: "severity",
813
+ key: "severity",
814
+ dataType: "tinyint",
815
+ columnType: "tinyint unsigned",
816
+ typeKind: "integer",
817
+ nullable: true,
818
+ hasDefault: false,
819
+ defaultValue: null,
820
+ autoIncrement: false,
821
+ unsigned: true,
822
+ extra: "",
823
+ maxLength: null,
824
+ numericPrecision: 3,
825
+ numericScale: 0,
826
+ datetimePrecision: null,
827
+ characterSetName: "",
828
+ collationName: "",
829
+ enumValues: Object.freeze([])
830
+ });
831
+
811
832
  const scaffoldColumns = __testables.resolveScaffoldColumns({
812
833
  ...snapshot,
813
834
  columns: Object.freeze([
814
835
  snapshot.columns[0],
815
836
  inputWeightColumn,
816
837
  batchedDailySequenceColumn,
817
- moistureLevelColumn
838
+ moistureLevelColumn,
839
+ severityColumn
818
840
  ]),
819
841
  checkConstraints: Object.freeze([
820
842
  Object.freeze({
@@ -828,6 +850,10 @@ test("resolveScaffoldColumns derives resource numeric bounds from check constrai
828
850
  Object.freeze({
829
851
  name: "chk_batches_moisture_level",
830
852
  clause: "`moisture_level` is null or `moisture_level` >= 0 and `moisture_level` <= 100"
853
+ }),
854
+ Object.freeze({
855
+ name: "chk_pet_notes_severity",
856
+ clause: "`severity` is null or `severity` between 1 and 10"
831
857
  })
832
858
  ])
833
859
  });
@@ -835,6 +861,7 @@ test("resolveScaffoldColumns derives resource numeric bounds from check constrai
835
861
  const inputWeight = scaffoldColumns.find((column) => column.name === "input_weight");
836
862
  const batchedDailySequence = scaffoldColumns.find((column) => column.name === "batched_daily_sequence");
837
863
  const moistureLevel = scaffoldColumns.find((column) => column.name === "moisture_level");
864
+ const severity = scaffoldColumns.find((column) => column.name === "severity");
838
865
 
839
866
  assert.equal(
840
867
  __testables.renderResourceFieldSchema(inputWeight),
@@ -848,6 +875,10 @@ test("resolveScaffoldColumns derives resource numeric bounds from check constrai
848
875
  __testables.renderResourceFieldSchema(moistureLevel),
849
876
  "Type.Union([Type.Number({ minimum: 0, maximum: 100 }), Type.Null()])"
850
877
  );
878
+ assert.equal(
879
+ __testables.renderResourceFieldSchema(severity),
880
+ "Type.Union([Type.Integer({ minimum: 1, maximum: 10 }), Type.Null()])"
881
+ );
851
882
  });
852
883
 
853
884
  test("buildReplacementsFromSnapshot normalizes nullable temporal inputs without invalid date errors", () => {