@executor-js/sdk 1.4.29 → 1.4.31
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/blob.d.ts +2 -1
- package/dist/blob.d.ts.map +1 -1
- package/dist/chunk-2LU3SC64.js +7 -0
- package/dist/chunk-2LU3SC64.js.map +1 -0
- package/dist/chunk-7D4SUZUM.js +38 -0
- package/dist/chunk-7D4SUZUM.js.map +1 -0
- package/dist/chunk-B4YGTZF6.js +22 -0
- package/dist/chunk-B4YGTZF6.js.map +1 -0
- package/dist/chunk-BZNIRSMG.js +7873 -0
- package/dist/chunk-BZNIRSMG.js.map +1 -0
- package/dist/chunk-ECMCGZYE.js +5814 -0
- package/dist/chunk-ECMCGZYE.js.map +1 -0
- package/dist/{chunk-6SQWMOM4.js → chunk-MLNVNVRI.js} +2 -6
- package/dist/chunk-MLNVNVRI.js.map +1 -0
- package/dist/chunk-P2WKYAC5.js +1 -0
- package/dist/chunk-P2WKYAC5.js.map +1 -0
- package/dist/chunk-ZANQD7E2.js +1035 -0
- package/dist/chunk-ZANQD7E2.js.map +1 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +3 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/core-schema.d.ts +283 -408
- package/dist/core-schema.d.ts.map +1 -1
- package/dist/core-tools.d.ts +6 -0
- package/dist/core-tools.d.ts.map +1 -0
- package/dist/core.js +120 -168
- package/dist/core.js.map +1 -1
- package/dist/credential-bindings.d.ts +86 -24
- package/dist/credential-bindings.d.ts.map +1 -1
- package/dist/errors.d.ts +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/executor.d.ts +46 -12
- package/dist/executor.d.ts.map +1 -1
- package/dist/fuma-runtime.d.ts +50 -0
- package/dist/fuma-runtime.d.ts.map +1 -0
- package/dist/http-source.d.ts +109 -0
- package/dist/http-source.d.ts.map +1 -0
- package/dist/http-source.js +155 -0
- package/dist/http-source.js.map +1 -0
- package/dist/index.d.ts +17 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -21
- package/dist/index.js.map +1 -1
- package/dist/oauth-service.d.ts +2 -10
- package/dist/oauth-service.d.ts.map +1 -1
- package/dist/oauth.d.ts +9 -1
- package/dist/oauth.d.ts.map +1 -1
- package/dist/plugin-storage.d.ts +40 -0
- package/dist/plugin-storage.d.ts.map +1 -0
- package/dist/plugin.d.ts +110 -34
- package/dist/plugin.d.ts.map +1 -1
- package/dist/promise-executor.d.ts +33 -8
- package/dist/promise-executor.d.ts.map +1 -1
- package/dist/promise.d.ts +3 -3
- package/dist/promise.d.ts.map +1 -1
- package/dist/schema-refs.d.ts +1 -0
- package/dist/schema-refs.d.ts.map +1 -1
- package/dist/schema-refs.test.d.ts +2 -0
- package/dist/schema-refs.test.d.ts.map +1 -0
- package/dist/schema-types.d.ts +22 -8
- package/dist/schema-types.d.ts.map +1 -1
- package/dist/scope-policy.d.ts +23 -0
- package/dist/scope-policy.d.ts.map +1 -0
- package/dist/scope-policy.test.d.ts +2 -0
- package/dist/scope-policy.test.d.ts.map +1 -0
- package/dist/scope.d.ts +10 -0
- package/dist/scope.d.ts.map +1 -1
- package/dist/secrets.d.ts +1 -1
- package/dist/secrets.d.ts.map +1 -1
- package/dist/shared.d.ts +13 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +103 -0
- package/dist/shared.js.map +1 -0
- package/dist/sqlite-test-db-OK2WQNR5.js +8 -0
- package/dist/sqlite-test-db-OK2WQNR5.js.map +1 -0
- package/dist/sqlite-test-db.d.ts +22 -0
- package/dist/sqlite-test-db.d.ts.map +1 -0
- package/dist/test-config.d.ts +45 -4
- package/dist/test-config.d.ts.map +1 -1
- package/dist/testing/oauth-test-server.d.ts +80 -0
- package/dist/testing/oauth-test-server.d.ts.map +1 -0
- package/dist/testing.d.ts +4 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +706 -18
- package/dist/testing.js.map +1 -1
- package/dist/testing.test.d.ts +2 -0
- package/dist/testing.test.d.ts.map +1 -0
- package/dist/tool-result.d.ts +22 -0
- package/dist/tool-result.d.ts.map +1 -0
- package/dist/tool-result.test.d.ts +2 -0
- package/dist/tool-result.test.d.ts.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/vendor/json-schema-to-typescript/applySchemaTyping.d.ts +3 -0
- package/dist/vendor/json-schema-to-typescript/applySchemaTyping.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/compat.d.ts +10 -0
- package/dist/vendor/json-schema-to-typescript/compat.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/formatter.d.ts +3 -0
- package/dist/vendor/json-schema-to-typescript/formatter.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/generator.d.ts +7 -0
- package/dist/vendor/json-schema-to-typescript/generator.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/index.d.ts +93 -0
- package/dist/vendor/json-schema-to-typescript/index.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/linker.d.ts +8 -0
- package/dist/vendor/json-schema-to-typescript/linker.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/normalizer.d.ts +5 -0
- package/dist/vendor/json-schema-to-typescript/normalizer.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/optimizer.d.ts +4 -0
- package/dist/vendor/json-schema-to-typescript/optimizer.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/optionValidator.d.ts +3 -0
- package/dist/vendor/json-schema-to-typescript/optionValidator.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/parser.d.ts +8 -0
- package/dist/vendor/json-schema-to-typescript/parser.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/resolver.d.ts +7 -0
- package/dist/vendor/json-schema-to-typescript/resolver.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/types/AST.d.ts +108 -0
- package/dist/vendor/json-schema-to-typescript/types/AST.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/types/JSONSchema.d.ts +103 -0
- package/dist/vendor/json-schema-to-typescript/types/JSONSchema.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/types/json-schema.d.ts +34 -0
- package/dist/vendor/json-schema-to-typescript/types/json-schema.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/typesOfSchema.d.ts +11 -0
- package/dist/vendor/json-schema-to-typescript/typesOfSchema.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/utils.d.ts +30 -0
- package/dist/vendor/json-schema-to-typescript/utils.d.ts.map +1 -0
- package/dist/vendor/json-schema-to-typescript/validator.d.ts +3 -0
- package/dist/vendor/json-schema-to-typescript/validator.d.ts.map +1 -0
- package/package.json +17 -2
- package/dist/chunk-6SQWMOM4.js.map +0 -1
- package/dist/chunk-I2OEQ2GJ.js +0 -103
- package/dist/chunk-I2OEQ2GJ.js.map +0 -1
- package/dist/chunk-OIJL6F6M.js +0 -5507
- package/dist/chunk-OIJL6F6M.js.map +0 -1
- package/dist/error-handling.test.d.ts +0 -2
- package/dist/error-handling.test.d.ts.map +0 -1
- package/dist/scoped-adapter.d.ts +0 -28
- package/dist/scoped-adapter.d.ts.map +0 -1
- package/dist/scoped-adapter.test.d.ts +0 -2
- package/dist/scoped-adapter.test.d.ts.map +0 -1
package/dist/testing.js
CHANGED
|
@@ -1,21 +1,698 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
Scope,
|
|
3
|
+
collectTables,
|
|
4
|
+
createExecutor,
|
|
5
|
+
definePlugin
|
|
6
|
+
} from "./chunk-BZNIRSMG.js";
|
|
7
|
+
import {
|
|
8
|
+
ScopeId
|
|
9
|
+
} from "./chunk-ZANQD7E2.js";
|
|
10
|
+
import {
|
|
11
|
+
createSqliteTestFumaDb
|
|
12
|
+
} from "./chunk-ECMCGZYE.js";
|
|
13
|
+
import "./chunk-7D4SUZUM.js";
|
|
6
14
|
|
|
7
15
|
// src/testing.ts
|
|
16
|
+
import * as NodeHttpServer2 from "@effect/platform-node/NodeHttpServer";
|
|
17
|
+
import { Context as Context3, Data as Data2, Effect as Effect3, Layer as Layer3, Predicate as Predicate2 } from "effect";
|
|
18
|
+
import {
|
|
19
|
+
HttpClient as HttpClient2,
|
|
20
|
+
HttpRouter,
|
|
21
|
+
HttpServer as HttpServer2,
|
|
22
|
+
HttpServerRequest as HttpServerRequest2
|
|
23
|
+
} from "effect/unstable/http";
|
|
24
|
+
|
|
25
|
+
// src/test-config.ts
|
|
26
|
+
import { Context, Effect, Layer } from "effect";
|
|
27
|
+
import { withQueryContext } from "fumadb/query";
|
|
28
|
+
var makeLazyTestFumaDb = (options) => {
|
|
29
|
+
let started;
|
|
30
|
+
const start = () => {
|
|
31
|
+
if (!started) {
|
|
32
|
+
started = import("./sqlite-test-db-OK2WQNR5.js").then(
|
|
33
|
+
({ createSqliteTestFumaDb: createSqliteTestFumaDb2 }) => createSqliteTestFumaDb2({
|
|
34
|
+
tables: options.tables,
|
|
35
|
+
namespace: "executor_test",
|
|
36
|
+
path: options.dataDir ? `${options.dataDir}/test.db` : void 0
|
|
37
|
+
})
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return started;
|
|
41
|
+
};
|
|
42
|
+
const internal = {
|
|
43
|
+
tables: options.tables,
|
|
44
|
+
count: async (table, value) => (await start()).db.internal.count(table, value),
|
|
45
|
+
create: async (table, values) => (await start()).db.internal.create(table, values),
|
|
46
|
+
createMany: async (table, values) => (await start()).db.internal.createMany(table, values),
|
|
47
|
+
deleteMany: async (table, value) => (await start()).db.internal.deleteMany(table, value),
|
|
48
|
+
findFirst: async (table, value) => (await start()).db.internal.findFirst(table, value),
|
|
49
|
+
findMany: async (table, value) => (await start()).db.internal.findMany(table, value),
|
|
50
|
+
transaction: async (run) => (await start()).db.internal.transaction(run),
|
|
51
|
+
updateMany: async (table, value) => (await start()).db.internal.updateMany(table, value),
|
|
52
|
+
upsert: async (table, value) => (await start()).db.internal.upsert(table, value)
|
|
53
|
+
};
|
|
54
|
+
const queryMethods = /* @__PURE__ */ new Set([
|
|
55
|
+
"count",
|
|
56
|
+
"create",
|
|
57
|
+
"createMany",
|
|
58
|
+
"deleteMany",
|
|
59
|
+
"findFirst",
|
|
60
|
+
"findMany",
|
|
61
|
+
"transaction",
|
|
62
|
+
"updateMany",
|
|
63
|
+
"upsert"
|
|
64
|
+
]);
|
|
65
|
+
const makeDb = (context) => new Proxy(
|
|
66
|
+
{ internal: context === void 0 ? internal : { ...internal, context } },
|
|
67
|
+
{
|
|
68
|
+
get(target, prop) {
|
|
69
|
+
if (prop === "internal") return target.internal;
|
|
70
|
+
if (prop === "withContext") {
|
|
71
|
+
return (nextContext) => makeDb(nextContext);
|
|
72
|
+
}
|
|
73
|
+
if (!queryMethods.has(prop)) return void 0;
|
|
74
|
+
return async (...args) => {
|
|
75
|
+
const actual = await start();
|
|
76
|
+
const actualDb = context === void 0 ? actual.db : withQueryContext(actual.db, context);
|
|
77
|
+
const method = Reflect.get(actualDb, prop);
|
|
78
|
+
return method.apply(actualDb, args);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
const db = makeDb();
|
|
84
|
+
return {
|
|
85
|
+
db,
|
|
86
|
+
warm: async () => {
|
|
87
|
+
await start();
|
|
88
|
+
},
|
|
89
|
+
close: async () => {
|
|
90
|
+
if (!started) return;
|
|
91
|
+
await (await started).close();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
var makeTestConfig = (options) => {
|
|
96
|
+
const scopes = options?.scopes ?? [
|
|
97
|
+
Scope.make({
|
|
98
|
+
id: ScopeId.make("test-scope"),
|
|
99
|
+
name: options?.scopeName ?? "test",
|
|
100
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
101
|
+
})
|
|
102
|
+
];
|
|
103
|
+
const tables = collectTables(options?.plugins ?? []);
|
|
104
|
+
const testDb = makeLazyTestFumaDb({
|
|
105
|
+
tables,
|
|
106
|
+
backend: options?.backend ?? "sqlite",
|
|
107
|
+
dataDir: options?.dataDir
|
|
108
|
+
});
|
|
109
|
+
const db = withQueryContext(testDb.db, {
|
|
110
|
+
allowedScopeIds: new Set(scopes.map((scope) => String(scope.id)))
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
scopes,
|
|
114
|
+
db,
|
|
115
|
+
plugins: options?.plugins,
|
|
116
|
+
testDb,
|
|
117
|
+
// Tests default to auto-accepting elicitation prompts. Override via
|
|
118
|
+
// a wrapping spread if a test exercises a real handler:
|
|
119
|
+
// { ...makeTestConfig(...), onElicitation: customHandler }
|
|
120
|
+
onElicitation: "accept-all"
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
var TestWorkspace = class _TestWorkspace extends Context.Service()(
|
|
124
|
+
"executor-sdk/TestWorkspace"
|
|
125
|
+
) {
|
|
126
|
+
static current = () => Effect.gen(function* () {
|
|
127
|
+
const workspace = yield* _TestWorkspace;
|
|
128
|
+
return workspace;
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
var makeTestWorkspaceHarness = (options) => Effect.acquireRelease(
|
|
132
|
+
Effect.gen(function* () {
|
|
133
|
+
const config = makeTestConfig(options);
|
|
134
|
+
const executor = yield* createExecutor(config);
|
|
135
|
+
return {
|
|
136
|
+
config,
|
|
137
|
+
executor,
|
|
138
|
+
testDb: config.testDb,
|
|
139
|
+
scopes: config.scopes
|
|
140
|
+
};
|
|
141
|
+
}),
|
|
142
|
+
({ executor, testDb }) => executor.close().pipe(
|
|
143
|
+
Effect.ignore,
|
|
144
|
+
Effect.andThen(Effect.promise(() => testDb.close()).pipe(Effect.ignore))
|
|
145
|
+
)
|
|
146
|
+
);
|
|
147
|
+
var makeTestWorkspaceLayer = (options) => Layer.effect(TestWorkspace)(
|
|
148
|
+
makeTestWorkspaceHarness(options).pipe(
|
|
149
|
+
Effect.tap(({ testDb }) => Effect.promise(() => testDb.warm()))
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
var makeTestExecutor = (options) => makeTestWorkspaceHarness(options).pipe(Effect.map(({ executor }) => executor));
|
|
153
|
+
var memorySecretsPlugin = definePlugin(() => {
|
|
154
|
+
const store = /* @__PURE__ */ new Map();
|
|
155
|
+
const provider = {
|
|
156
|
+
key: "memory",
|
|
157
|
+
writable: true,
|
|
158
|
+
get: (id, scope) => Effect.sync(() => store.get(`${scope}\0${id}`) ?? null),
|
|
159
|
+
set: (id, value, scope) => Effect.sync(() => {
|
|
160
|
+
store.set(`${scope}\0${id}`, value);
|
|
161
|
+
}),
|
|
162
|
+
delete: (id, scope) => Effect.sync(() => store.delete(`${scope}\0${id}`)),
|
|
163
|
+
list: () => Effect.sync(
|
|
164
|
+
() => Array.from(store.keys()).map((key) => {
|
|
165
|
+
const name = key.split("\0", 2)[1] ?? key;
|
|
166
|
+
return { id: name, name };
|
|
167
|
+
})
|
|
168
|
+
)
|
|
169
|
+
};
|
|
170
|
+
return {
|
|
171
|
+
id: "memory-secrets",
|
|
172
|
+
storage: () => ({}),
|
|
173
|
+
secretProviders: [provider]
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// src/testing/oauth-test-server.ts
|
|
8
178
|
import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";
|
|
9
|
-
import { Context, Data, Effect, Layer, Predicate } from "effect";
|
|
179
|
+
import { Context as Context2, Data, Effect as Effect2, Layer as Layer2, Option, Predicate, Ref, Schema } from "effect";
|
|
180
|
+
import { createHash, randomUUID } from "crypto";
|
|
10
181
|
import {
|
|
182
|
+
FetchHttpClient,
|
|
11
183
|
HttpClient,
|
|
12
|
-
|
|
184
|
+
HttpClientRequest,
|
|
13
185
|
HttpServer,
|
|
14
|
-
HttpServerRequest
|
|
186
|
+
HttpServerRequest,
|
|
187
|
+
HttpServerResponse
|
|
15
188
|
} from "effect/unstable/http";
|
|
16
|
-
var
|
|
189
|
+
var OAuthTestServerAddressError = class extends Data.TaggedError("OAuthTestServerAddressError") {
|
|
190
|
+
};
|
|
191
|
+
var OAuthTestServerFlowError = class extends Data.TaggedError("OAuthTestServerFlowError") {
|
|
192
|
+
};
|
|
193
|
+
var JsonObject = Schema.Record(Schema.String, Schema.Unknown);
|
|
194
|
+
var decodeJsonObject = Schema.decodeUnknownOption(Schema.fromJsonString(JsonObject));
|
|
195
|
+
var TokenResponse = Schema.Struct({
|
|
196
|
+
access_token: Schema.String,
|
|
197
|
+
refresh_token: Schema.optional(Schema.String),
|
|
198
|
+
token_type: Schema.String,
|
|
199
|
+
expires_in: Schema.optional(Schema.Number),
|
|
200
|
+
scope: Schema.optional(Schema.String)
|
|
201
|
+
});
|
|
202
|
+
var decodeTokenResponse = Schema.decodeUnknownEffect(TokenResponse);
|
|
203
|
+
var defaultScopes = ["read", "write"];
|
|
204
|
+
var jsonResponse = (status, body, headers = {}) => HttpServerResponse.jsonUnsafe(body, { status, headers });
|
|
205
|
+
var textResponse = (status, body, headers = {}) => HttpServerResponse.text(body, {
|
|
206
|
+
status,
|
|
207
|
+
headers,
|
|
208
|
+
contentType: "text/plain; charset=utf-8"
|
|
209
|
+
});
|
|
210
|
+
var redirectResponse = (location) => HttpServerResponse.redirect(location);
|
|
211
|
+
var parseJsonObject = (body) => {
|
|
212
|
+
const result = decodeJsonObject(body);
|
|
213
|
+
return Option.isSome(result) ? result.value : null;
|
|
214
|
+
};
|
|
215
|
+
var arrayOfStrings = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
216
|
+
var decodeBasicAuthorization = (value) => {
|
|
217
|
+
if (!value) return null;
|
|
218
|
+
const match = /^Basic\s+(.+)$/i.exec(value);
|
|
219
|
+
if (!match) return null;
|
|
220
|
+
const decoded = Buffer.from(match[1], "base64").toString("utf8");
|
|
221
|
+
const separator = decoded.indexOf(":");
|
|
222
|
+
if (separator < 0) return null;
|
|
223
|
+
return {
|
|
224
|
+
username: decoded.slice(0, separator),
|
|
225
|
+
password: decoded.slice(separator + 1)
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
var codeChallengeForVerifier = (verifier) => createHash("sha256").update(verifier).digest("base64url");
|
|
229
|
+
var oauthError = (status, error, errorDescription) => jsonResponse(
|
|
230
|
+
status,
|
|
231
|
+
{
|
|
232
|
+
error,
|
|
233
|
+
error_description: errorDescription
|
|
234
|
+
},
|
|
235
|
+
status === 401 ? { "www-authenticate": 'Basic realm="OAuth test server"' } : {}
|
|
236
|
+
);
|
|
237
|
+
var manualRedirectHttpClientLayer = FetchHttpClient.layer.pipe(
|
|
238
|
+
Layer2.provide(Layer2.succeed(FetchHttpClient.RequestInit, { redirect: "manual" }))
|
|
239
|
+
);
|
|
240
|
+
var executeManualRedirect = (request, requestUrl) => HttpClient.execute(request).pipe(
|
|
241
|
+
Effect2.mapError(
|
|
242
|
+
(cause) => new OAuthTestServerFlowError({
|
|
243
|
+
message: `OAuth test flow request failed for ${requestUrl}`,
|
|
244
|
+
cause
|
|
245
|
+
})
|
|
246
|
+
),
|
|
247
|
+
Effect2.provide(manualRedirectHttpClientLayer)
|
|
248
|
+
);
|
|
249
|
+
var executeOAuthHttp = (request, requestUrl) => HttpClient.execute(request).pipe(
|
|
250
|
+
Effect2.mapError(
|
|
251
|
+
(cause) => new OAuthTestServerFlowError({
|
|
252
|
+
message: `OAuth test flow request failed for ${requestUrl}`,
|
|
253
|
+
cause
|
|
254
|
+
})
|
|
255
|
+
),
|
|
256
|
+
Effect2.provide(FetchHttpClient.layer)
|
|
257
|
+
);
|
|
258
|
+
var requiredRedirectLocation = (response, requestUrl) => Effect2.gen(function* () {
|
|
259
|
+
if (response.status < 300 || response.status >= 400) {
|
|
260
|
+
return yield* new OAuthTestServerFlowError({
|
|
261
|
+
message: `Expected redirect from ${requestUrl}, got HTTP ${response.status}`
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
const location = response.headers.location;
|
|
265
|
+
if (!location) {
|
|
266
|
+
return yield* new OAuthTestServerFlowError({
|
|
267
|
+
message: `Expected Location header from ${requestUrl}`
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return new URL(location, requestUrl).toString();
|
|
271
|
+
});
|
|
272
|
+
var serveOAuthTestHttpApp = (handler) => Effect2.gen(function* () {
|
|
273
|
+
const context = yield* Layer2.build(
|
|
274
|
+
Layer2.fresh(
|
|
275
|
+
HttpServer.serve(
|
|
276
|
+
HttpServerRequest.HttpServerRequest.asEffect().pipe(Effect2.flatMap(handler))
|
|
277
|
+
).pipe(Layer2.provideMerge(NodeHttpServer.layerTest))
|
|
278
|
+
)
|
|
279
|
+
).pipe(Effect2.mapError((address2) => new OAuthTestServerAddressError({ address: address2 })));
|
|
280
|
+
const server = Context2.get(context, HttpServer.HttpServer);
|
|
281
|
+
const address = server.address;
|
|
282
|
+
if (!Predicate.isTagged(address, "TcpAddress")) {
|
|
283
|
+
return yield* new OAuthTestServerAddressError({ address });
|
|
284
|
+
}
|
|
285
|
+
return { baseUrl: `http://127.0.0.1:${address.port}` };
|
|
286
|
+
});
|
|
287
|
+
var requestBodyText = (request) => request.text.pipe(Effect2.catch(() => Effect2.succeed("")));
|
|
288
|
+
var completeAuthorizationCodeFlow = (defaults) => (input) => Effect2.gen(function* () {
|
|
289
|
+
const loginResponse = yield* executeManualRedirect(
|
|
290
|
+
HttpClientRequest.get(input.authorizationUrl),
|
|
291
|
+
input.authorizationUrl
|
|
292
|
+
);
|
|
293
|
+
const loginUrl = yield* requiredRedirectLocation(loginResponse, input.authorizationUrl);
|
|
294
|
+
const credentials = Buffer.from(
|
|
295
|
+
`${input.username ?? defaults.username}:${input.password ?? defaults.password}`
|
|
296
|
+
).toString("base64");
|
|
297
|
+
const callbackResponse = yield* executeManualRedirect(
|
|
298
|
+
HttpClientRequest.post(loginUrl).pipe(
|
|
299
|
+
HttpClientRequest.setHeader("authorization", `Basic ${credentials}`)
|
|
300
|
+
),
|
|
301
|
+
loginUrl
|
|
302
|
+
);
|
|
303
|
+
const callbackUrl = yield* requiredRedirectLocation(callbackResponse, loginUrl);
|
|
304
|
+
const parsed = new URL(callbackUrl);
|
|
305
|
+
const state = parsed.searchParams.get("state");
|
|
306
|
+
const code = parsed.searchParams.get("code");
|
|
307
|
+
if (!state || !code) {
|
|
308
|
+
return yield* new OAuthTestServerFlowError({
|
|
309
|
+
message: "OAuth callback did not include both state and code"
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
return { callbackUrl, state, code };
|
|
313
|
+
});
|
|
314
|
+
var completeAuthorizationCodeTokenFlow = (defaults) => (input = {}) => Effect2.gen(function* () {
|
|
315
|
+
const clientId = input.clientId ?? defaults.clientId;
|
|
316
|
+
const clientSecret = input.clientSecret ?? defaults.clientSecret;
|
|
317
|
+
const redirectUrl = input.redirectUrl ?? "http://127.0.0.1/callback";
|
|
318
|
+
const codeVerifier = `verifier_${randomUUID()}`;
|
|
319
|
+
const state = `state_${randomUUID()}`;
|
|
320
|
+
const authorizationUrl = new URL(defaults.authorizationEndpoint);
|
|
321
|
+
authorizationUrl.searchParams.set("response_type", "code");
|
|
322
|
+
authorizationUrl.searchParams.set("client_id", clientId);
|
|
323
|
+
authorizationUrl.searchParams.set("redirect_uri", redirectUrl);
|
|
324
|
+
authorizationUrl.searchParams.set("state", state);
|
|
325
|
+
authorizationUrl.searchParams.set("code_challenge", codeChallengeForVerifier(codeVerifier));
|
|
326
|
+
authorizationUrl.searchParams.set("code_challenge_method", "S256");
|
|
327
|
+
if (input.scopes && input.scopes.length > 0) {
|
|
328
|
+
authorizationUrl.searchParams.set("scope", input.scopes.join(" "));
|
|
329
|
+
}
|
|
330
|
+
if (input.resource) {
|
|
331
|
+
authorizationUrl.searchParams.set("resource", input.resource);
|
|
332
|
+
}
|
|
333
|
+
const callback = yield* completeAuthorizationCodeFlow({
|
|
334
|
+
username: defaults.username,
|
|
335
|
+
password: defaults.password
|
|
336
|
+
})({
|
|
337
|
+
authorizationUrl: authorizationUrl.toString(),
|
|
338
|
+
username: input.username,
|
|
339
|
+
password: input.password
|
|
340
|
+
});
|
|
341
|
+
const tokenResponse = yield* executeOAuthHttp(
|
|
342
|
+
HttpClientRequest.post(defaults.tokenEndpoint).pipe(
|
|
343
|
+
HttpClientRequest.bodyUrlParams({
|
|
344
|
+
grant_type: "authorization_code",
|
|
345
|
+
code: callback.code,
|
|
346
|
+
redirect_uri: redirectUrl,
|
|
347
|
+
client_id: clientId,
|
|
348
|
+
client_secret: clientSecret,
|
|
349
|
+
code_verifier: codeVerifier
|
|
350
|
+
})
|
|
351
|
+
),
|
|
352
|
+
defaults.tokenEndpoint
|
|
353
|
+
);
|
|
354
|
+
if (tokenResponse.status !== 200) {
|
|
355
|
+
const body = yield* tokenResponse.text.pipe(
|
|
356
|
+
Effect2.catch(() => Effect2.succeed("<unavailable>"))
|
|
357
|
+
);
|
|
358
|
+
return yield* new OAuthTestServerFlowError({
|
|
359
|
+
message: `Expected token response HTTP 200, got HTTP ${tokenResponse.status}: ${body}`
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
const raw = yield* tokenResponse.json.pipe(
|
|
363
|
+
Effect2.mapError(
|
|
364
|
+
(cause) => new OAuthTestServerFlowError({
|
|
365
|
+
message: "OAuth token response was not valid JSON",
|
|
366
|
+
cause
|
|
367
|
+
})
|
|
368
|
+
)
|
|
369
|
+
);
|
|
370
|
+
const token = yield* decodeTokenResponse(raw).pipe(
|
|
371
|
+
Effect2.mapError(
|
|
372
|
+
(cause) => new OAuthTestServerFlowError({
|
|
373
|
+
message: "OAuth token response did not match the expected shape",
|
|
374
|
+
cause
|
|
375
|
+
})
|
|
376
|
+
)
|
|
377
|
+
);
|
|
378
|
+
return {
|
|
379
|
+
accessToken: token.access_token,
|
|
380
|
+
refreshToken: token.refresh_token,
|
|
381
|
+
tokenType: token.token_type,
|
|
382
|
+
expiresIn: token.expires_in,
|
|
383
|
+
scope: token.scope
|
|
384
|
+
};
|
|
385
|
+
});
|
|
386
|
+
var serveOAuthTestServer = (options = {}) => Effect2.gen(function* () {
|
|
387
|
+
const requests = yield* Ref.make([]);
|
|
388
|
+
const issuedAccessTokens = yield* Ref.make(/* @__PURE__ */ new Set());
|
|
389
|
+
const users = {
|
|
390
|
+
[options.defaultUsername ?? "alice"]: options.defaultPassword ?? "password",
|
|
391
|
+
...options.users ?? {}
|
|
392
|
+
};
|
|
393
|
+
const supportRefresh = options.supportRefresh ?? true;
|
|
394
|
+
const scopes = options.scopes ?? defaultScopes;
|
|
395
|
+
const clients = /* @__PURE__ */ new Map();
|
|
396
|
+
const transactions = /* @__PURE__ */ new Map();
|
|
397
|
+
const authorizationCodes = /* @__PURE__ */ new Map();
|
|
398
|
+
const refreshTokens = /* @__PURE__ */ new Map();
|
|
399
|
+
const defaultClientId = options.defaultClientId ?? "test-client";
|
|
400
|
+
const defaultClientSecret = options.defaultClientSecret ?? "test-secret";
|
|
401
|
+
clients.set(defaultClientId, {
|
|
402
|
+
clientSecret: defaultClientSecret,
|
|
403
|
+
redirectUris: /* @__PURE__ */ new Set(),
|
|
404
|
+
tokenEndpointAuthMethod: "client_secret_post"
|
|
405
|
+
});
|
|
406
|
+
for (const [clientId, clientSecret] of Object.entries(options.clients ?? {})) {
|
|
407
|
+
clients.set(clientId, {
|
|
408
|
+
clientSecret,
|
|
409
|
+
redirectUris: /* @__PURE__ */ new Set(),
|
|
410
|
+
tokenEndpointAuthMethod: clientSecret === null ? "none" : "client_secret_post"
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
let issuerUrl = "";
|
|
414
|
+
const server = yield* serveOAuthTestHttpApp(
|
|
415
|
+
(request) => Effect2.gen(function* () {
|
|
416
|
+
const currentIssuerUrl = issuerUrl || "http://127.0.0.1";
|
|
417
|
+
const requestUrl = new URL(request.url, currentIssuerUrl);
|
|
418
|
+
const body = yield* requestBodyText(request);
|
|
419
|
+
const headers = request.headers;
|
|
420
|
+
yield* Ref.update(requests, (all) => [
|
|
421
|
+
...all,
|
|
422
|
+
{
|
|
423
|
+
method: request.method,
|
|
424
|
+
url: requestUrl.toString(),
|
|
425
|
+
path: requestUrl.pathname,
|
|
426
|
+
headers,
|
|
427
|
+
body
|
|
428
|
+
}
|
|
429
|
+
]);
|
|
430
|
+
if (requestUrl.pathname.startsWith("/.well-known/oauth-protected-resource")) {
|
|
431
|
+
const suffix = requestUrl.pathname.slice("/.well-known/oauth-protected-resource".length);
|
|
432
|
+
const resource = `${currentIssuerUrl}${suffix}`;
|
|
433
|
+
return jsonResponse(200, {
|
|
434
|
+
resource,
|
|
435
|
+
authorization_servers: [currentIssuerUrl],
|
|
436
|
+
bearer_methods_supported: ["header"],
|
|
437
|
+
scopes_supported: scopes
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
if (requestUrl.pathname === "/.well-known/oauth-authorization-server" || requestUrl.pathname === "/.well-known/openid-configuration") {
|
|
441
|
+
return jsonResponse(200, {
|
|
442
|
+
issuer: currentIssuerUrl,
|
|
443
|
+
authorization_endpoint: `${currentIssuerUrl}/authorize`,
|
|
444
|
+
token_endpoint: `${currentIssuerUrl}/token`,
|
|
445
|
+
registration_endpoint: `${currentIssuerUrl}/register`,
|
|
446
|
+
response_types_supported: ["code"],
|
|
447
|
+
grant_types_supported: ["authorization_code", "refresh_token", "client_credentials"],
|
|
448
|
+
code_challenge_methods_supported: ["S256"],
|
|
449
|
+
token_endpoint_auth_methods_supported: [
|
|
450
|
+
"none",
|
|
451
|
+
"client_secret_post",
|
|
452
|
+
"client_secret_basic"
|
|
453
|
+
],
|
|
454
|
+
scopes_supported: scopes
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
if (requestUrl.pathname === "/register" && request.method === "POST") {
|
|
458
|
+
const json = parseJsonObject(body);
|
|
459
|
+
if (!json) {
|
|
460
|
+
return oauthError(400, "invalid_client_metadata", "Expected JSON body");
|
|
461
|
+
}
|
|
462
|
+
const requestedMethod = typeof json.token_endpoint_auth_method === "string" ? json.token_endpoint_auth_method : "none";
|
|
463
|
+
const clientId = `client_${randomUUID()}`;
|
|
464
|
+
const clientSecret = requestedMethod === "client_secret_basic" || requestedMethod === "client_secret_post" ? `secret_${randomUUID()}` : null;
|
|
465
|
+
const redirectUris = new Set(arrayOfStrings(json.redirect_uris));
|
|
466
|
+
clients.set(clientId, {
|
|
467
|
+
clientSecret,
|
|
468
|
+
redirectUris,
|
|
469
|
+
tokenEndpointAuthMethod: requestedMethod
|
|
470
|
+
});
|
|
471
|
+
return jsonResponse(
|
|
472
|
+
201,
|
|
473
|
+
{
|
|
474
|
+
client_id: clientId,
|
|
475
|
+
...clientSecret ? { client_secret: clientSecret } : {},
|
|
476
|
+
client_id_issued_at: Math.floor(Date.now() / 1e3),
|
|
477
|
+
token_endpoint_auth_method: requestedMethod,
|
|
478
|
+
redirect_uris: [...redirectUris],
|
|
479
|
+
grant_types: arrayOfStrings(json.grant_types),
|
|
480
|
+
response_types: arrayOfStrings(json.response_types),
|
|
481
|
+
scope: typeof json.scope === "string" ? json.scope : scopes.join(" ")
|
|
482
|
+
},
|
|
483
|
+
{ "cache-control": "no-store" }
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
if (requestUrl.pathname === "/authorize" && request.method === "GET") {
|
|
487
|
+
const clientId = requestUrl.searchParams.get("client_id");
|
|
488
|
+
const redirectUri = requestUrl.searchParams.get("redirect_uri");
|
|
489
|
+
const state = requestUrl.searchParams.get("state");
|
|
490
|
+
const codeChallenge = requestUrl.searchParams.get("code_challenge");
|
|
491
|
+
const responseType = requestUrl.searchParams.get("response_type");
|
|
492
|
+
if (!clientId || !redirectUri || !state || !codeChallenge || responseType !== "code") {
|
|
493
|
+
return oauthError(400, "invalid_request", "Missing authorization parameters");
|
|
494
|
+
}
|
|
495
|
+
const client = clients.get(clientId);
|
|
496
|
+
if (client && client.redirectUris.size > 0 && !client.redirectUris.has(redirectUri)) {
|
|
497
|
+
return oauthError(400, "invalid_request", "redirect_uri is not registered");
|
|
498
|
+
}
|
|
499
|
+
if (!client) {
|
|
500
|
+
clients.set(clientId, {
|
|
501
|
+
clientSecret: null,
|
|
502
|
+
redirectUris: /* @__PURE__ */ new Set([redirectUri]),
|
|
503
|
+
tokenEndpointAuthMethod: "none"
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
const transaction = `txn_${randomUUID()}`;
|
|
507
|
+
transactions.set(transaction, {
|
|
508
|
+
clientId,
|
|
509
|
+
redirectUri,
|
|
510
|
+
state,
|
|
511
|
+
codeChallenge,
|
|
512
|
+
scope: requestUrl.searchParams.get("scope"),
|
|
513
|
+
resource: requestUrl.searchParams.get("resource")
|
|
514
|
+
});
|
|
515
|
+
return redirectResponse(
|
|
516
|
+
`${currentIssuerUrl}/login?transaction=${encodeURIComponent(transaction)}`
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
if (requestUrl.pathname === "/login") {
|
|
520
|
+
const transactionId = requestUrl.searchParams.get("transaction");
|
|
521
|
+
const transaction = transactionId ? transactions.get(transactionId) : void 0;
|
|
522
|
+
if (!transactionId || !transaction) {
|
|
523
|
+
return oauthError(400, "invalid_request", "Unknown login transaction");
|
|
524
|
+
}
|
|
525
|
+
if (request.method === "GET") {
|
|
526
|
+
return textResponse(200, "OAuth test login");
|
|
527
|
+
}
|
|
528
|
+
const basic = decodeBasicAuthorization(headers.authorization);
|
|
529
|
+
if (!basic || users[basic.username] !== basic.password) {
|
|
530
|
+
return jsonResponse(
|
|
531
|
+
401,
|
|
532
|
+
{ error: "access_denied" },
|
|
533
|
+
{ "www-authenticate": 'Basic realm="OAuth test server"' }
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
const code = `code_${randomUUID()}`;
|
|
537
|
+
transactions.delete(transactionId);
|
|
538
|
+
authorizationCodes.set(code, { ...transaction, username: basic.username });
|
|
539
|
+
const callbackUrl = new URL(transaction.redirectUri);
|
|
540
|
+
callbackUrl.searchParams.set("code", code);
|
|
541
|
+
callbackUrl.searchParams.set("state", transaction.state);
|
|
542
|
+
return redirectResponse(callbackUrl.toString());
|
|
543
|
+
}
|
|
544
|
+
if (requestUrl.pathname === "/token" && request.method === "POST") {
|
|
545
|
+
const params = new URLSearchParams(body);
|
|
546
|
+
const basic = decodeBasicAuthorization(headers.authorization);
|
|
547
|
+
const clientId = basic?.username ?? params.get("client_id");
|
|
548
|
+
const clientSecret = basic?.password ?? params.get("client_secret");
|
|
549
|
+
const client = clientId ? clients.get(clientId) : void 0;
|
|
550
|
+
if (!clientId || !client) {
|
|
551
|
+
return oauthError(401, "invalid_client", "Unknown client");
|
|
552
|
+
}
|
|
553
|
+
if (client.clientSecret !== null && client.clientSecret !== clientSecret) {
|
|
554
|
+
return oauthError(401, "invalid_client", "Invalid client secret");
|
|
555
|
+
}
|
|
556
|
+
const grantType = params.get("grant_type");
|
|
557
|
+
if (grantType === "authorization_code") {
|
|
558
|
+
const code = params.get("code");
|
|
559
|
+
const redirectUri = params.get("redirect_uri");
|
|
560
|
+
const codeVerifier = params.get("code_verifier");
|
|
561
|
+
const record = code ? authorizationCodes.get(code) : void 0;
|
|
562
|
+
if (!code || !redirectUri || !codeVerifier || !record) {
|
|
563
|
+
return oauthError(400, "invalid_grant", "Unknown authorization code");
|
|
564
|
+
}
|
|
565
|
+
if (record.clientId !== clientId || record.redirectUri !== redirectUri || record.codeChallenge !== codeChallengeForVerifier(codeVerifier)) {
|
|
566
|
+
return oauthError(400, "invalid_grant", "Authorization code validation failed");
|
|
567
|
+
}
|
|
568
|
+
authorizationCodes.delete(code);
|
|
569
|
+
const accessToken = `at_${randomUUID()}`;
|
|
570
|
+
const refreshToken = `rt_${randomUUID()}`;
|
|
571
|
+
yield* Ref.update(issuedAccessTokens, (tokens) => /* @__PURE__ */ new Set([...tokens, accessToken]));
|
|
572
|
+
refreshTokens.set(refreshToken, {
|
|
573
|
+
clientId,
|
|
574
|
+
username: record.username,
|
|
575
|
+
scope: record.scope,
|
|
576
|
+
resource: record.resource
|
|
577
|
+
});
|
|
578
|
+
return jsonResponse(
|
|
579
|
+
200,
|
|
580
|
+
{
|
|
581
|
+
access_token: accessToken,
|
|
582
|
+
refresh_token: refreshToken,
|
|
583
|
+
token_type: "Bearer",
|
|
584
|
+
expires_in: 3600,
|
|
585
|
+
...record.scope ? { scope: record.scope } : {}
|
|
586
|
+
},
|
|
587
|
+
{ "cache-control": "no-store" }
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
if (grantType === "refresh_token") {
|
|
591
|
+
const refreshToken = params.get("refresh_token");
|
|
592
|
+
const record = refreshToken ? refreshTokens.get(refreshToken) : void 0;
|
|
593
|
+
if (!supportRefresh || !refreshToken || !record || record.clientId !== clientId) {
|
|
594
|
+
return oauthError(400, "invalid_grant", "Unknown refresh token");
|
|
595
|
+
}
|
|
596
|
+
const nextAccessToken = `at_${randomUUID()}`;
|
|
597
|
+
const nextRefreshToken = `rt_${randomUUID()}`;
|
|
598
|
+
refreshTokens.delete(refreshToken);
|
|
599
|
+
refreshTokens.set(nextRefreshToken, record);
|
|
600
|
+
yield* Ref.update(
|
|
601
|
+
issuedAccessTokens,
|
|
602
|
+
(tokens) => /* @__PURE__ */ new Set([...tokens, nextAccessToken])
|
|
603
|
+
);
|
|
604
|
+
return jsonResponse(
|
|
605
|
+
200,
|
|
606
|
+
{
|
|
607
|
+
access_token: nextAccessToken,
|
|
608
|
+
refresh_token: nextRefreshToken,
|
|
609
|
+
token_type: "Bearer",
|
|
610
|
+
expires_in: 3600,
|
|
611
|
+
...record.scope ? { scope: record.scope } : {}
|
|
612
|
+
},
|
|
613
|
+
{ "cache-control": "no-store" }
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
if (grantType === "client_credentials") {
|
|
617
|
+
const accessToken = `at_${randomUUID()}`;
|
|
618
|
+
yield* Ref.update(issuedAccessTokens, (tokens) => /* @__PURE__ */ new Set([...tokens, accessToken]));
|
|
619
|
+
return jsonResponse(
|
|
620
|
+
200,
|
|
621
|
+
{
|
|
622
|
+
access_token: accessToken,
|
|
623
|
+
token_type: "Bearer",
|
|
624
|
+
expires_in: 3600,
|
|
625
|
+
scope: params.get("scope") ?? scopes.join(" ")
|
|
626
|
+
},
|
|
627
|
+
{ "cache-control": "no-store" }
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
return oauthError(400, "unsupported_grant_type", "Unsupported grant type");
|
|
631
|
+
}
|
|
632
|
+
if (requestUrl.pathname === "/mcp") {
|
|
633
|
+
const authorization = headers.authorization;
|
|
634
|
+
const token = authorization?.replace(/^Bearer\s+/i, "");
|
|
635
|
+
const valid = token ? yield* Ref.get(issuedAccessTokens).pipe(Effect2.map((tokens) => tokens.has(token))) : false;
|
|
636
|
+
if (!valid) {
|
|
637
|
+
return jsonResponse(
|
|
638
|
+
401,
|
|
639
|
+
{ error: "invalid_token" },
|
|
640
|
+
{
|
|
641
|
+
"www-authenticate": `Bearer resource_metadata="${currentIssuerUrl}/.well-known/oauth-protected-resource/mcp", error="invalid_token"`
|
|
642
|
+
}
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
return jsonResponse(200, {
|
|
646
|
+
jsonrpc: "2.0",
|
|
647
|
+
id: 1,
|
|
648
|
+
result: { protocolVersion: "2025-06-18", capabilities: {} }
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
return jsonResponse(404, { error: "not_found" });
|
|
652
|
+
})
|
|
653
|
+
);
|
|
654
|
+
issuerUrl = server.baseUrl;
|
|
655
|
+
const accessTokenSet = Ref.get(issuedAccessTokens);
|
|
656
|
+
return {
|
|
657
|
+
issuerUrl,
|
|
658
|
+
authorizationEndpoint: `${issuerUrl}/authorize`,
|
|
659
|
+
tokenEndpoint: `${issuerUrl}/token`,
|
|
660
|
+
registrationEndpoint: `${issuerUrl}/register`,
|
|
661
|
+
protectedResourceMetadataUrl: `${issuerUrl}/.well-known/oauth-protected-resource`,
|
|
662
|
+
resourceUrl: issuerUrl,
|
|
663
|
+
mcpResourceUrl: `${issuerUrl}/mcp`,
|
|
664
|
+
completeAuthorizationCodeFlow: completeAuthorizationCodeFlow({
|
|
665
|
+
username: options.defaultUsername ?? "alice",
|
|
666
|
+
password: options.defaultPassword ?? "password"
|
|
667
|
+
}),
|
|
668
|
+
completeAuthorizationCodeTokenFlow: completeAuthorizationCodeTokenFlow({
|
|
669
|
+
username: options.defaultUsername ?? "alice",
|
|
670
|
+
password: options.defaultPassword ?? "password",
|
|
671
|
+
clientId: defaultClientId,
|
|
672
|
+
clientSecret: defaultClientSecret,
|
|
673
|
+
authorizationEndpoint: `${issuerUrl}/authorize`,
|
|
674
|
+
tokenEndpoint: `${issuerUrl}/token`
|
|
675
|
+
}),
|
|
676
|
+
requests: Ref.get(requests),
|
|
677
|
+
clearRequests: Ref.set(requests, []),
|
|
678
|
+
issuedAccessTokens: accessTokenSet.pipe(Effect2.map((tokens) => [...tokens])),
|
|
679
|
+
acceptsAccessToken: (token) => accessTokenSet.pipe(Effect2.map((tokens) => tokens.has(token))),
|
|
680
|
+
acceptsAuthorizationHeader: (authorization) => {
|
|
681
|
+
const token = authorization?.replace(/^Bearer\s+/i, "");
|
|
682
|
+
return token ? accessTokenSet.pipe(Effect2.map((tokens) => tokens.has(token))) : Effect2.succeed(false);
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
});
|
|
686
|
+
var OAuthTestServer = class _OAuthTestServer extends Context2.Service()(
|
|
687
|
+
"@executor-js/sdk/testing/OAuthTestServer"
|
|
688
|
+
) {
|
|
689
|
+
static layer = (options) => Layer2.effect(_OAuthTestServer, serveOAuthTestServer(options));
|
|
17
690
|
};
|
|
18
|
-
|
|
691
|
+
|
|
692
|
+
// src/testing.ts
|
|
693
|
+
var TestHttpServerAddressError = class extends Data2.TaggedError("TestHttpServerAddressError") {
|
|
694
|
+
};
|
|
695
|
+
var TestHttpServerServeError = class extends Data2.TaggedError("TestHttpServerServeError") {
|
|
19
696
|
};
|
|
20
697
|
var testHttpRoute = HttpRouter.route;
|
|
21
698
|
var serveTestHttpRoutes = (routes) => makeTestHttpServer(
|
|
@@ -25,32 +702,43 @@ var serveTestHttpRoutes = (routes) => makeTestHttpServer(
|
|
|
25
702
|
})
|
|
26
703
|
);
|
|
27
704
|
var serveTestHttpApp = (handler) => makeTestHttpServer(
|
|
28
|
-
|
|
705
|
+
HttpServer2.serve(HttpServerRequest2.HttpServerRequest.asEffect().pipe(Effect3.flatMap(handler)))
|
|
29
706
|
);
|
|
30
|
-
var
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
707
|
+
var serveTestHttpServerLayer = (serverLayer) => makeTestHttpServer(serverLayer);
|
|
708
|
+
var makeTestHttpServer = (serverLayer) => Effect3.gen(function* () {
|
|
709
|
+
const context = yield* Layer3.build(
|
|
710
|
+
Layer3.fresh(serverLayer.pipe(Layer3.provideMerge(NodeHttpServer2.layerTest)))
|
|
711
|
+
).pipe(Effect3.mapError((cause) => new TestHttpServerServeError({ cause })));
|
|
712
|
+
const server = Context3.get(context, HttpServer2.HttpServer);
|
|
35
713
|
const address = server.address;
|
|
36
|
-
if (!
|
|
714
|
+
if (!Predicate2.isTagged(address, "TcpAddress")) {
|
|
37
715
|
return yield* new TestHttpServerAddressError({ address });
|
|
38
716
|
}
|
|
39
|
-
const client =
|
|
717
|
+
const client = Context3.get(context, HttpClient2.HttpClient);
|
|
40
718
|
const baseUrl = `http://127.0.0.1:${address.port}`;
|
|
41
719
|
return {
|
|
42
720
|
baseUrl,
|
|
43
|
-
httpClientLayer:
|
|
721
|
+
httpClientLayer: Layer3.succeed(HttpClient2.HttpClient, client),
|
|
44
722
|
url: (path = "") => new URL(path, baseUrl).toString()
|
|
45
723
|
};
|
|
46
724
|
});
|
|
47
725
|
export {
|
|
726
|
+
OAuthTestServer,
|
|
727
|
+
OAuthTestServerAddressError,
|
|
728
|
+
OAuthTestServerFlowError,
|
|
48
729
|
TestHttpServerAddressError,
|
|
49
730
|
TestHttpServerServeError,
|
|
731
|
+
TestWorkspace,
|
|
732
|
+
createSqliteTestFumaDb,
|
|
50
733
|
makeTestConfig,
|
|
734
|
+
makeTestExecutor,
|
|
735
|
+
makeTestWorkspaceHarness,
|
|
736
|
+
makeTestWorkspaceLayer,
|
|
51
737
|
memorySecretsPlugin,
|
|
738
|
+
serveOAuthTestServer,
|
|
52
739
|
serveTestHttpApp,
|
|
53
740
|
serveTestHttpRoutes,
|
|
741
|
+
serveTestHttpServerLayer,
|
|
54
742
|
testHttpRoute
|
|
55
743
|
};
|
|
56
744
|
//# sourceMappingURL=testing.js.map
|