@le-space/playwright 0.9.0 → 0.9.1

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 (3) hide show
  1. package/index.d.ts +17 -1
  2. package/index.js +51 -15
  3. package/package.json +3 -3
package/index.d.ts CHANGED
@@ -316,6 +316,22 @@ declare function waitForAlephMessageForgotten(options: {
316
316
  pollIntervalMs?: number;
317
317
  fetch?: typeof fetch;
318
318
  }): Promise<string>;
319
+ /**
320
+ * The address that published `messageHash`, or `null` when no replica will say.
321
+ *
322
+ * Aleph only honours a FORGET signed by a message's original sender, so this is
323
+ * what decides whether a cleanup account has any power over a message at all.
324
+ *
325
+ * `null` means "could not be read" — never "not owned". Callers must treat the
326
+ * two differently: an unreadable sender is a transient API problem, and turning
327
+ * it into a hard failure would make cleanup flaky for reasons unrelated to the
328
+ * relay being cleaned up.
329
+ */
330
+ declare function resolveAlephMessageSender(options: {
331
+ messageHash: string;
332
+ apiHosts?: readonly string[];
333
+ fetch?: typeof fetch;
334
+ }): Promise<string | null>;
319
335
  declare function cleanupRelay(options: CleanupRelayOptions): Promise<CleanupRelayResult>;
320
336
  declare function createRelayEvidence(options: {
321
337
  instanceName: string;
@@ -334,4 +350,4 @@ declare function createRelayTest(options: CreateRelayTestOptions): _playwright_t
334
350
  relayLifecycle: RelayLifecycleFixture;
335
351
  }, _playwright_test.PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions>;
336
352
 
337
- export { type AlephChromiumConnector, type AlephCostEvidence, type AlephCreditBalanceSnapshot, type AlephPricingSnapshot, type AlephRemoteBrowserOptions, type AlephRunnerInstanceCandidate, type AlephRunnerJanitorSelection, type CleanupRelayHooks, type CleanupRelayOptions, type CleanupRelayResult, type CreateRelayTestOptions, DEFAULT_ALEPH_SCHEDULER_URL, type ForwardBrowserConsoleOptions, LIBP2P_DIAGNOSTIC_CONSOLE_FILTER, PLAYWRIGHT_RUNNER_VERSION, type ProgressLogger, type ProvisionRelayOptions, type ProvisionedRelay, type RelayAddressPolicy, RelayButtonDriver, type RelayButtonDriverOptions, type RelayEvidence, type RelayEvidenceStep, type RelayLifecycleFixture, type RelayWalletAccount, SUPPORTED_ALEPH_API_HOSTS, type WaitForPubsubSubscriberOptions, appendRelayGithubSummary, buildAlephCostEvidence, cleanupRelay, connectAlephChromium, createProgressLogger, createRelayEvidence, createRelayTest, findAlephInstanceHash, formatAlephCostGithubSummary, formatRelayGithubSummary, forwardBrowserConsole, installEip1193WalletMock, provisionRelay, resolveAlephApiHosts, selectBrowserRelayAddresses, selectExpiredAlephPlaywrightRunners, updateRelayEvidenceStep, waitForAlephInstanceDeletion, waitForAlephMessageForgotten, waitForBootstrapRegistration, waitForDeployableManifest, waitForPubsubSubscriber, writeRelayEvidence };
353
+ export { type AlephChromiumConnector, type AlephCostEvidence, type AlephCreditBalanceSnapshot, type AlephPricingSnapshot, type AlephRemoteBrowserOptions, type AlephRunnerInstanceCandidate, type AlephRunnerJanitorSelection, type CleanupRelayHooks, type CleanupRelayOptions, type CleanupRelayResult, type CreateRelayTestOptions, DEFAULT_ALEPH_SCHEDULER_URL, type ForwardBrowserConsoleOptions, LIBP2P_DIAGNOSTIC_CONSOLE_FILTER, PLAYWRIGHT_RUNNER_VERSION, type ProgressLogger, type ProvisionRelayOptions, type ProvisionedRelay, type RelayAddressPolicy, RelayButtonDriver, type RelayButtonDriverOptions, type RelayEvidence, type RelayEvidenceStep, type RelayLifecycleFixture, type RelayWalletAccount, SUPPORTED_ALEPH_API_HOSTS, type WaitForPubsubSubscriberOptions, appendRelayGithubSummary, buildAlephCostEvidence, cleanupRelay, connectAlephChromium, createProgressLogger, createRelayEvidence, createRelayTest, findAlephInstanceHash, formatAlephCostGithubSummary, formatRelayGithubSummary, forwardBrowserConsole, installEip1193WalletMock, provisionRelay, resolveAlephApiHosts, resolveAlephMessageSender, selectBrowserRelayAddresses, selectExpiredAlephPlaywrightRunners, updateRelayEvidenceStep, waitForAlephInstanceDeletion, waitForAlephMessageForgotten, waitForBootstrapRegistration, waitForDeployableManifest, waitForPubsubSubscriber, writeRelayEvidence };
package/index.js CHANGED
@@ -573,6 +573,22 @@ async function waitForAlephMessageForgotten(options) {
573
573
  `Aleph bootstrap registration ${options.messageHash} was not deregistered within ${options.timeoutMs ?? 2 * 6e4}ms: ${lastSummary}`
574
574
  );
575
575
  }
576
+ async function resolveAlephMessageSender(options) {
577
+ const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);
578
+ if (!fetchImpl) throw new Error("A fetch implementation is required to resolve a message sender");
579
+ for (const apiHost of resolveAlephApiHosts(options.apiHosts)) {
580
+ try {
581
+ const response = await fetchImpl(new URL(`/api/v0/messages/${options.messageHash}`, apiHost), {
582
+ cache: "no-cache"
583
+ });
584
+ const payload = await response.json().catch(() => null);
585
+ const sender = payload?.message?.sender;
586
+ if (typeof sender === "string" && sender.length > 0) return sender;
587
+ } catch {
588
+ }
589
+ }
590
+ return null;
591
+ }
576
592
  async function cleanupRelay(options) {
577
593
  if (!/^[a-f0-9]{64}$/iu.test(options.instanceHash)) {
578
594
  throw new Error(`Invalid Aleph INSTANCE hash: ${options.instanceHash}`);
@@ -585,6 +601,35 @@ async function cleanupRelay(options) {
585
601
  const forget = options.hooks?.forget ?? forgetAlephMessages;
586
602
  const hosts = resolveAlephApiHosts(options.apiHosts);
587
603
  let uiDeleteRequested = false;
604
+ let ownership;
605
+ const resolveRegistrationOwnership = async () => {
606
+ if (!ownership) {
607
+ const sender = options.registrationHash ? await resolveAlephMessageSender({
608
+ messageHash: options.registrationHash,
609
+ apiHosts: hosts,
610
+ fetch: fetchImpl
611
+ }) : null;
612
+ ownership = {
613
+ owned: sender !== null && sender.toLowerCase() === options.account.address.toLowerCase(),
614
+ sender
615
+ };
616
+ }
617
+ return ownership;
618
+ };
619
+ const verifyRegistrationDeregistered = async () => {
620
+ if (!options.registrationHash) return void 0;
621
+ const { owned, sender } = await resolveRegistrationOwnership();
622
+ if (!owned) {
623
+ return sender === null ? `Bootstrap registration ${options.registrationHash} was not awaited: its sender could not be read from any Aleph replica.` : `Bootstrap registration ${options.registrationHash} was not awaited: it belongs to ${sender}, not the cleanup account ${options.account.address}, so only the relay itself can deregister it.`;
624
+ }
625
+ return waitForAlephMessageForgotten({
626
+ messageHash: options.registrationHash,
627
+ apiHosts: hosts,
628
+ timeoutMs: options.timeoutMs,
629
+ pollIntervalMs: options.pollIntervalMs,
630
+ fetch: fetchImpl
631
+ });
632
+ };
588
633
  try {
589
634
  await driver.requestDelete(options.instanceName);
590
635
  uiDeleteRequested = true;
@@ -600,13 +645,7 @@ async function cleanupRelay(options) {
600
645
  pollIntervalMs: options.pollIntervalMs,
601
646
  fetch: fetchImpl
602
647
  });
603
- const registrationVerificationSummary2 = options.registrationHash ? await waitForAlephMessageForgotten({
604
- messageHash: options.registrationHash,
605
- apiHosts: hosts,
606
- timeoutMs: options.timeoutMs,
607
- pollIntervalMs: options.pollIntervalMs,
608
- fetch: fetchImpl
609
- }) : void 0;
648
+ const registrationVerificationSummary2 = await verifyRegistrationDeregistered();
610
649
  return {
611
650
  instanceHash: options.instanceHash,
612
651
  uiDeleteRequested,
@@ -644,11 +683,13 @@ async function cleanupRelay(options) {
644
683
  const hasher = (content) => createHash("sha256").update(content).digest("hex");
645
684
  let forgetSummary = "";
646
685
  let lastForgetError = "No Aleph API host attempted.";
686
+ const { owned: registrationOwned } = await resolveRegistrationOwnership();
687
+ const forgetHashes = registrationOwned && options.registrationHash ? [options.instanceHash, options.registrationHash] : [options.instanceHash];
647
688
  for (const apiHost of hosts) {
648
689
  try {
649
690
  const result = await forget({
650
691
  sender: options.account.address,
651
- hashes: [options.instanceHash],
692
+ hashes: forgetHashes,
652
693
  reason: "Relay Button Playwright cleanup",
653
694
  signer,
654
695
  hasher,
@@ -676,13 +717,7 @@ async function cleanupRelay(options) {
676
717
  pollIntervalMs: options.pollIntervalMs,
677
718
  fetch: fetchImpl
678
719
  });
679
- const registrationVerificationSummary = options.registrationHash ? await waitForAlephMessageForgotten({
680
- messageHash: options.registrationHash,
681
- apiHosts: hosts,
682
- timeoutMs: options.timeoutMs,
683
- pollIntervalMs: options.pollIntervalMs,
684
- fetch: fetchImpl
685
- }) : void 0;
720
+ const registrationVerificationSummary = await verifyRegistrationDeregistered();
686
721
  return {
687
722
  instanceHash: options.instanceHash,
688
723
  uiDeleteRequested,
@@ -845,6 +880,7 @@ export {
845
880
  installEip1193WalletMock,
846
881
  provisionRelay,
847
882
  resolveAlephApiHosts,
883
+ resolveAlephMessageSender,
848
884
  selectBrowserRelayAddresses,
849
885
  selectExpiredAlephPlaywrightRunners,
850
886
  updateRelayEvidenceStep,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@le-space/playwright",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Reusable Playwright fixtures and Relay Button lifecycle helpers.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,8 +27,8 @@
27
27
  "url": "https://github.com/NiKrause/relay-button/issues"
28
28
  },
29
29
  "dependencies": {
30
- "@le-space/aleph-bootstrap": "0.9.0",
31
- "@le-space/core": "0.9.0"
30
+ "@le-space/aleph-bootstrap": "0.9.1",
31
+ "@le-space/core": "0.9.1"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "@playwright/test": ">=1.61.1 <1.62.0"