@sharpee/ext-testing 0.9.61-beta

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 (121) hide show
  1. package/LICENSE +21 -0
  2. package/dist/annotations/context.d.ts +16 -0
  3. package/dist/annotations/context.d.ts.map +1 -0
  4. package/dist/annotations/context.js +42 -0
  5. package/dist/annotations/context.js.map +1 -0
  6. package/dist/annotations/index.d.ts +6 -0
  7. package/dist/annotations/index.d.ts.map +1 -0
  8. package/dist/annotations/index.js +12 -0
  9. package/dist/annotations/index.js.map +1 -0
  10. package/dist/annotations/store.d.ts +11 -0
  11. package/dist/annotations/store.d.ts.map +1 -0
  12. package/dist/annotations/store.js +191 -0
  13. package/dist/annotations/store.js.map +1 -0
  14. package/dist/checkpoints/index.d.ts +3 -0
  15. package/dist/checkpoints/index.d.ts.map +1 -0
  16. package/dist/checkpoints/index.js +12 -0
  17. package/dist/checkpoints/index.js.map +1 -0
  18. package/dist/checkpoints/serializer.d.ts +21 -0
  19. package/dist/checkpoints/serializer.d.ts.map +1 -0
  20. package/dist/checkpoints/serializer.js +95 -0
  21. package/dist/checkpoints/serializer.js.map +1 -0
  22. package/dist/checkpoints/store.d.ts +20 -0
  23. package/dist/checkpoints/store.d.ts.map +1 -0
  24. package/dist/checkpoints/store.js +193 -0
  25. package/dist/checkpoints/store.js.map +1 -0
  26. package/dist/commands/index.d.ts +2 -0
  27. package/dist/commands/index.d.ts.map +1 -0
  28. package/dist/commands/index.js +8 -0
  29. package/dist/commands/index.js.map +1 -0
  30. package/dist/commands/registry.d.ts +28 -0
  31. package/dist/commands/registry.d.ts.map +1 -0
  32. package/dist/commands/registry.js +70 -0
  33. package/dist/commands/registry.js.map +1 -0
  34. package/dist/context/debug-context.d.ts +21 -0
  35. package/dist/context/debug-context.d.ts.map +1 -0
  36. package/dist/context/debug-context.js +172 -0
  37. package/dist/context/debug-context.js.map +1 -0
  38. package/dist/context/index.d.ts +2 -0
  39. package/dist/context/index.d.ts.map +1 -0
  40. package/dist/context/index.js +8 -0
  41. package/dist/context/index.js.map +1 -0
  42. package/dist/extension.d.ts +78 -0
  43. package/dist/extension.d.ts.map +1 -0
  44. package/dist/extension.js +938 -0
  45. package/dist/extension.js.map +1 -0
  46. package/dist/index.d.ts +40 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +63 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/types.d.ts +375 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +8 -0
  53. package/dist/types.js.map +1 -0
  54. package/dist-npm/annotations/context.d.ts +16 -0
  55. package/dist-npm/annotations/context.d.ts.map +1 -0
  56. package/dist-npm/annotations/context.js +42 -0
  57. package/dist-npm/annotations/context.js.map +1 -0
  58. package/dist-npm/annotations/index.d.ts +6 -0
  59. package/dist-npm/annotations/index.d.ts.map +1 -0
  60. package/dist-npm/annotations/index.js +12 -0
  61. package/dist-npm/annotations/index.js.map +1 -0
  62. package/dist-npm/annotations/store.d.ts +11 -0
  63. package/dist-npm/annotations/store.d.ts.map +1 -0
  64. package/dist-npm/annotations/store.js +191 -0
  65. package/dist-npm/annotations/store.js.map +1 -0
  66. package/dist-npm/checkpoints/index.d.ts +3 -0
  67. package/dist-npm/checkpoints/index.d.ts.map +1 -0
  68. package/dist-npm/checkpoints/index.js +12 -0
  69. package/dist-npm/checkpoints/index.js.map +1 -0
  70. package/dist-npm/checkpoints/serializer.d.ts +21 -0
  71. package/dist-npm/checkpoints/serializer.d.ts.map +1 -0
  72. package/dist-npm/checkpoints/serializer.js +95 -0
  73. package/dist-npm/checkpoints/serializer.js.map +1 -0
  74. package/dist-npm/checkpoints/store.d.ts +20 -0
  75. package/dist-npm/checkpoints/store.d.ts.map +1 -0
  76. package/dist-npm/checkpoints/store.js +193 -0
  77. package/dist-npm/checkpoints/store.js.map +1 -0
  78. package/dist-npm/commands/index.d.ts +2 -0
  79. package/dist-npm/commands/index.d.ts.map +1 -0
  80. package/dist-npm/commands/index.js +8 -0
  81. package/dist-npm/commands/index.js.map +1 -0
  82. package/dist-npm/commands/registry.d.ts +28 -0
  83. package/dist-npm/commands/registry.d.ts.map +1 -0
  84. package/dist-npm/commands/registry.js +70 -0
  85. package/dist-npm/commands/registry.js.map +1 -0
  86. package/dist-npm/context/debug-context.d.ts +21 -0
  87. package/dist-npm/context/debug-context.d.ts.map +1 -0
  88. package/dist-npm/context/debug-context.js +172 -0
  89. package/dist-npm/context/debug-context.js.map +1 -0
  90. package/dist-npm/context/index.d.ts +2 -0
  91. package/dist-npm/context/index.d.ts.map +1 -0
  92. package/dist-npm/context/index.js +8 -0
  93. package/dist-npm/context/index.js.map +1 -0
  94. package/dist-npm/extension.d.ts +78 -0
  95. package/dist-npm/extension.d.ts.map +1 -0
  96. package/dist-npm/extension.js +938 -0
  97. package/dist-npm/extension.js.map +1 -0
  98. package/dist-npm/index.d.ts +40 -0
  99. package/dist-npm/index.d.ts.map +1 -0
  100. package/dist-npm/index.js +63 -0
  101. package/dist-npm/index.js.map +1 -0
  102. package/dist-npm/types.d.ts +375 -0
  103. package/dist-npm/types.d.ts.map +1 -0
  104. package/dist-npm/types.js +8 -0
  105. package/dist-npm/types.js.map +1 -0
  106. package/package.json +42 -0
  107. package/src/annotations/context.ts +47 -0
  108. package/src/annotations/index.ts +6 -0
  109. package/src/annotations/store.ts +219 -0
  110. package/src/checkpoints/index.ts +2 -0
  111. package/src/checkpoints/serializer.ts +121 -0
  112. package/src/checkpoints/store.ts +188 -0
  113. package/src/commands/index.ts +1 -0
  114. package/src/commands/registry.ts +81 -0
  115. package/src/context/debug-context.ts +209 -0
  116. package/src/context/index.ts +1 -0
  117. package/src/extension.ts +1089 -0
  118. package/src/index.ts +69 -0
  119. package/src/types.ts +469 -0
  120. package/tsconfig.json +16 -0
  121. package/tsconfig.tsbuildinfo +1 -0
package/src/index.ts ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @sharpee/ext-testing
3
+ *
4
+ * Debug and testing tools extension for Sharpee IF engine.
5
+ *
6
+ * Provides:
7
+ * - Interactive debug mode (GDT-style) with short codes
8
+ * - Test commands ($teleport, $take, $assert, etc.) for transcripts
9
+ * - Checkpoint save/restore system
10
+ * - Playtester annotations (planned)
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { TestingExtension } from '@sharpee/ext-testing';
15
+ *
16
+ * const testing = new TestingExtension({
17
+ * debugMode: { enabled: true, prefix: 'gdt' },
18
+ * testMode: { enabled: true, deterministicRandom: true },
19
+ * checkpoints: { directory: './saves' }
20
+ * });
21
+ *
22
+ * // Execute GDT command
23
+ * const result = testing.executeGdtCommand('AH west-of-house', world);
24
+ *
25
+ * // Execute test command
26
+ * const result = testing.executeTestCommand('$teleport west-of-house', world);
27
+ *
28
+ * // Save/restore checkpoints
29
+ * await testing.saveCheckpoint('before-troll', world);
30
+ * await testing.restoreCheckpoint('before-troll', world);
31
+ * ```
32
+ */
33
+
34
+ // Main extension class
35
+ export { TestingExtension } from './extension.js';
36
+
37
+ // Types
38
+ export type {
39
+ TestingExtensionConfig,
40
+ ITestingExtension,
41
+ DebugContext,
42
+ DebugCommand,
43
+ CommandResult,
44
+ CommandCategory,
45
+ CommandRegistry,
46
+ CheckpointData,
47
+ CheckpointStore,
48
+ SerializedDaemon,
49
+ SerializedFuse,
50
+ // Annotation types (ADR-109)
51
+ AnnotationType,
52
+ Annotation,
53
+ AnnotationContext,
54
+ AnnotationSession,
55
+ AnnotationStore,
56
+ } from './types.js';
57
+
58
+ // Context utilities
59
+ export { createDebugContext, formatEntity, formatLocationChain } from './context/debug-context.js';
60
+
61
+ // Command registry utilities
62
+ export { createCommandRegistry, parseGdtInput, parseTestInput } from './commands/registry.js';
63
+
64
+ // Checkpoint utilities
65
+ export { serializeCheckpoint, deserializeCheckpoint, validateCheckpoint } from './checkpoints/serializer.js';
66
+ export { createFileStore, createMemoryStore, createLocalStorageStore } from './checkpoints/store.js';
67
+
68
+ // Annotation utilities (ADR-109)
69
+ export { createAnnotationStore, captureContext, createEmptyContext } from './annotations/index.js';
package/src/types.ts ADDED
@@ -0,0 +1,469 @@
1
+ /**
2
+ * @sharpee/ext-testing - Type definitions
3
+ *
4
+ * Core interfaces for the testing extension, generalized from Dungeo's GDT implementation.
5
+ */
6
+
7
+ import type { WorldModel, IFEntity, AuthorModel } from '@sharpee/world-model';
8
+
9
+ // ============================================================================
10
+ // Configuration Types
11
+ // ============================================================================
12
+
13
+ /**
14
+ * Configuration for the testing extension
15
+ */
16
+ export interface TestingExtensionConfig {
17
+ /**
18
+ * Interactive debug mode (GDT-style) configuration
19
+ */
20
+ debugMode?: {
21
+ /** Enable interactive debug commands */
22
+ enabled?: boolean;
23
+ /** Command prefix (default: 'gdt') */
24
+ prefix?: string;
25
+ /** Optional password to enter debug mode */
26
+ password?: string | null;
27
+ };
28
+
29
+ /**
30
+ * Test mode configuration for transcript testing
31
+ */
32
+ testMode?: {
33
+ /** Enable test commands ($teleport, $assert, etc.) */
34
+ enabled?: boolean;
35
+ /** Use deterministic random for reproducible tests */
36
+ deterministicRandom?: boolean;
37
+ /** Enable assertion commands */
38
+ assertions?: boolean;
39
+ };
40
+
41
+ /**
42
+ * Checkpoint configuration
43
+ */
44
+ checkpoints?: {
45
+ /** Directory for checkpoint files */
46
+ directory?: string;
47
+ };
48
+
49
+ /**
50
+ * Additional story-specific commands
51
+ */
52
+ commands?: DebugCommand[];
53
+ }
54
+
55
+ // ============================================================================
56
+ // Debug Context
57
+ // ============================================================================
58
+
59
+ /**
60
+ * Context provided to debug commands for inspecting and modifying game state.
61
+ * Wraps WorldModel with convenience methods for common debug operations.
62
+ */
63
+ export interface DebugContext {
64
+ /** Direct access to world model (read operations) */
65
+ readonly world: WorldModel;
66
+
67
+ /** Author model for bypassing game rules during setup */
68
+ readonly author: AuthorModel;
69
+
70
+ /** Current player entity */
71
+ readonly player: IFEntity;
72
+
73
+ /** Debug flags (story-specific state) */
74
+ readonly flags: Map<string, boolean>;
75
+
76
+ // Entity lookup
77
+ /**
78
+ * Find entity by ID or partial name match
79
+ */
80
+ findEntity(idOrName: string): IFEntity | undefined;
81
+
82
+ /**
83
+ * Find room by ID or partial name match
84
+ */
85
+ findRoom(idOrName: string): IFEntity | undefined;
86
+
87
+ /**
88
+ * Get all entities matching a predicate
89
+ */
90
+ findEntities(predicate: (entity: IFEntity) => boolean): IFEntity[];
91
+
92
+ // Location queries
93
+ /**
94
+ * Get current player location
95
+ */
96
+ getPlayerLocation(): IFEntity | undefined;
97
+
98
+ /**
99
+ * Get player inventory as array of entities
100
+ */
101
+ getInventory(): IFEntity[];
102
+
103
+ /**
104
+ * Get all entities at a location
105
+ */
106
+ getContents(locationId: string): IFEntity[];
107
+
108
+ // Mutations (bypass game rules)
109
+ /**
110
+ * Teleport player to a room
111
+ */
112
+ teleportPlayer(roomId: string): boolean;
113
+
114
+ /**
115
+ * Move an object to a location
116
+ */
117
+ moveObject(objectId: string, locationId: string): boolean;
118
+
119
+ /**
120
+ * Give an object to player (add to inventory)
121
+ */
122
+ takeObject(objectId: string): boolean;
123
+
124
+ /**
125
+ * Remove an object from the game (move to limbo)
126
+ */
127
+ removeObject(objectId: string): boolean;
128
+
129
+ /**
130
+ * Spawn an object at a location
131
+ */
132
+ spawnObject(objectId: string, locationId: string): boolean;
133
+
134
+ // Flag operations
135
+ /**
136
+ * Set a debug flag
137
+ */
138
+ setFlag(name: string, value: boolean): void;
139
+
140
+ /**
141
+ * Get a debug flag value
142
+ */
143
+ getFlag(name: string): boolean;
144
+ }
145
+
146
+ // ============================================================================
147
+ // Command System
148
+ // ============================================================================
149
+
150
+ /**
151
+ * Result of executing a debug command
152
+ */
153
+ export interface CommandResult {
154
+ /** Whether the command succeeded */
155
+ success: boolean;
156
+ /** Output lines to display */
157
+ output: string[];
158
+ /** Error message if failed */
159
+ error?: string;
160
+ /** Optional data for programmatic use */
161
+ data?: Record<string, unknown>;
162
+ }
163
+
164
+ /**
165
+ * Categories for organizing commands
166
+ */
167
+ export type CommandCategory = 'display' | 'alter' | 'toggle' | 'utility' | 'test' | 'annotation';
168
+
169
+ /**
170
+ * A debug/test command handler
171
+ */
172
+ export interface DebugCommand {
173
+ /**
174
+ * Short code for GDT mode (e.g., "AH" for teleport)
175
+ */
176
+ code: string;
177
+
178
+ /**
179
+ * Test syntax for transcript mode (e.g., "teleport")
180
+ */
181
+ testSyntax?: string;
182
+
183
+ /**
184
+ * Human-readable name
185
+ */
186
+ name: string;
187
+
188
+ /**
189
+ * Brief description for help text
190
+ */
191
+ description: string;
192
+
193
+ /**
194
+ * Command category for organization
195
+ */
196
+ category: CommandCategory;
197
+
198
+ /**
199
+ * Usage pattern (e.g., "teleport <room>")
200
+ */
201
+ usage?: string;
202
+
203
+ /**
204
+ * Execute the command
205
+ */
206
+ execute(context: DebugContext, args: string[]): CommandResult;
207
+ }
208
+
209
+ /**
210
+ * Registry for debug commands
211
+ */
212
+ export interface CommandRegistry {
213
+ /**
214
+ * Register a command
215
+ */
216
+ register(command: DebugCommand): void;
217
+
218
+ /**
219
+ * Get command by GDT code
220
+ */
221
+ getByCode(code: string): DebugCommand | undefined;
222
+
223
+ /**
224
+ * Get command by test syntax
225
+ */
226
+ getByTestSyntax(syntax: string): DebugCommand | undefined;
227
+
228
+ /**
229
+ * Get all commands
230
+ */
231
+ getAll(): DebugCommand[];
232
+
233
+ /**
234
+ * Get commands by category
235
+ */
236
+ getByCategory(category: CommandCategory): DebugCommand[];
237
+ }
238
+
239
+ // ============================================================================
240
+ // Checkpoint System
241
+ // ============================================================================
242
+
243
+ /**
244
+ * Serialized checkpoint data
245
+ */
246
+ export interface CheckpointData {
247
+ /** Format version */
248
+ version: '1.0.0';
249
+ /** When checkpoint was created */
250
+ timestamp: number;
251
+ /** Metadata about the checkpoint */
252
+ metadata: {
253
+ /** Optional name for the checkpoint */
254
+ name?: string;
255
+ /** Current turn number */
256
+ turn: number;
257
+ /** Player location at checkpoint */
258
+ location?: string;
259
+ };
260
+ /** Serialized world state */
261
+ worldState: string;
262
+ /** Scheduler state (daemons, fuses) */
263
+ schedulerState?: {
264
+ turn: number;
265
+ daemons: SerializedDaemon[];
266
+ fuses: SerializedFuse[];
267
+ };
268
+ }
269
+
270
+ /**
271
+ * Serialized daemon data
272
+ */
273
+ export interface SerializedDaemon {
274
+ id: string;
275
+ handler: string;
276
+ interval: number;
277
+ lastRun: number;
278
+ data?: Record<string, unknown>;
279
+ }
280
+
281
+ /**
282
+ * Serialized fuse data
283
+ */
284
+ export interface SerializedFuse {
285
+ id: string;
286
+ handler: string;
287
+ turnsRemaining: number;
288
+ data?: Record<string, unknown>;
289
+ }
290
+
291
+ /**
292
+ * Checkpoint storage interface
293
+ */
294
+ export interface CheckpointStore {
295
+ /**
296
+ * Save a checkpoint
297
+ */
298
+ save(name: string, data: CheckpointData): Promise<void>;
299
+
300
+ /**
301
+ * Load a checkpoint
302
+ */
303
+ load(name: string): Promise<CheckpointData | undefined>;
304
+
305
+ /**
306
+ * List available checkpoints
307
+ */
308
+ list(): Promise<string[]>;
309
+
310
+ /**
311
+ * Delete a checkpoint
312
+ */
313
+ delete(name: string): Promise<boolean>;
314
+
315
+ /**
316
+ * Check if checkpoint exists
317
+ */
318
+ exists(name: string): Promise<boolean>;
319
+ }
320
+
321
+ // ============================================================================
322
+ // Annotation System (ADR-109)
323
+ // ============================================================================
324
+
325
+ /**
326
+ * Types of annotations playtesters can create
327
+ */
328
+ export type AnnotationType = 'comment' | 'bug' | 'note' | 'confusing' | 'expected' | 'bookmark';
329
+
330
+ /**
331
+ * Context captured with each annotation
332
+ */
333
+ export interface AnnotationContext {
334
+ /** Current room ID */
335
+ roomId: string;
336
+ /** Current room name */
337
+ roomName: string;
338
+ /** Current turn number */
339
+ turn: number;
340
+ /** Current score */
341
+ score: number;
342
+ /** The command that was just executed */
343
+ lastCommand: string;
344
+ /** The game's response to that command */
345
+ lastResponse: string;
346
+ /** Items currently in player's inventory */
347
+ inventory: string[];
348
+ }
349
+
350
+ /**
351
+ * A single annotation from a playtester
352
+ */
353
+ export interface Annotation {
354
+ /** Unique identifier */
355
+ id: string;
356
+ /** When the annotation was created */
357
+ timestamp: number;
358
+ /** Type of annotation */
359
+ type: AnnotationType;
360
+ /** The annotation text */
361
+ text: string;
362
+ /** Game state context when annotation was made */
363
+ context: AnnotationContext;
364
+ /** Session this annotation belongs to (if any) */
365
+ sessionId?: string;
366
+ }
367
+
368
+ /**
369
+ * A playtest session containing multiple annotations
370
+ */
371
+ export interface AnnotationSession {
372
+ /** Unique session identifier */
373
+ id: string;
374
+ /** Human-readable session name */
375
+ name: string;
376
+ /** When session started */
377
+ startTime: number;
378
+ /** When session ended (undefined if still active) */
379
+ endTime?: number;
380
+ /** All annotations in this session */
381
+ annotations: Annotation[];
382
+ }
383
+
384
+ /**
385
+ * Storage interface for annotations
386
+ */
387
+ export interface AnnotationStore {
388
+ // Session management
389
+ /** Start a new annotation session */
390
+ startSession(name: string): string;
391
+ /** End the current session */
392
+ endSession(): AnnotationSession | undefined;
393
+ /** Get the current active session */
394
+ getCurrentSession(): AnnotationSession | undefined;
395
+
396
+ // Annotation capture
397
+ /** Add an annotation with context */
398
+ addAnnotation(type: AnnotationType, text: string, context: AnnotationContext): Annotation;
399
+ /** Get all annotations (current session or all if no session) */
400
+ getAnnotations(): Annotation[];
401
+ /** Get annotations filtered by type */
402
+ getAnnotationsByType(type: AnnotationType): Annotation[];
403
+
404
+ // Export
405
+ /** Export annotations as markdown report */
406
+ exportMarkdown(): string;
407
+ /** Export annotations as JSON */
408
+ exportJson(): string;
409
+
410
+ // Cleanup
411
+ /** Clear all annotations */
412
+ clear(): void;
413
+ }
414
+
415
+ // ============================================================================
416
+ // Extension Interface
417
+ // ============================================================================
418
+
419
+ /**
420
+ * Interface for the testing extension
421
+ */
422
+ export interface ITestingExtension {
423
+ /** Extension configuration */
424
+ readonly config: TestingExtensionConfig;
425
+
426
+ /** Command registry */
427
+ readonly commands: CommandRegistry;
428
+
429
+ /** Checkpoint store */
430
+ readonly checkpoints: CheckpointStore;
431
+
432
+ /** Annotation store */
433
+ readonly annotations: AnnotationStore;
434
+
435
+ /**
436
+ * Execute a GDT-style command (e.g., "AH room-id")
437
+ */
438
+ executeGdtCommand(input: string, world: WorldModel): CommandResult;
439
+
440
+ /**
441
+ * Execute a test command (e.g., "$teleport room-id")
442
+ */
443
+ executeTestCommand(input: string, world: WorldModel): CommandResult;
444
+
445
+ /**
446
+ * Create a debug context for the current world state
447
+ */
448
+ createContext(world: WorldModel): DebugContext;
449
+
450
+ /**
451
+ * Save current state as checkpoint
452
+ */
453
+ saveCheckpoint(name: string, world: WorldModel): Promise<void>;
454
+
455
+ /**
456
+ * Restore state from checkpoint
457
+ */
458
+ restoreCheckpoint(name: string, world: WorldModel): Promise<boolean>;
459
+
460
+ /**
461
+ * Set context for annotation commands (called by transcript-tester after each command)
462
+ */
463
+ setCommandContext(command: string, response: string): void;
464
+
465
+ /**
466
+ * Add an annotation directly (for # comments from transcript-tester)
467
+ */
468
+ addAnnotation(type: AnnotationType, text: string, world: WorldModel): Annotation;
469
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "declaration": true,
7
+ "declarationMap": true
8
+ },
9
+ "include": ["src/**/*"],
10
+ "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts", "tests"],
11
+ "references": [
12
+ { "path": "../../core" },
13
+ { "path": "../../if-domain" },
14
+ { "path": "../../world-model" }
15
+ ]
16
+ }