@artinstack/migrator 0.1.17 → 0.1.18

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 CHANGED
@@ -4,6 +4,15 @@ Stateless content normalizer and migration framework for transforming **WordPres
4
4
 
5
5
  Portable parsers and JSON export are useful without any specific host. Job orchestration, credentials, and UI are implemented separately via `MigrationSink`.
6
6
 
7
+ ## Used by [ArtInStack](https://www.artinstack.com) (as an optional host)
8
+
9
+ **MIT-licensed OSS** — use the CLI, export normalized JSON, or implement `MigrationSink` for **any** CMS or product. No ArtInStack account required.
10
+
11
+ [ArtInStack](https://www.artinstack.com) is one production integration: the creative operating system for photographers and studios (websites, portfolios, client delivery, commerce, and courses). The platform’s import UI runs this package under the hood.
12
+
13
+ - **Any host** — parsers, normalizer DTOs, conflict analysis, `MigrationSink` contract, and `artinstack-migrate` CLI (dry-run, filesystem export, custom sinks).
14
+ - **ArtInStack product** — workspace owners use **Settings → Import Site**: scan WordPress WXR, preview entities, confirm import, review reports. [Customer docs](https://www.artinstack.com/docs/platform-migration/platform-migration-overview) cover export formats, SEO redirects, and cutover.
15
+
7
16
  See [docs/architecture.md](./docs/architecture.md) for the high-level blueprint: data flow, DTOs, sink contract, and source mappings.
8
17
 
9
18
  ## Package layout
@@ -192,7 +201,7 @@ pnpm dev # watch build
192
201
  | CLI + filesystem JSON export | Yes | No |
193
202
  | `MigrationSink` interface | Yes | Implementation |
194
203
  | Map `data-wp-widget` stubs → platform blocks; populate slider heroes from hints | No | Yes |
195
- | Jobs, worker, credentials, UI | No | Yes |
204
+ | Jobs, worker, credentials, UI | No | Your host — [example: ArtInStack Import Site](https://www.artinstack.com/docs/platform-migration/platform-migration-overview) |
196
205
 
197
206
  ## License
198
207
 
@@ -607,9 +607,11 @@ var SMUGMUG_OAUTH_ENDPOINTS = {
607
607
  authorize: "https://api.smugmug.com/services/oauth/1.0a/authorize",
608
608
  accessToken: "https://api.smugmug.com/services/oauth/1.0a/getAccessToken"
609
609
  };
610
- var smugMugCredentialsSchema = z.object({
610
+ var smugMugConsumerCredentialsSchema = z.object({
611
611
  consumerKey: z.string().min(1),
612
- consumerSecret: z.string().min(1),
612
+ consumerSecret: z.string().min(1)
613
+ });
614
+ var smugMugCredentialsSchema = smugMugConsumerCredentialsSchema.extend({
613
615
  accessToken: z.string().min(1),
614
616
  accessTokenSecret: z.string().min(1)
615
617
  });
@@ -680,23 +682,28 @@ function signSmugMugOAuthRequest(input) {
680
682
  oauthPercentEncode(normalizeRequestUrl(url)),
681
683
  oauthPercentEncode(parameterString)
682
684
  ].join("&");
683
- const signingKey = `${oauthPercentEncode(input.credentials.consumerSecret)}&${oauthPercentEncode(input.credentials.accessTokenSecret)}`;
685
+ const tokenSecret = input.credentials.accessTokenSecret ?? "";
686
+ const signingKey = `${oauthPercentEncode(input.credentials.consumerSecret)}&${oauthPercentEncode(tokenSecret)}`;
684
687
  return createHmac("sha1", signingKey).update(signatureBase).digest("base64");
685
688
  }
686
- function buildOAuthParams(credentials, nonce, timestamp) {
687
- return {
689
+ function buildOAuthParams(credentials, nonce, timestamp, extras) {
690
+ const params = {
688
691
  oauth_consumer_key: credentials.consumerKey,
689
- oauth_token: credentials.accessToken,
690
692
  oauth_signature_method: "HMAC-SHA1",
691
693
  oauth_timestamp: timestamp,
692
694
  oauth_nonce: nonce,
693
- oauth_version: "1.0"
695
+ oauth_version: "1.0",
696
+ ...extras ?? {}
694
697
  };
698
+ if (credentials.accessToken) {
699
+ params.oauth_token = credentials.accessToken;
700
+ }
701
+ return params;
695
702
  }
696
703
  function buildSmugMugAuthorizationHeader(input) {
697
704
  const nonce = input.nonce ?? randomBytes(16).toString("hex");
698
705
  const timestamp = input.timestamp ?? String(Math.floor(Date.now() / 1e3));
699
- const oauthParams = buildOAuthParams(input.credentials, nonce, timestamp);
706
+ const oauthParams = buildOAuthParams(input.credentials, nonce, timestamp, input.oauthExtras);
700
707
  const signature = signSmugMugOAuthRequest({
701
708
  method: input.method,
702
709
  url: input.url,
@@ -708,6 +715,141 @@ function buildSmugMugAuthorizationHeader(input) {
708
715
  const headerValue = Object.keys(headerParams).sort().map((key) => `${oauthPercentEncode(key)}="${oauthPercentEncode(headerParams[key])}"`).join(", ");
709
716
  return `OAuth ${headerValue}`;
710
717
  }
718
+ function parseSmugMugOAuthFormBody(body) {
719
+ const params = new URLSearchParams(body.trim());
720
+ const out = {};
721
+ params.forEach((value, key) => {
722
+ out[key] = value;
723
+ });
724
+ return out;
725
+ }
726
+ function requireOAuthFormField(fields, name) {
727
+ const value = fields[name]?.trim();
728
+ if (!value) {
729
+ throw new Error(`SmugMug OAuth response missing ${name}`);
730
+ }
731
+ return value;
732
+ }
733
+ async function postSmugMugOAuthTokenEndpoint(input) {
734
+ const fetchImpl = input.fetchImpl ?? fetch;
735
+ const authorization = buildSmugMugAuthorizationHeader({
736
+ method: "POST",
737
+ url: input.url,
738
+ credentials: input.credentials,
739
+ oauthExtras: input.oauthExtras,
740
+ nonce: input.nonce,
741
+ timestamp: input.timestamp
742
+ });
743
+ const response = await fetchImpl(input.url, {
744
+ method: "POST",
745
+ headers: {
746
+ Accept: "application/x-www-form-urlencoded",
747
+ Authorization: authorization
748
+ }
749
+ });
750
+ const body = await response.text();
751
+ if (!response.ok) {
752
+ throw new Error(
753
+ `SmugMug OAuth HTTP ${response.status}${body ? `: ${body.slice(0, 200)}` : ""}`
754
+ );
755
+ }
756
+ return parseSmugMugOAuthFormBody(body);
757
+ }
758
+ async function getSmugMugRequestToken(input) {
759
+ const consumer = smugMugConsumerCredentialsSchema.parse(input.consumer);
760
+ const callbackUrl = input.callbackUrl.trim();
761
+ if (!callbackUrl) {
762
+ throw new Error('SmugMug OAuth callbackUrl is required (use a URL or "oob")');
763
+ }
764
+ const fields = await postSmugMugOAuthTokenEndpoint({
765
+ url: SMUGMUG_OAUTH_ENDPOINTS.requestToken,
766
+ credentials: {
767
+ consumerKey: consumer.consumerKey,
768
+ consumerSecret: consumer.consumerSecret,
769
+ accessTokenSecret: ""
770
+ },
771
+ oauthExtras: { oauth_callback: callbackUrl },
772
+ fetchImpl: input.fetchImpl,
773
+ nonce: input.nonce,
774
+ timestamp: input.timestamp
775
+ });
776
+ return {
777
+ token: requireOAuthFormField(fields, "oauth_token"),
778
+ tokenSecret: requireOAuthFormField(fields, "oauth_token_secret"),
779
+ callbackConfirmed: fields.oauth_callback_confirmed === "true"
780
+ };
781
+ }
782
+ function buildSmugMugAuthorizeUrl(options) {
783
+ const requestToken = options.requestToken.trim();
784
+ if (!requestToken) {
785
+ throw new Error("SmugMug authorize URL requires requestToken");
786
+ }
787
+ const url = new URL(SMUGMUG_OAUTH_ENDPOINTS.authorize);
788
+ url.searchParams.set("oauth_token", requestToken);
789
+ url.searchParams.set("Access", options.access ?? "Full");
790
+ url.searchParams.set("Permissions", options.permissions ?? "Modify");
791
+ if (options.allowThirdPartyLogin !== void 0) {
792
+ url.searchParams.set("allowThirdPartyLogin", options.allowThirdPartyLogin ? "1" : "0");
793
+ }
794
+ if (options.showSignUpButton !== void 0) {
795
+ url.searchParams.set("showSignUpButton", options.showSignUpButton ? "true" : "false");
796
+ }
797
+ if (options.username?.trim()) {
798
+ url.searchParams.set("username", options.username.trim());
799
+ }
800
+ return url.toString();
801
+ }
802
+ async function getSmugMugAccessToken(input) {
803
+ const consumer = smugMugConsumerCredentialsSchema.parse(input.consumer);
804
+ const token = input.requestToken.token.trim();
805
+ const tokenSecret = input.requestToken.tokenSecret;
806
+ const verifier = input.verifier.trim();
807
+ if (!token || tokenSecret === void 0 || tokenSecret === null) {
808
+ throw new Error("SmugMug access-token exchange requires request token + secret");
809
+ }
810
+ if (!verifier) {
811
+ throw new Error("SmugMug access-token exchange requires oauth_verifier");
812
+ }
813
+ const fields = await postSmugMugOAuthTokenEndpoint({
814
+ url: SMUGMUG_OAUTH_ENDPOINTS.accessToken,
815
+ credentials: {
816
+ consumerKey: consumer.consumerKey,
817
+ consumerSecret: consumer.consumerSecret,
818
+ accessToken: token,
819
+ accessTokenSecret: tokenSecret
820
+ },
821
+ oauthExtras: { oauth_verifier: verifier },
822
+ fetchImpl: input.fetchImpl,
823
+ nonce: input.nonce,
824
+ timestamp: input.timestamp
825
+ });
826
+ return {
827
+ token: requireOAuthFormField(fields, "oauth_token"),
828
+ tokenSecret: requireOAuthFormField(fields, "oauth_token_secret")
829
+ };
830
+ }
831
+ async function createSmugMugOAuthSession(input) {
832
+ const request = await getSmugMugRequestToken({
833
+ consumer: {
834
+ consumerKey: input.consumerKey,
835
+ consumerSecret: input.consumerSecret
836
+ },
837
+ callbackUrl: input.callbackUrl,
838
+ fetchImpl: input.fetchImpl,
839
+ nonce: input.nonce,
840
+ timestamp: input.timestamp
841
+ });
842
+ return {
843
+ requestToken: request.token,
844
+ requestTokenSecret: request.tokenSecret,
845
+ authorizeUrl: buildSmugMugAuthorizeUrl({
846
+ requestToken: request.token,
847
+ access: input.access,
848
+ permissions: input.permissions
849
+ }),
850
+ callbackConfirmed: request.callbackConfirmed
851
+ };
852
+ }
711
853
  function readSmugMugCredentialsFromEnv(env = process.env) {
712
854
  return smugMugCredentialsSchema.parse({
713
855
  consumerKey: env.SMUGMUG_CONSUMER_KEY,
@@ -2557,9 +2699,15 @@ export {
2557
2699
  wordpressAdapter,
2558
2700
  SMUGMUG_API_BASE,
2559
2701
  SMUGMUG_OAUTH_ENDPOINTS,
2702
+ smugMugConsumerCredentialsSchema,
2560
2703
  smugMugCredentialsSchema,
2561
2704
  signSmugMugOAuthRequest,
2562
2705
  buildSmugMugAuthorizationHeader,
2706
+ parseSmugMugOAuthFormBody,
2707
+ getSmugMugRequestToken,
2708
+ buildSmugMugAuthorizeUrl,
2709
+ getSmugMugAccessToken,
2710
+ createSmugMugOAuthSession,
2563
2711
  readSmugMugCredentialsFromEnv,
2564
2712
  SmugMugApiClient,
2565
2713
  smugmugAdapter,
@@ -2569,4 +2717,4 @@ export {
2569
2717
  wixAdapter,
2570
2718
  getAdapter
2571
2719
  };
2572
- //# sourceMappingURL=chunk-RLMYUUNK.js.map
2720
+ //# sourceMappingURL=chunk-PZADAGBA.js.map