@ftschopp/dynatable-core 1.2.1 → 1.2.3

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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## @ftschopp/dynatable-core [1.2.3](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.2.2...@ftschopp/dynatable-core@1.2.3) (2026-04-27)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * resolve both required and optional keys in generate fields ([2c06b43](https://github.com/ftschopp/dynatable/commit/2c06b435d4578cb28f773798c80c19bf4eb9ef28))
7
+
8
+ ## @ftschopp/dynatable-core [1.2.2](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.2.1...@ftschopp/dynatable-core@1.2.2) (2026-04-15)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * add filter type scan entity ([dddff15](https://github.com/ftschopp/dynatable/commit/dddff15228f84fd11b0c6d2daa6f5cc8d77c641d))
14
+
1
15
  ## @ftschopp/dynatable-core [1.2.1](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.2.0...@ftschopp/dynatable-core@1.2.1) (2026-04-15)
2
16
 
3
17
 
@@ -1 +1 @@
1
- {"version":3,"file":"create-entity-api.d.ts","sourceRoot":"","sources":["../../src/entity/create-entity-api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AActF,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAKtD;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,SAAS,eAAe,EAC3D,WAAW,MAAM,EACjB,WAAW,MAAM,EACjB,OAAO,KAAK,EACZ,QAAQ,cAAc,EACtB,UAAS,gBAAqB,KAC7B,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CA8KtE,CAAC"}
1
+ {"version":3,"file":"create-entity-api.d.ts","sourceRoot":"","sources":["../../src/entity/create-entity-api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AActF,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAMtD;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,SAAS,eAAe,EAC3D,WAAW,MAAM,EACjB,WAAW,MAAM,EACjB,OAAO,KAAK,EACZ,QAAQ,cAAc,EACtB,UAAS,gBAAqB,KAC7B,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CAqLtE,CAAC"}
@@ -38,8 +38,8 @@ const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
38
38
  isUpdate: false,
39
39
  timestamps,
40
40
  });
41
- // Resolve keys again with defaults
42
- const fullKey = (0, model_utils_1.resolveKeys)(model, withDefaults);
41
+ // Resolve keys again with defaults (including GSI index keys)
42
+ const fullKey = (0, model_utils_1.resolveKeys)(model, withDefaults, 'both');
43
43
  // Combine keys and data into full item, adding _type field
44
44
  const fullItem = {
45
45
  ...withDefaults,
@@ -54,7 +54,13 @@ const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
54
54
  return (0, builders_1.createQueryBuilder)(tableName, client, model, logger);
55
55
  },
56
56
  scan() {
57
- const builder = (0, builders_1.createScanBuilder)(tableName, client, [], [], undefined, false, undefined, undefined, undefined, logger);
57
+ // Auto-filter by entity type so scan only returns items for this entity
58
+ const typeFilter = {
59
+ expression: '#_type = :_type',
60
+ names: { '#_type': '_type' },
61
+ values: { ':_type': modelName },
62
+ };
63
+ const builder = (0, builders_1.createScanBuilder)(tableName, client, [typeFilter], [], undefined, false, undefined, undefined, undefined, logger);
58
64
  return (0, with_middleware_1.withMiddleware)(builder, (0, factories_1.createCleanKeysMiddleware)(cleanInternalKeys));
59
65
  },
60
66
  update(key) {
@@ -100,8 +106,8 @@ const createEntityAPI = (tableName, modelName, model, client, options = {}) => {
100
106
  isUpdate: false,
101
107
  timestamps,
102
108
  });
103
- // Resolve keys again with defaults
104
- const fullKey = (0, model_utils_1.resolveKeys)(model, withDefaults);
109
+ // Resolve keys again with defaults (including GSI index keys)
110
+ const fullKey = (0, model_utils_1.resolveKeys)(model, withDefaults, 'both');
105
111
  // Combine keys and data into full item, adding _type field
106
112
  return {
107
113
  ...withDefaults,
@@ -1 +1 @@
1
- {"version":3,"file":"zod-utils.d.ts","sourceRoot":"","sources":["../../src/utils/zod-utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAK,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,mBAAmB,KAAG,OA+BrD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,eAAe,KAAG,SAAS,CAAC,GAAG,CAMhE,CAAC"}
1
+ {"version":3,"file":"zod-utils.d.ts","sourceRoot":"","sources":["../../src/utils/zod-utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAK,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,mBAAmB,KAAG,OAgCrD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,eAAe,KAAG,SAAS,CAAC,GAAG,CAMhE,CAAC"}
@@ -39,7 +39,8 @@ const typeToZod = (attr) => {
39
39
  ? zod_1.z.date()
40
40
  : zod_1.z.unknown();
41
41
  }
42
- return attr.required ? zodType : zodType.optional();
42
+ const isGenerated = 'generate' in attr;
43
+ return attr.required && !isGenerated ? zodType : zodType.optional();
43
44
  };
44
45
  exports.typeToZod = typeToZod;
45
46
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ftschopp/dynatable-core",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Core library for DynamoDB single table design",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -18,6 +18,7 @@ import { EntityAPI, EntityAPIOptions } from './types';
18
18
  import { withMiddleware } from './middleware/with-middleware';
19
19
  import { createCleanKeysMiddleware } from './middleware/factories';
20
20
  import { validateKeyFields } from './validation/key-validation';
21
+ import { Condition } from '@/builders/shared/types';
21
22
 
22
23
  /**
23
24
  * Creates an entity API instance with validation, key resolution, and builder creation.
@@ -70,8 +71,8 @@ export const createEntityAPI = <Model extends ModelDefinition>(
70
71
  timestamps,
71
72
  });
72
73
 
73
- // Resolve keys again with defaults
74
- const fullKey = resolveKeys(model, withDefaults);
74
+ // Resolve keys again with defaults (including GSI index keys)
75
+ const fullKey = resolveKeys(model, withDefaults, 'both');
75
76
 
76
77
  // Combine keys and data into full item, adding _type field
77
78
  const fullItem = {
@@ -90,10 +91,17 @@ export const createEntityAPI = <Model extends ModelDefinition>(
90
91
  },
91
92
 
92
93
  scan() {
94
+ // Auto-filter by entity type so scan only returns items for this entity
95
+ const typeFilter: Condition = {
96
+ expression: '#_type = :_type',
97
+ names: { '#_type': '_type' },
98
+ values: { ':_type': modelName },
99
+ };
100
+
93
101
  const builder = createScanBuilder<InferModel<Model>>(
94
102
  tableName,
95
103
  client,
96
- [],
104
+ [typeFilter],
97
105
  [],
98
106
  undefined,
99
107
  false,
@@ -186,8 +194,8 @@ export const createEntityAPI = <Model extends ModelDefinition>(
186
194
  timestamps,
187
195
  });
188
196
 
189
- // Resolve keys again with defaults
190
- const fullKey = resolveKeys(model, withDefaults);
197
+ // Resolve keys again with defaults (including GSI index keys)
198
+ const fullKey = resolveKeys(model, withDefaults, 'both');
191
199
 
192
200
  // Combine keys and data into full item, adding _type field
193
201
  return {
@@ -38,7 +38,8 @@ export const typeToZod = (attr: AttributeDefinition): ZodType => {
38
38
  : z.unknown();
39
39
  }
40
40
 
41
- return attr.required ? zodType : zodType.optional();
41
+ const isGenerated = 'generate' in attr;
42
+ return attr.required && !isGenerated ? zodType : zodType.optional();
42
43
  };
43
44
 
44
45
  /**
@@ -726,7 +726,9 @@ describe('Should test dbParams function builder', () => {
726
726
  const params = await table.entities.User.scan().dbParams();
727
727
 
728
728
  expect(params.TableName).toBe('InstagramClone');
729
- expect(params.FilterExpression).toBeUndefined();
729
+ expect(params.FilterExpression).toBe('#_type = :_type');
730
+ expect(params.ExpressionAttributeNames).toMatchObject({ '#_type': '_type' });
731
+ expect(params.ExpressionAttributeValues).toMatchObject({ ':_type': 'User' });
730
732
  });
731
733
 
732
734
  test('SCAN Users with filter', async () => {
@@ -736,10 +738,12 @@ describe('Should test dbParams function builder', () => {
736
738
 
737
739
  expect(params.TableName).toBe('InstagramClone');
738
740
  expect(params.FilterExpression).toMatch(/#followerCount > :followerCount_\d+/);
739
- expect(params.ExpressionAttributeNames).toEqual({
741
+ expect(params.ExpressionAttributeNames).toMatchObject({
740
742
  '#followerCount': 'followerCount',
743
+ '#_type': '_type',
741
744
  });
742
745
  expect(Object.values(params.ExpressionAttributeValues || {})).toContain(100);
746
+ expect(Object.values(params.ExpressionAttributeValues || {})).toContain('User');
743
747
  });
744
748
 
745
749
  test('SCAN Users with multiple filters', async () => {
@@ -751,9 +755,10 @@ describe('Should test dbParams function builder', () => {
751
755
  expect(params.FilterExpression).toMatch(
752
756
  /\(#followerCount > :followerCount_\d+\) AND \(#followingCount > :followingCount_\d+\)/
753
757
  );
754
- expect(params.ExpressionAttributeNames).toEqual({
758
+ expect(params.ExpressionAttributeNames).toMatchObject({
755
759
  '#followerCount': 'followerCount',
756
760
  '#followingCount': 'followingCount',
761
+ '#_type': '_type',
757
762
  });
758
763
  });
759
764
 
@@ -763,6 +768,8 @@ describe('Should test dbParams function builder', () => {
763
768
  .dbParams();
764
769
 
765
770
  expect(params.ProjectionExpression).toBe('username, photoId, likesCount');
771
+ expect(params.FilterExpression).toBe('#_type = :_type');
772
+ expect(params.ExpressionAttributeValues).toMatchObject({ ':_type': 'Photo' });
766
773
  });
767
774
 
768
775
  test('SCAN Photos with limit', async () => {
@@ -773,6 +780,8 @@ describe('Should test dbParams function builder', () => {
773
780
 
774
781
  expect(params.Limit).toBe(10);
775
782
  expect(params.FilterExpression).toMatch(/#likesCount > :likesCount_\d+/);
783
+ expect(params.FilterExpression).toMatch(/#_type = :_type/);
784
+ expect(params.ExpressionAttributeValues).toMatchObject({ ':_type': 'Photo' });
776
785
  });
777
786
 
778
787
  test('SCAN with IN operator', async () => {
@@ -781,9 +790,11 @@ describe('Should test dbParams function builder', () => {
781
790
  .dbParams();
782
791
 
783
792
  expect(params.FilterExpression).toContain('IN');
793
+ expect(params.FilterExpression).toMatch(/#_type = :_type/);
784
794
  expect(Object.values(params.ExpressionAttributeValues || {})).toContain('alice');
785
795
  expect(Object.values(params.ExpressionAttributeValues || {})).toContain('bob');
786
796
  expect(Object.values(params.ExpressionAttributeValues || {})).toContain('charlie');
797
+ expect(Object.values(params.ExpressionAttributeValues || {})).toContain('User');
787
798
  });
788
799
 
789
800
  test('SCAN with size operator', async () => {
@@ -792,7 +803,9 @@ describe('Should test dbParams function builder', () => {
792
803
  .dbParams();
793
804
 
794
805
  expect(params.FilterExpression).toMatch(/size\(#name\) > :name_size_\d+/);
806
+ expect(params.FilterExpression).toMatch(/#_type = :_type/);
795
807
  expect(Object.values(params.ExpressionAttributeValues || {})).toContain(5);
808
+ expect(Object.values(params.ExpressionAttributeValues || {})).toContain('User');
796
809
  });
797
810
 
798
811
  test('SCAN Comments with complex filter', async () => {
@@ -804,6 +817,8 @@ describe('Should test dbParams function builder', () => {
804
817
 
805
818
  expect(params.FilterExpression).toMatch(/attribute_exists\(#content\)/);
806
819
  expect(params.FilterExpression).toMatch(/size\(#content\) > :content_size_\d+/);
820
+ expect(params.FilterExpression).toMatch(/#_type = :_type/);
821
+ expect(params.ExpressionAttributeValues).toMatchObject({ ':_type': 'Comment' });
807
822
  expect(params.ProjectionExpression).toBe('photoId, commentId, content');
808
823
  expect(params.Limit).toBe(50);
809
824
  });