dyno-table 0.1.6 → 0.1.8

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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -17
  3. package/dist/builders/condition-check-builder.cjs +394 -0
  4. package/dist/builders/condition-check-builder.cjs.map +1 -0
  5. package/dist/builders/condition-check-builder.js +392 -0
  6. package/dist/builders/condition-check-builder.js.map +1 -0
  7. package/dist/builders/delete-builder.cjs +422 -0
  8. package/dist/builders/delete-builder.cjs.map +1 -0
  9. package/dist/builders/delete-builder.js +420 -0
  10. package/dist/builders/delete-builder.js.map +1 -0
  11. package/dist/builders/paginator.cjs +199 -0
  12. package/dist/builders/paginator.cjs.map +1 -0
  13. package/dist/builders/paginator.js +197 -0
  14. package/dist/builders/paginator.js.map +1 -0
  15. package/dist/builders/put-builder.cjs +468 -0
  16. package/dist/builders/put-builder.cjs.map +1 -0
  17. package/dist/builders/put-builder.js +466 -0
  18. package/dist/builders/put-builder.js.map +1 -0
  19. package/dist/builders/query-builder.cjs +674 -0
  20. package/dist/builders/query-builder.cjs.map +1 -0
  21. package/dist/builders/query-builder.js +672 -0
  22. package/dist/builders/query-builder.js.map +1 -0
  23. package/dist/builders/transaction-builder.cjs +876 -0
  24. package/dist/builders/transaction-builder.cjs.map +1 -0
  25. package/dist/builders/transaction-builder.js +874 -0
  26. package/dist/builders/transaction-builder.js.map +1 -0
  27. package/dist/builders/update-builder.cjs +662 -0
  28. package/dist/builders/update-builder.cjs.map +1 -0
  29. package/dist/builders/update-builder.js +660 -0
  30. package/dist/builders/update-builder.js.map +1 -0
  31. package/dist/conditions.cjs +59 -0
  32. package/dist/conditions.cjs.map +1 -0
  33. package/dist/conditions.js +43 -0
  34. package/dist/conditions.js.map +1 -0
  35. package/dist/entity.cjs +169 -0
  36. package/dist/entity.cjs.map +1 -0
  37. package/dist/entity.js +165 -0
  38. package/dist/entity.js.map +1 -0
  39. package/dist/index.cjs +3333 -0
  40. package/dist/index.d.cts +2971 -0
  41. package/dist/index.d.ts +1504 -1383
  42. package/dist/index.js +391 -375
  43. package/dist/standard-schema.cjs +4 -0
  44. package/dist/standard-schema.cjs.map +1 -0
  45. package/dist/standard-schema.js +3 -0
  46. package/dist/standard-schema.js.map +1 -0
  47. package/dist/table.cjs +3265 -0
  48. package/dist/table.cjs.map +1 -0
  49. package/dist/table.js +3263 -0
  50. package/dist/table.js.map +1 -0
  51. package/dist/types.cjs +4 -0
  52. package/dist/types.cjs.map +1 -0
  53. package/dist/types.js +3 -0
  54. package/dist/types.js.map +1 -0
  55. package/dist/utils/key-template.cjs +19 -0
  56. package/dist/utils/key-template.cjs.map +1 -0
  57. package/dist/utils/key-template.js +17 -0
  58. package/dist/utils/key-template.js.map +1 -0
  59. package/dist/utils/sort-key-template.cjs +19 -0
  60. package/dist/utils/sort-key-template.cjs.map +1 -0
  61. package/dist/utils/sort-key-template.js +17 -0
  62. package/dist/utils/sort-key-template.js.map +1 -0
  63. package/package.json +12 -7
package/dist/entity.js ADDED
@@ -0,0 +1,165 @@
1
+ // src/entity.ts
2
+ function defineEntity(config) {
3
+ return {
4
+ createRepository: (table) => {
5
+ const repository = {
6
+ _hooks: config.hooks,
7
+ create: (data) => {
8
+ const builder = table.create(data);
9
+ const originalExecute = builder.execute;
10
+ builder.execute = async () => {
11
+ const validationResult = await config.schema["~standard"].validate(data);
12
+ if ("issues" in validationResult && validationResult.issues) {
13
+ throw new Error(`Validation failed: ${validationResult.issues.map((i) => i.message).join(", ")}`);
14
+ }
15
+ Object.assign(builder, { item: validationResult.value });
16
+ const result = await originalExecute.call(builder);
17
+ if (!result) {
18
+ throw new Error("Failed to create item");
19
+ }
20
+ return result;
21
+ };
22
+ return builder;
23
+ },
24
+ upsert: (data) => {
25
+ const builder = table.put(data);
26
+ const originalExecute = builder.execute;
27
+ builder.execute = async () => {
28
+ const validationResult = await config.schema["~standard"].validate(data);
29
+ if ("issues" in validationResult && validationResult.issues) {
30
+ throw new Error(`Validation failed: ${validationResult.issues.map((i) => i.message).join(", ")}`);
31
+ }
32
+ Object.assign(builder, { item: validationResult.value });
33
+ const result = await originalExecute.call(builder);
34
+ if (!result) {
35
+ throw new Error("Failed to upsert item");
36
+ }
37
+ return result;
38
+ };
39
+ return builder;
40
+ },
41
+ get: (key) => {
42
+ const builder = table.get(key);
43
+ const originalExecute = builder.execute;
44
+ builder.execute = async () => {
45
+ const result = await originalExecute.call(builder);
46
+ const item = result?.item;
47
+ return {
48
+ item: config.hooks?.afterGet ? await config.hooks.afterGet(item) : item
49
+ };
50
+ };
51
+ return builder;
52
+ },
53
+ update: (key, data) => {
54
+ const builder = table.update(key);
55
+ const originalExecute = builder.execute;
56
+ builder.execute = async () => {
57
+ const result = await originalExecute.call(builder);
58
+ if (!result?.item) {
59
+ throw new Error("Failed to update item");
60
+ }
61
+ return result.item;
62
+ };
63
+ return builder;
64
+ },
65
+ delete: (key) => {
66
+ return table.delete(key);
67
+ },
68
+ query: {},
69
+ // Add a method to create a query builder
70
+ queryBuilder: (key) => {
71
+ const primaryKey = {
72
+ pk: key.pk,
73
+ ...key.sk ? { sk: (op) => op.eq(key.sk) } : {}
74
+ };
75
+ return table.query(primaryKey);
76
+ },
77
+ // Implement findBy method to find entities by their attributes
78
+ findBy: (attribute, value, options) => {
79
+ const scanBuilder = table.scan().filter((op) => op.eq(attribute, value));
80
+ if (options?.limit) {
81
+ scanBuilder.limit(options.limit);
82
+ }
83
+ if (options?.lastEvaluatedKey) {
84
+ scanBuilder.startFrom(options.lastEvaluatedKey);
85
+ }
86
+ return scanBuilder;
87
+ },
88
+ // Implement scan method to scan all entities
89
+ scan: (options) => {
90
+ const scanBuilder = table.scan();
91
+ if (options?.limit) {
92
+ scanBuilder.limit(options.limit);
93
+ }
94
+ if (options?.lastEvaluatedKey) {
95
+ scanBuilder.startFrom(options.lastEvaluatedKey);
96
+ }
97
+ return scanBuilder;
98
+ }
99
+ };
100
+ if (config.queries) {
101
+ for (const [key, queryFn] of Object.entries(config.queries)) {
102
+ repository.query[key] = queryFn.bind(repository);
103
+ }
104
+ }
105
+ return repository;
106
+ }
107
+ };
108
+ }
109
+ function createQueries() {
110
+ return {
111
+ input: (schema) => ({
112
+ // biome-ignore lint/complexity/noBannedTypes: <explanation>
113
+ query: (handler) => {
114
+ const queryFn = async function(input) {
115
+ const validationResult = await schema["~standard"].validate(input);
116
+ if ("issues" in validationResult && validationResult.issues) {
117
+ throw new Error(`Validation failed: ${validationResult.issues.map((i) => i.message).join(", ")}`);
118
+ }
119
+ const queryBuilder = handler({ input: validationResult.value, entity: this });
120
+ const result = await queryBuilder.execute();
121
+ let items = result.items;
122
+ if (this._hooks?.afterGet) {
123
+ const processedItems = await Promise.all(
124
+ // biome-ignore lint/style/noNonNullAssertion: The method will exit as it's confirmed above
125
+ items.map(async (item) => await this._hooks.afterGet(item))
126
+ );
127
+ items = processedItems.filter((item) => item !== null);
128
+ }
129
+ return {
130
+ items,
131
+ lastEvaluatedKey: result.lastEvaluatedKey
132
+ };
133
+ };
134
+ return queryFn;
135
+ }
136
+ })
137
+ };
138
+ }
139
+ function createIndex() {
140
+ return {
141
+ partitionKey: (pkFn) => ({
142
+ sortKey: (skFn) => ({
143
+ name: "custom",
144
+ partitionKey: "pk",
145
+ sortKey: "sk",
146
+ generateKey: (item) => ({
147
+ pk: pkFn(item),
148
+ sk: skFn(item)
149
+ })
150
+ }),
151
+ // Allow creating an index with only a partition key
152
+ withoutSortKey: () => ({
153
+ name: "custom",
154
+ partitionKey: "pk",
155
+ generateKey: (item) => ({
156
+ pk: pkFn(item)
157
+ })
158
+ })
159
+ })
160
+ };
161
+ }
162
+
163
+ export { createIndex, createQueries, defineEntity };
164
+ //# sourceMappingURL=entity.js.map
165
+ //# sourceMappingURL=entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/entity.ts"],"names":[],"mappings":"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","file":"entity.js","sourcesContent":["import type { Table } from \"./table\";\nimport type { StandardSchemaV1 } from \"./standard-schema\";\nimport type { PrimaryKeyWithoutExpression } from \"./conditions\";\nimport type { Index } from \"./types\";\nimport type { QueryBuilder } from \"./builders/query-builder\";\nimport type { Path, PathType } from \"./builders/types\";\nimport type { KeyConditionOperator } from \"./conditions\";\nimport type { ScanBuilder } from \"./builders/scan-builder\";\nimport type { DeleteBuilder } from \"./builders/delete-builder\";\nimport type { UpdateBuilder } from \"./builders/update-builder\";\nimport type { GetBuilder } from \"./builders/get-builder\";\nimport type { PutBuilder } from \"./builders/put-builder\";\n\nexport interface EntityHooks<T extends Record<string, unknown>> {\n beforeCreate?: (data: T) => Promise<T> | T;\n afterCreate?: (data: T) => Promise<T> | T;\n beforeUpdate?: (data: Partial<T>) => Promise<Partial<T>> | Partial<T>;\n afterUpdate?: (data: T) => Promise<T> | T;\n afterGet?: (data: T | undefined) => Promise<T | undefined> | T | undefined;\n}\n\n// Define a type for query functions\nexport type QueryFunction<T extends Record<string, unknown>, I, R = T[]> = (input: I) => R;\n\n// Define a type for the query record\nexport type QueryRecord<T extends Record<string, unknown>> = Record<string, QueryBuilder<T>>;\n\n// Define a type for the result of a query builder\nexport type QueryBuilderResult<T extends Record<string, unknown>> = {\n items: T[];\n lastEvaluatedKey?: Record<string, unknown>;\n};\n\nexport interface EntityConfig<T extends Record<string, unknown>, Q extends QueryRecord<T> = {}> {\n name: string;\n schema: StandardSchemaV1<T>;\n primaryKey: Index;\n indexes?: Record<string, Index>;\n queries?: Q;\n hooks?: EntityHooks<T>;\n}\n\nexport interface PaginationOptions {\n limit?: number;\n lastEvaluatedKey?: Record<string, any>;\n}\n\nexport interface PaginatedResult<T> {\n items: T[];\n lastEvaluatedKey?: Record<string, any>;\n hasMore: boolean;\n}\n\nexport interface EntityRepository<T extends Record<string, unknown>, Q extends QueryRecord<T> = {}> {\n _hooks?: EntityHooks<T>;\n create: (data: T) => PutBuilder<T>;\n upsert: (data: T) => PutBuilder<T>;\n get: (key: PrimaryKeyWithoutExpression) => GetBuilder<T>;\n update: (key: PrimaryKeyWithoutExpression, data: Partial<T>) => UpdateBuilder<T>;\n delete: (key: PrimaryKeyWithoutExpression) => DeleteBuilder;\n query: Q;\n // Add a method to create a query builder\n queryBuilder: (key: PrimaryKeyWithoutExpression) => QueryBuilder<T, any>;\n // Add a method to find entities by their attributes\n findBy: <K extends keyof T>(attribute: K, value: T[K], options?: PaginationOptions) => any; // Returns ScanBuilder<T>\n // Add a method to scan all entities\n scan: (options?: PaginationOptions) => ScanBuilder<T>;\n}\n\nexport function defineEntity<T extends Record<string, unknown>, Q extends QueryRecord<T> = {}>(\n config: EntityConfig<T, Q>,\n) {\n return {\n createRepository: (table: Table): EntityRepository<T, Q> => {\n // Create a repository with hooks attached\n const repository = {\n _hooks: config.hooks,\n create: (data: T) => {\n // We need to handle the async operations when the consumer calls execute\n const builder = table.create(data);\n\n // Wrap the builder's execute method to apply validation only (no hooks)\n const originalExecute = builder.execute;\n builder.execute = async () => {\n // Validate data against schema\n const validationResult = await config.schema[\"~standard\"].validate(data);\n if (\"issues\" in validationResult && validationResult.issues) {\n throw new Error(`Validation failed: ${validationResult.issues.map((i) => i.message).join(\", \")}`);\n }\n\n // Update the item in the builder with the validated data\n Object.assign(builder, { item: validationResult.value });\n\n // Execute the builder\n const result = await originalExecute.call(builder);\n if (!result) {\n throw new Error(\"Failed to create item\");\n }\n\n return result;\n };\n\n return builder;\n },\n\n upsert: (data: T) => {\n // We need to handle the async operations when the consumer calls execute\n const builder = table.put(data);\n\n // Wrap the builder's execute method to apply validation only (no hooks)\n const originalExecute = builder.execute;\n builder.execute = async () => {\n // Validate data against schema\n const validationResult = await config.schema[\"~standard\"].validate(data);\n if (\"issues\" in validationResult && validationResult.issues) {\n throw new Error(`Validation failed: ${validationResult.issues.map((i) => i.message).join(\", \")}`);\n }\n\n // Update the item in the builder with the validated data\n Object.assign(builder, { item: validationResult.value });\n\n // Execute the builder\n const result = await originalExecute.call(builder);\n if (!result) {\n throw new Error(\"Failed to upsert item\");\n }\n\n return result;\n };\n\n return builder;\n },\n\n get: (key: PrimaryKeyWithoutExpression) => {\n const builder = table.get<T>(key);\n\n // Wrap the builder's execute method to apply afterGet hook\n const originalExecute = builder.execute;\n\n builder.execute = async () => {\n const result = await originalExecute.call(builder);\n const item = result?.item;\n\n // Apply afterGet hook if exists\n return {\n item: config.hooks?.afterGet ? await config.hooks.afterGet(item as T) : item,\n };\n };\n\n return builder;\n },\n\n update: (key: PrimaryKeyWithoutExpression, data: Partial<T>) => {\n // Create a builder without hooks\n const builder = table.update<T>(key);\n\n // Wrap the builder's execute method to handle errors\n const originalExecute = builder.execute;\n builder.execute = async () => {\n const result = await originalExecute.call(builder);\n if (!result?.item) {\n throw new Error(\"Failed to update item\");\n }\n\n // Return the item without applying hooks\n return result.item as T;\n };\n\n return builder;\n },\n\n delete: (key: PrimaryKeyWithoutExpression) => {\n return table.delete(key);\n },\n\n query: {} as Q,\n\n // Add a method to create a query builder\n queryBuilder: (key: PrimaryKeyWithoutExpression) => {\n // Convert PrimaryKeyWithoutExpression to PrimaryKey\n const primaryKey = {\n pk: key.pk,\n ...(key.sk ? { sk: (op: KeyConditionOperator) => op.eq(key.sk as string) } : {}),\n };\n return table.query<T>(primaryKey);\n },\n\n // Implement findBy method to find entities by their attributes\n findBy: <K extends keyof T>(attribute: K, value: T[K], options?: PaginationOptions) => {\n // Use scan with a filter to find entities by attribute\n const scanBuilder = table.scan<T>().filter((op) => op.eq(attribute as string as Path<T>, value as any));\n\n // Apply pagination options if provided\n if (options?.limit) {\n scanBuilder.limit(options.limit);\n }\n\n if (options?.lastEvaluatedKey) {\n scanBuilder.startFrom(options.lastEvaluatedKey);\n }\n\n // Return the builder without applying hooks\n return scanBuilder;\n },\n\n // Implement scan method to scan all entities\n scan: (options?: PaginationOptions) => {\n // Create a scan builder\n const scanBuilder = table.scan<T>();\n\n // Apply pagination options if provided\n if (options?.limit) {\n scanBuilder.limit(options.limit);\n }\n\n if (options?.lastEvaluatedKey) {\n scanBuilder.startFrom(options.lastEvaluatedKey);\n }\n\n // Return the builder without applying hooks\n return scanBuilder;\n },\n };\n\n // Bind query functions to the repository after it's fully defined\n if (config.queries) {\n for (const [key, queryFn] of Object.entries(config.queries)) {\n // biome-ignore lint/suspicious/noExplicitAny: Let the magic happen\n (repository.query as any)[key] = queryFn.bind(repository);\n }\n }\n\n return repository;\n },\n };\n}\n\nexport function createQueries<T extends Record<string, unknown>>() {\n return {\n input: <I, R = QueryBuilderResult<T>>(schema: StandardSchemaV1<I>) => ({\n // biome-ignore lint/complexity/noBannedTypes: <explanation>\n query: <Q extends QueryRecord<T> = {}>(\n // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n handler: (params: { input: I; entity: EntityRepository<T, Q> }) => QueryBuilder<T, any>,\n ): QueryFunction<T, I, R> => {\n // Return a function that will be bound to the repository when it's created\n const queryFn = async function (this: EntityRepository<T, Q>, input: I): Promise<R> {\n // Validate input against schema\n const validationResult = await schema[\"~standard\"].validate(input);\n if (\"issues\" in validationResult && validationResult.issues) {\n throw new Error(`Validation failed: ${validationResult.issues.map((i) => i.message).join(\", \")}`);\n }\n\n // Use 'this' as the entity repository (will be bound when the repository is created)\n const queryBuilder = handler({ input: validationResult.value, entity: this });\n\n // Execute the query\n const result = await queryBuilder.execute();\n\n // Apply afterGet hook to each item if exists\n let items = result.items;\n if (this._hooks?.afterGet) {\n const processedItems = await Promise.all(\n // biome-ignore lint/style/noNonNullAssertion: The method will exit as it's confirmed above\n items.map(async (item) => await this._hooks!.afterGet!(item as T)),\n );\n items = processedItems.filter((item): item is NonNullable<typeof item> => item !== null) as T[];\n }\n\n // Return the full result with processed items\n return {\n items,\n lastEvaluatedKey: result.lastEvaluatedKey,\n } as unknown as R;\n };\n\n return queryFn as unknown as QueryFunction<T, I, R>;\n },\n }),\n };\n}\n\nexport interface IndexDefinition<T extends Record<string, unknown>> extends Index {\n name: string;\n generateKey: (item: T) => { pk: string; sk?: string };\n}\n\nexport function createIndex<T extends Record<string, unknown>>() {\n return {\n partitionKey: <P extends (item: T) => string>(pkFn: P) => ({\n sortKey: <S extends (item: T) => string>(skFn: S) =>\n ({\n name: \"custom\",\n partitionKey: \"pk\",\n sortKey: \"sk\",\n generateKey: (item: T) => ({\n pk: pkFn(item),\n sk: skFn(item),\n }),\n }) as IndexDefinition<T>,\n\n // Allow creating an index with only a partition key\n withoutSortKey: () =>\n ({\n name: \"custom\",\n partitionKey: \"pk\",\n generateKey: (item: T) => ({\n pk: pkFn(item),\n }),\n }) as IndexDefinition<T>,\n }),\n };\n}\n"]}