@centia-io/sdk 0.1.3 → 0.2.1
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/LICENSE +21 -21
- package/README.md +570 -570
- package/dist/centia-io-sdk-node.cjs +174 -0
- package/dist/centia-io-sdk-node.d.cts +50 -0
- package/dist/centia-io-sdk-node.d.cts.map +1 -0
- package/dist/centia-io-sdk-node.d.ts +50 -0
- package/dist/centia-io-sdk-node.d.ts.map +1 -0
- package/dist/centia-io-sdk-node.js +174 -0
- package/dist/centia-io-sdk-node.js.map +1 -0
- package/dist/centia-io-sdk.cjs +93 -107
- package/dist/centia-io-sdk.d.cts +122 -34
- package/dist/centia-io-sdk.d.cts.map +1 -1
- package/dist/centia-io-sdk.d.ts +122 -34
- package/dist/centia-io-sdk.d.ts.map +1 -1
- package/dist/centia-io-sdk.js +94 -107
- package/dist/centia-io-sdk.js.map +1 -1
- package/dist/centia-io-sdk.umd.js +93 -107
- package/node.d.ts +1 -0
- package/package.json +48 -43
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
|
|
2
|
+
//#region \0@oxc-project+runtime@0.101.0/helpers/typeof.js
|
|
3
|
+
function _typeof(o) {
|
|
4
|
+
"@babel/helpers - typeof";
|
|
5
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
|
|
6
|
+
return typeof o$1;
|
|
7
|
+
} : function(o$1) {
|
|
8
|
+
return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
|
|
9
|
+
}, _typeof(o);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region \0@oxc-project+runtime@0.101.0/helpers/toPrimitive.js
|
|
14
|
+
function toPrimitive(t, r) {
|
|
15
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
16
|
+
var e = t[Symbol.toPrimitive];
|
|
17
|
+
if (void 0 !== e) {
|
|
18
|
+
var i = e.call(t, r || "default");
|
|
19
|
+
if ("object" != _typeof(i)) return i;
|
|
20
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
21
|
+
}
|
|
22
|
+
return ("string" === r ? String : Number)(t);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region \0@oxc-project+runtime@0.101.0/helpers/toPropertyKey.js
|
|
27
|
+
function toPropertyKey(t) {
|
|
28
|
+
var i = toPrimitive(t, "string");
|
|
29
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region \0@oxc-project+runtime@0.101.0/helpers/defineProperty.js
|
|
34
|
+
function _defineProperty(e, r, t) {
|
|
35
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
36
|
+
value: t,
|
|
37
|
+
enumerable: !0,
|
|
38
|
+
configurable: !0,
|
|
39
|
+
writable: !0
|
|
40
|
+
}) : e[r] = t, e;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region \0@oxc-project+runtime@0.101.0/helpers/objectSpread2.js
|
|
45
|
+
function ownKeys(e, r) {
|
|
46
|
+
var t = Object.keys(e);
|
|
47
|
+
if (Object.getOwnPropertySymbols) {
|
|
48
|
+
var o = Object.getOwnPropertySymbols(e);
|
|
49
|
+
r && (o = o.filter(function(r$1) {
|
|
50
|
+
return Object.getOwnPropertyDescriptor(e, r$1).enumerable;
|
|
51
|
+
})), t.push.apply(t, o);
|
|
52
|
+
}
|
|
53
|
+
return t;
|
|
54
|
+
}
|
|
55
|
+
function _objectSpread2(e) {
|
|
56
|
+
for (var r = 1; r < arguments.length; r++) {
|
|
57
|
+
var t = null != arguments[r] ? arguments[r] : {};
|
|
58
|
+
r % 2 ? ownKeys(Object(t), !0).forEach(function(r$1) {
|
|
59
|
+
_defineProperty(e, r$1, t[r$1]);
|
|
60
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r$1) {
|
|
61
|
+
Object.defineProperty(e, r$1, Object.getOwnPropertyDescriptor(t, r$1));
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return e;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/auth/configstoreTokenStore.ts
|
|
69
|
+
const LOCK_RETRIES = 5;
|
|
70
|
+
const LOCK_RETRY_MIN_MS = 100;
|
|
71
|
+
const LOCK_RETRY_MAX_MS = 500;
|
|
72
|
+
const LOCK_STALE_MS = 1e4;
|
|
73
|
+
/**
|
|
74
|
+
* Build a Node-only file-backed {@link TokenStore} that persists OAuth
|
|
75
|
+
* credentials at `~/.config/configstore/<name>.json` (or
|
|
76
|
+
* `$XDG_CONFIG_HOME/configstore/<name>.json` if set), with both in-process
|
|
77
|
+
* and cross-process write safety.
|
|
78
|
+
*
|
|
79
|
+
* **Shared-state intent.** The `name` is the file name on disk. Two processes
|
|
80
|
+
* (e.g. `gc2-cli` and a local MCP server) that pass the same name share the
|
|
81
|
+
* same on-disk credentials and therefore the same login session. The default
|
|
82
|
+
* `'gc2-env'` matches the name `gc2-cli` already uses, so a one-time
|
|
83
|
+
* `gc2 login` is observable to every process that calls
|
|
84
|
+
* `createConfigstoreTokenStore()` with no argument. Pass a different name
|
|
85
|
+
* to isolate.
|
|
86
|
+
*
|
|
87
|
+
* **In-process correctness.** A serial promise chain on `set()` ensures
|
|
88
|
+
* concurrent same-process calls do not race on the shared configstore cache.
|
|
89
|
+
*
|
|
90
|
+
* **Cross-process correctness.** `proper-lockfile` serializes the
|
|
91
|
+
* read-merge-write critical section across processes so two simultaneous
|
|
92
|
+
* `set()` calls from different processes cannot corrupt the file.
|
|
93
|
+
*
|
|
94
|
+
* **Node-only.** The dynamic imports keep `configstore` and `proper-lockfile`
|
|
95
|
+
* out of browser bundles even when this module is imported through the SDK
|
|
96
|
+
* barrel. Calling this function in a browser environment will fail at
|
|
97
|
+
* runtime when the deferred `await import('configstore')` cannot resolve.
|
|
98
|
+
*
|
|
99
|
+
* @param name - configstore file name (without `.json`). Default `'gc2-env'`
|
|
100
|
+
* matches `gc2-cli`'s configstore so credentials are shared.
|
|
101
|
+
* @returns A {@link TokenStore} suitable for passing to {@link createTokenProvider}.
|
|
102
|
+
*/
|
|
103
|
+
function createConfigstoreTokenStore(name = "gc2-env") {
|
|
104
|
+
let configstoreInstance = null;
|
|
105
|
+
let setChain = Promise.resolve();
|
|
106
|
+
async function getConfigstore() {
|
|
107
|
+
var _default;
|
|
108
|
+
if (configstoreInstance) return configstoreInstance;
|
|
109
|
+
const mod = await import("configstore");
|
|
110
|
+
const Configstore = (_default = mod.default) !== null && _default !== void 0 ? _default : mod;
|
|
111
|
+
const { homedir } = await import("node:os");
|
|
112
|
+
const { join } = await import("node:path");
|
|
113
|
+
configstoreInstance = new Configstore(name, void 0, { configPath: join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "configstore", `${name}.json`) });
|
|
114
|
+
return configstoreInstance;
|
|
115
|
+
}
|
|
116
|
+
async function getLockfile() {
|
|
117
|
+
var _default2;
|
|
118
|
+
const mod = await import("proper-lockfile");
|
|
119
|
+
return (_default2 = mod.default) !== null && _default2 !== void 0 ? _default2 : mod;
|
|
120
|
+
}
|
|
121
|
+
function readAll(cs) {
|
|
122
|
+
const result = {};
|
|
123
|
+
const token = cs.get("token");
|
|
124
|
+
const refresh_token = cs.get("refresh_token");
|
|
125
|
+
const host = cs.get("host");
|
|
126
|
+
if (token !== void 0) result.token = token;
|
|
127
|
+
if (refresh_token !== void 0) result.refresh_token = refresh_token;
|
|
128
|
+
if (host !== void 0) result.host = host;
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
async function doLockedSet(patch) {
|
|
132
|
+
const cs = await getConfigstore();
|
|
133
|
+
const lockfile = await getLockfile();
|
|
134
|
+
const filePath = cs.path;
|
|
135
|
+
const { mkdirSync, writeFileSync } = await import("node:fs");
|
|
136
|
+
const { dirname } = await import("node:path");
|
|
137
|
+
try {
|
|
138
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
139
|
+
writeFileSync(filePath, "{}", { flag: "wx" });
|
|
140
|
+
} catch (e) {
|
|
141
|
+
if ((e === null || e === void 0 ? void 0 : e.code) !== "EEXIST") throw e;
|
|
142
|
+
}
|
|
143
|
+
const release = await lockfile.lock(filePath, {
|
|
144
|
+
retries: {
|
|
145
|
+
retries: LOCK_RETRIES,
|
|
146
|
+
minTimeout: LOCK_RETRY_MIN_MS,
|
|
147
|
+
maxTimeout: LOCK_RETRY_MAX_MS,
|
|
148
|
+
factor: 2
|
|
149
|
+
},
|
|
150
|
+
stale: LOCK_STALE_MS,
|
|
151
|
+
realpath: false
|
|
152
|
+
});
|
|
153
|
+
try {
|
|
154
|
+
configstoreInstance = null;
|
|
155
|
+
const fresh = await getConfigstore();
|
|
156
|
+
fresh.all = _objectSpread2(_objectSpread2({}, readAll(fresh)), patch);
|
|
157
|
+
} finally {
|
|
158
|
+
await release();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
async get() {
|
|
163
|
+
return readAll(await getConfigstore());
|
|
164
|
+
},
|
|
165
|
+
async set(patch) {
|
|
166
|
+
const next = setChain.then(() => doLockedSet(patch));
|
|
167
|
+
setChain = next.catch(() => {});
|
|
168
|
+
return next;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
//#endregion
|
|
174
|
+
exports.createConfigstoreTokenStore = createConfigstoreTokenStore;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/auth/types.d.ts
|
|
2
|
+
|
|
3
|
+
interface StoredCredentials {
|
|
4
|
+
token?: string;
|
|
5
|
+
refresh_token?: string;
|
|
6
|
+
host?: string;
|
|
7
|
+
user?: string;
|
|
8
|
+
database?: string;
|
|
9
|
+
superUser?: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface TokenStore {
|
|
12
|
+
get(): Promise<StoredCredentials>;
|
|
13
|
+
set(patch: Partial<StoredCredentials>): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/auth/configstoreTokenStore.d.ts
|
|
17
|
+
/**
|
|
18
|
+
* Build a Node-only file-backed {@link TokenStore} that persists OAuth
|
|
19
|
+
* credentials at `~/.config/configstore/<name>.json` (or
|
|
20
|
+
* `$XDG_CONFIG_HOME/configstore/<name>.json` if set), with both in-process
|
|
21
|
+
* and cross-process write safety.
|
|
22
|
+
*
|
|
23
|
+
* **Shared-state intent.** The `name` is the file name on disk. Two processes
|
|
24
|
+
* (e.g. `gc2-cli` and a local MCP server) that pass the same name share the
|
|
25
|
+
* same on-disk credentials and therefore the same login session. The default
|
|
26
|
+
* `'gc2-env'` matches the name `gc2-cli` already uses, so a one-time
|
|
27
|
+
* `gc2 login` is observable to every process that calls
|
|
28
|
+
* `createConfigstoreTokenStore()` with no argument. Pass a different name
|
|
29
|
+
* to isolate.
|
|
30
|
+
*
|
|
31
|
+
* **In-process correctness.** A serial promise chain on `set()` ensures
|
|
32
|
+
* concurrent same-process calls do not race on the shared configstore cache.
|
|
33
|
+
*
|
|
34
|
+
* **Cross-process correctness.** `proper-lockfile` serializes the
|
|
35
|
+
* read-merge-write critical section across processes so two simultaneous
|
|
36
|
+
* `set()` calls from different processes cannot corrupt the file.
|
|
37
|
+
*
|
|
38
|
+
* **Node-only.** The dynamic imports keep `configstore` and `proper-lockfile`
|
|
39
|
+
* out of browser bundles even when this module is imported through the SDK
|
|
40
|
+
* barrel. Calling this function in a browser environment will fail at
|
|
41
|
+
* runtime when the deferred `await import('configstore')` cannot resolve.
|
|
42
|
+
*
|
|
43
|
+
* @param name - configstore file name (without `.json`). Default `'gc2-env'`
|
|
44
|
+
* matches `gc2-cli`'s configstore so credentials are shared.
|
|
45
|
+
* @returns A {@link TokenStore} suitable for passing to {@link createTokenProvider}.
|
|
46
|
+
*/
|
|
47
|
+
declare function createConfigstoreTokenStore(name?: string): TokenStore;
|
|
48
|
+
//#endregion
|
|
49
|
+
export { createConfigstoreTokenStore };
|
|
50
|
+
//# sourceMappingURL=centia-io-sdk-node.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"centia-io-sdk-node.d.cts","names":[],"sources":["../src/auth/types.ts","../src/auth/configstoreTokenStore.ts"],"sourcesContent":[],"mappings":";;AAoB4C,UAX3B,iBAAA,CAW2B;EAAO,KAAA,CAAA,EAAA,MAAA;;;;;;;UAFlC,UAAA;SACN,QAAQ;aACJ,QAAQ,qBAAqB;ACwB5C;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;iBAAgB,2BAAA,iBAA+C"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/auth/types.d.ts
|
|
2
|
+
|
|
3
|
+
interface StoredCredentials {
|
|
4
|
+
token?: string;
|
|
5
|
+
refresh_token?: string;
|
|
6
|
+
host?: string;
|
|
7
|
+
user?: string;
|
|
8
|
+
database?: string;
|
|
9
|
+
superUser?: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface TokenStore {
|
|
12
|
+
get(): Promise<StoredCredentials>;
|
|
13
|
+
set(patch: Partial<StoredCredentials>): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/auth/configstoreTokenStore.d.ts
|
|
17
|
+
/**
|
|
18
|
+
* Build a Node-only file-backed {@link TokenStore} that persists OAuth
|
|
19
|
+
* credentials at `~/.config/configstore/<name>.json` (or
|
|
20
|
+
* `$XDG_CONFIG_HOME/configstore/<name>.json` if set), with both in-process
|
|
21
|
+
* and cross-process write safety.
|
|
22
|
+
*
|
|
23
|
+
* **Shared-state intent.** The `name` is the file name on disk. Two processes
|
|
24
|
+
* (e.g. `gc2-cli` and a local MCP server) that pass the same name share the
|
|
25
|
+
* same on-disk credentials and therefore the same login session. The default
|
|
26
|
+
* `'gc2-env'` matches the name `gc2-cli` already uses, so a one-time
|
|
27
|
+
* `gc2 login` is observable to every process that calls
|
|
28
|
+
* `createConfigstoreTokenStore()` with no argument. Pass a different name
|
|
29
|
+
* to isolate.
|
|
30
|
+
*
|
|
31
|
+
* **In-process correctness.** A serial promise chain on `set()` ensures
|
|
32
|
+
* concurrent same-process calls do not race on the shared configstore cache.
|
|
33
|
+
*
|
|
34
|
+
* **Cross-process correctness.** `proper-lockfile` serializes the
|
|
35
|
+
* read-merge-write critical section across processes so two simultaneous
|
|
36
|
+
* `set()` calls from different processes cannot corrupt the file.
|
|
37
|
+
*
|
|
38
|
+
* **Node-only.** The dynamic imports keep `configstore` and `proper-lockfile`
|
|
39
|
+
* out of browser bundles even when this module is imported through the SDK
|
|
40
|
+
* barrel. Calling this function in a browser environment will fail at
|
|
41
|
+
* runtime when the deferred `await import('configstore')` cannot resolve.
|
|
42
|
+
*
|
|
43
|
+
* @param name - configstore file name (without `.json`). Default `'gc2-env'`
|
|
44
|
+
* matches `gc2-cli`'s configstore so credentials are shared.
|
|
45
|
+
* @returns A {@link TokenStore} suitable for passing to {@link createTokenProvider}.
|
|
46
|
+
*/
|
|
47
|
+
declare function createConfigstoreTokenStore(name?: string): TokenStore;
|
|
48
|
+
//#endregion
|
|
49
|
+
export { createConfigstoreTokenStore };
|
|
50
|
+
//# sourceMappingURL=centia-io-sdk-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"centia-io-sdk-node.d.ts","names":[],"sources":["../src/auth/types.ts","../src/auth/configstoreTokenStore.ts"],"sourcesContent":[],"mappings":";;AAoB4C,UAX3B,iBAAA,CAW2B;EAAO,KAAA,CAAA,EAAA,MAAA;;;;;;;UAFlC,UAAA;SACN,QAAQ;aACJ,QAAQ,qBAAqB;ACwB5C;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;iBAAgB,2BAAA,iBAA+C"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
//#region \0@oxc-project+runtime@0.101.0/helpers/typeof.js
|
|
2
|
+
function _typeof(o) {
|
|
3
|
+
"@babel/helpers - typeof";
|
|
4
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
|
|
5
|
+
return typeof o$1;
|
|
6
|
+
} : function(o$1) {
|
|
7
|
+
return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
|
|
8
|
+
}, _typeof(o);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region \0@oxc-project+runtime@0.101.0/helpers/toPrimitive.js
|
|
13
|
+
function toPrimitive(t, r) {
|
|
14
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
15
|
+
var e = t[Symbol.toPrimitive];
|
|
16
|
+
if (void 0 !== e) {
|
|
17
|
+
var i = e.call(t, r || "default");
|
|
18
|
+
if ("object" != _typeof(i)) return i;
|
|
19
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
20
|
+
}
|
|
21
|
+
return ("string" === r ? String : Number)(t);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region \0@oxc-project+runtime@0.101.0/helpers/toPropertyKey.js
|
|
26
|
+
function toPropertyKey(t) {
|
|
27
|
+
var i = toPrimitive(t, "string");
|
|
28
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region \0@oxc-project+runtime@0.101.0/helpers/defineProperty.js
|
|
33
|
+
function _defineProperty(e, r, t) {
|
|
34
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
35
|
+
value: t,
|
|
36
|
+
enumerable: !0,
|
|
37
|
+
configurable: !0,
|
|
38
|
+
writable: !0
|
|
39
|
+
}) : e[r] = t, e;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region \0@oxc-project+runtime@0.101.0/helpers/objectSpread2.js
|
|
44
|
+
function ownKeys(e, r) {
|
|
45
|
+
var t = Object.keys(e);
|
|
46
|
+
if (Object.getOwnPropertySymbols) {
|
|
47
|
+
var o = Object.getOwnPropertySymbols(e);
|
|
48
|
+
r && (o = o.filter(function(r$1) {
|
|
49
|
+
return Object.getOwnPropertyDescriptor(e, r$1).enumerable;
|
|
50
|
+
})), t.push.apply(t, o);
|
|
51
|
+
}
|
|
52
|
+
return t;
|
|
53
|
+
}
|
|
54
|
+
function _objectSpread2(e) {
|
|
55
|
+
for (var r = 1; r < arguments.length; r++) {
|
|
56
|
+
var t = null != arguments[r] ? arguments[r] : {};
|
|
57
|
+
r % 2 ? ownKeys(Object(t), !0).forEach(function(r$1) {
|
|
58
|
+
_defineProperty(e, r$1, t[r$1]);
|
|
59
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r$1) {
|
|
60
|
+
Object.defineProperty(e, r$1, Object.getOwnPropertyDescriptor(t, r$1));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return e;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/auth/configstoreTokenStore.ts
|
|
68
|
+
const LOCK_RETRIES = 5;
|
|
69
|
+
const LOCK_RETRY_MIN_MS = 100;
|
|
70
|
+
const LOCK_RETRY_MAX_MS = 500;
|
|
71
|
+
const LOCK_STALE_MS = 1e4;
|
|
72
|
+
/**
|
|
73
|
+
* Build a Node-only file-backed {@link TokenStore} that persists OAuth
|
|
74
|
+
* credentials at `~/.config/configstore/<name>.json` (or
|
|
75
|
+
* `$XDG_CONFIG_HOME/configstore/<name>.json` if set), with both in-process
|
|
76
|
+
* and cross-process write safety.
|
|
77
|
+
*
|
|
78
|
+
* **Shared-state intent.** The `name` is the file name on disk. Two processes
|
|
79
|
+
* (e.g. `gc2-cli` and a local MCP server) that pass the same name share the
|
|
80
|
+
* same on-disk credentials and therefore the same login session. The default
|
|
81
|
+
* `'gc2-env'` matches the name `gc2-cli` already uses, so a one-time
|
|
82
|
+
* `gc2 login` is observable to every process that calls
|
|
83
|
+
* `createConfigstoreTokenStore()` with no argument. Pass a different name
|
|
84
|
+
* to isolate.
|
|
85
|
+
*
|
|
86
|
+
* **In-process correctness.** A serial promise chain on `set()` ensures
|
|
87
|
+
* concurrent same-process calls do not race on the shared configstore cache.
|
|
88
|
+
*
|
|
89
|
+
* **Cross-process correctness.** `proper-lockfile` serializes the
|
|
90
|
+
* read-merge-write critical section across processes so two simultaneous
|
|
91
|
+
* `set()` calls from different processes cannot corrupt the file.
|
|
92
|
+
*
|
|
93
|
+
* **Node-only.** The dynamic imports keep `configstore` and `proper-lockfile`
|
|
94
|
+
* out of browser bundles even when this module is imported through the SDK
|
|
95
|
+
* barrel. Calling this function in a browser environment will fail at
|
|
96
|
+
* runtime when the deferred `await import('configstore')` cannot resolve.
|
|
97
|
+
*
|
|
98
|
+
* @param name - configstore file name (without `.json`). Default `'gc2-env'`
|
|
99
|
+
* matches `gc2-cli`'s configstore so credentials are shared.
|
|
100
|
+
* @returns A {@link TokenStore} suitable for passing to {@link createTokenProvider}.
|
|
101
|
+
*/
|
|
102
|
+
function createConfigstoreTokenStore(name = "gc2-env") {
|
|
103
|
+
let configstoreInstance = null;
|
|
104
|
+
let setChain = Promise.resolve();
|
|
105
|
+
async function getConfigstore() {
|
|
106
|
+
var _default;
|
|
107
|
+
if (configstoreInstance) return configstoreInstance;
|
|
108
|
+
const mod = await import("configstore");
|
|
109
|
+
const Configstore = (_default = mod.default) !== null && _default !== void 0 ? _default : mod;
|
|
110
|
+
const { homedir } = await import("node:os");
|
|
111
|
+
const { join } = await import("node:path");
|
|
112
|
+
configstoreInstance = new Configstore(name, void 0, { configPath: join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "configstore", `${name}.json`) });
|
|
113
|
+
return configstoreInstance;
|
|
114
|
+
}
|
|
115
|
+
async function getLockfile() {
|
|
116
|
+
var _default2;
|
|
117
|
+
const mod = await import("proper-lockfile");
|
|
118
|
+
return (_default2 = mod.default) !== null && _default2 !== void 0 ? _default2 : mod;
|
|
119
|
+
}
|
|
120
|
+
function readAll(cs) {
|
|
121
|
+
const result = {};
|
|
122
|
+
const token = cs.get("token");
|
|
123
|
+
const refresh_token = cs.get("refresh_token");
|
|
124
|
+
const host = cs.get("host");
|
|
125
|
+
if (token !== void 0) result.token = token;
|
|
126
|
+
if (refresh_token !== void 0) result.refresh_token = refresh_token;
|
|
127
|
+
if (host !== void 0) result.host = host;
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
async function doLockedSet(patch) {
|
|
131
|
+
const cs = await getConfigstore();
|
|
132
|
+
const lockfile = await getLockfile();
|
|
133
|
+
const filePath = cs.path;
|
|
134
|
+
const { mkdirSync, writeFileSync } = await import("node:fs");
|
|
135
|
+
const { dirname } = await import("node:path");
|
|
136
|
+
try {
|
|
137
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
138
|
+
writeFileSync(filePath, "{}", { flag: "wx" });
|
|
139
|
+
} catch (e) {
|
|
140
|
+
if ((e === null || e === void 0 ? void 0 : e.code) !== "EEXIST") throw e;
|
|
141
|
+
}
|
|
142
|
+
const release = await lockfile.lock(filePath, {
|
|
143
|
+
retries: {
|
|
144
|
+
retries: LOCK_RETRIES,
|
|
145
|
+
minTimeout: LOCK_RETRY_MIN_MS,
|
|
146
|
+
maxTimeout: LOCK_RETRY_MAX_MS,
|
|
147
|
+
factor: 2
|
|
148
|
+
},
|
|
149
|
+
stale: LOCK_STALE_MS,
|
|
150
|
+
realpath: false
|
|
151
|
+
});
|
|
152
|
+
try {
|
|
153
|
+
configstoreInstance = null;
|
|
154
|
+
const fresh = await getConfigstore();
|
|
155
|
+
fresh.all = _objectSpread2(_objectSpread2({}, readAll(fresh)), patch);
|
|
156
|
+
} finally {
|
|
157
|
+
await release();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
async get() {
|
|
162
|
+
return readAll(await getConfigstore());
|
|
163
|
+
},
|
|
164
|
+
async set(patch) {
|
|
165
|
+
const next = setChain.then(() => doLockedSet(patch));
|
|
166
|
+
setChain = next.catch(() => {});
|
|
167
|
+
return next;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
//#endregion
|
|
173
|
+
export { createConfigstoreTokenStore };
|
|
174
|
+
//# sourceMappingURL=centia-io-sdk-node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"centia-io-sdk-node.js","names":["configstoreInstance: any | null","setChain: Promise<void>","Configstore: any","result: StoredCredentials","filePath: string","e: any"],"sources":["../src/auth/configstoreTokenStore.ts"],"sourcesContent":["/**\r\n * @author Martin Høgh <mh@mapcentia.com>\r\n * @copyright 2013-2026 MapCentia ApS\r\n * @license https://opensource.org/license/mit The MIT License\r\n *\r\n */\r\n\r\nimport type { StoredCredentials, TokenStore } from './types'\r\n\r\nconst LOCK_RETRIES = 5\r\nconst LOCK_RETRY_MIN_MS = 100\r\nconst LOCK_RETRY_MAX_MS = 500\r\nconst LOCK_STALE_MS = 10_000\r\n\r\n/**\r\n * Build a Node-only file-backed {@link TokenStore} that persists OAuth\r\n * credentials at `~/.config/configstore/<name>.json` (or\r\n * `$XDG_CONFIG_HOME/configstore/<name>.json` if set), with both in-process\r\n * and cross-process write safety.\r\n *\r\n * **Shared-state intent.** The `name` is the file name on disk. Two processes\r\n * (e.g. `gc2-cli` and a local MCP server) that pass the same name share the\r\n * same on-disk credentials and therefore the same login session. The default\r\n * `'gc2-env'` matches the name `gc2-cli` already uses, so a one-time\r\n * `gc2 login` is observable to every process that calls\r\n * `createConfigstoreTokenStore()` with no argument. Pass a different name\r\n * to isolate.\r\n *\r\n * **In-process correctness.** A serial promise chain on `set()` ensures\r\n * concurrent same-process calls do not race on the shared configstore cache.\r\n *\r\n * **Cross-process correctness.** `proper-lockfile` serializes the\r\n * read-merge-write critical section across processes so two simultaneous\r\n * `set()` calls from different processes cannot corrupt the file.\r\n *\r\n * **Node-only.** The dynamic imports keep `configstore` and `proper-lockfile`\r\n * out of browser bundles even when this module is imported through the SDK\r\n * barrel. Calling this function in a browser environment will fail at\r\n * runtime when the deferred `await import('configstore')` cannot resolve.\r\n *\r\n * @param name - configstore file name (without `.json`). Default `'gc2-env'`\r\n * matches `gc2-cli`'s configstore so credentials are shared.\r\n * @returns A {@link TokenStore} suitable for passing to {@link createTokenProvider}.\r\n */\r\nexport function createConfigstoreTokenStore(name = 'gc2-env'): TokenStore {\r\n let configstoreInstance: any | null = null\r\n let setChain: Promise<void> = Promise.resolve()\r\n\r\n async function getConfigstore(): Promise<any> {\r\n if (configstoreInstance) return configstoreInstance\r\n const mod = await import('configstore')\r\n const Configstore: any = (mod as any).default ?? mod\r\n const { homedir } = await import('node:os')\r\n const { join } = await import('node:path')\r\n // Resolve XDG_CONFIG_HOME at call time (not at module-load time):\r\n // configstore@7's transitive xdg-basedir snapshots the env var when\r\n // first imported, which would ignore per-test mutations and (more\r\n // importantly) couple our path to xdg-basedir's caching across\r\n // process lifetimes. Computing configPath ourselves keeps the\r\n // SDK's storage location stable regardless of dep version churn.\r\n const xdgConfig = process.env.XDG_CONFIG_HOME || join(homedir(), '.config')\r\n const configPath = join(xdgConfig, 'configstore', `${name}.json`)\r\n configstoreInstance = new Configstore(name, undefined, { configPath })\r\n return configstoreInstance\r\n }\r\n\r\n async function getLockfile(): Promise<typeof import('proper-lockfile')> {\r\n const mod = await import('proper-lockfile')\r\n return ((mod as any).default ?? mod) as typeof import('proper-lockfile')\r\n }\r\n\r\n function readAll(cs: any): StoredCredentials {\r\n const result: StoredCredentials = {}\r\n const token = cs.get('token')\r\n const refresh_token = cs.get('refresh_token')\r\n const host = cs.get('host')\r\n if (token !== undefined) result.token = token\r\n if (refresh_token !== undefined) result.refresh_token = refresh_token\r\n if (host !== undefined) result.host = host\r\n return result\r\n }\r\n\r\n async function doLockedSet(patch: Partial<StoredCredentials>): Promise<void> {\r\n const cs = await getConfigstore()\r\n const lockfile = await getLockfile()\r\n const filePath: string = cs.path\r\n\r\n // Ensure the file (and its directory) exist so proper-lockfile has\r\n // something to anchor on. `wx` is atomic create-if-not-exists, so this\r\n // is safe even if another process is racing to create the same file.\r\n const { mkdirSync, writeFileSync } = await import('node:fs')\r\n const { dirname } = await import('node:path')\r\n try {\r\n mkdirSync(dirname(filePath), { recursive: true })\r\n writeFileSync(filePath, '{}', { flag: 'wx' })\r\n } catch (e: any) {\r\n if (e?.code !== 'EEXIST') throw e\r\n }\r\n\r\n // realpath:false skips symlink resolution. configstore returns an\r\n // already-absolute path, and resolving symlinks would force an extra\r\n // stat (and breaks on macOS test tmpdirs which are symlinks).\r\n const release = await lockfile.lock(filePath, {\r\n retries: {\r\n retries: LOCK_RETRIES,\r\n minTimeout: LOCK_RETRY_MIN_MS,\r\n maxTimeout: LOCK_RETRY_MAX_MS,\r\n factor: 2,\r\n },\r\n stale: LOCK_STALE_MS,\r\n realpath: false,\r\n })\r\n\r\n try {\r\n // Force configstore to re-read the on-disk state so we merge\r\n // against the latest cross-process value, not a stale cache.\r\n configstoreInstance = null\r\n const fresh = await getConfigstore()\r\n const merged: StoredCredentials = { ...readAll(fresh), ...patch }\r\n ;(fresh as any).all = merged\r\n } finally {\r\n await release()\r\n }\r\n }\r\n\r\n return {\r\n async get(): Promise<StoredCredentials> {\r\n const cs = await getConfigstore()\r\n return readAll(cs)\r\n },\r\n\r\n async set(patch: Partial<StoredCredentials>): Promise<void> {\r\n const next = setChain.then(() => doLockedSet(patch))\r\n // Don't poison the chain on a rejection; the original error still\r\n // propagates via `next` to the caller.\r\n setChain = next.catch(() => {})\r\n return next\r\n },\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,MAAM,eAAe;AACrB,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAC1B,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCtB,SAAgB,4BAA4B,OAAO,WAAuB;CACtE,IAAIA,sBAAkC;CACtC,IAAIC,WAA0B,QAAQ,SAAS;CAE/C,eAAe,iBAA+B;;AAC1C,MAAI,oBAAqB,QAAO;EAChC,MAAM,MAAM,MAAM,OAAO;EACzB,MAAMC,0BAAoB,IAAY,sDAAW;EACjD,MAAM,EAAE,YAAY,MAAM,OAAO;EACjC,MAAM,EAAE,SAAS,MAAM,OAAO;AAS9B,wBAAsB,IAAI,YAAY,MAAM,QAAW,EAAE,YADtC,KADD,QAAQ,IAAI,mBAAmB,KAAK,SAAS,EAAE,UAAU,EACxC,eAAe,GAAG,KAAK,OAAO,EACI,CAAC;AACtE,SAAO;;CAGX,eAAe,cAAyD;;EACpE,MAAM,MAAM,MAAM,OAAO;AACzB,sBAAS,IAAY,wDAAW;;CAGpC,SAAS,QAAQ,IAA4B;EACzC,MAAMC,SAA4B,EAAE;EACpC,MAAM,QAAQ,GAAG,IAAI,QAAQ;EAC7B,MAAM,gBAAgB,GAAG,IAAI,gBAAgB;EAC7C,MAAM,OAAO,GAAG,IAAI,OAAO;AAC3B,MAAI,UAAU,OAAW,QAAO,QAAQ;AACxC,MAAI,kBAAkB,OAAW,QAAO,gBAAgB;AACxD,MAAI,SAAS,OAAW,QAAO,OAAO;AACtC,SAAO;;CAGX,eAAe,YAAY,OAAkD;EACzE,MAAM,KAAK,MAAM,gBAAgB;EACjC,MAAM,WAAW,MAAM,aAAa;EACpC,MAAMC,WAAmB,GAAG;EAK5B,MAAM,EAAE,WAAW,kBAAkB,MAAM,OAAO;EAClD,MAAM,EAAE,YAAY,MAAM,OAAO;AACjC,MAAI;AACA,aAAU,QAAQ,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACjD,iBAAc,UAAU,MAAM,EAAE,MAAM,MAAM,CAAC;WACxCC,GAAQ;AACb,8CAAI,EAAG,UAAS,SAAU,OAAM;;EAMpC,MAAM,UAAU,MAAM,SAAS,KAAK,UAAU;GAC1C,SAAS;IACL,SAAS;IACT,YAAY;IACZ,YAAY;IACZ,QAAQ;IACX;GACD,OAAO;GACP,UAAU;GACb,CAAC;AAEF,MAAI;AAGA,yBAAsB;GACtB,MAAM,QAAQ,MAAM,gBAAgB;AAEnC,GAAC,MAAc,wCADuB,QAAQ,MAAM,GAAK;YAEpD;AACN,SAAM,SAAS;;;AAIvB,QAAO;EACH,MAAM,MAAkC;AAEpC,UAAO,QADI,MAAM,gBAAgB,CACf;;EAGtB,MAAM,IAAI,OAAkD;GACxD,MAAM,OAAO,SAAS,WAAW,YAAY,MAAM,CAAC;AAGpD,cAAW,KAAK,YAAY,GAAG;AAC/B,UAAO;;EAEd"}
|