riverbankcms 0.16.0 → 0.17.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.
@@ -90,8 +90,11 @@ Create `src/app/api/preview/keep-alive/route.ts`:
90
90
  import type { NextRequest } from 'next/server'
91
91
 
92
92
  import { createPreviewKeepAliveRouteHandler } from '@riverbankcms/sdk/preview-next/keep-alive'
93
+ import { getCmsConfig } from '@/lib/env'
93
94
 
94
- const handler = createPreviewKeepAliveRouteHandler()
95
+ const handler = createPreviewKeepAliveRouteHandler({
96
+ dashboardUrl: getCmsConfig().dashboardUrl,
97
+ })
95
98
 
96
99
  export function GET(request: NextRequest) {
97
100
  return handler(request)
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as classifySdkEventSchedule, i as sdkEventUpsertOptionalFieldSchemas, n as sdkCourseBookingSettingsSchema, o as classifySdkEventBookingOffer } from "./sdk-event-upsert-TBJhbame.mjs";
3
- import { l as EVENT_PRESET_VALUES } from "./src-DxiCPcDa.mjs";
2
+ import { a as classifySdkEventSchedule, i as sdkEventUpsertOptionalFieldSchemas, n as sdkCourseBookingSettingsSchema, o as classifySdkEventBookingOffer } from "./sdk-event-upsert-JKXlp2Ct.mjs";
3
+ import { u as EVENT_PRESET_VALUES } from "./src-JsJh7WAb.mjs";
4
4
  import { z } from "zod";
5
5
 
6
6
  //#region ../blocks/src/lib/typeGuards.ts
@@ -269,6 +269,7 @@ function resolveContentTitle(content, titleField, fallbackTitle) {
269
269
  //#endregion
270
270
  //#region ../content-model/src/metadataConfig.ts
271
271
  const ROUTE_METADATA_MANIFEST = {
272
+ variantMeaning: { kind: "arrangement" },
272
273
  name: "content-entry-metadata",
273
274
  version: "1.0.0",
274
275
  title: "Details",
@@ -3271,11 +3272,11 @@ const DEFAULT_TEMPLATE_DEFINITIONS = {
3271
3272
  blockKind: "block.event-registration",
3272
3273
  scope: "entry",
3273
3274
  content: {
3274
- heading: "Register for this event",
3275
+ heading: "",
3275
3276
  maxTickets: "5",
3276
3277
  showVenue: true,
3277
3278
  showCapacity: true,
3278
- buttonText: "Complete Registration",
3279
+ buttonText: "Book your place",
3279
3280
  buttonVariant: "primary"
3280
3281
  },
3281
3282
  bindings: {
@@ -3344,7 +3345,23 @@ const DEFAULT_TEMPLATE_DEFINITIONS = {
3344
3345
  }]
3345
3346
  }
3346
3347
  };
3347
- function getDefaultTemplateDefinition(contentTypeKey) {
3348
+ /** The legacy definition stays untouched; this alternative is selected only at creation. */
3349
+ const EVENT_HEADER_TEMPLATE_DEFINITION = {
3350
+ ...DEFAULT_TEMPLATE_DEFINITIONS.event,
3351
+ blocks: [{
3352
+ blockKind: "block.event-details",
3353
+ scope: "entry",
3354
+ content: {
3355
+ heading: "",
3356
+ showVenue: true,
3357
+ showMap: true,
3358
+ showOtherDates: true,
3359
+ otherDatesLimit: "5"
3360
+ }
3361
+ }, ...DEFAULT_TEMPLATE_DEFINITIONS.event.blocks.slice(2)]
3362
+ };
3363
+ function getDefaultTemplateDefinition(contentTypeKey, defaults) {
3364
+ if (contentTypeKey === "event" && defaults?.event === "event-header") return EVENT_HEADER_TEMPLATE_DEFINITION;
3348
3365
  return DEFAULT_TEMPLATE_DEFINITIONS[contentTypeKey] ?? null;
3349
3366
  }
3350
3367
  const DEFAULT_TEMPLATE_KEYS = Object.freeze(Object.keys(DEFAULT_TEMPLATE_DEFINITIONS));
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { t as loadConfigFile } from "./load-config-DR5qJgmx.mjs";
3
+
4
+ export { loadConfigFile };
@@ -7,7 +7,7 @@ import { existsSync as existsSync$1 } from "fs";
7
7
  import { fileURLToPath } from "url";
8
8
 
9
9
  //#region package.json
10
- var version = "0.16.0";
10
+ var version = "0.17.1";
11
11
  var peerDependencies = { "@riverbankcms/sdk": ">=0.139.0 <1.0.0" };
12
12
  var package_default = {
13
13
  name: "riverbankcms",
@@ -83,7 +83,7 @@ var package_default = {
83
83
  //#endregion
84
84
  //#region src/version.ts
85
85
  /** The package manifest is the source-run fallback; builds inject this value. */
86
- const CLI_VERSION = typeof "0.16.0" === "string" ? "0.16.0" : version;
86
+ const CLI_VERSION = typeof "0.17.1" === "string" ? "0.17.1" : version;
87
87
  /**
88
88
  * The CLI's SDK peer range is bundled from the CLI manifest alongside its
89
89
  * version. Runtime config loading uses this value as the single compatibility
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { T as eventBookingOfferIntentSchema, br as asPageFlowPresetId, d as stableStringify, in as buttonStyleSelectionSchema, n as sdkCourseBookingSettingsSchema, nt as themeSchema, on as pageDesignBoundaryTreatmentSchema, r as sdkEventOccurrenceInputSchema, s as eventVisibilitySchema, sn as pageDesignIntentSchemaVersion, vr as asAppearancePresetId, xr as asSiteStyleId } from "./sdk-event-upsert-TBJhbame.mjs";
3
- import { $t as unsafeAsEventSeriesId, Gt as unsafeAsBookingSessionId, Qt as unsafeAsEventOfferingId, St as mapNonEmptyArray, Vt as parseSiteId, Wt as unsafeAsBookingRequirementId, Xt as unsafeAsEntitlementId, Y as parseUtcIsoDateTimeString, Yt as unsafeAsDetectedMimeType, _n as unsafeAsUserId, a as parseVerticalId, an as unsafeAsPositiveByteCount, cn as unsafeAsProductId, dn as unsafeAsSiteId, en as unsafeAsFileChecksumSha256, fn as unsafeAsSiteScopedStoragePath, gn as unsafeAsUploadSessionId, hn as unsafeAsStoragePath, i as canonicalizeConfiguredBookingVerticalConfig, j as parseFooterCompositionV1, jt as assertNever, l as EVENT_PRESET_VALUES, ln as unsafeAsSafeDownloadFilename, mn as unsafeAsStorageBucketName, nn as unsafeAsOccurrenceId, o as parseVerticalPresetId, on as unsafeAsPositiveSeconds, qt as unsafeAsDeclaredMimeType, rn as unsafeAsOriginalFilename, s as resolveSiteVerticalBookingConfig, tn as unsafeAsMediaAssetId, un as unsafeAsSiteFontId, v as exposeSecret, vn as err, xt as fromArray, yn as ok } from "./src-DxiCPcDa.mjs";
2
+ import { T as eventBookingOfferIntentSchema, ar as asSiteStyleId, bn as pageDesignBoundaryTreatmentSchema, d as stableStringify, ft as themeSchema, hn as buttonStyleSelectionSchema, ir as asPageFlowPresetId, n as sdkCourseBookingSettingsSchema, r as sdkEventOccurrenceInputSchema, s as eventVisibilitySchema, tr as asAppearancePresetId, xn as pageDesignIntentSchemaVersion } from "./sdk-event-upsert-JKXlp2Ct.mjs";
3
+ import { Cn as unsafeAsUploadSessionId, Dt as mapNonEmptyArray, En as ok, Et as fromArray, Jt as parseSiteId, Lt as assertNever, Q as parseUtcIsoDateTimeString, Qt as unsafeAsBookingSessionId, Sn as unsafeAsStoragePath, Tn as err, Zt as unsafeAsBookingRequirementId, _n as unsafeAsSiteFontId, a as canonicalizeConfiguredBookingVerticalConfig, an as unsafeAsEventOfferingId, c as resolveSiteVerticalBookingConfig, cn as unsafeAsMediaAssetId, en as unsafeAsDeclaredMimeType, fn as unsafeAsPositiveByteCount, gn as unsafeAsSafeDownloadFilename, hn as unsafeAsProductId, j as parseFooterCompositionV1, l as DEFAULT_EVENT_TYPE_PRESETS, ln as unsafeAsOccurrenceId, nn as unsafeAsDetectedMimeType, o as parseVerticalId, on as unsafeAsEventSeriesId, pn as unsafeAsPositiveSeconds, rn as unsafeAsEntitlementId, s as parseVerticalPresetId, sn as unsafeAsFileChecksumSha256, t as resolveEventCatalogue, u as EVENT_PRESET_VALUES, un as unsafeAsOriginalFilename, vn as unsafeAsSiteId, wn as unsafeAsUserId, xn as unsafeAsStorageBucketName, y as exposeSecret, yn as unsafeAsSiteScopedStoragePath } from "./src-JsJh7WAb.mjs";
4
4
  import * as fs$1 from "fs/promises";
5
5
  import * as path$1 from "path";
6
6
  import { z } from "zod";
@@ -215,7 +215,59 @@ const sdkListEventOccurrencesResponseSchema = z.object({
215
215
  occurrences: z.array(sdkManagementEventOccurrenceSchema),
216
216
  nextCursor: z.string().nullable()
217
217
  });
218
- const sdkEventOccurrenceResponseSchema = z.object({ occurrence: sdkManagementEventOccurrenceSchema });
218
+ const businessCancellationResultSchema = z.object({
219
+ kind: z.enum([
220
+ "finished",
221
+ "unfinished",
222
+ "partial_application"
223
+ ]),
224
+ paymentTerms: z.enum([
225
+ "complete",
226
+ "unfinished",
227
+ "unavailable"
228
+ ]),
229
+ refunds: z.enum([
230
+ "complete",
231
+ "processing",
232
+ "needs_attention",
233
+ "unavailable"
234
+ ]),
235
+ emails: z.enum([
236
+ "complete",
237
+ "queued",
238
+ "needs_attention",
239
+ "unavailable"
240
+ ]),
241
+ operationId: identifierSchema$1,
242
+ directContactAttendeeIds: z.array(identifierSchema$1),
243
+ cancelledBookings: z.number().int().nonnegative(),
244
+ refundedBookings: z.number().int().nonnegative(),
245
+ restoredPassCredits: z.number().int().nonnegative(),
246
+ pending: z.object({
247
+ refundAttendeeIds: z.array(identifierSchema$1),
248
+ creditAttendeeIds: z.array(identifierSchema$1),
249
+ emailAttendeeIds: z.array(identifierSchema$1)
250
+ }),
251
+ structural: z.object({
252
+ created: z.number().int().nonnegative(),
253
+ deleted: z.number().int().nonnegative(),
254
+ cancelled: z.number().int().nonnegative(),
255
+ unfinishedOccurrenceIds: z.array(identifierSchema$1),
256
+ unfinishedCreateCount: z.number().int().nonnegative().optional(),
257
+ reason: z.enum([
258
+ "superseded",
259
+ "not_found",
260
+ "restore_blocked",
261
+ "failed",
262
+ "result_unavailable"
263
+ ]).optional()
264
+ })
265
+ });
266
+ const sdkEventOccurrenceResponseSchema = z.object({
267
+ occurrence: sdkManagementEventOccurrenceSchema,
268
+ cancellationResult: businessCancellationResultSchema.optional(),
269
+ cancellationResults: z.array(businessCancellationResultSchema).optional()
270
+ });
219
271
  const sdkDeleteEventOccurrenceResponseSchema = z.object({ deleted: z.literal(true) });
220
272
  const sdkReorderResponseSchema = z.object({
221
273
  reordered: z.literal(true),
@@ -932,6 +984,200 @@ const sdkPullEventsResponseSchema = z.object({
932
984
  meta: sdkPullMetaSchema
933
985
  });
934
986
 
987
+ //#endregion
988
+ //#region ../api/src/bookingSettingsSections.ts
989
+ const BOOKING_SETTINGS_SECTION_LABELS = {
990
+ basics: "Booking basics",
991
+ payments: "Payments",
992
+ events: "Events",
993
+ appointments: "Appointments",
994
+ messages: "Messages"
995
+ };
996
+ const BOOKING_SETTINGS_SECTION_KEYS = {
997
+ basics: [
998
+ "modules",
999
+ "verticalConfig",
1000
+ "eventTypePresets",
1001
+ "personRecordMode",
1002
+ "defaultTimezone",
1003
+ "calendarDisplay",
1004
+ "icsFeedsEnabled",
1005
+ "waitlists",
1006
+ "teamMemberProfilePages"
1007
+ ],
1008
+ payments: [
1009
+ "paymentDefaults",
1010
+ "currency",
1011
+ "refundsIssueCredit"
1012
+ ],
1013
+ events: [
1014
+ "defaultVenueId",
1015
+ "eventReminderHours",
1016
+ "eventReminderOverrides",
1017
+ "eventScheduleWindowMonths",
1018
+ "eventOrganizerNotifications",
1019
+ "eventOrganizerEmail",
1020
+ "eventTicketingDefaultEnabled",
1021
+ "collectAllAttendeeDetails",
1022
+ "eventTypeBookingDefaults",
1023
+ "eventBookingPolicies",
1024
+ "defaultEventImage",
1025
+ "eventDestinationPolicy"
1026
+ ],
1027
+ appointments: [
1028
+ "appointmentReminderHours",
1029
+ "appointmentReminderOverrides",
1030
+ "appointmentRescheduleHours",
1031
+ "appointmentDefaultMinLeadMinutes",
1032
+ "appointmentDefaultMaxBookingNoticeMinutes",
1033
+ "appointmentBookingPolicies"
1034
+ ],
1035
+ messages: [
1036
+ "remindersEnabled",
1037
+ "notificationSettings",
1038
+ "appointmentStaffReminder",
1039
+ "appointmentStaffChangeNotifications",
1040
+ "eventFeedback",
1041
+ "appointmentFollowUp"
1042
+ ]
1043
+ };
1044
+
1045
+ //#endregion
1046
+ //#region ../api/src/event-destination-policy.ts
1047
+ const eventDestinationModeValues = ["cms-managed", "system-managed"];
1048
+ const eventDestinationPresetValues = EVENT_PRESET_VALUES;
1049
+ const DEFAULT_EVENT_DESTINATION_POLICY = {
1050
+ defaultMode: "cms-managed",
1051
+ presetOverrides: {}
1052
+ };
1053
+ function isEventDestinationMode(value) {
1054
+ return eventDestinationModeValues.some((mode) => mode === value);
1055
+ }
1056
+ function resolveEventDestinationMode(input) {
1057
+ const policy = input.policy ?? DEFAULT_EVENT_DESTINATION_POLICY;
1058
+ if (input.preset) {
1059
+ const presetOverride = policy.presetOverrides[input.preset];
1060
+ if (presetOverride) return presetOverride;
1061
+ }
1062
+ if (input.publicExperience === "hosted_booking") return "system-managed";
1063
+ return policy.defaultMode;
1064
+ }
1065
+ function macadamiaDefaultEventDestinationPolicy(selectedPresets) {
1066
+ const presetOverrides = {};
1067
+ for (const preset of selectedPresets) {
1068
+ presetOverrides[preset] = preset === "class" ? "system-managed" : "cms-managed";
1069
+ }
1070
+ return {
1071
+ defaultMode: "cms-managed",
1072
+ presetOverrides
1073
+ };
1074
+ }
1075
+
1076
+ //#endregion
1077
+ //#region ../api/src/bookingConfig.ts
1078
+ /** Every option kind; the checkout routes validate the requested kind against it. */
1079
+ const DIRECT_CHECKOUT_COLLECTION_OPTION_KINDS = [
1080
+ "upfront",
1081
+ "deferred_manual",
1082
+ "instalment_plan",
1083
+ "flexible_balance"
1084
+ ];
1085
+ const eventOccurrenceRestorationSummarySchema = z.object({
1086
+ restoredOccurrenceIds: z.array(z.string().uuid()),
1087
+ excludedBookings: z.array(z.object({
1088
+ attendeeId: z.string().uuid(),
1089
+ occurrenceId: z.string().uuid(),
1090
+ customerId: z.string().uuid(),
1091
+ customerName: z.string().nullable(),
1092
+ customerEmail: z.string().nullable()
1093
+ }))
1094
+ });
1095
+ const DEFAULT_BOOKING_NOTIFICATION_SEND_RULE = {
1096
+ kind: "send",
1097
+ sendToAssignedStaff: true,
1098
+ extraEmail: null,
1099
+ sendToSiteOwnerIfNoStaff: true
1100
+ };
1101
+ const DEFAULT_BOOKING_NOTIFICATION_SETTINGS = {
1102
+ state: "configured",
1103
+ defaultRule: DEFAULT_BOOKING_NOTIFICATION_SEND_RULE,
1104
+ overrides: {}
1105
+ };
1106
+ const DEFAULT_APPOINTMENT_STAFF_DIGEST_LOCAL_SEND_TIME = "07:00";
1107
+ const BOOKING_SETTINGS_SECTIONS = [
1108
+ "basics",
1109
+ "payments",
1110
+ "events",
1111
+ "appointments",
1112
+ "messages"
1113
+ ];
1114
+ const unownedBookingSettingsKeys = {};
1115
+ void unownedBookingSettingsKeys;
1116
+ function pickBookingSettingsSection(settings, section) {
1117
+ const entries = BOOKING_SETTINGS_SECTION_KEYS[section].map((key) => [key, settings[key]]);
1118
+ return Object.fromEntries(entries);
1119
+ }
1120
+ function completeBookingSettingsForSectionUpdate(settings) {
1121
+ return {
1122
+ ...settings,
1123
+ eventTypePresets: settings.eventTypePresets ?? DEFAULT_EVENT_TYPE_PRESETS,
1124
+ verticalConfig: settings.verticalConfig ?? {
1125
+ verticalId: null,
1126
+ selectedPresetIds: []
1127
+ },
1128
+ teamMemberProfilePages: settings.teamMemberProfilePages ?? { policy: "selected" },
1129
+ eventDestinationPolicy: settings.eventDestinationPolicy ?? {
1130
+ defaultMode: "cms-managed",
1131
+ presetOverrides: {}
1132
+ },
1133
+ eventTypeBookingDefaults: settings.eventTypeBookingDefaults ?? {}
1134
+ };
1135
+ }
1136
+ function createBookingSettingsSectionUpdate(settings, section) {
1137
+ const completeSettings = completeBookingSettingsForSectionUpdate(settings);
1138
+ switch (section) {
1139
+ case "basics": return {
1140
+ section,
1141
+ settings: pickBookingSettingsSection(completeSettings, section)
1142
+ };
1143
+ case "payments": return {
1144
+ section,
1145
+ settings: pickBookingSettingsSection(completeSettings, section)
1146
+ };
1147
+ case "events": return {
1148
+ section,
1149
+ settings: pickBookingSettingsSection(completeSettings, section)
1150
+ };
1151
+ case "appointments": return {
1152
+ section,
1153
+ settings: pickBookingSettingsSection(completeSettings, section)
1154
+ };
1155
+ case "messages": return {
1156
+ section,
1157
+ settings: pickBookingSettingsSection(completeSettings, section)
1158
+ };
1159
+ }
1160
+ }
1161
+ function createBookingSettingsUpdateRequest(settings, sections = BOOKING_SETTINGS_SECTIONS) {
1162
+ return { updates: sections.map((section) => createBookingSettingsSectionUpdate(settings, section)) };
1163
+ }
1164
+ function applyBookingSettingsSectionUpdate(settings, update) {
1165
+ return {
1166
+ ...settings,
1167
+ ...update.settings
1168
+ };
1169
+ }
1170
+ function applyBookingSettingsSectionUpdates(settings, updates) {
1171
+ return updates.reduce(applyBookingSettingsSectionUpdate, settings);
1172
+ }
1173
+ /** Normalize optional wire defaults once before projecting operational Event policy. */
1174
+ function resolveBookingSettingsEventCatalogue(settings) {
1175
+ return resolveEventCatalogue({
1176
+ verticalConfig: settings.verticalConfig,
1177
+ eventTypePresets: settings.eventTypePresets ?? DEFAULT_EVENT_TYPE_PRESETS
1178
+ });
1179
+ }
1180
+
935
1181
  //#endregion
936
1182
  //#region ../api/src/sdkManagementResponseSchemas.ts
937
1183
  const sdkIdentifierSchema = z.string().min(1);
@@ -1065,7 +1311,15 @@ const sdkTakeEventOfflineResponseSchema = z.union([
1065
1311
  }),
1066
1312
  z.object({ kind: z.literal("already-offline") })
1067
1313
  ]);
1068
- const sdkUpsertEventResponseSchema = sdkCommandApplicationResultSchema(sdkManagementEventGraphSchema);
1314
+ const sdkUpsertEventResponseSchema = z.union([sdkCommandApplicationResultSchema(sdkManagementEventGraphSchema).extend({
1315
+ cancellationResult: businessCancellationResultSchema.optional(),
1316
+ restoration: eventOccurrenceRestorationSummarySchema.optional()
1317
+ }), z.object({
1318
+ kind: z.literal("accepted_unavailable"),
1319
+ id: sdkEventSeriesIdSchema,
1320
+ effect: z.object({ kind: z.literal("updated") }),
1321
+ cancellationResult: businessCancellationResultSchema
1322
+ })]);
1069
1323
  const sdkUpsertThemeResponseSchema = sdkCommandApplicationResultSchema(z.object({
1070
1324
  themeId: sdkIdentifierSchema,
1071
1325
  selectionId: sdkIdentifierSchema,
@@ -2573,6 +2827,31 @@ function normalizeDownloadFilename(value) {
2573
2827
  return value.trim().replace(/\s+/gu, " ");
2574
2828
  }
2575
2829
 
2830
+ //#endregion
2831
+ //#region ../media-core/src/storageObjectKey.ts
2832
+ const STORAGE_OBJECT_CHARACTER = "A-Za-z0-9_!.*'() &$=@;:+,-";
2833
+ const unsupportedFilenameCharacters = new RegExp(`[^${STORAGE_OBJECT_CHARACTER}]+`, "gu");
2834
+ const supportedObjectKey = new RegExp(`^[/${STORAGE_OBJECT_CHARACTER}]+$`, "u");
2835
+ /** Validate without trimming or rewriting: supplied keys are object identities. */
2836
+ function parseWritableStoragePath(value) {
2837
+ if (typeof value !== "string" || value.split("/").some(isUnsafePathSegment)) {
2838
+ return err("unsafe-path-segment");
2839
+ }
2840
+ if (!supportedObjectKey.test(value)) return err("unsupported-storage-character");
2841
+ return ok(value);
2842
+ }
2843
+ /** Choose a new internal filename; never use this to rewrite an existing key. */
2844
+ function storageObjectFilename(filename, maxLength = 255) {
2845
+ const converted = filename.trim().normalize("NFKD").replace(/[\u0300-\u036f]/gu, "").replace(unsupportedFilenameCharacters, "-").trim();
2846
+ const usable = isUnsafePathSegment(converted) ? "upload" : converted;
2847
+ const extension = /\.[A-Za-z0-9]{1,16}$/u.exec(usable)?.[0] ?? "";
2848
+ const stem = extension.length > 0 ? usable.slice(0, -extension.length) : usable;
2849
+ const validStem = /[A-Za-z0-9]/u.test(stem) || converted === filename.trim() ? stem : "upload";
2850
+ const shortenedStem = validStem.slice(0, Math.max(1, maxLength - extension.length)).trim();
2851
+ const shortenedFilename = `${shortenedStem || "upload"}${extension}`;
2852
+ return isUnsafePathSegment(shortenedFilename) ? "upload" : shortenedFilename;
2853
+ }
2854
+
2576
2855
  //#endregion
2577
2856
  //#region ../media-core/src/fileStorage.ts
2578
2857
  const FILE_STORAGE_BUCKETS = {
@@ -2650,31 +2929,29 @@ function storageBucketForStorageClass(storageClass) {
2650
2929
  }
2651
2930
  /**
2652
2931
  * Builds the `temp-uploads` object path allowed by Storage RLS:
2653
- * `temp-file-uploads/{userId}/{siteId}/{uploadSessionId}/{safeDownloadFilename}`.
2932
+ * `temp-file-uploads/{userId}/{siteId}/{uploadSessionId}/{storageObjectFilename}`.
2654
2933
  */
2655
2934
  function buildStagedUploadStoragePath(input) {
2656
2935
  const segments = [
2657
2936
  FILE_UPLOAD_STAGING_PREFIX,
2658
2937
  input.userId,
2659
2938
  input.siteId,
2660
- input.uploadSessionId,
2661
- input.safeDownloadFilename
2939
+ input.uploadSessionId
2662
2940
  ];
2663
- return joinStoragePathSegments(segments);
2941
+ return joinNewFileStoragePathSegments(segments, input.safeDownloadFilename);
2664
2942
  }
2665
2943
  /**
2666
2944
  * Builds the durable site-scoped object path allowed by public/private site-file RLS:
2667
- * `sites/{siteId}/files/{fileAssetId}/{safeDownloadFilename}`.
2945
+ * `sites/{siteId}/files/{fileAssetId}/{storageObjectFilename}`.
2668
2946
  */
2669
2947
  function buildDurableFileStoragePath(input) {
2670
2948
  const segments = [
2671
2949
  DURABLE_FILE_STORAGE_PREFIX,
2672
2950
  input.siteId,
2673
2951
  DURABLE_FILE_FOLDER,
2674
- input.fileAssetId,
2675
- input.safeDownloadFilename
2952
+ input.fileAssetId
2676
2953
  ];
2677
- const path$2 = joinStoragePathSegments(segments);
2954
+ const path$2 = joinNewFileStoragePathSegments(segments, input.safeDownloadFilename);
2678
2955
  if (!path$2.ok) return path$2;
2679
2956
  return ok(unsafeAsSiteScopedStoragePath(path$2.value));
2680
2957
  }
@@ -2688,12 +2965,21 @@ function parseStoragePath(value) {
2688
2965
  * after verification + sanitization.
2689
2966
  */
2690
2967
  function buildSdkStagedStoragePath(siteId, durableRelativePath) {
2691
- return joinStoragePathSegments([
2968
+ return joinWritableStoragePathSegments([
2692
2969
  SDK_UPLOAD_STAGING_PREFIX,
2693
2970
  siteId,
2694
2971
  ...durableRelativePath.split("/")
2695
2972
  ]);
2696
2973
  }
2974
+ function joinWritableStoragePathSegments(segments) {
2975
+ if (segments.some(isUnsafePathSegment)) return err("unsafe-path-segment");
2976
+ const path$2 = parseWritableStoragePath(segments.join("/"));
2977
+ return path$2.ok ? ok(unsafeAsStoragePath(path$2.value)) : path$2;
2978
+ }
2979
+ function joinNewFileStoragePathSegments(prefix, filename) {
2980
+ if (isUnsafePathSegment(filename)) return err("unsafe-path-segment");
2981
+ return joinWritableStoragePathSegments([...prefix, storageObjectFilename(filename)]);
2982
+ }
2697
2983
  /**
2698
2984
  * Joins already-branded path segments while preserving the Storage RLS invariant
2699
2985
  * that every tenant/site identifier remains one segment, never a nested path.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Vt as parseSiteId, vn as err, yn as ok } from "./src-DxiCPcDa.mjs";
2
+ import { En as ok, Jt as parseSiteId, Tn as err } from "./src-JsJh7WAb.mjs";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import "../src-DxiCPcDa.mjs";
3
- import { a as prepareScaffoldExecution, d as loadGeneratedPackageRanges, i as preflightScaffoldTargets, l as deriveGeneratedPackageRanges, o as writeScaffoldFiles, r as preflightScaffoldPlan, s as deriveScaffoldLocalManifest, t as deriveScaffoldPlan, u as findPackageManifestPath } from "../plan-kJ-LMUsD.mjs";
2
+ import "../src-JsJh7WAb.mjs";
3
+ import { a as prepareScaffoldExecution, d as loadGeneratedPackageRanges, i as preflightScaffoldTargets, l as deriveGeneratedPackageRanges, o as writeScaffoldFiles, r as preflightScaffoldPlan, s as deriveScaffoldLocalManifest, t as deriveScaffoldPlan, u as findPackageManifestPath } from "../plan-BreyfN6E.mjs";
4
4
 
5
5
  export { deriveGeneratedPackageRanges, deriveScaffoldLocalManifest, deriveScaffoldPlan, findPackageManifestPath, loadGeneratedPackageRanges, preflightScaffoldPlan, preflightScaffoldTargets, prepareScaffoldExecution, writeScaffoldFiles };