@opensaas/stack-core 0.1.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 (95) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/README.md +447 -0
  3. package/dist/access/engine.d.ts +73 -0
  4. package/dist/access/engine.d.ts.map +1 -0
  5. package/dist/access/engine.js +244 -0
  6. package/dist/access/engine.js.map +1 -0
  7. package/dist/access/field-transforms.d.ts +47 -0
  8. package/dist/access/field-transforms.d.ts.map +1 -0
  9. package/dist/access/field-transforms.js +2 -0
  10. package/dist/access/field-transforms.js.map +1 -0
  11. package/dist/access/index.d.ts +3 -0
  12. package/dist/access/index.d.ts.map +1 -0
  13. package/dist/access/index.js +2 -0
  14. package/dist/access/index.js.map +1 -0
  15. package/dist/access/types.d.ts +83 -0
  16. package/dist/access/types.d.ts.map +1 -0
  17. package/dist/access/types.js +2 -0
  18. package/dist/access/types.js.map +1 -0
  19. package/dist/config/index.d.ts +39 -0
  20. package/dist/config/index.d.ts.map +1 -0
  21. package/dist/config/index.js +38 -0
  22. package/dist/config/index.js.map +1 -0
  23. package/dist/config/types.d.ts +413 -0
  24. package/dist/config/types.d.ts.map +1 -0
  25. package/dist/config/types.js +2 -0
  26. package/dist/config/types.js.map +1 -0
  27. package/dist/context/index.d.ts +31 -0
  28. package/dist/context/index.d.ts.map +1 -0
  29. package/dist/context/index.js +524 -0
  30. package/dist/context/index.js.map +1 -0
  31. package/dist/context/nested-operations.d.ts +10 -0
  32. package/dist/context/nested-operations.d.ts.map +1 -0
  33. package/dist/context/nested-operations.js +261 -0
  34. package/dist/context/nested-operations.js.map +1 -0
  35. package/dist/fields/index.d.ts +78 -0
  36. package/dist/fields/index.d.ts.map +1 -0
  37. package/dist/fields/index.js +381 -0
  38. package/dist/fields/index.js.map +1 -0
  39. package/dist/hooks/index.d.ts +58 -0
  40. package/dist/hooks/index.d.ts.map +1 -0
  41. package/dist/hooks/index.js +79 -0
  42. package/dist/hooks/index.js.map +1 -0
  43. package/dist/index.d.ts +11 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +12 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/lib/case-utils.d.ts +49 -0
  48. package/dist/lib/case-utils.d.ts.map +1 -0
  49. package/dist/lib/case-utils.js +68 -0
  50. package/dist/lib/case-utils.js.map +1 -0
  51. package/dist/lib/case-utils.test.d.ts +2 -0
  52. package/dist/lib/case-utils.test.d.ts.map +1 -0
  53. package/dist/lib/case-utils.test.js +101 -0
  54. package/dist/lib/case-utils.test.js.map +1 -0
  55. package/dist/utils/password.d.ts +81 -0
  56. package/dist/utils/password.d.ts.map +1 -0
  57. package/dist/utils/password.js +132 -0
  58. package/dist/utils/password.js.map +1 -0
  59. package/dist/validation/schema.d.ts +17 -0
  60. package/dist/validation/schema.d.ts.map +1 -0
  61. package/dist/validation/schema.js +42 -0
  62. package/dist/validation/schema.js.map +1 -0
  63. package/dist/validation/schema.test.d.ts +2 -0
  64. package/dist/validation/schema.test.d.ts.map +1 -0
  65. package/dist/validation/schema.test.js +143 -0
  66. package/dist/validation/schema.test.js.map +1 -0
  67. package/docs/type-distribution-fix.md +136 -0
  68. package/package.json +48 -0
  69. package/src/access/engine.ts +360 -0
  70. package/src/access/field-transforms.ts +99 -0
  71. package/src/access/index.ts +20 -0
  72. package/src/access/types.ts +103 -0
  73. package/src/config/index.ts +71 -0
  74. package/src/config/types.ts +478 -0
  75. package/src/context/index.ts +814 -0
  76. package/src/context/nested-operations.ts +412 -0
  77. package/src/fields/index.ts +438 -0
  78. package/src/hooks/index.ts +132 -0
  79. package/src/index.ts +62 -0
  80. package/src/lib/case-utils.test.ts +127 -0
  81. package/src/lib/case-utils.ts +74 -0
  82. package/src/utils/password.ts +147 -0
  83. package/src/validation/schema.test.ts +171 -0
  84. package/src/validation/schema.ts +59 -0
  85. package/tests/access-relationships.test.ts +613 -0
  86. package/tests/access.test.ts +499 -0
  87. package/tests/config.test.ts +195 -0
  88. package/tests/context.test.ts +248 -0
  89. package/tests/hooks.test.ts +417 -0
  90. package/tests/password-type-distribution.test.ts +155 -0
  91. package/tests/password-types.test.ts +147 -0
  92. package/tests/password.test.ts +249 -0
  93. package/tsconfig.json +12 -0
  94. package/tsconfig.tsbuildinfo +1 -0
  95. package/vitest.config.ts +27 -0
@@ -0,0 +1,261 @@
1
+ import { checkAccess, filterWritableFields, getRelatedListConfig } from '../access/index.js';
2
+ import { executeResolveInput, executeValidateInput, validateFieldRules, ValidationError, } from '../hooks/index.js';
3
+ import { getDbKey } from '../lib/case-utils.js';
4
+ /**
5
+ * Check if a field config is a relationship field
6
+ */
7
+ function isRelationshipField(fieldConfig) {
8
+ return fieldConfig?.type === 'relationship';
9
+ }
10
+ /**
11
+ * Process nested create operations
12
+ * Applies hooks and access control to each item being created
13
+ */
14
+ async function processNestedCreate(items, relatedListConfig, context, config) {
15
+ const itemsArray = Array.isArray(items) ? items : [items];
16
+ const processedItems = await Promise.all(itemsArray.map(async (item) => {
17
+ // 1. Check create access
18
+ const createAccess = relatedListConfig.access?.operation?.create;
19
+ const accessResult = await checkAccess(createAccess, {
20
+ session: context.session,
21
+ context,
22
+ });
23
+ if (accessResult === false) {
24
+ throw new Error('Access denied: Cannot create related item');
25
+ }
26
+ // 2. Execute resolveInput hook
27
+ let resolvedData = await executeResolveInput(relatedListConfig.hooks, {
28
+ operation: 'create',
29
+ resolvedData: item,
30
+ context,
31
+ });
32
+ // 3. Execute validateInput hook
33
+ await executeValidateInput(relatedListConfig.hooks, {
34
+ operation: 'create',
35
+ resolvedData,
36
+ context,
37
+ });
38
+ // 4. Field validation
39
+ const validation = validateFieldRules(resolvedData, relatedListConfig.fields, 'create');
40
+ if (validation.errors.length > 0) {
41
+ throw new ValidationError(validation.errors, validation.fieldErrors);
42
+ }
43
+ // 5. Filter writable fields
44
+ const filtered = await filterWritableFields(resolvedData, relatedListConfig.fields, 'create', {
45
+ session: context.session,
46
+ context,
47
+ });
48
+ // 6. Recursively process nested operations in this item
49
+ return await processNestedOperations(filtered, relatedListConfig.fields, config, context, 'create');
50
+ }));
51
+ return Array.isArray(items) ? processedItems : processedItems[0];
52
+ }
53
+ /**
54
+ * Process nested connect operations
55
+ * Verifies update access to the items being connected
56
+ */
57
+ async function processNestedConnect(connections, relatedListName, relatedListConfig, context, prisma) {
58
+ const connectionsArray = Array.isArray(connections) ? connections : [connections];
59
+ // Check update access for each item being connected
60
+ for (const connection of connectionsArray) {
61
+ // Access Prisma model dynamically - required because model names are generated at runtime
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ const model = prisma[getDbKey(relatedListName)];
64
+ // Fetch the item to check access
65
+ const item = await model.findUnique({
66
+ where: connection,
67
+ });
68
+ if (!item) {
69
+ throw new Error(`Cannot connect: Item not found`);
70
+ }
71
+ // Check update access (connecting modifies the relationship)
72
+ const updateAccess = relatedListConfig.access?.operation?.update;
73
+ const accessResult = await checkAccess(updateAccess, {
74
+ session: context.session,
75
+ item,
76
+ context,
77
+ });
78
+ if (accessResult === false) {
79
+ throw new Error('Access denied: Cannot connect to this item');
80
+ }
81
+ // If access returns a filter, check if item matches
82
+ if (typeof accessResult === 'object') {
83
+ // Simple field matching
84
+ for (const [key, value] of Object.entries(accessResult)) {
85
+ if (typeof value === 'object' && value !== null && 'equals' in value) {
86
+ if (item[key] !== value.equals) {
87
+ throw new Error('Access denied: Cannot connect to this item');
88
+ }
89
+ }
90
+ else if (item[key] !== value) {
91
+ throw new Error('Access denied: Cannot connect to this item');
92
+ }
93
+ }
94
+ }
95
+ }
96
+ return connections;
97
+ }
98
+ /**
99
+ * Process nested update operations
100
+ * Applies hooks and access control to updates
101
+ */
102
+ async function processNestedUpdate(updates, relatedListName, relatedListConfig, context, config, prisma) {
103
+ const updatesArray = Array.isArray(updates) ? updates : [updates];
104
+ const processedUpdates = await Promise.all(updatesArray.map(async (update) => {
105
+ // Access Prisma model dynamically - required because model names are generated at runtime
106
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
+ const model = prisma[getDbKey(relatedListName)];
108
+ // Fetch the existing item
109
+ const item = await model.findUnique({
110
+ where: update.where,
111
+ });
112
+ if (!item) {
113
+ throw new Error('Cannot update: Item not found');
114
+ }
115
+ // Check update access
116
+ const updateAccess = relatedListConfig.access?.operation?.update;
117
+ const accessResult = await checkAccess(updateAccess, {
118
+ session: context.session,
119
+ item,
120
+ context,
121
+ });
122
+ if (accessResult === false) {
123
+ throw new Error('Access denied: Cannot update related item');
124
+ }
125
+ // Execute resolveInput hook
126
+ const updateData = update.data;
127
+ let resolvedData = await executeResolveInput(relatedListConfig.hooks, {
128
+ operation: 'update',
129
+ resolvedData: updateData,
130
+ item,
131
+ context,
132
+ });
133
+ // Execute validateInput hook
134
+ await executeValidateInput(relatedListConfig.hooks, {
135
+ operation: 'update',
136
+ resolvedData,
137
+ item,
138
+ context,
139
+ });
140
+ // Field validation
141
+ const validation = validateFieldRules(resolvedData, relatedListConfig.fields, 'update');
142
+ if (validation.errors.length > 0) {
143
+ throw new ValidationError(validation.errors, validation.fieldErrors);
144
+ }
145
+ // Filter writable fields
146
+ const filtered = await filterWritableFields(resolvedData, relatedListConfig.fields, 'update', {
147
+ session: context.session,
148
+ item,
149
+ context,
150
+ });
151
+ // Recursively process nested operations
152
+ const processedData = await processNestedOperations(filtered, relatedListConfig.fields, config, context, 'update');
153
+ return {
154
+ where: update.where,
155
+ data: processedData,
156
+ };
157
+ }));
158
+ return Array.isArray(updates) ? processedUpdates : processedUpdates[0];
159
+ }
160
+ /**
161
+ * Process nested connectOrCreate operations
162
+ */
163
+ async function processNestedConnectOrCreate(operations, relatedListName, relatedListConfig, context, config, prisma) {
164
+ const operationsArray = Array.isArray(operations) ? operations : [operations];
165
+ const processedOps = await Promise.all(operationsArray.map(async (op) => {
166
+ // Process the create portion through create hooks
167
+ const opRecord = op;
168
+ const processedCreate = await processNestedCreate(opRecord.create, relatedListConfig, context, config);
169
+ // Check access for the connect portion (try to find existing item)
170
+ try {
171
+ // Access Prisma model dynamically - required because model names are generated at runtime
172
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
173
+ const model = prisma[getDbKey(relatedListName)];
174
+ const existingItem = await model.findUnique({
175
+ where: opRecord.where,
176
+ });
177
+ if (existingItem) {
178
+ // Check update access for connection
179
+ const updateAccess = relatedListConfig.access?.operation?.update;
180
+ const accessResult = await checkAccess(updateAccess, {
181
+ session: context.session,
182
+ item: existingItem,
183
+ context,
184
+ });
185
+ if (accessResult === false) {
186
+ throw new Error('Access denied: Cannot connect to existing item');
187
+ }
188
+ }
189
+ }
190
+ catch {
191
+ // Item doesn't exist, will use create (already processed)
192
+ }
193
+ return {
194
+ where: op.where,
195
+ create: processedCreate,
196
+ };
197
+ }));
198
+ return Array.isArray(operations) ? processedOps : processedOps[0];
199
+ }
200
+ /**
201
+ * Process all nested operations in a data payload
202
+ * Recursively handles relationship fields with nested writes
203
+ */
204
+ export async function processNestedOperations(data, fieldConfigs, config, context, operation, depth = 0) {
205
+ const MAX_DEPTH = 5;
206
+ if (depth >= MAX_DEPTH) {
207
+ return data;
208
+ }
209
+ const processed = {};
210
+ for (const [fieldName, value] of Object.entries(data)) {
211
+ const fieldConfig = fieldConfigs[fieldName];
212
+ // If not a relationship field or no value, pass through
213
+ if (!isRelationshipField(fieldConfig) || value === null || value === undefined) {
214
+ processed[fieldName] = value;
215
+ continue;
216
+ }
217
+ // Get related list config
218
+ const relationshipField = fieldConfig;
219
+ const relatedConfig = getRelatedListConfig(relationshipField.ref, config);
220
+ if (!relatedConfig) {
221
+ processed[fieldName] = value;
222
+ continue;
223
+ }
224
+ const { listName: relatedListName, listConfig: relatedListConfig } = relatedConfig;
225
+ // Process different nested operation types
226
+ const nestedOp = {};
227
+ const valueRecord = value;
228
+ if (valueRecord.create !== undefined) {
229
+ nestedOp.create = await processNestedCreate(valueRecord.create, relatedListConfig, context, config);
230
+ }
231
+ if (valueRecord.connect !== undefined) {
232
+ nestedOp.connect = await processNestedConnect(valueRecord.connect, relatedListName, relatedListConfig, context, context.prisma);
233
+ }
234
+ if (valueRecord.connectOrCreate !== undefined) {
235
+ nestedOp.connectOrCreate = await processNestedConnectOrCreate(valueRecord.connectOrCreate, relatedListName, relatedListConfig, context, config, context.prisma);
236
+ }
237
+ if (valueRecord.update !== undefined) {
238
+ nestedOp.update = await processNestedUpdate(valueRecord.update, relatedListName, relatedListConfig, context, config, context.prisma);
239
+ }
240
+ // For other operations, pass through (disconnect, delete, set, etc.)
241
+ // These will be subject to Prisma's own constraints
242
+ if (valueRecord.disconnect !== undefined) {
243
+ nestedOp.disconnect = valueRecord.disconnect;
244
+ }
245
+ if (valueRecord.delete !== undefined) {
246
+ nestedOp.delete = valueRecord.delete;
247
+ }
248
+ if (valueRecord.deleteMany !== undefined) {
249
+ nestedOp.deleteMany = valueRecord.deleteMany;
250
+ }
251
+ if (valueRecord.set !== undefined) {
252
+ nestedOp.set = valueRecord.set;
253
+ }
254
+ if (valueRecord.updateMany !== undefined) {
255
+ nestedOp.updateMany = valueRecord.updateMany;
256
+ }
257
+ processed[fieldName] = nestedOp;
258
+ }
259
+ return processed;
260
+ }
261
+ //# sourceMappingURL=nested-operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested-operations.js","sourceRoot":"","sources":["../../src/context/nested-operations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ import type { TextField, IntegerField, CheckboxField, TimestampField, PasswordField, SelectField, RelationshipField } from '../config/types.js';
2
+ /**
3
+ * Text field
4
+ */
5
+ export declare function text(options?: Omit<TextField, 'type'>): TextField;
6
+ /**
7
+ * Integer field
8
+ */
9
+ export declare function integer(options?: Omit<IntegerField, 'type'>): IntegerField;
10
+ /**
11
+ * Checkbox (boolean) field
12
+ */
13
+ export declare function checkbox(options?: Omit<CheckboxField, 'type'>): CheckboxField;
14
+ /**
15
+ * Timestamp (DateTime) field
16
+ */
17
+ export declare function timestamp(options?: Omit<TimestampField, 'type'>): TimestampField;
18
+ /**
19
+ * Password field (automatically hashed using bcrypt)
20
+ *
21
+ * **Security Features:**
22
+ * - Passwords are automatically hashed during create/update operations
23
+ * - Uses bcrypt with cost factor 10 (good balance of security and performance)
24
+ * - Already-hashed passwords are not re-hashed (idempotent)
25
+ * - Password values in query results include a `compare()` method for authentication
26
+ *
27
+ * **Usage Example:**
28
+ * ```typescript
29
+ * // In opensaas.config.ts
30
+ * fields: {
31
+ * password: password({
32
+ * validation: { isRequired: true }
33
+ * })
34
+ * }
35
+ *
36
+ * // Creating a user - password is automatically hashed
37
+ * const user = await context.db.user.create({
38
+ * data: {
39
+ * email: 'user@example.com',
40
+ * password: 'plaintextPassword' // Automatically hashed before storage
41
+ * }
42
+ * })
43
+ *
44
+ * // Authenticating - use the compare() method
45
+ * const user = await context.db.user.findUnique({
46
+ * where: { email: 'user@example.com' }
47
+ * })
48
+ *
49
+ * if (user && await user.password.compare('plaintextPassword')) {
50
+ * // Password is correct - login successful
51
+ * }
52
+ * ```
53
+ *
54
+ * **Important Notes:**
55
+ * - Password fields are excluded from read operations by default in access control
56
+ * - Always use the `compare()` method to verify passwords - never compare strings directly
57
+ * - The password field value has type `HashedPassword` which extends string with compare()
58
+ * - Empty strings and undefined values are skipped (not hashed) to allow partial updates
59
+ *
60
+ * **Implementation Details:**
61
+ * - Uses field-level hooks (`resolveInput` and `resolveOutput`) for automatic transformations
62
+ * - The hashing happens via `hooks.resolveInput` during create/update operations
63
+ * - The wrapping happens via `hooks.resolveOutput` during read operations
64
+ * - This pattern allows third-party field types to define their own transformations
65
+ *
66
+ * @param options - Field configuration options
67
+ * @returns Password field configuration
68
+ */
69
+ export declare function password(options?: Omit<PasswordField, 'type'>): PasswordField;
70
+ /**
71
+ * Select field (enum-like)
72
+ */
73
+ export declare function select(options: Omit<SelectField, 'type'>): SelectField;
74
+ /**
75
+ * Relationship field
76
+ */
77
+ export declare function relationship(options: Omit<RelationshipField, 'type'>): RelationshipField;
78
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fields/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,EACX,iBAAiB,EAClB,MAAM,oBAAoB,CAAA;AAa3B;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,SAAS,CAqEjE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,GAAG,YAAY,CA4C1E;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,aAAa,CA2B7E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG,cAAc,CAsChF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,aAAa,CAgG7E;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG,WAAW,CA2CtE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAAG,iBAAiB,CAiBxF"}