@wictorwilen/cocogen 1.0.0 → 1.0.11

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 (67) hide show
  1. package/README.md +88 -35
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +84 -14
  4. package/dist/cli.js.map +1 -1
  5. package/dist/init/init.d.ts.map +1 -1
  6. package/dist/init/init.js +302 -156
  7. package/dist/init/init.js.map +1 -1
  8. package/dist/init/templates/dotnet/AGENTS.md.ejs +20 -0
  9. package/dist/init/templates/dotnet/Core/ConnectorCore.cs.ejs +602 -0
  10. package/dist/init/templates/dotnet/Datasource/CsvItemSource.cs.ejs +12 -3
  11. package/dist/init/templates/dotnet/Datasource/IItemSource.cs.ejs +10 -4
  12. package/dist/init/templates/dotnet/Generated/Constants.cs.ejs +5 -1
  13. package/dist/init/templates/dotnet/Generated/CsvParser.cs.ejs +61 -0
  14. package/dist/init/templates/dotnet/Generated/FromCsvRow.cs.ejs +11 -3
  15. package/dist/init/templates/dotnet/Generated/ItemPayload.cs.ejs +13 -3
  16. package/dist/init/templates/dotnet/Generated/Model.cs.ejs +5 -22
  17. package/dist/init/templates/dotnet/Generated/PropertyTransformBase.cs.ejs +45 -0
  18. package/dist/init/templates/dotnet/Generated/SchemaPayload.cs.ejs +9 -1
  19. package/dist/init/templates/dotnet/Program.commandline.cs.ejs +76 -278
  20. package/dist/init/templates/dotnet/PropertyTransform.cs.ejs +10 -0
  21. package/dist/init/templates/dotnet/README.md.ejs +6 -7
  22. package/dist/init/templates/dotnet/appsettings.json.ejs +1 -1
  23. package/dist/init/templates/ts/.env.example.ejs +1 -1
  24. package/dist/init/templates/ts/AGENTS.md.ejs +20 -0
  25. package/dist/init/templates/ts/README.md.ejs +6 -7
  26. package/dist/init/templates/ts/src/cli.ts.ejs +85 -173
  27. package/dist/init/templates/ts/src/core/connectorCore.ts.ejs +384 -0
  28. package/dist/init/templates/ts/src/datasource/csvItemSource.ts.ejs +12 -4
  29. package/dist/init/templates/ts/src/datasource/itemSource.ts.ejs +12 -5
  30. package/dist/init/templates/ts/src/generated/constants.ts.ejs +16 -0
  31. package/dist/init/templates/ts/src/generated/csv.ts.ejs +10 -0
  32. package/dist/init/templates/ts/src/generated/fromCsvRow.ts.ejs +12 -37
  33. package/dist/init/templates/ts/src/generated/index.ts.ejs +3 -0
  34. package/dist/init/templates/ts/src/generated/itemPayload.ts.ejs +12 -3
  35. package/dist/init/templates/ts/src/generated/model.ts.ejs +3 -11
  36. package/dist/init/templates/ts/src/generated/propertyTransformBase.ts.ejs +40 -0
  37. package/dist/init/templates/ts/src/generated/schemaPayload.ts.ejs +3 -0
  38. package/dist/init/templates/ts/src/index.ts.ejs +4 -1
  39. package/dist/init/templates/ts/src/propertyTransform.ts.ejs +16 -0
  40. package/dist/ir.d.ts +2 -0
  41. package/dist/ir.d.ts.map +1 -1
  42. package/dist/tsp/init-tsp.d.ts.map +1 -1
  43. package/dist/tsp/init-tsp.js +50 -7
  44. package/dist/tsp/init-tsp.js.map +1 -1
  45. package/dist/tsp/loader.d.ts.map +1 -1
  46. package/dist/tsp/loader.js +23 -9
  47. package/dist/tsp/loader.js.map +1 -1
  48. package/dist/typespec/decorators.d.ts +1 -0
  49. package/dist/typespec/decorators.d.ts.map +1 -1
  50. package/dist/typespec/decorators.js +7 -2
  51. package/dist/typespec/decorators.js.map +1 -1
  52. package/dist/typespec/state.d.ts +2 -0
  53. package/dist/typespec/state.d.ts.map +1 -1
  54. package/dist/typespec/state.js +1 -0
  55. package/dist/typespec/state.js.map +1 -1
  56. package/dist/validate/validator.d.ts.map +1 -1
  57. package/dist/validate/validator.js +127 -14
  58. package/dist/validate/validator.js.map +1 -1
  59. package/package.json +2 -1
  60. package/typespec/main.tsp +6 -2
  61. package/dist/init/templates/dotnet/Generated/PersonEntityDefaults.cs.ejs +0 -48
  62. package/dist/init/templates/dotnet/Generated/PropertyTransforms.cs.ejs +0 -22
  63. package/dist/init/templates/dotnet/PersonEntityOverrides.cs.ejs +0 -49
  64. package/dist/init/templates/dotnet/Program.cs.ejs +0 -487
  65. package/dist/init/templates/ts/src/generated/personEntityDefaults.ts.ejs +0 -33
  66. package/dist/init/templates/ts/src/generated/propertyTransforms.ts.ejs +0 -23
  67. package/dist/init/templates/ts/src/personEntityOverrides.ts.ejs +0 -36
package/dist/init/init.js CHANGED
@@ -1,5 +1,7 @@
1
+ import { readFileSync } from "node:fs";
1
2
  import { access, copyFile, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
2
3
  import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
3
5
  import { loadIrFromTypeSpec } from "../tsp/loader.js";
4
6
  import { validateIr } from "../validate/validator.js";
5
7
  import { renderTemplate } from "./template.js";
@@ -59,10 +61,26 @@ function toCsIdentifier(name) {
59
61
  return pascal || "Item";
60
62
  }
61
63
  function toTsIdentifier(name) {
62
- const cleaned = name.replaceAll(/[^A-Za-z0-9_]/g, "_");
64
+ const parts = name.split(/[^A-Za-z0-9]+/g).filter(Boolean);
65
+ if (parts.length === 0)
66
+ return "Item";
67
+ const pascal = parts.map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join("");
68
+ const sanitized = pascal.replaceAll(/[^A-Za-z0-9_]/g, "_");
69
+ return /^[A-Za-z_]/.test(sanitized) ? sanitized : `_${sanitized}`;
70
+ }
71
+ function toSchemaFolderName(connectionName) {
72
+ const cleaned = (connectionName ?? "").trim();
73
+ if (!cleaned)
74
+ return "Schema";
75
+ const candidate = toCsIdentifier(cleaned);
76
+ return candidate || "Schema";
77
+ }
78
+ function toTsSchemaFolderName(connectionName) {
79
+ const cleaned = (connectionName ?? "").trim();
63
80
  if (!cleaned)
64
- return "item";
65
- return /^[A-Za-z_]/.test(cleaned) ? cleaned : `_${cleaned}`;
81
+ return "schema";
82
+ const candidate = toTsIdentifier(cleaned);
83
+ return candidate || "schema";
66
84
  }
67
85
  function toCsNamespace(projectName) {
68
86
  const cleaned = projectName
@@ -85,7 +103,10 @@ function graphBaseUrl(ir) {
85
103
  }
86
104
  function schemaPayload(ir) {
87
105
  return {
88
- properties: ir.properties.map((p) => ({
106
+ baseType: "microsoft.graph.externalItem",
107
+ properties: ir.properties
108
+ .filter((p) => p.name !== ir.item.contentPropertyName)
109
+ .map((p) => ({
89
110
  name: p.name,
90
111
  type: p.type,
91
112
  labels: p.labels.length > 0 ? p.labels : undefined,
@@ -99,11 +120,43 @@ function schemaPayload(ir) {
99
120
  })),
100
121
  };
101
122
  }
123
+ function getGeneratorVersion() {
124
+ try {
125
+ const dir = path.dirname(fileURLToPath(import.meta.url));
126
+ const candidates = [
127
+ path.resolve(dir, "..", "package.json"),
128
+ path.resolve(dir, "..", "..", "package.json"),
129
+ path.resolve(dir, "..", "..", "..", "package.json"),
130
+ path.resolve(process.cwd(), "package.json"),
131
+ ];
132
+ for (const pkgPath of candidates) {
133
+ try {
134
+ const raw = readFileSync(pkgPath, "utf8");
135
+ const parsed = JSON.parse(raw);
136
+ const name = parsed.name?.trim();
137
+ if (name && name !== "@wictorwilen/cocogen" && name !== "cocogen") {
138
+ continue;
139
+ }
140
+ if (parsed.version && parsed.version.trim().length > 0) {
141
+ return parsed.version.trim();
142
+ }
143
+ }
144
+ catch {
145
+ continue;
146
+ }
147
+ }
148
+ }
149
+ catch {
150
+ // Ignore version lookup errors.
151
+ }
152
+ return "0.0.0";
153
+ }
102
154
  function projectConfigContents(outDir, tspPath, lang) {
103
155
  const rel = path.relative(outDir, path.resolve(tspPath)).replaceAll(path.sep, "/");
104
156
  const config = {
105
157
  lang,
106
158
  tsp: rel || "./schema.tsp",
159
+ cocogenVersion: getGeneratorVersion(),
107
160
  };
108
161
  return JSON.stringify(config, null, 2) + "\n";
109
162
  }
@@ -127,15 +180,22 @@ async function loadProjectConfig(outDir) {
127
180
  if ((parsed.lang !== "ts" && parsed.lang !== "dotnet") || typeof parsed.tsp !== "string") {
128
181
  throw new Error(`Invalid ${COCOGEN_CONFIG_FILE}. Re-run cocogen init or fix the file.`);
129
182
  }
130
- return { config: { lang: parsed.lang, tsp: parsed.tsp } };
183
+ return {
184
+ config: {
185
+ lang: parsed.lang,
186
+ tsp: parsed.tsp,
187
+ ...(parsed.cocogenVersion ? { cocogenVersion: parsed.cocogenVersion } : {}),
188
+ },
189
+ };
131
190
  }
132
- async function writeGeneratedTs(outDir, ir) {
133
- await mkdir(path.join(outDir, "src", "schema"), { recursive: true });
191
+ async function writeGeneratedTs(outDir, ir, schemaFolderName) {
192
+ await mkdir(path.join(outDir, "src", schemaFolderName), { recursive: true });
193
+ await mkdir(path.join(outDir, "src", "core"), { recursive: true });
134
194
  const modelProperties = ir.properties.map((p) => ({
135
195
  name: p.name,
136
196
  tsType: toTsType(p.type),
137
197
  }));
138
- const fromCsvProperties = ir.properties.map((p) => {
198
+ const transformProperties = ir.properties.map((p) => {
139
199
  const parser = (() => {
140
200
  switch (p.type) {
141
201
  case "stringCollection":
@@ -168,40 +228,33 @@ async function writeGeneratedTs(outDir, ir) {
168
228
  source: field.source,
169
229
  }))))
170
230
  : null;
231
+ const isPeopleLabel = p.labels.some((label) => label.startsWith("person"));
232
+ const needsManualEntity = isPeopleLabel && !p.personEntity;
233
+ const noSource = Boolean(p.source.noSource);
234
+ const expression = needsManualEntity
235
+ ? `(() => { throw new Error("Missing @coco.source(..., to) mappings for people entity '${p.name}'. Implement transform in propertyTransform.ts."); })()`
236
+ : personEntity
237
+ ? personEntity
238
+ : noSource
239
+ ? `undefined as unknown as ${toTsType(p.type)}`
240
+ : `${parser}(readSourceValue(row, ${JSON.stringify(p.source.csvHeaders)}))`;
171
241
  return {
172
242
  name: p.name,
173
- csvHeaders: p.source.csvHeaders,
174
243
  parser,
175
- expression: personEntity,
244
+ expression,
176
245
  isCollection: p.type === "stringCollection",
177
- isExplicitSource: p.source.explicit ?? false,
178
246
  transformName: toTsIdentifier(p.name),
179
247
  tsType: toTsType(p.type),
180
248
  };
181
249
  });
182
- const personEntityDefaults = ir.properties
183
- .filter((p) => p.personEntity)
184
- .map((p) => ({
185
- name: p.name,
186
- isCollection: p.type === "stringCollection",
187
- expression: p.type === "stringCollection"
188
- ? buildTsPersonEntityCollectionExpression((p.personEntity?.fields ?? []).map((field) => ({
189
- path: field.path,
190
- source: field.source,
191
- })))
192
- : buildTsPersonEntityExpression((p.personEntity?.fields ?? []).map((field) => ({
193
- path: field.path,
194
- source: field.source,
195
- }))),
196
- }));
197
- const propertyTransforms = fromCsvProperties.filter((p) => !p.expression);
198
- await writeFile(path.join(outDir, "src", "schema", "model.ts"), await renderTemplate("ts/src/generated/model.ts.ejs", {
250
+ await writeFile(path.join(outDir, "src", schemaFolderName, "model.ts"), await renderTemplate("ts/src/generated/model.ts.ejs", {
199
251
  itemTypeName: ir.item.typeName,
200
252
  properties: modelProperties,
201
253
  }), "utf8");
202
- await writeFile(path.join(outDir, "src", "schema", "constants.ts"), await renderTemplate("ts/src/generated/constants.ts.ejs", {
254
+ await writeFile(path.join(outDir, "src", schemaFolderName, "constants.ts"), await renderTemplate("ts/src/generated/constants.ts.ejs", {
203
255
  graphApiVersion: ir.connection.graphApiVersion,
204
256
  contentCategory: ir.connection.contentCategory ?? null,
257
+ connectionName: ir.connection.connectionName ?? null,
205
258
  connectionId: ir.connection.connectionId ?? null,
206
259
  connectionDescription: ir.connection.connectionDescription ?? null,
207
260
  profileSourceWebUrl: ir.connection.profileSource?.webUrl ?? null,
@@ -211,35 +264,26 @@ async function writeGeneratedTs(outDir, ir) {
211
264
  idPropertyName: ir.item.idPropertyName,
212
265
  contentPropertyName: ir.item.contentPropertyName ?? null,
213
266
  }), "utf8");
214
- await writeFile(path.join(outDir, "src", "schema", "schemaPayload.ts"), await renderTemplate("ts/src/generated/schemaPayload.ts.ejs", {
267
+ await writeFile(path.join(outDir, "src", schemaFolderName, "schemaPayload.ts"), await renderTemplate("ts/src/generated/schemaPayload.ts.ejs", {
215
268
  schemaPayloadJson: JSON.stringify(schemaPayload(ir), null, 2),
216
269
  }), "utf8");
217
270
  await writeFile(path.join(outDir, "src", "datasource", "csv.ts"), await renderTemplate("ts/src/generated/csv.ts.ejs", {}), "utf8");
218
- if (propertyTransforms.length > 0) {
219
- await writeFile(path.join(outDir, "src", "schema", "propertyTransforms.ts"), await renderTemplate("ts/src/generated/propertyTransforms.ts.ejs", {
220
- properties: propertyTransforms,
221
- }), "utf8");
222
- }
223
- await writeFile(path.join(outDir, "src", "schema", "fromCsvRow.ts"), await renderTemplate("ts/src/generated/fromCsvRow.ts.ejs", {
224
- properties: fromCsvProperties,
225
- hasPersonEntities: personEntityDefaults.length > 0,
226
- propertyTransforms,
271
+ await writeFile(path.join(outDir, "src", schemaFolderName, "propertyTransformBase.ts"), await renderTemplate("ts/src/generated/propertyTransformBase.ts.ejs", {
272
+ properties: transformProperties,
227
273
  }), "utf8");
228
- if (ir.connection.contentCategory === "people" && personEntityDefaults.length > 0) {
229
- await writeFile(path.join(outDir, "src", "schema", "personEntityDefaults.ts"), await renderTemplate("ts/src/generated/personEntityDefaults.ts.ejs", {
230
- defaults: personEntityDefaults,
231
- }), "utf8");
232
- const overridesPath = path.join(outDir, "src", "schema", "personEntityOverrides.ts");
233
- try {
234
- await access(overridesPath);
235
- }
236
- catch {
237
- await writeFile(overridesPath, await renderTemplate("ts/src/personEntityOverrides.ts.ejs", {
238
- names: personEntityDefaults.map((p) => p.name),
239
- }), "utf8");
240
- }
274
+ const transformOverridesPath = path.join(outDir, "src", schemaFolderName, "propertyTransform.ts");
275
+ try {
276
+ await access(transformOverridesPath);
277
+ }
278
+ catch {
279
+ await writeFile(transformOverridesPath, await renderTemplate("ts/src/propertyTransform.ts.ejs", {}), "utf8");
241
280
  }
281
+ await writeFile(path.join(outDir, "src", schemaFolderName, "fromCsvRow.ts"), await renderTemplate("ts/src/generated/fromCsvRow.ts.ejs", {
282
+ properties: transformProperties,
283
+ itemTypeName: ir.item.typeName,
284
+ }), "utf8");
242
285
  const propertiesObjectLines = ir.properties
286
+ .filter((p) => p.name !== ir.item.contentPropertyName)
243
287
  .flatMap((p) => {
244
288
  const lines = [];
245
289
  const odataType = toOdataCollectionType(p.type);
@@ -253,11 +297,16 @@ async function writeGeneratedTs(outDir, ir) {
253
297
  const contentBlock = ir.item.contentPropertyName
254
298
  ? `,\n content: {\n type: \"text\",\n value: String((item as any)[contentPropertyName ?? \"\"] ?? \"\"),\n }`
255
299
  : "";
256
- await writeFile(path.join(outDir, "src", "schema", "itemPayload.ts"), await renderTemplate("ts/src/generated/itemPayload.ts.ejs", {
300
+ await writeFile(path.join(outDir, "src", schemaFolderName, "itemPayload.ts"), await renderTemplate("ts/src/generated/itemPayload.ts.ejs", {
257
301
  propertiesObjectLines,
258
302
  contentBlock,
303
+ itemTypeName: ir.item.typeName,
304
+ }), "utf8");
305
+ await writeFile(path.join(outDir, "src", schemaFolderName, "index.ts"), await renderTemplate("ts/src/generated/index.ts.ejs", {}), "utf8");
306
+ await writeFile(path.join(outDir, "src", "core", "connectorCore.ts"), await renderTemplate("ts/src/core/connectorCore.ts.ejs", {
307
+ itemTypeName: ir.item.typeName,
308
+ isPeopleConnector: ir.connection.contentCategory === "people",
259
309
  }), "utf8");
260
- await writeFile(path.join(outDir, "src", "schema", "index.ts"), await renderTemplate("ts/src/generated/index.ts.ejs", {}), "utf8");
261
310
  }
262
311
  function toGraphPropertyTypeEnumName(type) {
263
312
  switch (type) {
@@ -361,20 +410,48 @@ function buildObjectTree(fields) {
361
410
  }
362
411
  function buildTsPersonEntityExpression(fields) {
363
412
  const tree = buildObjectTree(fields);
364
- const renderNode = (node) => {
413
+ const indentUnit = " ";
414
+ const renderNode = (node, level) => {
415
+ const indent = indentUnit.repeat(level);
416
+ const childIndent = indentUnit.repeat(level + 1);
365
417
  const entries = Object.entries(node).map(([key, value]) => {
366
418
  if (typeof value === "object" && value && "path" in value) {
367
419
  const field = value;
368
420
  const headers = JSON.stringify(field.source.csvHeaders);
369
- return `${JSON.stringify(key)}: parseString(readSourceValue(row, ${headers}))`;
421
+ return `${childIndent}${JSON.stringify(key)}: parseString(readSourceValue(row, ${headers}))`;
370
422
  }
371
- return `${JSON.stringify(key)}: ${renderNode(value)}`;
423
+ return `${childIndent}${JSON.stringify(key)}: ${renderNode(value, level + 1)}`;
372
424
  });
373
- return `{ ${entries.join(", ")} }`;
425
+ return `{
426
+ ${entries.join(",\n")}
427
+ ${indent}}`;
374
428
  };
375
- return `JSON.stringify(${renderNode(tree)})`;
429
+ const rendered = renderNode(tree, 2);
430
+ return `JSON.stringify(\n${indentUnit.repeat(2)}${rendered}\n${indentUnit.repeat(2)})`;
376
431
  }
377
432
  function buildTsPersonEntityCollectionExpression(fields) {
433
+ const indentUnit = " ";
434
+ const renderNode = (node, level, valueVar) => {
435
+ const indent = indentUnit.repeat(level);
436
+ const childIndent = indentUnit.repeat(level + 1);
437
+ const entries = Object.entries(node).map(([key, value]) => {
438
+ if (typeof value === "object" && value && "path" in value) {
439
+ return `${childIndent}${JSON.stringify(key)}: ${valueVar}`;
440
+ }
441
+ return `${childIndent}${JSON.stringify(key)}: ${renderNode(value, level + 1, valueVar)}`;
442
+ });
443
+ return `{
444
+ ${entries.join(",\n")}
445
+ ${indent}}`;
446
+ };
447
+ if (fields.length === 1) {
448
+ const tree = buildObjectTree(fields);
449
+ const field = fields[0];
450
+ const headers = JSON.stringify(field.source.csvHeaders);
451
+ const rendered = renderNode(tree, 2, "value");
452
+ return `parseStringCollection(readSourceValue(row, ${headers}))
453
+ .map((value) => JSON.stringify(\n${indentUnit.repeat(2)}${rendered}\n${indentUnit.repeat(2)}))`;
454
+ }
378
455
  const tree = buildObjectTree(fields);
379
456
  const fieldVarByPath = new Map();
380
457
  const fieldLines = fields.map((field, index) => {
@@ -383,39 +460,66 @@ function buildTsPersonEntityCollectionExpression(fields) {
383
460
  const headers = JSON.stringify(field.source.csvHeaders);
384
461
  return ` const ${varName} = parseStringCollection(readSourceValue(row, ${headers}));`;
385
462
  });
386
- const renderNode = (node) => {
463
+ const renderNodeMany = (node) => {
387
464
  const entries = Object.entries(node).map(([key, value]) => {
388
465
  if (typeof value === "object" && value && "path" in value) {
389
466
  const field = value;
390
467
  const varName = fieldVarByPath.get(field.path) ?? "";
391
- return `${JSON.stringify(key)}: getValue(${varName}, index)`;
468
+ return `${indentUnit.repeat(2)}${JSON.stringify(key)}: getValue(${varName}, index)`;
392
469
  }
393
- return `${JSON.stringify(key)}: ${renderNode(value)}`;
470
+ return `${indentUnit.repeat(2)}${JSON.stringify(key)}: ${renderNodeMany(value)}`;
394
471
  });
395
- return `{ ${entries.join(", ")} }`;
472
+ return `{
473
+ ${entries.join(",\n")}
474
+ ${indentUnit}}`;
396
475
  };
397
476
  const fieldVars = [...fieldVarByPath.values()].join(", ");
398
477
  const lengthVars = fieldVars
399
478
  ? `const lengths = [${fieldVars}].map((value) => value.length);`
400
479
  : "const lengths = [0];";
401
- return `(() => {\n${fieldLines.join("\n")}\n${lengthVars}\n const maxLen = Math.max(0, ...lengths);\n const getValue = (values: string[], index: number): string => {\n if (values.length === 0) return \"\";\n if (values.length === 1) return values[0] ?? \"\";\n return values[index] ?? \"\";\n };\n const results: string[] = [];\n for (let index = 0; index < maxLen; index++) {\n results.push(JSON.stringify(${renderNode(tree)}));\n }\n return results;\n})()`;
480
+ return `(() => {\n${fieldLines.join("\n")}\n${lengthVars}\n const maxLen = Math.max(0, ...lengths);\n const getValue = (values: string[], index: number): string => {\n if (values.length === 0) return \"\";\n if (values.length === 1) return values[0] ?? \"\";\n return values[index] ?? \"\";\n };\n const results: string[] = [];\n for (let index = 0; index < maxLen; index++) {\n results.push(JSON.stringify(\n ${renderNodeMany(tree)}\n ));\n }\n return results;\n})()`;
402
481
  }
403
482
  function buildCsPersonEntityExpression(fields) {
404
483
  const tree = buildObjectTree(fields);
405
- const renderNode = (node) => {
484
+ const indentUnit = " ";
485
+ const renderNode = (node, level) => {
486
+ const indent = indentUnit.repeat(level);
487
+ const childIndent = indentUnit.repeat(level + 1);
406
488
  const entries = Object.entries(node).map(([key, value]) => {
407
489
  if (typeof value === "object" && value && "path" in value) {
408
490
  const field = value;
409
491
  const headers = `new[] { ${field.source.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
410
- return `[${JSON.stringify(key)}] = ParseString(row, ${headers})`;
492
+ return `${childIndent}[${JSON.stringify(key)}] = CsvParser.ParseString(row, ${headers})`;
411
493
  }
412
- return `[${JSON.stringify(key)}] = ${renderNode(value)}`;
494
+ return `${childIndent}[${JSON.stringify(key)}] = ${renderNode(value, level + 1)}`;
413
495
  });
414
- return `new Dictionary<string, object?> { ${entries.join(", ")} }`;
496
+ return `new Dictionary<string, object?>\n${indent}{\n${entries.join(",\n")}\n${indent}}`;
415
497
  };
416
- return `JsonSerializer.Serialize(${renderNode(tree)})`;
498
+ const rendered = renderNode(tree, 2);
499
+ return `JsonSerializer.Serialize(\n${indentUnit.repeat(2)}${rendered}\n${indentUnit.repeat(2)})`;
417
500
  }
418
501
  function buildCsPersonEntityCollectionExpression(fields) {
502
+ const indentUnit = " ";
503
+ const renderNode = (node, level, valueVar) => {
504
+ const indent = indentUnit.repeat(level);
505
+ const childIndent = indentUnit.repeat(level + 1);
506
+ const entries = Object.entries(node).map(([key, value]) => {
507
+ if (typeof value === "object" && value && "path" in value) {
508
+ return `${childIndent}[${JSON.stringify(key)}] = ${valueVar}`;
509
+ }
510
+ return `${childIndent}[${JSON.stringify(key)}] = ${renderNode(value, level + 1, valueVar)}`;
511
+ });
512
+ return `new Dictionary<string, object?>\n${indent}{\n${entries.join(",\n")}\n${indent}}`;
513
+ };
514
+ if (fields.length === 1) {
515
+ const tree = buildObjectTree(fields);
516
+ const field = fields[0];
517
+ const headers = `new[] { ${field.source.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
518
+ const rendered = renderNode(tree, 3, "value");
519
+ return `CsvParser.ParseStringCollection(row, ${headers})
520
+ .Select(value => JsonSerializer.Serialize(\n${indentUnit.repeat(3)}${rendered}\n${indentUnit.repeat(3)}))
521
+ .ToList()`;
522
+ }
419
523
  const tree = buildObjectTree(fields);
420
524
  const fieldVarByPath = new Map();
421
525
  const fieldLines = fields.map((field, index) => {
@@ -424,22 +528,22 @@ function buildCsPersonEntityCollectionExpression(fields) {
424
528
  const headers = `new[] { ${field.source.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
425
529
  return ` var ${varName} = CsvParser.ParseStringCollection(row, ${headers});`;
426
530
  });
427
- const renderNode = (node) => {
531
+ const renderNodeMany = (node) => {
428
532
  const entries = Object.entries(node).map(([key, value]) => {
429
533
  if (typeof value === "object" && value && "path" in value) {
430
534
  const field = value;
431
535
  const varName = fieldVarByPath.get(field.path) ?? "";
432
- return `[${JSON.stringify(key)}] = GetValue(${varName}, index)`;
536
+ return `${indentUnit.repeat(3)}[${JSON.stringify(key)}] = GetValue(${varName}, index)`;
433
537
  }
434
- return `[${JSON.stringify(key)}] = ${renderNode(value)}`;
538
+ return `${indentUnit.repeat(3)}[${JSON.stringify(key)}] = ${renderNodeMany(value)}`;
435
539
  });
436
- return `new Dictionary<string, object?> { ${entries.join(", ")} }`;
540
+ return `new Dictionary<string, object?>\n${indentUnit.repeat(2)}{\n${entries.join(",\n")}\n${indentUnit.repeat(2)}}`;
437
541
  };
438
542
  const fieldVars = [...fieldVarByPath.values()];
439
543
  const lengthLines = fieldVars.length > 0
440
544
  ? ` var maxLen = new[] { ${fieldVars.map((v) => `${v}.Count`).join(", ")} }.Max();`
441
545
  : " var maxLen = 0;";
442
- return `new Func<List<string>>(() =>\n {\n${fieldLines.join("\n")}\n string GetValue(List<string> values, int index)\n {\n if (values.Count == 0) return \"\";\n if (values.Count == 1) return values[0] ?? \"\";\n return index < values.Count ? (values[index] ?? \"\") : \"\";\n }\n${lengthLines}\n var results = new List<string>();\n for (var index = 0; index < maxLen; index++)\n {\n results.Add(JsonSerializer.Serialize(${renderNode(tree)}));\n }\n return results;\n }).Invoke()`;
546
+ return `new Func<List<string>>(() =>\n {\n${fieldLines.join("\n")}\n string GetValue(List<string> values, int index)\n {\n if (values.Count == 0) return \"\";\n if (values.Count == 1) return values[0] ?? \"\";\n return index < values.Count ? (values[index] ?? \"\") : \"\";\n }\n${lengthLines}\n var results = new List<string>();\n for (var index = 0; index < maxLen; index++)\n {\n results.Add(JsonSerializer.Serialize(${renderNodeMany(tree)}));\n }\n return results;\n }).Invoke()`;
443
547
  }
444
548
  function csvEscape(value) {
445
549
  if (value.includes("\n") || value.includes("\r") || value.includes(",") || value.includes("\"")) {
@@ -466,7 +570,7 @@ function sampleValueForType(type) {
466
570
  case "dateTimeCollection":
467
571
  return "2024-01-01T00:00:00Z;2024-01-02T00:00:00Z";
468
572
  case "principal":
469
- return '{"id":"00000000-0000-0000-0000-000000000000","type":"user"}';
573
+ return 'alice@contoso.com';
470
574
  case "string":
471
575
  default:
472
576
  return "sample";
@@ -542,16 +646,14 @@ function buildSampleCsv(ir) {
542
646
  const valueLine = headers.map((h) => csvEscape(valueByHeader.get(h) ?? "sample")).join(",");
543
647
  return `${headerLine}\n${valueLine}\n`;
544
648
  }
545
- async function writeGeneratedDotnet(outDir, ir, namespaceName) {
546
- await mkdir(path.join(outDir, "Schema"), { recursive: true });
547
- const properties = ir.properties.map((p) => ({
548
- name: p.name,
549
- csName: toCsIdentifier(p.name),
550
- csType: toCsType(p.type),
551
- csvHeaders: p.source.csvHeaders,
552
- isExplicitSource: p.source.explicit ?? false,
553
- isCollection: p.type === "stringCollection",
554
- personEntity: p.personEntity
649
+ async function writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName, schemaNamespace) {
650
+ await mkdir(path.join(outDir, schemaFolderName), { recursive: true });
651
+ await mkdir(path.join(outDir, "Core"), { recursive: true });
652
+ const properties = ir.properties.map((p) => {
653
+ const parseFn = toCsParseFunction(p.type);
654
+ const csvHeadersLiteral = `new[] { ${p.source.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
655
+ const isCollection = p.type === "stringCollection";
656
+ const personEntity = p.personEntity
555
657
  ? {
556
658
  entity: p.personEntity.entity,
557
659
  fields: p.personEntity.fields.map((field) => ({
@@ -559,16 +661,40 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName) {
559
661
  source: field.source,
560
662
  })),
561
663
  }
562
- : null,
563
- parseFn: toCsParseFunction(p.type),
564
- graphTypeEnumName: toGraphPropertyTypeEnumName(p.type),
565
- description: p.description,
566
- labels: p.labels,
567
- aliases: p.aliases,
568
- search: p.search,
569
- type: p.type,
570
- }));
664
+ : null;
665
+ const isPeopleLabel = p.labels.some((label) => label.startsWith("person"));
666
+ const needsManualEntity = isPeopleLabel && !p.personEntity;
667
+ const noSource = Boolean(p.source.noSource);
668
+ const transformExpression = needsManualEntity
669
+ ? `throw new NotImplementedException("Missing @coco.source(..., to) mappings for people entity '${p.name}'. Implement in PropertyTransform.cs.")`
670
+ : personEntity
671
+ ? isCollection
672
+ ? buildCsPersonEntityCollectionExpression(personEntity.fields)
673
+ : buildCsPersonEntityExpression(personEntity.fields)
674
+ : noSource
675
+ ? "default!"
676
+ : `${parseFn}(row, ${csvHeadersLiteral})`;
677
+ return {
678
+ name: p.name,
679
+ csName: toCsIdentifier(p.name),
680
+ csType: toCsType(p.type),
681
+ csvHeaders: p.source.csvHeaders,
682
+ csvHeadersLiteral,
683
+ isCollection,
684
+ personEntity,
685
+ parseFn,
686
+ transformExpression,
687
+ transformThrows: needsManualEntity,
688
+ graphTypeEnumName: toGraphPropertyTypeEnumName(p.type),
689
+ description: p.description,
690
+ labels: p.labels,
691
+ aliases: p.aliases,
692
+ search: p.search,
693
+ type: p.type,
694
+ };
695
+ });
571
696
  const schemaPropertyLines = properties
697
+ .filter((p) => p.name !== ir.item.contentPropertyName)
572
698
  .map((p) => {
573
699
  const labels = p.labels.length > 0
574
700
  ? `new List<string> { ${p.labels.map((l) => JSON.stringify(l)).join(", ")} }`
@@ -615,29 +741,11 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName) {
615
741
  const constructorArgLines = properties
616
742
  .map((p, index) => {
617
743
  const comma = index < properties.length - 1 ? "," : "";
618
- if (p.personEntity) {
619
- const method = p.isCollection ? "TransformCollection" : "Transform";
620
- const defaults = p.isCollection ? "BuildDefaultCollection" : "BuildDefault";
621
- return ` PersonEntityOverrides.${method}(${JSON.stringify(p.name)}, row, PersonEntityDefaults.${defaults}(${JSON.stringify(p.name)}, row))${comma}`;
622
- }
623
- const headersLiteral = `new[] { ${p.csvHeaders.map((h) => JSON.stringify(h)).join(", ")} }`;
624
- if (p.isExplicitSource) {
625
- return ` PropertyTransforms.Transform${p.csName}(CsvParser.ReadValue(row, ${headersLiteral}))${comma}`;
626
- }
627
- return ` PropertyTransforms.Transform${p.csName}(row)${comma}`;
744
+ return ` (${p.csType})transforms.TransformProperty(${JSON.stringify(p.name)}, row)${comma}`;
628
745
  })
629
746
  .join("\n");
630
- const personEntityDefaults = properties
631
- .filter((p) => p.personEntity)
632
- .map((p) => ({
633
- name: p.name,
634
- isCollection: p.isCollection,
635
- expression: p.isCollection
636
- ? buildCsPersonEntityCollectionExpression(p.personEntity.fields)
637
- : buildCsPersonEntityExpression(p.personEntity.fields),
638
- }));
639
- const propertyTransforms = properties.filter((p) => !p.personEntity);
640
747
  const propertiesObjectLines = properties
748
+ .filter((p) => p.name !== ir.item.contentPropertyName)
641
749
  .flatMap((p) => {
642
750
  const lines = [];
643
751
  const odataType = toOdataCollectionType(p.type);
@@ -659,15 +767,16 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName) {
659
767
  " };",
660
768
  ].join("\n")
661
769
  : "";
662
- await writeFile(path.join(outDir, "Schema", "Model.cs"), await renderTemplate("dotnet/Generated/Model.cs.ejs", {
663
- namespaceName,
770
+ await writeFile(path.join(outDir, schemaFolderName, "Model.cs"), await renderTemplate("dotnet/Generated/Model.cs.ejs", {
771
+ schemaNamespace,
664
772
  itemTypeName: ir.item.typeName,
665
773
  properties: properties.map((p) => ({ csName: p.csName, csType: p.csType })),
666
774
  }), "utf8");
667
- await writeFile(path.join(outDir, "Schema", "Constants.cs"), await renderTemplate("dotnet/Generated/Constants.cs.ejs", {
668
- namespaceName,
775
+ await writeFile(path.join(outDir, schemaFolderName, "Constants.cs"), await renderTemplate("dotnet/Generated/Constants.cs.ejs", {
776
+ schemaNamespace,
669
777
  graphApiVersion: ir.connection.graphApiVersion,
670
778
  contentCategory: ir.connection.contentCategory ?? null,
779
+ connectionName: ir.connection.connectionName ?? null,
671
780
  profileSourceWebUrl: ir.connection.profileSource?.webUrl ?? null,
672
781
  profileSourceDisplayName: ir.connection.profileSource?.displayName ?? null,
673
782
  profileSourcePriority: ir.connection.profileSource?.priority ?? null,
@@ -675,47 +784,50 @@ async function writeGeneratedDotnet(outDir, ir, namespaceName) {
675
784
  idPropertyName: ir.item.idPropertyName,
676
785
  contentPropertyName: ir.item.contentPropertyName ?? null,
677
786
  }), "utf8");
678
- await writeFile(path.join(outDir, "Schema", "SchemaPayload.cs"), await renderTemplate("dotnet/Generated/SchemaPayload.cs.ejs", {
679
- namespaceName,
787
+ await writeFile(path.join(outDir, schemaFolderName, "SchemaPayload.cs"), await renderTemplate("dotnet/Generated/SchemaPayload.cs.ejs", {
788
+ schemaNamespace,
680
789
  schemaPropertyLines,
681
790
  graphApiVersion: ir.connection.graphApiVersion,
682
791
  }), "utf8");
683
792
  await writeFile(path.join(outDir, "Datasource", "CsvParser.cs"), await renderTemplate("dotnet/Generated/CsvParser.cs.ejs", {
684
793
  namespaceName,
685
794
  }), "utf8");
686
- if (propertyTransforms.length > 0) {
687
- await writeFile(path.join(outDir, "Schema", "PropertyTransforms.cs"), await renderTemplate("dotnet/Generated/PropertyTransforms.cs.ejs", {
688
- namespaceName,
689
- properties: propertyTransforms,
690
- }), "utf8");
691
- }
692
- await writeFile(path.join(outDir, "Schema", "FromCsvRow.cs"), await renderTemplate("dotnet/Generated/FromCsvRow.cs.ejs", {
795
+ await writeFile(path.join(outDir, schemaFolderName, "PropertyTransformBase.cs"), await renderTemplate("dotnet/Generated/PropertyTransformBase.cs.ejs", {
693
796
  namespaceName,
694
- constructorArgLines,
797
+ schemaNamespace,
798
+ properties,
799
+ usesPersonEntity: properties.some((p) => p.personEntity),
695
800
  }), "utf8");
696
- if (ir.connection.contentCategory === "people" && personEntityDefaults.length > 0) {
697
- await writeFile(path.join(outDir, "Schema", "PersonEntityDefaults.cs"), await renderTemplate("dotnet/Generated/PersonEntityDefaults.cs.ejs", {
698
- namespaceName,
699
- defaults: personEntityDefaults,
801
+ const transformOverridesPath = path.join(outDir, schemaFolderName, "PropertyTransform.cs");
802
+ try {
803
+ await access(transformOverridesPath);
804
+ }
805
+ catch {
806
+ await writeFile(transformOverridesPath, await renderTemplate("dotnet/PropertyTransform.cs.ejs", {
807
+ schemaNamespace,
700
808
  }), "utf8");
701
- const overridesPath = path.join(outDir, "Schema", "PersonEntityOverrides.cs");
702
- try {
703
- await access(overridesPath);
704
- }
705
- catch {
706
- await writeFile(overridesPath, await renderTemplate("dotnet/PersonEntityOverrides.cs.ejs", {
707
- namespaceName,
708
- names: personEntityDefaults.map((p) => p.name),
709
- }), "utf8");
710
- }
711
809
  }
712
- await writeFile(path.join(outDir, "Schema", "ItemPayload.cs"), await renderTemplate("dotnet/Generated/ItemPayload.cs.ejs", {
810
+ await writeFile(path.join(outDir, schemaFolderName, "FromCsvRow.cs"), await renderTemplate("dotnet/Generated/FromCsvRow.cs.ejs", {
713
811
  namespaceName,
812
+ schemaNamespace,
813
+ itemTypeName: ir.item.typeName,
814
+ constructorArgLines,
815
+ }), "utf8");
816
+ await writeFile(path.join(outDir, schemaFolderName, "ItemPayload.cs"), await renderTemplate("dotnet/Generated/ItemPayload.cs.ejs", {
817
+ schemaNamespace,
818
+ itemTypeName: ir.item.typeName,
714
819
  itemIdExpression,
715
820
  propertiesObjectLines,
716
821
  contentBlock,
717
822
  graphApiVersion: ir.connection.graphApiVersion,
718
823
  }), "utf8");
824
+ await writeFile(path.join(outDir, "Core", "ConnectorCore.cs"), await renderTemplate("dotnet/Core/ConnectorCore.cs.ejs", {
825
+ namespaceName,
826
+ schemaNamespace,
827
+ itemTypeName: ir.item.typeName,
828
+ isPeopleConnector: ir.connection.contentCategory === "people",
829
+ graphApiVersion: ir.connection.graphApiVersion,
830
+ }), "utf8");
719
831
  }
720
832
  function formatValidationErrors(ir) {
721
833
  const issues = validateIr(ir);
@@ -741,7 +853,8 @@ export async function updateTsProject(options) {
741
853
  if (validationMessage) {
742
854
  throw new Error(`Schema validation failed:\n${validationMessage}`);
743
855
  }
744
- await writeGeneratedTs(outDir, ir);
856
+ const schemaFolderName = toTsSchemaFolderName(ir.connection.connectionName);
857
+ await writeGeneratedTs(outDir, ir, schemaFolderName);
745
858
  if (options.tspPath) {
746
859
  await writeFile(path.join(outDir, COCOGEN_CONFIG_FILE), projectConfigContents(outDir, tspPath, "ts"), "utf8");
747
860
  }
@@ -763,7 +876,9 @@ export async function updateDotnetProject(options) {
763
876
  throw new Error(`Schema validation failed:\n${validationMessage}`);
764
877
  }
765
878
  const namespaceName = toCsNamespace(path.basename(outDir));
766
- await writeGeneratedDotnet(outDir, ir, namespaceName);
879
+ const schemaFolderName = toSchemaFolderName(ir.connection.connectionName);
880
+ const schemaNamespace = `${namespaceName}.${schemaFolderName}`;
881
+ await writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName, schemaNamespace);
767
882
  if (options.tspPath) {
768
883
  await writeFile(path.join(outDir, COCOGEN_CONFIG_FILE), projectConfigContents(outDir, tspPath, "dotnet"), "utf8");
769
884
  }
@@ -789,9 +904,10 @@ export async function initTsProject(options) {
789
904
  throw new Error(`Schema validation failed:\n${validationMessage}`);
790
905
  }
791
906
  const projectName = options.projectName ?? path.basename(outDir);
907
+ const schemaFolderName = toTsSchemaFolderName(ir.connection.connectionName);
792
908
  await mkdir(path.join(outDir, "src"), { recursive: true });
793
909
  await mkdir(path.join(outDir, "src", "datasource"), { recursive: true });
794
- await mkdir(path.join(outDir, "src", "schema"), { recursive: true });
910
+ await mkdir(path.join(outDir, "src", schemaFolderName), { recursive: true });
795
911
  await writeFile(path.join(outDir, "package.json"), await renderTemplate("ts/package.json.ejs", {
796
912
  projectName,
797
913
  isPeopleConnector: ir.connection.contentCategory === "people",
@@ -801,13 +917,18 @@ export async function initTsProject(options) {
801
917
  await writeFile(path.join(outDir, ".env.example"), await renderTemplate("ts/.env.example.ejs", {
802
918
  itemTypeName: ir.item.typeName,
803
919
  isPeopleConnector: ir.connection.contentCategory === "people",
920
+ connectionName: ir.connection.connectionName ?? null,
804
921
  connectionId: ir.connection.connectionId ?? null,
805
922
  connectionDescription: ir.connection.connectionDescription ?? null,
806
923
  profileSourceWebUrl: ir.connection.profileSource?.webUrl ?? null,
807
924
  profileSourceDisplayName: ir.connection.profileSource?.displayName ?? null,
808
925
  profileSourcePriority: ir.connection.profileSource?.priority ?? null,
809
926
  }), "utf8");
810
- await writeFile(path.join(outDir, "README.md"), await renderTemplate("ts/README.md.ejs", { isPeopleConnector: ir.connection.contentCategory === "people" }), "utf8");
927
+ await writeFile(path.join(outDir, "README.md"), await renderTemplate("ts/README.md.ejs", {
928
+ isPeopleConnector: ir.connection.contentCategory === "people",
929
+ itemTypeName: ir.item.typeName,
930
+ schemaFolderName,
931
+ }), "utf8");
811
932
  const copiedTspPath = path.join(outDir, "schema.tsp");
812
933
  await copyFile(path.resolve(options.tspPath), copiedTspPath);
813
934
  await writeFile(path.join(outDir, COCOGEN_CONFIG_FILE), projectConfigContents(outDir, copiedTspPath, "ts"), "utf8");
@@ -828,12 +949,20 @@ export async function initTsProject(options) {
828
949
  await writeFile(path.join(outDir, "src", "cli.ts"), await renderTemplate("ts/src/cli.ts.ejs", {
829
950
  graphBaseUrl: graphBaseUrl(ir),
830
951
  isPeopleConnector: ir.connection.contentCategory === "people",
952
+ itemTypeName: ir.item.typeName,
953
+ schemaFolderName,
954
+ }), "utf8");
955
+ await writeFile(path.join(outDir, "src", "datasource", "itemSource.ts"), await renderTemplate("ts/src/datasource/itemSource.ts.ejs", {
956
+ itemTypeName: ir.item.typeName,
957
+ schemaFolderName,
958
+ }), "utf8");
959
+ await writeFile(path.join(outDir, "src", "datasource", "csvItemSource.ts"), await renderTemplate("ts/src/datasource/csvItemSource.ts.ejs", {
960
+ itemTypeName: ir.item.typeName,
961
+ schemaFolderName,
831
962
  }), "utf8");
832
- await writeFile(path.join(outDir, "src", "datasource", "itemSource.ts"), await renderTemplate("ts/src/datasource/itemSource.ts.ejs", {}), "utf8");
833
- await writeFile(path.join(outDir, "src", "datasource", "csvItemSource.ts"), await renderTemplate("ts/src/datasource/csvItemSource.ts.ejs", {}), "utf8");
834
963
  await writeFile(path.join(outDir, "data.csv"), buildSampleCsv(ir), "utf8");
835
- await writeGeneratedTs(outDir, ir);
836
- await writeFile(path.join(outDir, "src", "index.ts"), await renderTemplate("ts/src/index.ts.ejs", {}), "utf8");
964
+ await writeGeneratedTs(outDir, ir, schemaFolderName);
965
+ await writeFile(path.join(outDir, "src", "index.ts"), await renderTemplate("ts/src/index.ts.ejs", { schemaFolderName }), "utf8");
837
966
  return { outDir, ir };
838
967
  }
839
968
  export async function initDotnetProject(options) {
@@ -849,8 +978,10 @@ export async function initDotnetProject(options) {
849
978
  }
850
979
  const projectName = options.projectName ?? path.basename(outDir);
851
980
  const namespaceName = toCsNamespace(projectName);
981
+ const schemaFolderName = toSchemaFolderName(ir.connection.connectionName);
982
+ const schemaNamespace = `${namespaceName}.${schemaFolderName}`;
852
983
  await mkdir(path.join(outDir, "Datasource"), { recursive: true });
853
- await mkdir(path.join(outDir, "Schema"), { recursive: true });
984
+ await mkdir(path.join(outDir, schemaFolderName), { recursive: true });
854
985
  await writeFile(path.join(outDir, `${projectName}.csproj`), await renderTemplate("dotnet/project.csproj.ejs", {
855
986
  graphApiVersion: ir.connection.graphApiVersion,
856
987
  }), "utf8");
@@ -860,15 +991,26 @@ export async function initDotnetProject(options) {
860
991
  await writeFile(path.join(outDir, "tspconfig.yaml"), await renderTemplate("dotnet/tspconfig.yaml.ejs", {}), "utf8");
861
992
  await writeFile(path.join(outDir, "Program.cs"), await renderTemplate("dotnet/Program.commandline.cs.ejs", {
862
993
  namespaceName,
994
+ schemaNamespace,
995
+ itemTypeName: ir.item.typeName,
863
996
  isPeopleConnector: ir.connection.contentCategory === "people",
864
997
  graphApiVersion: ir.connection.graphApiVersion,
865
998
  }), "utf8");
866
- await writeFile(path.join(outDir, "Datasource", "IItemSource.cs"), await renderTemplate("dotnet/Datasource/IItemSource.cs.ejs", { namespaceName }), "utf8");
867
- await writeFile(path.join(outDir, "Datasource", "CsvItemSource.cs"), await renderTemplate("dotnet/Datasource/CsvItemSource.cs.ejs", { namespaceName }), "utf8");
999
+ await writeFile(path.join(outDir, "Datasource", "IItemSource.cs"), await renderTemplate("dotnet/Datasource/IItemSource.cs.ejs", {
1000
+ namespaceName,
1001
+ schemaNamespace,
1002
+ itemTypeName: ir.item.typeName,
1003
+ }), "utf8");
1004
+ await writeFile(path.join(outDir, "Datasource", "CsvItemSource.cs"), await renderTemplate("dotnet/Datasource/CsvItemSource.cs.ejs", {
1005
+ namespaceName,
1006
+ schemaNamespace,
1007
+ itemTypeName: ir.item.typeName,
1008
+ }), "utf8");
868
1009
  await writeFile(path.join(outDir, "data.csv"), buildSampleCsv(ir), "utf8");
869
1010
  await writeFile(path.join(outDir, "appsettings.json"), await renderTemplate("dotnet/appsettings.json.ejs", {
870
1011
  itemTypeName: ir.item.typeName,
871
1012
  isPeopleConnector: ir.connection.contentCategory === "people",
1013
+ connectionName: ir.connection.connectionName ?? null,
872
1014
  connectionId: ir.connection.connectionId ?? null,
873
1015
  connectionDescription: ir.connection.connectionDescription ?? null,
874
1016
  profileSourceWebUrl: ir.connection.profileSource?.webUrl ?? null,
@@ -876,11 +1018,15 @@ export async function initDotnetProject(options) {
876
1018
  profileSourcePriority: ir.connection.profileSource?.priority ?? null,
877
1019
  }), "utf8");
878
1020
  await writeFile(path.join(outDir, ".gitignore"), await renderTemplate("dotnet/.gitignore.ejs", {}), "utf8");
879
- await writeFile(path.join(outDir, "README.md"), await renderTemplate("dotnet/README.md.ejs", { isPeopleConnector: ir.connection.contentCategory === "people" }), "utf8");
1021
+ await writeFile(path.join(outDir, "README.md"), await renderTemplate("dotnet/README.md.ejs", {
1022
+ isPeopleConnector: ir.connection.contentCategory === "people",
1023
+ itemTypeName: ir.item.typeName,
1024
+ schemaFolderName,
1025
+ }), "utf8");
880
1026
  const copiedTspPath = path.join(outDir, "schema.tsp");
881
1027
  await copyFile(path.resolve(options.tspPath), copiedTspPath);
882
1028
  await writeFile(path.join(outDir, COCOGEN_CONFIG_FILE), projectConfigContents(outDir, copiedTspPath, "dotnet"), "utf8");
883
- await writeGeneratedDotnet(outDir, ir, namespaceName);
1029
+ await writeGeneratedDotnet(outDir, ir, namespaceName, schemaFolderName, schemaNamespace);
884
1030
  return { outDir, ir };
885
1031
  }
886
1032
  //# sourceMappingURL=init.js.map