@plan2net/typo3-playwright-toolkit 0.1.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 (164) hide show
  1. package/LICENSE +338 -0
  2. package/README.md +404 -0
  3. package/dist/builders/content-builder.d.ts +30 -0
  4. package/dist/builders/content-builder.d.ts.map +1 -0
  5. package/dist/builders/content-builder.js +67 -0
  6. package/dist/builders/content-builder.js.map +1 -0
  7. package/dist/builders/content-factory.d.ts +10 -0
  8. package/dist/builders/content-factory.d.ts.map +1 -0
  9. package/dist/builders/content-factory.js +23 -0
  10. package/dist/builders/content-factory.js.map +1 -0
  11. package/dist/builders/core-content.d.ts +127 -0
  12. package/dist/builders/core-content.d.ts.map +1 -0
  13. package/dist/builders/core-content.js +223 -0
  14. package/dist/builders/core-content.js.map +1 -0
  15. package/dist/builders/fields.d.ts +7 -0
  16. package/dist/builders/fields.d.ts.map +1 -0
  17. package/dist/builders/fields.js +20 -0
  18. package/dist/builders/fields.js.map +1 -0
  19. package/dist/builders/identifier.d.ts +2 -0
  20. package/dist/builders/identifier.d.ts.map +1 -0
  21. package/dist/builders/identifier.js +11 -0
  22. package/dist/builders/identifier.js.map +1 -0
  23. package/dist/builders/page-builder.d.ts +40 -0
  24. package/dist/builders/page-builder.d.ts.map +1 -0
  25. package/dist/builders/page-builder.js +119 -0
  26. package/dist/builders/page-builder.js.map +1 -0
  27. package/dist/builders/request-context.d.ts +23 -0
  28. package/dist/builders/request-context.d.ts.map +1 -0
  29. package/dist/builders/request-context.js +32 -0
  30. package/dist/builders/request-context.js.map +1 -0
  31. package/dist/checks/accessibility.d.ts +34 -0
  32. package/dist/checks/accessibility.d.ts.map +1 -0
  33. package/dist/checks/accessibility.js +90 -0
  34. package/dist/checks/accessibility.js.map +1 -0
  35. package/dist/checks/csp.d.ts +39 -0
  36. package/dist/checks/csp.d.ts.map +1 -0
  37. package/dist/checks/csp.js +179 -0
  38. package/dist/checks/csp.js.map +1 -0
  39. package/dist/checks/screenshot.d.ts +18 -0
  40. package/dist/checks/screenshot.d.ts.map +1 -0
  41. package/dist/checks/screenshot.js +125 -0
  42. package/dist/checks/screenshot.js.map +1 -0
  43. package/dist/config.d.ts +100 -0
  44. package/dist/config.d.ts.map +1 -0
  45. package/dist/config.js +68 -0
  46. package/dist/config.js.map +1 -0
  47. package/dist/contract.d.ts +9 -0
  48. package/dist/contract.d.ts.map +1 -0
  49. package/dist/contract.js +25 -0
  50. package/dist/contract.js.map +1 -0
  51. package/dist/global-setup.d.ts +20 -0
  52. package/dist/global-setup.d.ts.map +1 -0
  53. package/dist/global-setup.js +103 -0
  54. package/dist/global-setup.js.map +1 -0
  55. package/dist/global-teardown.d.ts +34 -0
  56. package/dist/global-teardown.d.ts.map +1 -0
  57. package/dist/global-teardown.js +269 -0
  58. package/dist/global-teardown.js.map +1 -0
  59. package/dist/http/api-secret.d.ts +11 -0
  60. package/dist/http/api-secret.d.ts.map +1 -0
  61. package/dist/http/api-secret.js +27 -0
  62. package/dist/http/api-secret.js.map +1 -0
  63. package/dist/http/cleanup-client.d.ts +26 -0
  64. package/dist/http/cleanup-client.d.ts.map +1 -0
  65. package/dist/http/cleanup-client.js +91 -0
  66. package/dist/http/cleanup-client.js.map +1 -0
  67. package/dist/http/off-site-headers.d.ts +13 -0
  68. package/dist/http/off-site-headers.d.ts.map +1 -0
  69. package/dist/http/off-site-headers.js +27 -0
  70. package/dist/http/off-site-headers.js.map +1 -0
  71. package/dist/http/prepare-context.d.ts +4 -0
  72. package/dist/http/prepare-context.d.ts.map +1 -0
  73. package/dist/http/prepare-context.js +6 -0
  74. package/dist/http/prepare-context.js.map +1 -0
  75. package/dist/http/record-edit.d.ts +37 -0
  76. package/dist/http/record-edit.d.ts.map +1 -0
  77. package/dist/http/record-edit.js +86 -0
  78. package/dist/http/record-edit.js.map +1 -0
  79. package/dist/http/toolkit-request.d.ts +8 -0
  80. package/dist/http/toolkit-request.d.ts.map +1 -0
  81. package/dist/http/toolkit-request.js +45 -0
  82. package/dist/http/toolkit-request.js.map +1 -0
  83. package/dist/index.d.ts +18 -0
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.js +14 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/inspect/cli.d.ts +3 -0
  88. package/dist/inspect/cli.d.ts.map +1 -0
  89. package/dist/inspect/cli.js +36 -0
  90. package/dist/inspect/cli.js.map +1 -0
  91. package/dist/inspect/links.d.ts +10 -0
  92. package/dist/inspect/links.d.ts.map +1 -0
  93. package/dist/inspect/links.js +51 -0
  94. package/dist/inspect/links.js.map +1 -0
  95. package/dist/inspect/token.d.ts +5 -0
  96. package/dist/inspect/token.d.ts.map +1 -0
  97. package/dist/inspect/token.js +15 -0
  98. package/dist/inspect/token.js.map +1 -0
  99. package/dist/playwright/base-config.d.ts +4 -0
  100. package/dist/playwright/base-config.d.ts.map +1 -0
  101. package/dist/playwright/base-config.js +34 -0
  102. package/dist/playwright/base-config.js.map +1 -0
  103. package/dist/playwright/index.d.ts +3 -0
  104. package/dist/playwright/index.d.ts.map +1 -0
  105. package/dist/playwright/index.js +3 -0
  106. package/dist/playwright/index.js.map +1 -0
  107. package/dist/scenario.d.ts +46 -0
  108. package/dist/scenario.d.ts.map +1 -0
  109. package/dist/scenario.js +139 -0
  110. package/dist/scenario.js.map +1 -0
  111. package/dist/state/attempt-claim.d.ts +8 -0
  112. package/dist/state/attempt-claim.d.ts.map +1 -0
  113. package/dist/state/attempt-claim.js +47 -0
  114. package/dist/state/attempt-claim.js.map +1 -0
  115. package/dist/state/attempt-registry.d.ts +36 -0
  116. package/dist/state/attempt-registry.d.ts.map +1 -0
  117. package/dist/state/attempt-registry.js +68 -0
  118. package/dist/state/attempt-registry.js.map +1 -0
  119. package/dist/state/ensure-state.d.ts +42 -0
  120. package/dist/state/ensure-state.d.ts.map +1 -0
  121. package/dist/state/ensure-state.js +169 -0
  122. package/dist/state/ensure-state.js.map +1 -0
  123. package/dist/state/json-safe.d.ts +9 -0
  124. package/dist/state/json-safe.d.ts.map +1 -0
  125. package/dist/state/json-safe.js +18 -0
  126. package/dist/state/json-safe.js.map +1 -0
  127. package/dist/state/run-id.d.ts +6 -0
  128. package/dist/state/run-id.d.ts.map +1 -0
  129. package/dist/state/run-id.js +28 -0
  130. package/dist/state/run-id.js.map +1 -0
  131. package/dist/state/run-namespace.d.ts +33 -0
  132. package/dist/state/run-namespace.d.ts.map +1 -0
  133. package/dist/state/run-namespace.js +136 -0
  134. package/dist/state/run-namespace.js.map +1 -0
  135. package/dist/state/safe-paths.d.ts +10 -0
  136. package/dist/state/safe-paths.d.ts.map +1 -0
  137. package/dist/state/safe-paths.js +56 -0
  138. package/dist/state/safe-paths.js.map +1 -0
  139. package/dist/state/scenario-outcome.d.ts +9 -0
  140. package/dist/state/scenario-outcome.d.ts.map +1 -0
  141. package/dist/state/scenario-outcome.js +23 -0
  142. package/dist/state/scenario-outcome.js.map +1 -0
  143. package/dist/state/scenario-state.d.ts +41 -0
  144. package/dist/state/scenario-state.d.ts.map +1 -0
  145. package/dist/state/scenario-state.js +50 -0
  146. package/dist/state/scenario-state.js.map +1 -0
  147. package/dist/state/setup-lock.d.ts +22 -0
  148. package/dist/state/setup-lock.d.ts.map +1 -0
  149. package/dist/state/setup-lock.js +90 -0
  150. package/dist/state/setup-lock.js.map +1 -0
  151. package/dist/types/common.d.ts +17 -0
  152. package/dist/types/common.d.ts.map +1 -0
  153. package/dist/types/common.js +2 -0
  154. package/dist/types/common.js.map +1 -0
  155. package/dist/types/content-builder.d.ts +16 -0
  156. package/dist/types/content-builder.d.ts.map +1 -0
  157. package/dist/types/content-builder.js +2 -0
  158. package/dist/types/content-builder.js.map +1 -0
  159. package/dist/types/playwright-extensions.d.ts +8 -0
  160. package/dist/types/playwright-extensions.d.ts.map +1 -0
  161. package/dist/types/playwright-extensions.js +2 -0
  162. package/dist/types/playwright-extensions.js.map +1 -0
  163. package/package.json +88 -0
  164. package/tsconfig.base.json +13 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenario.js","sourceRoot":"","sources":["../src/scenario.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export declare function highestClaimedAttempt(locksDir: string, key: string): number;
2
+ /**
3
+ * The claim files are never removed during a run: they are how a replacement
4
+ * worker learns that a dead attempt already used its number, instead of
5
+ * restarting at 1 on its half-built database.
6
+ */
7
+ export declare function claimNextAttempt(locksDir: string, key: string): number;
8
+ //# sourceMappingURL=attempt-claim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attempt-claim.d.ts","sourceRoot":"","sources":["../../src/state/attempt-claim.ts"],"names":[],"mappings":"AAOA,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAgB3E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAsBtE"}
@@ -0,0 +1,47 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ function claimFile(locksDir, key, attempt) {
4
+ return path.join(locksDir, `${key}.attempt-${attempt}`);
5
+ }
6
+ export function highestClaimedAttempt(locksDir, key) {
7
+ if (!fs.existsSync(locksDir)) {
8
+ return 0;
9
+ }
10
+ const pattern = new RegExp(`^${key.replace(/[.*+?^${}()|[\]\\-]/g, '\\$&')}\\.attempt-(\\d+)$`);
11
+ let highest = 0;
12
+ for (const entry of fs.readdirSync(locksDir)) {
13
+ const match = pattern.exec(entry);
14
+ if (match) {
15
+ highest = Math.max(highest, Number(match[1]));
16
+ }
17
+ }
18
+ return highest;
19
+ }
20
+ /**
21
+ * The claim files are never removed during a run: they are how a replacement
22
+ * worker learns that a dead attempt already used its number, instead of
23
+ * restarting at 1 on its half-built database.
24
+ */
25
+ export function claimNextAttempt(locksDir, key) {
26
+ fs.mkdirSync(locksDir, { recursive: true });
27
+ for (let attempt = highestClaimedAttempt(locksDir, key) + 1;; attempt++) {
28
+ let handle;
29
+ try {
30
+ handle = fs.openSync(claimFile(locksDir, key, attempt), 'wx');
31
+ }
32
+ catch (error) {
33
+ if (error.code === 'EEXIST') {
34
+ continue;
35
+ }
36
+ throw error;
37
+ }
38
+ try {
39
+ fs.writeFileSync(handle, JSON.stringify({ pid: process.pid, claimedAt: Date.now() }));
40
+ }
41
+ finally {
42
+ fs.closeSync(handle);
43
+ }
44
+ return attempt;
45
+ }
46
+ }
47
+ //# sourceMappingURL=attempt-claim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attempt-claim.js","sourceRoot":"","sources":["../../src/state/attempt-claim.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAE5B,SAAS,SAAS,CAAC,QAAgB,EAAE,GAAW,EAAE,OAAe;IAC7D,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,GAAG,YAAY,OAAO,EAAE,CAAC,CAAA;AAC3D,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,QAAgB,EAAE,GAAW;IAC/D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,CAAA;IACZ,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,sBAAsB,EAAE,MAAM,CAAC,oBAAoB,CAAC,CAAA;IAE/F,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACjC,IAAI,KAAK,EAAE,CAAC;YACR,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACjD,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAA;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,GAAW;IAC1D,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAE3C,KAAK,IAAI,OAAO,GAAG,qBAAqB,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;QACvE,IAAI,MAAc,CAAA;QAClB,IAAI,CAAC;YACD,MAAM,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CAAA;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrD,SAAQ;YACZ,CAAC;YACD,MAAM,KAAK,CAAA;QACf,CAAC;QAED,IAAI,CAAC;YACD,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAA;QACzF,CAAC;gBAAS,CAAC;YACP,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;QACxB,CAAC;QAED,OAAO,OAAO,CAAA;IAClB,CAAC;AACL,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { ToolkitConfig } from '../config.js';
2
+ export type AttemptOutcome = 'committed' | 'failed' | 'abandoned';
3
+ export interface AttemptRecord {
4
+ type: 'attempt';
5
+ key: string;
6
+ /** What a person calls the scenario. Absent in runs recorded before it existed. */
7
+ name?: string;
8
+ attempt: number;
9
+ testId: string;
10
+ nonce: string;
11
+ startedAt: string;
12
+ }
13
+ export interface AttemptOutcomeRecord {
14
+ type: 'outcome';
15
+ testId: string;
16
+ outcome: AttemptOutcome;
17
+ durationMs: number;
18
+ endedAt: string;
19
+ }
20
+ export declare function registerAttempt(config: ToolkitConfig, input: {
21
+ key: string;
22
+ name?: string;
23
+ attempt: number;
24
+ testId: string;
25
+ nonce: string;
26
+ }): void;
27
+ export declare function registerSetupAttempt(config: ToolkitConfig, key: string, testId: string): void;
28
+ export declare function recordAttemptOutcome(config: ToolkitConfig, input: {
29
+ testId: string;
30
+ outcome: AttemptOutcome;
31
+ durationMs: number;
32
+ }): void;
33
+ export declare function readAttemptsFrom(attemptsFile: string): AttemptRecord[];
34
+ export declare function readAttempts(config: ToolkitConfig): AttemptRecord[];
35
+ export declare function readRegisteredTestIds(config: ToolkitConfig): string[];
36
+ //# sourceMappingURL=attempt-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attempt-registry.d.ts","sourceRoot":"","sources":["../../src/state/attempt-registry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAGjD,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAA;AAEjE,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,SAAS,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,SAAS,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,cAAc,CAAA;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;CAClB;AAQD,wBAAgB,eAAe,CAC3B,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACtF,IAAI,CAYN;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAE7F;AAED,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,cAAc,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACvE,IAAI,CAUN;AAWD,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,EAAE,CAqBtE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,EAAE,CAEnE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CAErE"}
@@ -0,0 +1,68 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import * as fs from 'fs';
3
+ import { ensureRunNamespace, runPaths, touchRunLiveness } from './run-namespace.js';
4
+ /** One line per write with O_APPEND, so separate workers never interleave. */
5
+ function appendLine(paths, record) {
6
+ fs.writeFileSync(paths.attemptsFile, `${JSON.stringify(record)}\n`, { flag: 'a' });
7
+ touchRunLiveness(paths.runDir);
8
+ }
9
+ export function registerAttempt(config, input) {
10
+ const paths = ensureRunNamespace(config);
11
+ appendLine(paths, {
12
+ type: 'attempt',
13
+ key: input.key,
14
+ name: input.name ?? input.key,
15
+ attempt: input.attempt,
16
+ testId: input.testId,
17
+ nonce: input.nonce,
18
+ startedAt: new Date().toISOString(),
19
+ });
20
+ }
21
+ export function registerSetupAttempt(config, key, testId) {
22
+ registerAttempt(config, { key, attempt: 1, testId, nonce: randomBytes(16).toString('hex') });
23
+ }
24
+ export function recordAttemptOutcome(config, input) {
25
+ const paths = ensureRunNamespace(config);
26
+ appendLine(paths, {
27
+ type: 'outcome',
28
+ testId: input.testId,
29
+ outcome: input.outcome,
30
+ durationMs: input.durationMs,
31
+ endedAt: new Date().toISOString(),
32
+ });
33
+ }
34
+ function isAttemptRecord(value) {
35
+ if (typeof value !== 'object' || value === null) {
36
+ return false;
37
+ }
38
+ const record = value;
39
+ return record.type === 'attempt' && typeof record.testId === 'string' && typeof record.key === 'string';
40
+ }
41
+ export function readAttemptsFrom(attemptsFile) {
42
+ if (!fs.existsSync(attemptsFile)) {
43
+ return [];
44
+ }
45
+ const records = [];
46
+ for (const line of fs.readFileSync(attemptsFile, 'utf-8').split('\n')) {
47
+ if (line.trim() === '') {
48
+ continue;
49
+ }
50
+ try {
51
+ const parsed = JSON.parse(line);
52
+ if (isAttemptRecord(parsed)) {
53
+ records.push(parsed);
54
+ }
55
+ }
56
+ catch {
57
+ // A crash mid-write leaves a partial last line.
58
+ }
59
+ }
60
+ return records;
61
+ }
62
+ export function readAttempts(config) {
63
+ return readAttemptsFrom(runPaths(config).attemptsFile);
64
+ }
65
+ export function readRegisteredTestIds(config) {
66
+ return [...new Set(readAttempts(config).map((record) => record.testId))];
67
+ }
68
+ //# sourceMappingURL=attempt-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attempt-registry.js","sourceRoot":"","sources":["../../src/state/attempt-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { ToolkitConfig } from '../config.js';
2
+ export declare const SETUP_DEFAULTS: {
3
+ attemptTimeoutMs: number;
4
+ waitTimeoutMs: number;
5
+ attempts: number;
6
+ lockStaleMs: number;
7
+ pollMs: number;
8
+ };
9
+ export interface SetupContext {
10
+ testId: string;
11
+ attempt: number;
12
+ signal: AbortSignal;
13
+ }
14
+ export interface EnsureStateOptions<S> {
15
+ key: string;
16
+ /** What the inspect listing and the backend call this scenario; the key otherwise. */
17
+ name?: string;
18
+ /** Identifies the caller, so its own retries rethrow instead of skipping. */
19
+ triggerId: string;
20
+ setup: (context: SetupContext) => Promise<S>;
21
+ now?: () => number;
22
+ sleep?: (ms: number) => Promise<void>;
23
+ }
24
+ export type EnsureStateOutcome<S> = {
25
+ status: 'ready';
26
+ testId: string;
27
+ attempt: number;
28
+ data: S;
29
+ setupRan: boolean;
30
+ waitedMs: number;
31
+ } | {
32
+ status: 'skip';
33
+ reason: string;
34
+ };
35
+ /**
36
+ * Same salt, scenario and attempt always give the same ID, so a claimed attempt gets
37
+ * its own database without anyone coordinating; a retry gets a different one.
38
+ * The salt is the run's, not the run ID — see runSalt().
39
+ */
40
+ export declare function deriveTestId(salt: string, key: string, attempt: number): string;
41
+ export declare function ensureState<S>(config: ToolkitConfig, options: EnsureStateOptions<S>): Promise<EnsureStateOutcome<S>>;
42
+ //# sourceMappingURL=ensure-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensure-state.d.ts","sourceRoot":"","sources":["../../src/state/ensure-state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAmBjD,eAAO,MAAM,cAAc;;;;;;CAM1B,CAAA;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,WAAW,CAAA;CACtB;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;IAC5C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACxC;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAC1B;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,CAAC,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAClG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAExC;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE/E;AAoGD,wBAAsB,WAAW,CAAC,CAAC,EAC/B,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CA0FhC"}
@@ -0,0 +1,169 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { registerAttempt, recordAttemptOutcome } from './attempt-registry.js';
3
+ import { commitScenarioState, readScenarioState, readScenarioFailure, recordScenarioFailure, } from './scenario-state.js';
4
+ import { ensureRunNamespace, runSalt } from './run-namespace.js';
5
+ import { acquireSetupLock, heartbeatSetupLock, releaseSetupLock, stealSetupLock, stillOwns, } from './setup-lock.js';
6
+ import { claimNextAttempt, highestClaimedAttempt } from './attempt-claim.js';
7
+ export const SETUP_DEFAULTS = {
8
+ attemptTimeoutMs: 90_000,
9
+ waitTimeoutMs: 300_000,
10
+ attempts: 2,
11
+ lockStaleMs: 15_000,
12
+ pollMs: 100,
13
+ };
14
+ /**
15
+ * Same salt, scenario and attempt always give the same ID, so a claimed attempt gets
16
+ * its own database without anyone coordinating; a retry gets a different one.
17
+ * The salt is the run's, not the run ID — see runSalt().
18
+ */
19
+ export function deriveTestId(salt, key, attempt) {
20
+ return createHash('sha256').update(`${salt}:${key}:${attempt}`).digest('hex').slice(0, 16).toUpperCase();
21
+ }
22
+ function statusError(reason) {
23
+ return new Error(`[typo3-playwright-toolkit] ${reason}`);
24
+ }
25
+ /**
26
+ * Records the failure before throwing, even when this worker has none of its own —
27
+ * another one may have burned the attempts. Without a status the remaining tests
28
+ * error instead of skipping with the reason, and teardown drops the databases that
29
+ * would have shown what happened.
30
+ */
31
+ function giveUp(config, key, triggerId, failures, reason, durationMs) {
32
+ const attempts = failures.length > 0 ? failures : [{ attempt: 0, testId: '', error: reason, durationMs }];
33
+ recordScenarioFailure(config, { key, triggeringTestId: triggerId, attempts });
34
+ return statusError(reason);
35
+ }
36
+ const TIMED_OUT = Symbol('timed-out');
37
+ /**
38
+ * The timeout runs on a real timer, not the injected poll sleep: an abandoned
39
+ * setup keeps running, so only elapsed wall time can tell us to give up on it.
40
+ */
41
+ async function runAttempt(setup, context, timeoutMs) {
42
+ const controller = new AbortController();
43
+ let timer;
44
+ const timeout = new Promise((resolve) => {
45
+ timer = setTimeout(() => {
46
+ controller.abort();
47
+ resolve(TIMED_OUT);
48
+ }, timeoutMs);
49
+ });
50
+ try {
51
+ const outcome = await Promise.race([setup({ ...context, signal: controller.signal }), timeout]);
52
+ if (outcome === TIMED_OUT) {
53
+ return { ok: false, error: `setup did not finish within ${timeoutMs}ms` };
54
+ }
55
+ return { ok: true, data: outcome };
56
+ }
57
+ catch (error) {
58
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
59
+ }
60
+ finally {
61
+ controller.abort();
62
+ clearTimeout(timer);
63
+ }
64
+ }
65
+ /**
66
+ * Throws instead of skipping when the failure is the caller's own attempt, so a
67
+ * retry sees the error rather than a skip.
68
+ */
69
+ function settledOutcome(config, key, triggerId, waitedMs) {
70
+ // State first: a worker that gave up may have written a status while another
71
+ // one went on to succeed, and content that exists beats a report that it does
72
+ // not.
73
+ const committed = readScenarioState(config, key);
74
+ if (committed) {
75
+ return {
76
+ status: 'ready',
77
+ testId: committed.testId,
78
+ attempt: committed.attempt,
79
+ data: committed.data,
80
+ setupRan: false,
81
+ waitedMs,
82
+ };
83
+ }
84
+ const failure = readScenarioFailure(config, key);
85
+ if (!failure) {
86
+ return undefined;
87
+ }
88
+ const reason = `setup for "${key}" failed: ${failure.attempts.at(-1)?.error ?? 'unknown error'}`;
89
+ if (failure.triggeringTestId === triggerId) {
90
+ throw statusError(reason);
91
+ }
92
+ return { status: 'skip', reason };
93
+ }
94
+ export async function ensureState(config, options) {
95
+ const { key, triggerId, setup } = options;
96
+ const name = options.name ?? key;
97
+ const now = options.now ?? (() => Date.now());
98
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
99
+ const tuning = { ...SETUP_DEFAULTS, ...config.setup };
100
+ const paths = ensureRunNamespace(config);
101
+ const startedAt = now();
102
+ const failures = [];
103
+ for (;;) {
104
+ const settled = settledOutcome(config, key, triggerId, now() - startedAt);
105
+ if (settled) {
106
+ return settled;
107
+ }
108
+ // No status here: whoever holds the lock may still succeed, and a status
109
+ // would skip every remaining test on a scenario that then works.
110
+ if (now() - startedAt > tuning.waitTimeoutMs) {
111
+ throw statusError(`Timed out after ${tuning.waitTimeoutMs}ms waiting for the setup of "${key}". ` +
112
+ `Another worker may have died holding ${paths.locksDir}/${key}.lock.`);
113
+ }
114
+ // Take the lock before claiming a number: claims are durable, so a
115
+ // waiter that claimed on every poll would burn the attempt budget.
116
+ const nonce = randomBytes(16).toString('hex');
117
+ if (!acquireSetupLock(paths.locksDir, key, { nonce, attempt: highestClaimedAttempt(paths.locksDir, key) + 1 })) {
118
+ stealSetupLock(paths.locksDir, key, nonce, tuning.lockStaleMs, now());
119
+ await sleep(tuning.pollMs);
120
+ continue;
121
+ }
122
+ const attempt = claimNextAttempt(paths.locksDir, key);
123
+ if (attempt > tuning.attempts) {
124
+ releaseSetupLock(paths.locksDir, key, nonce);
125
+ const reason = `setup for "${key}" gave up after ${tuning.attempts} attempt(s): ` +
126
+ `${failures.at(-1)?.error ?? 'another worker exhausted them'}`;
127
+ throw giveUp(config, key, triggerId, failures, reason, now() - startedAt);
128
+ }
129
+ const testId = deriveTestId(runSalt(config), key, attempt);
130
+ registerAttempt(config, { key, name, attempt, testId, nonce });
131
+ const heartbeat = setInterval(() => heartbeatSetupLock(paths.locksDir, key, nonce), 1_000);
132
+ const attemptStartedAt = now();
133
+ let result;
134
+ try {
135
+ result = await runAttempt(setup, { testId, attempt }, tuning.attemptTimeoutMs);
136
+ }
137
+ finally {
138
+ clearInterval(heartbeat);
139
+ }
140
+ const durationMs = now() - attemptStartedAt;
141
+ const fenced = !stillOwns(paths.locksDir, key, nonce);
142
+ if (fenced) {
143
+ recordAttemptOutcome(config, { testId, outcome: 'abandoned', durationMs });
144
+ await sleep(tuning.pollMs);
145
+ continue;
146
+ }
147
+ if (result.ok) {
148
+ commitScenarioState(config, { key, testId, attempt, setupMs: durationMs, data: result.data });
149
+ recordAttemptOutcome(config, { testId, outcome: 'committed', durationMs });
150
+ releaseSetupLock(paths.locksDir, key, nonce);
151
+ return {
152
+ status: 'ready',
153
+ testId,
154
+ attempt,
155
+ data: result.data,
156
+ setupRan: true,
157
+ waitedMs: 0,
158
+ };
159
+ }
160
+ failures.push({ attempt, testId, error: result.error, durationMs });
161
+ recordAttemptOutcome(config, { testId, outcome: 'failed', durationMs });
162
+ releaseSetupLock(paths.locksDir, key, nonce);
163
+ if (attempt >= tuning.attempts) {
164
+ recordScenarioFailure(config, { key, triggeringTestId: triggerId, attempts: failures });
165
+ throw statusError(`setup for "${key}" failed: ${result.error}`);
166
+ }
167
+ }
168
+ }
169
+ //# sourceMappingURL=ensure-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensure-state.js","sourceRoot":"","sources":["../../src/state/ensure-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * State is stored as JSON and read back by another process, so a value that
3
+ * changes shape on the way through — a Date becoming a string, undefined
4
+ * disappearing, NaN becoming null — is rejected here rather than surprising the
5
+ * reader later. Comparing the round trip against the original is what catches
6
+ * every such value, including ones JSON gains no opinion about until later.
7
+ */
8
+ export declare function assertJsonSafe(value: unknown, label?: string): void;
9
+ //# sourceMappingURL=json-safe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-safe.d.ts","sourceRoot":"","sources":["../../src/state/json-safe.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAU,GAAG,IAAI,CASpE"}
@@ -0,0 +1,18 @@
1
+ import { deepStrictEqual } from 'node:assert';
2
+ /**
3
+ * State is stored as JSON and read back by another process, so a value that
4
+ * changes shape on the way through — a Date becoming a string, undefined
5
+ * disappearing, NaN becoming null — is rejected here rather than surprising the
6
+ * reader later. Comparing the round trip against the original is what catches
7
+ * every such value, including ones JSON gains no opinion about until later.
8
+ */
9
+ export function assertJsonSafe(value, label = 'value') {
10
+ try {
11
+ deepStrictEqual(JSON.parse(JSON.stringify(value)), value);
12
+ }
13
+ catch (error) {
14
+ throw new Error(`[typo3-playwright-toolkit] ${label} does not survive a JSON round trip, so another ` +
15
+ `process would read back something else:\n${error instanceof Error ? error.message : String(error)}`);
16
+ }
17
+ }
18
+ //# sourceMappingURL=json-safe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-safe.js","sourceRoot":"","sources":["../../src/state/json-safe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,KAAK,GAAG,OAAO;IAC1D,IAAI,CAAC;QACD,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAY,EAAE,KAAK,CAAC,CAAA;IACxE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACX,8BAA8B,KAAK,kDAAkD;YACjF,4CAA4C,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC3G,CAAA;IACL,CAAC;AACL,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare const RUN_ID_ENV = "PW_RUN_ID";
2
+ /** The run ID becomes a directory name that teardown deletes recursively. */
3
+ export declare const RUN_ID_PATTERN: RegExp;
4
+ /** Written back to the environment so forked workers inherit it. */
5
+ export declare function resolveRunId(explicit?: string): string;
6
+ //# sourceMappingURL=run-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-id.d.ts","sourceRoot":"","sources":["../../src/state/run-id.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU,cAAc,CAAA;AAErC,6EAA6E;AAC7E,eAAO,MAAM,cAAc,QAA0B,CAAA;AAarD,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAkBtD"}
@@ -0,0 +1,28 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ export const RUN_ID_ENV = 'PW_RUN_ID';
3
+ /** The run ID becomes a directory name that teardown deletes recursively. */
4
+ export const RUN_ID_PATTERN = /^[A-Za-z0-9_-]{8,64}$/;
5
+ function assertValidRunId(runId) {
6
+ if (!RUN_ID_PATTERN.test(runId)) {
7
+ throw new Error(`[typo3-playwright-toolkit] Invalid run ID "${runId}". Expected 8-64 characters of ` +
8
+ `A-Z, a-z, 0-9, "_" or "-". Check ${RUN_ID_ENV}.`);
9
+ }
10
+ return runId;
11
+ }
12
+ /** Written back to the environment so forked workers inherit it. */
13
+ export function resolveRunId(explicit) {
14
+ if (explicit) {
15
+ // Validate before storing, or the next worker inherits a bad value.
16
+ assertValidRunId(explicit);
17
+ process.env[RUN_ID_ENV] = explicit;
18
+ return explicit;
19
+ }
20
+ const inherited = process.env[RUN_ID_ENV];
21
+ if (inherited) {
22
+ return assertValidRunId(inherited);
23
+ }
24
+ const minted = randomBytes(8).toString('hex');
25
+ process.env[RUN_ID_ENV] = minted;
26
+ return minted;
27
+ }
28
+ //# sourceMappingURL=run-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-id.js","sourceRoot":"","sources":["../../src/state/run-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,CAAC,MAAM,UAAU,GAAG,WAAW,CAAA;AAErC,6EAA6E;AAC7E,MAAM,CAAC,MAAM,cAAc,GAAG,uBAAuB,CAAA;AAErD,SAAS,gBAAgB,CAAC,KAAa;IACnC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACX,8CAA8C,KAAK,iCAAiC;YAChF,oCAAoC,UAAU,GAAG,CACxD,CAAA;IACL,CAAC;IAED,OAAO,KAAK,CAAA;AAChB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,YAAY,CAAC,QAAiB;IAC1C,IAAI,QAAQ,EAAE,CAAC;QACX,oEAAoE;QACpE,gBAAgB,CAAC,QAAQ,CAAC,CAAA;QAC1B,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAA;QAElC,OAAO,QAAQ,CAAA;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IACzC,IAAI,SAAS,EAAE,CAAC;QACZ,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAA;IACtC,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC7C,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,MAAM,CAAA;IAEhC,OAAO,MAAM,CAAA;AACjB,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { ToolkitConfig } from '../config.js';
2
+ export interface RunPaths {
3
+ runId: string;
4
+ runDir: string;
5
+ scenariosDir: string;
6
+ failuresDir: string;
7
+ locksDir: string;
8
+ attemptsFile: string;
9
+ metaFile: string;
10
+ }
11
+ export declare function runsRoot(stateDir: string): string;
12
+ export declare function runPaths(config: ToolkitConfig): RunPaths;
13
+ export declare function ensureRunNamespace(config: ToolkitConfig): RunPaths;
14
+ export declare function prepareRun(config: ToolkitConfig): RunPaths;
15
+ /**
16
+ * The random value every test ID of this run is built from. Without it the IDs
17
+ * would follow from the run ID, and a run ID can be set by hand and guessed. A
18
+ * test ID is enough to reach that test's database.
19
+ *
20
+ * Written with 'wx', so the first worker creates it and the others read it.
21
+ */
22
+ export declare function runSalt(config: ToolkitConfig): string;
23
+ export declare const LIVENESS_FILE = "liveness";
24
+ export declare function touchRunLiveness(runDir: string): void;
25
+ /**
26
+ * When the run was last active. Writing to a file inside a directory does not
27
+ * update the directory's own time, so a busy run would look idle.
28
+ */
29
+ export declare function runLastActiveMs(runDir: string): number;
30
+ export declare function listRunIds(stateDir: string): string[];
31
+ /** The hash suffix keeps two paths apart when sanitizing collapses them. */
32
+ export declare function sanitizeScenarioKey(key: string): string;
33
+ //# sourceMappingURL=run-namespace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-namespace.d.ts","sourceRoot":"","sources":["../../src/state/run-namespace.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAGjD,MAAM,WAAW,QAAQ;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,QAAQ,CAaxD;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,QAAQ,CAQlE;AAKD,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,QAAQ,CAqB1D;AA0CD;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAUrD;AAED,eAAO,MAAM,aAAa,aAAa,CAAA;AAEvC,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAMrD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAWtD;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAYrD;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKvD"}