@apicircle/core 1.0.0 → 1.0.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/LICENSE +110 -110
- package/README.md +181 -12
- package/dist/chunk-SGI6KGQ7.js +129 -0
- package/dist/chunk-SGI6KGQ7.js.map +1 -0
- package/dist/index.cjs +7 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/workspace/file-backed.js +5 -122
- package/dist/workspace/file-backed.js.map +1 -1
- package/dist/workspace/registry.cjs +301 -0
- package/dist/workspace/registry.cjs.map +1 -0
- package/dist/workspace/registry.d.cts +85 -0
- package/dist/workspace/registry.d.ts +85 -0
- package/dist/workspace/registry.js +162 -0
- package/dist/workspace/registry.js.map +1 -0
- package/package.json +63 -43
package/LICENSE
CHANGED
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
|
|
2
|
-
Custom Source-Available License, v1.0
|
|
3
|
-
|
|
4
|
-
Copyright (c) 2026 Deva Prakash ("Licensor")
|
|
5
|
-
|
|
6
|
-
The source code in this repository ("the Software") is made available for
|
|
7
|
-
the purposes of transparency, security review, contribution, and personal
|
|
8
|
-
evaluation. This is NOT an open-source license as defined by the Open
|
|
9
|
-
Source Initiative.
|
|
10
|
-
|
|
11
|
-
0. Definitions
|
|
12
|
-
|
|
13
|
-
"Commercial Use" means any use of the Software that is intended for
|
|
14
|
-
or directed toward commercial advantage or monetary compensation,
|
|
15
|
-
whether direct or indirect, including without limitation:
|
|
16
|
-
|
|
17
|
-
(a) using the Software to build, test, develop, deploy, or operate
|
|
18
|
-
any product, service, application, or system that is sold,
|
|
19
|
-
licensed, hosted, distributed, or otherwise made available to
|
|
20
|
-
any third party for a fee or other consideration;
|
|
21
|
-
(b) using the Software in the course of paid employment, paid
|
|
22
|
-
consulting, paid contracting, freelance work, or any other
|
|
23
|
-
revenue-generating activity, regardless of whether the Software
|
|
24
|
-
itself is sold or transferred;
|
|
25
|
-
(c) using the Software internally within a for-profit organization
|
|
26
|
-
beyond the Evaluation Period defined below;
|
|
27
|
-
(d) integrating the Software, or any portion of it, into any tool,
|
|
28
|
-
pipeline, automation, or workflow that supports the commercial
|
|
29
|
-
operations of any business;
|
|
30
|
-
(e) using the Software to provide a hosted, managed, or embedded
|
|
31
|
-
service of any kind to a third party, whether free or paid.
|
|
32
|
-
|
|
33
|
-
"Non-Commercial Use" means any use that is not Commercial Use,
|
|
34
|
-
including personal hobby projects, individual learning, academic
|
|
35
|
-
research, classroom instruction, and good-faith contribution to
|
|
36
|
-
this repository.
|
|
37
|
-
|
|
38
|
-
"Evaluation Period" means a single, continuous period of up to
|
|
39
|
-
thirty (30) days during which a for-profit organization may
|
|
40
|
-
internally evaluate the Software at no charge. After the Evaluation
|
|
41
|
-
Period expires, any further use of the Software by that organization
|
|
42
|
-
constitutes Commercial Use and requires a separate commercial
|
|
43
|
-
license from the Licensor.
|
|
44
|
-
|
|
45
|
-
1. Permitted Use
|
|
46
|
-
|
|
47
|
-
You may, without charge:
|
|
48
|
-
|
|
49
|
-
(a) view, read, and study the source code of the Software;
|
|
50
|
-
(b) run the Software, in source or compiled form, for your own
|
|
51
|
-
Non-Commercial Use, or for Commercial Use solely within the
|
|
52
|
-
Evaluation Period;
|
|
53
|
-
(c) submit improvements to the Software back to this repository via
|
|
54
|
-
pull request, subject to Section 3 (Contributions).
|
|
55
|
-
|
|
56
|
-
2. Prohibited Use
|
|
57
|
-
|
|
58
|
-
Without prior written permission from the Licensor, you may NOT:
|
|
59
|
-
|
|
60
|
-
(a) make any Commercial Use of the Software, in whole or in part,
|
|
61
|
-
except during the Evaluation Period as defined in Section 0;
|
|
62
|
-
(b) redistribute the Software, in source or compiled form, whether
|
|
63
|
-
modified or unmodified, to any third party;
|
|
64
|
-
(c) sublicense, sell, rent, lease, or otherwise transfer the
|
|
65
|
-
Software or any rights granted herein;
|
|
66
|
-
(d) remove, obscure, or alter any copyright, trademark, attribution,
|
|
67
|
-
or license notice contained in the Software;
|
|
68
|
-
(e) use the names "
|
|
69
|
-
logos or trademarks, except as required for accurate attribution.
|
|
70
|
-
|
|
71
|
-
3. Contributions
|
|
72
|
-
|
|
73
|
-
By submitting any contribution (including but not limited to code,
|
|
74
|
-
documentation, or assets) to this repository, you grant the Licensor
|
|
75
|
-
a perpetual, worldwide, irrevocable, royalty-free, sublicensable
|
|
76
|
-
license to use, modify, distribute, and relicense your contribution
|
|
77
|
-
under any terms, including the terms of this license or any future
|
|
78
|
-
version thereof.
|
|
79
|
-
|
|
80
|
-
4. No Trademark License
|
|
81
|
-
|
|
82
|
-
This license does not grant permission to use the trade names,
|
|
83
|
-
trademarks, service marks, or product names of the Licensor, except
|
|
84
|
-
as required for reasonable and customary use in describing the
|
|
85
|
-
origin of the Software.
|
|
86
|
-
|
|
87
|
-
5. Termination
|
|
88
|
-
|
|
89
|
-
The rights granted in Section 1 terminate automatically if you
|
|
90
|
-
breach any term of this license. Upon termination, you must cease
|
|
91
|
-
all use of the Software and destroy all copies in your possession.
|
|
92
|
-
|
|
93
|
-
6. Disclaimer of Warranty
|
|
94
|
-
|
|
95
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
96
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
97
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
|
|
98
|
-
NON-INFRINGEMENT.
|
|
99
|
-
|
|
100
|
-
7. Limitation of Liability
|
|
101
|
-
|
|
102
|
-
IN NO EVENT SHALL THE LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR
|
|
103
|
-
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR
|
|
104
|
-
OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE
|
|
105
|
-
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
106
|
-
|
|
107
|
-
8. Commercial Licensing
|
|
108
|
-
|
|
109
|
-
For commercial licensing, redistribution, or any use not permitted
|
|
110
|
-
under Section 1, contact: apicircle365@gmail.com
|
|
1
|
+
API Circle Studio License
|
|
2
|
+
Custom Source-Available License, v1.0
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 Deva Prakash ("Licensor")
|
|
5
|
+
|
|
6
|
+
The source code in this repository ("the Software") is made available for
|
|
7
|
+
the purposes of transparency, security review, contribution, and personal
|
|
8
|
+
evaluation. This is NOT an open-source license as defined by the Open
|
|
9
|
+
Source Initiative.
|
|
10
|
+
|
|
11
|
+
0. Definitions
|
|
12
|
+
|
|
13
|
+
"Commercial Use" means any use of the Software that is intended for
|
|
14
|
+
or directed toward commercial advantage or monetary compensation,
|
|
15
|
+
whether direct or indirect, including without limitation:
|
|
16
|
+
|
|
17
|
+
(a) using the Software to build, test, develop, deploy, or operate
|
|
18
|
+
any product, service, application, or system that is sold,
|
|
19
|
+
licensed, hosted, distributed, or otherwise made available to
|
|
20
|
+
any third party for a fee or other consideration;
|
|
21
|
+
(b) using the Software in the course of paid employment, paid
|
|
22
|
+
consulting, paid contracting, freelance work, or any other
|
|
23
|
+
revenue-generating activity, regardless of whether the Software
|
|
24
|
+
itself is sold or transferred;
|
|
25
|
+
(c) using the Software internally within a for-profit organization
|
|
26
|
+
beyond the Evaluation Period defined below;
|
|
27
|
+
(d) integrating the Software, or any portion of it, into any tool,
|
|
28
|
+
pipeline, automation, or workflow that supports the commercial
|
|
29
|
+
operations of any business;
|
|
30
|
+
(e) using the Software to provide a hosted, managed, or embedded
|
|
31
|
+
service of any kind to a third party, whether free or paid.
|
|
32
|
+
|
|
33
|
+
"Non-Commercial Use" means any use that is not Commercial Use,
|
|
34
|
+
including personal hobby projects, individual learning, academic
|
|
35
|
+
research, classroom instruction, and good-faith contribution to
|
|
36
|
+
this repository.
|
|
37
|
+
|
|
38
|
+
"Evaluation Period" means a single, continuous period of up to
|
|
39
|
+
thirty (30) days during which a for-profit organization may
|
|
40
|
+
internally evaluate the Software at no charge. After the Evaluation
|
|
41
|
+
Period expires, any further use of the Software by that organization
|
|
42
|
+
constitutes Commercial Use and requires a separate commercial
|
|
43
|
+
license from the Licensor.
|
|
44
|
+
|
|
45
|
+
1. Permitted Use
|
|
46
|
+
|
|
47
|
+
You may, without charge:
|
|
48
|
+
|
|
49
|
+
(a) view, read, and study the source code of the Software;
|
|
50
|
+
(b) run the Software, in source or compiled form, for your own
|
|
51
|
+
Non-Commercial Use, or for Commercial Use solely within the
|
|
52
|
+
Evaluation Period;
|
|
53
|
+
(c) submit improvements to the Software back to this repository via
|
|
54
|
+
pull request, subject to Section 3 (Contributions).
|
|
55
|
+
|
|
56
|
+
2. Prohibited Use
|
|
57
|
+
|
|
58
|
+
Without prior written permission from the Licensor, you may NOT:
|
|
59
|
+
|
|
60
|
+
(a) make any Commercial Use of the Software, in whole or in part,
|
|
61
|
+
except during the Evaluation Period as defined in Section 0;
|
|
62
|
+
(b) redistribute the Software, in source or compiled form, whether
|
|
63
|
+
modified or unmodified, to any third party;
|
|
64
|
+
(c) sublicense, sell, rent, lease, or otherwise transfer the
|
|
65
|
+
Software or any rights granted herein;
|
|
66
|
+
(d) remove, obscure, or alter any copyright, trademark, attribution,
|
|
67
|
+
or license notice contained in the Software;
|
|
68
|
+
(e) use the names "API Circle", "API Circle Studio", or any related
|
|
69
|
+
logos or trademarks, except as required for accurate attribution.
|
|
70
|
+
|
|
71
|
+
3. Contributions
|
|
72
|
+
|
|
73
|
+
By submitting any contribution (including but not limited to code,
|
|
74
|
+
documentation, or assets) to this repository, you grant the Licensor
|
|
75
|
+
a perpetual, worldwide, irrevocable, royalty-free, sublicensable
|
|
76
|
+
license to use, modify, distribute, and relicense your contribution
|
|
77
|
+
under any terms, including the terms of this license or any future
|
|
78
|
+
version thereof.
|
|
79
|
+
|
|
80
|
+
4. No Trademark License
|
|
81
|
+
|
|
82
|
+
This license does not grant permission to use the trade names,
|
|
83
|
+
trademarks, service marks, or product names of the Licensor, except
|
|
84
|
+
as required for reasonable and customary use in describing the
|
|
85
|
+
origin of the Software.
|
|
86
|
+
|
|
87
|
+
5. Termination
|
|
88
|
+
|
|
89
|
+
The rights granted in Section 1 terminate automatically if you
|
|
90
|
+
breach any term of this license. Upon termination, you must cease
|
|
91
|
+
all use of the Software and destroy all copies in your possession.
|
|
92
|
+
|
|
93
|
+
6. Disclaimer of Warranty
|
|
94
|
+
|
|
95
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
96
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
97
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
|
|
98
|
+
NON-INFRINGEMENT.
|
|
99
|
+
|
|
100
|
+
7. Limitation of Liability
|
|
101
|
+
|
|
102
|
+
IN NO EVENT SHALL THE LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR
|
|
103
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR
|
|
104
|
+
OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE
|
|
105
|
+
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
106
|
+
|
|
107
|
+
8. Commercial Licensing
|
|
108
|
+
|
|
109
|
+
For commercial licensing, redistribution, or any use not permitted
|
|
110
|
+
under Section 1, contact: apicircle365@gmail.com
|
package/README.md
CHANGED
|
@@ -1,35 +1,204 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/apicircle/studio/main/assets/logo.png" alt="
|
|
2
|
+
<img src="https://raw.githubusercontent.com/apicircle/studio/main/assets/logo.png" alt="API Circle Studio" width="120" height="120" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">@apicircle/core</h1>
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>An embeddable API engine — execute requests, sign auth, import specs, and mutate workspaces from any JavaScript runtime.</strong><br />
|
|
9
|
+
The same engine that powers the API Circle Studio desktop app, web app, CLI, and MCP server.
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://www.npmjs.com/package/@apicircle/core"><img src="https://img.shields.io/npm/v/@apicircle/core?color=cb3837&logo=npm" alt="npm version" /></a>
|
|
14
|
+
<img src="https://img.shields.io/badge/auth%20schemes-17-blueviolet" alt="17 auth schemes" />
|
|
15
|
+
<img src="https://img.shields.io/badge/imports-OpenAPI%20%C2%B7%20Postman%20%C2%B7%20Insomnia%20%C2%B7%20cURL-blue" alt="Importers" />
|
|
16
|
+
<img src="https://img.shields.io/badge/runtimes-Node%20%C2%B7%20Bun%20%C2%B7%20Browser-success" alt="Runtimes" />
|
|
17
|
+
<img src="https://img.shields.io/badge/node-%E2%89%A5%2020-brightgreen" alt="Node ≥ 20" />
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## What it is
|
|
23
|
+
|
|
24
|
+
`@apicircle/core` is the headless engine behind [API Circle Studio](https://github.com/apicircle/studio).
|
|
25
|
+
It does **everything an API client needs to do** — except draw a UI:
|
|
26
|
+
|
|
27
|
+
- Builds and executes HTTP requests with redirect, retry, and timeout handling.
|
|
28
|
+
- Signs **17 different auth schemes** end-to-end, including the full OAuth2 grant set.
|
|
29
|
+
- Imports **OpenAPI 3.x, Swagger 2.0, Postman v2/v2.1, Insomnia v4, and cURL** into a typed workspace.
|
|
30
|
+
- Mutates workspace state through a single, audited choke point (`applyMutation`).
|
|
31
|
+
- Runs saved execution plans headlessly — perfect for CI / regression gates.
|
|
32
|
+
- Reads and writes workspace JSON to disk with advisory locking, in either a single-folder or multi-workspace registry layout.
|
|
33
|
+
|
|
34
|
+
If you've ever wanted to embed "the Postman engine" inside a Node script,
|
|
35
|
+
a CI runner, an internal portal, or your own product — this is that engine,
|
|
36
|
+
typed and treeshakable.
|
|
8
37
|
|
|
9
38
|
## Install
|
|
10
39
|
|
|
11
40
|
```bash
|
|
12
|
-
npm install @apicircle/core
|
|
41
|
+
npm install @apicircle/core @apicircle/shared
|
|
42
|
+
# pnpm add @apicircle/core @apicircle/shared
|
|
13
43
|
```
|
|
14
44
|
|
|
15
|
-
|
|
45
|
+
Dual ESM + CJS, full `.d.ts`, no native deps. Browser-safe — all crypto and
|
|
46
|
+
signing primitives run on WebCrypto.
|
|
16
47
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
48
|
+
## What you can do with it
|
|
49
|
+
|
|
50
|
+
### Execute any request, with real auth
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { executeRequest } from '@apicircle/core';
|
|
54
|
+
|
|
55
|
+
const result = await executeRequest({
|
|
56
|
+
method: 'POST',
|
|
57
|
+
url: 'https://api.example.com/charges',
|
|
58
|
+
headers: [{ key: 'X-Idempotency-Key', value: 'k-001' }],
|
|
59
|
+
body: { kind: 'json', json: { amount: 4200, currency: 'usd' } },
|
|
60
|
+
auth: {
|
|
61
|
+
type: 'oauth2',
|
|
62
|
+
grant: 'client_credentials',
|
|
63
|
+
clientId: 'cid',
|
|
64
|
+
clientSecret: 'csec',
|
|
65
|
+
tokenUrl: 'https://auth.example.com/token',
|
|
66
|
+
scope: 'charges:write',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
console.log(result.response.status, result.response.bodyText);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Behind the scenes the engine acquires a token, caches it for its TTL,
|
|
74
|
+
refreshes it transparently when it expires, and signs the request. Every
|
|
75
|
+
other auth scheme — Bearer, Basic, API key, custom header, AWS SigV4, Digest
|
|
76
|
+
(with `stale=true` nonce rotation), NTLM (with the full 3-message handshake +
|
|
77
|
+
MIC), Hawk, JWT — works the same way: declarative config in, signed request out.
|
|
78
|
+
|
|
79
|
+
### The 17 auth schemes, in one place
|
|
80
|
+
|
|
81
|
+
| Family | Schemes |
|
|
82
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------ |
|
|
83
|
+
| Token | Bearer, API key, custom header |
|
|
84
|
+
| Credentials | Basic, Digest (MD5/SHA-256/SHA-512-256), NTLM v2, Hawk |
|
|
85
|
+
| AWS | SigV4 (header & query) |
|
|
86
|
+
| JWT | HS / RS / ES / PS family, custom claims |
|
|
87
|
+
| OAuth2 | Client Credentials, Authorization Code (+ PKCE), Password, Implicit, Device Code, Refresh, `private_key_jwt` |
|
|
88
|
+
| None | …because sometimes the API just lets you in |
|
|
89
|
+
|
|
90
|
+
Every implementation is verified against the original RFCs (1320, 1321, 2202,
|
|
91
|
+
2617, 7616, 7636, [MS-NLMP]) plus NIST CAVS vectors and Mozilla's Hawk
|
|
92
|
+
fixtures. See [`docs/auth.md`](https://github.com/apicircle/studio/blob/main/docs/auth.md)
|
|
93
|
+
for the full matrix.
|
|
94
|
+
|
|
95
|
+
### Import a spec, get a typed collection
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { importOpenApi, importPostman, importInsomnia, importCurl } from '@apicircle/core';
|
|
99
|
+
|
|
100
|
+
const { requests, folders, warnings } = await importOpenApi(rawYamlOrJson, {
|
|
101
|
+
format: 'yaml',
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
All four importers return the same typed shape, ready to merge into a
|
|
106
|
+
workspace with `applyMutation`.
|
|
107
|
+
|
|
108
|
+
### Run a saved plan in CI
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { runPlan, loadFromFile } from '@apicircle/core/workspace/file-backed';
|
|
112
|
+
|
|
113
|
+
const { synced, local } = await loadFromFile('./workspace');
|
|
114
|
+
const planId = synced.plans.find((p) => p.name === 'Smoke Tests')!.id;
|
|
115
|
+
|
|
116
|
+
const result = await runPlan({ synced, local }, planId, { bail: true });
|
|
117
|
+
process.exit(result.passed ? 0 : 1);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Plans chain requests, evaluate assertions, and pass extracted variables
|
|
121
|
+
forward — exit code 0 on green, non-zero on red. Drop it straight into GitHub
|
|
122
|
+
Actions, GitLab CI, or any pipeline.
|
|
123
|
+
|
|
124
|
+
### Mutate workspace state — one function, every entity
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { applyMutation, generateId } from '@apicircle/core';
|
|
128
|
+
|
|
129
|
+
const next = applyMutation(state, {
|
|
130
|
+
kind: 'request.create',
|
|
131
|
+
id: generateId(),
|
|
132
|
+
name: 'List Users',
|
|
133
|
+
parentFolderId: null,
|
|
134
|
+
method: 'GET',
|
|
135
|
+
url: 'https://api.example.com/users',
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`WorkspacePatch` is a discriminated union over `request.* | folder.* |
|
|
140
|
+
environment.* | assertion.* | mock.* | plan.*`. Adding a new entity in your
|
|
141
|
+
own UI? One union variant + one switch case. The CLI, MCP server, and desktop
|
|
142
|
+
app all flow through this single function — it's the audit log seam.
|
|
23
143
|
|
|
24
144
|
## Entry points
|
|
25
145
|
|
|
26
146
|
```ts
|
|
147
|
+
// Engine API
|
|
27
148
|
import { executeRequest, applyMutation, runPlan } from '@apicircle/core';
|
|
149
|
+
|
|
150
|
+
// Disk-backed single-workspace helpers (load/save/withWorkspace under an advisory lock)
|
|
28
151
|
import { loadFromFile, saveToFile, withWorkspace } from '@apicircle/core/workspace/file-backed';
|
|
152
|
+
|
|
153
|
+
// Multi-workspace registry (registry.json + per-id subdirectories)
|
|
154
|
+
import {
|
|
155
|
+
loadRegistry,
|
|
156
|
+
saveRegistry,
|
|
157
|
+
loadWorkspaceById,
|
|
158
|
+
saveWorkspaceById,
|
|
159
|
+
registerWorkspace,
|
|
160
|
+
setActiveWorkspace,
|
|
161
|
+
deleteWorkspaceById,
|
|
162
|
+
findWorkspaceEntry,
|
|
163
|
+
migrateLegacyWorkspace,
|
|
164
|
+
workspaceDirFor,
|
|
165
|
+
type WorkspaceRegistry,
|
|
166
|
+
} from '@apicircle/core/workspace/registry';
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
- **`/workspace/file-backed`** — one workspace, one folder. `proper-lockfile`
|
|
170
|
+
advisory locking, so concurrent CLI runs don't corrupt each other.
|
|
171
|
+
- **`/workspace/registry`** — many workspaces, one root. `registry.json` at
|
|
172
|
+
the top, per-id subdirectories underneath. The desktop app, CLI, and MCP
|
|
173
|
+
server all read this same shape, so an edit in one is visible to the others
|
|
174
|
+
on the next read.
|
|
175
|
+
|
|
176
|
+
## Use cases
|
|
177
|
+
|
|
178
|
+
- Build an **internal API explorer** for your engineering team.
|
|
179
|
+
- Run **regression smoke tests** against staging in CI, with real OAuth2.
|
|
180
|
+
- Generate a **runtime mock** from an OpenAPI spec on the fly.
|
|
181
|
+
- Embed an **AI-powered request authoring** flow in your own product.
|
|
182
|
+
- Migrate a Postman library into a typed, Git-trackable workspace.
|
|
183
|
+
- Lint or validate workspaces in a pre-commit hook.
|
|
184
|
+
|
|
185
|
+
## Where it fits
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
@apicircle/shared (types + IDs + crypto + MCP catalog)
|
|
189
|
+
└── @apicircle/core ◀── you are here
|
|
190
|
+
├── @apicircle/mcp-server (wraps core as MCP tools)
|
|
191
|
+
├── @apicircle/cli (wraps core as a CLI binary)
|
|
192
|
+
└── @apicircle/mock-server-core (sister package — mock-server engine)
|
|
29
193
|
```
|
|
30
194
|
|
|
31
|
-
|
|
195
|
+
## Stability & test coverage
|
|
196
|
+
|
|
197
|
+
- **1,911 unit + integration tests** in the OAuth2 / signing / executor suites
|
|
198
|
+
alone (mock IdP exercises every grant, every refresh path, every CSRF guard).
|
|
199
|
+
- Every auth primitive is **CAVS-verified** against the original spec vectors.
|
|
200
|
+
- The engine is built ESM-first and ships dual CJS bundles for legacy consumers.
|
|
32
201
|
|
|
33
202
|
## License
|
|
34
203
|
|
|
35
|
-
Released under the **
|
|
204
|
+
Released under the **API Circle Studio License** — a custom source-available license, not an OSI-approved open-source license. Free for personal, educational, and non-commercial use, plus a 30-day commercial evaluation period; ongoing commercial use requires a separate license. See [LICENSE](./LICENSE) for the full terms, or contact **apicircle365@gmail.com** for commercial licensing.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// src/workspace/fileBackedWorkspace.ts
|
|
2
|
+
import { promises as fs } from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { FONT_SIZE_PERCENT_DEFAULT } from "@apicircle/shared";
|
|
5
|
+
import lockfile from "proper-lockfile";
|
|
6
|
+
var SYNCED_FILE = "workspace.synced.json";
|
|
7
|
+
var LOCAL_FILE = "workspace.local.json";
|
|
8
|
+
async function loadFromFile(dir, options = {}) {
|
|
9
|
+
const syncedPath = path.join(dir, SYNCED_FILE);
|
|
10
|
+
const localPath = path.join(dir, LOCAL_FILE);
|
|
11
|
+
let syncedRaw;
|
|
12
|
+
try {
|
|
13
|
+
syncedRaw = await fs.readFile(syncedPath, "utf-8");
|
|
14
|
+
} catch (err) {
|
|
15
|
+
if (options.allowMissing && isENOENT(err)) return null;
|
|
16
|
+
throw err;
|
|
17
|
+
}
|
|
18
|
+
const synced = JSON.parse(syncedRaw);
|
|
19
|
+
let local;
|
|
20
|
+
try {
|
|
21
|
+
local = JSON.parse(await fs.readFile(localPath, "utf-8"));
|
|
22
|
+
} catch (err) {
|
|
23
|
+
if (!isENOENT(err)) throw err;
|
|
24
|
+
local = createEmptyLocalForSynced(synced);
|
|
25
|
+
}
|
|
26
|
+
return { synced, local };
|
|
27
|
+
}
|
|
28
|
+
async function saveToFile(dir, state, options = {}) {
|
|
29
|
+
await fs.mkdir(dir, { recursive: true });
|
|
30
|
+
const syncedPath = path.join(dir, SYNCED_FILE);
|
|
31
|
+
const localPath = path.join(dir, LOCAL_FILE);
|
|
32
|
+
await ensureFile(syncedPath);
|
|
33
|
+
const release = await lockfile.lock(syncedPath, {
|
|
34
|
+
retries: { retries: 5, minTimeout: 50, maxTimeout: 500 },
|
|
35
|
+
stale: options.lockTimeoutMs ?? 3e4
|
|
36
|
+
});
|
|
37
|
+
try {
|
|
38
|
+
await writeJsonAtomic(syncedPath, state.synced);
|
|
39
|
+
await writeJsonAtomic(localPath, state.local);
|
|
40
|
+
} finally {
|
|
41
|
+
await release();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function withWorkspace(dir, fn, options = {}) {
|
|
45
|
+
await fs.mkdir(dir, { recursive: true });
|
|
46
|
+
const syncedPath = path.join(dir, SYNCED_FILE);
|
|
47
|
+
const localPath = path.join(dir, LOCAL_FILE);
|
|
48
|
+
await ensureFile(syncedPath);
|
|
49
|
+
const release = await lockfile.lock(syncedPath, {
|
|
50
|
+
retries: { retries: 5, minTimeout: 50, maxTimeout: 500 },
|
|
51
|
+
stale: options.lockTimeoutMs ?? 3e4
|
|
52
|
+
});
|
|
53
|
+
try {
|
|
54
|
+
const syncedRaw = await fs.readFile(syncedPath, "utf-8");
|
|
55
|
+
const synced = JSON.parse(syncedRaw);
|
|
56
|
+
let local;
|
|
57
|
+
try {
|
|
58
|
+
local = JSON.parse(await fs.readFile(localPath, "utf-8"));
|
|
59
|
+
} catch (err) {
|
|
60
|
+
if (!isENOENT(err)) throw err;
|
|
61
|
+
local = createEmptyLocalForSynced(synced);
|
|
62
|
+
}
|
|
63
|
+
const out = await fn({ synced, local });
|
|
64
|
+
await writeJsonAtomic(syncedPath, out.next.synced);
|
|
65
|
+
await writeJsonAtomic(localPath, out.next.local);
|
|
66
|
+
return out.result;
|
|
67
|
+
} finally {
|
|
68
|
+
await release();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
var WORKSPACE_FILE_MODE = 384;
|
|
72
|
+
async function ensureFile(filePath) {
|
|
73
|
+
try {
|
|
74
|
+
await fs.access(filePath);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (!isENOENT(err)) throw err;
|
|
77
|
+
await fs.writeFile(filePath, "{}", { encoding: "utf-8", mode: WORKSPACE_FILE_MODE });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function writeJsonAtomic(filePath, value) {
|
|
81
|
+
const tmp = `${filePath}.tmp`;
|
|
82
|
+
await fs.writeFile(tmp, JSON.stringify(value, null, 2) + "\n", {
|
|
83
|
+
encoding: "utf-8",
|
|
84
|
+
mode: WORKSPACE_FILE_MODE
|
|
85
|
+
});
|
|
86
|
+
await fs.rename(tmp, filePath);
|
|
87
|
+
}
|
|
88
|
+
function isENOENT(err) {
|
|
89
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
90
|
+
}
|
|
91
|
+
function createEmptyLocalForSynced(synced) {
|
|
92
|
+
return {
|
|
93
|
+
schemaVersion: 1,
|
|
94
|
+
workspaceId: synced.workspaceId,
|
|
95
|
+
executionPlans: {},
|
|
96
|
+
history: { requestRuns: [], planRuns: [] },
|
|
97
|
+
secretIndex: { entries: {} },
|
|
98
|
+
sessions: { github: { workspace: null, links: {} } },
|
|
99
|
+
connectedRepo: null,
|
|
100
|
+
workingBranch: null,
|
|
101
|
+
seededWorkspaceSha: null,
|
|
102
|
+
retiredBranch: null,
|
|
103
|
+
sync: {
|
|
104
|
+
lastPulledSnapshot: null,
|
|
105
|
+
lastPulledSha: null,
|
|
106
|
+
lastPulledAt: null,
|
|
107
|
+
dirtyKeys: []
|
|
108
|
+
},
|
|
109
|
+
linkedCollections: {},
|
|
110
|
+
globalContext: {},
|
|
111
|
+
mockRuntime: { active: {} },
|
|
112
|
+
ui: {
|
|
113
|
+
activeRequestId: null,
|
|
114
|
+
sidebarExpandedSections: [],
|
|
115
|
+
themeId: "studio-dark",
|
|
116
|
+
fontId: "system-mono",
|
|
117
|
+
fontSizePercent: FONT_SIZE_PERCENT_DEFAULT
|
|
118
|
+
},
|
|
119
|
+
settings: { validateOnSend: true, monacoConsumesWheel: false },
|
|
120
|
+
snapshots: { entries: [], maxBytes: 50 * 1024 * 1024 }
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export {
|
|
125
|
+
loadFromFile,
|
|
126
|
+
saveToFile,
|
|
127
|
+
withWorkspace
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=chunk-SGI6KGQ7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/workspace/fileBackedWorkspace.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport { FONT_SIZE_PERCENT_DEFAULT } from '@apicircle/shared';\nimport type { WorkspaceLocal, WorkspaceSynced } from '@apicircle/shared';\nimport lockfile from 'proper-lockfile';\nimport type { WorkspaceState } from './patches';\n\n// =============================================================================\n// fileBackedWorkspace — load/save a `{ synced, local }` pair as two JSON\n// files on disk, with a `proper-lockfile` advisory lock so concurrent CLI /\n// MCP writers can't corrupt the document.\n//\n// Layout (relative to the directory passed in):\n// workspace.synced.json ← matches WorkspaceSynced exactly, push-to-git target\n// workspace.local.json ← WorkspaceLocal, host-private (CLI/MCP doesn't push)\n//\n// The lock is held on `workspace.synced.json` because that's the file the\n// editor races against. Stale locks are released after 30s.\n// =============================================================================\n\nconst SYNCED_FILE = 'workspace.synced.json';\nconst LOCAL_FILE = 'workspace.local.json';\n\nexport interface LoadFromFileOptions {\n /** When true, return `null` instead of throwing if the synced file is missing. */\n allowMissing?: boolean;\n}\n\nexport interface SaveToFileOptions {\n /** Lock timeout (ms). Defaults to 30000. */\n lockTimeoutMs?: number;\n}\n\n/**\n * Load both workspace documents from `dir`. The synced file is required;\n * the local file is optional and falls back to a minimal empty shape so a\n * CLI on a fresh machine can still operate (it just won't have history /\n * overrides until the desktop app runs once).\n */\nexport async function loadFromFile(\n dir: string,\n options: LoadFromFileOptions = {},\n): Promise<WorkspaceState | null> {\n const syncedPath = path.join(dir, SYNCED_FILE);\n const localPath = path.join(dir, LOCAL_FILE);\n\n let syncedRaw: string;\n try {\n syncedRaw = await fs.readFile(syncedPath, 'utf-8');\n } catch (err) {\n if (options.allowMissing && isENOENT(err)) return null;\n throw err;\n }\n const synced = JSON.parse(syncedRaw) as WorkspaceSynced;\n\n let local: WorkspaceLocal;\n try {\n local = JSON.parse(await fs.readFile(localPath, 'utf-8')) as WorkspaceLocal;\n } catch (err) {\n if (!isENOENT(err)) throw err;\n local = createEmptyLocalForSynced(synced);\n }\n\n return { synced, local };\n}\n\n/**\n * Atomically write both documents back to disk. Acquires an advisory lock\n * on the synced file for the duration of the write so a parallel CLI /\n * MCP / desktop save can't interleave.\n *\n * Both files are written via `<file>.tmp` + rename so a crash mid-write\n * never leaves a partial JSON document on disk.\n */\nexport async function saveToFile(\n dir: string,\n state: WorkspaceState,\n options: SaveToFileOptions = {},\n): Promise<void> {\n await fs.mkdir(dir, { recursive: true });\n const syncedPath = path.join(dir, SYNCED_FILE);\n const localPath = path.join(dir, LOCAL_FILE);\n\n // proper-lockfile requires the target file to exist. Touch it on first save.\n await ensureFile(syncedPath);\n\n const release = await lockfile.lock(syncedPath, {\n retries: { retries: 5, minTimeout: 50, maxTimeout: 500 },\n stale: options.lockTimeoutMs ?? 30000,\n });\n try {\n await writeJsonAtomic(syncedPath, state.synced);\n await writeJsonAtomic(localPath, state.local);\n } finally {\n await release();\n }\n}\n\n/**\n * Run a load → mutate → save cycle under one lock so a single mutation\n * can't be clobbered by a racing reader-then-writer.\n */\nexport async function withWorkspace<T>(\n dir: string,\n fn: (state: WorkspaceState) => Promise<{ next: WorkspaceState; result?: T }>,\n options: SaveToFileOptions = {},\n): Promise<T | undefined> {\n await fs.mkdir(dir, { recursive: true });\n const syncedPath = path.join(dir, SYNCED_FILE);\n const localPath = path.join(dir, LOCAL_FILE);\n await ensureFile(syncedPath);\n\n const release = await lockfile.lock(syncedPath, {\n retries: { retries: 5, minTimeout: 50, maxTimeout: 500 },\n stale: options.lockTimeoutMs ?? 30000,\n });\n try {\n const syncedRaw = await fs.readFile(syncedPath, 'utf-8');\n const synced = JSON.parse(syncedRaw) as WorkspaceSynced;\n let local: WorkspaceLocal;\n try {\n local = JSON.parse(await fs.readFile(localPath, 'utf-8')) as WorkspaceLocal;\n } catch (err) {\n if (!isENOENT(err)) throw err;\n local = createEmptyLocalForSynced(synced);\n }\n const out = await fn({ synced, local });\n await writeJsonAtomic(syncedPath, out.next.synced);\n await writeJsonAtomic(localPath, out.next.local);\n return out.result;\n } finally {\n await release();\n }\n}\n\n// ---------------------------------------------------------------------------\n// internals\n// ---------------------------------------------------------------------------\n\n// File mode for workspace JSON: owner read/write only. Default `fs.writeFile`\n// uses 0o666 minus umask (typically 0o644 — world-readable). The workspace\n// docs carry the synced state (which after redaction is mostly safe to read\n// but still includes per-workspace metadata) and the local state (which\n// holds the encrypted Secret Vault payload table, session metadata, and the\n// vault entries themselves). On multi-user POSIX hosts (CI runners,\n// classroom VMs, shared dev servers) the default would leak both. 0o600\n// keeps the file owner-only. Windows ignores POSIX modes — the inherited\n// per-user ACL under %USERPROFILE% is what protects it there.\nconst WORKSPACE_FILE_MODE = 0o600;\n\nasync function ensureFile(filePath: string): Promise<void> {\n try {\n await fs.access(filePath);\n } catch (err) {\n if (!isENOENT(err)) throw err;\n await fs.writeFile(filePath, '{}', { encoding: 'utf-8', mode: WORKSPACE_FILE_MODE });\n }\n}\n\nasync function writeJsonAtomic(filePath: string, value: unknown): Promise<void> {\n const tmp = `${filePath}.tmp`;\n await fs.writeFile(tmp, JSON.stringify(value, null, 2) + '\\n', {\n encoding: 'utf-8',\n mode: WORKSPACE_FILE_MODE,\n });\n await fs.rename(tmp, filePath);\n}\n\nfunction isENOENT(err: unknown): boolean {\n return typeof err === 'object' && err !== null && 'code' in err && err.code === 'ENOENT';\n}\n\nfunction createEmptyLocalForSynced(synced: WorkspaceSynced): WorkspaceLocal {\n return {\n schemaVersion: 1,\n workspaceId: synced.workspaceId,\n executionPlans: {},\n history: { requestRuns: [], planRuns: [] },\n secretIndex: { entries: {} },\n sessions: { github: { workspace: null, links: {} } },\n connectedRepo: null,\n workingBranch: null,\n seededWorkspaceSha: null,\n retiredBranch: null,\n sync: {\n lastPulledSnapshot: null,\n lastPulledSha: null,\n lastPulledAt: null,\n dirtyKeys: [],\n },\n linkedCollections: {},\n globalContext: {},\n mockRuntime: { active: {} },\n ui: {\n activeRequestId: null,\n sidebarExpandedSections: [],\n themeId: 'studio-dark',\n fontId: 'system-mono',\n fontSizePercent: FONT_SIZE_PERCENT_DEFAULT,\n },\n settings: { validateOnSend: true, monacoConsumesWheel: false },\n snapshots: { entries: [], maxBytes: 50 * 1024 * 1024 },\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -345,7 +345,11 @@ var HTTP_HEADERS_MAP = [
|
|
|
345
345
|
{
|
|
346
346
|
name: "User-Agent",
|
|
347
347
|
description: "Client application identifier",
|
|
348
|
-
values: [
|
|
348
|
+
values: [
|
|
349
|
+
"API Circle Studio/1.0.0",
|
|
350
|
+
"Mozilla/5.0 (compatible; API Circle Studio)",
|
|
351
|
+
"curl/8.0.0"
|
|
352
|
+
],
|
|
349
353
|
reserved: "browser",
|
|
350
354
|
reservedNote: "Forbidden in web fetch; settable in Desktop (native) requests"
|
|
351
355
|
},
|
|
@@ -357,7 +361,7 @@ var HTTP_HEADERS_MAP = [
|
|
|
357
361
|
{
|
|
358
362
|
name: "X-Client-Name",
|
|
359
363
|
description: "Auto-fed: client application name",
|
|
360
|
-
values: ["
|
|
364
|
+
values: ["API Circle Studio"],
|
|
361
365
|
reserved: "app",
|
|
362
366
|
reservedNote: "Injected automatically; your value overrides it"
|
|
363
367
|
},
|
|
@@ -2260,7 +2264,7 @@ function isDesktop() {
|
|
|
2260
2264
|
|
|
2261
2265
|
// src/request/autoHeaders.ts
|
|
2262
2266
|
var APP_VERSION = "1.0.0";
|
|
2263
|
-
var APP_NAME = "
|
|
2267
|
+
var APP_NAME = "API Circle Studio";
|
|
2264
2268
|
var DESKTOP_APP_ORIGIN = "http://app.studio.apicircle.dev";
|
|
2265
2269
|
function generateSpanId() {
|
|
2266
2270
|
const bytes = new Uint8Array(8);
|