@effect-app/infra 3.5.3 → 3.5.4
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 +6 -0
- package/dist/Model/Repository/ext.d.ts +3 -3
- package/dist/Model/Repository/internal/internal.d.ts +2 -2
- package/dist/Model/query/new-kid-interpreter.d.ts +2 -2
- package/dist/Operations.d.ts +3 -3
- package/dist/QueueMaker/SQLQueue.d.ts +1 -1
- package/dist/QueueMaker/memQueue.d.ts +1 -1
- package/dist/QueueMaker/sbqueue.d.ts +2 -2
- package/dist/Store/ContextMapContainer.d.ts +1 -1
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +7 -4
- package/dist/Store/service.d.ts +1 -1
- package/dist/api/internal/RequestContextMiddleware.d.ts +1 -1
- package/dist/api/setupRequest.d.ts +2 -2
- package/dist/fileUtil.d.ts +10 -0
- package/dist/fileUtil.d.ts.map +1 -1
- package/dist/fileUtil.js +35 -1
- package/package.json +1 -1
- package/src/Store/Disk.ts +16 -10
- package/src/fileUtil.ts +44 -0
- package/test/dist/fixtures.d.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,8 +8,8 @@ export declare const extendRepo: <T, Encoded extends FieldValues, Evt, ItemType
|
|
|
8
8
|
request: (id: T[IdKey]) => Effect.Effect<T, NotFoundError<ItemType>, never>;
|
|
9
9
|
get: (id: T[IdKey]) => Effect.Effect<T, NotFoundError<ItemType>, RSchema>;
|
|
10
10
|
log: (evt: Evt) => import("effect-app/Pure").PureLogT<any>;
|
|
11
|
-
save: (items_0: T, ...items: T[]) => Effect.Effect<void,
|
|
12
|
-
saveWithEvents: (events: Iterable<Evt>) => (...items: NonEmptyArray<T>) => Effect.Effect<void,
|
|
11
|
+
save: (items_0: T, ...items: T[]) => Effect.Effect<void, OptimisticConcurrencyException | InvalidStateError, RSchema | RPublish>;
|
|
12
|
+
saveWithEvents: (events: Iterable<Evt>) => (...items: NonEmptyArray<T>) => Effect.Effect<void, OptimisticConcurrencyException | InvalidStateError, RSchema | RPublish>;
|
|
13
13
|
queryAndSavePure: {
|
|
14
14
|
<A, E2, R2, T2 extends T>(q: (q: Query<Encoded>) => QueryEnd<Encoded, "one">, pure: Effect.Effect<A, E2, FixEnv<R2, Evt, T, T2>>): Effect.Effect<A, InvalidStateError | OptimisticConcurrencyException | NotFoundError<ItemType> | E2, Exclude<R2, {
|
|
15
15
|
env: PureEnv<Evt, T, T2>;
|
|
@@ -32,7 +32,7 @@ export declare const extendRepo: <T, Encoded extends FieldValues, Evt, ItemType
|
|
|
32
32
|
byIdAndSaveWithPure: <R, A_2, E_2, S2_2 extends T>(id: T[IdKey], pure: Effect.Effect<A_2, E_2, FixEnv<R, Evt, T, S2_2>>) => Effect.Effect<A_2, InvalidStateError | OptimisticConcurrencyException | NotFoundError<ItemType> | E_2, RSchema | RPublish | Exclude<R, {
|
|
33
33
|
env: PureEnv<Evt, T, S2_2>;
|
|
34
34
|
}>>;
|
|
35
|
-
saveWithPure: <R, A_3, E_3, S1_2 extends T, S2_3 extends T>(item: S1_2, pure: Effect.Effect<A_3, E_3, FixEnv<R, Evt, S1_2, S2_3>>) => Effect.Effect<A_3,
|
|
35
|
+
saveWithPure: <R, A_3, E_3, S1_2 extends T, S2_3 extends T>(item: S1_2, pure: Effect.Effect<A_3, E_3, FixEnv<R, Evt, S1_2, S2_3>>) => Effect.Effect<A_3, OptimisticConcurrencyException | InvalidStateError | E_3, RSchema | RPublish | Exclude<R, {
|
|
36
36
|
env: PureEnv<Evt, S1_2, S2_3>;
|
|
37
37
|
}>>;
|
|
38
38
|
};
|
|
@@ -20,12 +20,12 @@ export declare function makeRepoInternal<Evt = never>(): <ItemType extends strin
|
|
|
20
20
|
config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
|
|
21
21
|
partitionValue?: (e?: Encoded) => string;
|
|
22
22
|
};
|
|
23
|
-
}) => Effect.Effect<Repository<T, Encoded, Evt, ItemType, IdKey, Exclude<R, RCtx>, RPublish>, E,
|
|
23
|
+
}) => Effect.Effect<Repository<T, Encoded, Evt, ItemType, IdKey, Exclude<R, RCtx>, RPublish>, E, StoreMaker | R | RInitial>;
|
|
24
24
|
Q: Q.Query<Encoded>;
|
|
25
25
|
};
|
|
26
26
|
export declare function makeStore<Encoded extends FieldValues>(): <ItemType extends string, R, E, T, IdKey extends keyof Encoded>(name: ItemType, schema: S.Schema<T, E, R>, mapTo: (e: E, etag: string | undefined) => Encoded, idKey: IdKey) => <RInitial = never, EInitial = never>(makeInitial?: Effect.Effect<readonly T[], EInitial, RInitial>, config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
|
|
27
27
|
partitionValue?: (e?: Encoded) => string;
|
|
28
|
-
}) => Effect.Effect<import("../../../Store.js").Store<IdKey, Encoded, PersistenceModelType<Encoded>>, EInitial,
|
|
28
|
+
}) => Effect.Effect<import("../../../Store.js").Store<IdKey, Encoded, PersistenceModelType<Encoded>>, EInitial, StoreMaker | R | RInitial>;
|
|
29
29
|
export interface Repos<T, Encoded extends {
|
|
30
30
|
id: string;
|
|
31
31
|
}, RSchema, Evt, ItemType extends string, IdKey extends keyof T, RPublish> {
|
|
@@ -21,8 +21,8 @@ export declare const toFilter: <TFieldValues extends FieldValues, A, R, TFieldVa
|
|
|
21
21
|
key: import("../filter/types/path/eager.js").Path<TFieldValues>;
|
|
22
22
|
direction: "ASC" | "DESC";
|
|
23
23
|
}[]];
|
|
24
|
-
ttype: "
|
|
25
|
-
mode: "
|
|
24
|
+
ttype: "one" | "many" | "count";
|
|
25
|
+
mode: "project" | "collect" | "transform";
|
|
26
26
|
filter: FilterResult[];
|
|
27
27
|
};
|
|
28
28
|
//# sourceMappingURL=new-kid-interpreter.d.ts.map
|
package/dist/Operations.d.ts
CHANGED
|
@@ -54,15 +54,15 @@ declare const Operations_base: (abstract new (service: {
|
|
|
54
54
|
update: (id: OperationId, progress: OperationProgress) => Effect.Effect<void, never, never>;
|
|
55
55
|
}>) & {
|
|
56
56
|
toLayer: {
|
|
57
|
-
(): Layer.Layer<Operations, never,
|
|
57
|
+
(): Layer.Layer<Operations, never, RequestFiberSet | OperationsRepo>;
|
|
58
58
|
<E_1, R_1>(eff: Effect.Effect<Omit<Operations, keyof Context.TagClassShape<any, any>>, E_1, R_1>): Layer.Layer<Operations, E_1, R_1>;
|
|
59
59
|
};
|
|
60
60
|
toLayerScoped: {
|
|
61
|
-
(): Layer.Layer<Operations, never,
|
|
61
|
+
(): Layer.Layer<Operations, never, RequestFiberSet | OperationsRepo>;
|
|
62
62
|
<E_1, R_2>(eff: Effect.Effect<Omit<Operations, keyof Context.TagClassShape<any, any>>, E_1, R_2>): Layer.Layer<Operations, E_1, Exclude<R_2, Scope.Scope>>;
|
|
63
63
|
};
|
|
64
64
|
of: (service: Context.TagClassShape<any, any>) => Operations;
|
|
65
|
-
make: Effect.Effect<Operations, never,
|
|
65
|
+
make: Effect.Effect<Operations, never, RequestFiberSet | OperationsRepo>;
|
|
66
66
|
} & Context.Tag<Operations, Operations> & {
|
|
67
67
|
cleanup: Effect.Effect<void[], never, Operations>;
|
|
68
68
|
register: (title: S.NonEmptyString2k) => Effect.Effect<S.StringId, never, Scope.Scope | Operations>;
|
|
@@ -13,6 +13,6 @@ export declare function makeSQLQueue<Evt extends {
|
|
|
13
13
|
_tag: string;
|
|
14
14
|
}, EvtE, DrainEvtE>(queueName: NonEmptyString255, queueDrainName: NonEmptyString255, schema: S.Schema<Evt, EvtE>, drainSchema: S.Schema<DrainEvt, DrainEvtE>): Effect.Effect<{
|
|
15
15
|
publish: (messages_0: Evt, ...messages: Evt[]) => Effect.Effect<void, never, never>;
|
|
16
|
-
drain: <DrainE, DrainR>(handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>, sessionId?: string) => Effect.Effect<never, never, Exclude<Exclude<Exclude<DrainR, Tracer.ParentSpan>, import("../
|
|
16
|
+
drain: <DrainE, DrainR>(handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>, sessionId?: string) => Effect.Effect<never, never, Exclude<Exclude<Exclude<DrainR, Tracer.ParentSpan>, import("../RequestContext.js").LocaleRef | import("../Store/ContextMapContainer.js").ContextMapContainer | import("../Store/Memory.js").storeId>, Tracer.ParentSpan>>;
|
|
17
17
|
}, never, SqlClient.SqlClient>;
|
|
18
18
|
//# sourceMappingURL=SQLQueue.d.ts.map
|
|
@@ -9,6 +9,6 @@ export declare function makeMemQueue<Evt extends {
|
|
|
9
9
|
_tag: string;
|
|
10
10
|
}, EvtE, DrainEvtE>(queueName: string, queueDrainName: string, schema: S.Schema<Evt, EvtE>, drainSchema: S.Schema<DrainEvt, DrainEvtE>): Effect.Effect<{
|
|
11
11
|
publish: (messages_0: Evt, ...messages: Evt[]) => Effect.Effect<void, never, never>;
|
|
12
|
-
drain: <DrainE, DrainR>(handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>, sessionId?: string) => Effect.Effect<never, never, Exclude<Exclude<Exclude<DrainR, Tracer.ParentSpan>, import("../
|
|
12
|
+
drain: <DrainE, DrainR>(handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>, sessionId?: string) => Effect.Effect<never, never, Exclude<Exclude<Exclude<DrainR, Tracer.ParentSpan>, import("../RequestContext.js").LocaleRef | import("../Store/ContextMapContainer.js").ContextMapContainer | import("../Store/Memory.js").storeId>, Tracer.ParentSpan>>;
|
|
13
13
|
}, never, MemQueue>;
|
|
14
14
|
//# sourceMappingURL=memQueue.d.ts.map
|
|
@@ -9,7 +9,7 @@ export declare function makeServiceBusQueue<Evt extends {
|
|
|
9
9
|
id: StringId;
|
|
10
10
|
_tag: string;
|
|
11
11
|
}, EvtE, DrainEvtE>(schema: S.Schema<Evt, EvtE>, drainSchema: S.Schema<DrainEvt, DrainEvtE>): Effect.Effect<{
|
|
12
|
-
drain: <DrainE, DrainR>(handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>, sessionId?: string) => Effect.Effect<never, never, import("effect/Scope").Scope | Exclude<Exclude<DrainR, Tracer.ParentSpan>, import("../
|
|
12
|
+
drain: <DrainE, DrainR>(handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>, sessionId?: string) => Effect.Effect<never, never, import("effect/Scope").Scope | Exclude<Exclude<DrainR, Tracer.ParentSpan>, import("../RequestContext.js").LocaleRef | import("../Store/ContextMapContainer.js").ContextMapContainer | import("../Store/Memory.js").storeId>>;
|
|
13
13
|
publish: (messages_0: Evt, ...messages: Evt[]) => Effect.Effect<void, never, never>;
|
|
14
|
-
}, never,
|
|
14
|
+
}, never, Sender | Receiver>;
|
|
15
15
|
//# sourceMappingURL=sbqueue.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Context, Effect, Layer } from "effect-app";
|
|
2
2
|
import { ContextMap } from "./service.js";
|
|
3
|
-
declare const ContextMapContainer_base: Context.ReferenceClass<ContextMapContainer, "ContextMapContainer",
|
|
3
|
+
declare const ContextMapContainer_base: Context.ReferenceClass<ContextMapContainer, "ContextMapContainer", "root" | ContextMap>;
|
|
4
4
|
export declare class ContextMapContainer extends ContextMapContainer_base {
|
|
5
5
|
static readonly layer: Layer.Layer<ContextMapContainer, never, never>;
|
|
6
6
|
}
|
package/dist/Store/Disk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Disk.d.ts","sourceRoot":"","sources":["../../src/Store/Disk.ts"],"names":[],"mappings":"AAKA,OAAO,EAAW,MAAM,EAAQ,MAAM,YAAY,CAAA;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAE3D,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,aAAa,EAAE,KAAK,KAAK,EAAE,KAAK,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"Disk.d.ts","sourceRoot":"","sources":["../../src/Store/Disk.ts"],"names":[],"mappings":"AAKA,OAAO,EAAW,MAAM,EAAQ,MAAM,YAAY,CAAA;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAE3D,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,aAAa,EAAE,KAAK,KAAK,EAAE,KAAK,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAqHtH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM;WAMvD,KAAK,SAAS,MAAM,OAAO,EAAE,OAAO,SAAS,WAAW,EAAE,CAAC,EAAE,CAAC,QAC7D,MAAM,SACL,KAAK,SACL,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,WACpC,WAAW,CAAC,OAAO,CAAC;iBAsDpC;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,0DAEhE"}
|
package/dist/Store/Disk.js
CHANGED
|
@@ -38,10 +38,13 @@ function makeDiskStoreInt(prefix, idKey, namespace, dir, name, seed, defaultValu
|
|
|
38
38
|
attributes: { "disk.file": file }
|
|
39
39
|
}))
|
|
40
40
|
};
|
|
41
|
-
|
|
41
|
+
// lock file for cross-process coordination during initialization
|
|
42
|
+
const lockFile = file + ".lock";
|
|
43
|
+
// wrap initialization in file lock to prevent race conditions in multi-worker setups
|
|
44
|
+
const store = yield* fu.withFileLock(lockFile, makeMemoryStoreInt(name, idKey, namespace, !fs.existsSync(file)
|
|
42
45
|
? seed
|
|
43
|
-
: fsStore.get, defaultValues)
|
|
44
|
-
|
|
46
|
+
: fsStore.get, defaultValues)
|
|
47
|
+
.pipe(Effect.tap((store) => store.all.pipe(Effect.flatMap(fsStore.setRaw)))));
|
|
45
48
|
const sem = Effect.unsafeMakeSemaphore(1);
|
|
46
49
|
const withPermit = sem.withPermits(1);
|
|
47
50
|
const flushToDisk = Effect.flatMap(store.all, fsStore.setRaw).pipe(withPermit);
|
|
@@ -109,4 +112,4 @@ export function makeDiskStore({ prefix }, dir) {
|
|
|
109
112
|
export function DiskStoreLayer(config, dir) {
|
|
110
113
|
return StoreMaker.toLayer(makeDiskStore(config, dir));
|
|
111
114
|
}
|
|
112
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
115
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRGlzay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9TdG9yZS9EaXNrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHVEQUF1RDtBQUN2RCxPQUFPLEtBQUssRUFBRSxNQUFNLGdCQUFnQixDQUFBO0FBRXBDLE9BQU8sRUFBRSxNQUFNLElBQUksQ0FBQTtBQUVuQixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFFbEQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE9BQU8sRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUN6RCxPQUFPLEVBQStFLFVBQVUsRUFBRSxNQUFNLGNBQWMsQ0FBQTtBQUV0SCxTQUFTLGdCQUFnQixDQUN2QixNQUFjLEVBQ2QsS0FBWSxFQUNaLFNBQWlCLEVBQ2pCLEdBQVcsRUFDWCxJQUFZLEVBQ1osSUFBNkMsRUFDN0MsYUFBZ0M7SUFHaEMsT0FBTyxNQUFNLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQztRQUN6QixJQUFJLFNBQVMsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUM1QixHQUFHLEdBQUcsR0FBRyxHQUFHLEdBQUcsR0FBRyxTQUFTLENBQUE7WUFDM0IsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztnQkFDeEIsRUFBRSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQTtZQUNuQixDQUFDO1FBQ0gsQ0FBQztRQUNELE1BQU0sSUFBSSxHQUFHLEdBQUcsR0FBRyxHQUFHLEdBQUcsTUFBTSxHQUFHLElBQUksR0FBRyxPQUFPLENBQUE7UUFDaEQsTUFBTSxPQUFPLEdBQUc7WUFDZCxHQUFHLEVBQUUsRUFBRTtpQkFDSixZQUFZLENBQUMsSUFBSSxDQUFDO2lCQUNsQixJQUFJLENBQ0gsTUFBTSxDQUFDLFFBQVEsQ0FBQyw2Q0FBNkMsRUFBRSxFQUFFLGlCQUFpQixFQUFFLEtBQUssRUFBRSxDQUFDLEVB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
|
package/dist/Store/service.d.ts
CHANGED
|
@@ -93,7 +93,7 @@ declare const StoreMaker_base: (abstract new (service: {
|
|
|
93
93
|
} & Context.Tag<StoreMaker, StoreMaker> & {} & {
|
|
94
94
|
use: <X>(body: (_: {
|
|
95
95
|
make: <IdKey extends keyof Encoded, Encoded extends FieldValues, R = never, E = never>(name: string, idKey: IdKey, seed?: Effect.Effect<Iterable<Encoded>, E, R>, config?: StoreConfig<Encoded>) => Effect.Effect<Store<IdKey, Encoded>, E, R>;
|
|
96
|
-
}) => X) => X extends Effect.Effect<infer A, infer E_1, infer R_2> ? Effect.Effect<A, E_1,
|
|
96
|
+
}) => X) => X extends Effect.Effect<infer A, infer E_1, infer R_2> ? Effect.Effect<A, E_1, R_2 | StoreMaker> : Effect.Effect<X, never, StoreMaker>;
|
|
97
97
|
};
|
|
98
98
|
export declare class StoreMaker extends StoreMaker_base {
|
|
99
99
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect } from "effect-app";
|
|
2
2
|
import { HttpServerRequest, HttpServerResponse } from "effect-app/http";
|
|
3
3
|
import { Locale } from "../../RequestContext.js";
|
|
4
|
-
export declare const RequestContextMiddleware: (defaultLocale?: Locale) => <E, R>(app: import("@effect/platform/HttpApp").Default<E, R>) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, HttpServerRequest.HttpServerRequest | Exclude<Exclude<R, import("effect/Tracer").ParentSpan>, import("../../
|
|
4
|
+
export declare const RequestContextMiddleware: (defaultLocale?: Locale) => <E, R>(app: import("@effect/platform/HttpApp").Default<E, R>) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, HttpServerRequest.HttpServerRequest | Exclude<Exclude<R, import("effect/Tracer").ParentSpan>, import("../../RequestContext.js").LocaleRef | import("../../Store/ContextMapContainer.js").ContextMapContainer | import("../../Store/Memory.js").storeId>>;
|
|
5
5
|
//# sourceMappingURL=RequestContextMiddleware.d.ts.map
|
|
@@ -9,6 +9,6 @@ export declare const getRC: Effect.Effect<{
|
|
|
9
9
|
namespace: NonEmptyString255;
|
|
10
10
|
}, never, never>;
|
|
11
11
|
export declare const setupRequestContextFromCurrent: (name?: string, options?: Tracer.SpanOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<Exclude<R, Tracer.ParentSpan>, ContextMapContainer>>;
|
|
12
|
-
export declare function setupRequestContext<R, E, A>(self: Effect.Effect<A, E, R>, requestContext: RequestContext): Effect.Effect<A, E, Exclude<Exclude<R, Tracer.ParentSpan>,
|
|
13
|
-
export declare function setupRequestContextWithCustomSpan<R, E, A>(self: Effect.Effect<A, E, R>, requestContext: RequestContext, name: string, options?: Tracer.SpanOptions): Effect.Effect<A, E, Exclude<Exclude<R, Tracer.ParentSpan>,
|
|
12
|
+
export declare function setupRequestContext<R, E, A>(self: Effect.Effect<A, E, R>, requestContext: RequestContext): Effect.Effect<A, E, Exclude<Exclude<R, Tracer.ParentSpan>, LocaleRef | ContextMapContainer | storeId>>;
|
|
13
|
+
export declare function setupRequestContextWithCustomSpan<R, E, A>(self: Effect.Effect<A, E, R>, requestContext: RequestContext, name: string, options?: Tracer.SpanOptions): Effect.Effect<A, E, Exclude<Exclude<R, Tracer.ParentSpan>, LocaleRef | ContextMapContainer | storeId>>;
|
|
14
14
|
//# sourceMappingURL=setupRequest.d.ts.map
|
package/dist/fileUtil.d.ts
CHANGED
|
@@ -19,5 +19,15 @@ export declare function tempFile_(folder: string, prefix: string, data: Data, op
|
|
|
19
19
|
export declare function writeTextFile(fileName: string, content: string): Effect.Effect<void, never, never>;
|
|
20
20
|
export declare function fileExists(fileName: string): Effect.Effect<boolean, never, never>;
|
|
21
21
|
export declare function readTextFile(fileName: string): Effect.Effect<string, import("effect/Cause").UnknownException, never>;
|
|
22
|
+
/**
|
|
23
|
+
* Executes an action with an exclusive cross-process file lock.
|
|
24
|
+
* Uses proper-lockfile for robust lock management with stale lock detection,
|
|
25
|
+
* retry logic, and cross-platform support.
|
|
26
|
+
*
|
|
27
|
+
* @param filePath - The file to lock (will create {filePath}.lock)
|
|
28
|
+
* @param action - The Effect to execute while holding the lock
|
|
29
|
+
* @returns The result of the action
|
|
30
|
+
*/
|
|
31
|
+
export declare function withFileLock<A, E, R>(filePath: string, action: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>;
|
|
22
32
|
export {};
|
|
23
33
|
//# sourceMappingURL=fileUtil.d.ts.map
|
package/dist/fileUtil.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileUtil.d.ts","sourceRoot":"","sources":["../src/fileUtil.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;AAC/D,OAAO,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"fileUtil.d.ts","sourceRoot":"","sources":["../src/fileUtil.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;AAC/D,OAAO,EAAE,MAAM,aAAa,CAAA;AAI5B,OAAO,KAAK,QAAQ,MAAM,QAAQ,CAAA;AAElC,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,0FAExC;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,iHAEpD;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,uGAExC;AAED,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,IAEN,QAAQ,MAAM,MAAM,MAAM,IAAI,EAAE,UAAU,WAAW,kGAC9D;AAED,KAAK,IAAI,GACL,MAAM,GACN,MAAM,CAAC,eAAe,GACtB,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC,GACzC,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC,GAC9C,QAAQ,CAAC,MAAM,CAAA;AAEnB,MAAM,MAAM,WAAW,GACnB,CAAC,qBAAqB,GAAG;IACzB,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;IACvB,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;CAC5B,GAAG,SAAS,CAAC,GACZ,cAAc,GACd,IAAI,CAAA;AACR,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,IAAI,EACV,OAAO,CAAC,EAAE,WAAW,gGAgBtB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,qCAS9D;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,wCAG1C;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,yEAE5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAClC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CA6BxB"}
|
package/dist/fileUtil.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Effect } from "effect-app";
|
|
|
3
3
|
import fs from "fs/promises";
|
|
4
4
|
import os from "os";
|
|
5
5
|
import path from "path";
|
|
6
|
+
import lockfile from "proper-lockfile";
|
|
6
7
|
export function readFile(fileName) {
|
|
7
8
|
return Effect.tryPromise(() => fs.readFile(fileName));
|
|
8
9
|
}
|
|
@@ -38,4 +39,37 @@ export function fileExists(fileName) {
|
|
|
38
39
|
export function readTextFile(fileName) {
|
|
39
40
|
return Effect.tryPromise(() => fs.readFile(fileName, "utf-8"));
|
|
40
41
|
}
|
|
41
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Executes an action with an exclusive cross-process file lock.
|
|
44
|
+
* Uses proper-lockfile for robust lock management with stale lock detection,
|
|
45
|
+
* retry logic, and cross-platform support.
|
|
46
|
+
*
|
|
47
|
+
* @param filePath - The file to lock (will create {filePath}.lock)
|
|
48
|
+
* @param action - The Effect to execute while holding the lock
|
|
49
|
+
* @returns The result of the action
|
|
50
|
+
*/
|
|
51
|
+
export function withFileLock(filePath, action) {
|
|
52
|
+
return Effect
|
|
53
|
+
.gen(function* () {
|
|
54
|
+
// get lock
|
|
55
|
+
const release = yield* Effect
|
|
56
|
+
.tryPromise(() => lockfile.lock(filePath, {
|
|
57
|
+
retries: {
|
|
58
|
+
retries: 100, // retry up to 100 times
|
|
59
|
+
minTimeout: 50, // start with 50ms delay
|
|
60
|
+
maxTimeout: 2000, // max 2s delay between retries
|
|
61
|
+
randomize: true // add randomness to avoid thundering herd
|
|
62
|
+
},
|
|
63
|
+
stale: 10000, // lock is stale after 10s (process crashed)
|
|
64
|
+
realpath: false // don't resolve symlinks
|
|
65
|
+
}))
|
|
66
|
+
.pipe(Effect.orDie);
|
|
67
|
+
// ensure lock is released
|
|
68
|
+
yield* Effect.addFinalizer(() => Effect
|
|
69
|
+
.tryPromise(release)
|
|
70
|
+
.pipe(Effect.orDie));
|
|
71
|
+
return yield* action;
|
|
72
|
+
})
|
|
73
|
+
.pipe(Effect.scoped);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZVV0aWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZmlsZVV0aWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxNQUFNLE1BQU0sUUFBUSxDQUFBO0FBQzNCLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFHbkMsT0FBTyxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQzVCLE9BQU8sRUFBRSxNQUFNLElBQUksQ0FBQTtBQUNuQixPQUFPLElBQUksTUFBTSxNQUFNLENBQUE7QUFDdkIsT0FBTyxRQUFRLE1BQU0saUJBQWlCLENBQUE7QUFHdEMsTUFBTSxVQUFVLFFBQVEsQ0FBQyxRQUFnQjtJQUN2QyxPQUFPLE1BQU0sQ0FBQyxVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFBO0FBQ3ZELENBQUM7QUFFRCxNQUFNLFVBQVUsb0JBQW9CLENBQUMsUUFBZ0I7SUFDbkQsT0FBTyxNQUFNLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUMsQ0FBQTtBQUMxRSxDQUFDO0FBRUQsTUFBTSxVQUFVLFFBQVEsQ0FBQyxRQUFnQjtJQUN2QyxPQUFPLE1BQU0sQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQTtBQUNsSCxDQUFDO0FBRUQsTUFBTSxVQUFVLFFBQVEsQ0FDdEIsTUFBYztJQUVkLE9BQU8sQ0FBQyxNQUFjLEVBQUUsRUFBRSxDQUFDLENB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
|
package/package.json
CHANGED
package/src/Store/Disk.ts
CHANGED
|
@@ -69,17 +69,23 @@ function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValu
|
|
|
69
69
|
)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
idKey,
|
|
75
|
-
namespace,
|
|
76
|
-
!fs.existsSync(file)
|
|
77
|
-
? seed
|
|
78
|
-
: fsStore.get,
|
|
79
|
-
defaultValues
|
|
80
|
-
)
|
|
72
|
+
// lock file for cross-process coordination during initialization
|
|
73
|
+
const lockFile = file + ".lock"
|
|
81
74
|
|
|
82
|
-
|
|
75
|
+
// wrap initialization in file lock to prevent race conditions in multi-worker setups
|
|
76
|
+
const store = yield* fu.withFileLock(
|
|
77
|
+
lockFile,
|
|
78
|
+
makeMemoryStoreInt<IdKey, Encoded, R, E>(
|
|
79
|
+
name,
|
|
80
|
+
idKey,
|
|
81
|
+
namespace,
|
|
82
|
+
!fs.existsSync(file)
|
|
83
|
+
? seed
|
|
84
|
+
: fsStore.get,
|
|
85
|
+
defaultValues
|
|
86
|
+
)
|
|
87
|
+
.pipe(Effect.tap((store) => store.all.pipe(Effect.flatMap(fsStore.setRaw))))
|
|
88
|
+
)
|
|
83
89
|
|
|
84
90
|
const sem = Effect.unsafeMakeSemaphore(1)
|
|
85
91
|
const withPermit = sem.withPermits(1)
|
package/src/fileUtil.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { Mode, ObjectEncodingOptions, OpenMode } from "fs"
|
|
|
5
5
|
import fs from "fs/promises"
|
|
6
6
|
import os from "os"
|
|
7
7
|
import path from "path"
|
|
8
|
+
import lockfile from "proper-lockfile"
|
|
8
9
|
import type internal from "stream"
|
|
9
10
|
|
|
10
11
|
export function readFile(fileName: string) {
|
|
@@ -83,3 +84,46 @@ export function fileExists(fileName: string) {
|
|
|
83
84
|
export function readTextFile(fileName: string) {
|
|
84
85
|
return Effect.tryPromise(() => fs.readFile(fileName, "utf-8"))
|
|
85
86
|
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Executes an action with an exclusive cross-process file lock.
|
|
90
|
+
* Uses proper-lockfile for robust lock management with stale lock detection,
|
|
91
|
+
* retry logic, and cross-platform support.
|
|
92
|
+
*
|
|
93
|
+
* @param filePath - The file to lock (will create {filePath}.lock)
|
|
94
|
+
* @param action - The Effect to execute while holding the lock
|
|
95
|
+
* @returns The result of the action
|
|
96
|
+
*/
|
|
97
|
+
export function withFileLock<A, E, R>(
|
|
98
|
+
filePath: string,
|
|
99
|
+
action: Effect.Effect<A, E, R>
|
|
100
|
+
): Effect.Effect<A, E, R> {
|
|
101
|
+
return Effect
|
|
102
|
+
.gen(function*() {
|
|
103
|
+
// get lock
|
|
104
|
+
const release = yield* Effect
|
|
105
|
+
.tryPromise(() =>
|
|
106
|
+
lockfile.lock(filePath, {
|
|
107
|
+
retries: {
|
|
108
|
+
retries: 100, // retry up to 100 times
|
|
109
|
+
minTimeout: 50, // start with 50ms delay
|
|
110
|
+
maxTimeout: 2000, // max 2s delay between retries
|
|
111
|
+
randomize: true // add randomness to avoid thundering herd
|
|
112
|
+
},
|
|
113
|
+
stale: 10000, // lock is stale after 10s (process crashed)
|
|
114
|
+
realpath: false // don't resolve symlinks
|
|
115
|
+
})
|
|
116
|
+
)
|
|
117
|
+
.pipe(Effect.orDie)
|
|
118
|
+
|
|
119
|
+
// ensure lock is released
|
|
120
|
+
yield* Effect.addFinalizer(() =>
|
|
121
|
+
Effect
|
|
122
|
+
.tryPromise(release)
|
|
123
|
+
.pipe(Effect.orDie)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return yield* action
|
|
127
|
+
})
|
|
128
|
+
.pipe(Effect.scoped)
|
|
129
|
+
}
|
package/test/dist/fixtures.d.ts
CHANGED
|
@@ -122,7 +122,7 @@ declare const RequestContextMap_base: (new () => {
|
|
|
122
122
|
readonly requireRoles: RpcContextMap.RpcContextMap.Custom<never, typeof UnauthorizedError, string[]>;
|
|
123
123
|
readonly test: RpcContextMap.RpcContextMap<never, typeof S.Never>;
|
|
124
124
|
}>;
|
|
125
|
-
get: <Key extends "
|
|
125
|
+
get: <Key extends "test" | "allowAnonymous" | "requireRoles">(key: Key) => RpcX.RpcMiddleware.RpcDynamic<Key, {
|
|
126
126
|
readonly allowAnonymous: RpcContextMap.RpcContextMap.Inverted<UserProfile, typeof NotLoggedInError>;
|
|
127
127
|
readonly requireRoles: RpcContextMap.RpcContextMap.Custom<never, typeof UnauthorizedError, string[]>;
|
|
128
128
|
readonly test: RpcContextMap.RpcContextMap<never, typeof S.Never>;
|