miaoda-game-devkit 0.2.8 → 0.2.9

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.
@@ -46,6 +46,7 @@ function createGameplayContractMetadata(contract) {
46
46
  }
47
47
  function validateGameplayAuditOptions(options, projectRoot) {
48
48
  const issues = [];
49
+ const contracts = options.contracts;
49
50
  const scenes = [...new Set(options.scenes.map((scene) => scene.trim()).filter(Boolean))];
50
51
  const mode = options.mode ?? "interactive";
51
52
  if (mode === "non-interactive") {
@@ -67,7 +68,7 @@ function validateGameplayAuditOptions(options, projectRoot) {
67
68
  const coveredScenes = /* @__PURE__ */ new Set();
68
69
  const kinds = /* @__PURE__ */ new Map();
69
70
  const invalidKindContracts = [];
70
- for (const contract of options.contracts) {
71
+ for (const contract of contracts ?? []) {
71
72
  const id = contract.id.trim();
72
73
  if (!id) {
73
74
  issues.push("gameplayAudit.contracts \u4E2D\u5B58\u5728\u7A7A contract id\u3002");
@@ -93,7 +94,6 @@ function validateGameplayAuditOptions(options, projectRoot) {
93
94
  kinds.set(contract.kind, list);
94
95
  }
95
96
  if (contract.kind === "playthrough") {
96
- if (!contract.requireInput) issues.push(`playthrough contract "${id}" \u5FC5\u987B requireInput: true\u3002`);
97
97
  if (!contract.requireFrameAdvance) issues.push(`playthrough contract "${id}" \u5FC5\u987B requireFrameAdvance: true\u3002`);
98
98
  if (normalizeList(contract.requireCheckpoint).length === 0) issues.push(`playthrough contract "${id}" \u5FC5\u987B\u58F0\u660E requireCheckpoint\u3002`);
99
99
  }
@@ -101,7 +101,6 @@ function validateGameplayAuditOptions(options, projectRoot) {
101
101
  issues.push(`transition contract "${id}" \u5FC5\u987B\u58F0\u660E requireTransition\u3002`);
102
102
  }
103
103
  if (contract.kind === "recovery") {
104
- if (!contract.requireInput) issues.push(`recovery contract "${id}" \u5FC5\u987B requireInput: true\u3002`);
105
104
  if (!contract.requireFrameAdvance) issues.push(`recovery contract "${id}" \u5FC5\u987B requireFrameAdvance: true\u3002`);
106
105
  if (normalizeList(contract.requireRestart).length === 0) issues.push(`recovery contract "${id}" \u5FC5\u987B\u58F0\u660E requireRestart\u3002`);
107
106
  if (normalizeList(contract.requireCheckpoint).length < 2) issues.push(`recovery contract "${id}" \u81F3\u5C11\u9700\u8981\u4E24\u4E2A checkpoint\uFF08\u91CD\u7F6E\u524D\u540E\uFF09\u3002`);
@@ -123,32 +122,25 @@ function validateGameplayAuditOptions(options, projectRoot) {
123
122
  `\u4EE5\u4E0B contract \u7F3A\u5C11\u6709\u6548 kind\uFF1A${invalidKindContracts.map((id) => `"${id}"`).join("\u3001")}\u3002\u8BF7\u4E3A\u6BCF\u4E2A contract \u8BBE\u7F6E kind: "boot"\u3001"playthrough"\u3001"transition" \u6216 "recovery"\u3002`
124
123
  );
125
124
  }
126
- for (const scene of scenes) {
127
- if (!coveredScenes.has(scene)) {
128
- issues.push(`Scene "${scene}" \u6CA1\u6709\u4EFB\u4F55 gameplay contract \u8D1F\u8D23\u9A8C\u8BC1\u3002`);
125
+ if (contracts) {
126
+ for (const scene of scenes) {
127
+ if (!coveredScenes.has(scene)) {
128
+ issues.push(`Scene "${scene}" \u6CA1\u6709\u4EFB\u4F55 gameplay contract \u8D1F\u8D23\u9A8C\u8BC1\u3002`);
129
+ }
129
130
  }
131
+ if (contracts.length === 0)
132
+ issues.push("gameplayAudit.contracts \u5B58\u5728\u65F6\u4E0D\u80FD\u4E3A\u7A7A\uFF1B\u4E0D\u4F7F\u7528\u663E\u5F0F\u6E05\u5355\u8BF7\u7701\u7565\u8BE5\u5B57\u6BB5\u3002");
130
133
  }
131
- if (options.contracts.length === 0)
132
- issues.push("gameplayAudit.contracts \u4E0D\u80FD\u4E3A\u7A7A\u3002");
133
- if (mode === "interactive") {
134
- const hasPlaythrough = (kinds.get("playthrough") ?? []).length > 0;
135
- const hasInput = options.contracts.some((contract) => contract.requireInput);
136
- const hasCheckpoint = options.contracts.some((contract) => normalizeList(contract.requireCheckpoint).length > 0);
137
- if (!hasPlaythrough || !hasInput || !hasCheckpoint) {
134
+ if (mode === "interactive" && contracts) {
135
+ const hasPlaythrough = (kinds.get("playthrough") ?? []).some(
136
+ (contract) => Boolean(contract.requireFrameAdvance) && normalizeList(contract.requireCheckpoint).length > 0
137
+ );
138
+ const hasInput = contracts.some((contract) => contract.requireInput);
139
+ if (!hasPlaythrough || !hasInput) {
138
140
  issues.push(
139
- '\u4EA4\u4E92\u9879\u76EE\u7F3A\u5C11\u6700\u4F4E\u73A9\u6CD5\u57FA\u7EBF\uFF1A\u81F3\u5C11\u4E00\u4E2A kind: "playthrough" contract \u5FC5\u987B\u540C\u65F6\u58F0\u660E requireInput: true\u3001requireFrameAdvance: true \u548C requireCheckpoint: ["progress"]\u3002'
141
+ "\u4EA4\u4E92\u9879\u76EE\u7F3A\u5C11\u6700\u4F4E\u73A9\u6CD5\u57FA\u7EBF\uFF1A\u81F3\u5C11\u4E00\u4E2A playthrough contract \u5FC5\u987B\u58F0\u660E requireFrameAdvance \u548C checkpoint\uFF0C\u4E14\u81F3\u5C11\u4E00\u4E2A contract \u5FC5\u987B\u58F0\u660E requireInput: true\u3002"
140
142
  );
141
143
  }
142
- const entryScene = scenes[0];
143
- for (const scene of scenes.slice(1)) {
144
- const transitioned = options.contracts.some(
145
- (contract) => (contract.kind === "transition" || contract.kind === "playthrough") && normalizeList(contract.requireTransition).includes(scene)
146
- );
147
- if (!transitioned) issues.push(`\u975E\u5165\u53E3 Scene "${scene}" \u5FC5\u987B\u7531 transition/playthrough contract \u901A\u8FC7\u751F\u4EA7\u8F6C\u573A\u8986\u76D6\u3002`);
148
- }
149
- if (entryScene && scenes.length > 1 && (kinds.get("transition") ?? []).length === 0 && (kinds.get("playthrough") ?? []).length === 0 && invalidKindContracts.length === 0) {
150
- issues.push(`\u4EA4\u4E92\u9879\u76EE\u5165\u53E3 Scene "${entryScene}" \u7F3A\u5C11 transition/playthrough flow\u3002`);
151
- }
152
144
  const restartSignal = projectRoot ? containsProductionRestart(projectRoot) : false;
153
145
  if (restartSignal && options.flows?.restart !== true) {
154
146
  issues.push("\u68C0\u6D4B\u5230\u751F\u4EA7 Scene \u4F7F\u7528 scene.restart()\uFF1B\u5FC5\u987B\u58F0\u660E flows.restart: true \u5E76\u63D0\u4F9B kind: recovery contract\u3002");
@@ -209,11 +201,21 @@ function getDefinitionIssues(expected, actual) {
209
201
  }
210
202
  function auditGameplayCollection(options, file, tests) {
211
203
  const normalizedFile = file.replaceAll("\\", "/");
212
- const expected = options.contracts.filter(
204
+ const expected = (options.contracts ?? []).filter(
213
205
  (contract) => contract.testFile.replaceAll("\\", "/") === normalizedFile
214
206
  );
215
207
  const actual = tests.filter((test) => test.metadata);
216
208
  const issues = [];
209
+ if (!options.contracts) {
210
+ const actualIds = /* @__PURE__ */ new Set();
211
+ for (const test of actual) {
212
+ const id = test.metadata?.id;
213
+ if (!id) continue;
214
+ if (actualIds.has(id)) issues.push(`contract "${id}" \u5728 ${normalizedFile} \u4E2D\u91CD\u590D\u5B9A\u4E49\u3002`);
215
+ actualIds.add(id);
216
+ }
217
+ return { passed: issues.length === 0, issues };
218
+ }
217
219
  for (const contract of expected) {
218
220
  const matches = actual.filter((test) => test.metadata?.id === contract.id);
219
221
  if (matches.length === 0) {
@@ -286,7 +288,25 @@ function auditGameplayRun(options, tests) {
286
288
  const issues = [];
287
289
  const actualContracts = tests.filter((test) => test.metadata);
288
290
  const observedEvidence = [];
289
- for (const expected of options.contracts) {
291
+ const discoveredContracts = actualContracts.flatMap((test) => {
292
+ const metadata = test.metadata;
293
+ if (!metadata) return [];
294
+ return [{
295
+ id: metadata.id,
296
+ kind: metadata.kind,
297
+ testFile: test.file,
298
+ scenes: metadata.scenes,
299
+ ...metadata.requirements
300
+ }];
301
+ });
302
+ const expectedContracts = options.contracts ?? discoveredContracts;
303
+ if (!options.contracts) {
304
+ const discoveredIssues = validateGameplayAuditOptions(
305
+ { ...options, contracts: discoveredContracts }
306
+ );
307
+ issues.push(...discoveredIssues);
308
+ }
309
+ for (const expected of expectedContracts) {
290
310
  const matches = actualContracts.filter(
291
311
  (test2) => test2.metadata?.id === expected.id
292
312
  );
@@ -329,7 +349,7 @@ function auditGameplayRun(options, tests) {
329
349
  }
330
350
  for (const test of actualContracts) {
331
351
  const id = test.metadata?.id;
332
- if (id && !options.contracts.some((contract) => contract.id === id)) {
352
+ if (id && options.contracts && !options.contracts.some((contract) => contract.id === id)) {
333
353
  issues.push(
334
354
  `\u53D1\u73B0\u672A\u5217\u5165 gameplayAudit \u6E05\u5355\u7684 contract "${id}"\uFF08${test.file}\uFF09\u3002`
335
355
  );
@@ -524,7 +544,7 @@ var GameplayAuditReporter = class {
524
544
  (specification) => normalizePath(relative(this.projectRoot, specification.moduleId))
525
545
  )
526
546
  );
527
- for (const contract of this.options.contracts) {
547
+ for (const contract of this.options.contracts ?? []) {
528
548
  const testFile = normalizePath(contract.testFile);
529
549
  if (!scheduledFiles.has(testFile)) {
530
550
  this.reportPreflightIssue(
@@ -568,7 +588,7 @@ var GameplayAuditReporter = class {
568
588
  } else {
569
589
  console.log(
570
590
  `
571
- GAMEPLAY_AUDIT: ALL CONTRACTS PASSED (${this.options.contracts.length} contracts, ${this.options.scenes.length} scenes)`
591
+ GAMEPLAY_AUDIT: ALL CONTRACTS PASSED (${tests.filter((test) => test.metadata).length} contracts, ${this.options.scenes.length} scenes)`
572
592
  );
573
593
  console.log(
574
594
  "GAMEPLAY_AUDIT_NOTE: \u73A9\u6CD5\u68C0\u67E5\u5DF2\u901A\u8FC7\uFF1B\u4ECD\u9700\u4EE5\u6700\u540E\u7684 TEST_RESULT \u5224\u65AD\u6574\u4F53\u6210\u529F\u3002"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "miaoda-game-devkit",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Shared lint and deterministic Phaser HEADLESS testing tools for Miaoda games",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",