@tummycrypt/acuity-middleware 0.1.0 → 0.1.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.
- package/dist/adapters/acuity-scraper.d.ts +8 -0
- package/dist/adapters/acuity-scraper.d.ts.map +1 -0
- package/dist/adapters/acuity-scraper.js +8 -0
- package/dist/adapters/acuity-scraper.js.map +1 -0
- package/dist/adapters/types.d.ts +8 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +8 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/core/types.d.ts +10 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/acuity-wizard.d.ts +49 -0
- package/dist/middleware/acuity-wizard.d.ts.map +1 -0
- package/dist/middleware/acuity-wizard.js +265 -0
- package/dist/middleware/acuity-wizard.js.map +1 -0
- package/dist/middleware/browser-service.d.ts +53 -0
- package/dist/middleware/browser-service.d.ts.map +1 -0
- package/dist/middleware/browser-service.js +105 -0
- package/dist/middleware/browser-service.js.map +1 -0
- package/dist/middleware/errors.d.ts +58 -0
- package/dist/middleware/errors.d.ts.map +1 -0
- package/dist/middleware/errors.js +43 -0
- package/dist/middleware/errors.js.map +1 -0
- package/{src/middleware/index.ts → dist/middleware/index.d.ts} +5 -52
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +38 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/logger.d.ts +26 -0
- package/dist/middleware/logger.d.ts.map +1 -0
- package/dist/middleware/logger.js +65 -0
- package/dist/middleware/logger.js.map +1 -0
- package/dist/middleware/remote-adapter.d.ts +45 -0
- package/dist/middleware/remote-adapter.d.ts.map +1 -0
- package/dist/middleware/remote-adapter.js +178 -0
- package/dist/middleware/remote-adapter.js.map +1 -0
- package/dist/middleware/selector-health.d.ts +44 -0
- package/dist/middleware/selector-health.d.ts.map +1 -0
- package/dist/middleware/selector-health.js +144 -0
- package/dist/middleware/selector-health.js.map +1 -0
- package/dist/middleware/selectors.d.ts +108 -0
- package/dist/middleware/selectors.d.ts.map +1 -0
- package/dist/middleware/selectors.js +249 -0
- package/dist/middleware/selectors.js.map +1 -0
- package/dist/middleware/server.d.ts +34 -0
- package/dist/middleware/server.d.ts.map +1 -0
- package/dist/middleware/server.js +377 -0
- package/dist/middleware/server.js.map +1 -0
- package/dist/middleware/service-resolver.d.ts +46 -0
- package/dist/middleware/service-resolver.d.ts.map +1 -0
- package/dist/middleware/service-resolver.js +274 -0
- package/dist/middleware/service-resolver.js.map +1 -0
- package/dist/middleware/slot-parser.d.ts +29 -0
- package/dist/middleware/slot-parser.d.ts.map +1 -0
- package/dist/middleware/slot-parser.js +50 -0
- package/dist/middleware/slot-parser.js.map +1 -0
- package/dist/middleware/steps/__tests__/fixtures.d.ts +14 -0
- package/dist/middleware/steps/__tests__/fixtures.d.ts.map +1 -0
- package/dist/middleware/steps/__tests__/fixtures.js +204 -0
- package/dist/middleware/steps/__tests__/fixtures.js.map +1 -0
- package/dist/middleware/steps/bypass-payment.d.ts +54 -0
- package/dist/middleware/steps/bypass-payment.d.ts.map +1 -0
- package/dist/middleware/steps/bypass-payment.js +164 -0
- package/dist/middleware/steps/bypass-payment.js.map +1 -0
- package/dist/middleware/steps/extract-business.d.ts +93 -0
- package/dist/middleware/steps/extract-business.d.ts.map +1 -0
- package/dist/middleware/steps/extract-business.js +170 -0
- package/dist/middleware/steps/extract-business.js.map +1 -0
- package/dist/middleware/steps/extract.d.ts +41 -0
- package/dist/middleware/steps/extract.d.ts.map +1 -0
- package/dist/middleware/steps/extract.js +128 -0
- package/dist/middleware/steps/extract.js.map +1 -0
- package/dist/middleware/steps/fill-form.d.ts +45 -0
- package/dist/middleware/steps/fill-form.d.ts.map +1 -0
- package/dist/middleware/steps/fill-form.js +262 -0
- package/dist/middleware/steps/fill-form.js.map +1 -0
- package/dist/middleware/steps/index.d.ts +12 -0
- package/dist/middleware/steps/index.d.ts.map +1 -0
- package/dist/middleware/steps/index.js +12 -0
- package/dist/middleware/steps/index.js.map +1 -0
- package/dist/middleware/steps/navigate.d.ts +51 -0
- package/dist/middleware/steps/navigate.d.ts.map +1 -0
- package/dist/middleware/steps/navigate.js +391 -0
- package/dist/middleware/steps/navigate.js.map +1 -0
- package/dist/middleware/steps/read-availability.d.ts +37 -0
- package/dist/middleware/steps/read-availability.d.ts.map +1 -0
- package/dist/middleware/steps/read-availability.js +298 -0
- package/dist/middleware/steps/read-availability.js.map +1 -0
- package/dist/middleware/steps/read-slots.d.ts +33 -0
- package/dist/middleware/steps/read-slots.d.ts.map +1 -0
- package/dist/middleware/steps/read-slots.js +295 -0
- package/dist/middleware/steps/read-slots.js.map +1 -0
- package/dist/middleware/steps/read-via-url.d.ts +39 -0
- package/dist/middleware/steps/read-via-url.d.ts.map +1 -0
- package/dist/middleware/steps/read-via-url.js +141 -0
- package/dist/middleware/steps/read-via-url.js.map +1 -0
- package/dist/middleware/steps/submit.d.ts +22 -0
- package/dist/middleware/steps/submit.d.ts.map +1 -0
- package/dist/middleware/steps/submit.js +112 -0
- package/dist/middleware/steps/submit.js.map +1 -0
- package/dist/middleware/wizard-calendar.d.ts +37 -0
- package/dist/middleware/wizard-calendar.d.ts.map +1 -0
- package/dist/middleware/wizard-calendar.js +177 -0
- package/dist/middleware/wizard-calendar.js.map +1 -0
- package/dist/middleware/wizard-service.d.ts +30 -0
- package/dist/middleware/wizard-service.d.ts.map +1 -0
- package/dist/middleware/wizard-service.js +89 -0
- package/dist/middleware/wizard-service.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/{src/server.ts → dist/server.js} +1 -0
- package/dist/server.js.map +1 -0
- package/package.json +16 -4
- package/.github/workflows/build-paper.yml +0 -39
- package/.github/workflows/ci.yml +0 -37
- package/Dockerfile +0 -53
- package/docs/blog-post.mdx +0 -240
- package/docs/paper/IEEEtran.bst +0 -2409
- package/docs/paper/IEEEtran.cls +0 -6347
- package/docs/paper/acuity-middleware-paper.tex +0 -375
- package/docs/paper/balance.sty +0 -87
- package/docs/paper/references.bib +0 -231
- package/docs/paper.md +0 -400
- package/flake.nix +0 -32
- package/modal-app.py +0 -82
- package/src/adapters/acuity-scraper.ts +0 -543
- package/src/adapters/types.ts +0 -193
- package/src/core/types.ts +0 -325
- package/src/index.ts +0 -75
- package/src/middleware/acuity-wizard.ts +0 -456
- package/src/middleware/browser-service.ts +0 -183
- package/src/middleware/errors.ts +0 -70
- package/src/middleware/remote-adapter.ts +0 -246
- package/src/middleware/selectors.ts +0 -308
- package/src/middleware/server.ts +0 -372
- package/src/middleware/steps/bypass-payment.ts +0 -226
- package/src/middleware/steps/extract.ts +0 -174
- package/src/middleware/steps/fill-form.ts +0 -359
- package/src/middleware/steps/index.ts +0 -27
- package/src/middleware/steps/navigate.ts +0 -537
- package/src/middleware/steps/read-availability.ts +0 -399
- package/src/middleware/steps/read-slots.ts +0 -405
- package/src/middleware/steps/submit.ts +0 -168
- package/tsconfig.json +0 -25
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware Error Types
|
|
3
|
+
*
|
|
4
|
+
* Effect TS error types for the Acuity wizard middleware.
|
|
5
|
+
* Bridges to fp-ts SchedulingError at the adapter boundary.
|
|
6
|
+
*/
|
|
7
|
+
import { type SchedulingError } from '../core/types.js';
|
|
8
|
+
declare const BrowserError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
9
|
+
readonly _tag: "BrowserError";
|
|
10
|
+
} & Readonly<A>;
|
|
11
|
+
export declare class BrowserError extends BrowserError_base<{
|
|
12
|
+
readonly reason: 'PLAYWRIGHT_MISSING' | 'LAUNCH_FAILED' | 'PAGE_FAILED' | 'SCREENSHOT_FAILED' | 'NAVIGATION_FAILED';
|
|
13
|
+
readonly cause?: unknown;
|
|
14
|
+
}> {
|
|
15
|
+
}
|
|
16
|
+
declare const SelectorError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
17
|
+
readonly _tag: "SelectorError";
|
|
18
|
+
} & Readonly<A>;
|
|
19
|
+
export declare class SelectorError extends SelectorError_base<{
|
|
20
|
+
readonly candidates: readonly string[];
|
|
21
|
+
readonly message: string;
|
|
22
|
+
}> {
|
|
23
|
+
}
|
|
24
|
+
declare const WizardStepError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
25
|
+
readonly _tag: "WizardStepError";
|
|
26
|
+
} & Readonly<A>;
|
|
27
|
+
export declare class WizardStepError extends WizardStepError_base<{
|
|
28
|
+
readonly step: 'navigate' | 'fill-form' | 'bypass-payment' | 'submit' | 'extract' | 'read-availability' | 'read-slots' | 'extract-business';
|
|
29
|
+
readonly message: string;
|
|
30
|
+
readonly screenshot?: Buffer;
|
|
31
|
+
readonly cause?: unknown;
|
|
32
|
+
}> {
|
|
33
|
+
}
|
|
34
|
+
declare const CouponError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
35
|
+
readonly _tag: "CouponError";
|
|
36
|
+
} & Readonly<A>;
|
|
37
|
+
export declare class CouponError extends CouponError_base<{
|
|
38
|
+
readonly code: string;
|
|
39
|
+
readonly message: string;
|
|
40
|
+
}> {
|
|
41
|
+
}
|
|
42
|
+
declare const ServiceResolverError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
43
|
+
readonly _tag: "ServiceResolverError";
|
|
44
|
+
} & Readonly<A>;
|
|
45
|
+
export declare class ServiceResolverError extends ServiceResolverError_base<{
|
|
46
|
+
readonly serviceName: string;
|
|
47
|
+
readonly strategies: readonly string[];
|
|
48
|
+
readonly message: string;
|
|
49
|
+
}> {
|
|
50
|
+
}
|
|
51
|
+
export type MiddlewareError = BrowserError | SelectorError | WizardStepError | CouponError | ServiceResolverError;
|
|
52
|
+
/**
|
|
53
|
+
* Convert Effect middleware errors to fp-ts SchedulingError
|
|
54
|
+
* for compatibility with the existing booking pipeline.
|
|
55
|
+
*/
|
|
56
|
+
export declare const toSchedulingError: (error: MiddlewareError) => SchedulingError;
|
|
57
|
+
export {};
|
|
58
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/middleware/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAU,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;;;;AAMhE,qBAAa,YAAa,SAAQ,kBAAiC;IAClE,QAAQ,CAAC,MAAM,EACZ,oBAAoB,GACpB,eAAe,GACf,aAAa,GACb,mBAAmB,GACnB,mBAAmB,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;CAAG;;;;AAEL,qBAAa,aAAc,SAAQ,mBAAkC;IACpE,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB,CAAC;CAAG;;;;AAEL,qBAAa,eAAgB,SAAQ,qBAAoC;IACxE,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,mBAAmB,GAAG,YAAY,GAAG,kBAAkB,CAAC;IAC5I,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;CAAG;;;;AAEL,qBAAa,WAAY,SAAQ,iBAAgC;IAChE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB,CAAC;CAAG;;;;AAEL,qBAAa,oBAAqB,SAAQ,0BAAyC;IAClF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB,CAAC;CAAG;AAEL,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,aAAa,GAAG,eAAe,GAAG,WAAW,GAAG,oBAAoB,CAAC;AAMlH;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,eAAe,KAAG,eAoB1D,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware Error Types
|
|
3
|
+
*
|
|
4
|
+
* Effect TS error types for the Acuity wizard middleware.
|
|
5
|
+
* Bridges to fp-ts SchedulingError at the adapter boundary.
|
|
6
|
+
*/
|
|
7
|
+
import { Data } from 'effect';
|
|
8
|
+
import { Errors } from '../core/types.js';
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// ERROR CLASSES
|
|
11
|
+
// =============================================================================
|
|
12
|
+
export class BrowserError extends Data.TaggedError('BrowserError') {
|
|
13
|
+
}
|
|
14
|
+
export class SelectorError extends Data.TaggedError('SelectorError') {
|
|
15
|
+
}
|
|
16
|
+
export class WizardStepError extends Data.TaggedError('WizardStepError') {
|
|
17
|
+
}
|
|
18
|
+
export class CouponError extends Data.TaggedError('CouponError') {
|
|
19
|
+
}
|
|
20
|
+
export class ServiceResolverError extends Data.TaggedError('ServiceResolverError') {
|
|
21
|
+
}
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// BRIDGE: Effect errors -> fp-ts SchedulingError
|
|
24
|
+
// =============================================================================
|
|
25
|
+
/**
|
|
26
|
+
* Convert Effect middleware errors to fp-ts SchedulingError
|
|
27
|
+
* for compatibility with the existing booking pipeline.
|
|
28
|
+
*/
|
|
29
|
+
export const toSchedulingError = (error) => {
|
|
30
|
+
switch (error._tag) {
|
|
31
|
+
case 'BrowserError':
|
|
32
|
+
return Errors.infrastructure(error.reason === 'PLAYWRIGHT_MISSING' ? 'UNKNOWN' : 'NETWORK', `Browser error: ${error.reason}`, error.cause instanceof Error ? error.cause : undefined);
|
|
33
|
+
case 'SelectorError':
|
|
34
|
+
return Errors.acuity('SCRAPE_FAILED', error.message);
|
|
35
|
+
case 'WizardStepError':
|
|
36
|
+
return Errors.acuity('SCRAPE_FAILED', `Wizard step '${error.step}' failed: ${error.message}`);
|
|
37
|
+
case 'CouponError':
|
|
38
|
+
return Errors.acuity('BOOKING_FAILED', `Coupon error: ${error.message}`);
|
|
39
|
+
case 'ServiceResolverError':
|
|
40
|
+
return Errors.acuity('SCRAPE_FAILED', `Service resolution failed: ${error.message}`);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/middleware/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAwB,MAAM,kBAAkB,CAAC;AAEhE,gFAAgF;AAChF,gBAAgB;AAChB,gFAAgF;AAEhF,MAAM,OAAO,YAAa,SAAQ,IAAI,CAAC,WAAW,CAAC,cAAc,CAQ/D;CAAG;AAEL,MAAM,OAAO,aAAc,SAAQ,IAAI,CAAC,WAAW,CAAC,eAAe,CAGjE;CAAG;AAEL,MAAM,OAAO,eAAgB,SAAQ,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAKrE;CAAG;AAEL,MAAM,OAAO,WAAY,SAAQ,IAAI,CAAC,WAAW,CAAC,aAAa,CAG7D;CAAG;AAEL,MAAM,OAAO,oBAAqB,SAAQ,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAI/E;CAAG;AAIL,gFAAgF;AAChF,iDAAiD;AACjD,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAsB,EAAmB,EAAE;IAC5E,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,cAAc;YAClB,OAAO,MAAM,CAAC,cAAc,CAC3B,KAAK,CAAC,MAAM,KAAK,oBAAoB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAC7D,kBAAkB,KAAK,CAAC,MAAM,EAAE,EAChC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CACtD,CAAC;QACH,KAAK,eAAe;YACnB,OAAO,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACtD,KAAK,iBAAiB;YACrB,OAAO,MAAM,CAAC,MAAM,CACnB,eAAe,EACf,gBAAgB,KAAK,CAAC,IAAI,aAAa,KAAK,CAAC,OAAO,EAAE,CACtD,CAAC;QACH,KAAK,aAAa;YACjB,OAAO,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,iBAAiB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1E,KAAK,sBAAsB;YAC1B,OAAO,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,8BAA8B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACvF,CAAC;AACF,CAAC,CAAC"}
|
|
@@ -24,57 +24,10 @@
|
|
|
24
24
|
* const result = await kit.completeBooking(request, 'venmo')();
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
27
|
-
|
|
28
|
-
// Adapter factories
|
|
29
27
|
export { createWizardAdapter, type WizardAdapterConfig } from './acuity-wizard.js';
|
|
30
28
|
export { createRemoteWizardAdapter, type RemoteAdapterConfig } from './remote-adapter.js';
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
export {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
BrowserServiceTest,
|
|
37
|
-
defaultBrowserConfig,
|
|
38
|
-
type BrowserConfig,
|
|
39
|
-
type BrowserServiceShape,
|
|
40
|
-
} from './browser-service.js';
|
|
41
|
-
|
|
42
|
-
// Error types and bridge
|
|
43
|
-
export {
|
|
44
|
-
BrowserError,
|
|
45
|
-
SelectorError,
|
|
46
|
-
WizardStepError,
|
|
47
|
-
CouponError,
|
|
48
|
-
toSchedulingError,
|
|
49
|
-
type MiddlewareError,
|
|
50
|
-
} from './errors.js';
|
|
51
|
-
|
|
52
|
-
// Selector registry
|
|
53
|
-
export {
|
|
54
|
-
Selectors,
|
|
55
|
-
resolveSelector,
|
|
56
|
-
resolve,
|
|
57
|
-
probeSelector,
|
|
58
|
-
probe,
|
|
59
|
-
healthCheck,
|
|
60
|
-
type SelectorKey,
|
|
61
|
-
type ResolvedSelector,
|
|
62
|
-
} from './selectors.js';
|
|
63
|
-
|
|
64
|
-
// Individual wizard steps (for advanced composition)
|
|
65
|
-
export {
|
|
66
|
-
navigateToBooking,
|
|
67
|
-
fillFormFields,
|
|
68
|
-
bypassPayment,
|
|
69
|
-
generateCouponCode,
|
|
70
|
-
submitBooking,
|
|
71
|
-
extractConfirmation,
|
|
72
|
-
toBooking,
|
|
73
|
-
type NavigateParams,
|
|
74
|
-
type NavigateResult,
|
|
75
|
-
type FillFormParams,
|
|
76
|
-
type FillFormResult,
|
|
77
|
-
type BypassPaymentResult,
|
|
78
|
-
type SubmitResult,
|
|
79
|
-
type ConfirmationData,
|
|
80
|
-
} from './steps/index.js';
|
|
29
|
+
export { BrowserService, BrowserServiceLive, BrowserServiceTest, defaultBrowserConfig, type BrowserConfig, type BrowserServiceShape, } from './browser-service.js';
|
|
30
|
+
export { BrowserError, SelectorError, WizardStepError, CouponError, toSchedulingError, type MiddlewareError, } from './errors.js';
|
|
31
|
+
export { Selectors, resolveSelector, resolve, probeSelector, probe, healthCheck, type SelectorKey, type ResolvedSelector, } from './selectors.js';
|
|
32
|
+
export { navigateToBooking, fillFormFields, bypassPayment, generateCouponCode, submitBooking, extractConfirmation, toBooking, type NavigateParams, type NavigateResult, type FillFormParams, type FillFormResult, type BypassPaymentResult, type SubmitResult, type ConfirmationData, } from './steps/index.js';
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,EAAE,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAG1F,OAAO,EACN,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,aAAa,EAClB,KAAK,mBAAmB,GACxB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACN,YAAY,EACZ,aAAa,EACb,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,KAAK,eAAe,GACpB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACN,SAAS,EACT,eAAe,EACf,OAAO,EACP,aAAa,EACb,KAAK,EACL,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,gBAAgB,GACrB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACN,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACrB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware Module - Server-only Acuity wizard automation
|
|
3
|
+
*
|
|
4
|
+
* This module provides the Effect TS-based browser middleware for
|
|
5
|
+
* puppeteering the Acuity scheduling wizard. It is a SEPARATE subpath
|
|
6
|
+
* export (`@tummycrypt/scheduling-kit/middleware`) and should NOT be
|
|
7
|
+
* imported in client-side code (it depends on Playwright).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { createWizardAdapter } from '@tummycrypt/scheduling-kit/middleware';
|
|
12
|
+
* import { createSchedulingKit } from '@tummycrypt/scheduling-kit';
|
|
13
|
+
* import { createVenmoAdapter } from '@tummycrypt/scheduling-kit/payments';
|
|
14
|
+
*
|
|
15
|
+
* const scheduler = createWizardAdapter({
|
|
16
|
+
* baseUrl: process.env.ACUITY_BASE_URL,
|
|
17
|
+
* couponCode: process.env.ACUITY_BYPASS_COUPON,
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* const venmo = createVenmoAdapter({ ... });
|
|
21
|
+
* const kit = createSchedulingKit(scheduler, [venmo]);
|
|
22
|
+
*
|
|
23
|
+
* // Full booking with Venmo payment
|
|
24
|
+
* const result = await kit.completeBooking(request, 'venmo')();
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
// Adapter factories
|
|
28
|
+
export { createWizardAdapter } from './acuity-wizard.js';
|
|
29
|
+
export { createRemoteWizardAdapter } from './remote-adapter.js';
|
|
30
|
+
// Browser service (for custom Layer composition)
|
|
31
|
+
export { BrowserService, BrowserServiceLive, BrowserServiceTest, defaultBrowserConfig, } from './browser-service.js';
|
|
32
|
+
// Error types and bridge
|
|
33
|
+
export { BrowserError, SelectorError, WizardStepError, CouponError, toSchedulingError, } from './errors.js';
|
|
34
|
+
// Selector registry
|
|
35
|
+
export { Selectors, resolveSelector, resolve, probeSelector, probe, healthCheck, } from './selectors.js';
|
|
36
|
+
// Individual wizard steps (for advanced composition)
|
|
37
|
+
export { navigateToBooking, fillFormFields, bypassPayment, generateCouponCode, submitBooking, extractConfirmation, toBooking, } from './steps/index.js';
|
|
38
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,oBAAoB;AACpB,OAAO,EAAE,mBAAmB,EAA4B,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAA4B,MAAM,qBAAqB,CAAC;AAE1F,iDAAiD;AACjD,OAAO,EACN,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,GAGpB,MAAM,sBAAsB,CAAC;AAE9B,yBAAyB;AACzB,OAAO,EACN,YAAY,EACZ,aAAa,EACb,eAAe,EACf,WAAW,EACX,iBAAiB,GAEjB,MAAM,aAAa,CAAC;AAErB,oBAAoB;AACpB,OAAO,EACN,SAAS,EACT,eAAe,EACf,OAAO,EACP,aAAa,EACb,KAAK,EACL,WAAW,GAGX,MAAM,gBAAgB,CAAC;AAExB,qDAAqD;AACrD,OAAO,EACN,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,SAAS,GAQT,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured NDJSON Logger
|
|
3
|
+
*
|
|
4
|
+
* Provides two logging mechanisms:
|
|
5
|
+
* 1. Effect Logger (for code inside Effect.gen) — uses Effect's Logger API
|
|
6
|
+
* 2. ndjsonLog() standalone helper (for plain async handlers outside Effect)
|
|
7
|
+
*
|
|
8
|
+
* Both emit JSON lines to stdout/stderr, compatible with Modal's log capture.
|
|
9
|
+
*/
|
|
10
|
+
import { Layer } from 'effect';
|
|
11
|
+
/**
|
|
12
|
+
* Effect Layer that replaces the default logger with NDJSON output.
|
|
13
|
+
* Add to your layer composition: `Layer.merge(BrowserServiceLive(...), LoggerLive)`
|
|
14
|
+
*/
|
|
15
|
+
export declare const LoggerLive: Layer.Layer<never>;
|
|
16
|
+
type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
|
|
17
|
+
/**
|
|
18
|
+
* Write a structured NDJSON log entry.
|
|
19
|
+
* Use this in plain async handlers that are NOT inside Effect programs.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ndjsonLog('INFO', 'Request received', { endpoint: '/services', serviceId: '123' });
|
|
23
|
+
*/
|
|
24
|
+
export declare const ndjsonLog: (level: LogLevel, msg: string, data?: Record<string, unknown>) => void;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAU,KAAK,EAAW,MAAM,QAAQ,CAAC;AAiChD;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAoD,CAAC;AAM/F,KAAK,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GACrB,OAAO,QAAQ,EACf,KAAK,MAAM,EACX,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,IAeF,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured NDJSON Logger
|
|
3
|
+
*
|
|
4
|
+
* Provides two logging mechanisms:
|
|
5
|
+
* 1. Effect Logger (for code inside Effect.gen) — uses Effect's Logger API
|
|
6
|
+
* 2. ndjsonLog() standalone helper (for plain async handlers outside Effect)
|
|
7
|
+
*
|
|
8
|
+
* Both emit JSON lines to stdout/stderr, compatible with Modal's log capture.
|
|
9
|
+
*/
|
|
10
|
+
import { Logger, HashMap } from 'effect';
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// EFFECT LOGGER (for inside Effect programs)
|
|
13
|
+
// =============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* JSON-line logger that writes structured entries to stdout/stderr.
|
|
16
|
+
* Integrates with Effect.logInfo, Effect.logWarning, Effect.logError, etc.
|
|
17
|
+
*/
|
|
18
|
+
const JsonLogger = Logger.make(({ logLevel, message, annotations, date }) => {
|
|
19
|
+
const entry = {
|
|
20
|
+
ts: date.toISOString(),
|
|
21
|
+
level: logLevel.label.toUpperCase(),
|
|
22
|
+
msg: typeof message === 'string' ? message : JSON.stringify(message),
|
|
23
|
+
};
|
|
24
|
+
// Merge annotations as top-level fields
|
|
25
|
+
if (!HashMap.isEmpty(annotations)) {
|
|
26
|
+
for (const [key, value] of HashMap.toEntries(annotations)) {
|
|
27
|
+
entry[key] = value;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const line = JSON.stringify(entry) + '\n';
|
|
31
|
+
if (logLevel.ordinal >= 40000) { // Error, Fatal
|
|
32
|
+
process.stderr.write(line);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
process.stdout.write(line);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Effect Layer that replaces the default logger with NDJSON output.
|
|
40
|
+
* Add to your layer composition: `Layer.merge(BrowserServiceLive(...), LoggerLive)`
|
|
41
|
+
*/
|
|
42
|
+
export const LoggerLive = Logger.replace(Logger.defaultLogger, JsonLogger);
|
|
43
|
+
/**
|
|
44
|
+
* Write a structured NDJSON log entry.
|
|
45
|
+
* Use this in plain async handlers that are NOT inside Effect programs.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ndjsonLog('INFO', 'Request received', { endpoint: '/services', serviceId: '123' });
|
|
49
|
+
*/
|
|
50
|
+
export const ndjsonLog = (level, msg, data) => {
|
|
51
|
+
const entry = {
|
|
52
|
+
ts: new Date().toISOString(),
|
|
53
|
+
level,
|
|
54
|
+
msg,
|
|
55
|
+
...data,
|
|
56
|
+
};
|
|
57
|
+
const line = JSON.stringify(entry) + '\n';
|
|
58
|
+
if (level === 'ERROR') {
|
|
59
|
+
process.stderr.write(line);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
process.stdout.write(line);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAS,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEhD,gFAAgF;AAChF,6CAA6C;AAC7C,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE;IAC3E,MAAM,KAAK,GAA4B;QACtC,EAAE,EAAE,IAAI,CAAC,WAAW,EAAE;QACtB,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE;QACnC,GAAG,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KACpE,CAAC;IAEF,wCAAwC;IACxC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QACnC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3D,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACpB,CAAC;IACF,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAE1C,IAAI,QAAQ,CAAC,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,eAAe;QAC/C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;SAAM,CAAC;QACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;AACF,CAAC,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAuB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;AAQ/F;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACxB,KAAe,EACf,GAAW,EACX,IAA8B,EACvB,EAAE;IACT,MAAM,KAAK,GAA4B;QACtC,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,KAAK;QACL,GAAG;QACH,GAAG,IAAI;KACP,CAAC;IAEF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAE1C,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;SAAM,CAAC;QACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;AACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote Wizard Adapter
|
|
3
|
+
*
|
|
4
|
+
* SchedulingAdapter implementation backed by HTTP calls to a remote
|
|
5
|
+
* middleware server running Playwright + Chromium (e.g., Modal Labs,
|
|
6
|
+
* Fly.io, or any Docker host).
|
|
7
|
+
*
|
|
8
|
+
* This adapter is the client-side counterpart to `middleware/server.ts`.
|
|
9
|
+
* It serializes requests, sends them over HTTP, and deserializes
|
|
10
|
+
* responses back into fp-ts TaskEither types.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const adapter = createRemoteWizardAdapter({
|
|
15
|
+
* baseUrl: process.env.MODAL_MIDDLEWARE_URL,
|
|
16
|
+
* authToken: process.env.MODAL_AUTH_TOKEN,
|
|
17
|
+
* });
|
|
18
|
+
* const kit = createSchedulingKit(adapter, [venmoAdapter]);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import type { SchedulingAdapter } from '../adapters/types.js';
|
|
22
|
+
import type { Service } from '../core/types.js';
|
|
23
|
+
export interface RemoteAdapterConfig {
|
|
24
|
+
/** Base URL of the middleware server (e.g., https://scheduling-middleware--org.modal.run) */
|
|
25
|
+
readonly baseUrl: string;
|
|
26
|
+
/** Auth token for the middleware server */
|
|
27
|
+
readonly authToken?: string;
|
|
28
|
+
/** Request timeout in ms (default: 60000 - wizard flow can take 30s+) */
|
|
29
|
+
readonly timeout?: number;
|
|
30
|
+
/** Coupon code for payment bypass */
|
|
31
|
+
readonly couponCode?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Static service catalog. When provided, getServices()/getService() return
|
|
34
|
+
* from this list without hitting the remote server. Avoids dependency on
|
|
35
|
+
* the middleware's DOM scraper for service listing.
|
|
36
|
+
*/
|
|
37
|
+
readonly services?: readonly Service[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create a SchedulingAdapter that proxies all operations to a remote
|
|
41
|
+
* middleware server via HTTP. The remote server runs Playwright + Chromium
|
|
42
|
+
* and executes the actual wizard automation.
|
|
43
|
+
*/
|
|
44
|
+
export declare const createRemoteWizardAdapter: (config: RemoteAdapterConfig) => SchedulingAdapter;
|
|
45
|
+
//# sourceMappingURL=remote-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-adapter.d.ts","sourceRoot":"","sources":["../../src/middleware/remote-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAGX,OAAO,EAQP,MAAM,kBAAkB,CAAC;AAO1B,MAAM,WAAW,mBAAmB;IACnC,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CACvC;AAgGD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,QAAQ,mBAAmB,KAAG,iBAwHtE,CAAC"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote Wizard Adapter
|
|
3
|
+
*
|
|
4
|
+
* SchedulingAdapter implementation backed by HTTP calls to a remote
|
|
5
|
+
* middleware server running Playwright + Chromium (e.g., Modal Labs,
|
|
6
|
+
* Fly.io, or any Docker host).
|
|
7
|
+
*
|
|
8
|
+
* This adapter is the client-side counterpart to `middleware/server.ts`.
|
|
9
|
+
* It serializes requests, sends them over HTTP, and deserializes
|
|
10
|
+
* responses back into fp-ts TaskEither types.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const adapter = createRemoteWizardAdapter({
|
|
15
|
+
* baseUrl: process.env.MODAL_MIDDLEWARE_URL,
|
|
16
|
+
* authToken: process.env.MODAL_AUTH_TOKEN,
|
|
17
|
+
* });
|
|
18
|
+
* const kit = createSchedulingKit(adapter, [venmoAdapter]);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import { Effect } from 'effect';
|
|
22
|
+
import { Errors } from '../core/types.js';
|
|
23
|
+
const makeRequest = (config, path, method, body) => Effect.tryPromise({
|
|
24
|
+
try: async () => {
|
|
25
|
+
const url = `${config.baseUrl}${path}`;
|
|
26
|
+
const headers = {
|
|
27
|
+
'Content-Type': 'application/json',
|
|
28
|
+
};
|
|
29
|
+
if (config.authToken) {
|
|
30
|
+
headers['Authorization'] = `Bearer ${config.authToken}`;
|
|
31
|
+
}
|
|
32
|
+
const response = await fetch(url, {
|
|
33
|
+
method,
|
|
34
|
+
headers,
|
|
35
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
36
|
+
signal: AbortSignal.timeout(config.timeout ?? 60000),
|
|
37
|
+
});
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
40
|
+
throw Object.assign(new Error(errorBody.error?.message ?? `HTTP ${response.status}`), {
|
|
41
|
+
tag: errorBody.error?.tag ?? 'InfrastructureError',
|
|
42
|
+
code: errorBody.error?.code ?? 'NETWORK',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const json = (await response.json());
|
|
46
|
+
if (!json.success && json.error) {
|
|
47
|
+
throw Object.assign(new Error(json.error.message), {
|
|
48
|
+
tag: json.error.tag,
|
|
49
|
+
code: json.error.code,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return json.data;
|
|
53
|
+
},
|
|
54
|
+
catch: (e) => {
|
|
55
|
+
if (e instanceof Error && 'tag' in e) {
|
|
56
|
+
const tagged = e;
|
|
57
|
+
return mapRemoteError(tagged.tag, tagged.code, tagged.message);
|
|
58
|
+
}
|
|
59
|
+
if (e instanceof DOMException && e.name === 'TimeoutError') {
|
|
60
|
+
return Errors.infrastructure('TIMEOUT', 'Middleware server request timed out');
|
|
61
|
+
}
|
|
62
|
+
return Errors.infrastructure('NETWORK', `Middleware server error: ${e instanceof Error ? e.message : String(e)}`);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
const mapRemoteError = (tag, code, message) => {
|
|
66
|
+
switch (tag) {
|
|
67
|
+
case 'AcuityError':
|
|
68
|
+
return Errors.acuity(code, message);
|
|
69
|
+
case 'PaymentError':
|
|
70
|
+
return Errors.payment(code, message, 'remote');
|
|
71
|
+
case 'ValidationError':
|
|
72
|
+
return Errors.validation(code, message);
|
|
73
|
+
case 'ReservationError':
|
|
74
|
+
return Errors.reservation(code, message);
|
|
75
|
+
case 'InfrastructureError':
|
|
76
|
+
default:
|
|
77
|
+
return Errors.infrastructure(code ?? 'UNKNOWN', message);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// ADAPTER FACTORY
|
|
82
|
+
// =============================================================================
|
|
83
|
+
/**
|
|
84
|
+
* Create a SchedulingAdapter that proxies all operations to a remote
|
|
85
|
+
* middleware server via HTTP. The remote server runs Playwright + Chromium
|
|
86
|
+
* and executes the actual wizard automation.
|
|
87
|
+
*/
|
|
88
|
+
export const createRemoteWizardAdapter = (config) => ({
|
|
89
|
+
name: 'acuity-wizard-remote',
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Read operations - proxied to remote scraper
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
getServices: () => {
|
|
94
|
+
if (config.services) {
|
|
95
|
+
return Effect.succeed([...config.services]);
|
|
96
|
+
}
|
|
97
|
+
return makeRequest(config, '/services', 'GET');
|
|
98
|
+
},
|
|
99
|
+
getService: (serviceId) => {
|
|
100
|
+
if (config.services) {
|
|
101
|
+
const found = config.services.find((s) => s.id === serviceId);
|
|
102
|
+
return found
|
|
103
|
+
? Effect.succeed(found)
|
|
104
|
+
: Effect.fail(Errors.acuity('NOT_FOUND', `Service ${serviceId} not found`));
|
|
105
|
+
}
|
|
106
|
+
return makeRequest(config, `/services/${encodeURIComponent(serviceId)}`, 'GET');
|
|
107
|
+
},
|
|
108
|
+
getProviders: () => Effect.succeed([{
|
|
109
|
+
id: '1',
|
|
110
|
+
name: 'Default Provider',
|
|
111
|
+
email: 'provider@example.com',
|
|
112
|
+
description: 'Primary provider',
|
|
113
|
+
timezone: 'America/New_York',
|
|
114
|
+
}]),
|
|
115
|
+
getProvider: () => Effect.succeed({
|
|
116
|
+
id: '1',
|
|
117
|
+
name: 'Default Provider',
|
|
118
|
+
email: 'provider@example.com',
|
|
119
|
+
description: 'Primary provider',
|
|
120
|
+
timezone: 'America/New_York',
|
|
121
|
+
}),
|
|
122
|
+
getProvidersForService: () => Effect.succeed([{
|
|
123
|
+
id: '1',
|
|
124
|
+
name: 'Default Provider',
|
|
125
|
+
email: 'provider@example.com',
|
|
126
|
+
description: 'Primary provider',
|
|
127
|
+
timezone: 'America/New_York',
|
|
128
|
+
}]),
|
|
129
|
+
getAvailableDates: (params) => {
|
|
130
|
+
// Resolve service name for wizard navigation (Acuity navigates by name, not ID)
|
|
131
|
+
const serviceName = config.services?.find((s) => s.id === params.serviceId)?.name;
|
|
132
|
+
return makeRequest(config, '/availability/dates', 'POST', {
|
|
133
|
+
...params,
|
|
134
|
+
serviceName: serviceName ?? params.serviceId,
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
getAvailableSlots: (params) => {
|
|
138
|
+
const serviceName = config.services?.find((s) => s.id === params.serviceId)?.name;
|
|
139
|
+
return makeRequest(config, '/availability/slots', 'POST', {
|
|
140
|
+
...params,
|
|
141
|
+
serviceName: serviceName ?? params.serviceId,
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
checkSlotAvailability: (params) => {
|
|
145
|
+
const serviceName = config.services?.find((s) => s.id === params.serviceId)?.name;
|
|
146
|
+
return makeRequest(config, '/availability/check', 'POST', {
|
|
147
|
+
...params,
|
|
148
|
+
serviceName: serviceName ?? params.serviceId,
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Reservation - not supported (pipeline has graceful fallback)
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
createReservation: () => Effect.fail(Errors.reservation('BLOCK_FAILED', 'Reservations not supported by remote wizard adapter')),
|
|
155
|
+
releaseReservation: () => Effect.succeed(undefined),
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
// Write operations - proxied to remote wizard
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
createBooking: (request) => makeRequest(config, '/booking/create', 'POST', {
|
|
160
|
+
request,
|
|
161
|
+
couponCode: config.couponCode,
|
|
162
|
+
}),
|
|
163
|
+
createBookingWithPaymentRef: (request, paymentRef, paymentProcessor) => makeRequest(config, '/booking/create-with-payment', 'POST', {
|
|
164
|
+
request,
|
|
165
|
+
paymentRef,
|
|
166
|
+
paymentProcessor,
|
|
167
|
+
couponCode: config.couponCode,
|
|
168
|
+
}),
|
|
169
|
+
getBooking: () => Effect.fail(Errors.acuity('NOT_IMPLEMENTED', 'Get booking not yet supported via wizard')),
|
|
170
|
+
cancelBooking: () => Effect.fail(Errors.acuity('NOT_IMPLEMENTED', 'Cancel not yet supported via wizard')),
|
|
171
|
+
rescheduleBooking: () => Effect.fail(Errors.acuity('NOT_IMPLEMENTED', 'Reschedule not yet supported via wizard')),
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// Client - pass-through
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
findOrCreateClient: (client) => Effect.succeed({ id: `local-${client.email}`, isNew: true }),
|
|
176
|
+
getClientByEmail: () => Effect.succeed(null),
|
|
177
|
+
});
|
|
178
|
+
//# sourceMappingURL=remote-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-adapter.js","sourceRoot":"","sources":["../../src/middleware/remote-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selector Health Check
|
|
3
|
+
*
|
|
4
|
+
* Tiered probing of Acuity page selectors with degradation detection.
|
|
5
|
+
* "Degraded" means the primary selector (index 0) failed but a fallback
|
|
6
|
+
* (index > 0) still works — an early warning that Acuity changed their DOM.
|
|
7
|
+
*
|
|
8
|
+
* Tiers:
|
|
9
|
+
* depth=0: HTTP-only BUSINESS object check (~200ms, no browser)
|
|
10
|
+
* depth=1: Service page selectors (~3-5s, browser required)
|
|
11
|
+
* depth=2: + Calendar page selectors (~8-15s, clicks through wizard)
|
|
12
|
+
*/
|
|
13
|
+
import { Effect, Scope } from 'effect';
|
|
14
|
+
import { BrowserService } from './browser-service.js';
|
|
15
|
+
import { type SelectorKey } from './selectors.js';
|
|
16
|
+
export interface SelectorProbeResult {
|
|
17
|
+
readonly key: SelectorKey;
|
|
18
|
+
readonly status: 'passed' | 'degraded' | 'failed';
|
|
19
|
+
/** Which selector matched (null if failed) */
|
|
20
|
+
readonly matchedSelector: string | null;
|
|
21
|
+
/** Index in the candidates array (0 = primary, >0 = degraded) */
|
|
22
|
+
readonly matchedIndex: number | null;
|
|
23
|
+
/** Time to probe in ms */
|
|
24
|
+
readonly probeMs: number;
|
|
25
|
+
}
|
|
26
|
+
export interface SelectorHealthReport {
|
|
27
|
+
readonly status: 'healthy' | 'degraded' | 'unhealthy';
|
|
28
|
+
readonly selectors: SelectorProbeResult[];
|
|
29
|
+
readonly passed: number;
|
|
30
|
+
readonly degraded: number;
|
|
31
|
+
readonly failed: number;
|
|
32
|
+
readonly totalMs: number;
|
|
33
|
+
readonly pagesProbed: readonly string[];
|
|
34
|
+
readonly businessObjectAvailable: boolean;
|
|
35
|
+
readonly timestamp: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Run a selector health check at the specified depth.
|
|
39
|
+
*
|
|
40
|
+
* @param baseUrl - Acuity scheduling URL
|
|
41
|
+
* @param depth - 0 = HTTP-only, 1 = service page, 2 = service + calendar
|
|
42
|
+
*/
|
|
43
|
+
export declare const selectorHealthCheck: (baseUrl: string, depth?: 0 | 1 | 2) => Effect.Effect<SelectorHealthReport, never, BrowserService | Scope.Scope>;
|
|
44
|
+
//# sourceMappingURL=selector-health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector-health.d.ts","sourceRoot":"","sources":["../../src/middleware/selector-health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEvC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAO7D,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;IAClD,8CAA8C;IAC9C,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,iEAAiE;IACjE,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,0BAA0B;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;IACtD,QAAQ,CAAC,SAAS,EAAE,mBAAmB,EAAE,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AA8FD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAC/B,SAAS,MAAM,EACf,QAAO,CAAC,GAAG,CAAC,GAAG,CAAK,KAClB,MAAM,CAAC,MAAM,CAAC,oBAAoB,EAAE,KAAK,EAAE,cAAc,GAAG,KAAK,CAAC,KAAK,CA8DvE,CAAC"}
|