shopstack 0.2.0 → 0.2.2

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/src/config.d.ts CHANGED
@@ -7,11 +7,15 @@ export interface ShopstackProfile {
7
7
 
8
8
  export interface PendingSignup {
9
9
  accountType: "developer" | "personal";
10
+ attemptId?: string;
10
11
  email: string;
11
- expiresAt: string;
12
- id: string;
13
- pollToken: string;
12
+ expiresAt?: string;
13
+ id?: string;
14
+ idempotencyKey?: string;
15
+ pollToken?: string;
14
16
  profile: string;
17
+ recoveryKey?: string;
18
+ signupId?: string;
15
19
  }
16
20
 
17
21
  export class ConfigStore {
@@ -25,6 +29,21 @@ export class ConfigStore {
25
29
  useProfile(name: string): Promise<ShopstackProfile>;
26
30
  savePendingSignup(signup: PendingSignup): Promise<PendingSignup>;
27
31
  pendingSignup(signupId: string): Promise<PendingSignup | undefined>;
32
+ pendingSignups(): Promise<PendingSignup[]>;
33
+ findPendingSignup(input: {
34
+ accountType: "developer" | "personal";
35
+ email: string;
36
+ }): Promise<PendingSignup | undefined>;
37
+ completePendingSignup(
38
+ attemptId: string,
39
+ profileName: string,
40
+ result: {
41
+ account: { id: string };
42
+ api_key: string;
43
+ key_type: "developer" | "user";
44
+ user?: { id?: string };
45
+ },
46
+ ): Promise<ShopstackProfile>;
28
47
  deletePendingSignup(signupId: string): Promise<void>;
29
48
  load(): Promise<{
30
49
  active: string | null;
package/src/config.js CHANGED
@@ -46,6 +46,7 @@ export class ConfigStore {
46
46
  async write(config) {
47
47
  const directory = dirname(this.path);
48
48
  await mkdir(directory, { mode: 0o700, recursive: true });
49
+ await chmod(directory, 0o700);
49
50
  const temporary = `${this.path}.${process.pid}.tmp`;
50
51
  await writeFile(temporary, `${JSON.stringify(config, null, 2)}\n`, {
51
52
  mode: 0o600,
@@ -83,19 +84,70 @@ export class ConfigStore {
83
84
 
84
85
  async savePendingSignup(signup) {
85
86
  const config = await this.load();
86
- config.pendingSignups[signup.id] = { ...signup };
87
+ const key = signup.attemptId ?? signup.id;
88
+ if (typeof key !== "string" || key.length === 0) {
89
+ throw new Error("Pending signup identity is invalid.");
90
+ }
91
+ config.pendingSignups[key] = { ...signup };
87
92
  await this.write(config);
88
- return config.pendingSignups[signup.id];
93
+ return config.pendingSignups[key];
89
94
  }
90
95
 
91
96
  async pendingSignup(signupId) {
92
97
  const config = await this.load();
93
- return config.pendingSignups[signupId];
98
+ return (
99
+ config.pendingSignups[signupId] ??
100
+ Object.values(config.pendingSignups).find(
101
+ (pending) => pending.id === signupId || pending.signupId === signupId,
102
+ )
103
+ );
104
+ }
105
+
106
+ async pendingSignups() {
107
+ return Object.values((await this.load()).pendingSignups);
108
+ }
109
+
110
+ async findPendingSignup({ accountType, email }) {
111
+ const normalizedEmail = email.trim().toLowerCase();
112
+ return Object.values((await this.load()).pendingSignups).find(
113
+ (pending) =>
114
+ pending.accountType === accountType &&
115
+ pending.email.trim().toLowerCase() === normalizedEmail,
116
+ );
117
+ }
118
+
119
+ async completePendingSignup(attemptId, profileName, result) {
120
+ const config = await this.load();
121
+ const entry = Object.entries(config.pendingSignups).find(
122
+ ([key, pending]) =>
123
+ key === attemptId ||
124
+ pending.id === attemptId ||
125
+ pending.signupId === attemptId,
126
+ );
127
+ if (entry === undefined) {
128
+ throw new Error("Pending signup is unavailable.");
129
+ }
130
+ config.profiles[profileName] = {
131
+ accountId: result.account.id,
132
+ apiKey: result.api_key,
133
+ keyType: result.key_type,
134
+ ...(result.user?.id === undefined ? {} : { userId: result.user.id }),
135
+ };
136
+ config.active = profileName;
137
+ delete config.pendingSignups[entry[0]];
138
+ await this.write(config);
139
+ return config.profiles[profileName];
94
140
  }
95
141
 
96
142
  async deletePendingSignup(signupId) {
97
143
  const config = await this.load();
98
- delete config.pendingSignups[signupId];
144
+ const entry = Object.entries(config.pendingSignups).find(
145
+ ([key, pending]) =>
146
+ key === signupId ||
147
+ pending.id === signupId ||
148
+ pending.signupId === signupId,
149
+ );
150
+ if (entry !== undefined) delete config.pendingSignups[entry[0]];
99
151
  await this.write(config);
100
152
  }
101
153
  }