@effect-app/infra 1.21.0 → 1.23.0
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/CHANGELOG.md +12 -0
- package/_cjs/api/internal/auth.cjs +56 -0
- package/_cjs/api/internal/auth.cjs.map +1 -0
- package/_cjs/api/middlewares.cjs +12 -0
- package/_cjs/api/middlewares.cjs.map +1 -1
- package/_cjs/services/QueueMaker/SQLQueue.cjs +161 -0
- package/_cjs/services/QueueMaker/SQLQueue.cjs.map +1 -0
- package/dist/api/internal/auth.d.ts +8 -0
- package/dist/api/internal/auth.d.ts.map +1 -0
- package/dist/api/internal/auth.js +45 -0
- package/dist/api/middlewares.d.ts +1 -0
- package/dist/api/middlewares.d.ts.map +1 -1
- package/dist/api/middlewares.js +2 -1
- package/dist/services/QueueMaker/SQLQueue.d.ts +22 -0
- package/dist/services/QueueMaker/SQLQueue.d.ts.map +1 -0
- package/dist/services/QueueMaker/SQLQueue.js +143 -0
- package/package.json +13 -2
- package/src/api/internal/auth.ts +69 -0
- package/src/api/middlewares.ts +1 -0
- package/src/services/QueueMaker/SQLQueue.ts +201 -0
- package/vitest.config.ts.timestamp-1711656440838-19c636fe320df.mjs +0 -0
- package/vitest.config.ts.timestamp-1711724061890-6ecedb0a07fdd.mjs +0 -0
- package/vitest.config.ts.timestamp-1711743489537-da8d9e5f66c9f.mjs +0 -0
- package/vitest.config.ts.timestamp-1711744615239-dcf257a844e01.mjs +0 -37
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.checkJwt = exports.checkJWTI = void 0;
|
|
7
|
+
var _effectApp = require("effect-app");
|
|
8
|
+
var _http = require("effect-app/http");
|
|
9
|
+
var _expressOauth2JwtBearer = require("express-oauth2-jwt-bearer");
|
|
10
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
11
|
+
/* eslint-disable unused-imports/no-unused-vars */
|
|
12
|
+
|
|
13
|
+
const checkJWTI = config => {
|
|
14
|
+
const mw = (0, _expressOauth2JwtBearer.auth)(config);
|
|
15
|
+
return _effectApp.Effect.gen(function* () {
|
|
16
|
+
const req = yield* _http.HttpServerRequest.HttpServerRequest;
|
|
17
|
+
return yield* _effectApp.Effect.async(cb => {
|
|
18
|
+
const next = err => {
|
|
19
|
+
if (!err) return cb(_effectApp.Effect.void);
|
|
20
|
+
if (err instanceof _expressOauth2JwtBearer.InsufficientScopeError || err instanceof _expressOauth2JwtBearer.InvalidRequestError || err instanceof _expressOauth2JwtBearer.InvalidTokenError || err instanceof _expressOauth2JwtBearer.UnauthorizedError) {
|
|
21
|
+
return cb(_effectApp.Effect.fail(err));
|
|
22
|
+
}
|
|
23
|
+
return _effectApp.Effect.die(err);
|
|
24
|
+
};
|
|
25
|
+
const r = {
|
|
26
|
+
headers: req.headers,
|
|
27
|
+
query: {},
|
|
28
|
+
body: {},
|
|
29
|
+
is: () => false
|
|
30
|
+
}; // is("urlencoded")
|
|
31
|
+
try {
|
|
32
|
+
mw(r, {}, next);
|
|
33
|
+
} catch (e) {
|
|
34
|
+
return cb(_effectApp.Effect.die(e));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
exports.checkJWTI = checkJWTI;
|
|
40
|
+
const checkJwt = config => {
|
|
41
|
+
const check = checkJWTI(config);
|
|
42
|
+
return _http.HttpMiddleware.make(app => _effectApp.Effect.gen(function* () {
|
|
43
|
+
const response = yield* check.pipe(_effectApp.Effect.catchAll(e => _effectApp.Effect.succeed(_http.HttpServerResponse.unsafeJson({
|
|
44
|
+
message: e.message
|
|
45
|
+
}, {
|
|
46
|
+
status: e.status,
|
|
47
|
+
headers: _http.HttpHeaders.fromInput(e.headers)
|
|
48
|
+
}))));
|
|
49
|
+
if (response) {
|
|
50
|
+
return response;
|
|
51
|
+
}
|
|
52
|
+
return yield* app;
|
|
53
|
+
}));
|
|
54
|
+
};
|
|
55
|
+
exports.checkJwt = checkJwt;
|
|
56
|
+
//# sourceMappingURL=auth.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.cjs","names":["_effectApp","require","_http","_expressOauth2JwtBearer","checkJWTI","config","mw","auth","Effect","gen","req","HttpServerRequest","async","cb","next","err","void","InsufficientScopeError","InvalidRequestError","InvalidTokenError","UnauthorizedError","fail","die","r","headers","query","body","is","e","exports","checkJwt","check","HttpMiddleware","make","app","response","pipe","catchAll","succeed","HttpServerResponse","unsafeJson","message","status","HttpHeaders","fromInput"],"sources":["../../../src/api/internal/auth.ts"],"sourcesContent":[null],"mappings":";;;;;;AAEA,IAAAA,UAAA,GAAAC,OAAA;AACA,IAAAC,KAAA,GAAAD,OAAA;AACA,IAAAE,uBAAA,GAAAF,OAAA;AAJA;AACA;;AAgBO,MAAMG,SAAS,GAAIC,MAAc,IAAI;EAC1C,MAAMC,EAAE,GAAG,IAAAC,4BAAI,EAACF,MAAM,CAAC;EACvB,OAAOG,iBAAM,CAACC,GAAG,CAAC,aAAS;IACzB,MAAMC,GAAG,GAAG,OAAOC,uBAAiB,CAACA,iBAAiB;IAEtD,OAAO,OAAOH,iBAAM,CAACI,KAAK,CAIvBC,EAAE,IAAI;MACL,MAAMC,IAAI,GAAIC,GAAa,IAAI;QAC7B,IAAI,CAACA,GAAG,EAAE,OAAOF,EAAE,CAACL,iBAAM,CAACQ,IAAI,CAAC;QAChC,IACED,GAAG,YAAYE,8CAAsB,IAClCF,GAAG,YAAYG,2CAAmB,IAClCH,GAAG,YAAYI,yCAAiB,IAChCJ,GAAG,YAAYK,yCAAiB,EACnC;UACA,OAAOP,EAAE,CAACL,iBAAM,CAACa,IAAI,CAACN,GAAG,CAAC,CAAC;QAC7B;QACA,OAAOP,iBAAM,CAACc,GAAG,CAACP,GAAG,CAAC;MACxB,CAAC;MACD,MAAMQ,CAAC,GAAG;QAAEC,OAAO,EAAEd,GAAG,CAACc,OAAO;QAAEC,KAAK,EAAE,EAAE;QAAEC,IAAI,EAAE,EAAE;QAAEC,EAAE,EAAEA,CAAA,KAAM;MAAK,CAAE,EAAC;MACzE,IAAI;QACFrB,EAAE,CAACiB,CAAQ,EAAE,EAAS,EAAET,IAAI,CAAC;MAC/B,CAAC,CAAC,OAAOc,CAAC,EAAE;QACV,OAAOf,EAAE,CAACL,iBAAM,CAACc,GAAG,CAACM,CAAC,CAAC,CAAC;MAC1B;IACF,CAAC,CACF;EACH,CAAC,CAAC;AACJ,CAAC;AAAAC,OAAA,CAAAzB,SAAA,GAAAA,SAAA;AAEM,MAAM0B,QAAQ,GAAIzB,MAAc,IAAI;EACzC,MAAM0B,KAAK,GAAG3B,SAAS,CAACC,MAAM,CAAC;EAC/B,OAAO2B,oBAAc,CAACC,IAAI,CAAEC,GAAG,IAC7B1B,iBAAM,CAACC,GAAG,CAAC,aAAS;IAClB,MAAM0B,QAAQ,GAAG,OAAOJ,KAAK,CAACK,IAAI,CAAC5B,iBAAM,CAAC6B,QAAQ,CAAET,CAAC,IACnDpB,iBAAM,CAAC8B,OAAO,CACZC,wBAAkB,CAACC,UAAU,CAAC;MAAEC,OAAO,EAAEb,CAAC,CAACa;IAAO,CAAE,EAAE;MACpDC,MAAM,EAAEd,CAAC,CAACc,MAAM;MAChBlB,OAAO,EAAEmB,iBAAW,CAACC,SAAS,CAAChB,CAAC,CAACJ,OAAO;KACzC,CAAC,CACH,CACF,CAAC;IACF,IAAIW,QAAQ,EAAE;MACZ,OAAOA,QAAQ;IACjB;IACA,OAAO,OAAOD,GAAG;EACnB,CAAC,CAAC,CACH;AACH,CAAC;AAAAL,OAAA,CAAAC,QAAA,GAAAA,QAAA","ignoreList":[]}
|
package/_cjs/api/middlewares.cjs
CHANGED
|
@@ -13,6 +13,18 @@ var _exportNames = {
|
|
|
13
13
|
};
|
|
14
14
|
exports.uuidLogAnnotation = exports.errorLog = exports.endpointCallsMetric = exports.cors = exports.basicAuth = exports.accessLog = void 0;
|
|
15
15
|
var internal = _interopRequireWildcard(require("./internal/middlewares.cjs"));
|
|
16
|
+
var _auth = require("./internal/auth.cjs");
|
|
17
|
+
Object.keys(_auth).forEach(function (key) {
|
|
18
|
+
if (key === "default" || key === "__esModule") return;
|
|
19
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
20
|
+
if (key in exports && exports[key] === _auth[key]) return;
|
|
21
|
+
Object.defineProperty(exports, key, {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
get: function () {
|
|
24
|
+
return _auth[key];
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
16
28
|
var _events = require("./internal/events.cjs");
|
|
17
29
|
Object.keys(_events).forEach(function (key) {
|
|
18
30
|
if (key === "default" || key === "__esModule") return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middlewares.cjs","names":["internal","_interopRequireWildcard","require","
|
|
1
|
+
{"version":3,"file":"middlewares.cjs","names":["internal","_interopRequireWildcard","require","_auth","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_events","_health","_RequestContextMiddleware","_getRequireWildcardCache","e","WeakMap","r","t","__esModule","default","has","n","__proto__","a","getOwnPropertyDescriptor","u","i","set","accessLog","uuidLogAnnotation","endpointCallsMetric","errorLog","basicAuth","cors"],"sources":["../../src/api/middlewares.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.QueueId = void 0;
|
|
7
|
+
exports.makeSQLQueue = makeSQLQueue;
|
|
8
|
+
var _setupRequest = require("@effect-app/infra/api/setupRequest");
|
|
9
|
+
var _RequestContext = require("@effect-app/infra/RequestContext");
|
|
10
|
+
var _errors = require("@effect-app/infra/services/QueueMaker/errors");
|
|
11
|
+
var _service = require("@effect-app/infra/services/QueueMaker/service");
|
|
12
|
+
var _RequestContextContainer = require("@effect-app/infra/services/RequestContextContainer");
|
|
13
|
+
var _sql = require("@effect/sql");
|
|
14
|
+
var _dateFns = require("date-fns");
|
|
15
|
+
var _effectApp = require("effect-app");
|
|
16
|
+
var _ids = require("effect-app/ids");
|
|
17
|
+
var _schema = require("effect-app/schema");
|
|
18
|
+
var _utils = require("effect-app/utils");
|
|
19
|
+
const QueueId = exports.QueueId = _effectApp.S.Number.pipe(_effectApp.S.brand("QueueId"));
|
|
20
|
+
/**
|
|
21
|
+
* Currently limited to one process draining at a time, due to in-process Semaphore instead of row-level locking.
|
|
22
|
+
*/
|
|
23
|
+
function makeSQLQueue(queueName, queueDrainName, schema, drainSchema) {
|
|
24
|
+
return _effectApp.Effect.gen(function* () {
|
|
25
|
+
const base = {
|
|
26
|
+
id: _sql.Model.Generated(QueueId),
|
|
27
|
+
meta: _sql.Model.JsonFromString(_service.QueueMeta),
|
|
28
|
+
name: _effectApp.S.NonEmptyString255,
|
|
29
|
+
createdAt: _sql.Model.DateTimeInsert,
|
|
30
|
+
updatedAt: _sql.Model.DateTimeUpdate,
|
|
31
|
+
// TODO: at+owner
|
|
32
|
+
processingAt: _sql.Model.FieldOption(_effectApp.S.Date),
|
|
33
|
+
finishedAt: _sql.Model.FieldOption(_effectApp.S.Date)
|
|
34
|
+
// TODO: record locking.. / optimistic locking
|
|
35
|
+
// rowVersion: Model.DateTimeFromNumberWithNow
|
|
36
|
+
};
|
|
37
|
+
class Queue extends _sql.Model.Class("Queue")({
|
|
38
|
+
body: _sql.Model.JsonFromString(schema),
|
|
39
|
+
...base
|
|
40
|
+
}) {}
|
|
41
|
+
class Drain extends _sql.Model.Class("Drain")({
|
|
42
|
+
body: _sql.Model.JsonFromString(drainSchema),
|
|
43
|
+
...base
|
|
44
|
+
}) {}
|
|
45
|
+
const sql = yield* _sql.SqlClient.SqlClient;
|
|
46
|
+
const queueRepo = yield* _sql.Model.makeRepository(Queue, {
|
|
47
|
+
tableName: "queue",
|
|
48
|
+
spanPrefix: "QueueRepo",
|
|
49
|
+
idColumn: "id"
|
|
50
|
+
});
|
|
51
|
+
const drainRepo = yield* _sql.Model.makeRepository(Drain, {
|
|
52
|
+
tableName: "queue",
|
|
53
|
+
spanPrefix: "DrainRepo",
|
|
54
|
+
idColumn: "id"
|
|
55
|
+
});
|
|
56
|
+
const decodeDrain = _effectApp.S.decode(Drain);
|
|
57
|
+
const drain = () => {
|
|
58
|
+
const limit = (0, _dateFns.subMinutes)(new Date(), 15);
|
|
59
|
+
return sql`SELECT *
|
|
60
|
+
FROM queue
|
|
61
|
+
WHERE name = ${queueDrainName} AND finishedAt IS NULL AND (processingAt IS NULL OR processingAt < ${limit.getTime()})
|
|
62
|
+
LIMIT 1`;
|
|
63
|
+
};
|
|
64
|
+
// temporary workaround until we have a SQLite rowversion..
|
|
65
|
+
const lock = yield* _effectApp.Effect.makeSemaphore(1);
|
|
66
|
+
const q = {
|
|
67
|
+
offer: (body, meta) => _effectApp.Effect.gen(function* () {
|
|
68
|
+
yield* queueRepo.insert(Queue.insert.make({
|
|
69
|
+
body,
|
|
70
|
+
meta,
|
|
71
|
+
name: queueName,
|
|
72
|
+
processingAt: _effectApp.Option.none(),
|
|
73
|
+
finishedAt: _effectApp.Option.none()
|
|
74
|
+
}));
|
|
75
|
+
}),
|
|
76
|
+
take: _effectApp.Effect.gen(function* () {
|
|
77
|
+
while (true) {
|
|
78
|
+
const first = yield* lock.withPermits(1)(_effectApp.Effect.gen(function* () {
|
|
79
|
+
const [first] = yield* drain();
|
|
80
|
+
if (first) {
|
|
81
|
+
const dec = yield* decodeDrain(first);
|
|
82
|
+
const {
|
|
83
|
+
createdAt,
|
|
84
|
+
updatedAt,
|
|
85
|
+
...rest
|
|
86
|
+
} = dec;
|
|
87
|
+
yield* drainRepo.update(Drain.update.make({
|
|
88
|
+
...rest,
|
|
89
|
+
processingAt: _effectApp.Option.some(new Date())
|
|
90
|
+
}));
|
|
91
|
+
return dec;
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}));
|
|
95
|
+
if (first) return first;
|
|
96
|
+
yield* _effectApp.Effect.sleep(250);
|
|
97
|
+
}
|
|
98
|
+
}),
|
|
99
|
+
finish: ({
|
|
100
|
+
createdAt,
|
|
101
|
+
updatedAt,
|
|
102
|
+
...q
|
|
103
|
+
}) => drainRepo.update(Drain.update.make({
|
|
104
|
+
...q,
|
|
105
|
+
finishedAt: _effectApp.Option.some(new Date())
|
|
106
|
+
}))
|
|
107
|
+
};
|
|
108
|
+
const rcc = yield* _RequestContextContainer.RequestContextContainer;
|
|
109
|
+
return {
|
|
110
|
+
publish: (...messages) => _effectApp.Effect.gen(function* ($) {
|
|
111
|
+
const requestContext = yield* $(rcc.requestContext);
|
|
112
|
+
const span = yield* $(_effectApp.Effect.serviceOption(_effectApp.Tracer.ParentSpan));
|
|
113
|
+
return yield* $(_effectApp.Effect.forEach(messages, m => q.offer(m, {
|
|
114
|
+
requestContext: new _RequestContext.RequestContext(requestContext),
|
|
115
|
+
// workaround Schema expecting exact class
|
|
116
|
+
span: _effectApp.Option.getOrUndefined(span)
|
|
117
|
+
}), {
|
|
118
|
+
discard: true
|
|
119
|
+
}));
|
|
120
|
+
}).pipe(_effectApp.Effect.withSpan("queue.publish: " + queueName, {
|
|
121
|
+
captureStackTrace: false,
|
|
122
|
+
kind: "producer",
|
|
123
|
+
attributes: {
|
|
124
|
+
"message_tags": messages.map(_ => _._tag)
|
|
125
|
+
}
|
|
126
|
+
})),
|
|
127
|
+
drain: (handleEvent, sessionId) => _effectApp.Effect.gen(function* () {
|
|
128
|
+
const silenceAndReportError = (0, _errors.reportNonInterruptedFailure)({
|
|
129
|
+
name: "MemQueue.drain." + queueDrainName
|
|
130
|
+
});
|
|
131
|
+
const processMessage = msg => _effectApp.Effect.succeed(msg).pipe(_effectApp.Effect.flatMap(({
|
|
132
|
+
body,
|
|
133
|
+
meta
|
|
134
|
+
}) => {
|
|
135
|
+
let effect = _effectApp.Effect.logDebug(`[${queueDrainName}] Processing incoming message`).pipe(_effectApp.Effect.annotateLogs({
|
|
136
|
+
body: (0, _utils.pretty)(body),
|
|
137
|
+
meta: (0, _utils.pretty)(meta)
|
|
138
|
+
}), _effectApp.Effect.zipRight(handleEvent(body)), silenceAndReportError, _ => (0, _setupRequest.setupRequestContext)(_, _RequestContext.RequestContext.inherit(meta.requestContext, {
|
|
139
|
+
id: (0, _ids.RequestId)(body.id),
|
|
140
|
+
locale: "en",
|
|
141
|
+
name: (0, _schema.NonEmptyString255)(`${queueDrainName}.${body._tag}`)
|
|
142
|
+
})), _effectApp.Effect.withSpan(`queue.drain: ${queueDrainName}.${body._tag}`, {
|
|
143
|
+
captureStackTrace: false,
|
|
144
|
+
kind: "consumer",
|
|
145
|
+
attributes: {
|
|
146
|
+
"queue.name": queueDrainName,
|
|
147
|
+
"queue.sessionId": sessionId,
|
|
148
|
+
"queue.input": body
|
|
149
|
+
}
|
|
150
|
+
}));
|
|
151
|
+
if (meta.span) {
|
|
152
|
+
effect = _effectApp.Effect.withParentSpan(effect, _effectApp.Tracer.externalSpan(meta.span));
|
|
153
|
+
}
|
|
154
|
+
return effect;
|
|
155
|
+
}));
|
|
156
|
+
return yield* q.take.pipe(_effectApp.Effect.flatMap(x => processMessage(x).pipe(_effectApp.Effect.uninterruptible, _effectApp.Effect.fork, _effectApp.Effect.flatMap(_effectApp.Fiber.join), _effectApp.Effect.tap(q.finish(x)))), silenceAndReportError, _effectApp.Effect.forever);
|
|
157
|
+
})
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=SQLQueue.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SQLQueue.cjs","names":["_setupRequest","require","_RequestContext","_errors","_service","_RequestContextContainer","_sql","_dateFns","_effectApp","_ids","_schema","_utils","QueueId","exports","S","Number","pipe","brand","makeSQLQueue","queueName","queueDrainName","schema","drainSchema","Effect","gen","base","id","Model","Generated","meta","JsonFromString","QueueMeta","name","NonEmptyString255","createdAt","DateTimeInsert","updatedAt","DateTimeUpdate","processingAt","FieldOption","Date","finishedAt","Queue","Class","body","Drain","sql","SqlClient","queueRepo","makeRepository","tableName","spanPrefix","idColumn","drainRepo","decodeDrain","decode","drain","limit","subMinutes","getTime","lock","makeSemaphore","q","offer","insert","make","Option","none","take","first","withPermits","dec","rest","update","some","sleep","finish","rcc","RequestContextContainer","publish","messages","$","requestContext","span","serviceOption","Tracer","ParentSpan","forEach","m","RequestContext","getOrUndefined","discard","withSpan","captureStackTrace","kind","attributes","map","_","_tag","handleEvent","sessionId","silenceAndReportError","reportNonInterruptedFailure","processMessage","msg","succeed","flatMap","effect","logDebug","annotateLogs","pretty","zipRight","setupRequestContext","inherit","RequestId","locale","withParentSpan","externalSpan","x","uninterruptible","fork","Fiber","join","tap","forever"],"sources":["../../../src/services/QueueMaker/SQLQueue.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Effect } from "effect-app";
|
|
2
|
+
import { HttpServerRequest, HttpServerResponse } from "effect-app/http";
|
|
3
|
+
import { auth, UnauthorizedError } from "express-oauth2-jwt-bearer";
|
|
4
|
+
type Config = Parameters<typeof auth>[0];
|
|
5
|
+
export declare const checkJWTI: (config: Config) => Effect.Effect<void, UnauthorizedError, HttpServerRequest.HttpServerRequest>;
|
|
6
|
+
export declare const checkJwt: (config: Config) => <E, R>(app: import("@effect/platform/HttpApp").Default<E, R>) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, HttpServerRequest.HttpServerRequest | R>;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/api/internal/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACnC,OAAO,EAA+B,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACpG,OAAO,EACL,IAAI,EAIJ,iBAAiB,EAClB,MAAM,2BAA2B,CAAA;AAMlC,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;AACxC,eAAO,MAAM,SAAS,WAAY,MAAM,gFA+BvC,CAAA;AAED,eAAO,MAAM,QAAQ,WAAY,MAAM,sKAkBtC,CAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable unused-imports/no-unused-vars */
|
|
3
|
+
import { Effect } from "effect-app";
|
|
4
|
+
import { HttpHeaders, HttpMiddleware, HttpServerRequest, HttpServerResponse } from "effect-app/http";
|
|
5
|
+
import { auth, InsufficientScopeError, InvalidRequestError, InvalidTokenError, UnauthorizedError } from "express-oauth2-jwt-bearer";
|
|
6
|
+
export const checkJWTI = (config) => {
|
|
7
|
+
const mw = auth(config);
|
|
8
|
+
return Effect.gen(function* () {
|
|
9
|
+
const req = yield* HttpServerRequest.HttpServerRequest;
|
|
10
|
+
return yield* Effect.async((cb) => {
|
|
11
|
+
const next = (err) => {
|
|
12
|
+
if (!err)
|
|
13
|
+
return cb(Effect.void);
|
|
14
|
+
if (err instanceof InsufficientScopeError
|
|
15
|
+
|| err instanceof InvalidRequestError
|
|
16
|
+
|| err instanceof InvalidTokenError
|
|
17
|
+
|| err instanceof UnauthorizedError) {
|
|
18
|
+
return cb(Effect.fail(err));
|
|
19
|
+
}
|
|
20
|
+
return Effect.die(err);
|
|
21
|
+
};
|
|
22
|
+
const r = { headers: req.headers, query: {}, body: {}, is: () => false }; // is("urlencoded")
|
|
23
|
+
try {
|
|
24
|
+
mw(r, {}, next);
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
return cb(Effect.die(e));
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
export const checkJwt = (config) => {
|
|
33
|
+
const check = checkJWTI(config);
|
|
34
|
+
return HttpMiddleware.make((app) => Effect.gen(function* () {
|
|
35
|
+
const response = yield* check.pipe(Effect.catchAll((e) => Effect.succeed(HttpServerResponse.unsafeJson({ message: e.message }, {
|
|
36
|
+
status: e.status,
|
|
37
|
+
headers: HttpHeaders.fromInput(e.headers)
|
|
38
|
+
}))));
|
|
39
|
+
if (response) {
|
|
40
|
+
return response;
|
|
41
|
+
}
|
|
42
|
+
return yield* app;
|
|
43
|
+
}));
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9hcGkvaW50ZXJuYWwvYXV0aC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx1REFBdUQ7QUFDdkQsa0RBQWtEO0FBQ2xELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFDbkMsT0FBTyxFQUFFLFdBQVcsRUFBRSxjQUFjLEVBQUUsaUJBQWlCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUNwRyxPQUFPLEVBQ0wsSUFBSSxFQUNKLHNCQUFzQixFQUN0QixtQkFBbUIsRUFDbkIsaUJBQWlCLEVBQ2pCLGlCQUFpQixFQUNsQixNQUFNLDJCQUEyQixDQUFBO0FBT2xDLE1BQU0sQ0FBQyxNQUFNLFNBQVMsR0FBRyxDQUFDLE1BQWMsRUFBRSxFQUFFO0lBQzFDLE1BQU0sRUFBRSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQTtJQUN2QixPQUFPLE1BQU0sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDO1FBQ3pCLE1BQU0sR0FBRyxHQUFHLEtBQUssQ0FBQyxDQUFDLGlCQUFpQixDQUFDLGlCQUFpQixDQUFBO1FBRXRELE9BQU8sS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FJeEIsQ0FBQyxFQUFFLEVBQUUsRUFBRTtZQUNMLE1BQU0sSUFBSSxHQUFHLENBQUMsR0FBYSxFQUFFLEVBQUU7Z0JBQzdCLElBQUksQ0FBQyxHQUFHO29CQUFFLE9BQU8sRUFBRSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQTtnQkFDaEMsSUFDRSxHQUFHLFlBQVksc0JBQXNCO3VCQUNsQyxHQUFHLFlBQVksbUJBQW1CO3VCQUNsQyxHQUFHLFlBQVksaUJBQWlCO3VCQUNoQyxHQUFHLFlBQVksaUJBQWlCLEVBQ25DLENBQUM7b0JBQ0QsT0FBTyxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFBO2dCQUM3QixDQUFDO2dCQUNELE9BQU8sTUFBTSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQTtZQUN4QixDQUFDLENBQUE7WUFDRCxNQUFNLENBQUMsR0FBRyxFQUFFLE9BQU8sRUFBRSxHQUFHLENBQUMsT0FBTyxFQUFFLEtBQUssRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsS0FBSyxFQUFFLENBQUEsQ0FBQyxtQkFBbUI7WUFDNUYsSUFBSSxDQUFDO2dCQUNILEVBQUUsQ0FBQyxDQUFRLEVBQUUsRUFBUyxFQUFFLElBQUksQ0FBQyxDQUFBO1lBQy9CLENBQUM7WUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUNYLE9BQU8sRUFBRSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQTtZQUMxQixDQUFDO1FBQ0gsQ0FBQyxDQUNGLENBQUE7SUFDSCxDQUFDLENBQUMsQ0FBQTtBQUNKLENBQUMsQ0FBQTtBQUVELE1BQU0sQ0FBQyxNQUFNLFFBQVEsR0FBRyxDQUFDLE1BQWMsRUFBRSxFQUFFO0lBQ3pDLE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQTtJQUMvQixPQUFPLGNBQWMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUNqQyxNQUFNLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQztRQUNsQixNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUN2RCxNQUFNLENBQUMsT0FBTyxDQUNaLGtCQUFrQixDQUFDLFVBQVUsQ0FBQyxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUU7WUFDcEQsTUFBTSxFQUFFLENBQUMsQ0FBQyxNQUFNO1lBQ2hCLE9BQU8sRUFBRSxXQUFXLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUM7U0FDMUMsQ0FBQyxDQUNILENBQ0YsQ0FBQyxDQUFBO1FBQ0YsSUFBSSxRQUFRLEVBQUUsQ0FBQztZQUNiLE9BQU8sUUFBUSxDQUFBO1FBQ2pCLENBQUM7UUFDRCxPQUFPLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQTtJQUNuQixDQUFDLENBQUMsQ0FDSCxDQUFBO0FBQ0gsQ0FBQyxDQUFBIn0=
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type * as App from "@effect/platform/HttpApp";
|
|
7
7
|
import type { Effect } from "effect-app";
|
|
8
8
|
import type { NotLoggedInError } from "../errors.js";
|
|
9
|
+
export * from "./internal/auth.js";
|
|
9
10
|
export * from "./internal/events.js";
|
|
10
11
|
export * from "./internal/health.js";
|
|
11
12
|
export * from "./internal/RequestContextMiddleware.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middlewares.d.ts","sourceRoot":"","sources":["../../src/api/middlewares.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,KAAK,GAAG,MAAM,0BAA0B,CAAA;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAGpD,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,wCAAwC,CAAA;AAEtD;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,EAAE,CACtB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,KACjC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAsB,CAAA;AAE7E;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,EAAE,CAC9B,gBAAgB,CAAC,EAAE,MAAM,KACtB,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAA8B,CAAA;AAErF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAC3C,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KACnB,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAgC,CAAA;AAErD;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAqB,CAAA;AAE9F;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;;;GAKG;AACH,eAAO,MAAM,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,EAC5B,gBAAgB,EAAE,CAChB,WAAW,EAAE,oBAAoB,KAC9B,MAAM,CAAC,CAAC,EAAE,gBAAgB,EAAE,EAAE,CAAC,EACpC,OAAO,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;CAC7B,CAAC,KACC,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,CAAsB,CAAA;AAErF;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,OAAO,CAAA;CACrB;AAED;;;;;GAKG;AACH,eAAO,MAAM,IAAI,EAAE,CACjB,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,KAC3B,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAiB,CAAA"}
|
|
1
|
+
{"version":3,"file":"middlewares.d.ts","sourceRoot":"","sources":["../../src/api/middlewares.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,KAAK,GAAG,MAAM,0BAA0B,CAAA;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAGpD,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,wCAAwC,CAAA;AAEtD;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,EAAE,CACtB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,KACjC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAsB,CAAA;AAE7E;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,EAAE,CAC9B,gBAAgB,CAAC,EAAE,MAAM,KACtB,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAA8B,CAAA;AAErF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAC3C,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KACnB,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAgC,CAAA;AAErD;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAqB,CAAA;AAE9F;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;;;GAKG;AACH,eAAO,MAAM,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,EAC5B,gBAAgB,EAAE,CAChB,WAAW,EAAE,oBAAoB,KAC9B,MAAM,CAAC,CAAC,EAAE,gBAAgB,EAAE,EAAE,CAAC,EACpC,OAAO,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;CAC7B,CAAC,KACC,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,CAAsB,CAAA;AAErF;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,OAAO,CAAA;CACrB;AAED;;;;;GAKG;AACH,eAAO,MAAM,IAAI,EAAE,CACjB,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,KAC3B,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAiB,CAAA"}
|
package/dist/api/middlewares.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as internal from "./internal/middlewares.js";
|
|
2
|
+
export * from "./internal/auth.js";
|
|
2
3
|
export * from "./internal/events.js";
|
|
3
4
|
export * from "./internal/health.js";
|
|
4
5
|
export * from "./internal/RequestContextMiddleware.js";
|
|
@@ -51,4 +52,4 @@ export const basicAuth = internal.basicAuth;
|
|
|
51
52
|
* @since 1.0.0
|
|
52
53
|
*/
|
|
53
54
|
export const cors = internal.cors;
|
|
54
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
55
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpL21pZGRsZXdhcmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQVFBLE9BQU8sS0FBSyxRQUFRLE1BQU0sMkJBQTJCLENBQUE7QUFFckQsY0FBYyxvQkFBb0IsQ0FBQTtBQUNsQyxjQUFjLHNCQUFzQixDQUFBO0FBQ3BDLGNBQWMsc0JBQXNCLENBQUE7QUFDcEMsY0FBYyx3Q0FBd0MsQ0FBQTtBQUV0RDs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sU0FBUyxHQUVxQyxRQUFRLENBQUMsU0FBUyxDQUFBO0FBRTdFOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUU2QixRQUFRLENBQUMsaUJBQWlCLENBQUE7QUFFckY7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLEdBRVAsUUFBUSxDQUFDLG1CQUFtQixDQUFBO0FBRXJEOzs7OztHQUtHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sUUFBUSxHQUF3RCxRQUFRLENBQUMsUUFBUSxDQUFBO0FBVzlGOzs7OztHQUtHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sU0FBUyxHQVE2QyxRQUFRLENBQUMsU0FBUyxDQUFBO0FBZXJGOzs7OztHQUtHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sSUFBSSxHQUUwQyxRQUFRLENBQUMsSUFBSSxDQUFBIn0=
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RequestContextContainer } from "@effect-app/infra/services/RequestContextContainer";
|
|
2
|
+
import { SqlClient } from "@effect/sql";
|
|
3
|
+
import { Effect, S, Tracer } from "effect-app";
|
|
4
|
+
import { NonEmptyString255 } from "effect-app/schema";
|
|
5
|
+
export declare const QueueId: S.brand<typeof import("@effect/schema/Schema").Number & {
|
|
6
|
+
withDefault: S.PropertySignature<":", number, never, ":", number, true, never>;
|
|
7
|
+
}, "QueueId">;
|
|
8
|
+
export type QueueId = typeof QueueId.Type;
|
|
9
|
+
/**
|
|
10
|
+
* Currently limited to one process draining at a time, due to in-process Semaphore instead of row-level locking.
|
|
11
|
+
*/
|
|
12
|
+
export declare function makeSQLQueue<Evt extends {
|
|
13
|
+
id: S.StringId;
|
|
14
|
+
_tag: string;
|
|
15
|
+
}, DrainEvt extends {
|
|
16
|
+
id: S.StringId;
|
|
17
|
+
_tag: string;
|
|
18
|
+
}, EvtE, DrainEvtE>(queueName: NonEmptyString255, queueDrainName: NonEmptyString255, schema: S.Schema<Evt, EvtE>, drainSchema: S.Schema<DrainEvt, DrainEvtE>): Effect.Effect<{
|
|
19
|
+
publish: (messages_0: Evt, ...messages: Evt[]) => Effect.Effect<void, never, never>;
|
|
20
|
+
drain: <DrainE, DrainR>(handleEvent: (ks: DrainEvt) => Effect<void, DrainE, DrainR>, sessionId?: string) => Effect.Effect<never, never, RequestContextContainer | import("../Store/ContextMapContainer.js").ContextMapContainer | Exclude<Exclude<Exclude<DrainR, Tracer.ParentSpan>, never>, Tracer.ParentSpan>>;
|
|
21
|
+
}, never, RequestContextContainer | SqlClient.SqlClient>;
|
|
22
|
+
//# sourceMappingURL=SQLQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SQLQueue.d.ts","sourceRoot":"","sources":["../../../src/services/QueueMaker/SQLQueue.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,uBAAuB,EAAE,MAAM,oDAAoD,CAAA;AAC5F,OAAO,EAAS,SAAS,EAAE,MAAM,aAAa,CAAA;AAE9C,OAAO,EAAE,MAAM,EAAiB,CAAC,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAE7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAGrD,eAAO,MAAM,OAAO;;aAAoC,CAAA;AACxD,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAEzC;;GAEG;AACH,wBAAgB,YAAY,CAC1B,GAAG,SAAS;IAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAC5C,QAAQ,SAAS;IAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACjD,IAAI,EACJ,SAAS,EAET,SAAS,EAAE,iBAAiB,EAC5B,cAAc,EAAE,iBAAiB,EACjC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,EAC3B,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC;;YAgH9B,MAAM,EAAE,MAAM,eACP,CAAC,EAAE,EAAE,QAAQ,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,cAC/C,MAAM;yDA0DzB"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { setupRequestContext } from "@effect-app/infra/api/setupRequest";
|
|
2
|
+
import { RequestContext } from "@effect-app/infra/RequestContext";
|
|
3
|
+
import { reportNonInterruptedFailure } from "@effect-app/infra/services/QueueMaker/errors";
|
|
4
|
+
import { QueueMeta } from "@effect-app/infra/services/QueueMaker/service";
|
|
5
|
+
import { RequestContextContainer } from "@effect-app/infra/services/RequestContextContainer";
|
|
6
|
+
import { Model, SqlClient } from "@effect/sql";
|
|
7
|
+
import { subMinutes } from "date-fns";
|
|
8
|
+
import { Effect, Fiber, Option, S, Tracer } from "effect-app";
|
|
9
|
+
import { RequestId } from "effect-app/ids";
|
|
10
|
+
import { NonEmptyString255 } from "effect-app/schema";
|
|
11
|
+
import { pretty } from "effect-app/utils";
|
|
12
|
+
export const QueueId = S.Number.pipe(S.brand("QueueId"));
|
|
13
|
+
/**
|
|
14
|
+
* Currently limited to one process draining at a time, due to in-process Semaphore instead of row-level locking.
|
|
15
|
+
*/
|
|
16
|
+
export function makeSQLQueue(queueName, queueDrainName, schema, drainSchema) {
|
|
17
|
+
return Effect.gen(function* () {
|
|
18
|
+
const base = {
|
|
19
|
+
id: Model.Generated(QueueId),
|
|
20
|
+
meta: Model.JsonFromString(QueueMeta),
|
|
21
|
+
name: S.NonEmptyString255,
|
|
22
|
+
createdAt: Model.DateTimeInsert,
|
|
23
|
+
updatedAt: Model.DateTimeUpdate,
|
|
24
|
+
// TODO: at+owner
|
|
25
|
+
processingAt: Model.FieldOption(S.Date),
|
|
26
|
+
finishedAt: Model.FieldOption(S.Date)
|
|
27
|
+
// TODO: record locking.. / optimistic locking
|
|
28
|
+
// rowVersion: Model.DateTimeFromNumberWithNow
|
|
29
|
+
};
|
|
30
|
+
class Queue extends Model.Class("Queue")({
|
|
31
|
+
body: Model.JsonFromString(schema),
|
|
32
|
+
...base
|
|
33
|
+
}) {
|
|
34
|
+
}
|
|
35
|
+
class Drain extends Model.Class("Drain")({
|
|
36
|
+
body: Model.JsonFromString(drainSchema),
|
|
37
|
+
...base
|
|
38
|
+
}) {
|
|
39
|
+
}
|
|
40
|
+
const sql = yield* SqlClient.SqlClient;
|
|
41
|
+
const queueRepo = yield* Model.makeRepository(Queue, {
|
|
42
|
+
tableName: "queue",
|
|
43
|
+
spanPrefix: "QueueRepo",
|
|
44
|
+
idColumn: "id"
|
|
45
|
+
});
|
|
46
|
+
const drainRepo = yield* Model.makeRepository(Drain, {
|
|
47
|
+
tableName: "queue",
|
|
48
|
+
spanPrefix: "DrainRepo",
|
|
49
|
+
idColumn: "id"
|
|
50
|
+
});
|
|
51
|
+
const decodeDrain = S.decode(Drain);
|
|
52
|
+
const drain = () => {
|
|
53
|
+
const limit = subMinutes(new Date(), 15);
|
|
54
|
+
return sql `SELECT *
|
|
55
|
+
FROM queue
|
|
56
|
+
WHERE name = ${queueDrainName} AND finishedAt IS NULL AND (processingAt IS NULL OR processingAt < ${limit.getTime()})
|
|
57
|
+
LIMIT 1`;
|
|
58
|
+
};
|
|
59
|
+
// temporary workaround until we have a SQLite rowversion..
|
|
60
|
+
const lock = yield* Effect.makeSemaphore(1);
|
|
61
|
+
const q = {
|
|
62
|
+
offer: (body, meta) => Effect.gen(function* () {
|
|
63
|
+
yield* queueRepo.insert(Queue.insert.make({
|
|
64
|
+
body,
|
|
65
|
+
meta,
|
|
66
|
+
name: queueName,
|
|
67
|
+
processingAt: Option.none(),
|
|
68
|
+
finishedAt: Option.none()
|
|
69
|
+
}));
|
|
70
|
+
}),
|
|
71
|
+
take: Effect.gen(function* () {
|
|
72
|
+
while (true) {
|
|
73
|
+
const first = yield* lock.withPermits(1)(Effect.gen(function* () {
|
|
74
|
+
const [first] = yield* drain();
|
|
75
|
+
if (first) {
|
|
76
|
+
const dec = yield* decodeDrain(first);
|
|
77
|
+
const { createdAt, updatedAt, ...rest } = dec;
|
|
78
|
+
yield* drainRepo.update(Drain.update.make({ ...rest, processingAt: Option.some(new Date()) }));
|
|
79
|
+
return dec;
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}));
|
|
83
|
+
if (first)
|
|
84
|
+
return first;
|
|
85
|
+
yield* Effect.sleep(250);
|
|
86
|
+
}
|
|
87
|
+
}),
|
|
88
|
+
finish: ({ createdAt, updatedAt, ...q }) => drainRepo.update(Drain.update.make({ ...q, finishedAt: Option.some(new Date()) }))
|
|
89
|
+
};
|
|
90
|
+
const rcc = yield* RequestContextContainer;
|
|
91
|
+
return {
|
|
92
|
+
publish: (...messages) => Effect
|
|
93
|
+
.gen(function* ($) {
|
|
94
|
+
const requestContext = yield* $(rcc.requestContext);
|
|
95
|
+
const span = yield* $(Effect.serviceOption(Tracer.ParentSpan));
|
|
96
|
+
return yield* $(Effect
|
|
97
|
+
.forEach(messages, (m) => q.offer(m, {
|
|
98
|
+
requestContext: new RequestContext(requestContext), // workaround Schema expecting exact class
|
|
99
|
+
span: Option.getOrUndefined(span)
|
|
100
|
+
}), {
|
|
101
|
+
discard: true
|
|
102
|
+
}));
|
|
103
|
+
})
|
|
104
|
+
.pipe(Effect.withSpan("queue.publish: " + queueName, {
|
|
105
|
+
captureStackTrace: false,
|
|
106
|
+
kind: "producer",
|
|
107
|
+
attributes: { "message_tags": messages.map((_) => _._tag) }
|
|
108
|
+
})),
|
|
109
|
+
drain: (handleEvent, sessionId) => Effect.gen(function* () {
|
|
110
|
+
const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName });
|
|
111
|
+
const processMessage = (msg) => Effect
|
|
112
|
+
.succeed(msg)
|
|
113
|
+
.pipe(Effect
|
|
114
|
+
.flatMap(({ body, meta }) => {
|
|
115
|
+
let effect = Effect
|
|
116
|
+
.logDebug(`[${queueDrainName}] Processing incoming message`)
|
|
117
|
+
.pipe(Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }), Effect.zipRight(handleEvent(body)), silenceAndReportError, (_) => setupRequestContext(_, RequestContext.inherit(meta.requestContext, {
|
|
118
|
+
id: RequestId(body.id),
|
|
119
|
+
locale: "en",
|
|
120
|
+
name: NonEmptyString255(`${queueDrainName}.${body._tag}`)
|
|
121
|
+
})), Effect
|
|
122
|
+
.withSpan(`queue.drain: ${queueDrainName}.${body._tag}`, {
|
|
123
|
+
captureStackTrace: false,
|
|
124
|
+
kind: "consumer",
|
|
125
|
+
attributes: {
|
|
126
|
+
"queue.name": queueDrainName,
|
|
127
|
+
"queue.sessionId": sessionId,
|
|
128
|
+
"queue.input": body
|
|
129
|
+
}
|
|
130
|
+
}));
|
|
131
|
+
if (meta.span) {
|
|
132
|
+
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span));
|
|
133
|
+
}
|
|
134
|
+
return effect;
|
|
135
|
+
}));
|
|
136
|
+
return yield* q
|
|
137
|
+
.take
|
|
138
|
+
.pipe(Effect.flatMap((x) => processMessage(x).pipe(Effect.uninterruptible, Effect.fork, Effect.flatMap(Fiber.join), Effect.tap(q.finish(x)))), silenceAndReportError, Effect.forever);
|
|
139
|
+
})
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU1FMUXVldWUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc2VydmljZXMvUXVldWVNYWtlci9TUUxRdWV1ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQTtBQUN4RSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sa0NBQWtDLENBQUE7QUFDakUsT0FBTyxFQUFFLDJCQUEyQixFQUFFLE1BQU0sOENBQThDLENBQUE7QUFFMUYsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLCtDQUErQyxDQUFBO0FBQ3pFLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLG9EQUFvRCxDQUFBO0FBQzVGLE9BQU8sRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQzlDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxVQUFVLENBQUE7QUFDckMsT0FBTyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxNQUFNLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFDN0QsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFBO0FBQzFDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLG1CQUFtQixDQUFBO0FBQ3JELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQTtBQUV6QyxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFBO0FBR3hEOztHQUVHO0FBQ0gsTUFBTSxVQUFVLFlBQVksQ0FNMUIsU0FBNEIsRUFDNUIsY0FBaUMsRUFDakMsTUFBMkIsRUFDM0IsV0FBMEM7SUFFMUMsT0FBTyxNQUFNLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQztRQUN6QixNQUFNLElBQUksR0FBRztZ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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-app/infra",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"pure-rand": "6.1.0",
|
|
20
20
|
"redlock": "^4.2.0",
|
|
21
21
|
"@effect-app/core": "1.10.1",
|
|
22
|
-
"@effect-app/infra-adapters": "1.11.3",
|
|
23
22
|
"effect-app": "1.17.1",
|
|
23
|
+
"@effect-app/infra-adapters": "1.11.3",
|
|
24
24
|
"@effect-app/schema": "1.12.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"express": "^4.21.0",
|
|
45
45
|
"@effect/platform": "^0.66.2",
|
|
46
46
|
"@effect/schema": "^0.74.1",
|
|
47
|
+
"@effect/sql": "^0.14.0",
|
|
47
48
|
"@effect/vitest": "^0.10.5",
|
|
48
49
|
"effect": "^3.8.4"
|
|
49
50
|
},
|
|
@@ -415,6 +416,16 @@
|
|
|
415
416
|
"default": "./_cjs/services/OperationsRepo.cjs"
|
|
416
417
|
}
|
|
417
418
|
},
|
|
419
|
+
"./services/QueueMaker/SQLQueue": {
|
|
420
|
+
"import": {
|
|
421
|
+
"types": "./dist/services/QueueMaker/SQLQueue.d.ts",
|
|
422
|
+
"default": "./dist/services/QueueMaker/SQLQueue.js"
|
|
423
|
+
},
|
|
424
|
+
"require": {
|
|
425
|
+
"types": "./dist/services/QueueMaker/SQLQueue.d.ts",
|
|
426
|
+
"default": "./_cjs/services/QueueMaker/SQLQueue.cjs"
|
|
427
|
+
}
|
|
428
|
+
},
|
|
418
429
|
"./services/QueueMaker/errors": {
|
|
419
430
|
"import": {
|
|
420
431
|
"types": "./dist/services/QueueMaker/errors.d.ts",
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable unused-imports/no-unused-vars */
|
|
3
|
+
import { Effect } from "effect-app"
|
|
4
|
+
import { HttpHeaders, HttpMiddleware, HttpServerRequest, HttpServerResponse } from "effect-app/http"
|
|
5
|
+
import {
|
|
6
|
+
auth,
|
|
7
|
+
InsufficientScopeError,
|
|
8
|
+
InvalidRequestError,
|
|
9
|
+
InvalidTokenError,
|
|
10
|
+
UnauthorizedError
|
|
11
|
+
} from "express-oauth2-jwt-bearer"
|
|
12
|
+
|
|
13
|
+
// // Authorization middleware. When used, the Access Token must
|
|
14
|
+
// // exist and be verified against the Auth0 JSON Web Key Set.
|
|
15
|
+
|
|
16
|
+
// type Errors = InsufficientScopeError | InvalidRequestError | InvalidTokenError | UnauthorizedError
|
|
17
|
+
type Config = Parameters<typeof auth>[0]
|
|
18
|
+
export const checkJWTI = (config: Config) => {
|
|
19
|
+
const mw = auth(config)
|
|
20
|
+
return Effect.gen(function*() {
|
|
21
|
+
const req = yield* HttpServerRequest.HttpServerRequest
|
|
22
|
+
|
|
23
|
+
return yield* Effect.async<
|
|
24
|
+
void,
|
|
25
|
+
InsufficientScopeError | InvalidRequestError | InvalidTokenError | UnauthorizedError
|
|
26
|
+
>(
|
|
27
|
+
(cb) => {
|
|
28
|
+
const next = (err?: unknown) => {
|
|
29
|
+
if (!err) return cb(Effect.void)
|
|
30
|
+
if (
|
|
31
|
+
err instanceof InsufficientScopeError
|
|
32
|
+
|| err instanceof InvalidRequestError
|
|
33
|
+
|| err instanceof InvalidTokenError
|
|
34
|
+
|| err instanceof UnauthorizedError
|
|
35
|
+
) {
|
|
36
|
+
return cb(Effect.fail(err))
|
|
37
|
+
}
|
|
38
|
+
return Effect.die(err)
|
|
39
|
+
}
|
|
40
|
+
const r = { headers: req.headers, query: {}, body: {}, is: () => false } // is("urlencoded")
|
|
41
|
+
try {
|
|
42
|
+
mw(r as any, {} as any, next)
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return cb(Effect.die(e))
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const checkJwt = (config: Config) => {
|
|
52
|
+
const check = checkJWTI(config)
|
|
53
|
+
return HttpMiddleware.make((app) =>
|
|
54
|
+
Effect.gen(function*() {
|
|
55
|
+
const response = yield* check.pipe(Effect.catchAll((e) =>
|
|
56
|
+
Effect.succeed(
|
|
57
|
+
HttpServerResponse.unsafeJson({ message: e.message }, {
|
|
58
|
+
status: e.status,
|
|
59
|
+
headers: HttpHeaders.fromInput(e.headers)
|
|
60
|
+
})
|
|
61
|
+
)
|
|
62
|
+
))
|
|
63
|
+
if (response) {
|
|
64
|
+
return response
|
|
65
|
+
}
|
|
66
|
+
return yield* app
|
|
67
|
+
})
|
|
68
|
+
)
|
|
69
|
+
}
|
package/src/api/middlewares.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { Effect } from "effect-app"
|
|
|
8
8
|
import type { NotLoggedInError } from "../errors.js"
|
|
9
9
|
import * as internal from "./internal/middlewares.js"
|
|
10
10
|
|
|
11
|
+
export * from "./internal/auth.js"
|
|
11
12
|
export * from "./internal/events.js"
|
|
12
13
|
export * from "./internal/health.js"
|
|
13
14
|
export * from "./internal/RequestContextMiddleware.js"
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { setupRequestContext } from "@effect-app/infra/api/setupRequest"
|
|
2
|
+
import { RequestContext } from "@effect-app/infra/RequestContext"
|
|
3
|
+
import { reportNonInterruptedFailure } from "@effect-app/infra/services/QueueMaker/errors"
|
|
4
|
+
import type { QueueBase } from "@effect-app/infra/services/QueueMaker/service"
|
|
5
|
+
import { QueueMeta } from "@effect-app/infra/services/QueueMaker/service"
|
|
6
|
+
import { RequestContextContainer } from "@effect-app/infra/services/RequestContextContainer"
|
|
7
|
+
import { Model, SqlClient } from "@effect/sql"
|
|
8
|
+
import { subMinutes } from "date-fns"
|
|
9
|
+
import { Effect, Fiber, Option, S, Tracer } from "effect-app"
|
|
10
|
+
import { RequestId } from "effect-app/ids"
|
|
11
|
+
import { NonEmptyString255 } from "effect-app/schema"
|
|
12
|
+
import { pretty } from "effect-app/utils"
|
|
13
|
+
|
|
14
|
+
export const QueueId = S.Number.pipe(S.brand("QueueId"))
|
|
15
|
+
export type QueueId = typeof QueueId.Type
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Currently limited to one process draining at a time, due to in-process Semaphore instead of row-level locking.
|
|
19
|
+
*/
|
|
20
|
+
export function makeSQLQueue<
|
|
21
|
+
Evt extends { id: S.StringId; _tag: string },
|
|
22
|
+
DrainEvt extends { id: S.StringId; _tag: string },
|
|
23
|
+
EvtE,
|
|
24
|
+
DrainEvtE
|
|
25
|
+
>(
|
|
26
|
+
queueName: NonEmptyString255,
|
|
27
|
+
queueDrainName: NonEmptyString255,
|
|
28
|
+
schema: S.Schema<Evt, EvtE>,
|
|
29
|
+
drainSchema: S.Schema<DrainEvt, DrainEvtE>
|
|
30
|
+
) {
|
|
31
|
+
return Effect.gen(function*() {
|
|
32
|
+
const base = {
|
|
33
|
+
id: Model.Generated(QueueId),
|
|
34
|
+
meta: Model.JsonFromString(QueueMeta),
|
|
35
|
+
name: S.NonEmptyString255,
|
|
36
|
+
createdAt: Model.DateTimeInsert,
|
|
37
|
+
updatedAt: Model.DateTimeUpdate,
|
|
38
|
+
// TODO: at+owner
|
|
39
|
+
processingAt: Model.FieldOption(S.Date),
|
|
40
|
+
finishedAt: Model.FieldOption(S.Date)
|
|
41
|
+
// TODO: record locking.. / optimistic locking
|
|
42
|
+
// rowVersion: Model.DateTimeFromNumberWithNow
|
|
43
|
+
}
|
|
44
|
+
class Queue extends Model.Class<Queue>("Queue")({
|
|
45
|
+
body: Model.JsonFromString(schema),
|
|
46
|
+
...base
|
|
47
|
+
}) {}
|
|
48
|
+
class Drain extends Model.Class<Drain>("Drain")({
|
|
49
|
+
body: Model.JsonFromString(drainSchema),
|
|
50
|
+
...base
|
|
51
|
+
}) {}
|
|
52
|
+
const sql = yield* SqlClient.SqlClient
|
|
53
|
+
|
|
54
|
+
const queueRepo = yield* Model.makeRepository(Queue, {
|
|
55
|
+
tableName: "queue",
|
|
56
|
+
spanPrefix: "QueueRepo",
|
|
57
|
+
idColumn: "id"
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const drainRepo = yield* Model.makeRepository(Drain, {
|
|
61
|
+
tableName: "queue",
|
|
62
|
+
spanPrefix: "DrainRepo",
|
|
63
|
+
idColumn: "id"
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const decodeDrain = S.decode(Drain)
|
|
67
|
+
|
|
68
|
+
const drain = () => {
|
|
69
|
+
const limit = subMinutes(new Date(), 15)
|
|
70
|
+
return sql<typeof Drain.Encoded>`SELECT *
|
|
71
|
+
FROM queue
|
|
72
|
+
WHERE name = ${queueDrainName} AND finishedAt IS NULL AND (processingAt IS NULL OR processingAt < ${limit.getTime()})
|
|
73
|
+
LIMIT 1`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// temporary workaround until we have a SQLite rowversion..
|
|
77
|
+
const lock = yield* Effect.makeSemaphore(1)
|
|
78
|
+
|
|
79
|
+
const q = {
|
|
80
|
+
offer: (body: Evt, meta: typeof QueueMeta.Type) =>
|
|
81
|
+
Effect.gen(function*() {
|
|
82
|
+
yield* queueRepo.insert(
|
|
83
|
+
Queue.insert.make({
|
|
84
|
+
body,
|
|
85
|
+
meta,
|
|
86
|
+
name: queueName,
|
|
87
|
+
processingAt: Option.none(),
|
|
88
|
+
finishedAt: Option.none()
|
|
89
|
+
})
|
|
90
|
+
)
|
|
91
|
+
}),
|
|
92
|
+
take: Effect.gen(function*() {
|
|
93
|
+
while (true) {
|
|
94
|
+
const first = yield* lock.withPermits(1)(Effect.gen(function*() {
|
|
95
|
+
const [first] = yield* drain()
|
|
96
|
+
if (first) {
|
|
97
|
+
const dec = yield* decodeDrain(first)
|
|
98
|
+
const { createdAt, updatedAt, ...rest } = dec
|
|
99
|
+
yield* drainRepo.update(Drain.update.make({ ...rest, processingAt: Option.some(new Date()) }))
|
|
100
|
+
return dec
|
|
101
|
+
}
|
|
102
|
+
return null
|
|
103
|
+
}))
|
|
104
|
+
if (first) return first
|
|
105
|
+
yield* Effect.sleep(250)
|
|
106
|
+
}
|
|
107
|
+
}),
|
|
108
|
+
finish: ({ createdAt, updatedAt, ...q }: Drain) =>
|
|
109
|
+
drainRepo.update(Drain.update.make({ ...q, finishedAt: Option.some(new Date()) }))
|
|
110
|
+
}
|
|
111
|
+
const rcc = yield* RequestContextContainer
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
publish: (...messages) =>
|
|
115
|
+
Effect
|
|
116
|
+
.gen(function*($) {
|
|
117
|
+
const requestContext = yield* $(rcc.requestContext)
|
|
118
|
+
const span = yield* $(Effect.serviceOption(Tracer.ParentSpan))
|
|
119
|
+
return yield* $(
|
|
120
|
+
Effect
|
|
121
|
+
.forEach(
|
|
122
|
+
messages,
|
|
123
|
+
(m) =>
|
|
124
|
+
q.offer(m, {
|
|
125
|
+
requestContext: new RequestContext(requestContext), // workaround Schema expecting exact class
|
|
126
|
+
span: Option.getOrUndefined(span)
|
|
127
|
+
}),
|
|
128
|
+
{
|
|
129
|
+
discard: true
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
})
|
|
134
|
+
.pipe(
|
|
135
|
+
Effect.withSpan("queue.publish: " + queueName, {
|
|
136
|
+
captureStackTrace: false,
|
|
137
|
+
kind: "producer",
|
|
138
|
+
attributes: { "message_tags": messages.map((_) => _._tag) }
|
|
139
|
+
})
|
|
140
|
+
),
|
|
141
|
+
drain: <DrainE, DrainR>(
|
|
142
|
+
handleEvent: (ks: DrainEvt) => Effect<void, DrainE, DrainR>,
|
|
143
|
+
sessionId?: string
|
|
144
|
+
) =>
|
|
145
|
+
Effect.gen(function*() {
|
|
146
|
+
const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
|
|
147
|
+
const processMessage = (msg: Drain) =>
|
|
148
|
+
Effect
|
|
149
|
+
.succeed(msg)
|
|
150
|
+
.pipe(Effect
|
|
151
|
+
.flatMap(({ body, meta }) => {
|
|
152
|
+
let effect = Effect
|
|
153
|
+
.logDebug(`[${queueDrainName}] Processing incoming message`)
|
|
154
|
+
.pipe(
|
|
155
|
+
Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
|
|
156
|
+
Effect.zipRight(handleEvent(body)),
|
|
157
|
+
silenceAndReportError,
|
|
158
|
+
(_) =>
|
|
159
|
+
setupRequestContext(
|
|
160
|
+
_,
|
|
161
|
+
RequestContext.inherit(meta.requestContext, {
|
|
162
|
+
id: RequestId(body.id),
|
|
163
|
+
locale: "en" as const,
|
|
164
|
+
name: NonEmptyString255(`${queueDrainName}.${body._tag}`)
|
|
165
|
+
})
|
|
166
|
+
),
|
|
167
|
+
Effect
|
|
168
|
+
.withSpan(`queue.drain: ${queueDrainName}.${body._tag}`, {
|
|
169
|
+
captureStackTrace: false,
|
|
170
|
+
kind: "consumer",
|
|
171
|
+
attributes: {
|
|
172
|
+
"queue.name": queueDrainName,
|
|
173
|
+
"queue.sessionId": sessionId,
|
|
174
|
+
"queue.input": body
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
)
|
|
178
|
+
if (meta.span) {
|
|
179
|
+
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
180
|
+
}
|
|
181
|
+
return effect
|
|
182
|
+
}))
|
|
183
|
+
|
|
184
|
+
return yield* q
|
|
185
|
+
.take
|
|
186
|
+
.pipe(
|
|
187
|
+
Effect.flatMap((x) =>
|
|
188
|
+
processMessage(x).pipe(
|
|
189
|
+
Effect.uninterruptible,
|
|
190
|
+
Effect.fork,
|
|
191
|
+
Effect.flatMap(Fiber.join),
|
|
192
|
+
Effect.tap(q.finish(x))
|
|
193
|
+
)
|
|
194
|
+
),
|
|
195
|
+
silenceAndReportError,
|
|
196
|
+
Effect.forever
|
|
197
|
+
)
|
|
198
|
+
})
|
|
199
|
+
} satisfies QueueBase<Evt, DrainEvt>
|
|
200
|
+
})
|
|
201
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// packages/infra/vitest.config.ts
|
|
2
|
-
import { defineConfig } from "file:///Users/patrickroza/pj/effect-app/libs/node_modules/.pnpm/vite@5.2.6_@types+node@20.11.30/node_modules/vite/dist/node/index.js";
|
|
3
|
-
|
|
4
|
-
// vite.config.base.ts
|
|
5
|
-
import path from "path";
|
|
6
|
-
import fs from "fs";
|
|
7
|
-
var __vite_injected_original_dirname = "/Users/patrickroza/pj/effect-app/libs";
|
|
8
|
-
function makeConfig(dirName) {
|
|
9
|
-
const prefix = path.resolve(__vite_injected_original_dirname, "packages");
|
|
10
|
-
const packages = fs.readdirSync(prefix).map((f) => prefix + "/" + f).filter((f) => fs.lstatSync(f).isDirectory());
|
|
11
|
-
const cfg = {
|
|
12
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
13
|
-
//plugins: [autoImport],
|
|
14
|
-
test: {
|
|
15
|
-
include: ["./test/**/*.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
|
|
16
|
-
reporters: "verbose",
|
|
17
|
-
globals: true
|
|
18
|
-
},
|
|
19
|
-
resolve: {
|
|
20
|
-
alias: packages.reduce((acc, cur) => {
|
|
21
|
-
acc[JSON.parse(fs.readFileSync(cur + "/package.json", "utf-8")).name] = path.resolve(cur, cur.endsWith("core") ? "dist" : "src");
|
|
22
|
-
return acc;
|
|
23
|
-
}, {})
|
|
24
|
-
// "@effect-app/core/Prelude": path.join(__dirname, "packages/core/src/Prelude.code.ts")
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
console.log(cfg);
|
|
28
|
-
return cfg;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// packages/infra/vitest.config.ts
|
|
32
|
-
var __vite_injected_original_dirname2 = "/Users/patrickroza/pj/effect-app/libs/packages/infra";
|
|
33
|
-
var vitest_config_default = defineConfig(makeConfig(__vite_injected_original_dirname2));
|
|
34
|
-
export {
|
|
35
|
-
vitest_config_default as default
|
|
36
|
-
};
|
|
37
|
-
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsicGFja2FnZXMvaW5mcmEvdml0ZXN0LmNvbmZpZy50cyIsICJ2aXRlLmNvbmZpZy5iYXNlLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiL1VzZXJzL3BhdHJpY2tyb3phL3BqL2VmZmVjdC1hcHAvbGlicy9wYWNrYWdlcy9pbmZyYVwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL1VzZXJzL3BhdHJpY2tyb3phL3BqL2VmZmVjdC1hcHAvbGlicy9wYWNrYWdlcy9pbmZyYS92aXRlc3QuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9wYXRyaWNrcm96YS9wai9lZmZlY3QtYXBwL2xpYnMvcGFja2FnZXMvaW5mcmEvdml0ZXN0LmNvbmZpZy50c1wiOy8vLyA8cmVmZXJlbmNlIHR5cGVzPVwidml0ZXN0XCIgLz5cbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gXCJ2aXRlXCJcbmltcG9ydCBtYWtlQ29uZmlnIGZyb20gXCIuLi8uLi92aXRlLmNvbmZpZy5iYXNlXCJcblxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKG1ha2VDb25maWcoX19kaXJuYW1lKSlcbiIsICJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiL1VzZXJzL3BhdHJpY2tyb3phL3BqL2VmZmVjdC1hcHAvbGlic1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL1VzZXJzL3BhdHJpY2tyb3phL3BqL2VmZmVjdC1hcHAvbGlicy92aXRlLmNvbmZpZy5iYXNlLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9wYXRyaWNrcm96YS9wai9lZmZlY3QtYXBwL2xpYnMvdml0ZS5jb25maWcuYmFzZS50c1wiOy8vLyA8cmVmZXJlbmNlIHR5cGVzPVwidml0ZXN0XCIgLz5cbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCJcbmltcG9ydCBmcyBmcm9tIFwiZnNcIlxuaW1wb3J0IEF1dG9JbXBvcnQgZnJvbSBcInVucGx1Z2luLWF1dG8taW1wb3J0L3ZpdGVcIlxuaW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSBcInZpdGVzdC9jb25maWdcIlxuXG4vLyBjb25zdCBhdXRvSW1wb3J0ID0gQXV0b0ltcG9ydCh7XG4vLyAgIGR0czogXCIuL3Rlc3QvYXV0by1pbXBvcnRzLmQudHNcIixcbi8vICAgLy8gaW5jbHVkZTogW1xuLy8gICAvLyAgIC9cXC50ZXN0XFwuW3RqXXN4PyQvIC8vIC50cywgLnRzeCwgLmpzLCAuanN4XG4vLyAgIC8vIF0sXG4vLyAgIGltcG9ydHM6IFtcbi8vICAgICBcInZpdGVzdFwiXG4vLyAgIF1cbi8vIH0pXG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIG1ha2VDb25maWcoZGlyTmFtZT86IHN0cmluZykge1xuICBjb25zdCBwcmVmaXggPSBwYXRoLnJlc29sdmUoX19kaXJuYW1lLCBcInBhY2thZ2VzXCIpXG4gIGNvbnN0IHBhY2thZ2VzID0gZnMucmVhZGRpclN5bmMocHJlZml4KS5tYXAoZiA9PiBwcmVmaXggKyBcIi9cIiArIGYpLmZpbHRlcihmID0+IGZzLmxzdGF0U3luYyhmKS5pc0RpcmVjdG9yeSgpIClcbiAgY29uc3QgY2ZnID0ge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdmFyLXJlcXVpcmVzXG4gICAgLy9wbHVnaW5zOiBbYXV0b0ltcG9ydF0sXG4gICAgdGVzdDoge1xuICAgICAgaW5jbHVkZTogIFtcIi4vdGVzdC8qKi8qLnRlc3Que2pzLG1qcyxjanMsdHMsbXRzLGN0cyxqc3gsdHN4fVwiXSxcbiAgICAgIHJlcG9ydGVyczogXCJ2ZXJib3NlXCIsXG4gICAgICBnbG9iYWxzOiB0cnVlXG4gICAgfSxcbiAgICByZXNvbHZlOiB7XG4gICAgICBhbGlhczogcGFja2FnZXMucmVkdWNlKChhY2MsIGN1cikgPT4geyAvLyB3b3JrYXJvdW5kIGZvciAvUHJlbHVkZSBpc3N1ZVxuICAgICAgYWNjW0pTT04ucGFyc2UoZnMucmVhZEZpbGVTeW5jKGN1ciArIFwiL3BhY2thZ2UuanNvblwiLCBcInV0Zi04XCIpKS5uYW1lXSA9IHBhdGgucmVzb2x2ZShjdXIsIGN1ci5lbmRzV2l0aChcImNvcmVcIikgPyBcImRpc3RcIiA6IFwic3JjXCIpXG4gICAgICByZXR1cm4gYWNjXG4gICAgfSwgeyB9KSAvLyBcIkBlZmZlY3QtYXBwL2NvcmUvUHJlbHVkZVwiOiBwYXRoLmpvaW4oX19kaXJuYW1lLCBcInBhY2thZ2VzL2NvcmUvc3JjL1ByZWx1ZGUuY29kZS50c1wiKVxuICB9XG4gIH1cbiAgY29uc29sZS5sb2coY2ZnKVxuICByZXR1cm4gY2ZnXG59XG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQ0EsU0FBUyxvQkFBb0I7OztBQ0E3QixPQUFPLFVBQVU7QUFDakIsT0FBTyxRQUFRO0FBRmYsSUFBTSxtQ0FBbUM7QUFnQjFCLFNBQVIsV0FBNEIsU0FBa0I7QUFDbkQsUUFBTSxTQUFTLEtBQUssUUFBUSxrQ0FBVyxVQUFVO0FBQ2pELFFBQU0sV0FBVyxHQUFHLFlBQVksTUFBTSxFQUFFLElBQUksT0FBSyxTQUFTLE1BQU0sQ0FBQyxFQUFFLE9BQU8sT0FBSyxHQUFHLFVBQVUsQ0FBQyxFQUFFLFlBQVksQ0FBRTtBQUM3RyxRQUFNLE1BQU07QUFBQTtBQUFBO0FBQUEsSUFHVixNQUFNO0FBQUEsTUFDSixTQUFVLENBQUMsa0RBQWtEO0FBQUEsTUFDN0QsV0FBVztBQUFBLE1BQ1gsU0FBUztBQUFBLElBQ1g7QUFBQSxJQUNBLFNBQVM7QUFBQSxNQUNQLE9BQU8sU0FBUyxPQUFPLENBQUMsS0FBSyxRQUFRO0FBQ3JDLFlBQUksS0FBSyxNQUFNLEdBQUcsYUFBYSxNQUFNLGlCQUFpQixPQUFPLENBQUMsRUFBRSxJQUFJLElBQUksS0FBSyxRQUFRLEtBQUssSUFBSSxTQUFTLE1BQU0sSUFBSSxTQUFTLEtBQUs7QUFDL0gsZUFBTztBQUFBLE1BQ1QsR0FBRyxDQUFFLENBQUM7QUFBQTtBQUFBLElBQ1I7QUFBQSxFQUNBO0FBQ0EsVUFBUSxJQUFJLEdBQUc7QUFDZixTQUFPO0FBQ1Q7OztBRHBDQSxJQUFNQSxvQ0FBbUM7QUFJekMsSUFBTyx3QkFBUSxhQUFhLFdBQVdDLGlDQUFTLENBQUM7IiwKICAibmFtZXMiOiBbIl9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lIiwgIl9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lIl0KfQo=
|