@wneng/create-keel 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/index.js +74 -25
  2. package/dist/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/src/standards/templates/tech-stack-server.md.eta +34 -2
  5. package/src/templates/contracts-base/files/dictionaries/event-types.yaml +16 -0
  6. package/src/templates/contracts-base/files/dictionaries/locales.yaml +12 -0
  7. package/src/templates/contracts-base/files/dictionaries/roles.yaml +12 -0
  8. package/src/templates/contracts-base/files/dictionaries/status.yaml +16 -0
  9. package/src/templates/contracts-base/files/errors/common-errors.yaml +40 -0
  10. package/src/templates/contracts-base/files/errors/server-errors.yaml +16 -0
  11. package/src/templates/contracts-base/files/events/audit-event.schema.json +47 -0
  12. package/src/templates/contracts-base/fragment.yaml +22 -10
  13. package/src/templates/contracts-rest/fragment.yaml +2 -1
  14. package/src/templates/contracts-rest-by-audience/files/_components-README.md +32 -0
  15. package/src/templates/contracts-rest-by-audience/files/_components-schemas.yaml +78 -0
  16. package/src/templates/contracts-rest-by-audience/files/admin-api.yaml +37 -0
  17. package/src/templates/contracts-rest-by-audience/files/agent-api.yaml +37 -0
  18. package/src/templates/contracts-rest-by-audience/files/user-api.yaml +33 -0
  19. package/src/templates/contracts-rest-by-audience/fragment.yaml +22 -0
  20. package/src/templates/contracts-rest-events/fragment.yaml +2 -1
  21. package/src/templates/contracts-rest-events-by-audience/files/_components-README.md +32 -0
  22. package/src/templates/contracts-rest-events-by-audience/files/_components-schemas.yaml +78 -0
  23. package/src/templates/contracts-rest-events-by-audience/files/admin-api.yaml +37 -0
  24. package/src/templates/contracts-rest-events-by-audience/files/agent-api.yaml +37 -0
  25. package/src/templates/contracts-rest-events-by-audience/files/asyncapi.yaml +8 -0
  26. package/src/templates/contracts-rest-events-by-audience/files/user-api.yaml +33 -0
  27. package/src/templates/contracts-rest-events-by-audience/fragment.yaml +25 -0
  28. package/src/templates/docs-skeleton/files/governance-contracts-layout.md +172 -0
  29. package/src/templates/docs-skeleton/fragment.yaml +3 -0
  30. package/src/templates/root-files/files/AGENTS.md +2 -0
  31. package/src/templates/contracts-base/files/dictionaries/domain-models.yaml +0 -3
  32. package/src/templates/contracts-base/files/dictionaries/enums.yaml +0 -3
  33. package/src/templates/contracts-base/files/errors/error-codes.yaml +0 -6
  34. package/src/templates/contracts-base/files/events-gitkeep +0 -0
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/version.ts
4
- var SCAFFOLDER_VERSION = "0.4.2";
4
+ var SCAFFOLDER_VERSION = "0.5.0";
5
5
 
6
6
  // src/schema/options.ts
7
7
  import Ajv from "ajv";
@@ -29,6 +29,7 @@ var AI_TOOLS = ["kiro", "cursor", "claude-code", "codex", "none"];
29
29
  var LICENSE_KINDS = ["mit", "apache-2.0", "proprietary"];
30
30
  var CI_PLATFORMS = ["gitee", "github"];
31
31
  var ROLE_KINDS = ["design", "qa", "field", "data", "legal-security", "marketing"];
32
+ var OPENAPI_LAYOUTS = ["single", "by-audience"];
32
33
  var PROJECT_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,62}$/;
33
34
  var PROJECT_NAME_PATTERN_SOURCE = "^[a-z0-9][a-z0-9-]{0,62}$";
34
35
  var OPTIONS_FIELD_ORDER = [
@@ -47,7 +48,8 @@ var OPTIONS_FIELD_ORDER = [
47
48
  "ci",
48
49
  "roles",
49
50
  "multiApp",
50
- "database"
51
+ "database",
52
+ "openapiLayout"
51
53
  ];
52
54
  var OPTIONS_SCHEMA = {
53
55
  $schema: "http://json-schema.org/draft-07/schema#",
@@ -71,7 +73,8 @@ var OPTIONS_SCHEMA = {
71
73
  "ci",
72
74
  "roles",
73
75
  "multiApp",
74
- "database"
76
+ "database",
77
+ "openapiLayout"
75
78
  ],
76
79
  properties: {
77
80
  projectName: {
@@ -96,7 +99,8 @@ var OPTIONS_SCHEMA = {
96
99
  uniqueItems: true
97
100
  },
98
101
  multiApp: { type: "boolean" },
99
- database: { type: "string", enum: [...DATABASE_KINDS] }
102
+ database: { type: "string", enum: [...DATABASE_KINDS] },
103
+ openapiLayout: { type: "string", enum: [...OPENAPI_LAYOUTS] }
100
104
  },
101
105
  // Cross-axis constraint: `backend === 'none'` requires `database === 'none'`.
102
106
  // A non-'none' database without a backend would be a contradiction (no
@@ -452,6 +456,7 @@ function parseCreateArgs(args) {
452
456
  let gitLfs;
453
457
  let integrations;
454
458
  let database;
459
+ let openapiLayout;
455
460
  function takeEnum(flag, next, enumValues) {
456
461
  if (next === void 0 || next.startsWith("--")) {
457
462
  throw new UserInputError(
@@ -548,6 +553,11 @@ function parseCreateArgs(args) {
548
553
  i += 1;
549
554
  break;
550
555
  }
556
+ case "--openapi-layout": {
557
+ openapiLayout = takeEnum("--openapi-layout", args[i + 1], OPENAPI_LAYOUTS);
558
+ i += 1;
559
+ break;
560
+ }
551
561
  case "--engineering-standards": {
552
562
  const next = args[i + 1];
553
563
  if (next === void 0 || next.startsWith("--")) {
@@ -673,7 +683,8 @@ function parseCreateArgs(args) {
673
683
  ...license !== void 0 ? { license } : {},
674
684
  ...gitLfs !== void 0 ? { gitLfs } : {},
675
685
  ...integrations !== void 0 ? { integrations } : {},
676
- ...database !== void 0 ? { database } : {}
686
+ ...database !== void 0 ? { database } : {},
687
+ ...openapiLayout !== void 0 ? { openapiLayout } : {}
677
688
  };
678
689
  if (sampleFeature && dryRun) {
679
690
  throw new UserInputError(
@@ -762,6 +773,7 @@ function helpText() {
762
773
  " --ai <kind> " + AI_TOOLS.join("|"),
763
774
  " --license <kind> " + LICENSE_KINDS.join("|"),
764
775
  " --database <kind> " + DATABASE_KINDS.join("|") + " (requires backend != none)",
776
+ " --openapi-layout <kind> " + OPENAPI_LAYOUTS.join("|") + " (default: single; ignored when contract has no OpenAPI)",
765
777
  " --gitLfs Enable Git LFS in the generated project",
766
778
  " --integrations Enable docs/06-\u96C6\u6210\u5BF9\u63A5/ at create time",
767
779
  " -h, --help Show this help",
@@ -816,7 +828,8 @@ var OPTION_DEFAULTS = {
816
828
  ci: "gitee",
817
829
  roles: [],
818
830
  multiApp: false,
819
- database: "none"
831
+ database: "none",
832
+ openapiLayout: "single"
820
833
  };
821
834
  function buildDefaultOptions(projectName) {
822
835
  return { projectName, ...OPTION_DEFAULTS };
@@ -853,7 +866,8 @@ var PARTIAL_OPTIONS_SCHEMA = {
853
866
  uniqueItems: true
854
867
  },
855
868
  multiApp: { type: "boolean" },
856
- database: { type: "string", enum: [...DATABASE_KINDS] }
869
+ database: { type: "string", enum: [...DATABASE_KINDS] },
870
+ openapiLayout: { type: "string", enum: [...OPENAPI_LAYOUTS] }
857
871
  }
858
872
  };
859
873
  var ajv2 = new Ajv2({ allErrors: true, strict: true });
@@ -1051,6 +1065,25 @@ async function promptForOptions(input) {
1051
1065
  report,
1052
1066
  "contract"
1053
1067
  );
1068
+ let openapiLayout;
1069
+ const contractHasOpenapi = contract === "rest" || contract === "rest+events";
1070
+ if (!contractHasOpenapi) {
1071
+ openapiLayout = "single";
1072
+ report("openapiLayout", openapiLayout, true);
1073
+ } else if (pre.openapiLayout !== void 0) {
1074
+ openapiLayout = pre.openapiLayout;
1075
+ report("openapiLayout", openapiLayout, false);
1076
+ } else {
1077
+ openapiLayout = await p.select(
1078
+ "OpenAPI \u6587\u4EF6\u5E03\u5C40",
1079
+ [
1080
+ { value: "single", name: "single \u5355\u6587\u4EF6 contracts/openapi/api.yaml\uFF08\u5C0F\u9879\u76EE\uFF0C\u9ED8\u8BA4\uFF09" },
1081
+ { value: "by-audience", name: "by-audience \u6309\u53D7\u4F17\u62C6 admin / user / agent + _components/schemas.yaml\uFF08\u591A\u56E2\u961F\u573A\u666F\uFF09" }
1082
+ ],
1083
+ OPTION_DEFAULTS.openapiLayout
1084
+ );
1085
+ report("openapiLayout", openapiLayout, false);
1086
+ }
1054
1087
  const ai = await chooseAxis(
1055
1088
  p,
1056
1089
  pre.ai,
@@ -1121,7 +1154,8 @@ async function promptForOptions(input) {
1121
1154
  ci,
1122
1155
  roles,
1123
1156
  multiApp: OPTION_DEFAULTS.multiApp,
1124
- database
1157
+ database,
1158
+ openapiLayout
1125
1159
  };
1126
1160
  }
1127
1161
  async function chooseAxis(prompter, prefilled, question, domain, def, report, axisName) {
@@ -1173,7 +1207,8 @@ var APPLIES_WHEN_SCHEMA = {
1173
1207
  integrations: { type: "boolean" },
1174
1208
  ci: { type: "string", enum: [...CI_PLATFORMS] },
1175
1209
  role: { type: "string", enum: [...ROLE_KINDS] },
1176
- database: { type: "string", enum: [...DATABASE_KINDS] }
1210
+ database: { type: "string", enum: [...DATABASE_KINDS] },
1211
+ openapiLayout: { type: "string", enum: [...OPENAPI_LAYOUTS] }
1177
1212
  }
1178
1213
  };
1179
1214
  var MANIFEST_SCHEMA = {
@@ -1645,6 +1680,9 @@ function assertMetadata(value) {
1645
1680
  if (!("database" in opts)) {
1646
1681
  opts.database = "none";
1647
1682
  }
1683
+ if (!("openapiLayout" in opts)) {
1684
+ opts.openapiLayout = "single";
1685
+ }
1648
1686
  }
1649
1687
  if (!validateFn3(value)) {
1650
1688
  throw new MetadataValidationError(collectIssues3());
@@ -2224,6 +2262,20 @@ function detectRuntimeManifests(options) {
2224
2262
  }
2225
2263
 
2226
2264
  // src/standards/plan.ts
2265
+ function defaultOrmConvention(options) {
2266
+ if (options.backend === "none" || options.database === "none") return "none";
2267
+ if (options.database === "elasticsearch") return "none";
2268
+ switch (options.backend) {
2269
+ case "java":
2270
+ return "mybatis-plus-only";
2271
+ case "node":
2272
+ return "knex-only";
2273
+ case "python":
2274
+ return "sqlalchemy-orm-only";
2275
+ case "go":
2276
+ return "none";
2277
+ }
2278
+ }
2227
2279
  async function buildStandardsPlan(input) {
2228
2280
  const templatesRoot = input.templatesRoot ?? defaultStandardsTemplatesRoot();
2229
2281
  const now = input.now ?? /* @__PURE__ */ new Date();
@@ -2248,6 +2300,16 @@ async function buildStandardsPlan(input) {
2248
2300
  if (input.tier === "standard" || input.tier === "full") {
2249
2301
  for (const m of manifests) {
2250
2302
  const fileBaseName = `tech-stack-${m.env}.md`;
2303
+ const standardsBase = {
2304
+ tier: input.tier,
2305
+ env: m.env,
2306
+ language: m.language,
2307
+ packageManager: m.packageManager,
2308
+ manifestPath: m.manifestPath,
2309
+ lastReviewed,
2310
+ multiApp: input.options.multiApp
2311
+ };
2312
+ const standards = m.env === "server" ? { ...standardsBase, ormConvention: defaultOrmConvention(input.options) } : standardsBase;
2251
2313
  files.push(
2252
2314
  await renderStandardsTemplate({
2253
2315
  templatesRoot,
@@ -2257,21 +2319,7 @@ async function buildStandardsPlan(input) {
2257
2319
  options: input.options,
2258
2320
  scaffolderVersion: input.scaffolderVersion,
2259
2321
  now,
2260
- standards: {
2261
- tier: input.tier,
2262
- env: m.env,
2263
- language: m.language,
2264
- packageManager: m.packageManager,
2265
- manifestPath: m.manifestPath,
2266
- lastReviewed,
2267
- multiApp: input.options.multiApp
2268
- // Smuggle dependencies through standards context so the
2269
- // template can iterate. We attach via Object.assign-style
2270
- // cast because StandardsRenderContext does not declare it
2271
- // (deps are only read by the tech-stack templates and the
2272
- // shape varies per env, so keeping it untyped here is
2273
- // acceptable).
2274
- },
2322
+ standards,
2275
2323
  extraStandardsFields: { dependencies: m.dependencies }
2276
2324
  })
2277
2325
  );
@@ -2531,7 +2579,8 @@ async function resolveOptions(input) {
2531
2579
  ...flags.gitLfs !== void 0 ? { gitLfs: flags.gitLfs } : {},
2532
2580
  ...flags.integrations !== void 0 ? { integrations: flags.integrations } : {},
2533
2581
  ...flags.multiApp !== void 0 ? { multiApp: flags.multiApp } : {},
2534
- ...flags.database !== void 0 ? { database: flags.database } : {}
2582
+ ...flags.database !== void 0 ? { database: flags.database } : {},
2583
+ ...flags.openapiLayout !== void 0 ? { openapiLayout: flags.openapiLayout } : {}
2535
2584
  };
2536
2585
  if (flags.yes) {
2537
2586
  const full = { ...buildDefaultOptions(projectName), ...merged };