@usesocial/cli 0.3.2 → 0.3.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @usesocial/cli
2
2
 
3
+ ## 0.3.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#14](https://github.com/usesocial/monorepo/pull/14) [`22ba757`](https://github.com/usesocial/monorepo/commit/22ba757efd459a0cb3009c91ba833687e2ce2796) Thanks [@CyrusNuevoDia](https://github.com/CyrusNuevoDia)! - Ask for phone number during `social account login`, save it after device approval, and include it in account identity output.
8
+
3
9
  ## 0.3.2
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -212,12 +212,12 @@ jq -r '.items[].id' /tmp/recent-posts.json \
212
212
  default; clear Write in the prompt to grant read-only access.
213
213
  2. Asks for your email address.
214
214
  3. Sends a magic link to that email with the device approval screen already
215
- attached.
215
+ attached while asking for your phone number.
216
216
  4. Waits until you click the magic link and approve the CLI session in the
217
- browser.
218
- 5. Confirms billing checkout in the terminal when a seat is needed.
219
- 6. Stores the returned token in your OS keyring, falling back to
220
- `~/.social/credentials.json` (mode `0600`) when no keyring is available.
217
+ browser, then stores the returned token in your OS keyring.
218
+ 5. Stores the phone number, then confirms billing checkout in the terminal when
219
+ a seat is needed. If no keyring is available, credentials fall back to
220
+ `~/.social/credentials.json` (mode `0600`).
221
221
  Non-production `SOCIAL_API_URL` values use an API-specific credential
222
222
  namespace so staging tests do not overwrite the production session.
223
223
 
package/dist/index.mjs CHANGED
@@ -9583,6 +9583,7 @@ const user = pgTable("user", {
9583
9583
  emailVerified: boolean("emailVerified").notNull().default(false),
9584
9584
  name: text$1("name").notNull(),
9585
9585
  image: text$1("image"),
9586
+ phoneNumber: text$1("phone_number"),
9586
9587
  createdAt: timestamp("createdAt", { mode: "date" }).notNull().defaultNow(),
9587
9588
  updatedAt: timestamp("updatedAt", { mode: "date" }).notNull().defaultNow().$onUpdate(() => /* @__PURE__ */ new Date())
9588
9589
  });
@@ -21058,7 +21059,7 @@ const createInstance = (defaults) => {
21058
21059
  const ky = createInstance();
21059
21060
  //#endregion
21060
21061
  //#region package.json
21061
- var version$1 = "0.3.2";
21062
+ var version$1 = "0.3.3";
21062
21063
  //#endregion
21063
21064
  //#region src/lib/env.ts
21064
21065
  const URLWithTrailingSlash = url().transform(ensureTrailingSlash);
@@ -27411,6 +27412,23 @@ const accessPhase = async (ctx) => {
27411
27412
  };
27412
27413
  };
27413
27414
  //#endregion
27415
+ //#region ../../packages/lib/src/cli-schema-contract.ts
27416
+ const isZodSchema = (value) => Boolean(value && typeof value === "object" && "safeParse" in value && typeof value.safeParse === "function");
27417
+ const isZodUnknownSchema = (value) => value instanceof ZodUnknown;
27418
+ const zodToJSONSchema = (schema, options) => toJSONSchema(schema, { io: options?.io ?? "output" });
27419
+ const unknownOutputContract = (message = "Output schema is not declared.") => ({
27420
+ outputSchema: { unknown: true },
27421
+ hazards: [{
27422
+ code: "output_schema_unknown",
27423
+ message
27424
+ }]
27425
+ });
27426
+ new TextEncoder();
27427
+ //#endregion
27428
+ //#region ../../packages/lib/src/phone.ts
27429
+ const phoneFormattingRegex = /[\s().-]/g;
27430
+ const PhoneNumber = string().trim().transform((value) => value.replace(phoneFormattingRegex, "")).pipe(string().regex(/^\+[1-9]\d{1,14}$/, { message: "Enter a phone number with +country code." }));
27431
+ //#endregion
27414
27432
  //#region src/login/phases/0.sign-in.ts
27415
27433
  const CLIENT_ID = "social-cli";
27416
27434
  const DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
@@ -27495,7 +27513,30 @@ const sendMagicLinkSignIn = async (email, callbackURL, deps = {}) => {
27495
27513
  throw new Error(authErrorMessage("Could not send sign-in email", response, error));
27496
27514
  }
27497
27515
  };
27498
- const waitForDeviceToken = async (ctx, deviceCode) => {
27516
+ const startMagicLinkSignIn = (email, callbackURL) => sendMagicLinkSignIn(email, callbackURL).then(() => ({ status: "fulfilled" }), (error) => ({
27517
+ status: "rejected",
27518
+ error
27519
+ }));
27520
+ const waitForMagicLinkEmail = async (ctx, emailPromise) => {
27521
+ const result = await emailPromise;
27522
+ if (result.status === "rejected") {
27523
+ ctx.ui.error("Could not send sign-in email");
27524
+ throw result.error;
27525
+ }
27526
+ };
27527
+ const isMissingPhoneEndpointError = (error) => isRecord$2(error) && "status" in error && error.status === 404 && "message" in error && typeof error.message === "string" && error.message.toLowerCase().includes("not found");
27528
+ const updatePhoneNumber = async (ctx, phoneNumber) => {
27529
+ try {
27530
+ await ctx.client.cli.user.updatePhone({ phoneNumber });
27531
+ } catch (error) {
27532
+ if (isMissingPhoneEndpointError(error)) {
27533
+ ctx.ui.warn("Phone number could not be saved yet; login is complete.");
27534
+ return;
27535
+ }
27536
+ throw error;
27537
+ }
27538
+ };
27539
+ const waitForDeviceToken = async (ctx, deviceCode, phoneNumber) => {
27499
27540
  const spinner = ctx.ui.spinner();
27500
27541
  const message = "Waiting for magic link approval";
27501
27542
  spinner.start(message);
@@ -27509,6 +27550,7 @@ const waitForDeviceToken = async (ctx, deviceCode) => {
27509
27550
  expiresAt: tokenExpiresAt,
27510
27551
  scope: token.scope
27511
27552
  });
27553
+ await updatePhoneNumber(ctx, phoneNumber);
27512
27554
  clearTick();
27513
27555
  spinner.stop(LOGIN_SUCCESS_MESSAGE);
27514
27556
  ctx.ui.info(NEXT_COMMANDS_MESSAGE);
@@ -27530,14 +27572,17 @@ const signInPhase = async (ctx) => {
27530
27572
  const expiresAt = new Date(Date.now() + deviceCode.expires_in * 1e3).toISOString();
27531
27573
  const callbackURL = deviceCallbackURLFor(deviceCode, email);
27532
27574
  const userCode = formatUserCode(deviceCode.user_code);
27533
- await ctx.ui.spin("Sending sign-in email", () => sendMagicLinkSignIn(email, callbackURL), "Magic link sent");
27575
+ const emailPromise = startMagicLinkSignIn(email, callbackURL);
27576
+ const phoneNumber = await resolvePhoneNumber(ctx);
27577
+ await waitForMagicLinkEmail(ctx, emailPromise);
27534
27578
  ctx.ui.note(`Code: ${userCode}\nEmail: ${email}\n\nClick the magic link in your inbox to approve this CLI session.\n\n${onboardingLegalNotice()}`, "Check your email");
27535
27579
  return {
27536
27580
  status: "done",
27537
27581
  data: {
27538
27582
  email,
27539
27583
  expiresAt,
27540
- scope: (await waitForDeviceToken(ctx, deviceCode)).scope
27584
+ phoneNumber,
27585
+ scope: (await waitForDeviceToken(ctx, deviceCode, phoneNumber)).scope
27541
27586
  }
27542
27587
  };
27543
27588
  };
@@ -27547,6 +27592,17 @@ const resolveEmail = async (ctx) => await ctx.ui.text({
27547
27592
  placeholder: "you@example.com",
27548
27593
  validate: (value) => value?.includes("@") ? void 0 : "Enter an email address."
27549
27594
  });
27595
+ const resolvePhoneNumber = async (ctx) => {
27596
+ const phoneNumber = await ctx.ui.text({
27597
+ message: "Phone number",
27598
+ placeholder: "+1 555 123 4567",
27599
+ validate: (value) => {
27600
+ const result = PhoneNumber.safeParse(value);
27601
+ return result.success ? void 0 : result.error.issues[0]?.message;
27602
+ }
27603
+ });
27604
+ return PhoneNumber.parse(phoneNumber);
27605
+ };
27550
27606
  //#endregion
27551
27607
  //#region src/login/phases/1.scope.ts
27552
27608
  const DEFAULT_SCOPE_ALIAS = "read,write";
@@ -27691,7 +27747,8 @@ const ScopeAlias = _enum(["read", "read,write"]);
27691
27747
  const LoginUserOutput = object({
27692
27748
  id: string(),
27693
27749
  email: string().nullable(),
27694
- name: string().nullable()
27750
+ name: string().nullable(),
27751
+ phoneNumber: string().nullable()
27695
27752
  });
27696
27753
  const LoginSeatsOutput = object({
27697
27754
  total: number().int().nonnegative(),
@@ -28111,7 +28168,8 @@ const AccountSessionOutput = object({
28111
28168
  user: object({
28112
28169
  id: string(),
28113
28170
  email: string().nullable(),
28114
- name: string().nullable()
28171
+ name: string().nullable(),
28172
+ phoneNumber: string().nullable()
28115
28173
  }),
28116
28174
  scope: _enum(["read", "read,write"]),
28117
28175
  capabilities: array(string())
@@ -28591,18 +28649,6 @@ const createCLIDeps = () => {
28591
28649
  };
28592
28650
  };
28593
28651
  //#endregion
28594
- //#region ../../packages/lib/src/cli-schema-contract.ts
28595
- const isZodSchema = (value) => Boolean(value && typeof value === "object" && "safeParse" in value && typeof value.safeParse === "function");
28596
- const isZodUnknownSchema = (value) => value instanceof ZodUnknown;
28597
- const zodToJSONSchema = (schema, options) => toJSONSchema(schema, { io: options?.io ?? "output" });
28598
- const unknownOutputContract = (message = "Output schema is not declared.") => ({
28599
- outputSchema: { unknown: true },
28600
- hazards: [{
28601
- code: "output_schema_unknown",
28602
- message
28603
- }]
28604
- });
28605
- //#endregion
28606
28652
  //#region src/schema/index.ts
28607
28653
  const HTTPMethod = _enum([
28608
28654
  "DELETE",
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "access": "public"
12
12
  },
13
13
  "private": false,
14
- "version": "0.3.2",
14
+ "version": "0.3.3",
15
15
  "type": "module",
16
16
  "engines": {
17
17
  "node": ">=22.5.0"