chofex-cli 0.1.147 → 0.1.148

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 (2) hide show
  1. package/dist/index.js +80 -26
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -58828,6 +58828,39 @@ var ChallengeCatalogSchema = exports_Schema.Struct({
58828
58828
  });
58829
58829
 
58830
58830
  // ../../packages/registration-contract/src/index.ts
58831
+ var campaignAttributionHandoffHeader = "x-chofex-campaign-attribution";
58832
+ var eventUuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
58833
+ function campaignAttributionHandoffValue(handoff) {
58834
+ if (!eventUuidPattern.test(handoff.landingId))
58835
+ return;
58836
+ if (!Number.isSafeInteger(handoff.capturedAt) || handoff.capturedAt < 0)
58837
+ return;
58838
+ return `${handoff.landingId}.${handoff.capturedAt}`;
58839
+ }
58840
+ function campaignAttributionHandoffFromValue(value3) {
58841
+ if (!value3)
58842
+ return;
58843
+ const separator = value3.lastIndexOf(".");
58844
+ if (separator < 0)
58845
+ return;
58846
+ const landingId = value3.slice(0, separator);
58847
+ const capturedAt = Number(value3.slice(separator + 1));
58848
+ const handoff = { capturedAt, landingId };
58849
+ if (campaignAttributionHandoffValue(handoff) !== value3)
58850
+ return;
58851
+ return handoff;
58852
+ }
58853
+ function campaignAttributionFromOAuthState(expectedState, returnedState) {
58854
+ if (returnedState === expectedState)
58855
+ return { valid: true };
58856
+ const prefix = `${expectedState}.`;
58857
+ if (!returnedState?.startsWith(prefix))
58858
+ return { valid: false };
58859
+ const handoff = campaignAttributionHandoffFromValue(returnedState.slice(prefix.length));
58860
+ if (!handoff)
58861
+ return { valid: false };
58862
+ return { valid: true, handoff };
58863
+ }
58831
58864
  var nonBlank = (maximum) => exports_Schema.Trim.pipe(exports_Schema.check(exports_Schema.isMinLength(1), exports_Schema.isMaxLength(maximum)));
58832
58865
  var optionalText = (maximum) => exports_Schema.optional(nonBlank(maximum));
58833
58866
  var nullableOptionalText = (maximum) => exports_Schema.optional(exports_Schema.NullOr(nonBlank(maximum)));
@@ -59140,11 +59173,13 @@ import { createServer } from "node:http";
59140
59173
 
59141
59174
  // src/config.ts
59142
59175
  var oauthIssuer = process.env.CHOFEX_OAUTH_ISSUER ?? "https://clerk.hacktheandes.com";
59176
+ var publicSiteUrl = process.env.CHOFEX_PUBLIC_SITE_URL ?? "https://hacktheandes.com";
59143
59177
  var config = {
59144
59178
  apiUrl: process.env.CHOFEX_API_URL ?? "https://hacktheandes.com",
59145
- publicSiteUrl: process.env.CHOFEX_PUBLIC_SITE_URL ?? "https://hacktheandes.com",
59179
+ publicSiteUrl,
59146
59180
  oauthClientId: process.env.CHOFEX_OAUTH_CLIENT_ID ?? "HddO78wKpq5PFIEl",
59147
59181
  authorizationUrl: `${oauthIssuer}/oauth/authorize`,
59182
+ authorizationBridgeUrl: `${publicSiteUrl}/api/v1/oauth/authorize`,
59148
59183
  tokenUrl: `${oauthIssuer}/oauth/token`,
59149
59184
  revocationUrl: `${oauthIssuer}/oauth/token/revoke`
59150
59185
  };
@@ -59304,7 +59339,7 @@ var tokenEndpointError = (status, body) => {
59304
59339
  const detail = description ? `: ${description}` : "";
59305
59340
  return new Error(`Clerk OAuth token endpoint returned ${status}${detail}`);
59306
59341
  };
59307
- var credentialsFromToken = (body, previousRefreshToken) => {
59342
+ var credentialsFromToken = (body, previousRefreshToken, campaignAttribution) => {
59308
59343
  if (typeof body.access_token !== "string") {
59309
59344
  let detail = "";
59310
59345
  if (typeof body.error_description === "string") {
@@ -59322,6 +59357,7 @@ var credentialsFromToken = (body, previousRefreshToken) => {
59322
59357
  }
59323
59358
  return {
59324
59359
  accessToken: body.access_token,
59360
+ campaignAttribution,
59325
59361
  refreshToken,
59326
59362
  expiresAt
59327
59363
  };
@@ -59360,20 +59396,26 @@ var refresh = async (credentials) => {
59360
59396
  refresh_token: credentials.refreshToken,
59361
59397
  client_id: config.oauthClientId
59362
59398
  });
59363
- const updated = credentialsFromToken(body, credentials.refreshToken);
59399
+ const updated = credentialsFromToken(body, credentials.refreshToken, credentials.campaignAttribution);
59364
59400
  await saveCredentials(updated);
59365
59401
  return updated;
59366
59402
  };
59367
- var accessToken = async (forceRefresh = false) => {
59368
- if (process.env.CHOFEX_TOKEN)
59369
- return process.env.CHOFEX_TOKEN;
59403
+ var authentication = async (forceRefresh = false) => {
59404
+ if (process.env.CHOFEX_TOKEN) {
59405
+ return { accessToken: process.env.CHOFEX_TOKEN };
59406
+ }
59370
59407
  const credentials = await readCredentials();
59371
59408
  if (!credentials)
59372
59409
  throw new Error("Not logged in. Run `chofex login`.");
59373
59410
  if (forceRefresh || credentials.expiresAt !== undefined && credentials.expiresAt <= Date.now() + 30000) {
59374
- return (await refresh(credentials)).accessToken;
59411
+ return await refresh(credentials);
59375
59412
  }
59376
- return credentials.accessToken;
59413
+ return credentials;
59414
+ };
59415
+ var bridgedAuthorizationUrl = (bridgeUrl, authorizationUrl) => {
59416
+ const bridge = new URL(bridgeUrl);
59417
+ bridge.search = new URL(authorizationUrl).search;
59418
+ return bridge.toString();
59377
59419
  };
59378
59420
  var browserCommand = (url2, platform = process.platform) => {
59379
59421
  if (platform === "darwin")
@@ -59412,7 +59454,8 @@ var login = async () => {
59412
59454
  }
59413
59455
  const error2 = url2.searchParams.get("error");
59414
59456
  const authorizationCode = url2.searchParams.get("code");
59415
- if (url2.searchParams.get("state") !== state || error2 || !authorizationCode) {
59457
+ const attributionState = campaignAttributionFromOAuthState(state, url2.searchParams.get("state"));
59458
+ if (!attributionState.valid || error2 || !authorizationCode) {
59416
59459
  response.writeHead(400, { "content-type": "text/plain" }).end("Chofex login failed. You can close this window.");
59417
59460
  let message = "Invalid OAuth callback";
59418
59461
  if (error2)
@@ -59421,7 +59464,10 @@ var login = async () => {
59421
59464
  return;
59422
59465
  }
59423
59466
  response.writeHead(200, { "content-type": "text/plain" }).end("Chofex login complete. You can close this window.");
59424
- finish(authorizationCode);
59467
+ finish({
59468
+ authorizationCode,
59469
+ campaignAttribution: attributionState.handoff
59470
+ });
59425
59471
  });
59426
59472
  await new Promise((resolve4, reject) => {
59427
59473
  server.once("error", reject);
@@ -59448,23 +59494,24 @@ var login = async () => {
59448
59494
  server.close();
59449
59495
  throw error2;
59450
59496
  }
59497
+ const browserUrl = bridgedAuthorizationUrl(config.authorizationBridgeUrl, authorizationUrl.toString());
59451
59498
  console.error(`Sign in here (waiting up to five minutes):
59452
59499
 
59453
- ${authorizationUrl}
59500
+ ${browserUrl}
59454
59501
 
59455
59502
  If the browser did not open automatically, open that URL yourself.`);
59456
- openBrowser(authorizationUrl.toString());
59503
+ openBrowser(browserUrl);
59457
59504
  const timeout3 = setTimeout(() => fail11(new Error("Login timed out after five minutes")), 300000);
59458
59505
  try {
59459
- const authorizationCode = await code;
59506
+ const callback4 = await code;
59460
59507
  const body = await tokenRequest({
59461
59508
  grant_type: "authorization_code",
59462
59509
  client_id: config.oauthClientId,
59463
- code: authorizationCode,
59510
+ code: callback4.authorizationCode,
59464
59511
  redirect_uri: redirectUri,
59465
59512
  code_verifier: verifier
59466
59513
  });
59467
- const credentials = credentialsFromToken(body);
59514
+ const credentials = credentialsFromToken(body, undefined, callback4.campaignAttribution);
59468
59515
  await saveCredentials(credentials);
59469
59516
  return credentials.accessToken;
59470
59517
  } finally {
@@ -59537,20 +59584,27 @@ var exitCodeFor = (error2) => {
59537
59584
 
59538
59585
  // src/api-client.ts
59539
59586
  var endpoint = (apiUrl, path4) => `${apiUrl.replace(/\/$/, "")}${path4}`;
59540
- var resolveAccessToken = (suppliedToken, forceRefresh = false) => {
59541
- if (suppliedToken)
59542
- return exports_Effect.succeed(suppliedToken);
59587
+ var resolveAuthentication = (suppliedToken, campaignAttribution, forceRefresh = false) => {
59588
+ if (suppliedToken) {
59589
+ return exports_Effect.succeed({ accessToken: suppliedToken, campaignAttribution });
59590
+ }
59543
59591
  return exports_Effect.tryPromise({
59544
- try: () => accessToken(forceRefresh),
59592
+ try: () => authentication(forceRefresh),
59545
59593
  catch: (error2) => cliError("AUTHENTICATION_REQUIRED", String(error2), false)
59546
59594
  });
59547
59595
  };
59548
- var sendRequest = (options, path4, init, token) => exports_Effect.tryPromise({
59596
+ var sendRequest = (options, path4, init, credentials) => exports_Effect.tryPromise({
59549
59597
  try: () => {
59550
59598
  const headers = new Headers(init.headers);
59551
59599
  headers.set("accept", "application/json");
59552
- if (token)
59553
- headers.set("authorization", `Bearer ${token}`);
59600
+ if (credentials) {
59601
+ headers.set("authorization", `Bearer ${credentials.accessToken}`);
59602
+ if (credentials.campaignAttribution) {
59603
+ const handoff = campaignAttributionHandoffValue(credentials.campaignAttribution);
59604
+ if (handoff)
59605
+ headers.set(campaignAttributionHandoffHeader, handoff);
59606
+ }
59607
+ }
59554
59608
  headers.set("x-request-id", crypto.randomUUID());
59555
59609
  if (init.body !== undefined) {
59556
59610
  headers.set("content-type", "application/json");
@@ -59579,11 +59633,11 @@ var decodeHttpBody = exports_Effect.fn("decodeHttpBody")(function* (response, de
59579
59633
  });
59580
59634
  var request3 = exports_Effect.fn("apiRequest")(function* (options, path4, init, decodeResponse) {
59581
59635
  const suppliedToken = options.token ?? process.env.CHOFEX_TOKEN;
59582
- let token = yield* resolveAccessToken(suppliedToken);
59583
- let response = yield* sendRequest(options, path4, init, token);
59636
+ let credentials = yield* resolveAuthentication(suppliedToken, options.campaignAttribution);
59637
+ let response = yield* sendRequest(options, path4, init, credentials);
59584
59638
  if (response.status === 401 && !suppliedToken) {
59585
- token = yield* resolveAccessToken(undefined, true);
59586
- response = yield* sendRequest(options, path4, init, token);
59639
+ credentials = yield* resolveAuthentication(undefined, undefined, true);
59640
+ response = yield* sendRequest(options, path4, init, credentials);
59587
59641
  }
59588
59642
  return yield* decodeHttpBody(response, decodeResponse);
59589
59643
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chofex-cli",
3
- "version": "0.1.147",
3
+ "version": "0.1.148",
4
4
  "description": "Command-line client for Hack the Andes",
5
5
  "type": "module",
6
6
  "bin": {