@v-office/website-sdk 1.0.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/README.md +263 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +337 -0
- package/dist/client-BbAfk-qa.mjs +33672 -0
- package/dist/custom-attribute-ChCbJKf_.mjs +54 -0
- package/dist/errors-2cuUGSvi.mjs +5 -0
- package/dist/index.d.mts +16130 -0
- package/dist/index.mjs +3 -0
- package/dist/operations-BanW36PK.mjs +12616 -0
- package/dist/operations-CHxEQ3jG.mjs +904 -0
- package/dist/parser-D6UAf8Ld.mjs +65 -0
- package/dist/quote-C3HZsFua.mjs +313 -0
- package/dist/quote-Vl6Nutaa.mjs +513 -0
- package/dist/rentals-BFmmp26v.mjs +323 -0
- package/dist/rentals-cQAg5TTW.mjs +403 -0
- package/dist/search-JqA3v_Fx.mjs +342 -0
- package/dist/search-filter-metadata-DZP0-Udc.mjs +4294 -0
- package/dist/to-rental-highlights-BcRa9Odv.mjs +402 -0
- package/dist/translations/shared/de-DE/availability.json +15 -0
- package/dist/translations/shared/de-DE/booking.json +15 -0
- package/dist/translations/shared/de-DE/insurance.json +18 -0
- package/dist/translations/shared/de-DE/quote.json +8 -0
- package/dist/translations/shared/de-DE/reviews.json +11 -0
- package/dist/translations/shared/en-US/availability.json +15 -0
- package/dist/translations/shared/en-US/booking.json +15 -0
- package/dist/translations/shared/en-US/insurance.json +18 -0
- package/dist/translations/shared/en-US/quote.json +8 -0
- package/dist/translations/shared/en-US/reviews.json +11 -0
- package/dist/translations/v10/de-DE/core.json +7027 -0
- package/dist/translations/v10/en-US/core.json +7027 -0
- package/dist/translations/v9/de-DE/core.json +4 -0
- package/dist/translations/v9/de-DE/filter.json +25 -0
- package/dist/translations/v9/de-DE/rental-attribute-categories.json +19 -0
- package/dist/translations/v9/de-DE/rental-attributes.json +451 -0
- package/dist/translations/v9/en-US/core.json +4 -0
- package/dist/translations/v9/en-US/filter.json +25 -0
- package/dist/translations/v9/en-US/rental-attribute-categories.json +19 -0
- package/dist/translations/v9/en-US/rental-attributes.json +451 -0
- package/package.json +90 -0
package/README.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# tsdown-starter
|
|
2
|
+
|
|
3
|
+
A starter for creating a TypeScript package.
|
|
4
|
+
|
|
5
|
+
## Development
|
|
6
|
+
|
|
7
|
+
- Install dependencies:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- Run the unit tests:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm run test
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- Build the library:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm run build
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- Playground
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pnpm run playground
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## CLI
|
|
32
|
+
|
|
33
|
+
The CLI exposes common SDK operations without editing `playground.ts` for each
|
|
34
|
+
manual test. In local development, use `pnpm cli -- ...`; when installed as a
|
|
35
|
+
package binary, use `cms-sdk ...`.
|
|
36
|
+
|
|
37
|
+
The CLI reads the same environment variables as `playground.ts`. For v9:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
HUB_GRAPHQL_URL
|
|
41
|
+
HUB_V1_API_BASE_URL
|
|
42
|
+
HUB_V0_API_BASE_URL
|
|
43
|
+
HUB_API_KEY
|
|
44
|
+
IMAGE_PROXY_BASE_URL
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For v10:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
VOFFICE_API_ENDPOINT
|
|
51
|
+
VOFFICE_LOCAL_DEV_ACCESS_TOKEN
|
|
52
|
+
VOFFICE_IMAGE_BASE_URL
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
You can inspect the required environment variables and resolved config:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm cli --backend v9 config env
|
|
59
|
+
pnpm cli --backend v10 config show --output pretty
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
You can also pass a complete parsed config or shared SDK options file:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pnpm cli --backend v9 --config ./cms-config.v9.json config show
|
|
66
|
+
pnpm cli --backend v10 --options ./cms-options.json filters --locale de-DE
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Static Services
|
|
70
|
+
|
|
71
|
+
Fetch rentals:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pnpm cli --backend v9 rentals --locale de-DE
|
|
75
|
+
pnpm cli --backend v10 rentals --locale de-DE
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Fetch filters:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pnpm cli --backend v9 filters --locale de-DE
|
|
82
|
+
pnpm cli --backend v10 filters --locale de-DE
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Search Service
|
|
86
|
+
|
|
87
|
+
Run the same query shape used in `playground.ts`:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pnpm cli --backend v9 search \
|
|
91
|
+
--locale de-DE \
|
|
92
|
+
--limit 2 \
|
|
93
|
+
--query "start=20-06-2026&end=27-06-2026&adults=2&childrenAges=5%2C13&babies=1&pets=1&wifi&bedrooms=6&bbq=true&youthgroups=on_request&beachdistance=200&scope=entire_home%2Cprivate_room&reet&complexFilter"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Use the returned `pageInfo.nextCursor` for the next page:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pnpm cli --backend v10 search \
|
|
100
|
+
--locale de-DE \
|
|
101
|
+
--limit 2 \
|
|
102
|
+
--cursor "<nextCursor>" \
|
|
103
|
+
--query "start=20-06-2026&end=27-06-2026&adults=2&childrenAges=5%2C13&babies=1&pets=1"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Availability Service
|
|
107
|
+
|
|
108
|
+
Fetch the initial availability calendar:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pnpm cli --backend v9 availability initial \
|
|
112
|
+
--locale de-DE \
|
|
113
|
+
--rental-id 134990 \
|
|
114
|
+
--calculate-from-date 2026-05-01 \
|
|
115
|
+
--calculate-through-date 2026-08-31
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Fetch availability after selecting a start date:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
pnpm cli --backend v9 availability start-date-selected \
|
|
122
|
+
--locale de-DE \
|
|
123
|
+
--rental-id 134990 \
|
|
124
|
+
--selected-start-date 2026-06-17 \
|
|
125
|
+
--calculate-from-date 2026-05-01 \
|
|
126
|
+
--calculate-through-date 2026-08-31
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The v10 playground rental can be used in the same commands:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pnpm cli --backend v10 availability initial \
|
|
133
|
+
--locale de-DE \
|
|
134
|
+
--rental-id GMB2KczZnEXkdcF2LscQFX \
|
|
135
|
+
--calculate-from-date 2026-07-08 \
|
|
136
|
+
--calculate-through-date 2026-07-11
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Quote Service
|
|
140
|
+
|
|
141
|
+
Create a JSON fixture for a quote input, for example:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"locale": "de-DE",
|
|
146
|
+
"rentalId": "159521",
|
|
147
|
+
"period": {
|
|
148
|
+
"start": "2026-07-08",
|
|
149
|
+
"end": "2026-07-11"
|
|
150
|
+
},
|
|
151
|
+
"occupancy": {
|
|
152
|
+
"adults": 1,
|
|
153
|
+
"children": 0,
|
|
154
|
+
"babies": 0,
|
|
155
|
+
"pets": 0
|
|
156
|
+
},
|
|
157
|
+
"destinationCountryCode": "DE"
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Then run:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
pnpm cli --backend v9 quote --input ./quote-input.v9.json
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
For the v10 playground rental with the voucher:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"locale": "de-DE",
|
|
172
|
+
"rentalId": "GMB2KczZnEXkdcF2LscQFX",
|
|
173
|
+
"period": {
|
|
174
|
+
"start": "2026-07-08",
|
|
175
|
+
"end": "2026-07-11"
|
|
176
|
+
},
|
|
177
|
+
"occupancy": {
|
|
178
|
+
"adults": 1,
|
|
179
|
+
"children": 0,
|
|
180
|
+
"babies": 0,
|
|
181
|
+
"pets": 0
|
|
182
|
+
},
|
|
183
|
+
"destinationCountryCode": "DE",
|
|
184
|
+
"voucher": "OFFER11"
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
pnpm cli --backend v10 quote --input ./quote-input.v10.json
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Contact Service
|
|
193
|
+
|
|
194
|
+
Contact submission is guarded by `--yes` because it sends data to the backend.
|
|
195
|
+
Create a JSON fixture like the playground input:
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"title": "Herr",
|
|
200
|
+
"professionalTitle": "Dr.",
|
|
201
|
+
"forename": "Max",
|
|
202
|
+
"surname": "Mustermann",
|
|
203
|
+
"address": {
|
|
204
|
+
"streetAndHousenumber": "Musterstraße 42",
|
|
205
|
+
"postalCode": "11111",
|
|
206
|
+
"city": "Musterstadt",
|
|
207
|
+
"countryCode": "DE"
|
|
208
|
+
},
|
|
209
|
+
"email": "max.mustermann@muster.de",
|
|
210
|
+
"phone": "+49123456789",
|
|
211
|
+
"message": "Muster-Hinweis\nMuster"
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Then submit it explicitly:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
pnpm cli --backend v9 contact submit --input ./contact-input.json --yes
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Booking Service
|
|
222
|
+
|
|
223
|
+
Booking and quote-selection flows still need code for now because they operate
|
|
224
|
+
on a live `GuestQuote` class instance returned by the quote service. Keep using
|
|
225
|
+
`playground.ts` for booking, additional service selection, cancellation policy
|
|
226
|
+
selection, insurance selection, insurance pre-contracts, insurance payment
|
|
227
|
+
selection, and insurance booking until those flows have a stable JSON
|
|
228
|
+
serialization contract.
|
|
229
|
+
|
|
230
|
+
## TODO
|
|
231
|
+
|
|
232
|
+
### My-Stuff
|
|
233
|
+
|
|
234
|
+
- Payment (needs to be tested)
|
|
235
|
+
|
|
236
|
+
- Zahlung vor Ort (v9)
|
|
237
|
+
|
|
238
|
+
- Insurances
|
|
239
|
+
|
|
240
|
+
- Contact-Form (v10 -> Ticket)
|
|
241
|
+
- Contact-Form (v9 -> sendMessage)
|
|
242
|
+
|
|
243
|
+
- Auto-Regions (v10)
|
|
244
|
+
|
|
245
|
+
### Waiting
|
|
246
|
+
|
|
247
|
+
#### Custom Attributes
|
|
248
|
+
|
|
249
|
+
- Suche mit Filter nach Custom-Attribute (boolean)
|
|
250
|
+
- v10 nicht implementiert - wird bald
|
|
251
|
+
- v9 müsste über Hub Filter-Schema gemacht werden
|
|
252
|
+
- unified surface
|
|
253
|
+
|
|
254
|
+
### Optional
|
|
255
|
+
|
|
256
|
+
#### Filter-Counts
|
|
257
|
+
|
|
258
|
+
- In v10 müsste man ganze Suche triggern - nicht gut
|
|
259
|
+
- In v9 müsste über Hub Filter-Schema gemacht werden
|
|
260
|
+
|
|
261
|
+
### getRentals
|
|
262
|
+
|
|
263
|
+
- highlightsPrioritization does not correctly work with e.g. maxPersons
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { n as defineCMSSDKOptions, t as createCMSSDKFromParsedConfig } from "./client-BbAfk-qa.mjs";
|
|
3
|
+
import { Console, Effect, FileSystem, Layer, Option, Path, Stdio, Terminal } from "effect";
|
|
4
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
5
|
+
import { inspect } from "node:util";
|
|
6
|
+
import { ChildProcessSpawner } from "effect/unstable/process";
|
|
7
|
+
import { access } from "node:fs/promises";
|
|
8
|
+
//#region src/cli/output.ts
|
|
9
|
+
const toJson = (value) => JSON.stringify(value, (_key, item) => typeof item === "bigint" ? item.toString() : item, 2);
|
|
10
|
+
const printOutput = (root, value) => {
|
|
11
|
+
switch (root.output) {
|
|
12
|
+
case "json": return Console.log(toJson(value));
|
|
13
|
+
case "pretty": return Console.log(inspect(value, {
|
|
14
|
+
depth: null,
|
|
15
|
+
colors: process.stdout.isTTY
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const printSuccess = (root) => printOutput(root, { ok: true });
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/cli/root.ts
|
|
22
|
+
const backendFlag = Flag.choice("backend", ["v9", "v10"]).pipe(Flag.withDescription("Backend to use for this command."));
|
|
23
|
+
const localeFlag = Flag.choice("locale", ["de-DE", "en-US"]).pipe(Flag.withDescription("Locale used for localized SDK calls."), Flag.withDefault("de-DE"));
|
|
24
|
+
const inputFileFlag = Flag.file("input", { mustExist: true }).pipe(Flag.withDescription("Path to a JSON input file."));
|
|
25
|
+
const confirmFlag = Flag.boolean("yes").pipe(Flag.withDescription("Confirm execution of commands that submit or mutate backend state."));
|
|
26
|
+
const cmsSdkCommand = Command.make("cms-sdk").pipe(Command.withDescription("Run @v-office/cms-sdk operations from the command line."), Command.withSharedFlags({
|
|
27
|
+
backend: backendFlag,
|
|
28
|
+
config: Flag.file("config", { mustExist: true }).pipe(Flag.optional, Flag.withDescription("Path to a JSON ParsedCMSConfig file. Defaults to environment variables.")),
|
|
29
|
+
options: Flag.file("options", { mustExist: true }).pipe(Flag.optional, Flag.withDescription("Path to a JSON CMSOptions file passed through defineCMSSDKOptions.")),
|
|
30
|
+
output: Flag.choice("output", ["json", "pretty"]).pipe(Flag.withDescription("Output format."), Flag.withDefault("json"))
|
|
31
|
+
}));
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/cli/error.ts
|
|
34
|
+
var CLICheckFailed = class extends Error {
|
|
35
|
+
name = "CLICheckFailed";
|
|
36
|
+
constructor(message, options) {
|
|
37
|
+
super(message, options);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const toCLIError = (message, cause) => cause instanceof Error ? new CLICheckFailed(`${message}: ${cause.message}`, { cause }) : new CLICheckFailed(message, { cause });
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/cli/json.ts
|
|
43
|
+
const parseJson = (text, source) => Effect.try({
|
|
44
|
+
try: () => JSON.parse(text),
|
|
45
|
+
catch: (cause) => toCLIError(`Failed to parse JSON from ${source}`, cause)
|
|
46
|
+
});
|
|
47
|
+
const readJsonFile = (path, label) => Effect.tryPromise({
|
|
48
|
+
try: async () => {
|
|
49
|
+
const { readFile } = await import("node:fs/promises");
|
|
50
|
+
return await readFile(path, "utf8");
|
|
51
|
+
},
|
|
52
|
+
catch: (cause) => toCLIError(`Failed to read ${label} file at ${path}`, cause)
|
|
53
|
+
}).pipe(Effect.flatMap((text) => parseJson(text, path)));
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/cli/config.ts
|
|
56
|
+
const getRequiredEnv = (name) => Effect.sync(() => process.env[name]).pipe(Effect.flatMap((value) => value === void 0 || value.length === 0 ? Effect.fail(new CLICheckFailed(`Missing required environment variable ${name}.`)) : Effect.succeed(value)));
|
|
57
|
+
const readOptions = (root, fallback) => Option.isSome(root.options) ? readJsonFile(root.options.value, "CMS options").pipe(Effect.map(defineCMSSDKOptions)) : Effect.succeed(fallback);
|
|
58
|
+
const withOptions = (config, options) => {
|
|
59
|
+
if (options === void 0) return config;
|
|
60
|
+
switch (config.backend) {
|
|
61
|
+
case "v9": return {
|
|
62
|
+
...config,
|
|
63
|
+
options
|
|
64
|
+
};
|
|
65
|
+
case "v10": return {
|
|
66
|
+
...config,
|
|
67
|
+
options
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const fromConfigFile = (root) => Option.isSome(root.config) ? readJsonFile(root.config.value, "CMS config").pipe(Effect.flatMap((config) => config.backend !== root.backend ? Effect.fail(new CLICheckFailed(`Config file backend "${config.backend}" does not match --backend ${root.backend}.`)) : readOptions(root, config.options).pipe(Effect.map((options) => ({
|
|
72
|
+
config: withOptions(config, options),
|
|
73
|
+
options
|
|
74
|
+
}))))) : fromEnvironment(root);
|
|
75
|
+
const fromEnvironment = (root) => Effect.gen(function* () {
|
|
76
|
+
const options = yield* readOptions(root, void 0);
|
|
77
|
+
if (root.backend === "v9") {
|
|
78
|
+
const v0ApiBaseUrl = process.env.HUB_V0_API_BASE_URL;
|
|
79
|
+
return {
|
|
80
|
+
config: {
|
|
81
|
+
backend: "v9",
|
|
82
|
+
v9: {
|
|
83
|
+
graphqlUrl: yield* getRequiredEnv("HUB_GRAPHQL_URL"),
|
|
84
|
+
v1ApiBaseUrl: yield* getRequiredEnv("HUB_V1_API_BASE_URL"),
|
|
85
|
+
...v0ApiBaseUrl === void 0 || v0ApiBaseUrl.length === 0 ? {} : { v0ApiBaseUrl },
|
|
86
|
+
apiKey: yield* getRequiredEnv("HUB_API_KEY"),
|
|
87
|
+
imageProxyBaseUrl: yield* getRequiredEnv("IMAGE_PROXY_BASE_URL")
|
|
88
|
+
},
|
|
89
|
+
...options === void 0 ? {} : { options }
|
|
90
|
+
},
|
|
91
|
+
options
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
config: {
|
|
96
|
+
backend: "v10",
|
|
97
|
+
v10: {
|
|
98
|
+
apiEndpoint: yield* getRequiredEnv("VOFFICE_API_ENDPOINT"),
|
|
99
|
+
accessToken: yield* getRequiredEnv("VOFFICE_LOCAL_DEV_ACCESS_TOKEN"),
|
|
100
|
+
imageBaseUrl: yield* getRequiredEnv("VOFFICE_IMAGE_BASE_URL")
|
|
101
|
+
},
|
|
102
|
+
...options === void 0 ? {} : { options }
|
|
103
|
+
},
|
|
104
|
+
options
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
const redactValue = (value) => {
|
|
108
|
+
if (value === void 0 || value.length === 0) return value;
|
|
109
|
+
return value.length <= 4 ? "****" : `${value.slice(0, 2)}****${value.slice(-2)}`;
|
|
110
|
+
};
|
|
111
|
+
const redactConfig = (config) => {
|
|
112
|
+
switch (config.backend) {
|
|
113
|
+
case "v9": return {
|
|
114
|
+
...config,
|
|
115
|
+
v9: {
|
|
116
|
+
...config.v9,
|
|
117
|
+
apiKey: redactValue(config.v9.apiKey) ?? ""
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
case "v10": return {
|
|
121
|
+
...config,
|
|
122
|
+
v10: {
|
|
123
|
+
...config.v10,
|
|
124
|
+
accessToken: redactValue(config.v10.accessToken) ?? ""
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const loadCLIConfig = fromConfigFile;
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/cli/sdk.ts
|
|
132
|
+
const withSDK = (root, run) => Effect.gen(function* () {
|
|
133
|
+
const { config } = yield* loadCLIConfig(root);
|
|
134
|
+
return yield* Effect.tryPromise({
|
|
135
|
+
try: async () => {
|
|
136
|
+
const sdk = createCMSSDKFromParsedConfig(config);
|
|
137
|
+
try {
|
|
138
|
+
return await run(sdk);
|
|
139
|
+
} finally {
|
|
140
|
+
await sdk.dispose();
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
catch: (cause) => toCLIError("SDK operation failed", cause)
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/cli/commands/availability.ts
|
|
148
|
+
const rentalIdFlag = Flag.string("rental-id").pipe(Flag.withDescription("Backend rental identifier."));
|
|
149
|
+
const calculateFromDateFlag = Flag.string("calculate-from-date").pipe(Flag.withDescription("First local date to calculate, formatted as YYYY-MM-DD."));
|
|
150
|
+
const calculateThroughDateFlag = Flag.string("calculate-through-date").pipe(Flag.withDescription("Last local date to calculate, formatted as YYYY-MM-DD."));
|
|
151
|
+
const initial = Command.make("initial", {
|
|
152
|
+
locale: localeFlag,
|
|
153
|
+
rentalId: rentalIdFlag,
|
|
154
|
+
calculateFromDate: calculateFromDateFlag,
|
|
155
|
+
calculateThroughDate: calculateThroughDateFlag
|
|
156
|
+
}, (input) => cmsSdkCommand.pipe(Effect.flatMap((root) => withSDK(root, (sdk) => sdk.live.availability.getInitialAvailability(input)).pipe(Effect.flatMap((result) => printOutput(root, result)))))).pipe(Command.withDescription("Fetch initial availability for a rental."), Command.withExamples([{
|
|
157
|
+
command: "cms-sdk --backend v10 availability initial --rental-id 123 --calculate-from-date 2026-07-01 --calculate-through-date 2026-08-01",
|
|
158
|
+
description: "Fetch an initial availability calendar."
|
|
159
|
+
}]));
|
|
160
|
+
const startDateSelected = Command.make("start-date-selected", {
|
|
161
|
+
locale: localeFlag,
|
|
162
|
+
rentalId: rentalIdFlag,
|
|
163
|
+
selectedStartDate: Flag.string("selected-start-date").pipe(Flag.withDescription("Selected start date, formatted as YYYY-MM-DD.")),
|
|
164
|
+
calculateFromDate: calculateFromDateFlag,
|
|
165
|
+
calculateThroughDate: calculateThroughDateFlag
|
|
166
|
+
}, (input) => cmsSdkCommand.pipe(Effect.flatMap((root) => withSDK(root, (sdk) => sdk.live.availability.getStartDateSelectedAvailability(input)).pipe(Effect.flatMap((result) => printOutput(root, result)))))).pipe(Command.withDescription("Fetch availability after selecting a start date."), Command.withExamples([{
|
|
167
|
+
command: "cms-sdk --backend v9 availability start-date-selected --rental-id 123 --selected-start-date 2026-07-08 --calculate-from-date 2026-07-01 --calculate-through-date 2026-08-01",
|
|
168
|
+
description: "Fetch available end dates for a selected start date."
|
|
169
|
+
}]));
|
|
170
|
+
const availabilityCommand = Command.make("availability").pipe(Command.withDescription("Run live availability operations."), Command.withSubcommands([initial, startDateSelected]));
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/cli/commands/config.ts
|
|
173
|
+
const show = Command.make("show", {}, Effect.fn(function* () {
|
|
174
|
+
const root = yield* cmsSdkCommand;
|
|
175
|
+
const resolved = yield* loadCLIConfig(root);
|
|
176
|
+
yield* printOutput(root, {
|
|
177
|
+
...resolved,
|
|
178
|
+
config: redactConfig(resolved.config)
|
|
179
|
+
});
|
|
180
|
+
})).pipe(Command.withDescription("Show the resolved CLI config with secrets redacted."));
|
|
181
|
+
const env = Command.make("env", {}, Effect.fn(function* () {
|
|
182
|
+
const root = yield* cmsSdkCommand;
|
|
183
|
+
yield* printOutput(root, {
|
|
184
|
+
backend: root.backend,
|
|
185
|
+
required: root.backend === "v9" ? [
|
|
186
|
+
"HUB_GRAPHQL_URL",
|
|
187
|
+
"HUB_V1_API_BASE_URL",
|
|
188
|
+
"HUB_API_KEY",
|
|
189
|
+
"IMAGE_PROXY_BASE_URL"
|
|
190
|
+
] : [
|
|
191
|
+
"VOFFICE_API_ENDPOINT",
|
|
192
|
+
"VOFFICE_LOCAL_DEV_ACCESS_TOKEN",
|
|
193
|
+
"VOFFICE_IMAGE_BASE_URL"
|
|
194
|
+
],
|
|
195
|
+
optional: root.backend === "v9" ? ["HUB_V0_API_BASE_URL"] : []
|
|
196
|
+
});
|
|
197
|
+
})).pipe(Command.withDescription("List environment variables used by the selected backend."));
|
|
198
|
+
const configCommand = Command.make("config").pipe(Command.withDescription("Inspect CLI configuration."), Command.withSubcommands([show, env]));
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/cli/commands/shared.ts
|
|
201
|
+
const requireConfirmation = (root, confirmed, commandName) => confirmed ? Effect.void : Effect.fail(/* @__PURE__ */ new Error(`${commandName} can submit data to the configured backend. Re-run with --yes to confirm.`));
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/cli/commands/live.ts
|
|
204
|
+
const quote = Command.make("quote", { input: inputFileFlag }, ({ input }) => cmsSdkCommand.pipe(Effect.flatMap((root) => readJsonFile(input, "quote input").pipe(Effect.flatMap((quoteInput) => withSDK(root, (sdk) => sdk.live.quote.quote(quoteInput))), Effect.flatMap((result) => printOutput(root, result)))))).pipe(Command.withDescription("Create a live guest quote from a JSON GuestQuoteInput file."), Command.withExamples([{
|
|
205
|
+
command: "cms-sdk --backend v10 quote --input ./quote-input.json",
|
|
206
|
+
description: "Create a quote using a reusable JSON fixture."
|
|
207
|
+
}]));
|
|
208
|
+
const contactSubmit = Command.make("submit", {
|
|
209
|
+
input: inputFileFlag,
|
|
210
|
+
yes: confirmFlag
|
|
211
|
+
}, ({ input, yes }) => cmsSdkCommand.pipe(Effect.flatMap((root) => Effect.gen(function* () {
|
|
212
|
+
yield* requireConfirmation(root, yes, "contact submit");
|
|
213
|
+
const contactInput = yield* readJsonFile(input, "contact input");
|
|
214
|
+
yield* withSDK(root, (sdk) => sdk.live.contact.submit(contactInput));
|
|
215
|
+
yield* printSuccess(root);
|
|
216
|
+
})))).pipe(Command.withDescription("Submit a contact request from a JSON ContactInput file."), Command.withExamples([{
|
|
217
|
+
command: "cms-sdk --backend v9 contact submit --input ./contact-input.json --yes",
|
|
218
|
+
description: "Submit a contact request after explicit confirmation."
|
|
219
|
+
}]));
|
|
220
|
+
const liveCommands = [quote, Command.make("contact").pipe(Command.withDescription("Run live contact operations."), Command.withSubcommands([contactSubmit]))];
|
|
221
|
+
const staticCommands = [
|
|
222
|
+
Command.make("filters", { locale: localeFlag }, ({ locale }) => cmsSdkCommand.pipe(Effect.flatMap((root) => withSDK(root, (sdk) => sdk.static.filter.getFilters({ locale })).pipe(Effect.flatMap((result) => printOutput(root, result)))))).pipe(Command.withDescription("Fetch unified filter definitions."), Command.withExamples([{
|
|
223
|
+
command: "cms-sdk --backend v10 filters --locale de-DE",
|
|
224
|
+
description: "Fetch v10 filter definitions as JSON."
|
|
225
|
+
}])),
|
|
226
|
+
Command.make("rentals", { locale: localeFlag }, ({ locale }) => cmsSdkCommand.pipe(Effect.flatMap((root) => withSDK(root, (sdk) => sdk.static.rentals.getRentals({ locale })).pipe(Effect.flatMap((result) => printOutput(root, result)))))).pipe(Command.withDescription("Fetch rental summaries."), Command.withExamples([{
|
|
227
|
+
command: "cms-sdk --backend v9 rentals --locale en-US",
|
|
228
|
+
description: "Fetch v9 rentals in English."
|
|
229
|
+
}])),
|
|
230
|
+
Command.make("search", {
|
|
231
|
+
locale: localeFlag,
|
|
232
|
+
query: Flag.string("query").pipe(Flag.withDescription("Search query string, for example \"city=munich&adults=2\".")),
|
|
233
|
+
limit: Flag.integer("limit").pipe(Flag.optional, Flag.withDescription("Optional positive result limit.")),
|
|
234
|
+
cursor: Flag.string("cursor").pipe(Flag.optional, Flag.withDescription("Optional pagination cursor.")),
|
|
235
|
+
rentalIdsIn: Flag.string("rental-ids-in").pipe(Flag.optional, Flag.withDescription("Optional comma-separated rental IDs to restrict v10 search results."))
|
|
236
|
+
}, ({ locale, query, limit, cursor, rentalIdsIn }) => cmsSdkCommand.pipe(Effect.flatMap((root) => {
|
|
237
|
+
const parsedRentalIdsIn = Option.isSome(rentalIdsIn) ? rentalIdsIn.value.split(",").map((rentalId) => rentalId.trim()).filter((rentalId) => rentalId.length > 0) : [];
|
|
238
|
+
const input = {
|
|
239
|
+
locale,
|
|
240
|
+
query,
|
|
241
|
+
...parsedRentalIdsIn.length > 0 ? { rentalIdsIn: parsedRentalIdsIn } : {},
|
|
242
|
+
...Option.isSome(limit) ? { limit: limit.value } : {},
|
|
243
|
+
...Option.isSome(cursor) ? { cursor: cursor.value } : {}
|
|
244
|
+
};
|
|
245
|
+
return withSDK(root, (sdk) => sdk.live.search.search(input)).pipe(Effect.flatMap((result) => printOutput(root, result)));
|
|
246
|
+
}))).pipe(Command.withDescription("Run a live rental search."), Command.withExamples([{
|
|
247
|
+
command: "cms-sdk --backend v10 search --locale de-DE --query \"city=munich&adults=2\"",
|
|
248
|
+
description: "Search v10 rentals with a query string."
|
|
249
|
+
}]))
|
|
250
|
+
];
|
|
251
|
+
//#endregion
|
|
252
|
+
//#region src/cli/index.ts
|
|
253
|
+
const cliCommand = cmsSdkCommand.pipe(Command.withSubcommands([
|
|
254
|
+
configCommand,
|
|
255
|
+
...staticCommands,
|
|
256
|
+
availabilityCommand,
|
|
257
|
+
...liveCommands
|
|
258
|
+
]));
|
|
259
|
+
const runCLI = Command.run(cliCommand, { version: "1.0.0" });
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region src/cli/node-services.ts
|
|
262
|
+
const unsupported = (operation) => Effect.die(`Unsupported CLI platform operation: ${operation}`);
|
|
263
|
+
const FileSystemLive = Layer.succeed(FileSystem.FileSystem, {
|
|
264
|
+
access: (path) => Effect.tryPromise({
|
|
265
|
+
try: async () => {
|
|
266
|
+
await access(path);
|
|
267
|
+
},
|
|
268
|
+
catch: (cause) => cause
|
|
269
|
+
}),
|
|
270
|
+
exists: (path) => Effect.promise(async () => {
|
|
271
|
+
try {
|
|
272
|
+
await access(path);
|
|
273
|
+
return true;
|
|
274
|
+
} catch {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}),
|
|
278
|
+
readFileString: (path, encoding = "utf8") => Effect.tryPromise({
|
|
279
|
+
try: async () => {
|
|
280
|
+
const { readFile } = await import("node:fs/promises");
|
|
281
|
+
return await readFile(path, encoding);
|
|
282
|
+
},
|
|
283
|
+
catch: (cause) => cause
|
|
284
|
+
}),
|
|
285
|
+
copy: () => unsupported("FileSystem.copy"),
|
|
286
|
+
copyFile: () => unsupported("FileSystem.copyFile"),
|
|
287
|
+
chmod: () => unsupported("FileSystem.chmod"),
|
|
288
|
+
chown: () => unsupported("FileSystem.chown"),
|
|
289
|
+
link: () => unsupported("FileSystem.link"),
|
|
290
|
+
makeDirectory: () => unsupported("FileSystem.makeDirectory"),
|
|
291
|
+
makeTempDirectory: () => unsupported("FileSystem.makeTempDirectory"),
|
|
292
|
+
makeTempDirectoryScoped: () => unsupported("FileSystem.makeTempDirectoryScoped"),
|
|
293
|
+
makeTempFile: () => unsupported("FileSystem.makeTempFile"),
|
|
294
|
+
makeTempFileScoped: () => unsupported("FileSystem.makeTempFileScoped"),
|
|
295
|
+
open: () => unsupported("FileSystem.open"),
|
|
296
|
+
readDirectory: () => unsupported("FileSystem.readDirectory"),
|
|
297
|
+
readFile: () => unsupported("FileSystem.readFile"),
|
|
298
|
+
readLink: () => unsupported("FileSystem.readLink"),
|
|
299
|
+
realPath: () => unsupported("FileSystem.realPath"),
|
|
300
|
+
remove: () => unsupported("FileSystem.remove"),
|
|
301
|
+
rename: () => unsupported("FileSystem.rename"),
|
|
302
|
+
sink: () => unsupported("FileSystem.sink"),
|
|
303
|
+
stat: () => unsupported("FileSystem.stat"),
|
|
304
|
+
stream: () => unsupported("FileSystem.stream"),
|
|
305
|
+
symlink: () => unsupported("FileSystem.symlink"),
|
|
306
|
+
truncate: () => unsupported("FileSystem.truncate"),
|
|
307
|
+
updateFile: () => unsupported("FileSystem.updateFile"),
|
|
308
|
+
watch: () => unsupported("FileSystem.watch"),
|
|
309
|
+
writeFile: () => unsupported("FileSystem.writeFile"),
|
|
310
|
+
writeFileString: () => unsupported("FileSystem.writeFileString")
|
|
311
|
+
});
|
|
312
|
+
const terminal = Terminal.make({
|
|
313
|
+
columns: Effect.sync(() => process.stdout.columns ?? 80),
|
|
314
|
+
rows: Effect.sync(() => process.stdout.rows ?? 24),
|
|
315
|
+
readInput: unsupported("Terminal.readInput"),
|
|
316
|
+
readLine: unsupported("Terminal.readLine"),
|
|
317
|
+
display: (text) => Effect.tryPromise({
|
|
318
|
+
try: async () => {
|
|
319
|
+
await new Promise((resolve, reject) => {
|
|
320
|
+
process.stdout.write(text, (error) => error === void 0 ? resolve() : reject(error));
|
|
321
|
+
});
|
|
322
|
+
},
|
|
323
|
+
catch: (cause) => cause
|
|
324
|
+
})
|
|
325
|
+
});
|
|
326
|
+
const TerminalLive = Layer.succeed(Terminal.Terminal, terminal);
|
|
327
|
+
const StdioLive = Stdio.layerTest({ args: Effect.sync(() => process.argv.slice(2)) });
|
|
328
|
+
const ChildProcessSpawnerLive = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => unsupported("ChildProcessSpawner.spawn")));
|
|
329
|
+
const NodeCLIServicesLive = Layer.mergeAll(FileSystemLive, Path.layer, TerminalLive, StdioLive, ChildProcessSpawnerLive);
|
|
330
|
+
//#endregion
|
|
331
|
+
//#region src/cli/main.ts
|
|
332
|
+
Effect.runPromise(runCLI.pipe(Effect.provide(NodeCLIServicesLive))).catch((cause) => {
|
|
333
|
+
console.error(cause);
|
|
334
|
+
process.exitCode = 1;
|
|
335
|
+
});
|
|
336
|
+
//#endregion
|
|
337
|
+
export {};
|