@lhremote/core 0.0.0 → 0.2.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.
- package/LICENSE +661 -0
- package/README.md +124 -0
- package/dist/cdp/app-discovery.d.ts +22 -0
- package/dist/cdp/app-discovery.d.ts.map +1 -0
- package/dist/cdp/app-discovery.js +60 -0
- package/dist/cdp/app-discovery.js.map +1 -0
- package/dist/cdp/app-discovery.test.d.ts +2 -0
- package/dist/cdp/app-discovery.test.d.ts.map +1 -0
- package/dist/cdp/app-discovery.test.js +124 -0
- package/dist/cdp/app-discovery.test.js.map +1 -0
- package/dist/cdp/client.d.ts +114 -0
- package/dist/cdp/client.d.ts.map +1 -0
- package/dist/cdp/client.integration.test.d.ts +2 -0
- package/dist/cdp/client.integration.test.d.ts.map +1 -0
- package/dist/cdp/client.integration.test.js +77 -0
- package/dist/cdp/client.integration.test.js.map +1 -0
- package/dist/cdp/client.js +319 -0
- package/dist/cdp/client.js.map +1 -0
- package/dist/cdp/client.test.d.ts +2 -0
- package/dist/cdp/client.test.d.ts.map +1 -0
- package/dist/cdp/client.test.js +533 -0
- package/dist/cdp/client.test.js.map +1 -0
- package/dist/cdp/discovery.d.ts +14 -0
- package/dist/cdp/discovery.d.ts.map +1 -0
- package/dist/cdp/discovery.integration.test.d.ts +2 -0
- package/dist/cdp/discovery.integration.test.d.ts.map +1 -0
- package/dist/cdp/discovery.integration.test.js +27 -0
- package/dist/cdp/discovery.integration.test.js.map +1 -0
- package/dist/cdp/discovery.js +33 -0
- package/dist/cdp/discovery.js.map +1 -0
- package/dist/cdp/discovery.test.d.ts +2 -0
- package/dist/cdp/discovery.test.d.ts.map +1 -0
- package/dist/cdp/discovery.test.js +56 -0
- package/dist/cdp/discovery.test.js.map +1 -0
- package/dist/cdp/errors.d.ts +28 -0
- package/dist/cdp/errors.d.ts.map +1 -0
- package/dist/cdp/errors.js +42 -0
- package/dist/cdp/errors.js.map +1 -0
- package/dist/cdp/errors.test.d.ts +2 -0
- package/dist/cdp/errors.test.d.ts.map +1 -0
- package/dist/cdp/errors.test.js +37 -0
- package/dist/cdp/errors.test.js.map +1 -0
- package/dist/cdp/index.d.ts +6 -0
- package/dist/cdp/index.d.ts.map +1 -0
- package/dist/cdp/index.js +8 -0
- package/dist/cdp/index.js.map +1 -0
- package/dist/cdp/instance-discovery.d.ts +30 -0
- package/dist/cdp/instance-discovery.d.ts.map +1 -0
- package/dist/cdp/instance-discovery.integration.test.d.ts +2 -0
- package/dist/cdp/instance-discovery.integration.test.d.ts.map +1 -0
- package/dist/cdp/instance-discovery.integration.test.js +36 -0
- package/dist/cdp/instance-discovery.integration.test.js.map +1 -0
- package/dist/cdp/instance-discovery.js +118 -0
- package/dist/cdp/instance-discovery.js.map +1 -0
- package/dist/cdp/instance-discovery.test.d.ts +2 -0
- package/dist/cdp/instance-discovery.test.d.ts.map +1 -0
- package/dist/cdp/instance-discovery.test.js +112 -0
- package/dist/cdp/instance-discovery.test.js.map +1 -0
- package/dist/cdp/testing/launch-chromium.d.ts +24 -0
- package/dist/cdp/testing/launch-chromium.d.ts.map +1 -0
- package/dist/cdp/testing/launch-chromium.js +93 -0
- package/dist/cdp/testing/launch-chromium.js.map +1 -0
- package/dist/constants.d.ts +5 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +7 -0
- package/dist/constants.js.map +1 -0
- package/dist/data/action-types.d.ts +39 -0
- package/dist/data/action-types.d.ts.map +1 -0
- package/dist/data/action-types.js +497 -0
- package/dist/data/action-types.js.map +1 -0
- package/dist/data/action-types.test.d.ts +2 -0
- package/dist/data/action-types.test.d.ts.map +1 -0
- package/dist/data/action-types.test.js +544 -0
- package/dist/data/action-types.test.js.map +1 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.d.ts.map +1 -0
- package/dist/data/index.js +4 -0
- package/dist/data/index.js.map +1 -0
- package/dist/db/client.d.ts +27 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.integration.test.d.ts +2 -0
- package/dist/db/client.integration.test.d.ts.map +1 -0
- package/dist/db/client.integration.test.js +63 -0
- package/dist/db/client.integration.test.js.map +1 -0
- package/dist/db/client.js +23 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/client.test.d.ts +2 -0
- package/dist/db/client.test.d.ts.map +1 -0
- package/dist/db/client.test.js +48 -0
- package/dist/db/client.test.js.map +1 -0
- package/dist/db/discovery.d.ts +14 -0
- package/dist/db/discovery.d.ts.map +1 -0
- package/dist/db/discovery.integration.test.d.ts +2 -0
- package/dist/db/discovery.integration.test.d.ts.map +1 -0
- package/dist/db/discovery.integration.test.js +101 -0
- package/dist/db/discovery.integration.test.js.map +1 -0
- package/dist/db/discovery.js +76 -0
- package/dist/db/discovery.js.map +1 -0
- package/dist/db/discovery.test.d.ts +2 -0
- package/dist/db/discovery.test.d.ts.map +1 -0
- package/dist/db/discovery.test.js +125 -0
- package/dist/db/discovery.test.js.map +1 -0
- package/dist/db/errors.d.ts +52 -0
- package/dist/db/errors.d.ts.map +1 -0
- package/dist/db/errors.js +82 -0
- package/dist/db/errors.js.map +1 -0
- package/dist/db/errors.test.d.ts +2 -0
- package/dist/db/errors.test.d.ts.map +1 -0
- package/dist/db/errors.test.js +34 -0
- package/dist/db/errors.test.js.map +1 -0
- package/dist/db/escape-like.d.ts +8 -0
- package/dist/db/escape-like.d.ts.map +1 -0
- package/dist/db/escape-like.js +12 -0
- package/dist/db/escape-like.js.map +1 -0
- package/dist/db/escape-like.test.d.ts +2 -0
- package/dist/db/escape-like.test.d.ts.map +1 -0
- package/dist/db/escape-like.test.js +25 -0
- package/dist/db/escape-like.test.js.map +1 -0
- package/dist/db/index.d.ts +5 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +7 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/repositories/campaign-exclude-list.d.ts +89 -0
- package/dist/db/repositories/campaign-exclude-list.d.ts.map +1 -0
- package/dist/db/repositories/campaign-exclude-list.js +204 -0
- package/dist/db/repositories/campaign-exclude-list.js.map +1 -0
- package/dist/db/repositories/campaign-exclude-list.test.d.ts +2 -0
- package/dist/db/repositories/campaign-exclude-list.test.d.ts.map +1 -0
- package/dist/db/repositories/campaign-exclude-list.test.js +132 -0
- package/dist/db/repositories/campaign-exclude-list.test.js.map +1 -0
- package/dist/db/repositories/campaign-statistics.d.ts +58 -0
- package/dist/db/repositories/campaign-statistics.d.ts.map +1 -0
- package/dist/db/repositories/campaign-statistics.js +278 -0
- package/dist/db/repositories/campaign-statistics.js.map +1 -0
- package/dist/db/repositories/campaign-statistics.test.d.ts +2 -0
- package/dist/db/repositories/campaign-statistics.test.d.ts.map +1 -0
- package/dist/db/repositories/campaign-statistics.test.js +266 -0
- package/dist/db/repositories/campaign-statistics.test.js.map +1 -0
- package/dist/db/repositories/campaign.d.ts +103 -0
- package/dist/db/repositories/campaign.d.ts.map +1 -0
- package/dist/db/repositories/campaign.js +388 -0
- package/dist/db/repositories/campaign.js.map +1 -0
- package/dist/db/repositories/campaign.test.d.ts +2 -0
- package/dist/db/repositories/campaign.test.d.ts.map +1 -0
- package/dist/db/repositories/campaign.test.js +392 -0
- package/dist/db/repositories/campaign.test.js.map +1 -0
- package/dist/db/repositories/index.d.ts +6 -0
- package/dist/db/repositories/index.d.ts.map +1 -0
- package/dist/db/repositories/index.js +8 -0
- package/dist/db/repositories/index.js.map +1 -0
- package/dist/db/repositories/message.d.ts +57 -0
- package/dist/db/repositories/message.d.ts.map +1 -0
- package/dist/db/repositories/message.integration.test.d.ts +2 -0
- package/dist/db/repositories/message.integration.test.d.ts.map +1 -0
- package/dist/db/repositories/message.integration.test.js +137 -0
- package/dist/db/repositories/message.integration.test.js.map +1 -0
- package/dist/db/repositories/message.js +308 -0
- package/dist/db/repositories/message.js.map +1 -0
- package/dist/db/repositories/message.test.d.ts +2 -0
- package/dist/db/repositories/message.test.d.ts.map +1 -0
- package/dist/db/repositories/message.test.js +179 -0
- package/dist/db/repositories/message.test.js.map +1 -0
- package/dist/db/repositories/profile.d.ts +42 -0
- package/dist/db/repositories/profile.d.ts.map +1 -0
- package/dist/db/repositories/profile.integration.test.d.ts +2 -0
- package/dist/db/repositories/profile.integration.test.d.ts.map +1 -0
- package/dist/db/repositories/profile.integration.test.js +222 -0
- package/dist/db/repositories/profile.integration.test.js.map +1 -0
- package/dist/db/repositories/profile.js +163 -0
- package/dist/db/repositories/profile.js.map +1 -0
- package/dist/db/repositories/profile.test.d.ts +2 -0
- package/dist/db/repositories/profile.test.d.ts.map +1 -0
- package/dist/db/repositories/profile.test.js +216 -0
- package/dist/db/repositories/profile.test.js.map +1 -0
- package/dist/db/testing/create-fixture.d.ts +2 -0
- package/dist/db/testing/create-fixture.d.ts.map +1 -0
- package/dist/db/testing/create-fixture.js +745 -0
- package/dist/db/testing/create-fixture.js.map +1 -0
- package/dist/db/testing/open-fixture.d.ts +15 -0
- package/dist/db/testing/open-fixture.d.ts.map +1 -0
- package/dist/db/testing/open-fixture.js +39 -0
- package/dist/db/testing/open-fixture.js.map +1 -0
- package/dist/formats/campaign-format.d.ts +31 -0
- package/dist/formats/campaign-format.d.ts.map +1 -0
- package/dist/formats/campaign-format.js +225 -0
- package/dist/formats/campaign-format.js.map +1 -0
- package/dist/formats/campaign-format.test.d.ts +2 -0
- package/dist/formats/campaign-format.test.d.ts.map +1 -0
- package/dist/formats/campaign-format.test.js +442 -0
- package/dist/formats/campaign-format.test.js.map +1 -0
- package/dist/formats/errors.d.ts +7 -0
- package/dist/formats/errors.d.ts.map +1 -0
- package/dist/formats/errors.js +12 -0
- package/dist/formats/errors.js.map +1 -0
- package/dist/formats/index.d.ts +3 -0
- package/dist/formats/index.d.ts.map +1 -0
- package/dist/formats/index.js +5 -0
- package/dist/formats/index.js.map +1 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -1
- package/dist/index.js.map +1 -1
- package/dist/operations/campaign-add-action.d.ts +14 -0
- package/dist/operations/campaign-add-action.d.ts.map +1 -0
- package/dist/operations/campaign-add-action.js +33 -0
- package/dist/operations/campaign-add-action.js.map +1 -0
- package/dist/operations/campaign-add-action.test.d.ts +2 -0
- package/dist/operations/campaign-add-action.test.d.ts.map +1 -0
- package/dist/operations/campaign-add-action.test.js +122 -0
- package/dist/operations/campaign-add-action.test.js.map +1 -0
- package/dist/operations/campaign-create.d.ts +17 -0
- package/dist/operations/campaign-create.d.ts.map +1 -0
- package/dist/operations/campaign-create.js +21 -0
- package/dist/operations/campaign-create.js.map +1 -0
- package/dist/operations/campaign-create.test.d.ts +2 -0
- package/dist/operations/campaign-create.test.d.ts.map +1 -0
- package/dist/operations/campaign-create.test.js +113 -0
- package/dist/operations/campaign-create.test.js.map +1 -0
- package/dist/operations/campaign-delete.d.ts +20 -0
- package/dist/operations/campaign-delete.d.ts.map +1 -0
- package/dist/operations/campaign-delete.js +22 -0
- package/dist/operations/campaign-delete.js.map +1 -0
- package/dist/operations/campaign-delete.test.d.ts +2 -0
- package/dist/operations/campaign-delete.test.d.ts.map +1 -0
- package/dist/operations/campaign-delete.test.js +92 -0
- package/dist/operations/campaign-delete.test.js.map +1 -0
- package/dist/operations/campaign-exclude-add.d.ts +16 -0
- package/dist/operations/campaign-exclude-add.d.ts.map +1 -0
- package/dist/operations/campaign-exclude-add.js +27 -0
- package/dist/operations/campaign-exclude-add.js.map +1 -0
- package/dist/operations/campaign-exclude-add.test.d.ts +2 -0
- package/dist/operations/campaign-exclude-add.test.d.ts.map +1 -0
- package/dist/operations/campaign-exclude-add.test.js +114 -0
- package/dist/operations/campaign-exclude-add.test.js.map +1 -0
- package/dist/operations/campaign-exclude-list.d.ts +14 -0
- package/dist/operations/campaign-exclude-list.d.ts.map +1 -0
- package/dist/operations/campaign-exclude-list.js +26 -0
- package/dist/operations/campaign-exclude-list.js.map +1 -0
- package/dist/operations/campaign-exclude-list.test.d.ts +2 -0
- package/dist/operations/campaign-exclude-list.test.d.ts.map +1 -0
- package/dist/operations/campaign-exclude-list.test.js +113 -0
- package/dist/operations/campaign-exclude-list.test.js.map +1 -0
- package/dist/operations/campaign-exclude-remove.d.ts +16 -0
- package/dist/operations/campaign-exclude-remove.d.ts.map +1 -0
- package/dist/operations/campaign-exclude-remove.js +27 -0
- package/dist/operations/campaign-exclude-remove.js.map +1 -0
- package/dist/operations/campaign-exclude-remove.test.d.ts +2 -0
- package/dist/operations/campaign-exclude-remove.test.d.ts.map +1 -0
- package/dist/operations/campaign-exclude-remove.test.js +114 -0
- package/dist/operations/campaign-exclude-remove.test.js.map +1 -0
- package/dist/operations/campaign-export.d.ts +12 -0
- package/dist/operations/campaign-export.d.ts.map +1 -0
- package/dist/operations/campaign-export.js +24 -0
- package/dist/operations/campaign-export.js.map +1 -0
- package/dist/operations/campaign-export.test.d.ts +2 -0
- package/dist/operations/campaign-export.test.d.ts.map +1 -0
- package/dist/operations/campaign-export.test.js +134 -0
- package/dist/operations/campaign-export.test.js.map +1 -0
- package/dist/operations/campaign-get.d.ts +19 -0
- package/dist/operations/campaign-get.d.ts.map +1 -0
- package/dist/operations/campaign-get.js +23 -0
- package/dist/operations/campaign-get.js.map +1 -0
- package/dist/operations/campaign-get.test.d.ts +2 -0
- package/dist/operations/campaign-get.test.d.ts.map +1 -0
- package/dist/operations/campaign-get.test.js +99 -0
- package/dist/operations/campaign-get.test.js.map +1 -0
- package/dist/operations/campaign-list.d.ts +20 -0
- package/dist/operations/campaign-list.d.ts.map +1 -0
- package/dist/operations/campaign-list.js +23 -0
- package/dist/operations/campaign-list.js.map +1 -0
- package/dist/operations/campaign-list.test.d.ts +2 -0
- package/dist/operations/campaign-list.test.d.ts.map +1 -0
- package/dist/operations/campaign-list.test.js +106 -0
- package/dist/operations/campaign-list.test.js.map +1 -0
- package/dist/operations/campaign-move-next.d.ts +15 -0
- package/dist/operations/campaign-move-next.d.ts.map +1 -0
- package/dist/operations/campaign-move-next.js +25 -0
- package/dist/operations/campaign-move-next.js.map +1 -0
- package/dist/operations/campaign-move-next.test.d.ts +2 -0
- package/dist/operations/campaign-move-next.test.d.ts.map +1 -0
- package/dist/operations/campaign-move-next.test.js +105 -0
- package/dist/operations/campaign-move-next.test.js.map +1 -0
- package/dist/operations/campaign-remove-action.d.ts +12 -0
- package/dist/operations/campaign-remove-action.d.ts.map +1 -0
- package/dist/operations/campaign-remove-action.js +23 -0
- package/dist/operations/campaign-remove-action.js.map +1 -0
- package/dist/operations/campaign-remove-action.test.d.ts +2 -0
- package/dist/operations/campaign-remove-action.test.d.ts.map +1 -0
- package/dist/operations/campaign-remove-action.test.js +95 -0
- package/dist/operations/campaign-remove-action.test.js.map +1 -0
- package/dist/operations/campaign-reorder-actions.d.ts +13 -0
- package/dist/operations/campaign-reorder-actions.d.ts.map +1 -0
- package/dist/operations/campaign-reorder-actions.js +19 -0
- package/dist/operations/campaign-reorder-actions.js.map +1 -0
- package/dist/operations/campaign-reorder-actions.test.d.ts +2 -0
- package/dist/operations/campaign-reorder-actions.test.d.ts.map +1 -0
- package/dist/operations/campaign-reorder-actions.test.js +103 -0
- package/dist/operations/campaign-reorder-actions.test.js.map +1 -0
- package/dist/operations/campaign-retry.d.ts +13 -0
- package/dist/operations/campaign-retry.d.ts.map +1 -0
- package/dist/operations/campaign-retry.js +24 -0
- package/dist/operations/campaign-retry.js.map +1 -0
- package/dist/operations/campaign-retry.test.d.ts +2 -0
- package/dist/operations/campaign-retry.test.d.ts.map +1 -0
- package/dist/operations/campaign-retry.test.js +100 -0
- package/dist/operations/campaign-retry.test.js.map +1 -0
- package/dist/operations/campaign-start.d.ts +13 -0
- package/dist/operations/campaign-start.d.ts.map +1 -0
- package/dist/operations/campaign-start.js +24 -0
- package/dist/operations/campaign-start.js.map +1 -0
- package/dist/operations/campaign-start.test.d.ts +2 -0
- package/dist/operations/campaign-start.test.d.ts.map +1 -0
- package/dist/operations/campaign-start.test.js +96 -0
- package/dist/operations/campaign-start.test.js.map +1 -0
- package/dist/operations/campaign-statistics.d.ts +10 -0
- package/dist/operations/campaign-statistics.d.ts.map +1 -0
- package/dist/operations/campaign-statistics.js +23 -0
- package/dist/operations/campaign-statistics.js.map +1 -0
- package/dist/operations/campaign-statistics.test.d.ts +2 -0
- package/dist/operations/campaign-statistics.test.d.ts.map +1 -0
- package/dist/operations/campaign-statistics.test.js +120 -0
- package/dist/operations/campaign-statistics.test.js.map +1 -0
- package/dist/operations/campaign-status.d.ts +26 -0
- package/dist/operations/campaign-status.d.ts.map +1 -0
- package/dist/operations/campaign-status.js +32 -0
- package/dist/operations/campaign-status.js.map +1 -0
- package/dist/operations/campaign-status.test.d.ts +2 -0
- package/dist/operations/campaign-status.test.d.ts.map +1 -0
- package/dist/operations/campaign-status.test.js +142 -0
- package/dist/operations/campaign-status.test.js.map +1 -0
- package/dist/operations/campaign-stop.d.ts +11 -0
- package/dist/operations/campaign-stop.d.ts.map +1 -0
- package/dist/operations/campaign-stop.js +23 -0
- package/dist/operations/campaign-stop.js.map +1 -0
- package/dist/operations/campaign-stop.test.d.ts +2 -0
- package/dist/operations/campaign-stop.test.d.ts.map +1 -0
- package/dist/operations/campaign-stop.test.js +92 -0
- package/dist/operations/campaign-stop.test.js.map +1 -0
- package/dist/operations/campaign-update.d.ts +18 -0
- package/dist/operations/campaign-update.d.ts.map +1 -0
- package/dist/operations/campaign-update.js +21 -0
- package/dist/operations/campaign-update.js.map +1 -0
- package/dist/operations/campaign-update.test.d.ts +2 -0
- package/dist/operations/campaign-update.test.d.ts.map +1 -0
- package/dist/operations/campaign-update.test.js +105 -0
- package/dist/operations/campaign-update.test.js.map +1 -0
- package/dist/operations/check-replies.d.ts +12 -0
- package/dist/operations/check-replies.d.ts.map +1 -0
- package/dist/operations/check-replies.js +26 -0
- package/dist/operations/check-replies.js.map +1 -0
- package/dist/operations/check-replies.test.d.ts +2 -0
- package/dist/operations/check-replies.test.d.ts.map +1 -0
- package/dist/operations/check-replies.test.js +118 -0
- package/dist/operations/check-replies.test.js.map +1 -0
- package/dist/operations/import-people-from-urls.d.ts +16 -0
- package/dist/operations/import-people-from-urls.d.ts.map +1 -0
- package/dist/operations/import-people-from-urls.js +27 -0
- package/dist/operations/import-people-from-urls.js.map +1 -0
- package/dist/operations/import-people-from-urls.test.d.ts +2 -0
- package/dist/operations/import-people-from-urls.test.d.ts.map +1 -0
- package/dist/operations/import-people-from-urls.test.js +118 -0
- package/dist/operations/import-people-from-urls.test.js.map +1 -0
- package/dist/operations/index.d.ts +24 -0
- package/dist/operations/index.d.ts.map +1 -0
- package/dist/operations/index.js +30 -0
- package/dist/operations/index.js.map +1 -0
- package/dist/operations/query-messages.d.ts +23 -0
- package/dist/operations/query-messages.d.ts.map +1 -0
- package/dist/operations/query-messages.js +33 -0
- package/dist/operations/query-messages.js.map +1 -0
- package/dist/operations/query-messages.test.d.ts +2 -0
- package/dist/operations/query-messages.test.d.ts.map +1 -0
- package/dist/operations/query-messages.test.js +134 -0
- package/dist/operations/query-messages.test.js.map +1 -0
- package/dist/operations/scrape-messaging-history.d.ts +10 -0
- package/dist/operations/scrape-messaging-history.d.ts.map +1 -0
- package/dist/operations/scrape-messaging-history.js +24 -0
- package/dist/operations/scrape-messaging-history.js.map +1 -0
- package/dist/operations/scrape-messaging-history.test.d.ts +2 -0
- package/dist/operations/scrape-messaging-history.test.d.ts.map +1 -0
- package/dist/operations/scrape-messaging-history.test.js +112 -0
- package/dist/operations/scrape-messaging-history.test.js.map +1 -0
- package/dist/operations/types.d.ts +10 -0
- package/dist/operations/types.d.ts.map +1 -0
- package/dist/operations/types.js +4 -0
- package/dist/operations/types.js.map +1 -0
- package/dist/services/account-resolution.d.ts +22 -0
- package/dist/services/account-resolution.d.ts.map +1 -0
- package/dist/services/account-resolution.js +45 -0
- package/dist/services/account-resolution.js.map +1 -0
- package/dist/services/account-resolution.test.d.ts +2 -0
- package/dist/services/account-resolution.test.d.ts.map +1 -0
- package/dist/services/account-resolution.test.js +91 -0
- package/dist/services/account-resolution.test.js.map +1 -0
- package/dist/services/app.d.ts +62 -0
- package/dist/services/app.d.ts.map +1 -0
- package/dist/services/app.js +200 -0
- package/dist/services/app.js.map +1 -0
- package/dist/services/app.test.d.ts +2 -0
- package/dist/services/app.test.d.ts.map +1 -0
- package/dist/services/app.test.js +267 -0
- package/dist/services/app.test.js.map +1 -0
- package/dist/services/campaign.d.ts +143 -0
- package/dist/services/campaign.d.ts.map +1 -0
- package/dist/services/campaign.js +409 -0
- package/dist/services/campaign.js.map +1 -0
- package/dist/services/campaign.test.d.ts +2 -0
- package/dist/services/campaign.test.d.ts.map +1 -0
- package/dist/services/campaign.test.js +481 -0
- package/dist/services/campaign.test.js.map +1 -0
- package/dist/services/errors.d.ts +83 -0
- package/dist/services/errors.d.ts.map +1 -0
- package/dist/services/errors.js +126 -0
- package/dist/services/errors.js.map +1 -0
- package/dist/services/errors.test.d.ts +2 -0
- package/dist/services/errors.test.d.ts.map +1 -0
- package/dist/services/errors.test.js +123 -0
- package/dist/services/errors.test.js.map +1 -0
- package/dist/services/index.d.ts +10 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +12 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/instance-context.d.ts +37 -0
- package/dist/services/instance-context.d.ts.map +1 -0
- package/dist/services/instance-context.js +49 -0
- package/dist/services/instance-context.js.map +1 -0
- package/dist/services/instance-context.test.d.ts +2 -0
- package/dist/services/instance-context.test.d.ts.map +1 -0
- package/dist/services/instance-context.test.js +203 -0
- package/dist/services/instance-context.test.js.map +1 -0
- package/dist/services/instance-lifecycle.d.ts +38 -0
- package/dist/services/instance-lifecycle.d.ts.map +1 -0
- package/dist/services/instance-lifecycle.js +87 -0
- package/dist/services/instance-lifecycle.js.map +1 -0
- package/dist/services/instance-lifecycle.test.d.ts +2 -0
- package/dist/services/instance-lifecycle.test.d.ts.map +1 -0
- package/dist/services/instance-lifecycle.test.js +158 -0
- package/dist/services/instance-lifecycle.test.js.map +1 -0
- package/dist/services/instance.d.ts +90 -0
- package/dist/services/instance.d.ts.map +1 -0
- package/dist/services/instance.js +178 -0
- package/dist/services/instance.js.map +1 -0
- package/dist/services/instance.test.d.ts +2 -0
- package/dist/services/instance.test.d.ts.map +1 -0
- package/dist/services/instance.test.js +291 -0
- package/dist/services/instance.test.js.map +1 -0
- package/dist/services/launcher.d.ts +54 -0
- package/dist/services/launcher.d.ts.map +1 -0
- package/dist/services/launcher.js +162 -0
- package/dist/services/launcher.js.map +1 -0
- package/dist/services/launcher.test.d.ts +2 -0
- package/dist/services/launcher.test.d.ts.map +1 -0
- package/dist/services/launcher.test.js +163 -0
- package/dist/services/launcher.test.js.map +1 -0
- package/dist/services/status.d.ts +37 -0
- package/dist/services/status.d.ts.map +1 -0
- package/dist/services/status.js +85 -0
- package/dist/services/status.js.map +1 -0
- package/dist/services/status.test.d.ts +2 -0
- package/dist/services/status.test.d.ts.map +1 -0
- package/dist/services/status.test.js +248 -0
- package/dist/services/status.test.js.map +1 -0
- package/dist/testing/e2e-helpers.d.ts +41 -0
- package/dist/testing/e2e-helpers.d.ts.map +1 -0
- package/dist/testing/e2e-helpers.js +114 -0
- package/dist/testing/e2e-helpers.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +4 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/types/account.d.ts +13 -0
- package/dist/types/account.d.ts.map +1 -0
- package/dist/types/account.js +4 -0
- package/dist/types/account.js.map +1 -0
- package/dist/types/account.test.d.ts +2 -0
- package/dist/types/account.test.d.ts.map +1 -0
- package/dist/types/account.test.js +26 -0
- package/dist/types/account.test.js.map +1 -0
- package/dist/types/campaign.d.ts +267 -0
- package/dist/types/campaign.d.ts.map +1 -0
- package/dist/types/campaign.js +4 -0
- package/dist/types/campaign.js.map +1 -0
- package/dist/types/cdp.d.ts +18 -0
- package/dist/types/cdp.d.ts.map +1 -0
- package/dist/types/cdp.js +4 -0
- package/dist/types/cdp.js.map +1 -0
- package/dist/types/cdp.test.d.ts +2 -0
- package/dist/types/cdp.test.d.ts.map +1 -0
- package/dist/types/cdp.test.js +30 -0
- package/dist/types/cdp.test.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/instance.d.ts +36 -0
- package/dist/types/instance.d.ts.map +1 -0
- package/dist/types/instance.js +4 -0
- package/dist/types/instance.js.map +1 -0
- package/dist/types/instance.test.d.ts +2 -0
- package/dist/types/instance.test.d.ts.map +1 -0
- package/dist/types/instance.test.js +59 -0
- package/dist/types/instance.test.js.map +1 -0
- package/dist/types/messaging.d.ts +51 -0
- package/dist/types/messaging.d.ts.map +1 -0
- package/dist/types/messaging.js +4 -0
- package/dist/types/messaging.js.map +1 -0
- package/dist/types/profile.d.ts +81 -0
- package/dist/types/profile.d.ts.map +1 -0
- package/dist/types/profile.js +4 -0
- package/dist/types/profile.js.map +1 -0
- package/dist/types/profile.test.d.ts +2 -0
- package/dist/types/profile.test.d.ts.map +1 -0
- package/dist/types/profile.test.js +105 -0
- package/dist/types/profile.test.js.map +1 -0
- package/dist/utils/cdp-port.d.ts +5 -0
- package/dist/utils/cdp-port.d.ts.map +1 -0
- package/dist/utils/cdp-port.js +15 -0
- package/dist/utils/cdp-port.js.map +1 -0
- package/dist/utils/cdp-port.test.d.ts +2 -0
- package/dist/utils/cdp-port.test.d.ts.map +1 -0
- package/dist/utils/cdp-port.test.js +23 -0
- package/dist/utils/cdp-port.test.js.map +1 -0
- package/dist/utils/delay.d.ts +5 -0
- package/dist/utils/delay.d.ts.map +1 -0
- package/dist/utils/delay.js +9 -0
- package/dist/utils/delay.js.map +1 -0
- package/dist/utils/delay.test.d.ts +2 -0
- package/dist/utils/delay.test.d.ts.map +1 -0
- package/dist/utils/delay.test.js +17 -0
- package/dist/utils/delay.test.js.map +1 -0
- package/dist/utils/error-message.d.ts +5 -0
- package/dist/utils/error-message.d.ts.map +1 -0
- package/dist/utils/error-message.js +9 -0
- package/dist/utils/error-message.js.map +1 -0
- package/dist/utils/error-message.test.d.ts +2 -0
- package/dist/utils/error-message.test.d.ts.map +1 -0
- package/dist/utils/error-message.test.js +28 -0
- package/dist/utils/error-message.test.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/loopback.d.ts +10 -0
- package/dist/utils/loopback.d.ts.map +1 -0
- package/dist/utils/loopback.js +28 -0
- package/dist/utils/loopback.js.map +1 -0
- package/dist/utils/loopback.test.d.ts +2 -0
- package/dist/utils/loopback.test.d.ts.map +1 -0
- package/dist/utils/loopback.test.js +34 -0
- package/dist/utils/loopback.test.js.map +1 -0
- package/package.json +26 -9
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
vi.mock("./launcher.js", () => ({
|
|
5
|
+
LauncherService: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
import { LinkedHelperNotRunningError } from "./errors.js";
|
|
8
|
+
import { LauncherService } from "./launcher.js";
|
|
9
|
+
import { AccountResolutionError, resolveAccount } from "./account-resolution.js";
|
|
10
|
+
const mockedLauncherService = vi.mocked(LauncherService);
|
|
11
|
+
function mockLauncher(overrides = {}) {
|
|
12
|
+
const disconnect = vi.fn();
|
|
13
|
+
mockedLauncherService.mockImplementation(function () {
|
|
14
|
+
return {
|
|
15
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
16
|
+
disconnect,
|
|
17
|
+
listAccounts: vi.fn().mockResolvedValue([]),
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
return { disconnect };
|
|
22
|
+
}
|
|
23
|
+
describe("resolveAccount", () => {
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.clearAllMocks();
|
|
26
|
+
});
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
vi.restoreAllMocks();
|
|
29
|
+
});
|
|
30
|
+
it("returns the account ID when exactly one account exists", async () => {
|
|
31
|
+
mockLauncher({
|
|
32
|
+
listAccounts: vi.fn().mockResolvedValue([
|
|
33
|
+
{ id: 42, liId: 100, name: "Alice", email: "alice@test.com" },
|
|
34
|
+
]),
|
|
35
|
+
});
|
|
36
|
+
const id = await resolveAccount(9222);
|
|
37
|
+
expect(id).toBe(42);
|
|
38
|
+
expect(mockedLauncherService).toHaveBeenCalledWith(9222, undefined);
|
|
39
|
+
});
|
|
40
|
+
it("throws AccountResolutionError with reason 'no-accounts' when no accounts exist", async () => {
|
|
41
|
+
mockLauncher({
|
|
42
|
+
listAccounts: vi.fn().mockResolvedValue([]),
|
|
43
|
+
});
|
|
44
|
+
await expect(resolveAccount(9222)).rejects.toThrow(AccountResolutionError);
|
|
45
|
+
await expect(resolveAccount(9222)).rejects.toMatchObject({
|
|
46
|
+
reason: "no-accounts",
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
it("throws AccountResolutionError with reason 'multiple-accounts' when multiple accounts exist", async () => {
|
|
50
|
+
mockLauncher({
|
|
51
|
+
listAccounts: vi.fn().mockResolvedValue([
|
|
52
|
+
{ id: 1, liId: 100, name: "Alice" },
|
|
53
|
+
{ id: 2, liId: 200, name: "Bob" },
|
|
54
|
+
]),
|
|
55
|
+
});
|
|
56
|
+
await expect(resolveAccount(9222)).rejects.toThrow(AccountResolutionError);
|
|
57
|
+
await expect(resolveAccount(9222)).rejects.toMatchObject({
|
|
58
|
+
reason: "multiple-accounts",
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
it("propagates CDP connection failure from launcher.connect()", async () => {
|
|
62
|
+
mockLauncher({
|
|
63
|
+
connect: vi.fn().mockRejectedValue(new LinkedHelperNotRunningError(9222)),
|
|
64
|
+
});
|
|
65
|
+
await expect(resolveAccount(9222)).rejects.toThrow(LinkedHelperNotRunningError);
|
|
66
|
+
});
|
|
67
|
+
it("calls disconnect in the finally block on success", async () => {
|
|
68
|
+
const { disconnect } = mockLauncher({
|
|
69
|
+
listAccounts: vi.fn().mockResolvedValue([
|
|
70
|
+
{ id: 1, liId: 100, name: "Alice" },
|
|
71
|
+
]),
|
|
72
|
+
});
|
|
73
|
+
await resolveAccount(9222);
|
|
74
|
+
expect(disconnect).toHaveBeenCalledOnce();
|
|
75
|
+
});
|
|
76
|
+
it("calls disconnect in the finally block on AccountResolutionError", async () => {
|
|
77
|
+
const { disconnect } = mockLauncher({
|
|
78
|
+
listAccounts: vi.fn().mockResolvedValue([]),
|
|
79
|
+
});
|
|
80
|
+
await expect(resolveAccount(9222)).rejects.toThrow(AccountResolutionError);
|
|
81
|
+
expect(disconnect).toHaveBeenCalledOnce();
|
|
82
|
+
});
|
|
83
|
+
it("calls disconnect in the finally block on connection failure", async () => {
|
|
84
|
+
const { disconnect } = mockLauncher({
|
|
85
|
+
connect: vi.fn().mockRejectedValue(new LinkedHelperNotRunningError(9222)),
|
|
86
|
+
});
|
|
87
|
+
await expect(resolveAccount(9222)).rejects.toThrow(LinkedHelperNotRunningError);
|
|
88
|
+
expect(disconnect).toHaveBeenCalledOnce();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=account-resolution.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-resolution.test.js","sourceRoot":"","sources":["../../src/services/account-resolution.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface AppServiceOptions {
|
|
2
|
+
/** Delay in ms after spawn before checking if the app is reachable (default 3000). */
|
|
3
|
+
launchProbeDelay?: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Manages the LinkedHelper application process lifecycle.
|
|
7
|
+
*
|
|
8
|
+
* Provides methods to launch, quit, and probe the LinkedHelper
|
|
9
|
+
* Electron application. When no explicit CDP port is provided,
|
|
10
|
+
* a free port is selected automatically at launch time.
|
|
11
|
+
*/
|
|
12
|
+
export declare class AppService {
|
|
13
|
+
private assignedPort;
|
|
14
|
+
private childProcess;
|
|
15
|
+
private readonly launchProbeDelay;
|
|
16
|
+
/**
|
|
17
|
+
* @param cdpPort - Explicit CDP port. When omitted, `launch()` will
|
|
18
|
+
* select a free port automatically via `get-port`.
|
|
19
|
+
* @param options - Additional configuration options.
|
|
20
|
+
*/
|
|
21
|
+
constructor(cdpPort?: number, options?: AppServiceOptions);
|
|
22
|
+
/**
|
|
23
|
+
* The CDP port currently in use.
|
|
24
|
+
*
|
|
25
|
+
* @throws {Error} if neither an explicit port was provided nor
|
|
26
|
+
* `launch()` has been called yet.
|
|
27
|
+
*/
|
|
28
|
+
get cdpPort(): number;
|
|
29
|
+
/**
|
|
30
|
+
* Launch the LinkedHelper application with CDP enabled.
|
|
31
|
+
*
|
|
32
|
+
* If no CDP port was specified in the constructor, a free port
|
|
33
|
+
* is selected automatically.
|
|
34
|
+
*
|
|
35
|
+
* @throws {AppNotFoundError} if the binary cannot be found.
|
|
36
|
+
* @throws {AppLaunchError} if the process fails to start.
|
|
37
|
+
*/
|
|
38
|
+
launch(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Quit the LinkedHelper application.
|
|
41
|
+
*
|
|
42
|
+
* When a child process handle is available, sends `SIGTERM` and waits
|
|
43
|
+
* for the process to exit. If it does not exit within
|
|
44
|
+
* {@link QUIT_GRACEFUL_TIMEOUT}, escalates to `SIGKILL`.
|
|
45
|
+
*
|
|
46
|
+
* When no child process handle is available (app was launched
|
|
47
|
+
* externally), attempts to close via CDP.
|
|
48
|
+
*/
|
|
49
|
+
quit(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Check whether LinkedHelper is running by probing its CDP endpoint.
|
|
52
|
+
*/
|
|
53
|
+
isRunning(): Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Locate the LinkedHelper binary for the current platform.
|
|
56
|
+
*
|
|
57
|
+
* @throws {AppNotFoundError} if the binary does not exist at the
|
|
58
|
+
* expected location.
|
|
59
|
+
*/
|
|
60
|
+
static findBinary(): string;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/services/app.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,iBAAiB;IAChC,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAE1C;;;;OAIG;gBACS,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB;IAKzD;;;;;OAKG;IACH,IAAI,OAAO,IAAI,MAAM,CAKpB;IAED;;;;;;;;OAQG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IA0C7B;;;;;;;;;OASG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkC3B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAYnC;;;;;OAKG;IACH,MAAM,CAAC,UAAU,IAAI,MAAM;CAW5B"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { accessSync, constants } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import getPort from "get-port";
|
|
7
|
+
import { discoverTargets } from "../cdp/index.js";
|
|
8
|
+
import { AppLaunchError, AppNotFoundError } from "./errors.js";
|
|
9
|
+
/** Default delay after spawn before checking if the app is reachable (ms). */
|
|
10
|
+
const DEFAULT_LAUNCH_PROBE_DELAY = 3000;
|
|
11
|
+
/** Maximum time to wait for the process to exit after SIGTERM (ms). */
|
|
12
|
+
const QUIT_GRACEFUL_TIMEOUT = 10_000;
|
|
13
|
+
/** Maximum time to wait for the process to exit after SIGKILL (ms). */
|
|
14
|
+
const QUIT_FORCE_TIMEOUT = 5_000;
|
|
15
|
+
/**
|
|
16
|
+
* Manages the LinkedHelper application process lifecycle.
|
|
17
|
+
*
|
|
18
|
+
* Provides methods to launch, quit, and probe the LinkedHelper
|
|
19
|
+
* Electron application. When no explicit CDP port is provided,
|
|
20
|
+
* a free port is selected automatically at launch time.
|
|
21
|
+
*/
|
|
22
|
+
export class AppService {
|
|
23
|
+
assignedPort;
|
|
24
|
+
childProcess = null;
|
|
25
|
+
launchProbeDelay;
|
|
26
|
+
/**
|
|
27
|
+
* @param cdpPort - Explicit CDP port. When omitted, `launch()` will
|
|
28
|
+
* select a free port automatically via `get-port`.
|
|
29
|
+
* @param options - Additional configuration options.
|
|
30
|
+
*/
|
|
31
|
+
constructor(cdpPort, options) {
|
|
32
|
+
this.assignedPort = cdpPort ?? null;
|
|
33
|
+
this.launchProbeDelay = options?.launchProbeDelay ?? DEFAULT_LAUNCH_PROBE_DELAY;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The CDP port currently in use.
|
|
37
|
+
*
|
|
38
|
+
* @throws {Error} if neither an explicit port was provided nor
|
|
39
|
+
* `launch()` has been called yet.
|
|
40
|
+
*/
|
|
41
|
+
get cdpPort() {
|
|
42
|
+
if (this.assignedPort === null) {
|
|
43
|
+
throw new Error("CDP port not yet assigned — call launch() first or provide a port to the constructor");
|
|
44
|
+
}
|
|
45
|
+
return this.assignedPort;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Launch the LinkedHelper application with CDP enabled.
|
|
49
|
+
*
|
|
50
|
+
* If no CDP port was specified in the constructor, a free port
|
|
51
|
+
* is selected automatically.
|
|
52
|
+
*
|
|
53
|
+
* @throws {AppNotFoundError} if the binary cannot be found.
|
|
54
|
+
* @throws {AppLaunchError} if the process fails to start.
|
|
55
|
+
*/
|
|
56
|
+
async launch() {
|
|
57
|
+
if (this.assignedPort !== null && await this.isRunning()) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (this.assignedPort === null) {
|
|
61
|
+
this.assignedPort = await getPort();
|
|
62
|
+
}
|
|
63
|
+
const binary = AppService.findBinary();
|
|
64
|
+
const args = [`--remote-debugging-port=${String(this.assignedPort)}`];
|
|
65
|
+
const child = spawn(binary, args, {
|
|
66
|
+
detached: true,
|
|
67
|
+
stdio: "ignore",
|
|
68
|
+
});
|
|
69
|
+
child.unref();
|
|
70
|
+
// Wait for an early error (e.g. ENOENT from spawn) before probing
|
|
71
|
+
await new Promise((resolve, reject) => {
|
|
72
|
+
const onError = (err) => {
|
|
73
|
+
cleanup();
|
|
74
|
+
reject(new AppLaunchError(`Failed to launch LinkedHelper: ${err.message}`, { cause: err }));
|
|
75
|
+
};
|
|
76
|
+
const timer = setTimeout(() => {
|
|
77
|
+
cleanup();
|
|
78
|
+
resolve();
|
|
79
|
+
}, this.launchProbeDelay);
|
|
80
|
+
function cleanup() {
|
|
81
|
+
child.removeListener("error", onError);
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
}
|
|
84
|
+
child.on("error", onError);
|
|
85
|
+
});
|
|
86
|
+
this.childProcess = child;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Quit the LinkedHelper application.
|
|
90
|
+
*
|
|
91
|
+
* When a child process handle is available, sends `SIGTERM` and waits
|
|
92
|
+
* for the process to exit. If it does not exit within
|
|
93
|
+
* {@link QUIT_GRACEFUL_TIMEOUT}, escalates to `SIGKILL`.
|
|
94
|
+
*
|
|
95
|
+
* When no child process handle is available (app was launched
|
|
96
|
+
* externally), attempts to close via CDP.
|
|
97
|
+
*/
|
|
98
|
+
async quit() {
|
|
99
|
+
if (this.childProcess) {
|
|
100
|
+
const child = this.childProcess;
|
|
101
|
+
this.childProcess = null;
|
|
102
|
+
child.kill("SIGTERM");
|
|
103
|
+
const exited = await waitForExit(child, QUIT_GRACEFUL_TIMEOUT);
|
|
104
|
+
if (!exited) {
|
|
105
|
+
child.kill("SIGKILL");
|
|
106
|
+
await waitForExit(child, QUIT_FORCE_TIMEOUT);
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (this.assignedPort === null) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Fallback: close via CDP Browser.close
|
|
114
|
+
try {
|
|
115
|
+
const targets = await discoverTargets(this.assignedPort);
|
|
116
|
+
const first = targets[0];
|
|
117
|
+
if (first) {
|
|
118
|
+
await fetch(`http://127.0.0.1:${String(this.assignedPort)}/json/close/${first.id}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// App may already be closed
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check whether LinkedHelper is running by probing its CDP endpoint.
|
|
127
|
+
*/
|
|
128
|
+
async isRunning() {
|
|
129
|
+
if (this.assignedPort === null) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
await discoverTargets(this.assignedPort);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Locate the LinkedHelper binary for the current platform.
|
|
142
|
+
*
|
|
143
|
+
* @throws {AppNotFoundError} if the binary does not exist at the
|
|
144
|
+
* expected location.
|
|
145
|
+
*/
|
|
146
|
+
static findBinary() {
|
|
147
|
+
const envPath = process.env["LINKEDHELPER_PATH"];
|
|
148
|
+
if (envPath) {
|
|
149
|
+
assertFileExists(envPath);
|
|
150
|
+
return envPath;
|
|
151
|
+
}
|
|
152
|
+
const path = getDefaultBinaryPath();
|
|
153
|
+
assertFileExists(path);
|
|
154
|
+
return path;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function getDefaultBinaryPath() {
|
|
158
|
+
switch (process.platform) {
|
|
159
|
+
case "darwin":
|
|
160
|
+
return "/Applications/linked-helper.app/Contents/MacOS/linked-helper";
|
|
161
|
+
case "win32":
|
|
162
|
+
return join(process.env["LOCALAPPDATA"] ?? join(process.env["USERPROFILE"] ?? "C:\\Users\\Default", "AppData", "Local"), "Programs", "linked-helper", "linked-helper.exe");
|
|
163
|
+
default:
|
|
164
|
+
return "/opt/linked-helper/linked-helper";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Wait for a child process to exit, with a timeout.
|
|
169
|
+
*
|
|
170
|
+
* @returns `true` if the process exited within the timeout, `false` otherwise.
|
|
171
|
+
*/
|
|
172
|
+
function waitForExit(child, timeout) {
|
|
173
|
+
if (child.exitCode !== null) {
|
|
174
|
+
return Promise.resolve(true);
|
|
175
|
+
}
|
|
176
|
+
return new Promise((resolve) => {
|
|
177
|
+
const timer = setTimeout(() => {
|
|
178
|
+
cleanup();
|
|
179
|
+
resolve(false);
|
|
180
|
+
}, timeout);
|
|
181
|
+
const onExit = () => {
|
|
182
|
+
cleanup();
|
|
183
|
+
resolve(true);
|
|
184
|
+
};
|
|
185
|
+
function cleanup() {
|
|
186
|
+
child.removeListener("exit", onExit);
|
|
187
|
+
clearTimeout(timer);
|
|
188
|
+
}
|
|
189
|
+
child.on("exit", onExit);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function assertFileExists(path) {
|
|
193
|
+
try {
|
|
194
|
+
accessSync(path, constants.X_OK);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
throw new AppNotFoundError(`LinkedHelper binary not found at ${path}. Set LINKEDHELPER_PATH to override.`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/services/app.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAqB,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,OAAO,MAAM,UAAU,CAAC;AAE/B,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/D,8EAA8E;AAC9E,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAExC,uEAAuE;AACvE,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAErC,uEAAuE;AACvE,MAAM,kBAAkB,GAAG,KAAK,CAAC;AAOjC;;;;;;GAMG;AACH,MAAM,OAAO,UAAU;IACb,YAAY,CAAgB;IAC5B,YAAY,GAAwB,IAAI,CAAC;IAChC,gBAAgB,CAAS;IAE1C;;;;OAIG;IACH,YAAY,OAAgB,EAAE,OAA2B;QACvD,IAAI,CAAC,YAAY,GAAG,OAAO,IAAI,IAAI,CAAC;QACpC,IAAI,CAAC,gBAAgB,GAAG,OAAO,EAAE,gBAAgB,IAAI,0BAA0B,CAAC;IAClF,CAAC;IAED;;;;;OAKG;IACH,IAAI,OAAO;QACT,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,sFAAsF,CAAC,CAAC;QAC1G,CAAC;QACD,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM;QACV,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACzD,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YAC/B,IAAI,CAAC,YAAY,GAAG,MAAM,OAAO,EAAE,CAAC;QACtC,CAAC;QAED,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,CAAC,2BAA2B,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAEtE,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE;YAChC,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,QAAQ;SAChB,CAAC,CAAC;QAEH,KAAK,CAAC,KAAK,EAAE,CAAC;QAEd,kEAAkE;QAClE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,MAAM,OAAO,GAAG,CAAC,GAAU,EAAE,EAAE;gBAC7B,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,cAAc,CAAC,kCAAkC,GAAG,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;YAC9F,CAAC,CAAC;YAEF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,CAAC;YACZ,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAE1B,SAAS,OAAO;gBACd,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBACvC,YAAY,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;YAED,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC5B,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC;YAChC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YAEzB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAEtB,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;YAC/D,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACtB,MAAM,WAAW,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC;YAC/C,CAAC;YAED,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YAC/B,OAAO;QACT,CAAC;QAED,wCAAwC;QACxC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACzD,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACzB,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,KAAK,CACT,oBAAoB,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,eAAe,KAAK,CAAC,EAAE,EAAE,CACvE,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,4BAA4B;QAC9B,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS;QACb,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YAC/B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC;YACH,MAAM,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACzC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,UAAU;QACf,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACjD,IAAI,OAAO,EAAE,CAAC;YACZ,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAC1B,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;QACpC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,SAAS,oBAAoB;IAC3B,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,8DAA8D,CAAC;QACxE,KAAK,OAAO;YACV,OAAO,IAAI,CACT,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,oBAAoB,EAAE,SAAS,EAAE,OAAO,CAAC,EAC3G,UAAU,EACV,eAAe,EACf,mBAAmB,CACpB,CAAC;QACJ;YACE,OAAO,kCAAkC,CAAC;IAC9C,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,KAAmB,EAAE,OAAe;IACvD,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC5B,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;QACtC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,OAAO,EAAE,CAAC;YACV,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC,EAAE,OAAO,CAAC,CAAC;QAEZ,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,OAAO,EAAE,CAAC;YACV,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC,CAAC;QAEF,SAAS,OAAO;YACd,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACrC,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;QAED,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,gBAAgB,CACxB,oCAAoC,IAAI,sCAAsC,CAC/E,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.test.d.ts","sourceRoot":"","sources":["../../src/services/app.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { accessSync } from "node:fs";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { AppLaunchError, AppNotFoundError } from "./errors.js";
|
|
7
|
+
import { AppService } from "./app.js";
|
|
8
|
+
vi.mock("node:child_process", () => ({
|
|
9
|
+
spawn: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
vi.mock("node:fs", () => ({
|
|
12
|
+
accessSync: vi.fn(),
|
|
13
|
+
constants: { X_OK: 1 },
|
|
14
|
+
}));
|
|
15
|
+
vi.mock("../cdp/index.js", () => ({
|
|
16
|
+
discoverTargets: vi.fn(),
|
|
17
|
+
}));
|
|
18
|
+
vi.mock("get-port", () => ({
|
|
19
|
+
default: vi.fn(),
|
|
20
|
+
}));
|
|
21
|
+
import { discoverTargets } from "../cdp/index.js";
|
|
22
|
+
import getPort from "get-port";
|
|
23
|
+
const mockedSpawn = vi.mocked(spawn);
|
|
24
|
+
const mockedAccessSync = vi.mocked(accessSync);
|
|
25
|
+
const mockedDiscoverTargets = vi.mocked(discoverTargets);
|
|
26
|
+
const mockedGetPort = vi.mocked(getPort);
|
|
27
|
+
/** Use zero probe delay in tests to avoid 3s waits. */
|
|
28
|
+
const FAST_OPTIONS = { launchProbeDelay: 0 };
|
|
29
|
+
function makeMockChild() {
|
|
30
|
+
const listeners = new Map();
|
|
31
|
+
const child = {
|
|
32
|
+
unref: vi.fn(),
|
|
33
|
+
kill: vi.fn(),
|
|
34
|
+
on: vi.fn((event, handler) => {
|
|
35
|
+
if (!listeners.has(event))
|
|
36
|
+
listeners.set(event, new Set());
|
|
37
|
+
listeners.get(event).add(handler);
|
|
38
|
+
return child;
|
|
39
|
+
}),
|
|
40
|
+
removeListener: vi.fn((event, handler) => {
|
|
41
|
+
listeners.get(event)?.delete(handler);
|
|
42
|
+
return child;
|
|
43
|
+
}),
|
|
44
|
+
pid: 12345,
|
|
45
|
+
exitCode: null,
|
|
46
|
+
};
|
|
47
|
+
// Helper to simulate process exit from tests
|
|
48
|
+
child._emitExit = (code) => {
|
|
49
|
+
child.exitCode = code;
|
|
50
|
+
for (const handler of listeners.get("exit") ?? []) {
|
|
51
|
+
handler(code, null);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
return child;
|
|
55
|
+
}
|
|
56
|
+
afterEach(() => {
|
|
57
|
+
vi.restoreAllMocks();
|
|
58
|
+
delete process.env["LINKEDHELPER_PATH"];
|
|
59
|
+
});
|
|
60
|
+
describe("AppService", () => {
|
|
61
|
+
describe("cdpPort getter", () => {
|
|
62
|
+
it("returns the explicit port when provided", () => {
|
|
63
|
+
const service = new AppService(9222);
|
|
64
|
+
expect(service.cdpPort).toBe(9222);
|
|
65
|
+
});
|
|
66
|
+
it("throws when no port assigned and launch() not called", () => {
|
|
67
|
+
const service = new AppService();
|
|
68
|
+
expect(() => service.cdpPort).toThrow(/call launch\(\) first/);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe("findBinary", () => {
|
|
72
|
+
it("returns the darwin path on macOS", () => {
|
|
73
|
+
vi.stubGlobal("process", { ...process, platform: "darwin", env: {} });
|
|
74
|
+
mockedAccessSync.mockReturnValue(undefined);
|
|
75
|
+
const result = AppService.findBinary();
|
|
76
|
+
expect(result).toBe("/Applications/linked-helper.app/Contents/MacOS/linked-helper");
|
|
77
|
+
});
|
|
78
|
+
it("returns the win32 path on Windows", () => {
|
|
79
|
+
vi.stubGlobal("process", {
|
|
80
|
+
...process,
|
|
81
|
+
platform: "win32",
|
|
82
|
+
env: { LOCALAPPDATA: "C:\\Users\\test\\AppData\\Local" },
|
|
83
|
+
});
|
|
84
|
+
mockedAccessSync.mockReturnValue(undefined);
|
|
85
|
+
const result = AppService.findBinary();
|
|
86
|
+
expect(result).toContain("linked-helper.exe");
|
|
87
|
+
expect(result).toContain("Local");
|
|
88
|
+
});
|
|
89
|
+
it("returns the linux path on Linux", () => {
|
|
90
|
+
vi.stubGlobal("process", { ...process, platform: "linux", env: {} });
|
|
91
|
+
mockedAccessSync.mockReturnValue(undefined);
|
|
92
|
+
const result = AppService.findBinary();
|
|
93
|
+
expect(result).toBe("/opt/linked-helper/linked-helper");
|
|
94
|
+
});
|
|
95
|
+
it("uses LINKEDHELPER_PATH env override", () => {
|
|
96
|
+
vi.stubGlobal("process", {
|
|
97
|
+
...process,
|
|
98
|
+
platform: "darwin",
|
|
99
|
+
env: { LINKEDHELPER_PATH: "/custom/path/lh" },
|
|
100
|
+
});
|
|
101
|
+
mockedAccessSync.mockReturnValue(undefined);
|
|
102
|
+
const result = AppService.findBinary();
|
|
103
|
+
expect(result).toBe("/custom/path/lh");
|
|
104
|
+
});
|
|
105
|
+
it("throws AppNotFoundError when binary does not exist", () => {
|
|
106
|
+
vi.stubGlobal("process", { ...process, platform: "darwin", env: {} });
|
|
107
|
+
mockedAccessSync.mockImplementation(() => {
|
|
108
|
+
throw new Error("ENOENT");
|
|
109
|
+
});
|
|
110
|
+
expect(() => AppService.findBinary()).toThrow(AppNotFoundError);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe("isRunning", () => {
|
|
114
|
+
it("returns true when CDP endpoint responds", async () => {
|
|
115
|
+
const service = new AppService(9222);
|
|
116
|
+
mockedDiscoverTargets.mockResolvedValue([]);
|
|
117
|
+
expect(await service.isRunning()).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
it("returns false when CDP endpoint is unreachable", async () => {
|
|
120
|
+
const service = new AppService(9222);
|
|
121
|
+
mockedDiscoverTargets.mockRejectedValue(new Error("connection refused"));
|
|
122
|
+
expect(await service.isRunning()).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
it("returns false when no port is assigned", async () => {
|
|
125
|
+
const service = new AppService();
|
|
126
|
+
expect(await service.isRunning()).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe("launch", () => {
|
|
130
|
+
beforeEach(() => {
|
|
131
|
+
vi.stubGlobal("process", { ...process, platform: "darwin", env: {} });
|
|
132
|
+
});
|
|
133
|
+
it("skips launch if already running with explicit port", async () => {
|
|
134
|
+
const service = new AppService(9222, FAST_OPTIONS);
|
|
135
|
+
mockedDiscoverTargets.mockResolvedValue([]);
|
|
136
|
+
await service.launch();
|
|
137
|
+
expect(mockedSpawn).not.toHaveBeenCalled();
|
|
138
|
+
});
|
|
139
|
+
it("spawns the binary with explicit CDP port argument", async () => {
|
|
140
|
+
const service = new AppService(9222, FAST_OPTIONS);
|
|
141
|
+
mockedDiscoverTargets.mockRejectedValue(new Error("not running"));
|
|
142
|
+
mockedAccessSync.mockReturnValue(undefined);
|
|
143
|
+
const child = makeMockChild();
|
|
144
|
+
mockedSpawn.mockReturnValue(child);
|
|
145
|
+
await service.launch();
|
|
146
|
+
expect(mockedSpawn).toHaveBeenCalledWith("/Applications/linked-helper.app/Contents/MacOS/linked-helper", ["--remote-debugging-port=9222"], { detached: true, stdio: "ignore" });
|
|
147
|
+
expect(child.unref).toHaveBeenCalled();
|
|
148
|
+
});
|
|
149
|
+
it("selects a free port via get-port when no port provided", async () => {
|
|
150
|
+
const service = new AppService(undefined, FAST_OPTIONS);
|
|
151
|
+
mockedGetPort.mockResolvedValue(54321);
|
|
152
|
+
mockedAccessSync.mockReturnValue(undefined);
|
|
153
|
+
const child = makeMockChild();
|
|
154
|
+
mockedSpawn.mockReturnValue(child);
|
|
155
|
+
await service.launch();
|
|
156
|
+
expect(mockedGetPort).toHaveBeenCalled();
|
|
157
|
+
expect(mockedSpawn).toHaveBeenCalledWith(expect.any(String), ["--remote-debugging-port=54321"], { detached: true, stdio: "ignore" });
|
|
158
|
+
expect(service.cdpPort).toBe(54321);
|
|
159
|
+
});
|
|
160
|
+
it("reuses assigned port on second launch() call", async () => {
|
|
161
|
+
const service = new AppService(undefined, FAST_OPTIONS);
|
|
162
|
+
mockedGetPort.mockResolvedValue(54321);
|
|
163
|
+
mockedAccessSync.mockReturnValue(undefined);
|
|
164
|
+
const child = makeMockChild();
|
|
165
|
+
mockedSpawn.mockReturnValue(child);
|
|
166
|
+
await service.launch();
|
|
167
|
+
expect(service.cdpPort).toBe(54321);
|
|
168
|
+
// Reset call counts for second launch assertions
|
|
169
|
+
mockedGetPort.mockClear();
|
|
170
|
+
mockedSpawn.mockClear();
|
|
171
|
+
// Second launch — port is already assigned, app reported as running
|
|
172
|
+
mockedDiscoverTargets.mockResolvedValue([]);
|
|
173
|
+
await service.launch();
|
|
174
|
+
// getPort must not be called again — port was already assigned
|
|
175
|
+
expect(mockedGetPort).not.toHaveBeenCalled();
|
|
176
|
+
// spawn must not be called again — app is already running
|
|
177
|
+
expect(mockedSpawn).not.toHaveBeenCalled();
|
|
178
|
+
expect(service.cdpPort).toBe(54321);
|
|
179
|
+
});
|
|
180
|
+
it("throws AppLaunchError on spawn error", async () => {
|
|
181
|
+
const service = new AppService(9222, FAST_OPTIONS);
|
|
182
|
+
mockedDiscoverTargets.mockRejectedValue(new Error("not running"));
|
|
183
|
+
mockedAccessSync.mockReturnValue(undefined);
|
|
184
|
+
const child = makeMockChild();
|
|
185
|
+
child.on.mockImplementation((event, handler) => {
|
|
186
|
+
if (event === "error") {
|
|
187
|
+
queueMicrotask(() => handler(new Error("ENOENT")));
|
|
188
|
+
}
|
|
189
|
+
return child;
|
|
190
|
+
});
|
|
191
|
+
mockedSpawn.mockReturnValue(child);
|
|
192
|
+
await expect(service.launch()).rejects.toThrow(AppLaunchError);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
describe("quit", () => {
|
|
196
|
+
beforeEach(() => {
|
|
197
|
+
vi.stubGlobal("process", { ...process, platform: "darwin", env: {} });
|
|
198
|
+
});
|
|
199
|
+
it("sends SIGTERM and waits for exit", async () => {
|
|
200
|
+
const service = new AppService(9222, FAST_OPTIONS);
|
|
201
|
+
mockedDiscoverTargets.mockRejectedValue(new Error("not running"));
|
|
202
|
+
mockedAccessSync.mockReturnValue(undefined);
|
|
203
|
+
const child = makeMockChild();
|
|
204
|
+
const emitExit = child._emitExit;
|
|
205
|
+
// Simulate process exiting shortly after SIGTERM
|
|
206
|
+
child.kill.mockImplementation(() => {
|
|
207
|
+
queueMicrotask(() => emitExit(0));
|
|
208
|
+
});
|
|
209
|
+
mockedSpawn.mockReturnValue(child);
|
|
210
|
+
await service.launch();
|
|
211
|
+
await service.quit();
|
|
212
|
+
expect(child.kill).toHaveBeenCalledWith("SIGTERM");
|
|
213
|
+
});
|
|
214
|
+
it("escalates to SIGKILL when process does not exit after SIGTERM", async () => {
|
|
215
|
+
vi.useFakeTimers({ shouldAdvanceTime: true });
|
|
216
|
+
const service = new AppService(9222, FAST_OPTIONS);
|
|
217
|
+
mockedDiscoverTargets.mockRejectedValue(new Error("not running"));
|
|
218
|
+
mockedAccessSync.mockReturnValue(undefined);
|
|
219
|
+
const child = makeMockChild();
|
|
220
|
+
const emitExit = child._emitExit;
|
|
221
|
+
let killCount = 0;
|
|
222
|
+
child.kill.mockImplementation(() => {
|
|
223
|
+
killCount++;
|
|
224
|
+
// Only exit on SIGKILL (second kill call)
|
|
225
|
+
if (killCount === 2) {
|
|
226
|
+
queueMicrotask(() => emitExit(137));
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
mockedSpawn.mockReturnValue(child);
|
|
230
|
+
await service.launch();
|
|
231
|
+
const quitPromise = service.quit();
|
|
232
|
+
// Advance past the graceful timeout
|
|
233
|
+
await vi.advanceTimersByTimeAsync(11_000);
|
|
234
|
+
await quitPromise;
|
|
235
|
+
expect(child.kill).toHaveBeenCalledWith("SIGTERM");
|
|
236
|
+
expect(child.kill).toHaveBeenCalledWith("SIGKILL");
|
|
237
|
+
vi.useRealTimers();
|
|
238
|
+
});
|
|
239
|
+
it("falls back to CDP close when no spawned process", async () => {
|
|
240
|
+
const service = new AppService(9222);
|
|
241
|
+
mockedDiscoverTargets.mockResolvedValue([
|
|
242
|
+
{
|
|
243
|
+
id: "T1",
|
|
244
|
+
type: "page",
|
|
245
|
+
title: "",
|
|
246
|
+
url: "",
|
|
247
|
+
description: "",
|
|
248
|
+
devtoolsFrontendUrl: "",
|
|
249
|
+
},
|
|
250
|
+
]);
|
|
251
|
+
const mockFetch = vi.fn().mockResolvedValue({ ok: true });
|
|
252
|
+
vi.stubGlobal("fetch", mockFetch);
|
|
253
|
+
await service.quit();
|
|
254
|
+
expect(mockFetch).toHaveBeenCalledWith("http://127.0.0.1:9222/json/close/T1");
|
|
255
|
+
});
|
|
256
|
+
it("does not throw when CDP close fails", async () => {
|
|
257
|
+
const service = new AppService(9222);
|
|
258
|
+
mockedDiscoverTargets.mockRejectedValue(new Error("not running"));
|
|
259
|
+
await expect(service.quit()).resolves.toBeUndefined();
|
|
260
|
+
});
|
|
261
|
+
it("does nothing when no port assigned and no child process", async () => {
|
|
262
|
+
const service = new AppService();
|
|
263
|
+
await expect(service.quit()).resolves.toBeUndefined();
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
//# sourceMappingURL=app.test.js.map
|