@artinstack/migrator 0.1.18 → 0.1.20

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.
@@ -644,6 +644,10 @@ var ALBUM_IMAGES_CONFIG = {
644
644
  }
645
645
  }
646
646
  };
647
+ function asSmugMugList(value) {
648
+ if (value == null) return [];
649
+ return Array.isArray(value) ? value : [value];
650
+ }
647
651
  function oauthPercentEncode(value) {
648
652
  return encodeURIComponent(value).replace(
649
653
  /[!'()*]/g,
@@ -861,6 +865,14 @@ function readSmugMugCredentialsFromEnv(env = process.env) {
861
865
  function sleep(ms) {
862
866
  return new Promise((resolve) => setTimeout(resolve, ms));
863
867
  }
868
+ function resolveSmugMugUriLink(link, label) {
869
+ if (typeof link === "string" && link.trim()) return link.trim();
870
+ if (link && typeof link === "object") {
871
+ const uri = link.Uri;
872
+ if (typeof uri === "string" && uri.trim()) return uri.trim();
873
+ }
874
+ throw new Error(`SmugMug response missing ${label} URI`);
875
+ }
864
876
  function albumKeyFromUri(uri) {
865
877
  const match = uri.match(/\/album\/([^/?!]+)/i);
866
878
  if (!match?.[1]) {
@@ -871,7 +883,8 @@ function albumKeyFromUri(uri) {
871
883
  function mapAlbumImage(albumImage, portfolioSourceId, sort) {
872
884
  const image = albumImage.Image;
873
885
  const metadata = image?.ImageMetadata ?? albumImage.ImageMetadata;
874
- const originalUrl = image?.ImageSizeDetails?.OriginalImageUrl ?? albumImage.LargestImage?.Url ?? albumImage.WebUri;
886
+ const sizeDetails = image?.ImageSizeDetails;
887
+ const originalUrl = sizeDetails?.OriginalImageUrl ?? sizeDetails?.ImageSizeOriginal?.Url ?? albumImage.LargestImage?.Url ?? albumImage.WebUri;
875
888
  const fileName = image?.FileName ?? albumImage.FileName;
876
889
  return {
877
890
  sourceId: albumImage.ImageKey,
@@ -908,10 +921,13 @@ var SmugMugApiClient = class {
908
921
  this.requestIntervalMs = parsed.requestIntervalMs;
909
922
  this.fetchImpl = parsed.fetchImpl ?? fetch;
910
923
  }
911
- /** Validate credentials against `GET /user/!authuser`. */
924
+ /** Validate credentials against `GET /api/v2!authuser`. */
912
925
  async validateCredentials() {
913
926
  const user = await this.getAuthUser();
914
- return { nick: user.NickName, rootNodeUri: user.Uris.Node };
927
+ return {
928
+ nick: user.NickName,
929
+ rootNodeUri: resolveSmugMugUriLink(user.Uris.Node, "User.Uris.Node")
930
+ };
915
931
  }
916
932
  /** Crawl the authenticated user's node tree into flat export tables for `parse-node.ts`. */
917
933
  async crawlExport() {
@@ -919,7 +935,8 @@ var SmugMugApiClient = class {
919
935
  const folders = [];
920
936
  const albums = [];
921
937
  const images = [];
922
- await this.walkNode(user.Uris.Node, void 0, folders, albums, images);
938
+ const rootNodeUri = resolveSmugMugUriLink(user.Uris.Node, "User.Uris.Node");
939
+ await this.walkNode(rootNodeUri, void 0, folders, albums, images);
923
940
  return {
924
941
  exportVersion: 1,
925
942
  exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -929,7 +946,12 @@ var SmugMugApiClient = class {
929
946
  };
930
947
  }
931
948
  async getAuthUser() {
932
- const envelope = await this.requestJson(`${SMUGMUG_API_BASE}/user/!authuser`);
949
+ const envelope = await this.requestJson(
950
+ `${SMUGMUG_API_BASE}!authuser`
951
+ );
952
+ if (envelope.Response.User?.Uris?.Node != null) {
953
+ return envelope.Response.User;
954
+ }
933
955
  return envelope.Response;
934
956
  }
935
957
  async walkNode(nodeUri, parentFolderId, folders, albums, images) {
@@ -956,13 +978,30 @@ var SmugMugApiClient = class {
956
978
  description: child.Description,
957
979
  url: child.WebUri
958
980
  });
959
- const albumUri = child.Uris?.Album;
981
+ const albumUri = await this.resolveAlbumUri(child);
960
982
  if (albumUri) {
961
983
  await this.collectAlbumImages(albumUri, child.NodeID, images);
962
984
  }
963
985
  }
964
986
  }
965
987
  }
988
+ /**
989
+ * Album nodes usually expose `Uris.Album`; if missing, re-fetch the node with `_filteruri=Album`.
990
+ */
991
+ async resolveAlbumUri(child) {
992
+ if (child.Uris?.Album != null) {
993
+ return resolveSmugMugUriLink(child.Uris.Album, "Node.Uris.Album");
994
+ }
995
+ try {
996
+ const envelope = await this.requestJson(`${child.Uri}?_filteruri=Album`);
997
+ const node = envelope.Response.Node ?? envelope.Response;
998
+ if (node.Uris?.Album != null) {
999
+ return resolveSmugMugUriLink(node.Uris.Album, "Node.Uris.Album");
1000
+ }
1001
+ } catch {
1002
+ }
1003
+ return null;
1004
+ }
966
1005
  async collectAlbumImages(albumUri, portfolioSourceId, images) {
967
1006
  const albumKey = albumKeyFromUri(albumUri);
968
1007
  const configQuery = `_config=${encodeURIComponent(JSON.stringify(ALBUM_IMAGES_CONFIG))}`;
@@ -975,14 +1014,14 @@ var SmugMugApiClient = class {
975
1014
  }
976
1015
  async *paginateNodes(path) {
977
1016
  for await (const page of this.paginate(path)) {
978
- for (const node of page.Node ?? []) {
1017
+ for (const node of asSmugMugList(page.Node)) {
979
1018
  yield node;
980
1019
  }
981
1020
  }
982
1021
  }
983
1022
  async *paginateAlbumImages(path) {
984
1023
  for await (const page of this.paginate(path)) {
985
- for (const albumImage of page.AlbumImage ?? []) {
1024
+ for (const albumImage of asSmugMugList(page.AlbumImage)) {
986
1025
  yield albumImage;
987
1026
  }
988
1027
  }
@@ -995,17 +1034,37 @@ var SmugMugApiClient = class {
995
1034
  nextPath = envelope.Response.Pages?.NextPage;
996
1035
  }
997
1036
  }
1037
+ /**
1038
+ * OAuth-signed GET for private original/media URLs (and API endpoints).
1039
+ * Hosts should prefer this over anonymous `fetch` when downloading vault images.
1040
+ */
1041
+ async fetchAuthenticated(url, options) {
1042
+ return this.requestWithRetry(toAbsoluteUrl(url), {
1043
+ accept: options?.accept ?? "*/*"
1044
+ });
1045
+ }
1046
+ /** Convenience: signed GET → bytes (for MigrationSink `resolveAssetStream`). */
1047
+ async fetchAuthenticatedBytes(url) {
1048
+ const response = await this.fetchAuthenticated(url);
1049
+ const buffer = new Uint8Array(await response.arrayBuffer());
1050
+ return {
1051
+ body: buffer,
1052
+ contentType: response.headers.get("content-type") ?? void 0,
1053
+ contentLength: buffer.byteLength
1054
+ };
1055
+ }
998
1056
  async requestJson(pathOrUrl) {
999
1057
  const url = toAbsoluteUrl(pathOrUrl);
1000
- const response = await this.requestWithRetry(url);
1058
+ const response = await this.requestWithRetry(url, { accept: "application/json" });
1001
1059
  const body = await response.json();
1002
1060
  if (body.Code !== 200) {
1003
1061
  throw new Error(`SmugMug API error ${body.Code}: ${body.Message}`);
1004
1062
  }
1005
1063
  return body;
1006
1064
  }
1007
- async requestWithRetry(url) {
1065
+ async requestWithRetry(url, options) {
1008
1066
  let attempt = 0;
1067
+ const accept = options?.accept ?? "application/json";
1009
1068
  while (true) {
1010
1069
  await this.throttle();
1011
1070
  const authorization = buildSmugMugAuthorizationHeader({
@@ -1016,7 +1075,7 @@ var SmugMugApiClient = class {
1016
1075
  const response = await this.fetchImpl(url, {
1017
1076
  method: "GET",
1018
1077
  headers: {
1019
- Accept: "application/json",
1078
+ Accept: accept,
1020
1079
  Authorization: authorization
1021
1080
  }
1022
1081
  });
@@ -2701,6 +2760,7 @@ export {
2701
2760
  SMUGMUG_OAUTH_ENDPOINTS,
2702
2761
  smugMugConsumerCredentialsSchema,
2703
2762
  smugMugCredentialsSchema,
2763
+ asSmugMugList,
2704
2764
  signSmugMugOAuthRequest,
2705
2765
  buildSmugMugAuthorizationHeader,
2706
2766
  parseSmugMugOAuthFormBody,
@@ -2709,6 +2769,7 @@ export {
2709
2769
  getSmugMugAccessToken,
2710
2770
  createSmugMugOAuthSession,
2711
2771
  readSmugMugCredentialsFromEnv,
2772
+ resolveSmugMugUriLink,
2712
2773
  SmugMugApiClient,
2713
2774
  smugmugAdapter,
2714
2775
  squarespaceAdapter,
@@ -2717,4 +2778,4 @@ export {
2717
2778
  wixAdapter,
2718
2779
  getAdapter
2719
2780
  };
2720
- //# sourceMappingURL=chunk-PZADAGBA.js.map
2781
+ //# sourceMappingURL=chunk-CG5ON2RW.js.map