@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,244 @@
1
+ /**
2
+ * Check if access control result is a boolean
3
+ */
4
+ export function isBoolean(value) {
5
+ return typeof value === 'boolean';
6
+ }
7
+ /**
8
+ * Check if access control result is a Prisma filter
9
+ */
10
+ export function isPrismaFilter(value) {
11
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
12
+ }
13
+ /**
14
+ * Parse a relationship ref and get the related list configuration
15
+ * Relationship refs are in the format "ListName.fieldName"
16
+ *
17
+ * @param relationshipRef - The ref string (e.g., "Post.author")
18
+ * @param config - The OpenSaas configuration
19
+ * @returns The related list name and config, or null if not found
20
+ */
21
+ export function getRelatedListConfig(relationshipRef, config) {
22
+ // Parse ref format: "ListName.fieldName"
23
+ const parts = relationshipRef.split('.');
24
+ if (parts.length !== 2) {
25
+ return null;
26
+ }
27
+ const listName = parts[0];
28
+ const listConfig = config.lists[listName];
29
+ if (!listConfig) {
30
+ return null;
31
+ }
32
+ return { listName, listConfig };
33
+ }
34
+ /**
35
+ * Execute an access control function
36
+ */
37
+ export async function checkAccess(accessControl, args) {
38
+ // No access control means deny by default
39
+ if (!accessControl) {
40
+ return false;
41
+ }
42
+ // Execute the access control function
43
+ const result = await accessControl(args);
44
+ return result;
45
+ }
46
+ /**
47
+ * Merge user filter with access control filter
48
+ */
49
+ export function mergeFilters(userFilter, accessFilter) {
50
+ // If access is denied, return null
51
+ if (accessFilter === false) {
52
+ return null;
53
+ }
54
+ // If access is fully granted, use user filter
55
+ if (accessFilter === true) {
56
+ return userFilter || {};
57
+ }
58
+ // Merge access filter with user filter
59
+ if (!userFilter) {
60
+ return accessFilter;
61
+ }
62
+ // Combine filters with AND
63
+ return {
64
+ AND: [accessFilter, userFilter],
65
+ };
66
+ }
67
+ /**
68
+ * Check field-level access for a specific operation
69
+ */
70
+ export async function checkFieldAccess(fieldAccess, operation, args) {
71
+ if (!fieldAccess) {
72
+ return true; // No field access means allow
73
+ }
74
+ const accessControl = fieldAccess[operation];
75
+ if (!accessControl) {
76
+ return true; // No specific access control means allow
77
+ }
78
+ const result = await accessControl(args);
79
+ // If result is false, deny access
80
+ if (result === false) {
81
+ return false;
82
+ }
83
+ // If result is true, allow access
84
+ if (result === true) {
85
+ return true;
86
+ }
87
+ // If result is a filter object, check if the item matches
88
+ // For field-level access, we need to evaluate the filter against the item
89
+ if (typeof result === 'object' && args.item) {
90
+ return matchesFilter(args.item, result);
91
+ }
92
+ // Default to allowing access if we can't determine
93
+ return true;
94
+ }
95
+ /**
96
+ * Simple filter matching for field-level access
97
+ * Checks if an item matches a Prisma-like filter object
98
+ */
99
+ function matchesFilter(item, filter) {
100
+ for (const [key, condition] of Object.entries(filter)) {
101
+ if (typeof condition === 'object' && condition !== null) {
102
+ // Handle nested conditions like { equals: value }
103
+ if ('equals' in condition) {
104
+ if (item[key] !== condition.equals) {
105
+ return false;
106
+ }
107
+ }
108
+ else if ('not' in condition) {
109
+ if (item[key] === condition.not) {
110
+ return false;
111
+ }
112
+ }
113
+ // Add more condition types as needed
114
+ }
115
+ else {
116
+ // Direct equality check
117
+ if (item[key] !== condition) {
118
+ return false;
119
+ }
120
+ }
121
+ }
122
+ return true;
123
+ }
124
+ /**
125
+ * Build Prisma include object with access control filters
126
+ * This allows us to filter relationships at the database level instead of in memory
127
+ */
128
+ export async function buildIncludeWithAccessControl(fieldConfigs, args, config, depth = 0) {
129
+ const MAX_DEPTH = 5;
130
+ if (depth >= MAX_DEPTH) {
131
+ return undefined;
132
+ }
133
+ const include = {};
134
+ let hasRelationships = false;
135
+ for (const [fieldName, fieldConfig] of Object.entries(fieldConfigs)) {
136
+ if (fieldConfig?.type === 'relationship' && 'ref' in fieldConfig && fieldConfig.ref) {
137
+ hasRelationships = true;
138
+ const relatedConfig = getRelatedListConfig(fieldConfig.ref, config);
139
+ if (relatedConfig) {
140
+ // Check query access for the related list
141
+ const queryAccess = relatedConfig.listConfig.access?.operation?.query;
142
+ const accessResult = await checkAccess(queryAccess, {
143
+ session: args.session,
144
+ context: args.context,
145
+ });
146
+ // If access is completely denied, exclude this relationship
147
+ if (accessResult === false) {
148
+ continue;
149
+ }
150
+ // Build the include entry
151
+ const includeEntry = {};
152
+ // If access returns a filter, add it to the where clause
153
+ if (typeof accessResult === 'object') {
154
+ includeEntry.where = accessResult;
155
+ }
156
+ // Recursively build nested includes
157
+ const nestedInclude = await buildIncludeWithAccessControl(relatedConfig.listConfig.fields, args, config, depth + 1);
158
+ if (nestedInclude && Object.keys(nestedInclude).length > 0) {
159
+ includeEntry.include = nestedInclude;
160
+ }
161
+ // Add to include object
162
+ include[fieldName] = Object.keys(includeEntry).length > 0 ? includeEntry : true;
163
+ }
164
+ }
165
+ }
166
+ return hasRelationships ? include : undefined;
167
+ }
168
+ /**
169
+ * Filter fields from an object based on read access
170
+ * Recursively applies access control to nested relationships
171
+ */
172
+ export async function filterReadableFields(item, fieldConfigs, args, config, depth = 0) {
173
+ const filtered = {};
174
+ const MAX_DEPTH = 5; // Prevent infinite recursion
175
+ for (const [fieldName, value] of Object.entries(item)) {
176
+ const fieldConfig = fieldConfigs[fieldName];
177
+ // Always include id, createdAt, updatedAt
178
+ if (['id', 'createdAt', 'updatedAt'].includes(fieldName)) {
179
+ filtered[fieldName] = value;
180
+ continue;
181
+ }
182
+ // Check field access
183
+ const canRead = await checkFieldAccess(fieldConfig?.access, 'read', {
184
+ ...args,
185
+ item,
186
+ });
187
+ if (!canRead) {
188
+ continue;
189
+ }
190
+ // Handle relationship fields - recursively filter fields within related items
191
+ // Note: Access control filtering is now done at database level via buildIncludeWithAccessControl
192
+ // This only handles field-level access (hiding sensitive fields)
193
+ if (config &&
194
+ fieldConfig?.type === 'relationship' &&
195
+ 'ref' in fieldConfig &&
196
+ fieldConfig.ref &&
197
+ value !== null &&
198
+ value !== undefined &&
199
+ depth < MAX_DEPTH) {
200
+ const relatedConfig = getRelatedListConfig(fieldConfig.ref, config);
201
+ if (relatedConfig) {
202
+ // For many relationships (arrays) - recursively filter fields in each item
203
+ if (Array.isArray(value)) {
204
+ filtered[fieldName] = await Promise.all(value.map((relatedItem) => filterReadableFields(relatedItem, relatedConfig.listConfig.fields, args, config, depth + 1)));
205
+ }
206
+ // For single relationships (objects) - recursively filter fields
207
+ else if (typeof value === 'object') {
208
+ filtered[fieldName] = await filterReadableFields(value, relatedConfig.listConfig.fields, args, config, depth + 1);
209
+ }
210
+ }
211
+ else {
212
+ // Related config not found, include the value as-is
213
+ filtered[fieldName] = value;
214
+ }
215
+ }
216
+ else {
217
+ // Non-relationship field or no config provided
218
+ filtered[fieldName] = value;
219
+ }
220
+ }
221
+ return filtered;
222
+ }
223
+ /**
224
+ * Filter fields from input data based on write access (create/update)
225
+ */
226
+ export async function filterWritableFields(data, fieldConfigs, operation, args) {
227
+ const filtered = {};
228
+ for (const [fieldName, value] of Object.entries(data)) {
229
+ const fieldConfig = fieldConfigs[fieldName];
230
+ // Skip system fields
231
+ if (['id', 'createdAt', 'updatedAt'].includes(fieldName)) {
232
+ continue;
233
+ }
234
+ // Check field access
235
+ const canWrite = await checkFieldAccess(fieldConfig?.access, operation, {
236
+ ...args,
237
+ });
238
+ if (canWrite) {
239
+ filtered[fieldName] = value;
240
+ }
241
+ }
242
+ return filtered;
243
+ }
244
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/access/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import type { OpenSaasConfig, FieldConfig } from '../config/types.js';
2
+ import type { HashedPassword } from '../utils/password.js';
3
+ /**
4
+ * Extract the return type of a field's afterOperation hook
5
+ * If the field has an afterOperation hook, infer its return type
6
+ * Otherwise, use the original type
7
+ */
8
+ export type InferFieldReadType<TField extends FieldConfig, TOriginal> = TField extends {
9
+ hooks?: {
10
+ afterOperation?: (...args: any[]) => infer R;
11
+ };
12
+ } ? R extends never ? TOriginal : R : TOriginal;
13
+ /**
14
+ * Transform a Prisma model's field types based on OpenSaas field configs
15
+ * This applies afterOperation hook transformations to field types
16
+ */
17
+ export type TransformModelFields<TModel extends Record<string, any>, TFields extends Record<string, FieldConfig>> = {
18
+ [K in keyof TModel]: K extends keyof TFields ? InferFieldReadType<TFields[K], TModel[K]> : TModel[K];
19
+ };
20
+ /**
21
+ * Get the field configs for a specific list from the OpenSaas config
22
+ */
23
+ export type GetListFields<TConfig extends OpenSaasConfig, TListKey extends keyof TConfig['lists']> = TConfig['lists'][TListKey]['fields'];
24
+ /**
25
+ * Transform a Prisma model result based on OpenSaas config
26
+ * Applies field hooks transformations
27
+ */
28
+ export type TransformResult<TConfig extends OpenSaasConfig, TListKey extends keyof TConfig['lists'], TResult> = TResult extends Record<string, any> ? TransformModelFields<TResult, GetListFields<TConfig, TListKey>> : TResult;
29
+ /**
30
+ * Transform a Prisma operation's return type
31
+ * Handles single results, arrays, and null cases
32
+ */
33
+ export type TransformOperationResult<TConfig extends OpenSaasConfig, TListKey extends keyof TConfig['lists'], TResult> = TResult extends Promise<infer R> ? Promise<R extends Array<infer Item> ? Array<TransformResult<TConfig, TListKey, Item>> : R extends null ? null : TransformResult<TConfig, TListKey, R>> : never;
34
+ /**
35
+ * Known field type mappings for afterOperation hooks
36
+ * These provide concrete type hints for common field transformations
37
+ */
38
+ export interface FieldTypeTransforms {
39
+ password: HashedPassword;
40
+ }
41
+ /**
42
+ * Helper to infer field type based on field config type
43
+ */
44
+ export type InferFieldTypeTransform<TField extends FieldConfig> = TField extends {
45
+ type: infer TType;
46
+ } ? TType extends keyof FieldTypeTransforms ? FieldTypeTransforms[TType] : never : never;
47
+ //# sourceMappingURL=field-transforms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-transforms.d.ts","sourceRoot":"","sources":["../../src/access/field-transforms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE1D;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAAC,MAAM,SAAS,WAAW,EAAE,SAAS,IAAI,MAAM,SAAS;IAIrF,KAAK,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,MAAM,CAAC,CAAA;KAAE,CAAA;CACzD,GACG,CAAC,SAAS,KAAK,GACb,SAAS,GACT,CAAC,GACH,SAAS,CAAA;AAEb;;;GAGG;AACH,MAAM,MAAM,oBAAoB,CAI9B,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,IACzC;KACD,CAAC,IAAI,MAAM,MAAM,GAAG,CAAC,SAAS,MAAM,OAAO,GACxC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,GACzC,MAAM,CAAC,CAAC,CAAC;CACd,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,CACvB,OAAO,SAAS,cAAc,EAC9B,QAAQ,SAAS,MAAM,OAAO,CAAC,OAAO,CAAC,IACrC,OAAO,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAA;AAExC;;;GAGG;AACH,MAAM,MAAM,eAAe,CACzB,OAAO,SAAS,cAAc,EAC9B,QAAQ,SAAS,MAAM,OAAO,CAAC,OAAO,CAAC,EACvC,OAAO,IAKP,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC/B,oBAAoB,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,GAC/D,OAAO,CAAA;AAEb;;;GAGG;AACH,MAAM,MAAM,wBAAwB,CAClC,OAAO,SAAS,cAAc,EAC9B,QAAQ,SAAS,MAAM,OAAO,CAAC,OAAO,CAAC,EACvC,OAAO,IAEP,OAAO,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAC5B,OAAO,CACL,CAAC,SAAS,KAAK,CAAC,MAAM,IAAI,CAAC,GACvB,KAAK,CAAC,eAAe,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,GAC/C,CAAC,SAAS,IAAI,GACZ,IAAI,GACJ,eAAe,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,CAC5C,GACD,KAAK,CAAA;AAEX;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,cAAc,CAAA;CAIzB;AAED;;GAEG;AACH,MAAM,MAAM,uBAAuB,CAAC,MAAM,SAAS,WAAW,IAAI,MAAM,SAAS;IAC/E,IAAI,EAAE,MAAM,KAAK,CAAA;CAClB,GACG,KAAK,SAAS,MAAM,mBAAmB,GACrC,mBAAmB,CAAC,KAAK,CAAC,GAC1B,KAAK,GACP,KAAK,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=field-transforms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-transforms.js","sourceRoot":"","sources":["../../src/access/field-transforms.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ export type { AccessControl, FieldAccess, Session, AccessContext, PrismaFilter, AccessControlledDB, PrismaClientLike, } from './types.js';
2
+ export { checkAccess, mergeFilters, checkFieldAccess, filterReadableFields, filterWritableFields, isBoolean, isPrismaFilter, getRelatedListConfig, buildIncludeWithAccessControl, } from './engine.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,WAAW,EACX,OAAO,EACP,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,SAAS,EACT,cAAc,EACd,oBAAoB,EACpB,6BAA6B,GAC9B,MAAM,aAAa,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { checkAccess, mergeFilters, checkFieldAccess, filterReadableFields, filterWritableFields, isBoolean, isPrismaFilter, getRelatedListConfig, buildIncludeWithAccessControl, } from './engine.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/access/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,SAAS,EACT,cAAc,EACd,oBAAoB,EACpB,6BAA6B,GAC9B,MAAM,aAAa,CAAA"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Session type - can be extended by users
3
+ */
4
+ export type Session = {
5
+ userId?: string;
6
+ [key: string]: unknown;
7
+ } | null;
8
+ /**
9
+ * Generic Prisma model delegate type
10
+ */
11
+ export type PrismaModelDelegate = {
12
+ findUnique: (args: unknown) => Promise<unknown>;
13
+ findFirst: (args: unknown) => Promise<unknown>;
14
+ findMany: (args: unknown) => Promise<unknown[]>;
15
+ create: (args: unknown) => Promise<unknown>;
16
+ update: (args: unknown) => Promise<unknown>;
17
+ delete: (args: unknown) => Promise<unknown>;
18
+ count: (args?: unknown) => Promise<number>;
19
+ };
20
+ /**
21
+ * Generic Prisma client type
22
+ * This is intentionally permissive to allow actual PrismaClient types
23
+ * Uses `any` because Prisma generates highly complex client types that are difficult to constrain
24
+ * This type is used as a generic constraint and the actual type safety comes from TPrisma parameter
25
+ */
26
+ export type PrismaClientLike = any;
27
+ /**
28
+ * Map Prisma client to access-controlled database context
29
+ * Preserves Prisma's type information for each model
30
+ */
31
+ export type AccessControlledDB<TPrisma extends PrismaClientLike> = {
32
+ [K in keyof TPrisma]: TPrisma[K] extends {
33
+ findUnique: any;
34
+ findMany: any;
35
+ create: any;
36
+ update: any;
37
+ delete: any;
38
+ count: any;
39
+ } ? {
40
+ findUnique: TPrisma[K]['findUnique'];
41
+ findMany: TPrisma[K]['findMany'];
42
+ create: TPrisma[K]['create'];
43
+ update: TPrisma[K]['update'];
44
+ delete: TPrisma[K]['delete'];
45
+ count: TPrisma[K]['count'];
46
+ } : never;
47
+ } & {
48
+ [key: string]: any;
49
+ };
50
+ /**
51
+ * Context type (simplified for access control)
52
+ */
53
+ export type AccessContext<TPrisma extends PrismaClientLike = PrismaClientLike> = {
54
+ session: Session;
55
+ prisma: TPrisma;
56
+ db: AccessControlledDB<TPrisma>;
57
+ [key: string]: unknown;
58
+ };
59
+ /**
60
+ * Prisma filter type - represents a where clause
61
+ * Uses Partial to allow filtering by any subset of fields
62
+ */
63
+ export type PrismaFilter<T = Record<string, unknown>> = Partial<Record<keyof T, unknown>>;
64
+ /**
65
+ * Access control function type
66
+ * Can return:
67
+ * - boolean: true = allow, false = deny
68
+ * - PrismaFilter: Prisma where clause to filter results
69
+ */
70
+ export type AccessControl<T = Record<string, unknown>> = (args: {
71
+ session: Session;
72
+ item?: T;
73
+ context: AccessContext;
74
+ }) => boolean | PrismaFilter<T> | Promise<boolean | PrismaFilter<T>>;
75
+ /**
76
+ * Field-level access control
77
+ */
78
+ export type FieldAccess = {
79
+ read?: AccessControl;
80
+ create?: AccessControl;
81
+ update?: AccessControl;
82
+ };
83
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/access/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/access/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,39 @@
1
+ import type { OpenSaasConfig, ListConfig, FieldConfig, OperationAccess, Hooks } from './types.js';
2
+ /**
3
+ * Helper function to define configuration with type safety
4
+ */
5
+ export declare function config(config: OpenSaasConfig): OpenSaasConfig;
6
+ /**
7
+ * Helper function to define a list with type safety
8
+ *
9
+ * Accepts raw field configs and transforms them to inject the item type T
10
+ * This enables proper typing in field hooks where item: T
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import type { User } from './.opensaas/types'
15
+ *
16
+ * User: list<User>({
17
+ * fields: {
18
+ * password: password({
19
+ * hooks: {
20
+ * resolveInput: async ({ inputValue, item }) => {
21
+ * // item is typed as User | undefined
22
+ * // inputValue is typed as string | undefined
23
+ * return hashPassword(inputValue)
24
+ * }
25
+ * }
26
+ * })
27
+ * }
28
+ * })
29
+ * ```
30
+ */
31
+ export declare function list<T = any>(config: {
32
+ fields: Record<string, FieldConfig>;
33
+ access?: {
34
+ operation?: OperationAccess<T>;
35
+ };
36
+ hooks?: Hooks<T>;
37
+ }): ListConfig<T>;
38
+ export type { OpenSaasConfig, ListConfig, FieldConfig, BaseFieldConfig, TextField, IntegerField, CheckboxField, TimestampField, PasswordField, SelectField, RelationshipField, OperationAccess, Hooks, FieldHooks, FieldsWithItemType, DatabaseConfig, SessionConfig, UIConfig, ThemeConfig, ThemePreset, ThemeColors, } from './types.js';
39
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAEjG;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAE7D;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,wBAAgB,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE;IACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IACnC,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,CAAA;KAC/B,CAAA;IACD,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;CACjB,GAAG,UAAU,CAAC,CAAC,CAAC,CAIhB;AAGD,YAAY,EACV,cAAc,EACd,UAAU,EACV,WAAW,EACX,eAAe,EACf,SAAS,EACT,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,KAAK,EACL,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,QAAQ,EACR,WAAW,EACX,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAA"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Helper function to define configuration with type safety
3
+ */
4
+ export function config(config) {
5
+ return config;
6
+ }
7
+ /**
8
+ * Helper function to define a list with type safety
9
+ *
10
+ * Accepts raw field configs and transforms them to inject the item type T
11
+ * This enables proper typing in field hooks where item: T
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import type { User } from './.opensaas/types'
16
+ *
17
+ * User: list<User>({
18
+ * fields: {
19
+ * password: password({
20
+ * hooks: {
21
+ * resolveInput: async ({ inputValue, item }) => {
22
+ * // item is typed as User | undefined
23
+ * // inputValue is typed as string | undefined
24
+ * return hashPassword(inputValue)
25
+ * }
26
+ * }
27
+ * })
28
+ * }
29
+ * })
30
+ * ```
31
+ */
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ export function list(config) {
34
+ // At runtime, field configs are unchanged
35
+ // At type level, they're transformed to inject T as the item type
36
+ return config;
37
+ }
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,MAAM,CAAC,MAAsB;IAC3C,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,8DAA8D;AAC9D,MAAM,UAAU,IAAI,CAAU,MAM7B;IACC,0CAA0C;IAC1C,kEAAkE;IAClE,OAAO,MAAuB,CAAA;AAChC,CAAC"}