@eleven-am/golem-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.
package/dist/naming.js ADDED
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.lcFirst = lcFirst;
7
+ exports.ucFirst = ucFirst;
8
+ exports.findOneFieldName = findOneFieldName;
9
+ exports.findManyFieldName = findManyFieldName;
10
+ exports.createFieldName = createFieldName;
11
+ exports.updateFieldName = updateFieldName;
12
+ exports.deleteFieldName = deleteFieldName;
13
+ exports.updateManyFieldName = updateManyFieldName;
14
+ exports.deleteManyFieldName = deleteManyFieldName;
15
+ exports.eventsFieldName = eventsFieldName;
16
+ const pluralize_1 = __importDefault(require("pluralize"));
17
+ function lcFirst(name) {
18
+ return name.charAt(0).toLowerCase() + name.slice(1);
19
+ }
20
+ function ucFirst(name) {
21
+ return name.charAt(0).toUpperCase() + name.slice(1);
22
+ }
23
+ function pluralOrList(name) {
24
+ const plural = (0, pluralize_1.default)(name);
25
+ return plural === name ? `${name}List` : plural;
26
+ }
27
+ function findOneFieldName(model) {
28
+ return lcFirst(model);
29
+ }
30
+ function findManyFieldName(model) {
31
+ return pluralOrList(lcFirst(model));
32
+ }
33
+ function createFieldName(model) {
34
+ return `create${model}`;
35
+ }
36
+ function updateFieldName(model) {
37
+ return `update${model}`;
38
+ }
39
+ function deleteFieldName(model) {
40
+ return `delete${model}`;
41
+ }
42
+ function updateManyFieldName(model) {
43
+ return `updateMany${pluralOrList(model)}`;
44
+ }
45
+ function deleteManyFieldName(model) {
46
+ return `deleteMany${pluralOrList(model)}`;
47
+ }
48
+ function eventsFieldName(model) {
49
+ return `${lcFirst(model)}Events`;
50
+ }
@@ -0,0 +1,24 @@
1
+ import { GolemAction } from './authorization';
2
+ import { DatamodelField, DatamodelModel } from './datamodel';
3
+ import { ModelMetadataIndex } from './model-meta';
4
+ export type NestedWriteKind = 'create' | 'createMany' | 'connectOrCreate' | 'connect' | 'disconnect' | 'set' | 'update' | 'updateMany' | 'upsert' | 'delete' | 'deleteMany';
5
+ export interface NestedWriteOperation {
6
+ readonly kind: NestedWriteKind;
7
+ readonly payloads: readonly unknown[];
8
+ readonly createPayloads: readonly Record<string, unknown>[];
9
+ readonly updatePayloads: readonly Record<string, unknown>[];
10
+ }
11
+ export interface NestedRelationWritePlan {
12
+ readonly field: DatamodelField;
13
+ readonly target: DatamodelModel;
14
+ readonly operations: readonly NestedWriteOperation[];
15
+ readonly createPayloads: readonly Record<string, unknown>[];
16
+ readonly updatePayloads: readonly Record<string, unknown>[];
17
+ readonly modelActions: ReadonlySet<GolemAction>;
18
+ readonly addedActions: ReadonlySet<GolemAction>;
19
+ readonly removedActions: ReadonlySet<GolemAction>;
20
+ readonly retainedActions: ReadonlySet<GolemAction>;
21
+ }
22
+ export declare function planNestedWrites(metadata: ModelMetadataIndex, model: DatamodelModel, data: Record<string, unknown>): readonly NestedRelationWritePlan[];
23
+ /** Identifies no-op retained updates so they still receive an instance authorization check. */
24
+ export declare function nestedPlanTargetsRow(plan: NestedRelationWritePlan, row: Record<string, unknown>): boolean;
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.planNestedWrites = planNestedWrites;
4
+ exports.nestedPlanTargetsRow = nestedPlanTargetsRow;
5
+ const KINDS = new Set([
6
+ 'create',
7
+ 'createMany',
8
+ 'connectOrCreate',
9
+ 'connect',
10
+ 'disconnect',
11
+ 'set',
12
+ 'update',
13
+ 'updateMany',
14
+ 'upsert',
15
+ 'delete',
16
+ 'deleteMany',
17
+ ]);
18
+ function array(value) {
19
+ return Array.isArray(value) ? value : [value];
20
+ }
21
+ function record(value) {
22
+ return value && typeof value === 'object' ? value : undefined;
23
+ }
24
+ function operation(kind, value) {
25
+ const payloads = array(value);
26
+ const createPayloads = [];
27
+ const updatePayloads = [];
28
+ for (const payload of payloads) {
29
+ const item = record(payload);
30
+ if (!item)
31
+ continue;
32
+ if (kind === 'create')
33
+ createPayloads.push(item);
34
+ if (kind === 'createMany') {
35
+ for (const data of array(item.data)) {
36
+ const candidate = record(data);
37
+ if (candidate)
38
+ createPayloads.push(candidate);
39
+ }
40
+ }
41
+ if (kind === 'connectOrCreate') {
42
+ const candidate = record(item.create);
43
+ if (candidate)
44
+ createPayloads.push(candidate);
45
+ }
46
+ if (kind === 'update' || kind === 'updateMany') {
47
+ const candidate = record(item.data) ?? item;
48
+ if (candidate)
49
+ updatePayloads.push(candidate);
50
+ }
51
+ if (kind === 'upsert') {
52
+ const created = record(item.create);
53
+ const updated = record(item.update);
54
+ if (created)
55
+ createPayloads.push(created);
56
+ if (updated)
57
+ updatePayloads.push(updated);
58
+ }
59
+ }
60
+ return Object.freeze({ kind, payloads, createPayloads, updatePayloads });
61
+ }
62
+ function actionSets(operations) {
63
+ const model = new Set();
64
+ const added = new Set();
65
+ const removed = new Set();
66
+ const retained = new Set();
67
+ for (const { kind } of operations) {
68
+ if (kind === 'create' || kind === 'createMany') {
69
+ model.add('create');
70
+ added.add('create');
71
+ }
72
+ else if (kind === 'connectOrCreate') {
73
+ model.add('create');
74
+ model.add('update');
75
+ added.add('create');
76
+ added.add('update');
77
+ }
78
+ else if (kind === 'connect') {
79
+ model.add('update');
80
+ added.add('update');
81
+ }
82
+ else if (kind === 'disconnect' || kind === 'set') {
83
+ model.add('update');
84
+ removed.add('update');
85
+ if (kind === 'set')
86
+ added.add('update');
87
+ }
88
+ else if (kind === 'update' || kind === 'updateMany') {
89
+ model.add('update');
90
+ retained.add('update');
91
+ }
92
+ else if (kind === 'upsert') {
93
+ model.add('create');
94
+ model.add('update');
95
+ added.add('create');
96
+ retained.add('update');
97
+ }
98
+ else if (kind === 'delete' || kind === 'deleteMany') {
99
+ model.add('delete');
100
+ removed.add('delete');
101
+ }
102
+ }
103
+ return { model, added, removed, retained };
104
+ }
105
+ function planNestedWrites(metadata, model, data) {
106
+ const plans = [];
107
+ const relations = metadata.get(model.name)?.relations ?? model.fields.filter((f) => f.kind === 'object');
108
+ for (const field of relations) {
109
+ const envelope = record(data[field.name]);
110
+ const target = metadata.get(field.type)?.model;
111
+ if (!envelope || !target)
112
+ continue;
113
+ const operations = Object.entries(envelope)
114
+ .filter(([key, value]) => KINDS.has(key) && value !== undefined)
115
+ .map(([key, value]) => operation(key, value));
116
+ if (operations.length === 0)
117
+ continue;
118
+ const sets = actionSets(operations);
119
+ plans.push(Object.freeze({
120
+ field,
121
+ target,
122
+ operations,
123
+ createPayloads: operations.flatMap((entry) => entry.createPayloads),
124
+ updatePayloads: operations.flatMap((entry) => entry.updatePayloads),
125
+ modelActions: sets.model,
126
+ addedActions: sets.added,
127
+ removedActions: sets.removed,
128
+ retainedActions: sets.retained,
129
+ }));
130
+ }
131
+ return Object.freeze(plans);
132
+ }
133
+ function compareFilter(value, filter) {
134
+ if (value instanceof Date || filter instanceof Date) {
135
+ const left = value instanceof Date ? value.getTime() : value;
136
+ const right = filter instanceof Date ? filter.getTime() : filter;
137
+ return Object.is(left, right);
138
+ }
139
+ if (!filter || typeof filter !== 'object' || Array.isArray(filter))
140
+ return Object.is(value, filter);
141
+ const operations = filter;
142
+ return Object.entries(operations).every(([operator, expected]) => {
143
+ if (operator === 'equals')
144
+ return Object.is(value, expected);
145
+ if (operator === 'in')
146
+ return Array.isArray(expected) && expected.some((item) => Object.is(value, item));
147
+ if (operator === 'notIn')
148
+ return Array.isArray(expected) && expected.every((item) => !Object.is(value, item));
149
+ if (operator === 'not')
150
+ return !compareFilter(value, expected);
151
+ if (operator === 'lt')
152
+ return value < expected;
153
+ if (operator === 'lte')
154
+ return value <= expected;
155
+ if (operator === 'gt')
156
+ return value > expected;
157
+ if (operator === 'gte')
158
+ return value >= expected;
159
+ if (operator === 'contains')
160
+ return typeof value === 'string' && value.includes(String(expected));
161
+ if (operator === 'startsWith')
162
+ return typeof value === 'string' && value.startsWith(String(expected));
163
+ if (operator === 'endsWith')
164
+ return typeof value === 'string' && value.endsWith(String(expected));
165
+ return false;
166
+ });
167
+ }
168
+ function matchesWhere(row, where) {
169
+ if (!where || typeof where !== 'object' || Array.isArray(where))
170
+ return true;
171
+ return Object.entries(where).every(([key, expected]) => {
172
+ const branches = Array.isArray(expected) ? expected : [expected];
173
+ if (key === 'AND')
174
+ return branches.every((branch) => matchesWhere(row, branch));
175
+ if (key === 'OR')
176
+ return branches.some((branch) => matchesWhere(row, branch));
177
+ if (key === 'NOT')
178
+ return branches.every((branch) => !matchesWhere(row, branch));
179
+ return compareFilter(row[key], expected);
180
+ });
181
+ }
182
+ /** Identifies no-op retained updates so they still receive an instance authorization check. */
183
+ function nestedPlanTargetsRow(plan, row) {
184
+ return plan.operations.some((operation) => {
185
+ if (operation.kind !== 'update' && operation.kind !== 'updateMany' && operation.kind !== 'upsert') {
186
+ return false;
187
+ }
188
+ return operation.payloads.some((payload) => {
189
+ const item = record(payload);
190
+ return item ? matchesWhere(row, item.where) : false;
191
+ });
192
+ });
193
+ }
@@ -0,0 +1,122 @@
1
+ import { AuthorizationProvider } from './authorization';
2
+ import { DatamodelModel } from './datamodel';
3
+ import { HookRegistry } from './hooks';
4
+ import { PrismaSelect } from './select';
5
+ export interface FindOneRequest {
6
+ context?: unknown;
7
+ model: string;
8
+ where: unknown;
9
+ select?: PrismaSelect;
10
+ include?: unknown;
11
+ }
12
+ export interface FindManyRequest {
13
+ context?: unknown;
14
+ model: string;
15
+ where?: unknown;
16
+ orderBy?: unknown;
17
+ take?: number;
18
+ skip?: number;
19
+ cursor?: unknown;
20
+ distinct?: unknown;
21
+ select?: PrismaSelect;
22
+ include?: unknown;
23
+ }
24
+ export interface CreateRequest {
25
+ context?: unknown;
26
+ model: string;
27
+ data: unknown;
28
+ select?: PrismaSelect;
29
+ include?: unknown;
30
+ }
31
+ export interface UpdateRequest {
32
+ context?: unknown;
33
+ model: string;
34
+ where: unknown;
35
+ data: unknown;
36
+ select?: PrismaSelect;
37
+ include?: unknown;
38
+ }
39
+ export interface UpdateManyRequest {
40
+ context?: unknown;
41
+ model: string;
42
+ where?: unknown;
43
+ data: unknown;
44
+ }
45
+ export interface DeleteRequest {
46
+ context?: unknown;
47
+ model: string;
48
+ where: unknown;
49
+ select?: PrismaSelect;
50
+ include?: unknown;
51
+ }
52
+ export interface DeleteManyRequest {
53
+ context?: unknown;
54
+ model: string;
55
+ where?: unknown;
56
+ }
57
+ export interface FindFirstRequest {
58
+ context?: unknown;
59
+ model: string;
60
+ where?: unknown;
61
+ select?: PrismaSelect;
62
+ include?: unknown;
63
+ }
64
+ export interface UpsertRequest {
65
+ context?: unknown;
66
+ model: string;
67
+ where: unknown;
68
+ create: unknown;
69
+ update: unknown;
70
+ select?: PrismaSelect;
71
+ include?: unknown;
72
+ }
73
+ export interface GolemEngineOptions {
74
+ hooks?: HookRegistry;
75
+ takeLimits?: ReadonlyMap<string, number>;
76
+ authorization?: AuthorizationProvider;
77
+ maxDepth?: number;
78
+ checkWriteResults?: boolean;
79
+ checkReadFields?: boolean;
80
+ }
81
+ export interface GolemEngineRef {
82
+ current?: GolemEngine;
83
+ }
84
+ export interface BatchResult {
85
+ count: number;
86
+ }
87
+ export declare class GolemEngine {
88
+ private readonly client;
89
+ private readonly modelsByName;
90
+ private readonly metadata;
91
+ private readonly hooks?;
92
+ private readonly takeLimits;
93
+ private readonly authorization?;
94
+ private readonly maxDepth;
95
+ private readonly checkWriteResults;
96
+ private readonly checkReadFields;
97
+ private readonly authorizationSessions;
98
+ constructor(client: Record<string, any>, models: readonly DatamodelModel[], options?: GolemEngineOptions);
99
+ private verifyContext;
100
+ private pkOf;
101
+ private prepareRead;
102
+ private finishRead;
103
+ private enforced;
104
+ private constraintFor;
105
+ private authorizeNestedWrites;
106
+ private walkNestedWrites;
107
+ private resolveConstrainedTarget;
108
+ private runBefore;
109
+ private runAfter;
110
+ private enforceTakeLimit;
111
+ private delegate;
112
+ private run;
113
+ findOne(request: FindOneRequest): Promise<unknown>;
114
+ findMany(request: FindManyRequest): Promise<unknown[]>;
115
+ findFirst(request: FindFirstRequest): Promise<unknown>;
116
+ create(request: CreateRequest): Promise<unknown>;
117
+ update(request: UpdateRequest): Promise<unknown>;
118
+ updateMany(request: UpdateManyRequest): Promise<BatchResult>;
119
+ delete(request: DeleteRequest): Promise<unknown>;
120
+ upsert(request: UpsertRequest): Promise<unknown>;
121
+ deleteMany(request: DeleteManyRequest): Promise<BatchResult>;
122
+ }