@voyantjs/products 0.52.1 → 0.52.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/action-ledger-drift.d.ts +29 -0
  2. package/dist/action-ledger-drift.d.ts.map +1 -0
  3. package/dist/action-ledger-drift.js +335 -0
  4. package/dist/action-ledger.d.ts +104 -0
  5. package/dist/action-ledger.d.ts.map +1 -0
  6. package/dist/action-ledger.js +100 -0
  7. package/dist/booking-extension.d.ts +3 -3
  8. package/dist/catalog-policy.d.ts.map +1 -1
  9. package/dist/catalog-policy.js +18 -0
  10. package/dist/content-shape.d.ts +2 -0
  11. package/dist/content-shape.d.ts.map +1 -1
  12. package/dist/content-shape.js +2 -0
  13. package/dist/events.d.ts +1 -1
  14. package/dist/events.d.ts.map +1 -1
  15. package/dist/route-env.d.ts +22 -0
  16. package/dist/route-env.d.ts.map +1 -0
  17. package/dist/route-env.js +1 -0
  18. package/dist/routes-associations.d.ts +164 -0
  19. package/dist/routes-associations.d.ts.map +1 -0
  20. package/dist/routes-associations.js +100 -0
  21. package/dist/routes-catalog.d.ts +436 -0
  22. package/dist/routes-catalog.d.ts.map +1 -0
  23. package/dist/routes-catalog.js +104 -0
  24. package/dist/routes-configuration.d.ts +773 -0
  25. package/dist/routes-configuration.d.ts.map +1 -0
  26. package/dist/routes-configuration.js +364 -0
  27. package/dist/routes-core.d.ts +302 -0
  28. package/dist/routes-core.d.ts.map +1 -0
  29. package/dist/routes-core.js +79 -0
  30. package/dist/routes-itinerary.d.ts +614 -0
  31. package/dist/routes-itinerary.d.ts.map +1 -0
  32. package/dist/routes-itinerary.js +309 -0
  33. package/dist/routes-maintenance.d.ts +32 -0
  34. package/dist/routes-maintenance.d.ts.map +1 -0
  35. package/dist/routes-maintenance.js +14 -0
  36. package/dist/routes-media.d.ts +634 -0
  37. package/dist/routes-media.d.ts.map +1 -0
  38. package/dist/routes-media.js +245 -0
  39. package/dist/routes-merchandising.d.ts +1108 -0
  40. package/dist/routes-merchandising.d.ts.map +1 -0
  41. package/dist/routes-merchandising.js +376 -0
  42. package/dist/routes-options.d.ts +363 -0
  43. package/dist/routes-options.d.ts.map +1 -0
  44. package/dist/routes-options.js +173 -0
  45. package/dist/routes-public.d.ts +4 -4
  46. package/dist/routes-translations.d.ts +477 -0
  47. package/dist/routes-translations.d.ts.map +1 -0
  48. package/dist/routes-translations.js +258 -0
  49. package/dist/routes.d.ts +417 -355
  50. package/dist/routes.d.ts.map +1 -1
  51. package/dist/routes.js +21 -1133
  52. package/dist/schema-core.d.ts +3 -3
  53. package/dist/schema-itinerary.d.ts +1 -1
  54. package/dist/schema-settings.d.ts +4 -4
  55. package/dist/service-catalog-plane.d.ts.map +1 -1
  56. package/dist/service-catalog-plane.js +48 -1
  57. package/dist/service-catalog.d.ts +2 -2
  58. package/dist/service-content-owned.d.ts.map +1 -1
  59. package/dist/service-content-owned.js +98 -4
  60. package/dist/service-public.d.ts +4 -4
  61. package/dist/service.d.ts +225 -97
  62. package/dist/service.d.ts.map +1 -1
  63. package/dist/service.js +91 -0
  64. package/dist/tasks/brochures.d.ts +1 -1
  65. package/dist/validation-catalog.d.ts +10 -10
  66. package/dist/validation-config.d.ts +17 -17
  67. package/dist/validation-content.d.ts +26 -26
  68. package/dist/validation-core.d.ts +46 -46
  69. package/dist/validation-core.d.ts.map +1 -1
  70. package/dist/validation-core.js +17 -1
  71. package/dist/validation-public.d.ts +25 -25
  72. package/dist/validation-shared.d.ts +11 -11
  73. package/package.json +13 -7
@@ -0,0 +1,363 @@
1
+ import type { Env } from "./route-env.js";
2
+ export declare const productOptionRoutes: import("hono/hono-base").HonoBase<Env, {
3
+ "/options": {
4
+ $get: {
5
+ input: {};
6
+ output: {
7
+ data: {
8
+ id: string;
9
+ productId: string;
10
+ name: string;
11
+ code: string | null;
12
+ description: string | null;
13
+ status: "draft" | "active" | "archived";
14
+ isDefault: boolean;
15
+ sortOrder: number;
16
+ availableFrom: string | null;
17
+ availableTo: string | null;
18
+ createdAt: string;
19
+ updatedAt: string;
20
+ }[];
21
+ total: number;
22
+ limit: number;
23
+ offset: number;
24
+ };
25
+ outputFormat: "json";
26
+ status: import("hono/utils/http-status").ContentfulStatusCode;
27
+ };
28
+ };
29
+ } & {
30
+ "/options/:optionId": {
31
+ $get: {
32
+ input: {
33
+ param: {
34
+ optionId: string;
35
+ };
36
+ };
37
+ output: {
38
+ error: string;
39
+ };
40
+ outputFormat: "json";
41
+ status: 404;
42
+ } | {
43
+ input: {
44
+ param: {
45
+ optionId: string;
46
+ };
47
+ };
48
+ output: {
49
+ data: {
50
+ id: string;
51
+ productId: string;
52
+ name: string;
53
+ code: string | null;
54
+ description: string | null;
55
+ status: "draft" | "active" | "archived";
56
+ isDefault: boolean;
57
+ sortOrder: number;
58
+ availableFrom: string | null;
59
+ availableTo: string | null;
60
+ createdAt: string;
61
+ updatedAt: string;
62
+ };
63
+ };
64
+ outputFormat: "json";
65
+ status: import("hono/utils/http-status").ContentfulStatusCode;
66
+ };
67
+ };
68
+ } & {
69
+ "/:id/options": {
70
+ $post: {
71
+ input: {
72
+ param: {
73
+ id: string;
74
+ };
75
+ };
76
+ output: {
77
+ error: string;
78
+ };
79
+ outputFormat: "json";
80
+ status: 404;
81
+ } | {
82
+ input: {
83
+ param: {
84
+ id: string;
85
+ };
86
+ };
87
+ output: {
88
+ data: {
89
+ name: string;
90
+ id: string;
91
+ description: string | null;
92
+ status: "draft" | "active" | "archived";
93
+ createdAt: string;
94
+ updatedAt: string;
95
+ productId: string;
96
+ code: string | null;
97
+ isDefault: boolean;
98
+ sortOrder: number;
99
+ availableFrom: string | null;
100
+ availableTo: string | null;
101
+ };
102
+ };
103
+ outputFormat: "json";
104
+ status: 201;
105
+ };
106
+ };
107
+ } & {
108
+ "/options/:optionId": {
109
+ $patch: {
110
+ input: {
111
+ param: {
112
+ optionId: string;
113
+ };
114
+ };
115
+ output: {
116
+ error: string;
117
+ };
118
+ outputFormat: "json";
119
+ status: 404;
120
+ } | {
121
+ input: {
122
+ param: {
123
+ optionId: string;
124
+ };
125
+ };
126
+ output: {
127
+ data: {
128
+ id: string;
129
+ productId: string;
130
+ name: string;
131
+ code: string | null;
132
+ description: string | null;
133
+ status: "draft" | "active" | "archived";
134
+ isDefault: boolean;
135
+ sortOrder: number;
136
+ availableFrom: string | null;
137
+ availableTo: string | null;
138
+ createdAt: string;
139
+ updatedAt: string;
140
+ };
141
+ };
142
+ outputFormat: "json";
143
+ status: import("hono/utils/http-status").ContentfulStatusCode;
144
+ };
145
+ };
146
+ } & {
147
+ "/options/:optionId": {
148
+ $delete: {
149
+ input: {
150
+ param: {
151
+ optionId: string;
152
+ };
153
+ };
154
+ output: {
155
+ error: string;
156
+ };
157
+ outputFormat: "json";
158
+ status: 404;
159
+ } | {
160
+ input: {
161
+ param: {
162
+ optionId: string;
163
+ };
164
+ };
165
+ output: {
166
+ success: true;
167
+ };
168
+ outputFormat: "json";
169
+ status: 200;
170
+ };
171
+ };
172
+ } & {
173
+ "/units": {
174
+ $get: {
175
+ input: {};
176
+ output: {
177
+ data: {
178
+ id: string;
179
+ optionId: string;
180
+ name: string;
181
+ code: string | null;
182
+ description: string | null;
183
+ unitType: "person" | "group" | "room" | "vehicle" | "service" | "other";
184
+ minQuantity: number | null;
185
+ maxQuantity: number | null;
186
+ minAge: number | null;
187
+ maxAge: number | null;
188
+ occupancyMin: number | null;
189
+ occupancyMax: number | null;
190
+ isRequired: boolean;
191
+ isHidden: boolean;
192
+ sortOrder: number;
193
+ createdAt: string;
194
+ updatedAt: string;
195
+ }[];
196
+ total: number;
197
+ limit: number;
198
+ offset: number;
199
+ };
200
+ outputFormat: "json";
201
+ status: import("hono/utils/http-status").ContentfulStatusCode;
202
+ };
203
+ };
204
+ } & {
205
+ "/units/:unitId": {
206
+ $get: {
207
+ input: {
208
+ param: {
209
+ unitId: string;
210
+ };
211
+ };
212
+ output: {
213
+ error: string;
214
+ };
215
+ outputFormat: "json";
216
+ status: 404;
217
+ } | {
218
+ input: {
219
+ param: {
220
+ unitId: string;
221
+ };
222
+ };
223
+ output: {
224
+ data: {
225
+ id: string;
226
+ optionId: string;
227
+ name: string;
228
+ code: string | null;
229
+ description: string | null;
230
+ unitType: "person" | "group" | "room" | "vehicle" | "service" | "other";
231
+ minQuantity: number | null;
232
+ maxQuantity: number | null;
233
+ minAge: number | null;
234
+ maxAge: number | null;
235
+ occupancyMin: number | null;
236
+ occupancyMax: number | null;
237
+ isRequired: boolean;
238
+ isHidden: boolean;
239
+ sortOrder: number;
240
+ createdAt: string;
241
+ updatedAt: string;
242
+ };
243
+ };
244
+ outputFormat: "json";
245
+ status: import("hono/utils/http-status").ContentfulStatusCode;
246
+ };
247
+ };
248
+ } & {
249
+ "/options/:optionId/units": {
250
+ $post: {
251
+ input: {
252
+ param: {
253
+ optionId: string;
254
+ };
255
+ };
256
+ output: {
257
+ error: string;
258
+ };
259
+ outputFormat: "json";
260
+ status: 404;
261
+ } | {
262
+ input: {
263
+ param: {
264
+ optionId: string;
265
+ };
266
+ };
267
+ output: {
268
+ data: {
269
+ name: string;
270
+ id: string;
271
+ description: string | null;
272
+ createdAt: string;
273
+ updatedAt: string;
274
+ code: string | null;
275
+ sortOrder: number;
276
+ optionId: string;
277
+ unitType: "person" | "group" | "room" | "vehicle" | "service" | "other";
278
+ minQuantity: number | null;
279
+ maxQuantity: number | null;
280
+ minAge: number | null;
281
+ maxAge: number | null;
282
+ occupancyMin: number | null;
283
+ occupancyMax: number | null;
284
+ isRequired: boolean;
285
+ isHidden: boolean;
286
+ };
287
+ };
288
+ outputFormat: "json";
289
+ status: 201;
290
+ };
291
+ };
292
+ } & {
293
+ "/units/:unitId": {
294
+ $patch: {
295
+ input: {
296
+ param: {
297
+ unitId: string;
298
+ };
299
+ };
300
+ output: {
301
+ error: string;
302
+ };
303
+ outputFormat: "json";
304
+ status: 404;
305
+ } | {
306
+ input: {
307
+ param: {
308
+ unitId: string;
309
+ };
310
+ };
311
+ output: {
312
+ data: {
313
+ id: string;
314
+ optionId: string;
315
+ name: string;
316
+ code: string | null;
317
+ description: string | null;
318
+ unitType: "person" | "group" | "room" | "vehicle" | "service" | "other";
319
+ minQuantity: number | null;
320
+ maxQuantity: number | null;
321
+ minAge: number | null;
322
+ maxAge: number | null;
323
+ occupancyMin: number | null;
324
+ occupancyMax: number | null;
325
+ isRequired: boolean;
326
+ isHidden: boolean;
327
+ sortOrder: number;
328
+ createdAt: string;
329
+ updatedAt: string;
330
+ };
331
+ };
332
+ outputFormat: "json";
333
+ status: import("hono/utils/http-status").ContentfulStatusCode;
334
+ };
335
+ };
336
+ } & {
337
+ "/units/:unitId": {
338
+ $delete: {
339
+ input: {
340
+ param: {
341
+ unitId: string;
342
+ };
343
+ };
344
+ output: {
345
+ error: string;
346
+ };
347
+ outputFormat: "json";
348
+ status: 404;
349
+ } | {
350
+ input: {
351
+ param: {
352
+ unitId: string;
353
+ };
354
+ };
355
+ output: {
356
+ success: true;
357
+ };
358
+ outputFormat: "json";
359
+ status: 200;
360
+ };
361
+ };
362
+ }, "/", "/units/:unitId">;
363
+ //# sourceMappingURL=routes-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes-options.d.ts","sourceRoot":"","sources":["../src/routes-options.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;AAIzC,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAsM5B,CAAA"}
@@ -0,0 +1,173 @@
1
+ import { parseJsonBody, parseQuery } from "@voyantjs/hono";
2
+ import { Hono } from "hono";
3
+ import { appendProductMutationLedgerEntry, changedMutationFields } from "./action-ledger.js";
4
+ import { emitProductContentChanged } from "./events.js";
5
+ import { productsService } from "./service.js";
6
+ import * as validation from "./validation.js";
7
+ export const productOptionRoutes = new Hono()
8
+ // ==========================================================================
9
+ // Options
10
+ // ==========================================================================
11
+ // GET /options — List options
12
+ .get("/options", async (c) => {
13
+ const query = parseQuery(c, validation.productOptionListQuerySchema);
14
+ return c.json(await productsService.listOptions(c.get("db"), query));
15
+ })
16
+ // GET /options/:optionId — Get single option
17
+ .get("/options/:optionId", async (c) => {
18
+ const row = await productsService.getOptionById(c.get("db"), c.req.param("optionId"));
19
+ if (!row) {
20
+ return c.json({ error: "Product option not found" }, 404);
21
+ }
22
+ return c.json({ data: row });
23
+ })
24
+ // POST /:id/options — Create option for product
25
+ .post("/:id/options", async (c) => {
26
+ const productId = c.req.param("id");
27
+ const body = await parseJsonBody(c, validation.insertProductOptionSchema);
28
+ const row = await productsService.createOption(c.get("db"), productId, body);
29
+ if (!row) {
30
+ return c.json({ error: "Product not found" }, 404);
31
+ }
32
+ await appendProductMutationLedgerEntry(c, {
33
+ action: "create",
34
+ productId,
35
+ changedFields: changedMutationFields(body, null, row),
36
+ subject: "product option",
37
+ actionName: "product.option.create",
38
+ routeOrToolName: "products.option.create",
39
+ });
40
+ await emitProductContentChanged(c.get("eventBus"), { id: productId, axis: "option" });
41
+ return c.json({ data: row }, 201);
42
+ })
43
+ // PATCH /options/:optionId — Update option
44
+ .patch("/options/:optionId", async (c) => {
45
+ const optionId = c.req.param("optionId");
46
+ const body = await parseJsonBody(c, validation.updateProductOptionSchema);
47
+ const before = await productsService.getOptionById(c.get("db"), optionId);
48
+ if (!before) {
49
+ return c.json({ error: "Product option not found" }, 404);
50
+ }
51
+ const row = await productsService.updateOption(c.get("db"), optionId, body);
52
+ if (!row) {
53
+ return c.json({ error: "Product option not found" }, 404);
54
+ }
55
+ await appendProductMutationLedgerEntry(c, {
56
+ action: "update",
57
+ productId: row.productId,
58
+ changedFields: changedMutationFields(body, before, row),
59
+ subject: "product option",
60
+ actionName: "product.option.update",
61
+ routeOrToolName: "products.option.update",
62
+ });
63
+ if (row.productId) {
64
+ await emitProductContentChanged(c.get("eventBus"), { id: row.productId, axis: "option" });
65
+ }
66
+ return c.json({ data: row });
67
+ })
68
+ // DELETE /options/:optionId — Delete option
69
+ .delete("/options/:optionId", async (c) => {
70
+ const optionId = c.req.param("optionId");
71
+ const before = await productsService.getOptionById(c.get("db"), optionId);
72
+ if (!before) {
73
+ return c.json({ error: "Product option not found" }, 404);
74
+ }
75
+ const row = await productsService.deleteOption(c.get("db"), optionId);
76
+ if (!row) {
77
+ return c.json({ error: "Product option not found" }, 404);
78
+ }
79
+ await appendProductMutationLedgerEntry(c, {
80
+ action: "delete",
81
+ productId: before.productId,
82
+ changedFields: [],
83
+ subject: "product option",
84
+ actionName: "product.option.delete",
85
+ routeOrToolName: "products.option.delete",
86
+ });
87
+ await emitProductContentChanged(c.get("eventBus"), { id: before.productId, axis: "option" });
88
+ return c.json({ success: true }, 200);
89
+ })
90
+ // ==========================================================================
91
+ // Option Units
92
+ // ==========================================================================
93
+ // GET /units — List units
94
+ .get("/units", async (c) => {
95
+ const query = parseQuery(c, validation.optionUnitListQuerySchema);
96
+ return c.json(await productsService.listUnits(c.get("db"), query));
97
+ })
98
+ // GET /units/:unitId — Get single unit
99
+ .get("/units/:unitId", async (c) => {
100
+ const row = await productsService.getUnitById(c.get("db"), c.req.param("unitId"));
101
+ if (!row) {
102
+ return c.json({ error: "Option unit not found" }, 404);
103
+ }
104
+ return c.json({ data: row });
105
+ })
106
+ // POST /options/:optionId/units — Create unit for option
107
+ .post("/options/:optionId/units", async (c) => {
108
+ const optionId = c.req.param("optionId");
109
+ const body = await parseJsonBody(c, validation.insertOptionUnitSchema);
110
+ const option = await productsService.getOptionById(c.get("db"), optionId);
111
+ if (!option) {
112
+ return c.json({ error: "Product option not found" }, 404);
113
+ }
114
+ const row = await productsService.createUnit(c.get("db"), optionId, body);
115
+ if (!row) {
116
+ return c.json({ error: "Product option not found" }, 404);
117
+ }
118
+ await appendProductMutationLedgerEntry(c, {
119
+ action: "create",
120
+ productId: option.productId,
121
+ changedFields: changedMutationFields(body, null, row),
122
+ subject: "product option unit",
123
+ actionName: "product.option_unit.create",
124
+ routeOrToolName: "products.option_unit.create",
125
+ });
126
+ await emitProductContentChanged(c.get("eventBus"), { id: option.productId, axis: "option" });
127
+ return c.json({ data: row }, 201);
128
+ })
129
+ // PATCH /units/:unitId — Update unit
130
+ .patch("/units/:unitId", async (c) => {
131
+ const unitId = c.req.param("unitId");
132
+ const body = await parseJsonBody(c, validation.updateOptionUnitSchema);
133
+ const before = await productsService.getUnitForProductMutation(c.get("db"), unitId);
134
+ if (!before) {
135
+ return c.json({ error: "Option unit not found" }, 404);
136
+ }
137
+ const row = await productsService.updateUnit(c.get("db"), unitId, body);
138
+ if (!row) {
139
+ return c.json({ error: "Option unit not found" }, 404);
140
+ }
141
+ await appendProductMutationLedgerEntry(c, {
142
+ action: "update",
143
+ productId: before.productId,
144
+ changedFields: changedMutationFields(body, before, row),
145
+ subject: "product option unit",
146
+ actionName: "product.option_unit.update",
147
+ routeOrToolName: "products.option_unit.update",
148
+ });
149
+ await emitProductContentChanged(c.get("eventBus"), { id: before.productId, axis: "option" });
150
+ return c.json({ data: row });
151
+ })
152
+ // DELETE /units/:unitId — Delete unit
153
+ .delete("/units/:unitId", async (c) => {
154
+ const unitId = c.req.param("unitId");
155
+ const before = await productsService.getUnitForProductMutation(c.get("db"), unitId);
156
+ if (!before) {
157
+ return c.json({ error: "Option unit not found" }, 404);
158
+ }
159
+ const row = await productsService.deleteUnit(c.get("db"), unitId);
160
+ if (!row) {
161
+ return c.json({ error: "Option unit not found" }, 404);
162
+ }
163
+ await appendProductMutationLedgerEntry(c, {
164
+ action: "delete",
165
+ productId: before.productId,
166
+ changedFields: [],
167
+ subject: "product option unit",
168
+ actionName: "product.option_unit.delete",
169
+ routeOrToolName: "products.option_unit.delete",
170
+ });
171
+ await emitProductContentChanged(c.get("eventBus"), { id: before.productId, axis: "option" });
172
+ return c.json({ success: true }, 200);
173
+ });
@@ -59,7 +59,7 @@ export declare const publicProductRoutes: import("hono/hono-base").HonoBase<Env,
59
59
  }[];
60
60
  locations: {
61
61
  id: string;
62
- locationType: "start" | "other" | "end" | "meeting_point" | "pickup" | "dropoff" | "point_of_interest";
62
+ locationType: "end" | "other" | "start" | "meeting_point" | "pickup" | "dropoff" | "point_of_interest";
63
63
  title: string;
64
64
  address: string | null;
65
65
  city: string | null;
@@ -170,7 +170,7 @@ export declare const publicProductRoutes: import("hono/hono-base").HonoBase<Env,
170
170
  }[];
171
171
  locations: {
172
172
  id: string;
173
- locationType: "start" | "other" | "end" | "meeting_point" | "pickup" | "dropoff" | "point_of_interest";
173
+ locationType: "end" | "other" | "start" | "meeting_point" | "pickup" | "dropoff" | "point_of_interest";
174
174
  title: string;
175
175
  address: string | null;
176
176
  city: string | null;
@@ -271,7 +271,7 @@ export declare const publicProductRoutes: import("hono/hono-base").HonoBase<Env,
271
271
  }[];
272
272
  locations: {
273
273
  id: string;
274
- locationType: "start" | "other" | "end" | "meeting_point" | "pickup" | "dropoff" | "point_of_interest";
274
+ locationType: "end" | "other" | "start" | "meeting_point" | "pickup" | "dropoff" | "point_of_interest";
275
275
  title: string;
276
276
  address: string | null;
277
277
  city: string | null;
@@ -431,7 +431,7 @@ export declare const publicProductRoutes: import("hono/hono-base").HonoBase<Env,
431
431
  }[];
432
432
  locations: {
433
433
  id: string;
434
- locationType: "start" | "other" | "end" | "meeting_point" | "pickup" | "dropoff" | "point_of_interest";
434
+ locationType: "end" | "other" | "start" | "meeting_point" | "pickup" | "dropoff" | "point_of_interest";
435
435
  title: string;
436
436
  address: string | null;
437
437
  city: string | null;