@slates/profiles 1.0.0-rc.2 → 1.0.0-rc.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.
- package/dist/index.cjs +42 -25
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.module.js +42 -25
- package/package.json +6 -6
- package/src/client.ts +29 -14
- package/src/store.test.ts +221 -3
- package/src/store.ts +38 -18
- package/src/types.ts +2 -2
package/dist/index.cjs
CHANGED
|
@@ -82,11 +82,13 @@ var resolveSlatesCliRoot = (cwd = process.cwd()) => {
|
|
|
82
82
|
return cwd;
|
|
83
83
|
}
|
|
84
84
|
};
|
|
85
|
-
var
|
|
86
|
-
var
|
|
85
|
+
var resolveSlatesCliDirFromRoot = (rootDir) => import_path.default.join(rootDir, CLI_DIR_NAME);
|
|
86
|
+
var resolveSlatesCliProfilesDirFromRoot = (rootDir) => import_path.default.join(resolveSlatesCliDirFromRoot(rootDir), PROFILES_DIR_NAME);
|
|
87
|
+
var resolveSlatesCliDir = (cwd = process.cwd()) => resolveSlatesCliDirFromRoot(resolveSlatesCliRoot(cwd));
|
|
88
|
+
var resolveSlatesCliProfilesDir = (cwd = process.cwd()) => resolveSlatesCliProfilesDirFromRoot(resolveSlatesCliRoot(cwd));
|
|
87
89
|
var normalizeScopeKey = (scopeKey) => import_path.default.posix.normalize(scopeKey.replace(/\\/g, "/")).replace(/^\.?\//, "");
|
|
88
90
|
var splitScopeKey = (scopeKey) => normalizeScopeKey(scopeKey).split("/").filter(Boolean).filter((segment) => segment !== "." && segment !== "..");
|
|
89
|
-
var resolveScopedStoreDir = (rootDir, scopeKey) => import_path.default.join(
|
|
91
|
+
var resolveScopedStoreDir = (rootDir, scopeKey) => import_path.default.join(resolveSlatesCliProfilesDirFromRoot(rootDir), ...splitScopeKey(scopeKey));
|
|
90
92
|
var resolveScopedStorePath = (rootDir, scopeKey) => import_path.default.join(resolveScopedStoreDir(rootDir, scopeKey), STORE_FILE_NAME);
|
|
91
93
|
var inferRootDirFromStorePath = (storePath) => {
|
|
92
94
|
let normalized = import_path.default.resolve(storePath);
|
|
@@ -99,7 +101,7 @@ var inferRootDirFromStorePath = (storePath) => {
|
|
|
99
101
|
return import_path.default.join(import_path.default.sep, ...rootParts);
|
|
100
102
|
};
|
|
101
103
|
var resolveScopeKeyFromStorePath = (rootDir, storePath) => {
|
|
102
|
-
let profilesDir =
|
|
104
|
+
let profilesDir = resolveSlatesCliProfilesDirFromRoot(rootDir);
|
|
103
105
|
let relativeDir = import_path.default.relative(profilesDir, import_path.default.dirname(storePath));
|
|
104
106
|
if (!relativeDir || relativeDir.startsWith("..") || import_path.default.isAbsolute(relativeDir)) {
|
|
105
107
|
return null;
|
|
@@ -130,7 +132,7 @@ var isProfileInScope = (rootDir, scopeKey, profile) => {
|
|
|
130
132
|
return relative !== "" && !relative.startsWith("..") && !import_path.default.isAbsolute(relative);
|
|
131
133
|
};
|
|
132
134
|
var loadMigratedLegacyStore = async (rootDir, scopeKey) => {
|
|
133
|
-
let legacyStorePath = import_path.default.join(
|
|
135
|
+
let legacyStorePath = import_path.default.join(resolveSlatesCliDirFromRoot(rootDir), STORE_FILE_NAME);
|
|
134
136
|
let legacy = await readStoreData(legacyStorePath);
|
|
135
137
|
if (!legacy) {
|
|
136
138
|
return createEmptyStore();
|
|
@@ -149,22 +151,25 @@ var loadMigratedLegacyStore = async (rootDir, scopeKey) => {
|
|
|
149
151
|
};
|
|
150
152
|
};
|
|
151
153
|
var SlatesCliStore = class _SlatesCliStore {
|
|
152
|
-
constructor(rootDir, dirPath, storePath, scope, data) {
|
|
154
|
+
constructor(rootDir, cliDir, dirPath, storePath, scope, data) {
|
|
153
155
|
this.rootDir = rootDir;
|
|
156
|
+
this.cliDir = cliDir;
|
|
154
157
|
this.dirPath = dirPath;
|
|
155
158
|
this.storePath = storePath;
|
|
156
159
|
this.scope = scope;
|
|
157
160
|
this.data = data;
|
|
158
161
|
}
|
|
159
162
|
rootDir;
|
|
163
|
+
cliDir;
|
|
160
164
|
dirPath;
|
|
161
165
|
storePath;
|
|
162
166
|
scope;
|
|
163
167
|
data;
|
|
164
168
|
static async open(opts = {}) {
|
|
165
|
-
let
|
|
166
|
-
let
|
|
167
|
-
let
|
|
169
|
+
let storageRootDir = opts.storePath ? inferRootDirFromStorePath(opts.storePath) : resolveSlatesCliRoot(opts.cwd);
|
|
170
|
+
let rootDir = opts.rootDir ?? storageRootDir;
|
|
171
|
+
let cliDir = resolveSlatesCliDirFromRoot(storageRootDir);
|
|
172
|
+
let scopeKey = opts.storePath ? resolveScopeKeyFromStorePath(storageRootDir, opts.storePath) : opts.scope?.key ? normalizeScopeKey(opts.scope.key) : null;
|
|
168
173
|
let dirPath = opts.storePath ? import_path.default.dirname(opts.storePath) : scopeKey ? resolveScopedStoreDir(rootDir, scopeKey) : cliDir;
|
|
169
174
|
let storePath = opts.storePath ?? (scopeKey ? resolveScopedStorePath(rootDir, scopeKey) : import_path.default.join(cliDir, STORE_FILE_NAME));
|
|
170
175
|
await ensureDir(cliDir);
|
|
@@ -176,12 +181,14 @@ var SlatesCliStore = class _SlatesCliStore {
|
|
|
176
181
|
currentProfileId: parsed.currentProfileId ?? null,
|
|
177
182
|
profiles: parsed.profiles ?? {},
|
|
178
183
|
oauthCredentials: parsed.oauthCredentials ?? {}
|
|
179
|
-
} : scopeKey ? await loadMigratedLegacyStore(rootDir, scopeKey) : createEmptyStore();
|
|
184
|
+
} : opts.storePath ? createEmptyStore() : scopeKey ? await loadMigratedLegacyStore(rootDir, scopeKey) : createEmptyStore();
|
|
180
185
|
if (!parsed) {
|
|
181
|
-
await (0, import_promises.writeFile)(storePath, JSON.stringify(data, null, 2)
|
|
186
|
+
await (0, import_promises.writeFile)(storePath, `${JSON.stringify(data, null, 2)}
|
|
187
|
+
`, "utf-8");
|
|
182
188
|
}
|
|
183
189
|
return new _SlatesCliStore(
|
|
184
190
|
rootDir,
|
|
191
|
+
cliDir,
|
|
185
192
|
dirPath,
|
|
186
193
|
storePath,
|
|
187
194
|
scopeKey ? { key: scopeKey, name: opts.scope?.name } : null,
|
|
@@ -190,9 +197,10 @@ var SlatesCliStore = class _SlatesCliStore {
|
|
|
190
197
|
}
|
|
191
198
|
async save() {
|
|
192
199
|
await ensureDir(this.dirPath);
|
|
193
|
-
await ensureDir(
|
|
194
|
-
await ensureGitIgnore(
|
|
195
|
-
await (0, import_promises.writeFile)(this.storePath, JSON.stringify(this.data, null, 2)
|
|
200
|
+
await ensureDir(this.cliDir);
|
|
201
|
+
await ensureGitIgnore(this.cliDir);
|
|
202
|
+
await (0, import_promises.writeFile)(this.storePath, `${JSON.stringify(this.data, null, 2)}
|
|
203
|
+
`, "utf-8");
|
|
196
204
|
}
|
|
197
205
|
listProfiles() {
|
|
198
206
|
return Object.values(this.data.profiles).sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -336,15 +344,18 @@ var SlatesCliStore = class _SlatesCliStore {
|
|
|
336
344
|
var openSlatesCliStore = async (opts = {}) => SlatesCliStore.open(opts);
|
|
337
345
|
|
|
338
346
|
// src/client.ts
|
|
339
|
-
var resolveEntryPath = (entry,
|
|
347
|
+
var resolveEntryPath = (entry, opts = {}) => {
|
|
340
348
|
if (import_path2.default.isAbsolute(entry)) return entry;
|
|
341
|
-
return import_path2.default.resolve(resolveSlatesCliRoot(cwd), entry);
|
|
349
|
+
return import_path2.default.resolve(opts.rootDir ?? resolveSlatesCliRoot(opts.cwd), entry);
|
|
342
350
|
};
|
|
343
|
-
var loadSlateFromProfile = async (profile,
|
|
351
|
+
var loadSlateFromProfile = async (profile, opts = {}) => {
|
|
344
352
|
if (profile.target.type !== "local") {
|
|
345
353
|
throw new Error(`Unsupported profile target type: ${profile.target.type}`);
|
|
346
354
|
}
|
|
347
|
-
let modulePath = resolveEntryPath(profile.target.entry,
|
|
355
|
+
let modulePath = resolveEntryPath(profile.target.entry, {
|
|
356
|
+
cwd: opts.cwd,
|
|
357
|
+
rootDir: opts.store?.rootDir
|
|
358
|
+
});
|
|
348
359
|
let loaded = await import((0, import_url.pathToFileURL)(modulePath).href);
|
|
349
360
|
let exportName = profile.target.exportName;
|
|
350
361
|
let slate = (exportName ? loaded[exportName] : void 0) ?? loaded.provider ?? loaded.slate ?? loaded.default;
|
|
@@ -357,7 +368,7 @@ var loadSlateFromProfile = async (profile, cwd) => {
|
|
|
357
368
|
};
|
|
358
369
|
var createSlatesClientFromProfile = async (profile, opts = {}) => {
|
|
359
370
|
let firstAuth = Object.values(profile.auth)[0];
|
|
360
|
-
let slate = await loadSlateFromProfile(profile, opts
|
|
371
|
+
let slate = await loadSlateFromProfile(profile, opts);
|
|
361
372
|
let client = (0, import_client.createSlatesClient)({
|
|
362
373
|
transport: (0, import_client.createLocalSlateTransport)({ slate }),
|
|
363
374
|
state: {
|
|
@@ -369,20 +380,26 @@ var createSlatesClientFromProfile = async (profile, opts = {}) => {
|
|
|
369
380
|
} : null
|
|
370
381
|
}
|
|
371
382
|
});
|
|
372
|
-
if (firstAuth) {
|
|
383
|
+
if (firstAuth && opts.autoRefresh !== false) {
|
|
373
384
|
attachAutoRefresh(client, profile, firstAuth, opts.store);
|
|
374
385
|
}
|
|
375
386
|
return client;
|
|
376
387
|
};
|
|
377
388
|
var EXPIRY_BUFFER_MS = 60 * 1e3;
|
|
378
|
-
var
|
|
379
|
-
if (auth.authType !== "auth.oauth") return false;
|
|
380
|
-
if (!auth.clientId || !auth.clientSecret) return false;
|
|
381
|
-
if (typeof auth.output?.refreshToken !== "string" || !auth.output.refreshToken) return false;
|
|
389
|
+
var hasExpiredOrExpiringOutput = (auth) => {
|
|
382
390
|
if (typeof auth.output?.expiresAt !== "string" || !auth.output.expiresAt) return false;
|
|
383
391
|
let expiresAt = Date.parse(auth.output.expiresAt);
|
|
384
392
|
return Number.isFinite(expiresAt) && expiresAt <= Date.now() + EXPIRY_BUFFER_MS;
|
|
385
393
|
};
|
|
394
|
+
var shouldRefreshAuth = (auth) => {
|
|
395
|
+
if (!hasExpiredOrExpiringOutput(auth)) return false;
|
|
396
|
+
if (auth.authType === "auth.oauth") {
|
|
397
|
+
if (!auth.clientId || !auth.clientSecret) return false;
|
|
398
|
+
if (typeof auth.output?.refreshToken !== "string" || !auth.output.refreshToken)
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
return true;
|
|
402
|
+
};
|
|
386
403
|
var attachAutoRefresh = (client, profile, storedAuth, store) => {
|
|
387
404
|
let baseRequest = client.request.bind(client);
|
|
388
405
|
let inflightRefresh = null;
|
|
@@ -391,7 +408,7 @@ var attachAutoRefresh = (client, profile, storedAuth, store) => {
|
|
|
391
408
|
await inflightRefresh;
|
|
392
409
|
return;
|
|
393
410
|
}
|
|
394
|
-
if (!
|
|
411
|
+
if (!shouldRefreshAuth(storedAuth)) {
|
|
395
412
|
return;
|
|
396
413
|
}
|
|
397
414
|
inflightRefresh = (async () => {
|
package/dist/index.d.cts
CHANGED
|
@@ -66,13 +66,15 @@ declare let resolveSlatesCliDir: (cwd?: string) => string;
|
|
|
66
66
|
declare let resolveSlatesCliProfilesDir: (cwd?: string) => string;
|
|
67
67
|
declare class SlatesCliStore {
|
|
68
68
|
readonly rootDir: string;
|
|
69
|
+
readonly cliDir: string;
|
|
69
70
|
readonly dirPath: string;
|
|
70
71
|
readonly storePath: string;
|
|
71
72
|
readonly scope: SlatesCliStoreScope | null;
|
|
72
73
|
readonly data: SlatesCliStoreData;
|
|
73
|
-
constructor(rootDir: string, dirPath: string, storePath: string, scope: SlatesCliStoreScope | null, data: SlatesCliStoreData);
|
|
74
|
+
constructor(rootDir: string, cliDir: string, dirPath: string, storePath: string, scope: SlatesCliStoreScope | null, data: SlatesCliStoreData);
|
|
74
75
|
static open(opts?: {
|
|
75
76
|
cwd?: string;
|
|
77
|
+
rootDir?: string;
|
|
76
78
|
scope?: SlatesCliStoreScope;
|
|
77
79
|
storePath?: string;
|
|
78
80
|
}): Promise<SlatesCliStore>;
|
|
@@ -110,6 +112,7 @@ declare class SlatesCliStore {
|
|
|
110
112
|
}
|
|
111
113
|
declare let openSlatesCliStore: (opts?: {
|
|
112
114
|
cwd?: string;
|
|
115
|
+
rootDir?: string;
|
|
113
116
|
scope?: SlatesCliStoreScope;
|
|
114
117
|
storePath?: string;
|
|
115
118
|
}) => Promise<SlatesCliStore>;
|
|
@@ -117,6 +120,7 @@ declare let openSlatesCliStore: (opts?: {
|
|
|
117
120
|
declare let createSlatesClientFromProfile: (profile: SlatesProfileRecord, opts?: {
|
|
118
121
|
cwd?: string;
|
|
119
122
|
store?: SlatesCliStore;
|
|
123
|
+
autoRefresh?: boolean;
|
|
120
124
|
}) => Promise<_slates_client.SlatesProtocolClient>;
|
|
121
125
|
|
|
122
126
|
export { SlatesCliStore, type SlatesCliStoreData, type SlatesCliStoreScope, type SlatesLocalProfileTarget, type SlatesOAuthCredentialRecord, type SlatesProfileRecord, type SlatesProfileTarget, type SlatesStoredAuth, createSlatesClientFromProfile, openSlatesCliStore, resolveSlatesCliDir, resolveSlatesCliProfilesDir, resolveSlatesCliRoot };
|
package/dist/index.d.ts
CHANGED
|
@@ -66,13 +66,15 @@ declare let resolveSlatesCliDir: (cwd?: string) => string;
|
|
|
66
66
|
declare let resolveSlatesCliProfilesDir: (cwd?: string) => string;
|
|
67
67
|
declare class SlatesCliStore {
|
|
68
68
|
readonly rootDir: string;
|
|
69
|
+
readonly cliDir: string;
|
|
69
70
|
readonly dirPath: string;
|
|
70
71
|
readonly storePath: string;
|
|
71
72
|
readonly scope: SlatesCliStoreScope | null;
|
|
72
73
|
readonly data: SlatesCliStoreData;
|
|
73
|
-
constructor(rootDir: string, dirPath: string, storePath: string, scope: SlatesCliStoreScope | null, data: SlatesCliStoreData);
|
|
74
|
+
constructor(rootDir: string, cliDir: string, dirPath: string, storePath: string, scope: SlatesCliStoreScope | null, data: SlatesCliStoreData);
|
|
74
75
|
static open(opts?: {
|
|
75
76
|
cwd?: string;
|
|
77
|
+
rootDir?: string;
|
|
76
78
|
scope?: SlatesCliStoreScope;
|
|
77
79
|
storePath?: string;
|
|
78
80
|
}): Promise<SlatesCliStore>;
|
|
@@ -110,6 +112,7 @@ declare class SlatesCliStore {
|
|
|
110
112
|
}
|
|
111
113
|
declare let openSlatesCliStore: (opts?: {
|
|
112
114
|
cwd?: string;
|
|
115
|
+
rootDir?: string;
|
|
113
116
|
scope?: SlatesCliStoreScope;
|
|
114
117
|
storePath?: string;
|
|
115
118
|
}) => Promise<SlatesCliStore>;
|
|
@@ -117,6 +120,7 @@ declare let openSlatesCliStore: (opts?: {
|
|
|
117
120
|
declare let createSlatesClientFromProfile: (profile: SlatesProfileRecord, opts?: {
|
|
118
121
|
cwd?: string;
|
|
119
122
|
store?: SlatesCliStore;
|
|
123
|
+
autoRefresh?: boolean;
|
|
120
124
|
}) => Promise<_slates_client.SlatesProtocolClient>;
|
|
121
125
|
|
|
122
126
|
export { SlatesCliStore, type SlatesCliStoreData, type SlatesCliStoreScope, type SlatesLocalProfileTarget, type SlatesOAuthCredentialRecord, type SlatesProfileRecord, type SlatesProfileTarget, type SlatesStoredAuth, createSlatesClientFromProfile, openSlatesCliStore, resolveSlatesCliDir, resolveSlatesCliProfilesDir, resolveSlatesCliRoot };
|
package/dist/index.module.js
CHANGED
|
@@ -41,11 +41,13 @@ var resolveSlatesCliRoot = (cwd = process.cwd()) => {
|
|
|
41
41
|
return cwd;
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
|
-
var
|
|
45
|
-
var
|
|
44
|
+
var resolveSlatesCliDirFromRoot = (rootDir) => path.join(rootDir, CLI_DIR_NAME);
|
|
45
|
+
var resolveSlatesCliProfilesDirFromRoot = (rootDir) => path.join(resolveSlatesCliDirFromRoot(rootDir), PROFILES_DIR_NAME);
|
|
46
|
+
var resolveSlatesCliDir = (cwd = process.cwd()) => resolveSlatesCliDirFromRoot(resolveSlatesCliRoot(cwd));
|
|
47
|
+
var resolveSlatesCliProfilesDir = (cwd = process.cwd()) => resolveSlatesCliProfilesDirFromRoot(resolveSlatesCliRoot(cwd));
|
|
46
48
|
var normalizeScopeKey = (scopeKey) => path.posix.normalize(scopeKey.replace(/\\/g, "/")).replace(/^\.?\//, "");
|
|
47
49
|
var splitScopeKey = (scopeKey) => normalizeScopeKey(scopeKey).split("/").filter(Boolean).filter((segment) => segment !== "." && segment !== "..");
|
|
48
|
-
var resolveScopedStoreDir = (rootDir, scopeKey) => path.join(
|
|
50
|
+
var resolveScopedStoreDir = (rootDir, scopeKey) => path.join(resolveSlatesCliProfilesDirFromRoot(rootDir), ...splitScopeKey(scopeKey));
|
|
49
51
|
var resolveScopedStorePath = (rootDir, scopeKey) => path.join(resolveScopedStoreDir(rootDir, scopeKey), STORE_FILE_NAME);
|
|
50
52
|
var inferRootDirFromStorePath = (storePath) => {
|
|
51
53
|
let normalized = path.resolve(storePath);
|
|
@@ -58,7 +60,7 @@ var inferRootDirFromStorePath = (storePath) => {
|
|
|
58
60
|
return path.join(path.sep, ...rootParts);
|
|
59
61
|
};
|
|
60
62
|
var resolveScopeKeyFromStorePath = (rootDir, storePath) => {
|
|
61
|
-
let profilesDir =
|
|
63
|
+
let profilesDir = resolveSlatesCliProfilesDirFromRoot(rootDir);
|
|
62
64
|
let relativeDir = path.relative(profilesDir, path.dirname(storePath));
|
|
63
65
|
if (!relativeDir || relativeDir.startsWith("..") || path.isAbsolute(relativeDir)) {
|
|
64
66
|
return null;
|
|
@@ -89,7 +91,7 @@ var isProfileInScope = (rootDir, scopeKey, profile) => {
|
|
|
89
91
|
return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
90
92
|
};
|
|
91
93
|
var loadMigratedLegacyStore = async (rootDir, scopeKey) => {
|
|
92
|
-
let legacyStorePath = path.join(
|
|
94
|
+
let legacyStorePath = path.join(resolveSlatesCliDirFromRoot(rootDir), STORE_FILE_NAME);
|
|
93
95
|
let legacy = await readStoreData(legacyStorePath);
|
|
94
96
|
if (!legacy) {
|
|
95
97
|
return createEmptyStore();
|
|
@@ -108,22 +110,25 @@ var loadMigratedLegacyStore = async (rootDir, scopeKey) => {
|
|
|
108
110
|
};
|
|
109
111
|
};
|
|
110
112
|
var SlatesCliStore = class _SlatesCliStore {
|
|
111
|
-
constructor(rootDir, dirPath, storePath, scope, data) {
|
|
113
|
+
constructor(rootDir, cliDir, dirPath, storePath, scope, data) {
|
|
112
114
|
this.rootDir = rootDir;
|
|
115
|
+
this.cliDir = cliDir;
|
|
113
116
|
this.dirPath = dirPath;
|
|
114
117
|
this.storePath = storePath;
|
|
115
118
|
this.scope = scope;
|
|
116
119
|
this.data = data;
|
|
117
120
|
}
|
|
118
121
|
rootDir;
|
|
122
|
+
cliDir;
|
|
119
123
|
dirPath;
|
|
120
124
|
storePath;
|
|
121
125
|
scope;
|
|
122
126
|
data;
|
|
123
127
|
static async open(opts = {}) {
|
|
124
|
-
let
|
|
125
|
-
let
|
|
126
|
-
let
|
|
128
|
+
let storageRootDir = opts.storePath ? inferRootDirFromStorePath(opts.storePath) : resolveSlatesCliRoot(opts.cwd);
|
|
129
|
+
let rootDir = opts.rootDir ?? storageRootDir;
|
|
130
|
+
let cliDir = resolveSlatesCliDirFromRoot(storageRootDir);
|
|
131
|
+
let scopeKey = opts.storePath ? resolveScopeKeyFromStorePath(storageRootDir, opts.storePath) : opts.scope?.key ? normalizeScopeKey(opts.scope.key) : null;
|
|
127
132
|
let dirPath = opts.storePath ? path.dirname(opts.storePath) : scopeKey ? resolveScopedStoreDir(rootDir, scopeKey) : cliDir;
|
|
128
133
|
let storePath = opts.storePath ?? (scopeKey ? resolveScopedStorePath(rootDir, scopeKey) : path.join(cliDir, STORE_FILE_NAME));
|
|
129
134
|
await ensureDir(cliDir);
|
|
@@ -135,12 +140,14 @@ var SlatesCliStore = class _SlatesCliStore {
|
|
|
135
140
|
currentProfileId: parsed.currentProfileId ?? null,
|
|
136
141
|
profiles: parsed.profiles ?? {},
|
|
137
142
|
oauthCredentials: parsed.oauthCredentials ?? {}
|
|
138
|
-
} : scopeKey ? await loadMigratedLegacyStore(rootDir, scopeKey) : createEmptyStore();
|
|
143
|
+
} : opts.storePath ? createEmptyStore() : scopeKey ? await loadMigratedLegacyStore(rootDir, scopeKey) : createEmptyStore();
|
|
139
144
|
if (!parsed) {
|
|
140
|
-
await writeFile(storePath, JSON.stringify(data, null, 2)
|
|
145
|
+
await writeFile(storePath, `${JSON.stringify(data, null, 2)}
|
|
146
|
+
`, "utf-8");
|
|
141
147
|
}
|
|
142
148
|
return new _SlatesCliStore(
|
|
143
149
|
rootDir,
|
|
150
|
+
cliDir,
|
|
144
151
|
dirPath,
|
|
145
152
|
storePath,
|
|
146
153
|
scopeKey ? { key: scopeKey, name: opts.scope?.name } : null,
|
|
@@ -149,9 +156,10 @@ var SlatesCliStore = class _SlatesCliStore {
|
|
|
149
156
|
}
|
|
150
157
|
async save() {
|
|
151
158
|
await ensureDir(this.dirPath);
|
|
152
|
-
await ensureDir(
|
|
153
|
-
await ensureGitIgnore(
|
|
154
|
-
await writeFile(this.storePath, JSON.stringify(this.data, null, 2)
|
|
159
|
+
await ensureDir(this.cliDir);
|
|
160
|
+
await ensureGitIgnore(this.cliDir);
|
|
161
|
+
await writeFile(this.storePath, `${JSON.stringify(this.data, null, 2)}
|
|
162
|
+
`, "utf-8");
|
|
155
163
|
}
|
|
156
164
|
listProfiles() {
|
|
157
165
|
return Object.values(this.data.profiles).sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -295,15 +303,18 @@ var SlatesCliStore = class _SlatesCliStore {
|
|
|
295
303
|
var openSlatesCliStore = async (opts = {}) => SlatesCliStore.open(opts);
|
|
296
304
|
|
|
297
305
|
// src/client.ts
|
|
298
|
-
var resolveEntryPath = (entry,
|
|
306
|
+
var resolveEntryPath = (entry, opts = {}) => {
|
|
299
307
|
if (path2.isAbsolute(entry)) return entry;
|
|
300
|
-
return path2.resolve(resolveSlatesCliRoot(cwd), entry);
|
|
308
|
+
return path2.resolve(opts.rootDir ?? resolveSlatesCliRoot(opts.cwd), entry);
|
|
301
309
|
};
|
|
302
|
-
var loadSlateFromProfile = async (profile,
|
|
310
|
+
var loadSlateFromProfile = async (profile, opts = {}) => {
|
|
303
311
|
if (profile.target.type !== "local") {
|
|
304
312
|
throw new Error(`Unsupported profile target type: ${profile.target.type}`);
|
|
305
313
|
}
|
|
306
|
-
let modulePath = resolveEntryPath(profile.target.entry,
|
|
314
|
+
let modulePath = resolveEntryPath(profile.target.entry, {
|
|
315
|
+
cwd: opts.cwd,
|
|
316
|
+
rootDir: opts.store?.rootDir
|
|
317
|
+
});
|
|
307
318
|
let loaded = await import(pathToFileURL(modulePath).href);
|
|
308
319
|
let exportName = profile.target.exportName;
|
|
309
320
|
let slate = (exportName ? loaded[exportName] : void 0) ?? loaded.provider ?? loaded.slate ?? loaded.default;
|
|
@@ -316,7 +327,7 @@ var loadSlateFromProfile = async (profile, cwd) => {
|
|
|
316
327
|
};
|
|
317
328
|
var createSlatesClientFromProfile = async (profile, opts = {}) => {
|
|
318
329
|
let firstAuth = Object.values(profile.auth)[0];
|
|
319
|
-
let slate = await loadSlateFromProfile(profile, opts
|
|
330
|
+
let slate = await loadSlateFromProfile(profile, opts);
|
|
320
331
|
let client = createSlatesClient({
|
|
321
332
|
transport: createLocalSlateTransport({ slate }),
|
|
322
333
|
state: {
|
|
@@ -328,20 +339,26 @@ var createSlatesClientFromProfile = async (profile, opts = {}) => {
|
|
|
328
339
|
} : null
|
|
329
340
|
}
|
|
330
341
|
});
|
|
331
|
-
if (firstAuth) {
|
|
342
|
+
if (firstAuth && opts.autoRefresh !== false) {
|
|
332
343
|
attachAutoRefresh(client, profile, firstAuth, opts.store);
|
|
333
344
|
}
|
|
334
345
|
return client;
|
|
335
346
|
};
|
|
336
347
|
var EXPIRY_BUFFER_MS = 60 * 1e3;
|
|
337
|
-
var
|
|
338
|
-
if (auth.authType !== "auth.oauth") return false;
|
|
339
|
-
if (!auth.clientId || !auth.clientSecret) return false;
|
|
340
|
-
if (typeof auth.output?.refreshToken !== "string" || !auth.output.refreshToken) return false;
|
|
348
|
+
var hasExpiredOrExpiringOutput = (auth) => {
|
|
341
349
|
if (typeof auth.output?.expiresAt !== "string" || !auth.output.expiresAt) return false;
|
|
342
350
|
let expiresAt = Date.parse(auth.output.expiresAt);
|
|
343
351
|
return Number.isFinite(expiresAt) && expiresAt <= Date.now() + EXPIRY_BUFFER_MS;
|
|
344
352
|
};
|
|
353
|
+
var shouldRefreshAuth = (auth) => {
|
|
354
|
+
if (!hasExpiredOrExpiringOutput(auth)) return false;
|
|
355
|
+
if (auth.authType === "auth.oauth") {
|
|
356
|
+
if (!auth.clientId || !auth.clientSecret) return false;
|
|
357
|
+
if (typeof auth.output?.refreshToken !== "string" || !auth.output.refreshToken)
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
return true;
|
|
361
|
+
};
|
|
345
362
|
var attachAutoRefresh = (client, profile, storedAuth, store) => {
|
|
346
363
|
let baseRequest = client.request.bind(client);
|
|
347
364
|
let inflightRefresh = null;
|
|
@@ -350,7 +367,7 @@ var attachAutoRefresh = (client, profile, storedAuth, store) => {
|
|
|
350
367
|
await inflightRefresh;
|
|
351
368
|
return;
|
|
352
369
|
}
|
|
353
|
-
if (!
|
|
370
|
+
if (!shouldRefreshAuth(storedAuth)) {
|
|
354
371
|
return;
|
|
355
372
|
}
|
|
356
373
|
inflightRefresh = (async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slates/profiles",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.20",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -26,16 +26,16 @@
|
|
|
26
26
|
"unpkg": "./dist/index.module.js",
|
|
27
27
|
"scripts": {
|
|
28
28
|
"test": "vitest run --passWithNoTests",
|
|
29
|
-
"lint": "prettier src/**/*.ts --check",
|
|
30
29
|
"build": "tsup --config ../../tsup.packages.config.ts --external async_hooks",
|
|
31
|
-
"typecheck": "tsc --noEmit"
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"biome:check": "biome check --write src"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@slates/client": "1.0.0-rc.
|
|
35
|
-
"@slates/proto": "1.0.0-rc.
|
|
34
|
+
"@slates/client": "1.0.0-rc.22",
|
|
35
|
+
"@slates/proto": "1.0.0-rc.19"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@slates/provider": "1.0.0-rc.
|
|
38
|
+
"@slates/provider": "1.0.0-rc.23",
|
|
39
39
|
"@slates/tsconfig": "1.0.0-rc.1",
|
|
40
40
|
"typescript": "5.8.2",
|
|
41
41
|
"vitest": "^3.1.2",
|
package/src/client.ts
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import { createLocalSlateTransport, createSlatesClient } from '@slates/client';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { pathToFileURL } from 'url';
|
|
4
|
-
import { resolveSlatesCliRoot, SlatesCliStore } from './store';
|
|
5
|
-
import { SlatesProfileRecord, SlatesStoredAuth } from './types';
|
|
4
|
+
import { resolveSlatesCliRoot, type SlatesCliStore } from './store';
|
|
5
|
+
import type { SlatesProfileRecord, SlatesStoredAuth } from './types';
|
|
6
6
|
|
|
7
|
-
let resolveEntryPath = (entry: string, cwd?: string) => {
|
|
7
|
+
let resolveEntryPath = (entry: string, opts: { cwd?: string; rootDir?: string } = {}) => {
|
|
8
8
|
if (path.isAbsolute(entry)) return entry;
|
|
9
|
-
return path.resolve(resolveSlatesCliRoot(cwd), entry);
|
|
9
|
+
return path.resolve(opts.rootDir ?? resolveSlatesCliRoot(opts.cwd), entry);
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
let loadSlateFromProfile = async (
|
|
12
|
+
let loadSlateFromProfile = async (
|
|
13
|
+
profile: SlatesProfileRecord,
|
|
14
|
+
opts: { cwd?: string; store?: SlatesCliStore } = {}
|
|
15
|
+
) => {
|
|
13
16
|
if (profile.target.type !== 'local') {
|
|
14
17
|
throw new Error(`Unsupported profile target type: ${(profile.target as any).type}`);
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
let modulePath = resolveEntryPath(profile.target.entry,
|
|
20
|
+
let modulePath = resolveEntryPath(profile.target.entry, {
|
|
21
|
+
cwd: opts.cwd,
|
|
22
|
+
rootDir: opts.store?.rootDir
|
|
23
|
+
});
|
|
18
24
|
let loaded = await import(pathToFileURL(modulePath).href);
|
|
19
25
|
let exportName = profile.target.exportName;
|
|
20
26
|
|
|
@@ -37,10 +43,10 @@ let loadSlateFromProfile = async (profile: SlatesProfileRecord, cwd?: string) =>
|
|
|
37
43
|
|
|
38
44
|
export let createSlatesClientFromProfile = async (
|
|
39
45
|
profile: SlatesProfileRecord,
|
|
40
|
-
opts: { cwd?: string; store?: SlatesCliStore } = {}
|
|
46
|
+
opts: { cwd?: string; store?: SlatesCliStore; autoRefresh?: boolean } = {}
|
|
41
47
|
) => {
|
|
42
48
|
let firstAuth = Object.values(profile.auth)[0];
|
|
43
|
-
let slate = await loadSlateFromProfile(profile, opts
|
|
49
|
+
let slate = await loadSlateFromProfile(profile, opts);
|
|
44
50
|
let client = createSlatesClient({
|
|
45
51
|
transport: createLocalSlateTransport({ slate }),
|
|
46
52
|
state: {
|
|
@@ -55,7 +61,7 @@ export let createSlatesClientFromProfile = async (
|
|
|
55
61
|
}
|
|
56
62
|
});
|
|
57
63
|
|
|
58
|
-
if (firstAuth) {
|
|
64
|
+
if (firstAuth && opts.autoRefresh !== false) {
|
|
59
65
|
attachAutoRefresh(client, profile, firstAuth, opts.store);
|
|
60
66
|
}
|
|
61
67
|
|
|
@@ -64,16 +70,25 @@ export let createSlatesClientFromProfile = async (
|
|
|
64
70
|
|
|
65
71
|
let EXPIRY_BUFFER_MS = 60 * 1000;
|
|
66
72
|
|
|
67
|
-
let
|
|
68
|
-
if (auth.authType !== 'auth.oauth') return false;
|
|
69
|
-
if (!auth.clientId || !auth.clientSecret) return false;
|
|
70
|
-
if (typeof auth.output?.refreshToken !== 'string' || !auth.output.refreshToken) return false;
|
|
73
|
+
let hasExpiredOrExpiringOutput = (auth: SlatesStoredAuth) => {
|
|
71
74
|
if (typeof auth.output?.expiresAt !== 'string' || !auth.output.expiresAt) return false;
|
|
72
75
|
|
|
73
76
|
let expiresAt = Date.parse(auth.output.expiresAt);
|
|
74
77
|
return Number.isFinite(expiresAt) && expiresAt <= Date.now() + EXPIRY_BUFFER_MS;
|
|
75
78
|
};
|
|
76
79
|
|
|
80
|
+
let shouldRefreshAuth = (auth: SlatesStoredAuth) => {
|
|
81
|
+
if (!hasExpiredOrExpiringOutput(auth)) return false;
|
|
82
|
+
|
|
83
|
+
if (auth.authType === 'auth.oauth') {
|
|
84
|
+
if (!auth.clientId || !auth.clientSecret) return false;
|
|
85
|
+
if (typeof auth.output?.refreshToken !== 'string' || !auth.output.refreshToken)
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return true;
|
|
90
|
+
};
|
|
91
|
+
|
|
77
92
|
let attachAutoRefresh = (
|
|
78
93
|
client: ReturnType<typeof createSlatesClient>,
|
|
79
94
|
profile: SlatesProfileRecord,
|
|
@@ -89,7 +104,7 @@ let attachAutoRefresh = (
|
|
|
89
104
|
return;
|
|
90
105
|
}
|
|
91
106
|
|
|
92
|
-
if (!
|
|
107
|
+
if (!shouldRefreshAuth(storedAuth)) {
|
|
93
108
|
return;
|
|
94
109
|
}
|
|
95
110
|
|
package/src/store.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, mkdtemp, rm, writeFile } from 'fs/promises';
|
|
1
|
+
import { access, mkdir, mkdtemp, rm, writeFile } from 'fs/promises';
|
|
2
2
|
import { tmpdir } from 'os';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
@@ -55,7 +55,56 @@ describe('@slates/profiles store', () => {
|
|
|
55
55
|
expect(reopenedProfile.config).toEqual({ prefix: 'Hello' });
|
|
56
56
|
expect(reopened.getAuth(profile.id, 'token_auth')?.output).toEqual({ token: 'abc' });
|
|
57
57
|
expect(reopened.scope?.key).toBe('integrations/demo');
|
|
58
|
-
expect(reopened.
|
|
58
|
+
expect(reopened.cliDir).toBe(path.join(cwd, '.slates-cli'));
|
|
59
|
+
expect(reopened.storePath).toBe(
|
|
60
|
+
path.join(cwd, '.slates-cli', 'profiles', 'integrations', 'demo', 'store.json')
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('opens an external scoped store with a separate logical root', async () => {
|
|
65
|
+
let workspaceRoot = await createTempDir();
|
|
66
|
+
let storageRoot = await createTempDir();
|
|
67
|
+
let storePath = path.join(
|
|
68
|
+
storageRoot,
|
|
69
|
+
'.slates-cli',
|
|
70
|
+
'profiles',
|
|
71
|
+
'integrations',
|
|
72
|
+
'demo',
|
|
73
|
+
'store.json'
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
let store = await SlatesCliStore.open({
|
|
77
|
+
storePath,
|
|
78
|
+
rootDir: workspaceRoot
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
let profile = store.upsertProfile({
|
|
82
|
+
name: 'Demo',
|
|
83
|
+
target: {
|
|
84
|
+
type: 'local',
|
|
85
|
+
entry: 'integrations/demo/src/index.ts',
|
|
86
|
+
exportName: 'provider'
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
store.setCurrentProfile(profile.id);
|
|
90
|
+
await store.save();
|
|
91
|
+
|
|
92
|
+
let reopened = await SlatesCliStore.open({
|
|
93
|
+
storePath,
|
|
94
|
+
rootDir: workspaceRoot
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(reopened.rootDir).toBe(workspaceRoot);
|
|
98
|
+
expect(reopened.cliDir).toBe(path.join(storageRoot, '.slates-cli'));
|
|
99
|
+
expect(reopened.scope?.key).toBe('integrations/demo');
|
|
100
|
+
expect(reopened.getProfile()?.target.entry).toBe('integrations/demo/src/index.ts');
|
|
101
|
+
|
|
102
|
+
await expect(
|
|
103
|
+
access(path.join(storageRoot, '.slates-cli', '.gitignore'))
|
|
104
|
+
).resolves.toBeUndefined();
|
|
105
|
+
await expect(access(path.join(workspaceRoot, '.slates-cli'))).rejects.toMatchObject({
|
|
106
|
+
code: 'ENOENT'
|
|
107
|
+
});
|
|
59
108
|
});
|
|
60
109
|
|
|
61
110
|
it('migrates matching legacy profiles into an integration-scoped store', async () => {
|
|
@@ -182,6 +231,62 @@ export let provider = Slate.create({
|
|
|
182
231
|
expect(provider.provider.name).toBe('Demo Slate');
|
|
183
232
|
});
|
|
184
233
|
|
|
234
|
+
it('resolves profile entry paths against the store root when the store lives elsewhere', async () => {
|
|
235
|
+
let workspaceRoot = await createTempDir();
|
|
236
|
+
let storageRoot = await createTempDir();
|
|
237
|
+
let entry = path.join(workspaceRoot, 'integrations', 'demo-slate.mjs');
|
|
238
|
+
await mkdir(path.dirname(entry), { recursive: true });
|
|
239
|
+
await writeFile(
|
|
240
|
+
entry,
|
|
241
|
+
`
|
|
242
|
+
import { Slate, SlateAuth, SlateConfig, SlateSpecification } from '@slates/provider';
|
|
243
|
+
import { z } from 'zod';
|
|
244
|
+
|
|
245
|
+
let config = SlateConfig.create(z.object({})).getDefaultConfig(() => ({}));
|
|
246
|
+
let auth = SlateAuth.create();
|
|
247
|
+
|
|
248
|
+
export let provider = Slate.create({
|
|
249
|
+
spec: SlateSpecification.create({
|
|
250
|
+
key: 'external-demo',
|
|
251
|
+
name: 'External Demo',
|
|
252
|
+
description: 'Store root resolution test',
|
|
253
|
+
config,
|
|
254
|
+
auth
|
|
255
|
+
}),
|
|
256
|
+
tools: [],
|
|
257
|
+
triggers: []
|
|
258
|
+
});
|
|
259
|
+
`,
|
|
260
|
+
'utf-8'
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
let store = await SlatesCliStore.open({
|
|
264
|
+
storePath: path.join(
|
|
265
|
+
storageRoot,
|
|
266
|
+
'.slates-cli',
|
|
267
|
+
'profiles',
|
|
268
|
+
'integrations',
|
|
269
|
+
'demo',
|
|
270
|
+
'store.json'
|
|
271
|
+
),
|
|
272
|
+
rootDir: workspaceRoot
|
|
273
|
+
});
|
|
274
|
+
let profile = store.upsertProfile({
|
|
275
|
+
name: 'Demo',
|
|
276
|
+
target: {
|
|
277
|
+
type: 'local',
|
|
278
|
+
entry: 'integrations/demo-slate.mjs',
|
|
279
|
+
exportName: 'provider'
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
let client = await createSlatesClientFromProfile(profile, { store });
|
|
284
|
+
let provider = await client.identify();
|
|
285
|
+
|
|
286
|
+
expect(provider.provider.id).toBe('external-demo');
|
|
287
|
+
expect(provider.provider.name).toBe('External Demo');
|
|
288
|
+
});
|
|
289
|
+
|
|
185
290
|
it('stores OAuth client credentials per integration', async () => {
|
|
186
291
|
let cwd = await createTempDir();
|
|
187
292
|
let store = await SlatesCliStore.open({
|
|
@@ -202,7 +307,9 @@ export let provider = Slate.create({
|
|
|
202
307
|
|
|
203
308
|
let reopened = await SlatesCliStore.open({ storePath: store.storePath });
|
|
204
309
|
expect(reopened.getOAuthCredential(credential.id)?.clientId).toBe('client-id');
|
|
205
|
-
expect(reopened.getOAuthCredential('Primary OAuth App')?.clientSecret).toBe(
|
|
310
|
+
expect(reopened.getOAuthCredential('Primary OAuth App')?.clientSecret).toBe(
|
|
311
|
+
'client-secret'
|
|
312
|
+
);
|
|
206
313
|
});
|
|
207
314
|
|
|
208
315
|
it('refreshes expired OAuth auth automatically in the profile-backed client', async () => {
|
|
@@ -312,4 +419,115 @@ export let provider = Slate.create({
|
|
|
312
419
|
token: 'expired-token-refreshed'
|
|
313
420
|
});
|
|
314
421
|
});
|
|
422
|
+
|
|
423
|
+
it('refreshes expired custom auth automatically in the profile-backed client', async () => {
|
|
424
|
+
let cwd = await createTempDir();
|
|
425
|
+
let entry = path.join(cwd, 'custom-slate.mjs');
|
|
426
|
+
await writeFile(
|
|
427
|
+
entry,
|
|
428
|
+
`
|
|
429
|
+
import { Slate, SlateAuth, SlateConfig, SlateSpecification, SlateTool } from '@slates/provider';
|
|
430
|
+
import { z } from 'zod';
|
|
431
|
+
|
|
432
|
+
let config = SlateConfig.create(z.object({})).getDefaultConfig(() => ({}));
|
|
433
|
+
let auth = SlateAuth.create()
|
|
434
|
+
.output(
|
|
435
|
+
z.object({
|
|
436
|
+
token: z.string(),
|
|
437
|
+
expiresAt: z.string().optional()
|
|
438
|
+
})
|
|
439
|
+
)
|
|
440
|
+
.addCustomAuth({
|
|
441
|
+
type: 'auth.custom',
|
|
442
|
+
key: 'custom',
|
|
443
|
+
name: 'Custom',
|
|
444
|
+
inputSchema: z.object({
|
|
445
|
+
tokenPrefix: z.string()
|
|
446
|
+
}),
|
|
447
|
+
getOutput: async ctx => ({
|
|
448
|
+
output: {
|
|
449
|
+
token: \`\${ctx.input.tokenPrefix}-initial\`,
|
|
450
|
+
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString()
|
|
451
|
+
}
|
|
452
|
+
}),
|
|
453
|
+
handleTokenRefresh: async ctx => ({
|
|
454
|
+
output: {
|
|
455
|
+
token: \`\${ctx.input.tokenPrefix}-refreshed\`,
|
|
456
|
+
expiresAt: new Date(Date.now() + 60 * 60 * 1000).toISOString()
|
|
457
|
+
}
|
|
458
|
+
})
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
let spec = SlateSpecification.create({
|
|
462
|
+
key: 'custom-demo',
|
|
463
|
+
name: 'Custom Demo',
|
|
464
|
+
description: 'Custom auth test slate',
|
|
465
|
+
config,
|
|
466
|
+
auth
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
let tool = SlateTool.create(spec, {
|
|
470
|
+
key: 'whoami',
|
|
471
|
+
name: 'Who Am I'
|
|
472
|
+
})
|
|
473
|
+
.input(z.object({}))
|
|
474
|
+
.output(
|
|
475
|
+
z.object({
|
|
476
|
+
token: z.string()
|
|
477
|
+
})
|
|
478
|
+
)
|
|
479
|
+
.handleInvocation(async ctx => ({
|
|
480
|
+
output: {
|
|
481
|
+
token: ctx.auth.token
|
|
482
|
+
}
|
|
483
|
+
}))
|
|
484
|
+
.build();
|
|
485
|
+
|
|
486
|
+
export let provider = Slate.create({
|
|
487
|
+
spec,
|
|
488
|
+
tools: [tool],
|
|
489
|
+
triggers: []
|
|
490
|
+
});
|
|
491
|
+
`,
|
|
492
|
+
'utf-8'
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
let store = await SlatesCliStore.open({
|
|
496
|
+
cwd,
|
|
497
|
+
scope: {
|
|
498
|
+
key: 'integrations/demo',
|
|
499
|
+
name: 'demo'
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
let profile = store.upsertProfile({
|
|
503
|
+
name: 'Demo',
|
|
504
|
+
target: {
|
|
505
|
+
type: 'local',
|
|
506
|
+
entry: './custom-slate.mjs',
|
|
507
|
+
exportName: 'provider'
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
store.setProfileConfig(profile.id, {});
|
|
511
|
+
store.upsertAuth(profile.id, {
|
|
512
|
+
authMethodId: 'custom',
|
|
513
|
+
authType: 'auth.custom',
|
|
514
|
+
input: {
|
|
515
|
+
tokenPrefix: 'custom-token'
|
|
516
|
+
},
|
|
517
|
+
output: {
|
|
518
|
+
token: 'expired-token',
|
|
519
|
+
expiresAt: new Date(Date.now() - 60 * 1000).toISOString()
|
|
520
|
+
},
|
|
521
|
+
scopes: []
|
|
522
|
+
});
|
|
523
|
+
await store.save();
|
|
524
|
+
|
|
525
|
+
let client = await createSlatesClientFromProfile(profile, { cwd, store });
|
|
526
|
+
let result = await client.invokeTool('whoami', {});
|
|
527
|
+
|
|
528
|
+
expect(result.output).toEqual({ token: 'custom-token-refreshed' });
|
|
529
|
+
expect(store.getAuth(profile.id, 'custom')?.output).toMatchObject({
|
|
530
|
+
token: 'custom-token-refreshed'
|
|
531
|
+
});
|
|
532
|
+
});
|
|
315
533
|
});
|
package/src/store.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { execFileSync } from 'child_process';
|
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
3
|
import { mkdir, readFile, writeFile } from 'fs/promises';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import {
|
|
5
|
+
import type {
|
|
6
6
|
SlatesCliStoreData,
|
|
7
7
|
SlatesCliStoreScope,
|
|
8
8
|
SlatesOAuthCredentialRecord,
|
|
@@ -56,11 +56,16 @@ export let resolveSlatesCliRoot = (cwd: string = process.cwd()) => {
|
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
+
let resolveSlatesCliDirFromRoot = (rootDir: string) => path.join(rootDir, CLI_DIR_NAME);
|
|
60
|
+
|
|
61
|
+
let resolveSlatesCliProfilesDirFromRoot = (rootDir: string) =>
|
|
62
|
+
path.join(resolveSlatesCliDirFromRoot(rootDir), PROFILES_DIR_NAME);
|
|
63
|
+
|
|
59
64
|
export let resolveSlatesCliDir = (cwd: string = process.cwd()) =>
|
|
60
|
-
|
|
65
|
+
resolveSlatesCliDirFromRoot(resolveSlatesCliRoot(cwd));
|
|
61
66
|
|
|
62
67
|
export let resolveSlatesCliProfilesDir = (cwd: string = process.cwd()) =>
|
|
63
|
-
|
|
68
|
+
resolveSlatesCliProfilesDirFromRoot(resolveSlatesCliRoot(cwd));
|
|
64
69
|
|
|
65
70
|
let normalizeScopeKey = (scopeKey: string) =>
|
|
66
71
|
path.posix.normalize(scopeKey.replace(/\\/g, '/')).replace(/^\.?\//, '');
|
|
@@ -72,7 +77,7 @@ let splitScopeKey = (scopeKey: string) =>
|
|
|
72
77
|
.filter(segment => segment !== '.' && segment !== '..');
|
|
73
78
|
|
|
74
79
|
let resolveScopedStoreDir = (rootDir: string, scopeKey: string) =>
|
|
75
|
-
path.join(
|
|
80
|
+
path.join(resolveSlatesCliProfilesDirFromRoot(rootDir), ...splitScopeKey(scopeKey));
|
|
76
81
|
|
|
77
82
|
let resolveScopedStorePath = (rootDir: string, scopeKey: string) =>
|
|
78
83
|
path.join(resolveScopedStoreDir(rootDir, scopeKey), STORE_FILE_NAME);
|
|
@@ -91,7 +96,7 @@ let inferRootDirFromStorePath = (storePath: string) => {
|
|
|
91
96
|
};
|
|
92
97
|
|
|
93
98
|
let resolveScopeKeyFromStorePath = (rootDir: string, storePath: string) => {
|
|
94
|
-
let profilesDir =
|
|
99
|
+
let profilesDir = resolveSlatesCliProfilesDirFromRoot(rootDir);
|
|
95
100
|
let relativeDir = path.relative(profilesDir, path.dirname(storePath));
|
|
96
101
|
if (!relativeDir || relativeDir.startsWith('..') || path.isAbsolute(relativeDir)) {
|
|
97
102
|
return null;
|
|
@@ -138,7 +143,7 @@ let loadMigratedLegacyStore = async (
|
|
|
138
143
|
rootDir: string,
|
|
139
144
|
scopeKey: string
|
|
140
145
|
): Promise<SlatesCliStoreData> => {
|
|
141
|
-
let legacyStorePath = path.join(
|
|
146
|
+
let legacyStorePath = path.join(resolveSlatesCliDirFromRoot(rootDir), STORE_FILE_NAME);
|
|
142
147
|
let legacy = await readStoreData<SlatesLegacyCliStoreData>(legacyStorePath);
|
|
143
148
|
if (!legacy) {
|
|
144
149
|
return createEmptyStore();
|
|
@@ -166,6 +171,7 @@ let loadMigratedLegacyStore = async (
|
|
|
166
171
|
export class SlatesCliStore {
|
|
167
172
|
constructor(
|
|
168
173
|
readonly rootDir: string,
|
|
174
|
+
readonly cliDir: string,
|
|
169
175
|
readonly dirPath: string,
|
|
170
176
|
readonly storePath: string,
|
|
171
177
|
readonly scope: SlatesCliStoreScope | null,
|
|
@@ -173,14 +179,20 @@ export class SlatesCliStore {
|
|
|
173
179
|
) {}
|
|
174
180
|
|
|
175
181
|
static async open(
|
|
176
|
-
opts: {
|
|
182
|
+
opts: {
|
|
183
|
+
cwd?: string;
|
|
184
|
+
rootDir?: string;
|
|
185
|
+
scope?: SlatesCliStoreScope;
|
|
186
|
+
storePath?: string;
|
|
187
|
+
} = {}
|
|
177
188
|
) {
|
|
178
|
-
let
|
|
189
|
+
let storageRootDir = opts.storePath
|
|
179
190
|
? inferRootDirFromStorePath(opts.storePath)
|
|
180
191
|
: resolveSlatesCliRoot(opts.cwd);
|
|
181
|
-
let
|
|
192
|
+
let rootDir = opts.rootDir ?? storageRootDir;
|
|
193
|
+
let cliDir = resolveSlatesCliDirFromRoot(storageRootDir);
|
|
182
194
|
let scopeKey = opts.storePath
|
|
183
|
-
? resolveScopeKeyFromStorePath(
|
|
195
|
+
? resolveScopeKeyFromStorePath(storageRootDir, opts.storePath)
|
|
184
196
|
: opts.scope?.key
|
|
185
197
|
? normalizeScopeKey(opts.scope.key)
|
|
186
198
|
: null;
|
|
@@ -207,16 +219,19 @@ export class SlatesCliStore {
|
|
|
207
219
|
profiles: parsed.profiles ?? {},
|
|
208
220
|
oauthCredentials: parsed.oauthCredentials ?? {}
|
|
209
221
|
}
|
|
210
|
-
:
|
|
211
|
-
?
|
|
212
|
-
:
|
|
222
|
+
: opts.storePath
|
|
223
|
+
? createEmptyStore()
|
|
224
|
+
: scopeKey
|
|
225
|
+
? await loadMigratedLegacyStore(rootDir, scopeKey)
|
|
226
|
+
: createEmptyStore();
|
|
213
227
|
|
|
214
228
|
if (!parsed) {
|
|
215
|
-
await writeFile(storePath, JSON.stringify(data, null, 2)
|
|
229
|
+
await writeFile(storePath, `${JSON.stringify(data, null, 2)}\n`, 'utf-8');
|
|
216
230
|
}
|
|
217
231
|
|
|
218
232
|
return new SlatesCliStore(
|
|
219
233
|
rootDir,
|
|
234
|
+
cliDir,
|
|
220
235
|
dirPath,
|
|
221
236
|
storePath,
|
|
222
237
|
scopeKey ? { key: scopeKey, name: opts.scope?.name } : null,
|
|
@@ -226,9 +241,9 @@ export class SlatesCliStore {
|
|
|
226
241
|
|
|
227
242
|
async save() {
|
|
228
243
|
await ensureDir(this.dirPath);
|
|
229
|
-
await ensureDir(
|
|
230
|
-
await ensureGitIgnore(
|
|
231
|
-
await writeFile(this.storePath, JSON.stringify(this.data, null, 2)
|
|
244
|
+
await ensureDir(this.cliDir);
|
|
245
|
+
await ensureGitIgnore(this.cliDir);
|
|
246
|
+
await writeFile(this.storePath, `${JSON.stringify(this.data, null, 2)}\n`, 'utf-8');
|
|
232
247
|
}
|
|
233
248
|
|
|
234
249
|
listProfiles() {
|
|
@@ -418,5 +433,10 @@ export class SlatesCliStore {
|
|
|
418
433
|
}
|
|
419
434
|
|
|
420
435
|
export let openSlatesCliStore = async (
|
|
421
|
-
opts: {
|
|
436
|
+
opts: {
|
|
437
|
+
cwd?: string;
|
|
438
|
+
rootDir?: string;
|
|
439
|
+
scope?: SlatesCliStoreScope;
|
|
440
|
+
storePath?: string;
|
|
441
|
+
} = {}
|
|
422
442
|
) => SlatesCliStore.open(opts);
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SlatesJsonObject } from '@slates/client';
|
|
2
|
-
import { SlateAuthenticationMethod, SlatesAction } from '@slates/proto';
|
|
1
|
+
import type { SlatesJsonObject } from '@slates/client';
|
|
2
|
+
import type { SlateAuthenticationMethod, SlatesAction } from '@slates/proto';
|
|
3
3
|
|
|
4
4
|
export interface SlatesStoredAuth {
|
|
5
5
|
id: string;
|