@cedarjs/internal 5.0.6 → 5.0.7-next.219
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/ast.d.ts +1 -0
- package/dist/ast.d.ts.map +1 -1
- package/dist/ast.js +21 -0
- package/dist/build/api-graphql-transforms.d.ts +34 -0
- package/dist/build/api-graphql-transforms.d.ts.map +1 -0
- package/dist/build/api-graphql-transforms.js +129 -0
- package/dist/build/api.d.ts +2 -0
- package/dist/build/api.d.ts.map +1 -1
- package/dist/build/api.js +202 -39
- package/dist/build/auto-import.d.ts +10 -0
- package/dist/build/auto-import.d.ts.map +1 -0
- package/dist/build/auto-import.js +120 -0
- package/dist/build/directory-named-import.d.ts +2 -0
- package/dist/build/directory-named-import.d.ts.map +1 -0
- package/dist/build/directory-named-import.js +26 -0
- package/dist/build/esbuild-plugin-api-graphql.d.ts +6 -0
- package/dist/build/esbuild-plugin-api-graphql.d.ts.map +1 -0
- package/dist/build/esbuild-plugin-api-graphql.js +80 -0
- package/dist/build/esbuild-plugin-cedar-otel-wrapping.d.ts +13 -0
- package/dist/build/esbuild-plugin-cedar-otel-wrapping.d.ts.map +1 -0
- package/dist/build/esbuild-plugin-cedar-otel-wrapping.js +180 -0
- package/dist/build/esbuild-plugin-handler-als-wrapping.d.ts +4 -0
- package/dist/build/esbuild-plugin-handler-als-wrapping.d.ts.map +1 -0
- package/dist/build/{esbuild-plugin-cedar-context-wrapping.js → esbuild-plugin-handler-als-wrapping.js} +12 -12
- package/dist/build/esm-extensions.d.ts +33 -0
- package/dist/build/esm-extensions.d.ts.map +1 -0
- package/dist/build/esm-extensions.js +37 -0
- package/dist/build/import-dir.d.ts +22 -0
- package/dist/build/import-dir.d.ts.map +1 -0
- package/dist/build/import-dir.js +54 -0
- package/dist/build/job-path-injector.d.ts +15 -0
- package/dist/build/job-path-injector.d.ts.map +1 -0
- package/dist/build/job-path-injector.js +69 -0
- package/dist/build/src-alias.d.ts +14 -0
- package/dist/build/src-alias.d.ts.map +1 -0
- package/dist/build/src-alias.js +17 -0
- package/dist/build/tsconfig-paths.d.ts +2 -0
- package/dist/build/tsconfig-paths.d.ts.map +1 -0
- package/dist/build/tsconfig-paths.js +72 -0
- package/dist/generate/clientPreset.d.ts.map +1 -1
- package/dist/generate/clientPreset.js +10 -3
- package/dist/generate/graphqlSchema.d.ts.map +1 -1
- package/dist/generate/templates/mirror-cell-fragment.d.ts.template +18 -0
- package/dist/generate/templates.d.ts.map +1 -1
- package/dist/generate/templates.js +1 -6
- package/dist/generate/typeDefinitions.d.ts.map +1 -1
- package/dist/generate/typeDefinitions.js +15 -1
- package/dist/gql.d.ts +20 -0
- package/dist/gql.d.ts.map +1 -1
- package/dist/gql.js +24 -0
- package/dist/jsx.d.ts.map +1 -1
- package/dist/routes.d.ts.map +1 -1
- package/package.json +52 -88
- package/dist/build/esbuild-plugin-cedar-context-wrapping.d.ts +0 -4
- package/dist/build/esbuild-plugin-cedar-context-wrapping.d.ts.map +0 -1
- package/dist/cjs/ast.d.ts +0 -26
- package/dist/cjs/ast.d.ts.map +0 -1
- package/dist/cjs/ast.js +0 -201
- package/dist/cjs/build/api.d.ts +0 -7
- package/dist/cjs/build/api.d.ts.map +0 -1
- package/dist/cjs/build/api.js +0 -251
- package/dist/cjs/build/esbuild-plugin-cedar-context-wrapping.d.ts +0 -4
- package/dist/cjs/build/esbuild-plugin-cedar-context-wrapping.d.ts.map +0 -1
- package/dist/cjs/build/esbuild-plugin-cedar-context-wrapping.js +0 -59
- package/dist/cjs/cliLogger.d.ts +0 -11
- package/dist/cjs/cliLogger.d.ts.map +0 -1
- package/dist/cjs/cliLogger.js +0 -36
- package/dist/cjs/dev.d.ts +0 -2
- package/dist/cjs/dev.d.ts.map +0 -1
- package/dist/cjs/dev.js +0 -41
- package/dist/cjs/files.d.ts +0 -56
- package/dist/cjs/files.d.ts.map +0 -1
- package/dist/cjs/files.js +0 -230
- package/dist/cjs/generate/clientPreset.d.ts +0 -17
- package/dist/cjs/generate/clientPreset.d.ts.map +0 -1
- package/dist/cjs/generate/clientPreset.js +0 -84
- package/dist/cjs/generate/generate.d.ts +0 -10
- package/dist/cjs/generate/generate.d.ts.map +0 -1
- package/dist/cjs/generate/generate.js +0 -97
- package/dist/cjs/generate/gqlormSchema.d.ts +0 -113
- package/dist/cjs/generate/gqlormSchema.d.ts.map +0 -1
- package/dist/cjs/generate/gqlormSchema.js +0 -1061
- package/dist/cjs/generate/graphqlCodeGen.d.ts +0 -16
- package/dist/cjs/generate/graphqlCodeGen.d.ts.map +0 -1
- package/dist/cjs/generate/graphqlCodeGen.js +0 -380
- package/dist/cjs/generate/graphqlSchema.d.ts +0 -8
- package/dist/cjs/generate/graphqlSchema.d.ts.map +0 -1
- package/dist/cjs/generate/graphqlSchema.js +0 -157
- package/dist/cjs/generate/plugins/rw-typescript-resolvers/index.d.ts +0 -7
- package/dist/cjs/generate/plugins/rw-typescript-resolvers/index.d.ts.map +0 -1
- package/dist/cjs/generate/plugins/rw-typescript-resolvers/index.js +0 -65
- package/dist/cjs/generate/plugins/rw-typescript-resolvers/visitor.d.ts +0 -10
- package/dist/cjs/generate/plugins/rw-typescript-resolvers/visitor.d.ts.map +0 -1
- package/dist/cjs/generate/plugins/rw-typescript-resolvers/visitor.js +0 -92
- package/dist/cjs/generate/possibleTypes.d.ts +0 -36
- package/dist/cjs/generate/possibleTypes.d.ts.map +0 -1
- package/dist/cjs/generate/possibleTypes.js +0 -106
- package/dist/cjs/generate/templates/all-currentUser.d.ts.template +0 -30
- package/dist/cjs/generate/templates/all-gqlorm.d.ts.template +0 -7
- package/dist/cjs/generate/templates/api-globImports.d.ts.template +0 -9
- package/dist/cjs/generate/templates/api-globalContext.d.ts.template +0 -7
- package/dist/cjs/generate/templates/api-scenarios.d.ts.template +0 -10
- package/dist/cjs/generate/templates/api-test-globals.d.ts.template +0 -5
- package/dist/cjs/generate/templates/mirror-cell.d.ts.template +0 -13
- package/dist/cjs/generate/templates/mirror-directoryNamedModule.d.ts.template +0 -5
- package/dist/cjs/generate/templates/web-routerRoutes.d.ts.template +0 -20
- package/dist/cjs/generate/templates/web-routesPages.d.ts.template +0 -13
- package/dist/cjs/generate/templates/web-test-globals.d.ts.template +0 -7
- package/dist/cjs/generate/templates.d.ts +0 -6
- package/dist/cjs/generate/templates.d.ts.map +0 -1
- package/dist/cjs/generate/templates.js +0 -68
- package/dist/cjs/generate/trustedDocuments.d.ts +0 -3
- package/dist/cjs/generate/trustedDocuments.d.ts.map +0 -1
- package/dist/cjs/generate/trustedDocuments.js +0 -95
- package/dist/cjs/generate/typeDefinitions.d.ts +0 -31
- package/dist/cjs/generate/typeDefinitions.d.ts.map +0 -1
- package/dist/cjs/generate/typeDefinitions.js +0 -372
- package/dist/cjs/generate/types.d.ts +0 -6
- package/dist/cjs/generate/types.d.ts.map +0 -1
- package/dist/cjs/generate/types.js +0 -16
- package/dist/cjs/generate/watch.d.ts +0 -3
- package/dist/cjs/generate/watch.d.ts.map +0 -1
- package/dist/cjs/generate/watch.js +0 -132
- package/dist/cjs/gql.d.ts +0 -14
- package/dist/cjs/gql.d.ts.map +0 -1
- package/dist/cjs/gql.js +0 -108
- package/dist/cjs/index.d.ts +0 -11
- package/dist/cjs/index.d.ts.map +0 -1
- package/dist/cjs/index.js +0 -49
- package/dist/cjs/jsx.d.ts +0 -16
- package/dist/cjs/jsx.d.ts.map +0 -1
- package/dist/cjs/jsx.js +0 -101
- package/dist/cjs/jsxAttributeValue.d.ts +0 -2
- package/dist/cjs/jsxAttributeValue.d.ts.map +0 -1
- package/dist/cjs/jsxAttributeValue.js +0 -129
- package/dist/cjs/package.json +0 -1
- package/dist/cjs/project.d.ts +0 -8
- package/dist/cjs/project.d.ts.map +0 -1
- package/dist/cjs/project.js +0 -82
- package/dist/cjs/routes.d.ts +0 -42
- package/dist/cjs/routes.d.ts.map +0 -1
- package/dist/cjs/routes.js +0 -111
- package/dist/cjs/ts2js.d.ts +0 -40
- package/dist/cjs/ts2js.d.ts.map +0 -1
- package/dist/cjs/ts2js.js +0 -159
- package/dist/cjs/validateSchema.d.ts +0 -14
- package/dist/cjs/validateSchema.d.ts.map +0 -1
- package/dist/cjs/validateSchema.js +0 -195
|
@@ -1,1061 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
var gqlormSchema_exports = {};
|
|
30
|
-
__export(gqlormSchema_exports, {
|
|
31
|
-
buildBackendModelInfo: () => buildBackendModelInfo,
|
|
32
|
-
buildFrontendModelInfo: () => buildFrontendModelInfo,
|
|
33
|
-
buildModelSchema: () => buildModelSchema,
|
|
34
|
-
generateGqlormArtifacts: () => generateGqlormArtifacts,
|
|
35
|
-
generateGqlormBackendContent: () => generateGqlormBackendContent,
|
|
36
|
-
generateWebGqlormModelsContent: () => generateWebGqlormModelsContent,
|
|
37
|
-
getExistingSdlTypeNames: () => getExistingSdlTypeNames,
|
|
38
|
-
mapDmmfTypeToGraphql: () => mapDmmfTypeToGraphql
|
|
39
|
-
});
|
|
40
|
-
module.exports = __toCommonJS(gqlormSchema_exports);
|
|
41
|
-
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
42
|
-
var import_node_path = __toESM(require("node:path"), 1);
|
|
43
|
-
var import_project_config = require("@cedarjs/project-config");
|
|
44
|
-
var import_cedarPluralize = require("@cedarjs/utils/cedarPluralize");
|
|
45
|
-
const INTERNAL_MODEL_NAMES = /* @__PURE__ */ new Set([
|
|
46
|
-
"RW_DataMigration",
|
|
47
|
-
"Cedar_DataMigration"
|
|
48
|
-
]);
|
|
49
|
-
const SENSITIVE_PATTERNS = [
|
|
50
|
-
"password",
|
|
51
|
-
"secret",
|
|
52
|
-
"token",
|
|
53
|
-
"hash",
|
|
54
|
-
"salt",
|
|
55
|
-
"apikey",
|
|
56
|
-
"secretkey",
|
|
57
|
-
"encryptionkey",
|
|
58
|
-
"privatekey"
|
|
59
|
-
];
|
|
60
|
-
const DEFAULT_GQLORM_BACKEND_CONFIG = {
|
|
61
|
-
membershipModel: "Membership",
|
|
62
|
-
membershipModelCamel: "membership",
|
|
63
|
-
membershipUserField: "userId",
|
|
64
|
-
membershipOrganizationField: "organizationId",
|
|
65
|
-
membershipModelExists: false
|
|
66
|
-
};
|
|
67
|
-
function hasDirective(doc, directive) {
|
|
68
|
-
if (!doc) {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
return doc.split("\n").some((line) => line.trimStart().startsWith(`@gqlorm ${directive}`));
|
|
72
|
-
}
|
|
73
|
-
function isSensitiveField(fieldName) {
|
|
74
|
-
const lower = fieldName.toLowerCase();
|
|
75
|
-
return SENSITIVE_PATTERNS.some((pattern) => lower.includes(pattern));
|
|
76
|
-
}
|
|
77
|
-
function emitSensitivityWarning(modelName, fieldName) {
|
|
78
|
-
console.warn(
|
|
79
|
-
`[gqlorm] ${modelName}.${fieldName} was automatically hidden because its name appears sensitive. Add a directive to suppress this warning:
|
|
80
|
-
|
|
81
|
-
/// @gqlorm hide \u2013 to confirm it should stay hidden
|
|
82
|
-
/// @gqlorm show \u2013 to explicitly expose it
|
|
83
|
-
`
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
const DMMF_TYPE_TO_GRAPHQL = {
|
|
87
|
-
String: "String",
|
|
88
|
-
Int: "Int",
|
|
89
|
-
Float: "Float",
|
|
90
|
-
BigInt: "BigInt",
|
|
91
|
-
Boolean: "Boolean",
|
|
92
|
-
DateTime: "DateTime",
|
|
93
|
-
Json: "JSON",
|
|
94
|
-
Decimal: "String",
|
|
95
|
-
Bytes: "String"
|
|
96
|
-
};
|
|
97
|
-
function mapDmmfTypeToGraphql(type, kind) {
|
|
98
|
-
if (kind === "enum") {
|
|
99
|
-
return "String";
|
|
100
|
-
}
|
|
101
|
-
return DMMF_TYPE_TO_GRAPHQL[type] ?? "String";
|
|
102
|
-
}
|
|
103
|
-
function graphqlTypeToTsType(graphqlType) {
|
|
104
|
-
switch (graphqlType) {
|
|
105
|
-
case "Int":
|
|
106
|
-
case "Float":
|
|
107
|
-
case "BigInt":
|
|
108
|
-
return "number";
|
|
109
|
-
case "Boolean":
|
|
110
|
-
return "boolean";
|
|
111
|
-
default:
|
|
112
|
-
return "string";
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function graphqlTypeToTsInterfaceType(graphqlType, isRequired) {
|
|
116
|
-
let tsType;
|
|
117
|
-
switch (graphqlType) {
|
|
118
|
-
case "Int":
|
|
119
|
-
case "Float":
|
|
120
|
-
case "BigInt":
|
|
121
|
-
tsType = "number";
|
|
122
|
-
break;
|
|
123
|
-
case "Boolean":
|
|
124
|
-
tsType = "boolean";
|
|
125
|
-
break;
|
|
126
|
-
case "DateTime":
|
|
127
|
-
tsType = "Date";
|
|
128
|
-
break;
|
|
129
|
-
default:
|
|
130
|
-
tsType = "string";
|
|
131
|
-
}
|
|
132
|
-
return isRequired ? tsType : `${tsType} | null`;
|
|
133
|
-
}
|
|
134
|
-
function dmmfTypeToFrontendTsType(type, kind, isRequired) {
|
|
135
|
-
let tsType;
|
|
136
|
-
if (kind === "enum") {
|
|
137
|
-
tsType = "string";
|
|
138
|
-
} else {
|
|
139
|
-
switch (type) {
|
|
140
|
-
case "String":
|
|
141
|
-
tsType = "string";
|
|
142
|
-
break;
|
|
143
|
-
case "Int":
|
|
144
|
-
case "Float":
|
|
145
|
-
tsType = "number";
|
|
146
|
-
break;
|
|
147
|
-
case "BigInt":
|
|
148
|
-
tsType = "bigint";
|
|
149
|
-
break;
|
|
150
|
-
case "Boolean":
|
|
151
|
-
tsType = "boolean";
|
|
152
|
-
break;
|
|
153
|
-
case "DateTime":
|
|
154
|
-
tsType = "string";
|
|
155
|
-
break;
|
|
156
|
-
case "Json":
|
|
157
|
-
tsType = "unknown";
|
|
158
|
-
break;
|
|
159
|
-
case "Bytes":
|
|
160
|
-
case "Decimal":
|
|
161
|
-
tsType = "string";
|
|
162
|
-
break;
|
|
163
|
-
default:
|
|
164
|
-
tsType = "unknown";
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (isRequired) {
|
|
168
|
-
return tsType;
|
|
169
|
-
}
|
|
170
|
-
return `${tsType} | null`;
|
|
171
|
-
}
|
|
172
|
-
function buildFrontendModelInfo(dmmf) {
|
|
173
|
-
const models = [];
|
|
174
|
-
for (const model of dmmf.datamodel.models) {
|
|
175
|
-
if (INTERNAL_MODEL_NAMES.has(model.name)) {
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
if (hasDirective(model.documentation, "hide")) {
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
const fields = [];
|
|
182
|
-
for (const field of model.fields) {
|
|
183
|
-
if (field.kind !== "scalar" && field.kind !== "enum") {
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
if (hasDirective(field.documentation, "hide")) {
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
const isShown = hasDirective(field.documentation, "show");
|
|
190
|
-
if (!isShown && isSensitiveField(field.name)) {
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
fields.push({
|
|
194
|
-
name: field.name,
|
|
195
|
-
tsType: dmmfTypeToFrontendTsType(
|
|
196
|
-
field.type,
|
|
197
|
-
field.kind,
|
|
198
|
-
field.isRequired
|
|
199
|
-
)
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
if (fields.length > 0) {
|
|
203
|
-
models.push({
|
|
204
|
-
modelName: model.name,
|
|
205
|
-
camelName: model.name.charAt(0).toLowerCase() + model.name.slice(1),
|
|
206
|
-
fields
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return models;
|
|
211
|
-
}
|
|
212
|
-
function generateWebGqlormModelsContent(models) {
|
|
213
|
-
if (models.length === 0) {
|
|
214
|
-
return [
|
|
215
|
-
"// Auto-generated by Cedar \u2014 do not edit",
|
|
216
|
-
"// Regenerated on every codegen run. Source: api/db/schema.prisma",
|
|
217
|
-
"",
|
|
218
|
-
"declare module '@cedarjs/gqlorm/types/orm' {",
|
|
219
|
-
" interface GqlormTypeMap {}",
|
|
220
|
-
"}",
|
|
221
|
-
""
|
|
222
|
-
].join("\n");
|
|
223
|
-
}
|
|
224
|
-
const lines = [
|
|
225
|
-
"// Auto-generated by Cedar \u2014 do not edit",
|
|
226
|
-
"// Regenerated on every codegen run. Source: api/db/schema.prisma",
|
|
227
|
-
"",
|
|
228
|
-
"declare namespace GqlormScalar {"
|
|
229
|
-
];
|
|
230
|
-
for (const model of models) {
|
|
231
|
-
lines.push(` interface ${model.modelName} {`);
|
|
232
|
-
for (const field of model.fields) {
|
|
233
|
-
lines.push(` ${field.name}: ${field.tsType}`);
|
|
234
|
-
}
|
|
235
|
-
lines.push(" }");
|
|
236
|
-
lines.push("");
|
|
237
|
-
}
|
|
238
|
-
lines.push("}");
|
|
239
|
-
lines.push("");
|
|
240
|
-
lines.push("declare module '@cedarjs/gqlorm/types/orm' {");
|
|
241
|
-
lines.push(" interface GqlormTypeMap {");
|
|
242
|
-
lines.push(" models: {");
|
|
243
|
-
for (const model of models) {
|
|
244
|
-
lines.push(` ${model.camelName}: GqlormScalar.${model.modelName}`);
|
|
245
|
-
}
|
|
246
|
-
lines.push(" }");
|
|
247
|
-
lines.push(" }");
|
|
248
|
-
lines.push("}");
|
|
249
|
-
lines.push("");
|
|
250
|
-
return lines.join("\n");
|
|
251
|
-
}
|
|
252
|
-
function buildModelSchema(dmmf) {
|
|
253
|
-
const schema = {};
|
|
254
|
-
for (const model of dmmf.datamodel.models) {
|
|
255
|
-
if (INTERNAL_MODEL_NAMES.has(model.name)) {
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
if (hasDirective(model.documentation, "hide")) {
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
const visibleFields = [];
|
|
262
|
-
for (const field of model.fields) {
|
|
263
|
-
if (field.kind !== "scalar" && field.kind !== "enum") {
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
if (hasDirective(field.documentation, "hide")) {
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
|
-
if (hasDirective(field.documentation, "show")) {
|
|
270
|
-
visibleFields.push(field.name);
|
|
271
|
-
continue;
|
|
272
|
-
}
|
|
273
|
-
if (isSensitiveField(field.name)) {
|
|
274
|
-
emitSensitivityWarning(model.name, field.name);
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
visibleFields.push(field.name);
|
|
278
|
-
}
|
|
279
|
-
if (visibleFields.length > 0) {
|
|
280
|
-
const camelCaseName = model.name.charAt(0).toLowerCase() + model.name.slice(1);
|
|
281
|
-
schema[camelCaseName] = visibleFields;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
return schema;
|
|
285
|
-
}
|
|
286
|
-
function buildBackendModelInfo(dmmf) {
|
|
287
|
-
const models = [];
|
|
288
|
-
for (const model of dmmf.datamodel.models) {
|
|
289
|
-
if (INTERNAL_MODEL_NAMES.has(model.name)) {
|
|
290
|
-
continue;
|
|
291
|
-
}
|
|
292
|
-
if (hasDirective(model.documentation, "hide")) {
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
const fields = [];
|
|
296
|
-
for (const field of model.fields) {
|
|
297
|
-
if (field.kind !== "scalar" && field.kind !== "enum") {
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
|
-
if (hasDirective(field.documentation, "hide")) {
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
const isShown = hasDirective(field.documentation, "show");
|
|
304
|
-
if (!isShown && isSensitiveField(field.name)) {
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
fields.push({
|
|
308
|
-
name: field.name,
|
|
309
|
-
graphqlType: mapDmmfTypeToGraphql(field.type, field.kind),
|
|
310
|
-
isRequired: field.isRequired,
|
|
311
|
-
isId: field.isId,
|
|
312
|
-
hasDefaultValue: field.hasDefaultValue,
|
|
313
|
-
isUpdatedAt: field.isUpdatedAt ?? false
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
if (fields.length > 0) {
|
|
317
|
-
const camelName = model.name.charAt(0).toLowerCase() + model.name.slice(1);
|
|
318
|
-
const pluralName = (0, import_cedarPluralize.pluralize)(camelName);
|
|
319
|
-
models.push({
|
|
320
|
-
modelName: model.name,
|
|
321
|
-
camelName,
|
|
322
|
-
pluralName,
|
|
323
|
-
fields,
|
|
324
|
-
idField: fields.find((f) => f.isId)
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
return models;
|
|
329
|
-
}
|
|
330
|
-
const TYPE_DEF_REGEX = /\btype\s+([A-Z]\w*)\s*\{/g;
|
|
331
|
-
const STRUCTURAL_TYPE_NAMES = /* @__PURE__ */ new Set(["Query", "Mutation", "Subscription"]);
|
|
332
|
-
function getExistingSdlTypeNames(graphqlDir) {
|
|
333
|
-
const typeNames = /* @__PURE__ */ new Set();
|
|
334
|
-
if (!import_node_fs.default.existsSync(graphqlDir)) {
|
|
335
|
-
return typeNames;
|
|
336
|
-
}
|
|
337
|
-
const sdlFiles = import_node_fs.default.readdirSync(graphqlDir).filter((file) => {
|
|
338
|
-
return /\.sdl\.(ts|js)$/.test(file) && !file.startsWith("__gqlorm__");
|
|
339
|
-
});
|
|
340
|
-
for (const file of sdlFiles) {
|
|
341
|
-
const content = import_node_fs.default.readFileSync(import_node_path.default.join(graphqlDir, file), "utf-8");
|
|
342
|
-
let match;
|
|
343
|
-
TYPE_DEF_REGEX.lastIndex = 0;
|
|
344
|
-
while ((match = TYPE_DEF_REGEX.exec(content)) !== null) {
|
|
345
|
-
const name = match[1];
|
|
346
|
-
if (!STRUCTURAL_TYPE_NAMES.has(name)) {
|
|
347
|
-
typeNames.add(name);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return typeNames;
|
|
352
|
-
}
|
|
353
|
-
function generateGqlormBackendContent(models, config = DEFAULT_GQLORM_BACKEND_CONFIG) {
|
|
354
|
-
if (models.length === 0) {
|
|
355
|
-
return "";
|
|
356
|
-
}
|
|
357
|
-
const anyModelNeedsOrgScoping = config.membershipModelExists && models.some(
|
|
358
|
-
(m) => m.camelName !== config.membershipModelCamel && m.fields.some((f) => f.name === config.membershipOrganizationField)
|
|
359
|
-
);
|
|
360
|
-
const anyModelNeedsAuth = models.some((m) => {
|
|
361
|
-
const hasUserField = m.fields.some(
|
|
362
|
-
(f) => f.name === config.membershipUserField
|
|
363
|
-
);
|
|
364
|
-
const hasOrgField = m.fields.some(
|
|
365
|
-
(f) => f.name === config.membershipOrganizationField
|
|
366
|
-
);
|
|
367
|
-
const isMembership = m.camelName === config.membershipModelCamel;
|
|
368
|
-
return hasUserField || hasOrgField && config.membershipModelExists && !isMembership;
|
|
369
|
-
});
|
|
370
|
-
const lines = [
|
|
371
|
-
"// This file is auto-generated by Cedar gqlorm codegen.",
|
|
372
|
-
"// Do not edit \u2014 it will be overwritten on every codegen run.",
|
|
373
|
-
"// To hide a model from gqlorm, add /// @gqlorm hide in schema.prisma.",
|
|
374
|
-
"",
|
|
375
|
-
"import gql from 'graphql-tag'",
|
|
376
|
-
...anyModelNeedsAuth ? [
|
|
377
|
-
"import { AuthenticationError, ForbiddenError } from '@cedarjs/graphql-server'"
|
|
378
|
-
] : [],
|
|
379
|
-
"",
|
|
380
|
-
"// Minimal context type used in auth checks",
|
|
381
|
-
"interface GqlormContext {",
|
|
382
|
-
" currentUser: Record<string, unknown> | null | undefined",
|
|
383
|
-
"}",
|
|
384
|
-
"",
|
|
385
|
-
"// Generated minimal interface \u2014 only visible models and fields, only the",
|
|
386
|
-
"// operations used by this file. No @gqlorm hide models, no sensitive fields.",
|
|
387
|
-
"// Scoped to avoid any dependency on the generated Prisma client path or",
|
|
388
|
-
"// @prisma/client (which is an empty shim in Prisma v7).",
|
|
389
|
-
"interface GqlormDb {"
|
|
390
|
-
];
|
|
391
|
-
for (const model of models) {
|
|
392
|
-
const selectType = model.fields.map((f) => `${f.name}: true`).join("; ");
|
|
393
|
-
lines.push(` ${model.camelName}: {`);
|
|
394
|
-
lines.push(" findMany(args: {");
|
|
395
|
-
lines.push(" where?: Record<string, unknown>");
|
|
396
|
-
lines.push(` select: Partial<{ ${selectType} }>`);
|
|
397
|
-
lines.push(" }): Promise<");
|
|
398
|
-
lines.push(" Array<{");
|
|
399
|
-
for (const field of model.fields) {
|
|
400
|
-
const tsType = graphqlTypeToTsInterfaceType(
|
|
401
|
-
field.graphqlType,
|
|
402
|
-
field.isRequired
|
|
403
|
-
);
|
|
404
|
-
lines.push(` ${field.name}: ${tsType}`);
|
|
405
|
-
}
|
|
406
|
-
lines.push(" }>");
|
|
407
|
-
lines.push(" >");
|
|
408
|
-
if (model.idField) {
|
|
409
|
-
const idTsType = graphqlTypeToTsType(model.idField.graphqlType);
|
|
410
|
-
lines.push(" findUnique(args: {");
|
|
411
|
-
lines.push(` where: { ${model.idField.name}: ${idTsType} }`);
|
|
412
|
-
lines.push(` select: { ${selectType} }`);
|
|
413
|
-
lines.push(" }): Promise<{");
|
|
414
|
-
for (const field of model.fields) {
|
|
415
|
-
const tsType = graphqlTypeToTsInterfaceType(
|
|
416
|
-
field.graphqlType,
|
|
417
|
-
field.isRequired
|
|
418
|
-
);
|
|
419
|
-
lines.push(` ${field.name}: ${tsType}`);
|
|
420
|
-
}
|
|
421
|
-
lines.push(" } | null>");
|
|
422
|
-
}
|
|
423
|
-
if (anyModelNeedsOrgScoping && model.camelName === config.membershipModelCamel) {
|
|
424
|
-
lines.push(" findFirst(args: {");
|
|
425
|
-
lines.push(" where: Record<string, unknown>");
|
|
426
|
-
lines.push(" }): Promise<Record<string, unknown> | null>");
|
|
427
|
-
}
|
|
428
|
-
lines.push(" create(args: {");
|
|
429
|
-
lines.push(" data: Record<string, unknown>");
|
|
430
|
-
lines.push(` select: { ${selectType} }`);
|
|
431
|
-
lines.push(" }): Promise<{");
|
|
432
|
-
for (const field of model.fields) {
|
|
433
|
-
const tsType = graphqlTypeToTsInterfaceType(
|
|
434
|
-
field.graphqlType,
|
|
435
|
-
field.isRequired
|
|
436
|
-
);
|
|
437
|
-
lines.push(` ${field.name}: ${tsType}`);
|
|
438
|
-
}
|
|
439
|
-
lines.push(" }>");
|
|
440
|
-
if (model.idField) {
|
|
441
|
-
const idTsType = graphqlTypeToTsType(model.idField.graphqlType);
|
|
442
|
-
lines.push(" update(args: {");
|
|
443
|
-
lines.push(` where: { ${model.idField.name}: ${idTsType} }`);
|
|
444
|
-
lines.push(" data: Record<string, unknown>");
|
|
445
|
-
lines.push(` select: { ${selectType} }`);
|
|
446
|
-
lines.push(" }): Promise<{");
|
|
447
|
-
for (const field of model.fields) {
|
|
448
|
-
const tsType = graphqlTypeToTsInterfaceType(
|
|
449
|
-
field.graphqlType,
|
|
450
|
-
field.isRequired
|
|
451
|
-
);
|
|
452
|
-
lines.push(` ${field.name}: ${tsType}`);
|
|
453
|
-
}
|
|
454
|
-
lines.push(" }>");
|
|
455
|
-
lines.push(" delete(args: {");
|
|
456
|
-
lines.push(` where: { ${model.idField.name}: ${idTsType} }`);
|
|
457
|
-
lines.push(` select: { ${selectType} }`);
|
|
458
|
-
lines.push(" }): Promise<{");
|
|
459
|
-
for (const field of model.fields) {
|
|
460
|
-
const tsType = graphqlTypeToTsInterfaceType(
|
|
461
|
-
field.graphqlType,
|
|
462
|
-
field.isRequired
|
|
463
|
-
);
|
|
464
|
-
lines.push(` ${field.name}: ${tsType}`);
|
|
465
|
-
}
|
|
466
|
-
lines.push(" }>");
|
|
467
|
-
}
|
|
468
|
-
lines.push(" }");
|
|
469
|
-
}
|
|
470
|
-
const membershipAlreadyInModels = models.some(
|
|
471
|
-
(m) => m.camelName === config.membershipModelCamel
|
|
472
|
-
);
|
|
473
|
-
if (anyModelNeedsOrgScoping && !membershipAlreadyInModels) {
|
|
474
|
-
lines.push(` ${config.membershipModelCamel}: {`);
|
|
475
|
-
lines.push(" findMany(args: {");
|
|
476
|
-
lines.push(" where: Record<string, unknown>");
|
|
477
|
-
lines.push(` select: { ${config.membershipOrganizationField}: true }`);
|
|
478
|
-
lines.push(" }): Promise<");
|
|
479
|
-
lines.push(
|
|
480
|
-
` Array<{ ${config.membershipOrganizationField}: unknown }>`
|
|
481
|
-
);
|
|
482
|
-
lines.push(" >");
|
|
483
|
-
lines.push(" findFirst(args: {");
|
|
484
|
-
lines.push(" where: Record<string, unknown>");
|
|
485
|
-
lines.push(" }): Promise<Record<string, unknown> | null>");
|
|
486
|
-
lines.push(" }");
|
|
487
|
-
}
|
|
488
|
-
lines.push("}");
|
|
489
|
-
lines.push("");
|
|
490
|
-
lines.push("export const schema = gql`");
|
|
491
|
-
for (const model of models) {
|
|
492
|
-
lines.push(` type ${model.modelName} {`);
|
|
493
|
-
for (const field of model.fields) {
|
|
494
|
-
const nullMark = field.isRequired ? "!" : "";
|
|
495
|
-
lines.push(` ${field.name}: ${field.graphqlType}${nullMark}`);
|
|
496
|
-
}
|
|
497
|
-
lines.push(" }");
|
|
498
|
-
lines.push("");
|
|
499
|
-
if (!model.idField) {
|
|
500
|
-
continue;
|
|
501
|
-
}
|
|
502
|
-
const writableFields = model.fields.filter(
|
|
503
|
-
(field) => !field.isId && !field.isUpdatedAt && field.name !== config.membershipUserField
|
|
504
|
-
);
|
|
505
|
-
lines.push(` input Create${model.modelName}Input {`);
|
|
506
|
-
for (const field of writableFields) {
|
|
507
|
-
const isClientRequired = field.isRequired && !field.hasDefaultValue;
|
|
508
|
-
const nullMark = isClientRequired ? "!" : "";
|
|
509
|
-
lines.push(` ${field.name}: ${field.graphqlType}${nullMark}`);
|
|
510
|
-
}
|
|
511
|
-
lines.push(" }");
|
|
512
|
-
lines.push("");
|
|
513
|
-
lines.push(` input Update${model.modelName}Input {`);
|
|
514
|
-
for (const field of writableFields) {
|
|
515
|
-
lines.push(` ${field.name}: ${field.graphqlType}`);
|
|
516
|
-
}
|
|
517
|
-
lines.push(" }");
|
|
518
|
-
lines.push("");
|
|
519
|
-
}
|
|
520
|
-
lines.push(" type Query {");
|
|
521
|
-
for (const model of models) {
|
|
522
|
-
const hasUserField = model.fields.some(
|
|
523
|
-
(f) => f.name === config.membershipUserField
|
|
524
|
-
);
|
|
525
|
-
const hasOrgField = model.fields.some(
|
|
526
|
-
(f) => f.name === config.membershipOrganizationField
|
|
527
|
-
);
|
|
528
|
-
const isMembershipModel = model.camelName === config.membershipModelCamel;
|
|
529
|
-
const needsAuth = hasUserField || hasOrgField && config.membershipModelExists && !isMembershipModel;
|
|
530
|
-
const authDirective = needsAuth ? "@requireAuth" : "@skipAuth";
|
|
531
|
-
lines.push(
|
|
532
|
-
` ${model.pluralName}: [${model.modelName}!]! ${authDirective}`
|
|
533
|
-
);
|
|
534
|
-
if (model.idField) {
|
|
535
|
-
const idNullMark = model.idField.isRequired ? "!" : "";
|
|
536
|
-
lines.push(
|
|
537
|
-
` ${model.camelName}(${model.idField.name}: ${model.idField.graphqlType}${idNullMark}): ${model.modelName} ${authDirective}`
|
|
538
|
-
);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
lines.push(" }");
|
|
542
|
-
lines.push("");
|
|
543
|
-
lines.push(" type Mutation {");
|
|
544
|
-
for (const model of models) {
|
|
545
|
-
if (model.idField) {
|
|
546
|
-
const hasUserField = model.fields.some(
|
|
547
|
-
(f) => f.name === config.membershipUserField
|
|
548
|
-
);
|
|
549
|
-
const hasOrgField = model.fields.some(
|
|
550
|
-
(f) => f.name === config.membershipOrganizationField
|
|
551
|
-
);
|
|
552
|
-
const isMembershipModel = model.camelName === config.membershipModelCamel;
|
|
553
|
-
const needsAuth = hasUserField || hasOrgField && config.membershipModelExists && !isMembershipModel;
|
|
554
|
-
const authDirective = needsAuth ? "@requireAuth" : "@skipAuth";
|
|
555
|
-
const idNullMark = model.idField.isRequired ? "!" : "";
|
|
556
|
-
lines.push(
|
|
557
|
-
` create${model.modelName}(input: Create${model.modelName}Input!): ${model.modelName}! ${authDirective}`
|
|
558
|
-
);
|
|
559
|
-
lines.push(
|
|
560
|
-
` update${model.modelName}(${model.idField.name}: ${model.idField.graphqlType}${idNullMark}, input: Update${model.modelName}Input!): ${model.modelName}! ${authDirective}`
|
|
561
|
-
);
|
|
562
|
-
lines.push(
|
|
563
|
-
` delete${model.modelName}(${model.idField.name}: ${model.idField.graphqlType}${idNullMark}): ${model.modelName}! ${authDirective}`
|
|
564
|
-
);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
lines.push(" }");
|
|
568
|
-
lines.push("`");
|
|
569
|
-
lines.push("");
|
|
570
|
-
lines.push(
|
|
571
|
-
"// db is passed in from graphql.ts by the Babel inject plugin, which imports it"
|
|
572
|
-
);
|
|
573
|
-
lines.push(
|
|
574
|
-
"// from 'src/lib/db' in a context where that alias resolves correctly."
|
|
575
|
-
);
|
|
576
|
-
lines.push("export function createGqlormResolvers(db: GqlormDb) {");
|
|
577
|
-
lines.push(" return {");
|
|
578
|
-
lines.push(" Query: {");
|
|
579
|
-
for (let i = 0; i < models.length; i++) {
|
|
580
|
-
const model = models[i];
|
|
581
|
-
const selectObj = model.fields.map((f) => `${f.name}: true`).join(", ");
|
|
582
|
-
const hasUserField = model.fields.some(
|
|
583
|
-
(f) => f.name === config.membershipUserField
|
|
584
|
-
);
|
|
585
|
-
const hasOrgField = model.fields.some(
|
|
586
|
-
(f) => f.name === config.membershipOrganizationField
|
|
587
|
-
);
|
|
588
|
-
const isMembershipModel = model.camelName === config.membershipModelCamel;
|
|
589
|
-
const useOrgScoping = hasOrgField && config.membershipModelExists && !isMembershipModel;
|
|
590
|
-
lines.push(
|
|
591
|
-
` ${model.pluralName}: async (_root: unknown, _args: unknown, ${hasUserField || useOrgScoping ? "context" : "_context"}: GqlormContext) => {`
|
|
592
|
-
);
|
|
593
|
-
if (hasUserField || useOrgScoping) {
|
|
594
|
-
lines.push(" if (!context.currentUser) {");
|
|
595
|
-
lines.push(
|
|
596
|
-
` throw new AuthenticationError("You don't have permission to do that.")`
|
|
597
|
-
);
|
|
598
|
-
lines.push(" }");
|
|
599
|
-
lines.push(" const currentUserId = context.currentUser['id']");
|
|
600
|
-
lines.push(
|
|
601
|
-
" if (currentUserId === undefined || currentUserId === null) {"
|
|
602
|
-
);
|
|
603
|
-
lines.push(
|
|
604
|
-
` throw new AuthenticationError("Could not determine the current user's ID.")`
|
|
605
|
-
);
|
|
606
|
-
lines.push(" }");
|
|
607
|
-
lines.push(" const where: Record<string, unknown> = {}");
|
|
608
|
-
if (hasUserField) {
|
|
609
|
-
lines.push(" // Scope to the current user");
|
|
610
|
-
lines.push(
|
|
611
|
-
` where['${config.membershipUserField}'] = currentUserId`
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
if (useOrgScoping) {
|
|
615
|
-
lines.push(" // Scope to the current user's organizations");
|
|
616
|
-
lines.push(
|
|
617
|
-
` const memberships = await db.${config.membershipModelCamel}.findMany({`
|
|
618
|
-
);
|
|
619
|
-
lines.push(
|
|
620
|
-
` where: { ${config.membershipUserField}: currentUserId },`
|
|
621
|
-
);
|
|
622
|
-
lines.push(
|
|
623
|
-
` select: { ${config.membershipOrganizationField}: true },`
|
|
624
|
-
);
|
|
625
|
-
lines.push(" })");
|
|
626
|
-
lines.push(
|
|
627
|
-
` const organizationIds = memberships.map((m) => m.${config.membershipOrganizationField})`
|
|
628
|
-
);
|
|
629
|
-
lines.push(
|
|
630
|
-
` where['${config.membershipOrganizationField}'] = { in: organizationIds }`
|
|
631
|
-
);
|
|
632
|
-
}
|
|
633
|
-
lines.push(` return db.${model.camelName}.findMany({`);
|
|
634
|
-
lines.push(" where,");
|
|
635
|
-
lines.push(` select: { ${selectObj} },`);
|
|
636
|
-
lines.push(" })");
|
|
637
|
-
} else {
|
|
638
|
-
lines.push(` return db.${model.camelName}.findMany({`);
|
|
639
|
-
lines.push(` select: { ${selectObj} },`);
|
|
640
|
-
lines.push(" })");
|
|
641
|
-
}
|
|
642
|
-
lines.push(" },");
|
|
643
|
-
if (model.idField) {
|
|
644
|
-
const idFieldName = model.idField.name;
|
|
645
|
-
const tsType = graphqlTypeToTsType(model.idField.graphqlType);
|
|
646
|
-
lines.push(
|
|
647
|
-
` ${model.camelName}: async (_root: unknown, { ${idFieldName} }: { ${idFieldName}: ${tsType} }, ${hasUserField || useOrgScoping ? "context" : "_context"}: GqlormContext) => {`
|
|
648
|
-
);
|
|
649
|
-
if (hasUserField || useOrgScoping) {
|
|
650
|
-
lines.push(" if (!context.currentUser) {");
|
|
651
|
-
lines.push(
|
|
652
|
-
` throw new AuthenticationError("You don't have permission to do that.")`
|
|
653
|
-
);
|
|
654
|
-
lines.push(" }");
|
|
655
|
-
lines.push(" const currentUserId = context.currentUser['id']");
|
|
656
|
-
lines.push(
|
|
657
|
-
" if (currentUserId === undefined || currentUserId === null) {"
|
|
658
|
-
);
|
|
659
|
-
lines.push(
|
|
660
|
-
` throw new AuthenticationError("Could not determine the current user's ID.")`
|
|
661
|
-
);
|
|
662
|
-
lines.push(" }");
|
|
663
|
-
}
|
|
664
|
-
lines.push("");
|
|
665
|
-
lines.push(
|
|
666
|
-
` const record = await db.${model.camelName}.findUnique({`
|
|
667
|
-
);
|
|
668
|
-
lines.push(` where: { ${idFieldName} },`);
|
|
669
|
-
lines.push(` select: { ${selectObj} },`);
|
|
670
|
-
lines.push(" })");
|
|
671
|
-
lines.push("");
|
|
672
|
-
lines.push(" if (!record) {");
|
|
673
|
-
lines.push(" return null");
|
|
674
|
-
lines.push(" }");
|
|
675
|
-
if (hasUserField) {
|
|
676
|
-
lines.push("");
|
|
677
|
-
lines.push(" // Verify the current user owns this record");
|
|
678
|
-
lines.push(
|
|
679
|
-
` if (record.${config.membershipUserField} !== currentUserId) {`
|
|
680
|
-
);
|
|
681
|
-
lines.push(
|
|
682
|
-
` throw new ForbiddenError('Not authorized to access this resource')`
|
|
683
|
-
);
|
|
684
|
-
lines.push(" }");
|
|
685
|
-
}
|
|
686
|
-
if (useOrgScoping) {
|
|
687
|
-
lines.push("");
|
|
688
|
-
lines.push(
|
|
689
|
-
" // Verify the current user belongs to the record's organization"
|
|
690
|
-
);
|
|
691
|
-
lines.push(
|
|
692
|
-
` const membership = await db.${config.membershipModelCamel}.findFirst({`
|
|
693
|
-
);
|
|
694
|
-
lines.push(" where: {");
|
|
695
|
-
lines.push(` ${config.membershipUserField}: currentUserId,`);
|
|
696
|
-
lines.push(
|
|
697
|
-
` ${config.membershipOrganizationField}: record.${config.membershipOrganizationField},`
|
|
698
|
-
);
|
|
699
|
-
lines.push(" },");
|
|
700
|
-
lines.push(" })");
|
|
701
|
-
lines.push(" if (!membership) {");
|
|
702
|
-
lines.push(
|
|
703
|
-
` throw new ForbiddenError('Not authorized to access this resource')`
|
|
704
|
-
);
|
|
705
|
-
lines.push(" }");
|
|
706
|
-
}
|
|
707
|
-
lines.push("");
|
|
708
|
-
lines.push(" return record");
|
|
709
|
-
lines.push(" },");
|
|
710
|
-
}
|
|
711
|
-
if (i < models.length - 1) {
|
|
712
|
-
lines.push("");
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
lines.push(" },");
|
|
716
|
-
lines.push(" Mutation: {");
|
|
717
|
-
for (const model of models) {
|
|
718
|
-
if (!model.idField) {
|
|
719
|
-
continue;
|
|
720
|
-
}
|
|
721
|
-
const selectObj = model.fields.map((f) => `${f.name}: true`).join(", ");
|
|
722
|
-
const hasUserField = model.fields.some(
|
|
723
|
-
(f) => f.name === config.membershipUserField
|
|
724
|
-
);
|
|
725
|
-
const hasOrgField = model.fields.some(
|
|
726
|
-
(f) => f.name === config.membershipOrganizationField
|
|
727
|
-
);
|
|
728
|
-
const isMembershipModel = model.camelName === config.membershipModelCamel;
|
|
729
|
-
const useOrgScoping = hasOrgField && config.membershipModelExists && !isMembershipModel;
|
|
730
|
-
const idFieldName = model.idField.name;
|
|
731
|
-
const idTsType = graphqlTypeToTsType(model.idField.graphqlType);
|
|
732
|
-
lines.push(
|
|
733
|
-
` create${model.modelName}: async (_root: unknown, { input }: { input: Record<string, unknown> }, ${hasUserField || useOrgScoping ? "context" : "_context"}: GqlormContext) => {`
|
|
734
|
-
);
|
|
735
|
-
if (hasUserField || useOrgScoping) {
|
|
736
|
-
lines.push(" if (!context.currentUser) {");
|
|
737
|
-
lines.push(
|
|
738
|
-
` throw new AuthenticationError("You don't have permission to do that.")`
|
|
739
|
-
);
|
|
740
|
-
lines.push(" }");
|
|
741
|
-
lines.push(" const currentUserId = context.currentUser['id']");
|
|
742
|
-
lines.push(
|
|
743
|
-
" if (currentUserId === undefined || currentUserId === null) {"
|
|
744
|
-
);
|
|
745
|
-
lines.push(
|
|
746
|
-
` throw new AuthenticationError("Could not determine the current user's ID.")`
|
|
747
|
-
);
|
|
748
|
-
lines.push(" }");
|
|
749
|
-
}
|
|
750
|
-
lines.push(" const data: Record<string, unknown> = { ...input }");
|
|
751
|
-
if (hasUserField) {
|
|
752
|
-
lines.push(
|
|
753
|
-
` data['${config.membershipUserField}'] = currentUserId`
|
|
754
|
-
);
|
|
755
|
-
}
|
|
756
|
-
if (useOrgScoping) {
|
|
757
|
-
lines.push(
|
|
758
|
-
` const organizationId = data['${config.membershipOrganizationField}']`
|
|
759
|
-
);
|
|
760
|
-
lines.push(
|
|
761
|
-
" if (organizationId === undefined || organizationId === null) {"
|
|
762
|
-
);
|
|
763
|
-
lines.push(
|
|
764
|
-
` throw new ForbiddenError('Organization membership is required for this operation')`
|
|
765
|
-
);
|
|
766
|
-
lines.push(" }");
|
|
767
|
-
lines.push(
|
|
768
|
-
` const membership = await db.${config.membershipModelCamel}.findFirst({`
|
|
769
|
-
);
|
|
770
|
-
lines.push(" where: {");
|
|
771
|
-
lines.push(` ${config.membershipUserField}: currentUserId,`);
|
|
772
|
-
lines.push(
|
|
773
|
-
` ${config.membershipOrganizationField}: organizationId,`
|
|
774
|
-
);
|
|
775
|
-
lines.push(" },");
|
|
776
|
-
lines.push(" })");
|
|
777
|
-
lines.push(" if (!membership) {");
|
|
778
|
-
lines.push(
|
|
779
|
-
` throw new ForbiddenError('Not authorized to access this resource')`
|
|
780
|
-
);
|
|
781
|
-
lines.push(" }");
|
|
782
|
-
}
|
|
783
|
-
lines.push(` return db.${model.camelName}.create({`);
|
|
784
|
-
lines.push(" data,");
|
|
785
|
-
lines.push(` select: { ${selectObj} },`);
|
|
786
|
-
lines.push(" })");
|
|
787
|
-
lines.push(" },");
|
|
788
|
-
lines.push(
|
|
789
|
-
` update${model.modelName}: async (_root: unknown, { ${idFieldName}, input }: { ${idFieldName}: ${idTsType}; input: Record<string, unknown> }, ${hasUserField || useOrgScoping ? "context" : "_context"}: GqlormContext) => {`
|
|
790
|
-
);
|
|
791
|
-
if (hasUserField || useOrgScoping) {
|
|
792
|
-
lines.push(" if (!context.currentUser) {");
|
|
793
|
-
lines.push(
|
|
794
|
-
` throw new AuthenticationError("You don't have permission to do that.")`
|
|
795
|
-
);
|
|
796
|
-
lines.push(" }");
|
|
797
|
-
lines.push(" const currentUserId = context.currentUser['id']");
|
|
798
|
-
lines.push(
|
|
799
|
-
" if (currentUserId === undefined || currentUserId === null) {"
|
|
800
|
-
);
|
|
801
|
-
lines.push(
|
|
802
|
-
` throw new AuthenticationError("Could not determine the current user's ID.")`
|
|
803
|
-
);
|
|
804
|
-
lines.push(" }");
|
|
805
|
-
lines.push(
|
|
806
|
-
` const existingRecord = await db.${model.camelName}.findUnique({`
|
|
807
|
-
);
|
|
808
|
-
lines.push(` where: { ${idFieldName} },`);
|
|
809
|
-
lines.push(` select: { ${selectObj} },`);
|
|
810
|
-
lines.push(" })");
|
|
811
|
-
lines.push(" if (!existingRecord) {");
|
|
812
|
-
lines.push(
|
|
813
|
-
` throw new ForbiddenError('Not authorized to access this resource')`
|
|
814
|
-
);
|
|
815
|
-
lines.push(" }");
|
|
816
|
-
lines.push(" const data: Record<string, unknown> = { ...input }");
|
|
817
|
-
if (hasUserField) {
|
|
818
|
-
lines.push(
|
|
819
|
-
` if (existingRecord.${config.membershipUserField} !== currentUserId) {`
|
|
820
|
-
);
|
|
821
|
-
lines.push(
|
|
822
|
-
` throw new ForbiddenError('Not authorized to access this resource')`
|
|
823
|
-
);
|
|
824
|
-
lines.push(" }");
|
|
825
|
-
lines.push(` delete data['${config.membershipUserField}']`);
|
|
826
|
-
}
|
|
827
|
-
if (useOrgScoping) {
|
|
828
|
-
lines.push(
|
|
829
|
-
` const currentOrganizationId = existingRecord.${config.membershipOrganizationField}`
|
|
830
|
-
);
|
|
831
|
-
lines.push(
|
|
832
|
-
` const currentOrganizationMembership = await db.${config.membershipModelCamel}.findFirst({`
|
|
833
|
-
);
|
|
834
|
-
lines.push(" where: {");
|
|
835
|
-
lines.push(` ${config.membershipUserField}: currentUserId,`);
|
|
836
|
-
lines.push(
|
|
837
|
-
` ${config.membershipOrganizationField}: currentOrganizationId,`
|
|
838
|
-
);
|
|
839
|
-
lines.push(" },");
|
|
840
|
-
lines.push(" })");
|
|
841
|
-
lines.push(" if (!currentOrganizationMembership) {");
|
|
842
|
-
lines.push(
|
|
843
|
-
` throw new ForbiddenError('Not authorized to access this resource')`
|
|
844
|
-
);
|
|
845
|
-
lines.push(" }");
|
|
846
|
-
lines.push(
|
|
847
|
-
` const requestedOrganizationId = input['${config.membershipOrganizationField}'] ?? currentOrganizationId`
|
|
848
|
-
);
|
|
849
|
-
lines.push(
|
|
850
|
-
` const requestedOrganizationMembership = await db.${config.membershipModelCamel}.findFirst({`
|
|
851
|
-
);
|
|
852
|
-
lines.push(" where: {");
|
|
853
|
-
lines.push(` ${config.membershipUserField}: currentUserId,`);
|
|
854
|
-
lines.push(
|
|
855
|
-
` ${config.membershipOrganizationField}: requestedOrganizationId,`
|
|
856
|
-
);
|
|
857
|
-
lines.push(" },");
|
|
858
|
-
lines.push(" })");
|
|
859
|
-
lines.push(" if (!requestedOrganizationMembership) {");
|
|
860
|
-
lines.push(
|
|
861
|
-
` throw new ForbiddenError('Not authorized to access this resource')`
|
|
862
|
-
);
|
|
863
|
-
lines.push(" }");
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
if (!(hasUserField || useOrgScoping)) {
|
|
867
|
-
lines.push(
|
|
868
|
-
` const existingRecord = await db.${model.camelName}.findUnique({`
|
|
869
|
-
);
|
|
870
|
-
lines.push(` where: { ${idFieldName} },`);
|
|
871
|
-
lines.push(` select: { ${selectObj} },`);
|
|
872
|
-
lines.push(" })");
|
|
873
|
-
lines.push(" if (!existingRecord) {");
|
|
874
|
-
lines.push(
|
|
875
|
-
` throw new ForbiddenError('Not authorized to access this resource')`
|
|
876
|
-
);
|
|
877
|
-
lines.push(" }");
|
|
878
|
-
lines.push(" const data: Record<string, unknown> = { ...input }");
|
|
879
|
-
}
|
|
880
|
-
lines.push(` return db.${model.camelName}.update({`);
|
|
881
|
-
lines.push(` where: { ${idFieldName} },`);
|
|
882
|
-
lines.push(" data,");
|
|
883
|
-
lines.push(` select: { ${selectObj} },`);
|
|
884
|
-
lines.push(" })");
|
|
885
|
-
lines.push(" },");
|
|
886
|
-
lines.push(
|
|
887
|
-
` delete${model.modelName}: async (_root: unknown, { ${idFieldName} }: { ${idFieldName}: ${idTsType} }, ${hasUserField || useOrgScoping ? "context" : "_context"}: GqlormContext) => {`
|
|
888
|
-
);
|
|
889
|
-
lines.push(
|
|
890
|
-
` const existingRecord = await db.${model.camelName}.findUnique({`
|
|
891
|
-
);
|
|
892
|
-
lines.push(` where: { ${idFieldName} },`);
|
|
893
|
-
lines.push(` select: { ${selectObj} },`);
|
|
894
|
-
lines.push(" })");
|
|
895
|
-
lines.push(" if (!existingRecord) {");
|
|
896
|
-
lines.push(
|
|
897
|
-
` throw new ForbiddenError('Not authorized to access this resource')`
|
|
898
|
-
);
|
|
899
|
-
lines.push(" }");
|
|
900
|
-
if (hasUserField || useOrgScoping) {
|
|
901
|
-
lines.push(" if (!context.currentUser) {");
|
|
902
|
-
lines.push(
|
|
903
|
-
` throw new AuthenticationError("You don't have permission to do that.")`
|
|
904
|
-
);
|
|
905
|
-
lines.push(" }");
|
|
906
|
-
lines.push(" const currentUserId = context.currentUser['id']");
|
|
907
|
-
lines.push(
|
|
908
|
-
" if (currentUserId === undefined || currentUserId === null) {"
|
|
909
|
-
);
|
|
910
|
-
lines.push(
|
|
911
|
-
` throw new AuthenticationError("Could not determine the current user's ID.")`
|
|
912
|
-
);
|
|
913
|
-
lines.push(" }");
|
|
914
|
-
if (hasUserField) {
|
|
915
|
-
lines.push(
|
|
916
|
-
` if (existingRecord.${config.membershipUserField} !== currentUserId) {`
|
|
917
|
-
);
|
|
918
|
-
lines.push(
|
|
919
|
-
` throw new ForbiddenError('Not authorized to access this resource')`
|
|
920
|
-
);
|
|
921
|
-
lines.push(" }");
|
|
922
|
-
}
|
|
923
|
-
if (useOrgScoping) {
|
|
924
|
-
lines.push(
|
|
925
|
-
` const membership = await db.${config.membershipModelCamel}.findFirst({`
|
|
926
|
-
);
|
|
927
|
-
lines.push(" where: {");
|
|
928
|
-
lines.push(` ${config.membershipUserField}: currentUserId,`);
|
|
929
|
-
lines.push(
|
|
930
|
-
` ${config.membershipOrganizationField}: existingRecord.${config.membershipOrganizationField},`
|
|
931
|
-
);
|
|
932
|
-
lines.push(" },");
|
|
933
|
-
lines.push(" })");
|
|
934
|
-
lines.push(" if (!membership) {");
|
|
935
|
-
lines.push(
|
|
936
|
-
` throw new ForbiddenError('Not authorized to access this resource')`
|
|
937
|
-
);
|
|
938
|
-
lines.push(" }");
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
lines.push(` return db.${model.camelName}.delete({`);
|
|
942
|
-
lines.push(` where: { ${idFieldName} },`);
|
|
943
|
-
lines.push(` select: { ${selectObj} },`);
|
|
944
|
-
lines.push(" })");
|
|
945
|
-
lines.push(" },");
|
|
946
|
-
lines.push("");
|
|
947
|
-
}
|
|
948
|
-
while (lines[lines.length - 1] === "") {
|
|
949
|
-
lines.pop();
|
|
950
|
-
}
|
|
951
|
-
lines.push(" },");
|
|
952
|
-
lines.push(" }");
|
|
953
|
-
lines.push("}");
|
|
954
|
-
lines.push("");
|
|
955
|
-
return lines.join("\n");
|
|
956
|
-
}
|
|
957
|
-
async function generateGqlormArtifacts() {
|
|
958
|
-
if (!(0, import_project_config.getConfig)().experimental?.gqlorm?.enabled) {
|
|
959
|
-
const generatedBase = (0, import_project_config.getPaths)().generated.base;
|
|
960
|
-
const staleFiles = [
|
|
961
|
-
import_node_path.default.join(generatedBase, "gqlorm", "backend.ts"),
|
|
962
|
-
import_node_path.default.join(generatedBase, "gqlorm-schema.json"),
|
|
963
|
-
import_node_path.default.join(generatedBase, "types", "includes", "web-gqlorm-models.d.ts")
|
|
964
|
-
];
|
|
965
|
-
for (const staleFile of staleFiles) {
|
|
966
|
-
if (import_node_fs.default.existsSync(staleFile)) {
|
|
967
|
-
import_node_fs.default.unlinkSync(staleFile);
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
return { files: [], errors: [] };
|
|
971
|
-
}
|
|
972
|
-
const files = [];
|
|
973
|
-
const errors = [];
|
|
974
|
-
try {
|
|
975
|
-
const { schemas } = await (0, import_project_config.getPrismaSchemas)();
|
|
976
|
-
const mod = await import("@prisma/internals");
|
|
977
|
-
const { getDMMF } = mod.default || mod;
|
|
978
|
-
const dmmf = await getDMMF({ datamodel: schemas });
|
|
979
|
-
const paths = (0, import_project_config.getPaths)();
|
|
980
|
-
const generatedBase = paths.generated.base;
|
|
981
|
-
const modelSchema = buildModelSchema(dmmf);
|
|
982
|
-
const frontendModels = buildFrontendModelInfo(dmmf);
|
|
983
|
-
const schemaOutputPath = import_node_path.default.join(generatedBase, "gqlorm-schema.json");
|
|
984
|
-
const webTypesOutputPath = import_node_path.default.join(
|
|
985
|
-
generatedBase,
|
|
986
|
-
"types",
|
|
987
|
-
"includes",
|
|
988
|
-
"web-gqlorm-models.d.ts"
|
|
989
|
-
);
|
|
990
|
-
import_node_fs.default.mkdirSync(import_node_path.default.dirname(schemaOutputPath), { recursive: true });
|
|
991
|
-
import_node_fs.default.writeFileSync(schemaOutputPath, JSON.stringify(modelSchema, null, 2));
|
|
992
|
-
files.push(schemaOutputPath);
|
|
993
|
-
import_node_fs.default.mkdirSync(import_node_path.default.dirname(webTypesOutputPath), { recursive: true });
|
|
994
|
-
import_node_fs.default.writeFileSync(
|
|
995
|
-
webTypesOutputPath,
|
|
996
|
-
generateWebGqlormModelsContent(frontendModels)
|
|
997
|
-
);
|
|
998
|
-
files.push(webTypesOutputPath);
|
|
999
|
-
const backendOutputDir = import_node_path.default.join(generatedBase, "gqlorm");
|
|
1000
|
-
const backendOutputPath = import_node_path.default.join(backendOutputDir, "backend.ts");
|
|
1001
|
-
const graphqlDir = paths.api.graphql;
|
|
1002
|
-
const existingTypes = getExistingSdlTypeNames(graphqlDir);
|
|
1003
|
-
const allModels = buildBackendModelInfo(dmmf);
|
|
1004
|
-
const gqlormConfig = (0, import_project_config.getConfig)().experimental.gqlorm;
|
|
1005
|
-
const membershipModel = gqlormConfig.membershipModel ?? "Membership";
|
|
1006
|
-
const membershipModelCamel = membershipModel.charAt(0).toLowerCase() + membershipModel.slice(1);
|
|
1007
|
-
const membershipUserField = gqlormConfig.membershipUserField ?? "userId";
|
|
1008
|
-
const membershipOrganizationField = gqlormConfig.membershipOrganizationField ?? "organizationId";
|
|
1009
|
-
const membershipModelExists = dmmf.datamodel.models.some(
|
|
1010
|
-
(m) => m.name === membershipModel
|
|
1011
|
-
);
|
|
1012
|
-
const backendConfig = {
|
|
1013
|
-
membershipModel,
|
|
1014
|
-
membershipModelCamel,
|
|
1015
|
-
membershipUserField,
|
|
1016
|
-
membershipOrganizationField,
|
|
1017
|
-
membershipModelExists
|
|
1018
|
-
};
|
|
1019
|
-
const gqlormModels = allModels.filter(
|
|
1020
|
-
(m) => !existingTypes.has(m.modelName)
|
|
1021
|
-
);
|
|
1022
|
-
const anyModelHasOrgField = gqlormModels.some(
|
|
1023
|
-
(m) => m.fields.some((f) => f.name === membershipOrganizationField)
|
|
1024
|
-
);
|
|
1025
|
-
if (anyModelHasOrgField && !membershipModelExists) {
|
|
1026
|
-
console.warn(
|
|
1027
|
-
`[gqlorm] One or more models have a \`${membershipOrganizationField}\` field, but the membership model "${membershipModel}" was not found in the schema. Organization-based access scoping will not be applied for these models. Add a \`${membershipModel}\` model to your schema.prisma or configure \`experimental.gqlorm.membershipModel\` in cedar.toml.`
|
|
1028
|
-
);
|
|
1029
|
-
}
|
|
1030
|
-
if (gqlormModels.length > 0) {
|
|
1031
|
-
const backendContent = generateGqlormBackendContent(
|
|
1032
|
-
gqlormModels,
|
|
1033
|
-
backendConfig
|
|
1034
|
-
);
|
|
1035
|
-
import_node_fs.default.mkdirSync(backendOutputDir, { recursive: true });
|
|
1036
|
-
import_node_fs.default.writeFileSync(backendOutputPath, backendContent);
|
|
1037
|
-
files.push(backendOutputPath);
|
|
1038
|
-
} else {
|
|
1039
|
-
if (import_node_fs.default.existsSync(backendOutputPath)) {
|
|
1040
|
-
import_node_fs.default.unlinkSync(backendOutputPath);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
} catch (error) {
|
|
1044
|
-
errors.push({
|
|
1045
|
-
message: "Failed to generate gqlorm schema artifacts",
|
|
1046
|
-
error
|
|
1047
|
-
});
|
|
1048
|
-
}
|
|
1049
|
-
return { files, errors };
|
|
1050
|
-
}
|
|
1051
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1052
|
-
0 && (module.exports = {
|
|
1053
|
-
buildBackendModelInfo,
|
|
1054
|
-
buildFrontendModelInfo,
|
|
1055
|
-
buildModelSchema,
|
|
1056
|
-
generateGqlormArtifacts,
|
|
1057
|
-
generateGqlormBackendContent,
|
|
1058
|
-
generateWebGqlormModelsContent,
|
|
1059
|
-
getExistingSdlTypeNames,
|
|
1060
|
-
mapDmmfTypeToGraphql
|
|
1061
|
-
});
|