@voyantjs/cruises 0.40.1 → 0.41.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.
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AA8CjE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AAE7E,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf;;;;;;;;;;;;WAYG;QACH,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;KAC9C,CAAA;CACF,CAAA;AAuND,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAyBhB,UAAU;oCACF,MAAM;;;;;;yBAEjB,MAAM;;;;;;;6BAGyB,MAAM;2BAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAisB3D,CAAA;AAEJ,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAA"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAE9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AA8CjE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AAE7E,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,QAAQ,CAAC,EAAE,QAAQ,CAAA;QACnB;;;;;;;;;;;;WAYG;QACH,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;KAC9C,CAAA;CACF,CAAA;AAuND,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAyBhB,UAAU;oCACF,MAAM;;;;;;yBAEjB,MAAM;;;;;;;6BAGyB,MAAM;2BAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAysB3D,CAAA;AAEJ,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAA"}
package/dist/routes.js CHANGED
@@ -252,251 +252,10 @@ export const cruiseAdminRoutes = new Hono()
252
252
  })
253
253
  .post("/", async (c) => {
254
254
  const data = await parseJsonBody(c, insertCruiseSchema);
255
- const row = await cruisesService.createCruise(c.get("db"), data);
256
- return c.json({ data: row }, 201);
257
- })
258
- // --- per-cruise (parses unified key, dispatches local or external) ---
259
- // External branch dispatches through the catalog content service
260
- // (cache-first, SWR refresh, synthesizer fallback) — flipped from
261
- // ad-hoc adapter.fetchCruise() per the catalog-sourced-content
262
- // migration. Returns the rich CruiseContent shape; templates that
263
- // need backwards-compatible ExternalCruise can post-process the
264
- // response.
265
- .get("/:key", async (c) => {
266
- const parsed = parseUnifiedKey(c.req.param("key"));
267
- if (parsed.kind === "invalid")
268
- return c.json(invalidKey(parsed.raw), 400);
269
- if (parsed.kind === "external") {
270
- const registry = c.get("sourceAdapterRegistry");
271
- if (!registry)
272
- return c.json(registryNotConfigured(), 503);
273
- const entityId = entityIdFromExternal(parsed);
274
- const result = await getCruiseContent(c.get("db"), entityId, readContentScope(c), {
275
- registry,
276
- });
277
- if (!result) {
278
- return c.json({
279
- error: "not_found",
280
- detail: `No sourced-entry row for cruise ${parsed.provider}:${parsed.ref} (entity ${entityId}). Run discovery first or check that an adapter is registered for "${parsed.provider}".`,
281
- }, 404);
282
- }
283
- return c.json({
284
- data: {
285
- source: "external",
286
- sourceProvider: parsed.provider,
287
- sourceRef: parsed.ref,
288
- entityId,
289
- content: result.content,
290
- servedLocale: result.resolution.served_locale,
291
- matchKind: result.resolution.match_kind,
292
- contentSource: result.source,
293
- servedStale: result.served_stale,
294
- synthesized: result.synthesized,
295
- machineTranslated: result.machine_translated,
296
- },
297
- });
298
- }
299
- const includeRaw = c.req.query("include") ?? "";
300
- const includes = new Set(includeRaw
301
- .split(",")
302
- .map((s) => s.trim())
303
- .filter(Boolean));
304
- const row = await cruisesService.getCruiseById(c.get("db"), parsed.id, {
305
- withSailings: includes.has("sailings"),
306
- withDays: includes.has("days"),
307
- });
308
- if (!row)
309
- return c.json({ error: "not_found" }, 404);
310
- return c.json({
311
- data: {
312
- source: "local",
313
- sourceProvider: null,
314
- sourceRef: null,
315
- cruise: row,
316
- },
317
- });
318
- })
319
- .put("/:key", async (c) => {
320
- const parsed = parseUnifiedKey(c.req.param("key"));
321
- if (parsed.kind === "external") {
322
- return c.json({
323
- error: "external_cruise_read_only",
324
- detail: `External cruise from '${parsed.provider}' cannot be edited locally. Edit at the upstream system, or POST /:key/detach to convert to a local cruise first.`,
325
- }, 409);
326
- }
327
- if (parsed.kind === "invalid")
328
- return c.json(invalidKey(parsed.raw), 400);
329
- const data = await parseJsonBody(c, updateCruiseSchema);
330
- const row = await cruisesService.updateCruise(c.get("db"), parsed.id, data);
331
- if (!row)
332
- return c.json({ error: "not_found" }, 404);
333
- return c.json({ data: row });
334
- })
335
- .delete("/:key", async (c) => {
336
- const parsed = parseUnifiedKey(c.req.param("key"));
337
- if (parsed.kind === "external") {
338
- return c.json({
339
- error: "external_cruise_read_only",
340
- detail: "External cruises can't be deleted locally.",
341
- }, 409);
342
- }
343
- if (parsed.kind === "invalid")
344
- return c.json(invalidKey(parsed.raw), 400);
345
- const row = await cruisesService.archiveCruise(c.get("db"), parsed.id);
346
- if (!row)
347
- return c.json({ error: "not_found" }, 404);
348
- return c.json({ data: row });
349
- })
350
- .post("/:key/aggregates/recompute", async (c) => {
351
- const parsed = parseUnifiedKey(c.req.param("key"));
352
- if (parsed.kind === "external") {
353
- return c.json({ error: "external_cruise_read_only", detail: "Aggregates only apply to local cruises." }, 409);
354
- }
355
- if (parsed.kind === "invalid")
356
- return c.json(invalidKey(parsed.raw), 400);
357
- const row = await cruisesService.recomputeCruiseAggregates(c.get("db"), parsed.id);
358
- if (!row)
359
- return c.json({ error: "not_found" }, 404);
360
- return c.json({ data: row });
361
- })
362
- .get("/:key/sailings", async (c) => {
363
- const parsed = parseUnifiedKey(c.req.param("key"));
364
- if (parsed.kind === "invalid")
365
- return c.json(invalidKey(parsed.raw), 400);
366
- if (parsed.kind === "external") {
367
- const ext = resolveExternal(parsed);
368
- if (!ext)
369
- return c.json(adapterNotRegistered(parsed.provider), 501);
370
- const sailings = await ext.adapter.listSailingsForCruise(ext.sourceRef);
371
- return c.json({
372
- data: sailings.map((s) => ({
373
- source: "external",
374
- sourceProvider: ext.adapter.name,
375
- key: makeExternalKey(ext.adapter, s.sourceRef),
376
- sailing: s,
377
- })),
378
- total: sailings.length,
379
- });
380
- }
381
- const result = await cruisesService.listSailings(c.get("db"), {
382
- cruiseId: parsed.id,
383
- limit: 100,
384
- offset: 0,
385
- });
386
- return c.json(result);
387
- })
388
- .put("/:key/days/bulk", async (c) => {
389
- const parsed = parseUnifiedKey(c.req.param("key"));
390
- if (parsed.kind === "external") {
391
- return c.json({ error: "external_cruise_read_only" }, 409);
392
- }
393
- if (parsed.kind === "invalid")
394
- return c.json(invalidKey(parsed.raw), 400);
395
- const payload = await parseJsonBody(c, replaceCruiseDaysSchema.omit({ cruiseId: true }));
396
- const days = await cruisesService.replaceCruiseDays(c.get("db"), {
397
- cruiseId: parsed.id,
398
- days: payload.days,
399
- });
400
- return c.json({ data: days });
401
- })
402
- // --- external-only operations ---
403
- // Refresh dispatches through the catalog content service. The
404
- // invalidator marks the cache row stale; the subsequent
405
- // getCruiseContent call sees the staleness and triggers a SWR
406
- // refresh. Templates that need synchronous "force fresh from
407
- // upstream" semantics should call adapter.getContent() directly
408
- // — this route's contract is "best effort refresh, eventually
409
- // consistent."
410
- .post("/:key/refresh", async (c) => {
411
- const parsed = parseUnifiedKey(c.req.param("key"));
412
- if (parsed.kind !== "external")
413
- return c.json({ error: "local_cruise_no_refresh" }, 400);
414
- const registry = c.get("sourceAdapterRegistry");
415
- if (!registry)
416
- return c.json(registryNotConfigured(), 503);
417
- const entityId = entityIdFromExternal(parsed);
418
- const { invalidateCruiseContentOnDrift } = await import("./service-content.js");
419
- await invalidateCruiseContentOnDrift(c.get("db"), {
420
- id: `cnde_refresh_${Date.now()}`,
421
- entity_module: "cruises",
422
- entity_id: entityId,
423
- kind: "content_invalidated",
424
- detected_at: new Date(),
425
- });
426
- const result = await getCruiseContent(c.get("db"), entityId, readContentScope(c), {
427
- registry,
428
- });
429
- if (!result) {
430
- return c.json({
431
- error: "not_found",
432
- detail: `No sourced-entry row for cruise ${parsed.provider}:${parsed.ref} (entity ${entityId}).`,
433
- }, 404);
434
- }
435
- return c.json({
436
- data: {
437
- source: "external",
438
- sourceProvider: parsed.provider,
439
- sourceRef: parsed.ref,
440
- entityId,
441
- content: result.content,
442
- contentSource: result.source,
443
- servedStale: result.served_stale,
444
- refreshedAt: new Date().toISOString(),
445
- },
446
- });
447
- })
448
- .post("/:key/detach", async (c) => {
449
- const parsed = parseUnifiedKey(c.req.param("key"));
450
- if (parsed.kind !== "external")
451
- return c.json({ error: "local_cruise_no_detach" }, 400);
452
- const ext = resolveExternal(parsed);
453
- if (!ext)
454
- return c.json(adapterNotRegistered(parsed.provider), 501);
455
- const cruise = await detachExternalCruise(c.get("db"), ext.adapter, ext.sourceRef);
456
- return c.json({ data: cruise }, 201);
457
- })
458
- // --- enrichment programs (expedition-focused; local cruises only) ---
459
- .get("/:key/enrichment", async (c) => {
460
- const parsed = parseUnifiedKey(c.req.param("key"));
461
- if (parsed.kind === "external") {
462
- const ext = resolveExternal(parsed);
463
- if (!ext)
464
- return c.json(adapterNotRegistered(parsed.provider), 501);
465
- // Adapters surface enrichment via the rich cruise detail; we return an
466
- // empty list here for shape compatibility. Templates that need richer
467
- // external enrichment should read from adapter.fetchCruise() directly.
468
- return c.json({ data: [] });
469
- }
470
- if (parsed.kind === "invalid")
471
- return c.json(invalidKey(parsed.raw), 400);
472
- const programs = await cruisesService.listEnrichmentPrograms(c.get("db"), parsed.id);
473
- return c.json({ data: programs });
474
- })
475
- .post("/:key/enrichment", async (c) => {
476
- const parsed = parseUnifiedKey(c.req.param("key"));
477
- if (parsed.kind === "external")
478
- return c.json({ error: "external_cruise_read_only" }, 409);
479
- if (parsed.kind === "invalid")
480
- return c.json(invalidKey(parsed.raw), 400);
481
- const data = await parseJsonBody(c, insertEnrichmentProgramSchema.omit({ cruiseId: true }));
482
- const row = await cruisesService.createEnrichmentProgram(c.get("db"), {
483
- ...data,
484
- cruiseId: parsed.id,
255
+ const row = await cruisesService.createCruise(c.get("db"), data, {
256
+ eventBus: c.get("eventBus"),
485
257
  });
486
258
  return c.json({ data: row }, 201);
487
- })
488
- .put("/:key/enrichment/bulk", async (c) => {
489
- const parsed = parseUnifiedKey(c.req.param("key"));
490
- if (parsed.kind === "external")
491
- return c.json({ error: "external_cruise_read_only" }, 409);
492
- if (parsed.kind === "invalid")
493
- return c.json(invalidKey(parsed.raw), 400);
494
- const payload = await parseJsonBody(c, replaceEnrichmentProgramsSchema.omit({ cruiseId: true }));
495
- const rows = await cruisesService.replaceEnrichmentPrograms(c.get("db"), {
496
- cruiseId: parsed.id,
497
- programs: payload.programs,
498
- });
499
- return c.json({ data: rows });
500
259
  })
501
260
  .put("/enrichment/:programId", async (c) => {
502
261
  const data = await parseJsonBody(c, updateEnrichmentProgramSchema);
@@ -914,4 +673,253 @@ export const cruiseAdminRoutes = new Hono()
914
673
  .post("/search-index/rebuild", async (c) => {
915
674
  const result = await cruisesSearchService.rebuildAll(c.get("db"));
916
675
  return c.json({ data: result });
676
+ })
677
+ // --- per-cruise (parses unified key, dispatches local or external) ---
678
+ // Keep wildcard key routes after static admin subresources so reserved
679
+ // segments such as /sailings, /ships, and /prices reach their handlers.
680
+ // External branch dispatches through the catalog content service
681
+ // (cache-first, SWR refresh, synthesizer fallback) — flipped from
682
+ // ad-hoc adapter.fetchCruise() per the catalog-sourced-content
683
+ // migration. Returns the rich CruiseContent shape; templates that
684
+ // need backwards-compatible ExternalCruise can post-process the
685
+ // response.
686
+ .get("/:key", async (c) => {
687
+ const parsed = parseUnifiedKey(c.req.param("key"));
688
+ if (parsed.kind === "invalid")
689
+ return c.json(invalidKey(parsed.raw), 400);
690
+ if (parsed.kind === "external") {
691
+ const registry = c.get("sourceAdapterRegistry");
692
+ if (!registry)
693
+ return c.json(registryNotConfigured(), 503);
694
+ const entityId = entityIdFromExternal(parsed);
695
+ const result = await getCruiseContent(c.get("db"), entityId, readContentScope(c), {
696
+ registry,
697
+ });
698
+ if (!result) {
699
+ return c.json({
700
+ error: "not_found",
701
+ detail: `No sourced-entry row for cruise ${parsed.provider}:${parsed.ref} (entity ${entityId}). Run discovery first or check that an adapter is registered for "${parsed.provider}".`,
702
+ }, 404);
703
+ }
704
+ return c.json({
705
+ data: {
706
+ source: "external",
707
+ sourceProvider: parsed.provider,
708
+ sourceRef: parsed.ref,
709
+ entityId,
710
+ content: result.content,
711
+ servedLocale: result.resolution.served_locale,
712
+ matchKind: result.resolution.match_kind,
713
+ contentSource: result.source,
714
+ servedStale: result.served_stale,
715
+ synthesized: result.synthesized,
716
+ machineTranslated: result.machine_translated,
717
+ },
718
+ });
719
+ }
720
+ const includeRaw = c.req.query("include") ?? "";
721
+ const includes = new Set(includeRaw
722
+ .split(",")
723
+ .map((s) => s.trim())
724
+ .filter(Boolean));
725
+ const row = await cruisesService.getCruiseById(c.get("db"), parsed.id, {
726
+ withSailings: includes.has("sailings"),
727
+ withDays: includes.has("days"),
728
+ });
729
+ if (!row)
730
+ return c.json({ error: "not_found" }, 404);
731
+ return c.json({
732
+ data: {
733
+ source: "local",
734
+ sourceProvider: null,
735
+ sourceRef: null,
736
+ cruise: row,
737
+ },
738
+ });
739
+ })
740
+ .put("/:key", async (c) => {
741
+ const parsed = parseUnifiedKey(c.req.param("key"));
742
+ if (parsed.kind === "external") {
743
+ return c.json({
744
+ error: "external_cruise_read_only",
745
+ detail: `External cruise from '${parsed.provider}' cannot be edited locally. Edit at the upstream system, or POST /:key/detach to convert to a local cruise first.`,
746
+ }, 409);
747
+ }
748
+ if (parsed.kind === "invalid")
749
+ return c.json(invalidKey(parsed.raw), 400);
750
+ const data = await parseJsonBody(c, updateCruiseSchema);
751
+ const row = await cruisesService.updateCruise(c.get("db"), parsed.id, data, {
752
+ eventBus: c.get("eventBus"),
753
+ });
754
+ if (!row)
755
+ return c.json({ error: "not_found" }, 404);
756
+ return c.json({ data: row });
757
+ })
758
+ .delete("/:key", async (c) => {
759
+ const parsed = parseUnifiedKey(c.req.param("key"));
760
+ if (parsed.kind === "external") {
761
+ return c.json({
762
+ error: "external_cruise_read_only",
763
+ detail: "External cruises can't be deleted locally.",
764
+ }, 409);
765
+ }
766
+ if (parsed.kind === "invalid")
767
+ return c.json(invalidKey(parsed.raw), 400);
768
+ const row = await cruisesService.archiveCruise(c.get("db"), parsed.id, {
769
+ eventBus: c.get("eventBus"),
770
+ });
771
+ if (!row)
772
+ return c.json({ error: "not_found" }, 404);
773
+ return c.json({ data: row });
774
+ })
775
+ .post("/:key/aggregates/recompute", async (c) => {
776
+ const parsed = parseUnifiedKey(c.req.param("key"));
777
+ if (parsed.kind === "external") {
778
+ return c.json({ error: "external_cruise_read_only", detail: "Aggregates only apply to local cruises." }, 409);
779
+ }
780
+ if (parsed.kind === "invalid")
781
+ return c.json(invalidKey(parsed.raw), 400);
782
+ const row = await cruisesService.recomputeCruiseAggregates(c.get("db"), parsed.id);
783
+ if (!row)
784
+ return c.json({ error: "not_found" }, 404);
785
+ return c.json({ data: row });
786
+ })
787
+ .get("/:key/sailings", async (c) => {
788
+ const parsed = parseUnifiedKey(c.req.param("key"));
789
+ if (parsed.kind === "invalid")
790
+ return c.json(invalidKey(parsed.raw), 400);
791
+ if (parsed.kind === "external") {
792
+ const ext = resolveExternal(parsed);
793
+ if (!ext)
794
+ return c.json(adapterNotRegistered(parsed.provider), 501);
795
+ const sailings = await ext.adapter.listSailingsForCruise(ext.sourceRef);
796
+ return c.json({
797
+ data: sailings.map((s) => ({
798
+ source: "external",
799
+ sourceProvider: ext.adapter.name,
800
+ key: makeExternalKey(ext.adapter, s.sourceRef),
801
+ sailing: s,
802
+ })),
803
+ total: sailings.length,
804
+ });
805
+ }
806
+ const result = await cruisesService.listSailings(c.get("db"), {
807
+ cruiseId: parsed.id,
808
+ limit: 100,
809
+ offset: 0,
810
+ });
811
+ return c.json(result);
812
+ })
813
+ .put("/:key/days/bulk", async (c) => {
814
+ const parsed = parseUnifiedKey(c.req.param("key"));
815
+ if (parsed.kind === "external") {
816
+ return c.json({ error: "external_cruise_read_only" }, 409);
817
+ }
818
+ if (parsed.kind === "invalid")
819
+ return c.json(invalidKey(parsed.raw), 400);
820
+ const payload = await parseJsonBody(c, replaceCruiseDaysSchema.omit({ cruiseId: true }));
821
+ const days = await cruisesService.replaceCruiseDays(c.get("db"), {
822
+ cruiseId: parsed.id,
823
+ days: payload.days,
824
+ });
825
+ return c.json({ data: days });
826
+ })
827
+ // --- external-only operations ---
828
+ // Refresh dispatches through the catalog content service. The
829
+ // invalidator marks the cache row stale; the subsequent
830
+ // getCruiseContent call sees the staleness and triggers a SWR
831
+ // refresh. Templates that need synchronous "force fresh from
832
+ // upstream" semantics should call adapter.getContent() directly
833
+ // — this route's contract is "best effort refresh, eventually
834
+ // consistent."
835
+ .post("/:key/refresh", async (c) => {
836
+ const parsed = parseUnifiedKey(c.req.param("key"));
837
+ if (parsed.kind !== "external")
838
+ return c.json({ error: "local_cruise_no_refresh" }, 400);
839
+ const registry = c.get("sourceAdapterRegistry");
840
+ if (!registry)
841
+ return c.json(registryNotConfigured(), 503);
842
+ const entityId = entityIdFromExternal(parsed);
843
+ const { invalidateCruiseContentOnDrift } = await import("./service-content.js");
844
+ await invalidateCruiseContentOnDrift(c.get("db"), {
845
+ id: `cnde_refresh_${Date.now()}`,
846
+ entity_module: "cruises",
847
+ entity_id: entityId,
848
+ kind: "content_invalidated",
849
+ detected_at: new Date(),
850
+ });
851
+ const result = await getCruiseContent(c.get("db"), entityId, readContentScope(c), {
852
+ registry,
853
+ });
854
+ if (!result) {
855
+ return c.json({
856
+ error: "not_found",
857
+ detail: `No sourced-entry row for cruise ${parsed.provider}:${parsed.ref} (entity ${entityId}).`,
858
+ }, 404);
859
+ }
860
+ return c.json({
861
+ data: {
862
+ source: "external",
863
+ sourceProvider: parsed.provider,
864
+ sourceRef: parsed.ref,
865
+ entityId,
866
+ content: result.content,
867
+ contentSource: result.source,
868
+ servedStale: result.served_stale,
869
+ refreshedAt: new Date().toISOString(),
870
+ },
871
+ });
872
+ })
873
+ .post("/:key/detach", async (c) => {
874
+ const parsed = parseUnifiedKey(c.req.param("key"));
875
+ if (parsed.kind !== "external")
876
+ return c.json({ error: "local_cruise_no_detach" }, 400);
877
+ const ext = resolveExternal(parsed);
878
+ if (!ext)
879
+ return c.json(adapterNotRegistered(parsed.provider), 501);
880
+ const cruise = await detachExternalCruise(c.get("db"), ext.adapter, ext.sourceRef);
881
+ return c.json({ data: cruise }, 201);
882
+ })
883
+ // --- enrichment programs (expedition-focused; local cruises only) ---
884
+ .get("/:key/enrichment", async (c) => {
885
+ const parsed = parseUnifiedKey(c.req.param("key"));
886
+ if (parsed.kind === "external") {
887
+ const ext = resolveExternal(parsed);
888
+ if (!ext)
889
+ return c.json(adapterNotRegistered(parsed.provider), 501);
890
+ // Adapters surface enrichment via the rich cruise detail; we return an
891
+ // empty list here for shape compatibility. Templates that need richer
892
+ // external enrichment should read from adapter.fetchCruise() directly.
893
+ return c.json({ data: [] });
894
+ }
895
+ if (parsed.kind === "invalid")
896
+ return c.json(invalidKey(parsed.raw), 400);
897
+ const programs = await cruisesService.listEnrichmentPrograms(c.get("db"), parsed.id);
898
+ return c.json({ data: programs });
899
+ })
900
+ .post("/:key/enrichment", async (c) => {
901
+ const parsed = parseUnifiedKey(c.req.param("key"));
902
+ if (parsed.kind === "external")
903
+ return c.json({ error: "external_cruise_read_only" }, 409);
904
+ if (parsed.kind === "invalid")
905
+ return c.json(invalidKey(parsed.raw), 400);
906
+ const data = await parseJsonBody(c, insertEnrichmentProgramSchema.omit({ cruiseId: true }));
907
+ const row = await cruisesService.createEnrichmentProgram(c.get("db"), {
908
+ ...data,
909
+ cruiseId: parsed.id,
910
+ });
911
+ return c.json({ data: row }, 201);
912
+ })
913
+ .put("/:key/enrichment/bulk", async (c) => {
914
+ const parsed = parseUnifiedKey(c.req.param("key"));
915
+ if (parsed.kind === "external")
916
+ return c.json({ error: "external_cruise_read_only" }, 409);
917
+ if (parsed.kind === "invalid")
918
+ return c.json(invalidKey(parsed.raw), 400);
919
+ const payload = await parseJsonBody(c, replaceEnrichmentProgramsSchema.omit({ cruiseId: true }));
920
+ const rows = await cruisesService.replaceEnrichmentPrograms(c.get("db"), {
921
+ cruiseId: parsed.id,
922
+ programs: payload.programs,
923
+ });
924
+ return c.json({ data: rows });
917
925
  });
package/dist/service.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { EventBus } from "@voyantjs/core";
1
2
  import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
3
  import { type CruiseCabin, type CruiseCabinCategory, type CruiseDeck, type CruiseShip } from "./schema-cabins.js";
3
4
  import { type CruiseEnrichmentProgram } from "./schema-content.js";
@@ -9,6 +10,9 @@ import type { InsertEnrichmentProgram, ReplaceEnrichmentPrograms, UpdateEnrichme
9
10
  import type { CruiseListQuery, InsertCruise, InsertSailing, SailingListQuery, UpdateCruise, UpdateSailing } from "./validation-core.js";
10
11
  import type { ReplaceCruiseDays, ReplaceSailingDays } from "./validation-itinerary.js";
11
12
  import type { InsertPrice, InsertPriceComponent, PriceListQuery, UpdatePrice } from "./validation-pricing.js";
13
+ export interface CruiseMutationRuntime {
14
+ eventBus?: EventBus;
15
+ }
12
16
  export declare const cruisesService: {
13
17
  listCruises(db: PostgresJsDatabase, query: CruiseListQuery): Promise<{
14
18
  data: {
@@ -51,9 +55,9 @@ export declare const cruisesService: {
51
55
  sailings?: CruiseSailing[];
52
56
  days?: CruiseDay[];
53
57
  }) | null>;
54
- createCruise(db: PostgresJsDatabase, data: InsertCruise): Promise<Cruise>;
55
- updateCruise(db: PostgresJsDatabase, id: string, data: UpdateCruise): Promise<Cruise | null>;
56
- archiveCruise(db: PostgresJsDatabase, id: string): Promise<Cruise | null>;
58
+ createCruise(db: PostgresJsDatabase, data: InsertCruise, runtime?: CruiseMutationRuntime): Promise<Cruise>;
59
+ updateCruise(db: PostgresJsDatabase, id: string, data: UpdateCruise, runtime?: CruiseMutationRuntime): Promise<Cruise | null>;
60
+ archiveCruise(db: PostgresJsDatabase, id: string, runtime?: CruiseMutationRuntime): Promise<Cruise | null>;
57
61
  recomputeCruiseAggregates(db: PostgresJsDatabase, cruiseId: string): Promise<Cruise | null>;
58
62
  listSailings(db: PostgresJsDatabase, query: SailingListQuery): Promise<{
59
63
  data: {
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
package/dist/service.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { and, asc, count, desc, eq, gte, ilike, inArray, lte, or, sql } from "drizzle-orm";
2
+ import { CRUISE_CREATED_EVENT, CRUISE_DELETED_EVENT, CRUISE_UPDATED_EVENT, emitCruiseLifecycleEvent, } from "./events.js";
2
3
  import { cruiseCabinCategories, cruiseCabins, cruiseDecks, cruiseShips, } from "./schema-cabins.js";
3
4
  import { cruiseEnrichmentPrograms } from "./schema-content.js";
4
5
  import { cruiseSailings, cruises, } from "./schema-core.js";
@@ -85,7 +86,7 @@ export const cruisesService = {
85
86
  }
86
87
  return out;
87
88
  },
88
- async createCruise(db, data) {
89
+ async createCruise(db, data, runtime = {}) {
89
90
  const [row] = await db
90
91
  .insert(cruises)
91
92
  .values(data)
@@ -93,26 +94,31 @@ export const cruisesService = {
93
94
  if (!row)
94
95
  throw new Error("Failed to create cruise");
95
96
  await reprojectIfPossible(db, row.id);
97
+ await emitCruiseLifecycleEvent(runtime.eventBus, CRUISE_CREATED_EVENT, { id: row.id });
96
98
  return row;
97
99
  },
98
- async updateCruise(db, id, data) {
100
+ async updateCruise(db, id, data, runtime = {}) {
99
101
  const [row] = await db
100
102
  .update(cruises)
101
103
  .set({ ...data, ...setUpdated })
102
104
  .where(eq(cruises.id, id))
103
105
  .returning();
104
- if (row)
106
+ if (row) {
105
107
  await reprojectIfPossible(db, row.id);
108
+ await emitCruiseLifecycleEvent(runtime.eventBus, CRUISE_UPDATED_EVENT, { id: row.id });
109
+ }
106
110
  return row ?? null;
107
111
  },
108
- async archiveCruise(db, id) {
112
+ async archiveCruise(db, id, runtime = {}) {
109
113
  const [row] = await db
110
114
  .update(cruises)
111
115
  .set({ status: "archived", ...setUpdated })
112
116
  .where(eq(cruises.id, id))
113
117
  .returning();
114
- if (row)
118
+ if (row) {
115
119
  await reprojectIfPossible(db, row.id);
120
+ await emitCruiseLifecycleEvent(runtime.eventBus, CRUISE_DELETED_EVENT, { id: row.id });
121
+ }
116
122
  return row ?? null;
117
123
  },
118
124
  async recomputeCruiseAggregates(db, cruiseId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/cruises",
3
- "version": "0.40.1",
3
+ "version": "0.41.1",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -89,11 +89,11 @@
89
89
  "drizzle-orm": "^0.45.2",
90
90
  "hono": "^4.12.10",
91
91
  "zod": "^4.3.6",
92
- "@voyantjs/bookings": "0.40.1",
93
- "@voyantjs/core": "0.40.1",
94
- "@voyantjs/db": "0.40.1",
95
- "@voyantjs/hono": "0.40.1",
96
- "@voyantjs/catalog": "0.40.1"
92
+ "@voyantjs/bookings": "0.41.1",
93
+ "@voyantjs/core": "0.41.1",
94
+ "@voyantjs/db": "0.41.1",
95
+ "@voyantjs/hono": "0.41.1",
96
+ "@voyantjs/catalog": "0.41.1"
97
97
  },
98
98
  "devDependencies": {
99
99
  "typescript": "^6.0.2",