@uns-kit/cli 0.0.13 → 0.0.15

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/README.md CHANGED
@@ -25,6 +25,8 @@ pnpm run dev
25
25
 
26
26
  - `uns-kit create <name>` – create a new UNS project in the specified directory.
27
27
  - `uns-kit configure-devops [path]` – add Azure DevOps tooling (dependencies, script, config) to an existing project.
28
+ - `uns-kit configure-vscode [path]` – copy VS Code launch/workspace files into an existing project.
29
+ - `uns-kit configure-codegen [path]` – scaffold GraphQL code generation and UNS refresh scripts.
28
30
  - `uns-kit help` – display usage information.
29
31
 
30
32
  ### Configure Azure DevOps
@@ -37,7 +39,26 @@ pnpm install
37
39
  pnpm run pull-request
38
40
  ```
39
41
 
40
- The command prompts for your Azure DevOps organization and updates `config.json` along with the necessary dev dependencies.
42
+ The command prompts for your Azure DevOps organization/project, ensures the remote repository exists, and updates `config.json` along with the necessary dev dependencies.
43
+
44
+ ### Configure VS Code workspace
45
+
46
+ ```bash
47
+ uns-kit configure-vscode
48
+ ```
49
+
50
+ Copies `.vscode/launch.json` plus a baseline workspace file into the project. Existing files are never overwritten, making it safe to re-run after hand edits.
51
+
52
+ ### Configure GraphQL code generation
53
+
54
+ ```bash
55
+ uns-kit configure-codegen
56
+ pnpm install
57
+ pnpm run codegen
58
+ pnpm run refresh-uns
59
+ ```
60
+
61
+ Adds `codegen.ts`, seeds `src/uns/` placeholder types, and wires the GraphQL Code Generator / `refresh-uns` script into `package.json`. After installing the new dev dependencies you can regenerate strongly-typed operations (`pnpm run codegen`) and rebuild UNS topics/tags from your environment (`pnpm run refresh-uns`).
41
62
 
42
63
  ### Extend the Config Schema
43
64
 
package/dist/index.js CHANGED
@@ -1,17 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import { execFile } from "node:child_process";
3
- import { access, cp, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
3
+ import { access, cp, mkdir, readFile, readdir, stat, writeFile, copyFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { createRequire } from "node:module";
7
7
  import process from "node:process";
8
8
  import readline from "node:readline/promises";
9
9
  import { promisify } from "node:util";
10
+ import * as azdev from "azure-devops-node-api";
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = path.dirname(__filename);
12
13
  const require = createRequire(import.meta.url);
13
14
  const coreVersion = resolveCoreVersion();
14
15
  const execFileAsync = promisify(execFile);
16
+ const AZURE_DEVOPS_PROVIDER = "azure-devops";
15
17
  async function main() {
16
18
  const args = process.argv.slice(2);
17
19
  const command = args[0];
@@ -30,6 +32,28 @@ async function main() {
30
32
  }
31
33
  return;
32
34
  }
35
+ if (command === "configure-vscode") {
36
+ const targetPath = args[1];
37
+ try {
38
+ await configureVscode(targetPath);
39
+ }
40
+ catch (error) {
41
+ console.error(error.message);
42
+ process.exitCode = 1;
43
+ }
44
+ return;
45
+ }
46
+ if (command === "configure-codegen") {
47
+ const targetPath = args[1];
48
+ try {
49
+ await configureCodegen(targetPath);
50
+ }
51
+ catch (error) {
52
+ console.error(error.message);
53
+ process.exitCode = 1;
54
+ }
55
+ return;
56
+ }
33
57
  if (command === "create") {
34
58
  const projectName = args[1];
35
59
  if (!projectName) {
@@ -51,7 +75,13 @@ async function main() {
51
75
  process.exitCode = 1;
52
76
  }
53
77
  function printHelp() {
54
- console.log(`\nUsage: uns-kit <command> [options]\n\nCommands:\n create <name> Scaffold a new UNS application\n configure-devops [dir] Configure Azure DevOps tooling in an existing project\n help Show this message\n`);
78
+ console.log("\nUsage: uns-kit <command> [options]\n" +
79
+ "\nCommands:\n" +
80
+ " create <name> Scaffold a new UNS application\n" +
81
+ " configure-devops [dir] Configure Azure DevOps tooling in an existing project\n" +
82
+ " configure-vscode [dir] Add VS Code workspace configuration files\n" +
83
+ " configure-codegen [dir] Copy GraphQL codegen template and dependencies\n" +
84
+ " help Show this message\n");
55
85
  }
56
86
  async function createProject(projectName) {
57
87
  const targetDir = path.resolve(process.cwd(), projectName);
@@ -96,25 +126,45 @@ async function configureDevops(targetPath) {
96
126
  const config = JSON.parse(configRaw);
97
127
  await ensureGitRepository(targetDir);
98
128
  const remoteUrl = await getGitRemoteUrl(targetDir, "origin");
99
- let gitRemoteMessage;
100
- let ensuredRemoteUrl = remoteUrl;
101
- if (!ensuredRemoteUrl) {
102
- const remoteAnswer = (await promptQuestion("Azure DevOps repository URL (e.g. https://dev.azure.com/sijit/industry40/_git/my-repo): ")).trim();
103
- if (!remoteAnswer) {
104
- throw new Error("A repository URL is required to set the git remote origin.");
105
- }
106
- await addGitRemote(targetDir, "origin", remoteAnswer);
107
- ensuredRemoteUrl = remoteAnswer;
108
- gitRemoteMessage = ` Added git remote origin -> ${remoteAnswer}`;
109
- }
110
- const inferredOrganization = ensuredRemoteUrl ? inferAzureOrganization(ensuredRemoteUrl) : undefined;
111
- const defaultOrg = config.devops?.organization?.trim() || inferredOrganization || "sijit";
112
- const answer = (await promptQuestion(`Azure DevOps organization [${defaultOrg}]: `)).trim();
113
- const organization = answer || defaultOrg;
129
+ const remoteInfo = remoteUrl ? parseAzureRemote(remoteUrl) : undefined;
130
+ const repositoryName = inferRepositoryNameFromPackage(pkg.name) || inferRepositoryNameFromPackage(path.basename(targetDir));
131
+ const defaultOrganization = config.devops?.organization?.trim() || remoteInfo?.organization || "sijit";
132
+ const organization = await promptWithDefault(defaultOrganization ? `Azure DevOps organization [${defaultOrganization}]: ` : "Azure DevOps organization: ", defaultOrganization, "Azure DevOps organization is required.");
133
+ const defaultProject = config.devops?.project?.trim() || remoteInfo?.project || "";
134
+ const project = await promptWithDefault(defaultProject ? `Azure DevOps project [${defaultProject}]: ` : "Azure DevOps project: ", defaultProject, "Azure DevOps project is required.");
114
135
  if (!config.devops || typeof config.devops !== "object") {
115
136
  config.devops = {};
116
137
  }
117
- config.devops.organization = organization;
138
+ const devopsConfig = config.devops;
139
+ devopsConfig.provider = AZURE_DEVOPS_PROVIDER;
140
+ devopsConfig.organization = organization;
141
+ devopsConfig.project = project;
142
+ let gitRemoteMessage;
143
+ let repositoryUrlMessage;
144
+ let ensuredRemoteUrl = remoteUrl ?? "";
145
+ if (!remoteUrl) {
146
+ const { gitApi } = await resolveAzureGitApi(organization);
147
+ const repositoryDetails = await ensureAzureRepositoryExists(gitApi, {
148
+ organization,
149
+ project,
150
+ repository: repositoryName,
151
+ });
152
+ ensuredRemoteUrl = repositoryDetails.remoteUrl ?? buildAzureGitRemoteUrl(organization, project, repositoryName);
153
+ await addGitRemote(targetDir, "origin", ensuredRemoteUrl);
154
+ gitRemoteMessage = ` Added git remote origin -> ${ensuredRemoteUrl}`;
155
+ const friendlyUrl = repositoryDetails.webUrl ?? buildAzureRepositoryUrl(organization, project, repositoryName);
156
+ if (friendlyUrl) {
157
+ repositoryUrlMessage = ` Repository URL: ${friendlyUrl}`;
158
+ }
159
+ }
160
+ else {
161
+ ensuredRemoteUrl = remoteUrl;
162
+ gitRemoteMessage = ` Git remote origin detected -> ${ensuredRemoteUrl}`;
163
+ const friendlyUrl = buildAzureRepositoryUrl(remoteInfo?.organization ?? organization, remoteInfo?.project ?? project, remoteInfo?.repository ?? repositoryName);
164
+ if (friendlyUrl) {
165
+ repositoryUrlMessage = ` Repository URL: ${friendlyUrl}`;
166
+ }
167
+ }
118
168
  const requiredDevDeps = {
119
169
  "azure-devops-node-api": "^15.1.0",
120
170
  "simple-git": "^3.27.0",
@@ -140,7 +190,12 @@ async function configureDevops(targetPath) {
140
190
  await writeFile(packagePath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
141
191
  }
142
192
  console.log(`\nDevOps tooling configured.`);
193
+ console.log(` DevOps provider: ${AZURE_DEVOPS_PROVIDER}`);
143
194
  console.log(` Azure organization: ${organization}`);
195
+ console.log(` Azure project: ${project}`);
196
+ if (repositoryUrlMessage) {
197
+ console.log(repositoryUrlMessage);
198
+ }
144
199
  if (gitRemoteMessage) {
145
200
  console.log(gitRemoteMessage);
146
201
  }
@@ -151,6 +206,107 @@ async function configureDevops(targetPath) {
151
206
  console.log(" Existing package.json already contained required entries.");
152
207
  }
153
208
  }
209
+ async function configureVscode(targetPath) {
210
+ const targetDir = path.resolve(process.cwd(), targetPath ?? ".");
211
+ const templateDir = path.resolve(__dirname, "../templates/vscode");
212
+ try {
213
+ await access(templateDir);
214
+ }
215
+ catch (error) {
216
+ throw new Error("VS Code template directory is missing. Please ensure templates/vscode is available.");
217
+ }
218
+ const { copied, skipped } = await copyTemplateDirectory(templateDir, targetDir, targetDir);
219
+ console.log("\nVS Code configuration files processed.");
220
+ if (copied.length) {
221
+ console.log(" Added:");
222
+ for (const file of copied) {
223
+ console.log(` ${file}`);
224
+ }
225
+ }
226
+ if (skipped.length) {
227
+ console.log(" Skipped (already exists):");
228
+ for (const file of skipped) {
229
+ console.log(` ${file}`);
230
+ }
231
+ }
232
+ if (!copied.length && !skipped.length) {
233
+ console.log(" No files were found in the VS Code template directory.");
234
+ }
235
+ }
236
+ async function configureCodegen(targetPath) {
237
+ const targetDir = path.resolve(process.cwd(), targetPath ?? ".");
238
+ const templateDir = path.resolve(__dirname, "../templates/codegen");
239
+ const packagePath = path.join(targetDir, "package.json");
240
+ try {
241
+ await access(templateDir);
242
+ }
243
+ catch (error) {
244
+ throw new Error("GraphQL codegen template directory is missing. Please ensure templates/codegen is available.");
245
+ }
246
+ let pkgRaw;
247
+ try {
248
+ pkgRaw = await readFile(packagePath, "utf8");
249
+ }
250
+ catch (error) {
251
+ if (error.code === "ENOENT") {
252
+ throw new Error(`Could not find package.json in ${targetDir}`);
253
+ }
254
+ throw error;
255
+ }
256
+ const pkg = JSON.parse(pkgRaw);
257
+ const { copied, skipped } = await copyTemplateDirectory(templateDir, targetDir, targetDir);
258
+ const devDeps = (pkg.devDependencies ??= {});
259
+ const deps = pkg.dependencies ?? {};
260
+ const requiredDevDeps = {
261
+ "@graphql-codegen/cli": "^5.0.7",
262
+ "@graphql-codegen/typescript": "^4.1.6",
263
+ "@graphql-codegen/typescript-operations": "^4.6.1",
264
+ "@graphql-codegen/typescript-resolvers": "^4.3.1",
265
+ "graphql": "^16.11.0",
266
+ "graphql-request": "^7.2.0"
267
+ };
268
+ let pkgChanged = false;
269
+ for (const [name, version] of Object.entries(requiredDevDeps)) {
270
+ if (!devDeps[name] && !deps[name]) {
271
+ devDeps[name] = version;
272
+ pkgChanged = true;
273
+ }
274
+ }
275
+ const scripts = (pkg.scripts ??= {});
276
+ if (!scripts.codegen) {
277
+ scripts.codegen = "graphql-code-generator --config codegen.ts";
278
+ pkgChanged = true;
279
+ }
280
+ if (!scripts["refresh-uns"]) {
281
+ scripts["refresh-uns"] = "node ./node_modules/@uns-kit/core/dist/tools/refresh-uns.js";
282
+ pkgChanged = true;
283
+ }
284
+ if (pkgChanged) {
285
+ await writeFile(packagePath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
286
+ }
287
+ console.log("\nGraphQL code generation setup complete.");
288
+ if (copied.length) {
289
+ console.log(" Added files:");
290
+ for (const file of copied) {
291
+ console.log(` ${file}`);
292
+ }
293
+ }
294
+ if (skipped.length) {
295
+ console.log(" Skipped existing files:");
296
+ for (const file of skipped) {
297
+ console.log(` ${file}`);
298
+ }
299
+ }
300
+ if (!copied.length && !skipped.length) {
301
+ console.log(" No template files were copied.");
302
+ }
303
+ if (pkgChanged) {
304
+ console.log(" Updated package.json scripts/devDependencies. Run pnpm install to fetch new packages.");
305
+ }
306
+ else {
307
+ console.log(" Existing package.json already contained required scripts and dependencies.");
308
+ }
309
+ }
154
310
  async function ensureGitRepository(dir) {
155
311
  try {
156
312
  const { stdout } = await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
@@ -210,32 +366,194 @@ async function addGitRemote(dir, remoteName, remoteUrl) {
210
366
  throw new Error(`Failed to add git remote origin: ${error.message}`);
211
367
  }
212
368
  }
213
- function inferAzureOrganization(remoteUrl) {
369
+ async function copyTemplateDirectory(sourceDir, targetDir, targetRoot) {
370
+ const entries = await readdir(sourceDir, { withFileTypes: true });
371
+ const copied = [];
372
+ const skipped = [];
373
+ for (const entry of entries) {
374
+ const sourcePath = path.join(sourceDir, entry.name);
375
+ const destinationPath = path.join(targetDir, entry.name);
376
+ const relativePath = path.relative(targetRoot, destinationPath) || entry.name;
377
+ if (entry.isDirectory()) {
378
+ await mkdir(destinationPath, { recursive: true });
379
+ const result = await copyTemplateDirectory(sourcePath, destinationPath, targetRoot);
380
+ copied.push(...result.copied);
381
+ skipped.push(...result.skipped);
382
+ continue;
383
+ }
384
+ if (entry.isFile()) {
385
+ await mkdir(path.dirname(destinationPath), { recursive: true });
386
+ if (await fileExists(destinationPath)) {
387
+ skipped.push(relativePath);
388
+ continue;
389
+ }
390
+ await copyFile(sourcePath, destinationPath);
391
+ copied.push(relativePath);
392
+ }
393
+ }
394
+ return { copied, skipped };
395
+ }
396
+ async function resolveAzureGitApi(organization) {
397
+ const tokensUrl = `https://dev.azure.com/${organization}/_usersSettings/tokens`;
398
+ const envPat = process.env.AZURE_PAT?.trim();
399
+ if (envPat) {
400
+ try {
401
+ const gitApi = await createAzureGitApi(organization, envPat);
402
+ console.log("Using PAT from AZURE_PAT environment variable.");
403
+ return { gitApi };
404
+ }
405
+ catch (error) {
406
+ console.log("The AZURE_PAT environment variable is invalid or expired. Please provide a new PAT.");
407
+ }
408
+ }
409
+ while (true) {
410
+ const input = (await promptQuestion(`Azure DevOps Personal Access Token (create at ${tokensUrl}): `)).trim();
411
+ if (!input) {
412
+ console.log("A Personal Access Token is required to create the repository.");
413
+ continue;
414
+ }
415
+ try {
416
+ const gitApi = await createAzureGitApi(organization, input);
417
+ return { gitApi };
418
+ }
419
+ catch (error) {
420
+ console.log("The provided PAT is invalid or expired. Please try again.");
421
+ }
422
+ }
423
+ }
424
+ async function createAzureGitApi(organization, personalAccessToken) {
425
+ const authHandler = azdev.getPersonalAccessTokenHandler(personalAccessToken);
426
+ const connection = new azdev.WebApi(`https://dev.azure.com/${organization}`, authHandler);
427
+ await connection.connect();
428
+ return connection.getGitApi();
429
+ }
430
+ async function ensureAzureRepositoryExists(gitApi, params) {
431
+ const repositoryName = params.repository.trim();
432
+ if (!repositoryName) {
433
+ throw new Error("Repository name is required.");
434
+ }
435
+ let existingRemoteUrl;
436
+ let existingWebUrl;
437
+ try {
438
+ const repositories = await gitApi.getRepositories(params.project);
439
+ const existing = repositories?.find((repo) => repo.name?.toLowerCase() === repositoryName.toLowerCase());
440
+ if (existing) {
441
+ existingRemoteUrl = existing.remoteUrl ?? undefined;
442
+ existingWebUrl = existing.webUrl ?? existingRemoteUrl;
443
+ return { remoteUrl: existingRemoteUrl, webUrl: existingWebUrl };
444
+ }
445
+ }
446
+ catch (error) {
447
+ // Fallback to attempting creation even if listing failed (e.g., limited permissions)
448
+ }
449
+ try {
450
+ const created = await gitApi.createRepository({ name: repositoryName }, params.project);
451
+ const remoteUrl = created?.remoteUrl ?? buildAzureGitRemoteUrl(params.organization, params.project, repositoryName);
452
+ const webUrl = created?.webUrl ?? created?.remoteUrl ?? buildAzureRepositoryUrl(params.organization, params.project, repositoryName);
453
+ console.log(` Created Azure DevOps repository "${repositoryName}" in project "${params.project}".`);
454
+ return { remoteUrl, webUrl };
455
+ }
456
+ catch (error) {
457
+ try {
458
+ const repository = await gitApi.getRepository(repositoryName, params.project);
459
+ if (repository?.remoteUrl) {
460
+ return {
461
+ remoteUrl: repository.remoteUrl,
462
+ webUrl: repository.webUrl ?? repository.remoteUrl,
463
+ };
464
+ }
465
+ }
466
+ catch (lookupError) {
467
+ // Ignore lookup failure; we'll rethrow original error below.
468
+ }
469
+ const message = error.message || String(error);
470
+ throw new Error(`Failed to create Azure DevOps repository "${repositoryName}" in project "${params.project}": ${message}`);
471
+ }
472
+ }
473
+ function parseAzureRemote(remoteUrl) {
214
474
  try {
215
475
  const url = new URL(remoteUrl);
216
476
  const hostname = url.hostname.toLowerCase();
217
- if (hostname === "dev.azure.com" || hostname.endsWith(".visualstudio.com")) {
218
- const segments = url.pathname.split("/").filter(Boolean);
219
- if (segments.length >= 1) {
220
- return segments[0];
221
- }
477
+ const segments = url.pathname.split("/").filter(Boolean);
478
+ if (hostname === "dev.azure.com") {
479
+ const organization = segments[0] || url.username || undefined;
480
+ const project = segments[1];
481
+ const repository = extractRepositoryFromSegments(segments.slice(2));
482
+ return {
483
+ organization: organization ? decodeURIComponent(organization) : undefined,
484
+ project: project ? decodeURIComponent(project) : undefined,
485
+ repository,
486
+ };
487
+ }
488
+ if (hostname.endsWith(".visualstudio.com")) {
489
+ const organization = hostname.replace(/\.visualstudio\.com$/, "");
490
+ const project = segments[0];
491
+ const repository = extractRepositoryFromSegments(segments.slice(1));
492
+ return {
493
+ organization,
494
+ project: project ? decodeURIComponent(project) : undefined,
495
+ repository,
496
+ };
222
497
  }
223
498
  }
224
499
  catch (error) {
225
- // ignore parse errors for non-HTTP(S) remotes
500
+ // Non-HTTP remote (e.g., SSH)
226
501
  }
227
502
  if (remoteUrl.startsWith("git@ssh.dev.azure.com:")) {
228
503
  const [, pathPart] = remoteUrl.split(":", 2);
229
504
  if (pathPart) {
230
505
  const segments = pathPart.split("/").filter(Boolean);
231
506
  // Format: v3/{organization}/{project}/{repo}
232
- if (segments.length >= 2) {
233
- return segments[1];
507
+ if (segments[0]?.toLowerCase() === "v3") {
508
+ return {
509
+ organization: segments[1] ? decodeURIComponent(segments[1]) : undefined,
510
+ project: segments[2] ? decodeURIComponent(segments[2]) : undefined,
511
+ repository: stripGitExtension(segments.slice(3).join("/")),
512
+ };
234
513
  }
235
514
  }
236
515
  }
237
516
  return undefined;
238
517
  }
518
+ function extractRepositoryFromSegments(segments) {
519
+ if (!segments.length) {
520
+ return undefined;
521
+ }
522
+ if (segments[0] === "_git") {
523
+ return stripGitExtension(segments.slice(1).join("/"));
524
+ }
525
+ if (segments.length >= 2 && segments[1] === "_git") {
526
+ return stripGitExtension(segments.slice(2).join("/"));
527
+ }
528
+ return stripGitExtension(segments.join("/"));
529
+ }
530
+ function stripGitExtension(value) {
531
+ if (!value) {
532
+ return undefined;
533
+ }
534
+ return value.endsWith(".git") ? value.slice(0, -4) : value;
535
+ }
536
+ function encodeAzureSegment(segment) {
537
+ return encodeURIComponent(segment.trim());
538
+ }
539
+ function buildAzureGitRemoteUrl(organization, project, repository) {
540
+ const segments = [organization, project, "_git", repository].map(encodeAzureSegment);
541
+ return `https://dev.azure.com/${segments.join("/")}`;
542
+ }
543
+ function buildAzureRepositoryUrl(organization, project, repository) {
544
+ if (!organization || !project || !repository) {
545
+ return undefined;
546
+ }
547
+ return buildAzureGitRemoteUrl(organization, project, repository);
548
+ }
549
+ function inferRepositoryNameFromPackage(pkgName) {
550
+ if (typeof pkgName !== "string" || !pkgName.trim()) {
551
+ return "uns-app";
552
+ }
553
+ const trimmed = pkgName.trim();
554
+ const baseName = trimmed.startsWith("@") ? trimmed.split("/").pop() ?? trimmed : trimmed;
555
+ return baseName.replace(/[^A-Za-z0-9._-]+/g, "-");
556
+ }
239
557
  function isGitRemoteMissingError(error) {
240
558
  if (!error || typeof error !== "object") {
241
559
  return false;
@@ -249,6 +567,18 @@ function isGitRemoteMissingError(error) {
249
567
  function isGitCommandNotFoundError(error) {
250
568
  return Boolean(error && typeof error === "object" && error.code === "ENOENT");
251
569
  }
570
+ async function promptWithDefault(message, defaultValue, requiredMessage) {
571
+ while (true) {
572
+ const answer = (await promptQuestion(message)).trim();
573
+ if (answer) {
574
+ return answer;
575
+ }
576
+ if (defaultValue) {
577
+ return defaultValue;
578
+ }
579
+ console.log(requiredMessage);
580
+ }
581
+ }
252
582
  async function promptQuestion(message) {
253
583
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
254
584
  try {
@@ -258,6 +588,18 @@ async function promptQuestion(message) {
258
588
  rl.close();
259
589
  }
260
590
  }
591
+ async function fileExists(filePath) {
592
+ try {
593
+ await access(filePath);
594
+ return true;
595
+ }
596
+ catch (error) {
597
+ if (error.code === "ENOENT") {
598
+ return false;
599
+ }
600
+ throw error;
601
+ }
602
+ }
261
603
  async function ensureTargetDir(dir) {
262
604
  try {
263
605
  const stats = await stat(dir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uns-kit/cli",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "Command line scaffolding tool for UNS applications",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,7 +25,7 @@
25
25
  "templates"
26
26
  ],
27
27
  "dependencies": {
28
- "@uns-kit/core": "0.0.16"
28
+ "@uns-kit/core": "0.0.19"
29
29
  },
30
30
  "scripts": {
31
31
  "build": "tsc -p tsconfig.build.json",
@@ -0,0 +1,15 @@
1
+ import type { CodegenConfig } from '@graphql-codegen/cli';
2
+ import { ConfigFile } from '@uns-kit/core';
3
+
4
+ const appConfig = await ConfigFile.loadConfig();
5
+
6
+ const config: CodegenConfig = {
7
+ schema: appConfig.uns.graphql,
8
+ generates: {
9
+ 'src/graphql/schema.ts': {
10
+ plugins: ['typescript', 'typescript-operations', 'typescript-resolvers'],
11
+ },
12
+ },
13
+ };
14
+
15
+ export default config;
@@ -0,0 +1 @@
1
+ export type UnsTags = string & {};
@@ -0,0 +1 @@
1
+ export type UnsTopics = string & {};
@@ -9,6 +9,8 @@ pnpm run dev # start the local development loop
9
9
  pnpm run build # emit dist/ output
10
10
  pnpm run start # run the compiled entrypoint
11
11
  pnpm run generate-config-schema # regenerate config.schema.json and AppConfig types
12
+ pnpm run codegen # regenerate typed GraphQL operations (after configure-codegen)
13
+ pnpm run refresh-uns # rebuild UNS topics/tags from the live schema
12
14
  ```
13
15
 
14
16
  ## Configuration
@@ -21,4 +23,6 @@ Update `config.json` with your broker, UNS URLs, and credentials. The generated
21
23
  - Create MQTT proxies or Temporal workflows inside `src/index.ts`.
22
24
  - Extend `src/config/project.config.extension.ts` with project-specific sections and run `pnpm run generate-config-schema`.
23
25
  - Run `uns-kit configure-devops` to add the Azure DevOps pull-request tooling.
26
+ - Run `uns-kit configure-vscode` to copy workspace/launch configuration for VS Code.
27
+ - Run `uns-kit configure-codegen` to scaffold GraphQL code generation and UNS refresh scripts.
24
28
  - Commit your new project and start building!
@@ -18,6 +18,8 @@
18
18
  "host": "localhost:1883"
19
19
  },
20
20
  "devops": {
21
- "organization": "sijit"
21
+ "provider": "azure-devops",
22
+ "organization": "sijit",
23
+ "project": "industry40"
22
24
  }
23
25
  }
@@ -0,0 +1,2 @@
1
+ // Generated UNS tag union. Run `pnpm refresh-uns` to update.
2
+ export type UnsTags = string & {};
@@ -0,0 +1,2 @@
1
+ // Generated UNS topic union. Run `pnpm refresh-uns` to update.
2
+ export type UnsTopics = string & {};
@@ -0,0 +1,22 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "type": "node",
6
+ "request": "launch",
7
+ "name": "uns-kit: dev",
8
+ "runtimeArgs": [
9
+ "--enable-source-maps",
10
+ "--import",
11
+ "tsx"
12
+ ],
13
+ "program": "${workspaceFolder}/src/index.ts",
14
+ "cwd": "${workspaceFolder}",
15
+ "sourceMaps": true,
16
+ "skipFiles": ["<node_internals>/**"],
17
+ "resolveSourceMapLocations": [
18
+ "${workspaceFolder}/**"
19
+ ]
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "folders": [
3
+ {
4
+ "path": "."
5
+ }
6
+ ],
7
+ "settings": {
8
+ "files.exclude": {
9
+ "dist": true,
10
+ "node_modules": true
11
+ }
12
+ }
13
+ }
@@ -1,4 +0,0 @@
1
- {
2
- "name": "template-uns-rtt",
3
- "version": "2.0.0"
4
- }