diff --git a/backend/node_modules/effect/src/Arbitrary.ts b/backend/node_modules/effect/src/Arbitrary.ts new file mode 100644 index 0000000000000000000000000000000000000000..a46e96b37a2474ee1df4fd4d5718aeb89b3e0f2e --- /dev/null +++ b/backend/node_modules/effect/src/Arbitrary.ts @@ -0,0 +1,1101 @@ +/** + * @since 3.10.0 + */ + +import * as Arr from "./Array.js" +import * as FastCheck from "./FastCheck.js" +import { globalValue } from "./GlobalValue.js" +import * as errors_ from "./internal/schema/errors.js" +import * as schemaId_ from "./internal/schema/schemaId.js" +import * as util_ from "./internal/schema/util.js" +import * as Option from "./Option.js" +import * as Predicate from "./Predicate.js" +import type * as Schema from "./Schema.js" +import * as SchemaAST from "./SchemaAST.js" +import type * as Types from "./Types.js" + +/** + * @category model + * @since 3.10.0 + */ +export interface LazyArbitrary { + (fc: typeof FastCheck): FastCheck.Arbitrary +} + +/** + * @category annotations + * @since 3.10.0 + */ +export interface ArbitraryGenerationContext { + readonly maxDepth: number + readonly depthIdentifier?: string + readonly constraints?: StringConstraints | NumberConstraints | BigIntConstraints | DateConstraints | ArrayConstraints +} + +/** + * @category annotations + * @since 3.10.0 + */ +export type ArbitraryAnnotation = readonly []> = ( + ...arbitraries: [ + ...{ readonly [K in keyof TypeParameters]: LazyArbitrary }, + ctx: ArbitraryGenerationContext + ] +) => LazyArbitrary + +/** + * Returns a LazyArbitrary for the `A` type of the provided schema. + * + * @category arbitrary + * @since 3.10.0 + */ +export const makeLazy = (schema: Schema.Schema): LazyArbitrary => { + const description = getDescription(schema.ast, []) + return go(description, { maxDepth: 2 }) +} + +/** + * Returns a fast-check Arbitrary for the `A` type of the provided schema. + * + * @category arbitrary + * @since 3.10.0 + */ +export const make = (schema: Schema.Schema): FastCheck.Arbitrary => makeLazy(schema)(FastCheck) + +interface StringConstraints { + readonly _tag: "StringConstraints" + readonly constraints: FastCheck.StringSharedConstraints + readonly pattern?: string +} + +/** @internal */ +export const makeStringConstraints = (options: { + readonly minLength?: number | undefined + readonly maxLength?: number | undefined + readonly pattern?: string | undefined +}): StringConstraints => { + const out: Types.Mutable = { + _tag: "StringConstraints", + constraints: {} + } + if (Predicate.isNumber(options.minLength)) { + out.constraints.minLength = options.minLength + } + if (Predicate.isNumber(options.maxLength)) { + out.constraints.maxLength = options.maxLength + } + if (Predicate.isString(options.pattern)) { + out.pattern = options.pattern + } + return out +} + +interface NumberConstraints { + readonly _tag: "NumberConstraints" + readonly constraints: FastCheck.FloatConstraints + readonly isInteger: boolean +} + +/** @internal */ +export const makeNumberConstraints = (options: { + readonly isInteger?: boolean | undefined + readonly min?: unknown + readonly minExcluded?: boolean | undefined + readonly max?: unknown + readonly maxExcluded?: boolean | undefined + readonly noNaN?: boolean | undefined + readonly noDefaultInfinity?: boolean | undefined +}): NumberConstraints => { + const out: Types.Mutable = { + _tag: "NumberConstraints", + constraints: {}, + isInteger: options.isInteger ?? false + } + if (Predicate.isNumber(options.min)) { + out.constraints.min = Math.fround(options.min) + } + if (Predicate.isBoolean(options.minExcluded)) { + out.constraints.minExcluded = options.minExcluded + } + if (Predicate.isNumber(options.max)) { + out.constraints.max = Math.fround(options.max) + } + if (Predicate.isBoolean(options.maxExcluded)) { + out.constraints.maxExcluded = options.maxExcluded + } + if (Predicate.isBoolean(options.noNaN)) { + out.constraints.noNaN = options.noNaN + } + if (Predicate.isBoolean(options.noDefaultInfinity)) { + out.constraints.noDefaultInfinity = options.noDefaultInfinity + } + return out +} + +interface BigIntConstraints { + readonly _tag: "BigIntConstraints" + readonly constraints: FastCheck.BigIntConstraints +} + +/** @internal */ +export const makeBigIntConstraints = (options: { + readonly min?: bigint | undefined + readonly max?: bigint | undefined +}): BigIntConstraints => { + const out: Types.Mutable = { + _tag: "BigIntConstraints", + constraints: {} + } + if (Predicate.isBigInt(options.min)) { + out.constraints.min = options.min + } + if (Predicate.isBigInt(options.max)) { + out.constraints.max = options.max + } + return out +} + +interface ArrayConstraints { + readonly _tag: "ArrayConstraints" + readonly constraints: FastCheck.ArrayConstraints +} + +/** @internal */ +export const makeArrayConstraints = (options: { + readonly minLength?: unknown + readonly maxLength?: unknown +}): ArrayConstraints => { + const out: Types.Mutable = { + _tag: "ArrayConstraints", + constraints: {} + } + if (Predicate.isNumber(options.minLength)) { + out.constraints.minLength = options.minLength + } + if (Predicate.isNumber(options.maxLength)) { + out.constraints.maxLength = options.maxLength + } + return out +} + +interface DateConstraints { + readonly _tag: "DateConstraints" + readonly constraints: FastCheck.DateConstraints +} + +/** @internal */ +export const makeDateConstraints = (options: { + readonly min?: Date | undefined + readonly max?: Date | undefined + readonly noInvalidDate?: boolean | undefined +}): DateConstraints => { + const out: Types.Mutable = { + _tag: "DateConstraints", + constraints: {} + } + if (Predicate.isDate(options.min)) { + out.constraints.min = options.min + } + if (Predicate.isDate(options.max)) { + out.constraints.max = options.max + } + if (Predicate.isBoolean(options.noInvalidDate)) { + out.constraints.noInvalidDate = options.noInvalidDate + } + return out +} + +type Refinements = ReadonlyArray + +interface Base { + readonly path: ReadonlyArray + readonly refinements: Refinements + readonly annotations: ReadonlyArray> +} + +interface StringKeyword extends Base { + readonly _tag: "StringKeyword" + readonly constraints: ReadonlyArray +} + +interface NumberKeyword extends Base { + readonly _tag: "NumberKeyword" + readonly constraints: ReadonlyArray +} + +interface BigIntKeyword extends Base { + readonly _tag: "BigIntKeyword" + readonly constraints: ReadonlyArray +} + +interface DateFromSelf extends Base { + readonly _tag: "DateFromSelf" + readonly constraints: ReadonlyArray +} + +interface Declaration extends Base { + readonly _tag: "Declaration" + readonly typeParameters: ReadonlyArray + readonly ast: SchemaAST.AST +} + +interface TupleType extends Base { + readonly _tag: "TupleType" + readonly constraints: ReadonlyArray + readonly elements: ReadonlyArray<{ + readonly isOptional: boolean + readonly description: Description + }> + readonly rest: ReadonlyArray +} + +interface TypeLiteral extends Base { + readonly _tag: "TypeLiteral" + readonly propertySignatures: ReadonlyArray<{ + readonly isOptional: boolean + readonly name: PropertyKey + readonly value: Description + }> + readonly indexSignatures: ReadonlyArray<{ + readonly parameter: Description + readonly value: Description + }> +} + +interface Union extends Base { + readonly _tag: "Union" + readonly members: ReadonlyArray +} + +interface Suspend extends Base { + readonly _tag: "Suspend" + readonly id: string + readonly ast: SchemaAST.AST + readonly description: () => Description +} + +interface Ref extends Base { + readonly _tag: "Ref" + readonly id: string + readonly ast: SchemaAST.AST +} + +interface NeverKeyword extends Base { + readonly _tag: "NeverKeyword" + readonly ast: SchemaAST.AST +} + +interface Keyword extends Base { + readonly _tag: "Keyword" + readonly value: + | "UndefinedKeyword" + | "VoidKeyword" + | "UnknownKeyword" + | "AnyKeyword" + | "BooleanKeyword" + | "SymbolKeyword" + | "ObjectKeyword" +} + +interface Literal extends Base { + readonly _tag: "Literal" + readonly literal: SchemaAST.LiteralValue +} + +interface UniqueSymbol extends Base { + readonly _tag: "UniqueSymbol" + readonly symbol: symbol +} + +interface Enums extends Base { + readonly _tag: "Enums" + readonly enums: ReadonlyArray + readonly ast: SchemaAST.AST +} + +interface TemplateLiteral extends Base { + readonly _tag: "TemplateLiteral" + readonly head: string + readonly spans: ReadonlyArray<{ + readonly description: Description + readonly literal: string + }> +} + +type Description = + | Declaration + | NeverKeyword + | Keyword + | Literal + | UniqueSymbol + | Enums + | TemplateLiteral + | StringKeyword + | NumberKeyword + | BigIntKeyword + | DateFromSelf + | TupleType + | TypeLiteral + | Union + | Suspend + | Ref + +const getArbitraryAnnotation = SchemaAST.getAnnotation>(SchemaAST.ArbitraryAnnotationId) + +const getASTConstraints = (ast: SchemaAST.AST) => { + const TypeAnnotationId = ast.annotations[SchemaAST.SchemaIdAnnotationId] + if (Predicate.isPropertyKey(TypeAnnotationId)) { + const out = ast.annotations[TypeAnnotationId] + if (Predicate.isReadonlyRecord(out)) { + return out + } + } +} + +const idMemoMap = globalValue( + Symbol.for("effect/Arbitrary/IdMemoMap"), + () => new Map() +) + +let counter = 0 + +function wrapGetDescription( + f: (ast: SchemaAST.AST, description: Description) => Description, + g: (ast: SchemaAST.AST, path: ReadonlyArray) => Description +): (ast: SchemaAST.AST, path: ReadonlyArray) => Description { + return (ast, path) => f(ast, g(ast, path)) +} + +function parseMeta(ast: SchemaAST.AST): [SchemaAST.SchemaIdAnnotation | undefined, Record] { + const jsonSchema = SchemaAST.getJSONSchemaAnnotation(ast).pipe( + Option.filter(Predicate.isReadonlyRecord), + Option.getOrUndefined + ) + const schemaId = Option.getOrElse(SchemaAST.getSchemaIdAnnotation(ast), () => undefined) + const schemaParams = Option.fromNullable(schemaId).pipe( + Option.map((id) => ast.annotations[id]), + Option.filter(Predicate.isReadonlyRecord), + Option.getOrUndefined + ) + return [schemaId, { ...schemaParams, ...jsonSchema }] +} + +/** @internal */ +export const getDescription = wrapGetDescription( + (ast, description) => { + const annotation = getArbitraryAnnotation(ast) + if (Option.isSome(annotation)) { + return { + ...description, + annotations: [...description.annotations, annotation.value] + } + } + return description + }, + (ast, path) => { + const [schemaId, meta] = parseMeta(ast) + switch (ast._tag) { + case "Refinement": { + const from = getDescription(ast.from, path) + switch (from._tag) { + case "StringKeyword": + return { + ...from, + constraints: [...from.constraints, makeStringConstraints(meta)], + refinements: [...from.refinements, ast] + } + case "NumberKeyword": { + const c = schemaId === schemaId_.NonNaNSchemaId ? + makeNumberConstraints({ noNaN: true }) : + schemaId === schemaId_.FiniteSchemaId || schemaId === schemaId_.JsonNumberSchemaId ? + makeNumberConstraints({ noDefaultInfinity: true, noNaN: true }) : + makeNumberConstraints({ + isInteger: "type" in meta && meta.type === "integer", + noNaN: undefined, + noDefaultInfinity: undefined, + min: meta.exclusiveMinimum ?? meta.minimum, + minExcluded: "exclusiveMinimum" in meta ? true : undefined, + max: meta.exclusiveMaximum ?? meta.maximum, + maxExcluded: "exclusiveMaximum" in meta ? true : undefined + }) + return { + ...from, + constraints: [...from.constraints, c], + refinements: [...from.refinements, ast] + } + } + case "BigIntKeyword": { + const c = getASTConstraints(ast) + return { + ...from, + constraints: c !== undefined ? [...from.constraints, makeBigIntConstraints(c)] : from.constraints, + refinements: [...from.refinements, ast] + } + } + case "TupleType": + return { + ...from, + constraints: [ + ...from.constraints, + makeArrayConstraints({ + minLength: meta.minItems, + maxLength: meta.maxItems + }) + ], + refinements: [...from.refinements, ast] + } + case "DateFromSelf": + return { + ...from, + constraints: [...from.constraints, makeDateConstraints(meta)], + refinements: [...from.refinements, ast] + } + default: + return { + ...from, + refinements: [...from.refinements, ast] + } + } + } + case "Declaration": { + if (schemaId === schemaId_.DateFromSelfSchemaId) { + return { + _tag: "DateFromSelf", + constraints: [makeDateConstraints(meta)], + path, + refinements: [], + annotations: [] + } + } + return { + _tag: "Declaration", + typeParameters: ast.typeParameters.map((ast) => getDescription(ast, path)), + path, + refinements: [], + annotations: [], + ast + } + } + case "Literal": { + return { + _tag: "Literal", + literal: ast.literal, + path, + refinements: [], + annotations: [] + } + } + case "UniqueSymbol": { + return { + _tag: "UniqueSymbol", + symbol: ast.symbol, + path, + refinements: [], + annotations: [] + } + } + case "Enums": { + return { + _tag: "Enums", + enums: ast.enums, + path, + refinements: [], + annotations: [], + ast + } + } + case "TemplateLiteral": { + return { + _tag: "TemplateLiteral", + head: ast.head, + spans: ast.spans.map((span) => ({ + description: getDescription(span.type, path), + literal: span.literal + })), + path, + refinements: [], + annotations: [] + } + } + case "StringKeyword": + return { + _tag: "StringKeyword", + constraints: [], + path, + refinements: [], + annotations: [] + } + case "NumberKeyword": + return { + _tag: "NumberKeyword", + constraints: [], + path, + refinements: [], + annotations: [] + } + case "BigIntKeyword": + return { + _tag: "BigIntKeyword", + constraints: [], + path, + refinements: [], + annotations: [] + } + case "TupleType": + return { + _tag: "TupleType", + constraints: [], + elements: ast.elements.map((element, i) => ({ + isOptional: element.isOptional, + description: getDescription(element.type, [...path, i]) + })), + rest: ast.rest.map((element, i) => getDescription(element.type, [...path, i])), + path, + refinements: [], + annotations: [] + } + case "TypeLiteral": + return { + _tag: "TypeLiteral", + propertySignatures: ast.propertySignatures.map((ps) => ({ + isOptional: ps.isOptional, + name: ps.name, + value: getDescription(ps.type, [...path, ps.name]) + })), + indexSignatures: ast.indexSignatures.map((is) => ({ + parameter: getDescription(is.parameter, path), + value: getDescription(is.type, path) + })), + path, + refinements: [], + annotations: [] + } + case "Union": + return { + _tag: "Union", + members: ast.types.map((member, i) => getDescription(member, [...path, i])), + path, + refinements: [], + annotations: [] + } + case "Suspend": { + const memoId = idMemoMap.get(ast) + if (memoId !== undefined) { + return { + _tag: "Ref", + id: memoId, + ast, + path, + refinements: [], + annotations: [] + } + } + counter++ + const id = `__id-${counter}__` + idMemoMap.set(ast, id) + return { + _tag: "Suspend", + id, + ast, + description: () => getDescription(ast.f(), path), + path, + refinements: [], + annotations: [] + } + } + case "Transformation": + return getDescription(ast.to, path) + case "NeverKeyword": + return { + _tag: "NeverKeyword", + path, + refinements: [], + annotations: [], + ast + } + default: { + return { + _tag: "Keyword", + value: ast._tag, + path, + refinements: [], + annotations: [] + } + } + } + } +) + +function getMax(n1: Date | undefined, n2: Date | undefined): Date | undefined +function getMax(n1: bigint | undefined, n2: bigint | undefined): bigint | undefined +function getMax(n1: number | undefined, n2: number | undefined): number | undefined +function getMax( + n1: bigint | number | Date | undefined, + n2: bigint | number | Date | undefined +): bigint | number | Date | undefined { + return n1 === undefined ? n2 : n2 === undefined ? n1 : n1 <= n2 ? n2 : n1 +} + +function getMin(n1: Date | undefined, n2: Date | undefined): Date | undefined +function getMin(n1: bigint | undefined, n2: bigint | undefined): bigint | undefined +function getMin(n1: number | undefined, n2: number | undefined): number | undefined +function getMin( + n1: bigint | number | Date | undefined, + n2: bigint | number | Date | undefined +): bigint | number | Date | undefined { + return n1 === undefined ? n2 : n2 === undefined ? n1 : n1 <= n2 ? n1 : n2 +} + +const getOr = (a: boolean | undefined, b: boolean | undefined): boolean | undefined => { + return a === undefined ? b : b === undefined ? a : a || b +} + +function mergePattern(pattern1: string | undefined, pattern2: string | undefined): string | undefined { + if (pattern1 === undefined) { + return pattern2 + } + if (pattern2 === undefined) { + return pattern1 + } + return `(?:${pattern1})|(?:${pattern2})` +} + +function mergeStringConstraints(c1: StringConstraints, c2: StringConstraints): StringConstraints { + return makeStringConstraints({ + minLength: getMax(c1.constraints.minLength, c2.constraints.minLength), + maxLength: getMin(c1.constraints.maxLength, c2.constraints.maxLength), + pattern: mergePattern(c1.pattern, c2.pattern) + }) +} + +function buildStringConstraints(description: StringKeyword): StringConstraints | undefined { + return description.constraints.length === 0 + ? undefined + : description.constraints.reduce(mergeStringConstraints) +} + +function mergeNumberConstraints(c1: NumberConstraints, c2: NumberConstraints): NumberConstraints { + return makeNumberConstraints({ + isInteger: c1.isInteger || c2.isInteger, + min: getMax(c1.constraints.min, c2.constraints.min), + minExcluded: getOr(c1.constraints.minExcluded, c2.constraints.minExcluded), + max: getMin(c1.constraints.max, c2.constraints.max), + maxExcluded: getOr(c1.constraints.maxExcluded, c2.constraints.maxExcluded), + noNaN: getOr(c1.constraints.noNaN, c2.constraints.noNaN), + noDefaultInfinity: getOr(c1.constraints.noDefaultInfinity, c2.constraints.noDefaultInfinity) + }) +} + +function buildNumberConstraints(description: NumberKeyword): NumberConstraints | undefined { + return description.constraints.length === 0 + ? undefined + : description.constraints.reduce(mergeNumberConstraints) +} + +function mergeBigIntConstraints(c1: BigIntConstraints, c2: BigIntConstraints): BigIntConstraints { + return makeBigIntConstraints({ + min: getMax(c1.constraints.min, c2.constraints.min), + max: getMin(c1.constraints.max, c2.constraints.max) + }) +} + +function buildBigIntConstraints(description: BigIntKeyword): BigIntConstraints | undefined { + return description.constraints.length === 0 + ? undefined + : description.constraints.reduce(mergeBigIntConstraints) +} + +function mergeDateConstraints(c1: DateConstraints, c2: DateConstraints): DateConstraints { + return makeDateConstraints({ + min: getMax(c1.constraints.min, c2.constraints.min), + max: getMin(c1.constraints.max, c2.constraints.max), + noInvalidDate: getOr(c1.constraints.noInvalidDate, c2.constraints.noInvalidDate) + }) +} + +function buildDateConstraints(description: DateFromSelf): DateConstraints | undefined { + return description.constraints.length === 0 + ? undefined + : description.constraints.reduce(mergeDateConstraints) +} + +const constArrayConstraints = makeArrayConstraints({}) + +function mergeArrayConstraints(c1: ArrayConstraints, c2: ArrayConstraints): ArrayConstraints { + return makeArrayConstraints({ + minLength: getMax(c1.constraints.minLength, c2.constraints.minLength), + maxLength: getMin(c1.constraints.maxLength, c2.constraints.maxLength) + }) +} + +function buildArrayConstraints(description: TupleType): ArrayConstraints | undefined { + return description.constraints.length === 0 + ? undefined + : description.constraints.reduce(mergeArrayConstraints) +} + +const arbitraryMemoMap = globalValue( + Symbol.for("effect/Arbitrary/arbitraryMemoMap"), + () => new WeakMap>() +) + +function applyFilters(filters: ReadonlyArray>, arb: LazyArbitrary): LazyArbitrary { + return (fc) => filters.reduce((arb, filter) => arb.filter(filter), arb(fc)) +} + +function absurd(message: string): LazyArbitrary { + return () => { + throw new Error(message) + } +} + +function getContextConstraints(description: Description): ArbitraryGenerationContext["constraints"] { + switch (description._tag) { + case "StringKeyword": + return buildStringConstraints(description) + case "NumberKeyword": + return buildNumberConstraints(description) + case "BigIntKeyword": + return buildBigIntConstraints(description) + case "DateFromSelf": + return buildDateConstraints(description) + case "TupleType": + return buildArrayConstraints(description) + } +} + +function wrapGo( + f: (description: Description, ctx: ArbitraryGenerationContext, lazyArb: LazyArbitrary) => LazyArbitrary, + g: (description: Description, ctx: ArbitraryGenerationContext) => LazyArbitrary +): (description: Description, ctx: ArbitraryGenerationContext) => LazyArbitrary { + return (description, ctx) => f(description, ctx, g(description, ctx)) +} + +const go = wrapGo( + (description, ctx, lazyArb) => { + const annotation: ArbitraryAnnotation | undefined = + description.annotations[description.annotations.length - 1] + + // error handling + if (annotation === undefined) { + switch (description._tag) { + case "Declaration": + case "NeverKeyword": + throw new Error(errors_.getArbitraryMissingAnnotationErrorMessage(description.path, description.ast)) + case "Enums": + if (description.enums.length === 0) { + throw new Error(errors_.getArbitraryEmptyEnumErrorMessage(description.path)) + } + } + } + + const filters = description.refinements.map((ast) => (a: any) => + Option.isNone(ast.filter(a, SchemaAST.defaultParseOption, ast)) + ) + if (annotation === undefined) { + return applyFilters(filters, lazyArb) + } + + const constraints = getContextConstraints(description) + if (constraints !== undefined) { + ctx = { ...ctx, constraints } + } + + if (description._tag === "Declaration") { + return applyFilters(filters, annotation(...description.typeParameters.map((p) => go(p, ctx)), ctx)) + } + if (description.refinements.length > 0) { + // TODO(4.0): remove the `lazyArb` parameter + return applyFilters(filters, annotation(lazyArb, ctx)) + } + return annotation(ctx) + }, + (description, ctx) => { + switch (description._tag) { + case "DateFromSelf": { + const constraints = buildDateConstraints(description) + return (fc) => fc.date(constraints?.constraints) + } + case "Declaration": + case "NeverKeyword": + return absurd(`BUG: cannot generate an arbitrary for ${description._tag}`) + case "Literal": + return (fc) => fc.constant(description.literal) + case "UniqueSymbol": + return (fc) => fc.constant(description.symbol) + case "Keyword": { + switch (description.value) { + case "UndefinedKeyword": + return (fc) => fc.constant(undefined) + case "VoidKeyword": + case "UnknownKeyword": + case "AnyKeyword": + return (fc) => fc.anything() + case "BooleanKeyword": + return (fc) => fc.boolean() + case "SymbolKeyword": + return (fc) => fc.string().map((s) => Symbol.for(s)) + case "ObjectKeyword": + return (fc) => fc.oneof(fc.object(), fc.array(fc.anything())) + } + } + case "Enums": + return (fc) => fc.oneof(...description.enums.map(([_, value]) => fc.constant(value))) + case "TemplateLiteral": { + return (fc) => { + const string = fc.string({ maxLength: 5 }) + const number = fc.float({ noDefaultInfinity: true, noNaN: true }) + + const getTemplateLiteralArb = (description: TemplateLiteral) => { + const components: Array> = description.head !== "" + ? [fc.constant(description.head)] + : [] + + const getTemplateLiteralSpanTypeArb = ( + description: Description + ): FastCheck.Arbitrary => { + switch (description._tag) { + case "StringKeyword": + return string + case "NumberKeyword": + return number + case "Literal": + return fc.constant(String(description.literal)) + case "Union": + return fc.oneof(...description.members.map(getTemplateLiteralSpanTypeArb)) + case "TemplateLiteral": + return getTemplateLiteralArb(description) + default: + return fc.constant("") + } + } + + description.spans.forEach((span) => { + components.push(getTemplateLiteralSpanTypeArb(span.description)) + if (span.literal !== "") { + components.push(fc.constant(span.literal)) + } + }) + + return fc.tuple(...components).map((spans) => spans.join("")) + } + + return getTemplateLiteralArb(description) + } + } + case "StringKeyword": { + const constraints = buildStringConstraints(description) + const pattern = constraints?.pattern + return pattern !== undefined ? + (fc) => fc.stringMatching(new RegExp(pattern)) : + (fc) => fc.string(constraints?.constraints) + } + case "NumberKeyword": { + const constraints = buildNumberConstraints(description) + return constraints?.isInteger ? + (fc) => fc.integer(constraints.constraints) : + (fc) => fc.float(constraints?.constraints) + } + case "BigIntKeyword": { + const constraints = buildBigIntConstraints(description) + return (fc) => fc.bigInt(constraints?.constraints ?? {}) + } + case "TupleType": { + const elements: Array> = [] + let hasOptionals = false + for (const element of description.elements) { + elements.push(go(element.description, ctx)) + if (element.isOptional) { + hasOptionals = true + } + } + const rest = description.rest.map((d) => go(d, ctx)) + return (fc) => { + // --------------------------------------------- + // handle elements + // --------------------------------------------- + let output = fc.tuple(...elements.map((arb) => arb(fc))) + if (hasOptionals) { + const indexes = fc.tuple( + ...description.elements.map((element) => element.isOptional ? fc.boolean() : fc.constant(true)) + ) + output = output.chain((tuple) => + indexes.map((booleans) => { + for (const [i, b] of booleans.reverse().entries()) { + if (!b) { + tuple.splice(booleans.length - i, 1) + } + } + return tuple + }) + ) + } + + // --------------------------------------------- + // handle rest element + // --------------------------------------------- + if (Arr.isNonEmptyReadonlyArray(rest)) { + const constraints = buildArrayConstraints(description) ?? constArrayConstraints + const [head, ...tail] = rest + const item = head(fc) + output = output.chain((as) => { + const len = as.length + // We must adjust the constraints for the rest element + // because the elements might have generated some values + const restArrayConstraints = subtractElementsLength(constraints.constraints, len) + if (restArrayConstraints.maxLength === 0) { + return fc.constant(as) + } + /* + + `getSuspendedArray` is used to generate less values in + the context of a recursive schema. Without it, the following schema + would generate an big amount of values possibly leading to a stack + overflow: + + ```ts + type A = ReadonlyArray + + const schema = S.Array( + S.NullOr(S.suspend((): S.Schema => schema)) + ) + ``` + + */ + const arr = ctx.depthIdentifier !== undefined + ? getSuspendedArray(fc, ctx.depthIdentifier, ctx.maxDepth, item, restArrayConstraints) + : fc.array(item, restArrayConstraints) + if (len === 0) { + return arr + } + return arr.map((rest) => [...as, ...rest]) + }) + // --------------------------------------------- + // handle post rest elements + // --------------------------------------------- + for (let j = 0; j < tail.length; j++) { + output = output.chain((as) => tail[j](fc).map((a) => [...as, a])) + } + } + + return output + } + } + case "TypeLiteral": { + const propertySignatures: Array> = [] + const requiredKeys: Array = [] + for (const ps of description.propertySignatures) { + if (!ps.isOptional) { + requiredKeys.push(ps.name) + } + propertySignatures.push(go(ps.value, ctx)) + } + const indexSignatures = description.indexSignatures.map((is) => + [go(is.parameter, ctx), go(is.value, ctx)] as const + ) + return (fc) => { + const pps: any = {} + for (let i = 0; i < propertySignatures.length; i++) { + const ps = description.propertySignatures[i] + pps[ps.name] = propertySignatures[i](fc) + } + let output = fc.record(pps, { requiredKeys }) + // --------------------------------------------- + // handle index signatures + // --------------------------------------------- + for (let i = 0; i < indexSignatures.length; i++) { + const key = indexSignatures[i][0](fc) + const value = indexSignatures[i][1](fc) + output = output.chain((o) => { + const item = fc.tuple(key, value) + /* + + `getSuspendedArray` is used to generate less key/value pairs in + the context of a recursive schema. Without it, the following schema + would generate an big amount of values possibly leading to a stack + overflow: + + ```ts + type A = { [_: string]: A } + + const schema = S.Record({ key: S.String, value: S.suspend((): S.Schema => schema) }) + ``` + + */ + const arr = ctx.depthIdentifier !== undefined ? + getSuspendedArray(fc, ctx.depthIdentifier, ctx.maxDepth, item, { maxLength: 2 }) : + fc.array(item) + return arr.map((tuples) => ({ ...Object.fromEntries(tuples), ...o })) + }) + } + + return output + } + } + case "Union": { + const members = description.members.map((member) => go(member, ctx)) + return (fc) => fc.oneof(...members.map((arb) => arb(fc))) + } + case "Suspend": { + const memo = arbitraryMemoMap.get(description.ast) + if (memo) { + return memo + } + if (ctx.depthIdentifier === undefined) { + ctx = { ...ctx, depthIdentifier: description.id } + } + const get = util_.memoizeThunk(() => { + return go(description.description(), ctx) + }) + const out: LazyArbitrary = (fc) => fc.constant(null).chain(() => get()(fc)) + arbitraryMemoMap.set(description.ast, out) + return out + } + case "Ref": { + const memo = arbitraryMemoMap.get(description.ast) + if (memo) { + return memo + } + throw new Error(`BUG: Ref ${JSON.stringify(description.id)} not found`) + } + } + } +) + +function subtractElementsLength( + constraints: FastCheck.ArrayConstraints, + len: number +): FastCheck.ArrayConstraints { + if (len === 0 || (constraints.minLength === undefined && constraints.maxLength === undefined)) { + return constraints + } + const out = { ...constraints } + if (out.minLength !== undefined) { + out.minLength = Math.max(out.minLength - len, 0) + } + if (out.maxLength !== undefined) { + out.maxLength = Math.max(out.maxLength - len, 0) + } + return out +} + +const getSuspendedArray = ( + fc: typeof FastCheck, + depthIdentifier: string, + maxDepth: number, + item: FastCheck.Arbitrary, + constraints: FastCheck.ArrayConstraints +) => { + // In the context of a recursive schema, we don't want a `maxLength` greater than 2. + // The only exception is when `minLength` is also set, in which case we set + // `maxLength` to the minimum value, which is `minLength`. + const maxLengthLimit = Math.max(2, constraints.minLength ?? 0) + if (constraints.maxLength !== undefined && constraints.maxLength > maxLengthLimit) { + constraints = { ...constraints, maxLength: maxLengthLimit } + } + return fc.oneof( + { maxDepth, depthIdentifier }, + fc.constant([]), + fc.array(item, constraints) + ) +} diff --git a/backend/node_modules/effect/src/BigInt.ts b/backend/node_modules/effect/src/BigInt.ts new file mode 100644 index 0000000000000000000000000000000000000000..aa187ed5eedf995058ec069a28e80cb9ac03da8a --- /dev/null +++ b/backend/node_modules/effect/src/BigInt.ts @@ -0,0 +1,1139 @@ +/** + * This module provides utility functions and type class instances for working with the `bigint` type in TypeScript. + * It includes functions for basic arithmetic operations, as well as type class instances for + * `Equivalence` and `Order`. + * + * @module BigInt + * @since 2.0.0 + * @see {@link module:BigDecimal} for more similar operations on `BigDecimal` types + * @see {@link module:Number} for more similar operations on `number` types + */ + +import * as equivalence from "./Equivalence.js" +import { dual } from "./Function.js" +import * as Option from "./Option.js" +import * as order from "./Order.js" +import type { Ordering } from "./Ordering.js" +import * as predicate from "./Predicate.js" + +const bigint0 = BigInt(0) +const bigint1 = BigInt(1) +const bigint2 = BigInt(2) + +/** + * Tests if a value is a `bigint`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isBigInt } from "effect/BigInt" + * + * assert.deepStrictEqual(isBigInt(1n), true) + * assert.deepStrictEqual(isBigInt(1), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isBigInt: (u: unknown) => u is bigint = predicate.isBigInt + +/** + * Provides an addition operation on `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { sum } from "effect/BigInt" + * + * assert.deepStrictEqual(sum(2n, 3n), 5n) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const sum: { + /** + * Provides an addition operation on `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { sum } from "effect/BigInt" + * + * assert.deepStrictEqual(sum(2n, 3n), 5n) + * ``` + * + * @category math + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => bigint + /** + * Provides an addition operation on `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { sum } from "effect/BigInt" + * + * assert.deepStrictEqual(sum(2n, 3n), 5n) + * ``` + * + * @category math + * @since 2.0.0 + */ + (self: bigint, that: bigint): bigint +} = dual(2, (self: bigint, that: bigint): bigint => self + that) + +/** + * Provides a multiplication operation on `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { multiply } from "effect/BigInt" + * + * assert.deepStrictEqual(multiply(2n, 3n), 6n) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const multiply: { + /** + * Provides a multiplication operation on `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { multiply } from "effect/BigInt" + * + * assert.deepStrictEqual(multiply(2n, 3n), 6n) + * ``` + * + * @category math + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => bigint + /** + * Provides a multiplication operation on `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { multiply } from "effect/BigInt" + * + * assert.deepStrictEqual(multiply(2n, 3n), 6n) + * ``` + * + * @category math + * @since 2.0.0 + */ + (self: bigint, that: bigint): bigint +} = dual(2, (self: bigint, that: bigint): bigint => self * that) + +/** + * Provides a subtraction operation on `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { subtract } from "effect/BigInt" + * + * assert.deepStrictEqual(subtract(2n, 3n), -1n) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const subtract: { + /** + * Provides a subtraction operation on `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { subtract } from "effect/BigInt" + * + * assert.deepStrictEqual(subtract(2n, 3n), -1n) + * ``` + * + * @category math + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => bigint + /** + * Provides a subtraction operation on `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { subtract } from "effect/BigInt" + * + * assert.deepStrictEqual(subtract(2n, 3n), -1n) + * ``` + * + * @category math + * @since 2.0.0 + */ + (self: bigint, that: bigint): bigint +} = dual(2, (self: bigint, that: bigint): bigint => self - that) + +/** + * Provides a division operation on `bigint`s. + * + * If the dividend is not a multiple of the divisor the result will be a `bigint` value + * which represents the integer division rounded down to the nearest integer. + * + * Returns `None` if the divisor is `0n`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt, Option } from "effect" + * + * assert.deepStrictEqual(BigInt.divide(6n, 3n), Option.some(2n)) + * assert.deepStrictEqual(BigInt.divide(6n, 0n), Option.none()) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const divide: { + /** + * Provides a division operation on `bigint`s. + * + * If the dividend is not a multiple of the divisor the result will be a `bigint` value + * which represents the integer division rounded down to the nearest integer. + * + * Returns `None` if the divisor is `0n`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt, Option } from "effect" + * + * assert.deepStrictEqual(BigInt.divide(6n, 3n), Option.some(2n)) + * assert.deepStrictEqual(BigInt.divide(6n, 0n), Option.none()) + * ``` + * + * @category math + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => Option.Option + /** + * Provides a division operation on `bigint`s. + * + * If the dividend is not a multiple of the divisor the result will be a `bigint` value + * which represents the integer division rounded down to the nearest integer. + * + * Returns `None` if the divisor is `0n`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt, Option } from "effect" + * + * assert.deepStrictEqual(BigInt.divide(6n, 3n), Option.some(2n)) + * assert.deepStrictEqual(BigInt.divide(6n, 0n), Option.none()) + * ``` + * + * @category math + * @since 2.0.0 + */ + (self: bigint, that: bigint): Option.Option +} = dual( + 2, + (self: bigint, that: bigint): Option.Option => that === bigint0 ? Option.none() : Option.some(self / that) +) + +/** + * Provides a division operation on `bigint`s. + * + * If the dividend is not a multiple of the divisor the result will be a `bigint` value + * which represents the integer division rounded down to the nearest integer. + * + * Throws a `RangeError` if the divisor is `0n`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { unsafeDivide } from "effect/BigInt" + * + * assert.deepStrictEqual(unsafeDivide(6n, 3n), 2n) + * assert.deepStrictEqual(unsafeDivide(6n, 4n), 1n) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const unsafeDivide: { + /** + * Provides a division operation on `bigint`s. + * + * If the dividend is not a multiple of the divisor the result will be a `bigint` value + * which represents the integer division rounded down to the nearest integer. + * + * Throws a `RangeError` if the divisor is `0n`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { unsafeDivide } from "effect/BigInt" + * + * assert.deepStrictEqual(unsafeDivide(6n, 3n), 2n) + * assert.deepStrictEqual(unsafeDivide(6n, 4n), 1n) + * ``` + * + * @category math + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => bigint + /** + * Provides a division operation on `bigint`s. + * + * If the dividend is not a multiple of the divisor the result will be a `bigint` value + * which represents the integer division rounded down to the nearest integer. + * + * Throws a `RangeError` if the divisor is `0n`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { unsafeDivide } from "effect/BigInt" + * + * assert.deepStrictEqual(unsafeDivide(6n, 3n), 2n) + * assert.deepStrictEqual(unsafeDivide(6n, 4n), 1n) + * ``` + * + * @category math + * @since 2.0.0 + */ + (self: bigint, that: bigint): bigint +} = dual(2, (self: bigint, that: bigint): bigint => self / that) + +/** + * Returns the result of adding `1n` to a given number. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { increment } from "effect/BigInt" + * + * assert.deepStrictEqual(increment(2n), 3n) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const increment = (n: bigint): bigint => n + bigint1 + +/** + * Decrements a number by `1n`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { decrement } from "effect/BigInt" + * + * assert.deepStrictEqual(decrement(3n), 2n) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const decrement = (n: bigint): bigint => n - bigint1 + +/** + * @category instances + * @since 2.0.0 + */ +export const Equivalence: equivalence.Equivalence = equivalence.bigint + +/** + * @category instances + * @since 2.0.0 + */ +export const Order: order.Order = order.bigint + +/** + * Returns `true` if the first argument is less than the second, otherwise `false`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { lessThan } from "effect/BigInt" + * + * assert.deepStrictEqual(lessThan(2n, 3n), true) + * assert.deepStrictEqual(lessThan(3n, 3n), false) + * assert.deepStrictEqual(lessThan(4n, 3n), false) + * ``` + * + * @category predicates + * @since 2.0.0 + */ +export const lessThan: { + /** + * Returns `true` if the first argument is less than the second, otherwise `false`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { lessThan } from "effect/BigInt" + * + * assert.deepStrictEqual(lessThan(2n, 3n), true) + * assert.deepStrictEqual(lessThan(3n, 3n), false) + * assert.deepStrictEqual(lessThan(4n, 3n), false) + * ``` + * + * @category predicates + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => boolean + /** + * Returns `true` if the first argument is less than the second, otherwise `false`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { lessThan } from "effect/BigInt" + * + * assert.deepStrictEqual(lessThan(2n, 3n), true) + * assert.deepStrictEqual(lessThan(3n, 3n), false) + * assert.deepStrictEqual(lessThan(4n, 3n), false) + * ``` + * + * @category predicates + * @since 2.0.0 + */ + (self: bigint, that: bigint): boolean +} = order.lessThan(Order) + +/** + * Returns a function that checks if a given `bigint` is less than or equal to the provided one. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { lessThanOrEqualTo } from "effect/BigInt" + * + * assert.deepStrictEqual(lessThanOrEqualTo(2n, 3n), true) + * assert.deepStrictEqual(lessThanOrEqualTo(3n, 3n), true) + * assert.deepStrictEqual(lessThanOrEqualTo(4n, 3n), false) + * ``` + * + * @category predicates + * @since 2.0.0 + */ +export const lessThanOrEqualTo: { + /** + * Returns a function that checks if a given `bigint` is less than or equal to the provided one. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { lessThanOrEqualTo } from "effect/BigInt" + * + * assert.deepStrictEqual(lessThanOrEqualTo(2n, 3n), true) + * assert.deepStrictEqual(lessThanOrEqualTo(3n, 3n), true) + * assert.deepStrictEqual(lessThanOrEqualTo(4n, 3n), false) + * ``` + * + * @category predicates + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => boolean + /** + * Returns a function that checks if a given `bigint` is less than or equal to the provided one. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { lessThanOrEqualTo } from "effect/BigInt" + * + * assert.deepStrictEqual(lessThanOrEqualTo(2n, 3n), true) + * assert.deepStrictEqual(lessThanOrEqualTo(3n, 3n), true) + * assert.deepStrictEqual(lessThanOrEqualTo(4n, 3n), false) + * ``` + * + * @category predicates + * @since 2.0.0 + */ + (self: bigint, that: bigint): boolean +} = order.lessThanOrEqualTo(Order) + +/** + * Returns `true` if the first argument is greater than the second, otherwise `false`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { greaterThan } from "effect/BigInt" + * + * assert.deepStrictEqual(greaterThan(2n, 3n), false) + * assert.deepStrictEqual(greaterThan(3n, 3n), false) + * assert.deepStrictEqual(greaterThan(4n, 3n), true) + * ``` + * + * @category predicates + * @since 2.0.0 + */ +export const greaterThan: { + /** + * Returns `true` if the first argument is greater than the second, otherwise `false`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { greaterThan } from "effect/BigInt" + * + * assert.deepStrictEqual(greaterThan(2n, 3n), false) + * assert.deepStrictEqual(greaterThan(3n, 3n), false) + * assert.deepStrictEqual(greaterThan(4n, 3n), true) + * ``` + * + * @category predicates + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => boolean + /** + * Returns `true` if the first argument is greater than the second, otherwise `false`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { greaterThan } from "effect/BigInt" + * + * assert.deepStrictEqual(greaterThan(2n, 3n), false) + * assert.deepStrictEqual(greaterThan(3n, 3n), false) + * assert.deepStrictEqual(greaterThan(4n, 3n), true) + * ``` + * + * @category predicates + * @since 2.0.0 + */ + (self: bigint, that: bigint): boolean +} = order.greaterThan(Order) + +/** + * Returns a function that checks if a given `bigint` is greater than or equal to the provided one. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { greaterThanOrEqualTo } from "effect/BigInt" + * + * assert.deepStrictEqual(greaterThanOrEqualTo(2n, 3n), false) + * assert.deepStrictEqual(greaterThanOrEqualTo(3n, 3n), true) + * assert.deepStrictEqual(greaterThanOrEqualTo(4n, 3n), true) + * ``` + * + * @category predicates + * @since 2.0.0 + */ +export const greaterThanOrEqualTo: { + /** + * Returns a function that checks if a given `bigint` is greater than or equal to the provided one. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { greaterThanOrEqualTo } from "effect/BigInt" + * + * assert.deepStrictEqual(greaterThanOrEqualTo(2n, 3n), false) + * assert.deepStrictEqual(greaterThanOrEqualTo(3n, 3n), true) + * assert.deepStrictEqual(greaterThanOrEqualTo(4n, 3n), true) + * ``` + * + * @category predicates + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => boolean + /** + * Returns a function that checks if a given `bigint` is greater than or equal to the provided one. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { greaterThanOrEqualTo } from "effect/BigInt" + * + * assert.deepStrictEqual(greaterThanOrEqualTo(2n, 3n), false) + * assert.deepStrictEqual(greaterThanOrEqualTo(3n, 3n), true) + * assert.deepStrictEqual(greaterThanOrEqualTo(4n, 3n), true) + * ``` + * + * @category predicates + * @since 2.0.0 + */ + (self: bigint, that: bigint): boolean +} = order.greaterThanOrEqualTo(Order) + +/** + * Checks if a `bigint` is between a `minimum` and `maximum` value (inclusive). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt } from "effect" + * + * const between = BigInt.between({ minimum: 0n, maximum: 5n }) + * + * assert.deepStrictEqual(between(3n), true) + * assert.deepStrictEqual(between(-1n), false) + * assert.deepStrictEqual(between(6n), false) + * ``` + * + * @category predicates + * @since 2.0.0 + */ +export const between: { + /** + * Checks if a `bigint` is between a `minimum` and `maximum` value (inclusive). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt } from "effect" + * + * const between = BigInt.between({ minimum: 0n, maximum: 5n }) + * + * assert.deepStrictEqual(between(3n), true) + * assert.deepStrictEqual(between(-1n), false) + * assert.deepStrictEqual(between(6n), false) + * ``` + * + * @category predicates + * @since 2.0.0 + */ + ( + options: { + minimum: bigint + maximum: bigint + } + ): (self: bigint) => boolean + /** + * Checks if a `bigint` is between a `minimum` and `maximum` value (inclusive). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt } from "effect" + * + * const between = BigInt.between({ minimum: 0n, maximum: 5n }) + * + * assert.deepStrictEqual(between(3n), true) + * assert.deepStrictEqual(between(-1n), false) + * assert.deepStrictEqual(between(6n), false) + * ``` + * + * @category predicates + * @since 2.0.0 + */ + ( + self: bigint, + options: { + minimum: bigint + maximum: bigint + } + ): boolean +} = order.between(Order) + +/** + * Restricts the given `bigint` to be within the range specified by the `minimum` and `maximum` values. + * + * - If the `bigint` is less than the `minimum` value, the function returns the `minimum` value. + * - If the `bigint` is greater than the `maximum` value, the function returns the `maximum` value. + * - Otherwise, it returns the original `bigint`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt } from "effect" + * + * const clamp = BigInt.clamp({ minimum: 1n, maximum: 5n }) + * + * assert.equal(clamp(3n), 3n) + * assert.equal(clamp(0n), 1n) + * assert.equal(clamp(6n), 5n) + * ``` + * + * @since 2.0.0 + */ +export const clamp: { + /** + * Restricts the given `bigint` to be within the range specified by the `minimum` and `maximum` values. + * + * - If the `bigint` is less than the `minimum` value, the function returns the `minimum` value. + * - If the `bigint` is greater than the `maximum` value, the function returns the `maximum` value. + * - Otherwise, it returns the original `bigint`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt } from "effect" + * + * const clamp = BigInt.clamp({ minimum: 1n, maximum: 5n }) + * + * assert.equal(clamp(3n), 3n) + * assert.equal(clamp(0n), 1n) + * assert.equal(clamp(6n), 5n) + * ``` + * + * @since 2.0.0 + */ + ( + options: { + minimum: bigint + maximum: bigint + } + ): (self: bigint) => bigint + /** + * Restricts the given `bigint` to be within the range specified by the `minimum` and `maximum` values. + * + * - If the `bigint` is less than the `minimum` value, the function returns the `minimum` value. + * - If the `bigint` is greater than the `maximum` value, the function returns the `maximum` value. + * - Otherwise, it returns the original `bigint`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt } from "effect" + * + * const clamp = BigInt.clamp({ minimum: 1n, maximum: 5n }) + * + * assert.equal(clamp(3n), 3n) + * assert.equal(clamp(0n), 1n) + * assert.equal(clamp(6n), 5n) + * ``` + * + * @since 2.0.0 + */ + ( + self: bigint, + options: { + minimum: bigint + maximum: bigint + } + ): bigint +} = order.clamp(Order) + +/** + * Returns the minimum between two `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { min } from "effect/BigInt" + * + * assert.deepStrictEqual(min(2n, 3n), 2n) + * ``` + * + * @since 2.0.0 + */ +export const min: { + /** + * Returns the minimum between two `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { min } from "effect/BigInt" + * + * assert.deepStrictEqual(min(2n, 3n), 2n) + * ``` + * + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => bigint + /** + * Returns the minimum between two `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { min } from "effect/BigInt" + * + * assert.deepStrictEqual(min(2n, 3n), 2n) + * ``` + * + * @since 2.0.0 + */ + (self: bigint, that: bigint): bigint +} = order.min(Order) + +/** + * Returns the maximum between two `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { max } from "effect/BigInt" + * + * assert.deepStrictEqual(max(2n, 3n), 3n) + * ``` + * + * @since 2.0.0 + */ +export const max: { + /** + * Returns the maximum between two `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { max } from "effect/BigInt" + * + * assert.deepStrictEqual(max(2n, 3n), 3n) + * ``` + * + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => bigint + /** + * Returns the maximum between two `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { max } from "effect/BigInt" + * + * assert.deepStrictEqual(max(2n, 3n), 3n) + * ``` + * + * @since 2.0.0 + */ + (self: bigint, that: bigint): bigint +} = order.max(Order) + +/** + * Determines the sign of a given `bigint`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { sign } from "effect/BigInt" + * + * assert.deepStrictEqual(sign(-5n), -1) + * assert.deepStrictEqual(sign(0n), 0) + * assert.deepStrictEqual(sign(5n), 1) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const sign = (n: bigint): Ordering => Order(n, bigint0) + +/** + * Determines the absolute value of a given `bigint`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { abs } from "effect/BigInt" + * + * assert.deepStrictEqual(abs(-5n), 5n) + * assert.deepStrictEqual(abs(0n), 0n) + * assert.deepStrictEqual(abs(5n), 5n) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const abs = (n: bigint): bigint => (n < bigint0 ? -n : n) + +/** + * Determines the greatest common divisor of two `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { gcd } from "effect/BigInt" + * + * assert.deepStrictEqual(gcd(2n, 3n), 1n) + * assert.deepStrictEqual(gcd(2n, 4n), 2n) + * assert.deepStrictEqual(gcd(16n, 24n), 8n) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const gcd: { + /** + * Determines the greatest common divisor of two `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { gcd } from "effect/BigInt" + * + * assert.deepStrictEqual(gcd(2n, 3n), 1n) + * assert.deepStrictEqual(gcd(2n, 4n), 2n) + * assert.deepStrictEqual(gcd(16n, 24n), 8n) + * ``` + * + * @category math + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => bigint + /** + * Determines the greatest common divisor of two `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { gcd } from "effect/BigInt" + * + * assert.deepStrictEqual(gcd(2n, 3n), 1n) + * assert.deepStrictEqual(gcd(2n, 4n), 2n) + * assert.deepStrictEqual(gcd(16n, 24n), 8n) + * ``` + * + * @category math + * @since 2.0.0 + */ + (self: bigint, that: bigint): bigint +} = dual(2, (self: bigint, that: bigint): bigint => { + while (that !== bigint0) { + const t = that + that = self % that + self = t + } + return self +}) + +/** + * Determines the least common multiple of two `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { lcm } from "effect/BigInt" + * + * assert.deepStrictEqual(lcm(2n, 3n), 6n) + * assert.deepStrictEqual(lcm(2n, 4n), 4n) + * assert.deepStrictEqual(lcm(16n, 24n), 48n) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const lcm: { + /** + * Determines the least common multiple of two `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { lcm } from "effect/BigInt" + * + * assert.deepStrictEqual(lcm(2n, 3n), 6n) + * assert.deepStrictEqual(lcm(2n, 4n), 4n) + * assert.deepStrictEqual(lcm(16n, 24n), 48n) + * ``` + * + * @category math + * @since 2.0.0 + */ + (that: bigint): (self: bigint) => bigint + /** + * Determines the least common multiple of two `bigint`s. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { lcm } from "effect/BigInt" + * + * assert.deepStrictEqual(lcm(2n, 3n), 6n) + * assert.deepStrictEqual(lcm(2n, 4n), 4n) + * assert.deepStrictEqual(lcm(16n, 24n), 48n) + * ``` + * + * @category math + * @since 2.0.0 + */ + (self: bigint, that: bigint): bigint +} = dual(2, (self: bigint, that: bigint): bigint => (self * that) / gcd(self, that)) + +/** + * Determines the square root of a given `bigint` unsafely. Throws if the given `bigint` is negative. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { unsafeSqrt } from "effect/BigInt" + * + * assert.deepStrictEqual(unsafeSqrt(4n), 2n) + * assert.deepStrictEqual(unsafeSqrt(9n), 3n) + * assert.deepStrictEqual(unsafeSqrt(16n), 4n) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const unsafeSqrt = (n: bigint): bigint => { + if (n < bigint0) { + throw new RangeError("Cannot take the square root of a negative number") + } + if (n < bigint2) { + return n + } + let x = n / bigint2 + while (x * x > n) { + x = ((n / x) + x) / bigint2 + } + return x +} + +/** + * Determines the square root of a given `bigint` safely. Returns `none` if the given `bigint` is negative. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt, Option } from "effect" + * + * assert.deepStrictEqual(BigInt.sqrt(4n), Option.some(2n)) + * assert.deepStrictEqual(BigInt.sqrt(9n), Option.some(3n)) + * assert.deepStrictEqual(BigInt.sqrt(16n), Option.some(4n)) + * assert.deepStrictEqual(BigInt.sqrt(-1n), Option.none()) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const sqrt = (n: bigint): Option.Option => + greaterThanOrEqualTo(n, bigint0) ? Option.some(unsafeSqrt(n)) : Option.none() + +/** + * Takes an `Iterable` of `bigint`s and returns their sum as a single `bigint + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { sumAll } from "effect/BigInt" + * + * assert.deepStrictEqual(sumAll([2n, 3n, 4n]), 9n) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const sumAll = (collection: Iterable): bigint => { + let out = bigint0 + for (const n of collection) { + out += n + } + return out +} + +/** + * Takes an `Iterable` of `bigint`s and returns their multiplication as a single `number`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { multiplyAll } from "effect/BigInt" + * + * assert.deepStrictEqual(multiplyAll([2n, 3n, 4n]), 24n) + * ``` + * + * @category math + * @since 2.0.0 + */ +export const multiplyAll = (collection: Iterable): bigint => { + let out = bigint1 + for (const n of collection) { + if (n === bigint0) { + return bigint0 + } + out *= n + } + return out +} + +/** + * Takes a `bigint` and returns an `Option` of `number`. + * + * If the `bigint` is outside the safe integer range for JavaScript (`Number.MAX_SAFE_INTEGER` + * and `Number.MIN_SAFE_INTEGER`), it returns `Option.none()`. Otherwise, it converts the `bigint` + * to a number and returns `Option.some(number)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt as BI, Option } from "effect" + * + * assert.deepStrictEqual(BI.toNumber(BigInt(42)), Option.some(42)) + * assert.deepStrictEqual(BI.toNumber(BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1)), Option.none()) + * assert.deepStrictEqual(BI.toNumber(BigInt(Number.MIN_SAFE_INTEGER) - BigInt(1)), Option.none()) + * ``` + * + * @category conversions + * @since 2.0.0 + */ +export const toNumber = (b: bigint): Option.Option => { + if (b > BigInt(Number.MAX_SAFE_INTEGER) || b < BigInt(Number.MIN_SAFE_INTEGER)) { + return Option.none() + } + return Option.some(Number(b)) +} + +/** + * Takes a string and returns an `Option` of `bigint`. + * + * If the string is empty or contains characters that cannot be converted into a `bigint`, + * it returns `Option.none()`, otherwise, it returns `Option.some(bigint)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt as BI, Option } from "effect" + * + * assert.deepStrictEqual(BI.fromString("42"), Option.some(BigInt(42))) + * assert.deepStrictEqual(BI.fromString(" "), Option.none()) + * assert.deepStrictEqual(BI.fromString("a"), Option.none()) + * ``` + * + * @category conversions + * @since 2.4.12 + */ +export const fromString = (s: string): Option.Option => { + try { + return s.trim() === "" + ? Option.none() + : Option.some(BigInt(s)) + } catch { + return Option.none() + } +} + +/** + * Takes a number and returns an `Option` of `bigint`. + * + * If the number is outside the safe integer range for JavaScript (`Number.MAX_SAFE_INTEGER` + * and `Number.MIN_SAFE_INTEGER`), it returns `Option.none()`. Otherwise, it attempts to + * convert the number to a `bigint` and returns `Option.some(bigint)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { BigInt as BI, Option } from "effect" + * + * assert.deepStrictEqual(BI.fromNumber(42), Option.some(BigInt(42))) + * assert.deepStrictEqual(BI.fromNumber(Number.MAX_SAFE_INTEGER + 1), Option.none()) + * assert.deepStrictEqual(BI.fromNumber(Number.MIN_SAFE_INTEGER - 1), Option.none()) + * ``` + * + * @category conversions + * @since 2.4.12 + */ +export const fromNumber = (n: number): Option.Option => { + if (n > Number.MAX_SAFE_INTEGER || n < Number.MIN_SAFE_INTEGER) { + return Option.none() + } + + try { + return Option.some(BigInt(n)) + } catch { + return Option.none() + } +} diff --git a/backend/node_modules/effect/src/Boolean.ts b/backend/node_modules/effect/src/Boolean.ts new file mode 100644 index 0000000000000000000000000000000000000000..57c59b2de4d684df3c9f8f8cba21bdb8c28a3549 --- /dev/null +++ b/backend/node_modules/effect/src/Boolean.ts @@ -0,0 +1,560 @@ +/** + * This module provides utility functions and type class instances for working with the `boolean` type in TypeScript. + * It includes functions for basic boolean operations, as well as type class instances for + * `Equivalence` and `Order`. + * + * @since 2.0.0 + */ +import * as equivalence from "./Equivalence.js" +import type { LazyArg } from "./Function.js" +import { dual } from "./Function.js" +import * as order from "./Order.js" +import * as predicate from "./Predicate.js" + +/** + * Tests if a value is a `boolean`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isBoolean } from "effect/Boolean" + * + * assert.deepStrictEqual(isBoolean(true), true) + * assert.deepStrictEqual(isBoolean("true"), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isBoolean: (input: unknown) => input is boolean = predicate.isBoolean + +/** + * This function returns the result of either of the given functions depending on the value of the boolean parameter. + * It is useful when you have to run one of two functions depending on the boolean value. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Boolean } from "effect" + * + * assert.deepStrictEqual(Boolean.match(true, { onFalse: () => "It's false!", onTrue: () => "It's true!" }), "It's true!") + * ``` + * + * @category pattern matching + * @since 2.0.0 + */ +export const match: { + /** + * This function returns the result of either of the given functions depending on the value of the boolean parameter. + * It is useful when you have to run one of two functions depending on the boolean value. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Boolean } from "effect" + * + * assert.deepStrictEqual(Boolean.match(true, { onFalse: () => "It's false!", onTrue: () => "It's true!" }), "It's true!") + * ``` + * + * @category pattern matching + * @since 2.0.0 + */ + ( + options: { + readonly onFalse: LazyArg + readonly onTrue: LazyArg + } + ): (value: boolean) => A | B + /** + * This function returns the result of either of the given functions depending on the value of the boolean parameter. + * It is useful when you have to run one of two functions depending on the boolean value. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Boolean } from "effect" + * + * assert.deepStrictEqual(Boolean.match(true, { onFalse: () => "It's false!", onTrue: () => "It's true!" }), "It's true!") + * ``` + * + * @category pattern matching + * @since 2.0.0 + */ + ( + value: boolean, + options: { + readonly onFalse: LazyArg + readonly onTrue: LazyArg + } + ): A | B +} = dual(2, (value: boolean, options: { + readonly onFalse: LazyArg + readonly onTrue: LazyArg +}): A | B => value ? options.onTrue() : options.onFalse()) + +/** + * @category instances + * @since 2.0.0 + */ +export const Equivalence: equivalence.Equivalence = equivalence.boolean + +/** + * @category instances + * @since 2.0.0 + */ +export const Order: order.Order = order.boolean + +/** + * Negates the given boolean: `!self` + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { not } from "effect/Boolean" + * + * assert.deepStrictEqual(not(true), false) + * assert.deepStrictEqual(not(false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const not = (self: boolean): boolean => !self + +/** + * Combines two boolean using AND: `self && that`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { and } from "effect/Boolean" + * + * assert.deepStrictEqual(and(true, true), true) + * assert.deepStrictEqual(and(true, false), false) + * assert.deepStrictEqual(and(false, true), false) + * assert.deepStrictEqual(and(false, false), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const and: { + /** + * Combines two boolean using AND: `self && that`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { and } from "effect/Boolean" + * + * assert.deepStrictEqual(and(true, true), true) + * assert.deepStrictEqual(and(true, false), false) + * assert.deepStrictEqual(and(false, true), false) + * assert.deepStrictEqual(and(false, false), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: boolean): (self: boolean) => boolean + /** + * Combines two boolean using AND: `self && that`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { and } from "effect/Boolean" + * + * assert.deepStrictEqual(and(true, true), true) + * assert.deepStrictEqual(and(true, false), false) + * assert.deepStrictEqual(and(false, true), false) + * assert.deepStrictEqual(and(false, false), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: boolean, that: boolean): boolean +} = dual(2, (self: boolean, that: boolean): boolean => self && that) + +/** + * Combines two boolean using NAND: `!(self && that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { nand } from "effect/Boolean" + * + * assert.deepStrictEqual(nand(true, true), false) + * assert.deepStrictEqual(nand(true, false), true) + * assert.deepStrictEqual(nand(false, true), true) + * assert.deepStrictEqual(nand(false, false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const nand: { + /** + * Combines two boolean using NAND: `!(self && that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { nand } from "effect/Boolean" + * + * assert.deepStrictEqual(nand(true, true), false) + * assert.deepStrictEqual(nand(true, false), true) + * assert.deepStrictEqual(nand(false, true), true) + * assert.deepStrictEqual(nand(false, false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: boolean): (self: boolean) => boolean + /** + * Combines two boolean using NAND: `!(self && that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { nand } from "effect/Boolean" + * + * assert.deepStrictEqual(nand(true, true), false) + * assert.deepStrictEqual(nand(true, false), true) + * assert.deepStrictEqual(nand(false, true), true) + * assert.deepStrictEqual(nand(false, false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: boolean, that: boolean): boolean +} = dual(2, (self: boolean, that: boolean): boolean => !(self && that)) + +/** + * Combines two boolean using OR: `self || that`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { or } from "effect/Boolean" + * + * assert.deepStrictEqual(or(true, true), true) + * assert.deepStrictEqual(or(true, false), true) + * assert.deepStrictEqual(or(false, true), true) + * assert.deepStrictEqual(or(false, false), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const or: { + /** + * Combines two boolean using OR: `self || that`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { or } from "effect/Boolean" + * + * assert.deepStrictEqual(or(true, true), true) + * assert.deepStrictEqual(or(true, false), true) + * assert.deepStrictEqual(or(false, true), true) + * assert.deepStrictEqual(or(false, false), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: boolean): (self: boolean) => boolean + /** + * Combines two boolean using OR: `self || that`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { or } from "effect/Boolean" + * + * assert.deepStrictEqual(or(true, true), true) + * assert.deepStrictEqual(or(true, false), true) + * assert.deepStrictEqual(or(false, true), true) + * assert.deepStrictEqual(or(false, false), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: boolean, that: boolean): boolean +} = dual(2, (self: boolean, that: boolean): boolean => self || that) + +/** + * Combines two booleans using NOR: `!(self || that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { nor } from "effect/Boolean" + * + * assert.deepStrictEqual(nor(true, true), false) + * assert.deepStrictEqual(nor(true, false), false) + * assert.deepStrictEqual(nor(false, true), false) + * assert.deepStrictEqual(nor(false, false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const nor: { + /** + * Combines two booleans using NOR: `!(self || that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { nor } from "effect/Boolean" + * + * assert.deepStrictEqual(nor(true, true), false) + * assert.deepStrictEqual(nor(true, false), false) + * assert.deepStrictEqual(nor(false, true), false) + * assert.deepStrictEqual(nor(false, false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: boolean): (self: boolean) => boolean + /** + * Combines two booleans using NOR: `!(self || that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { nor } from "effect/Boolean" + * + * assert.deepStrictEqual(nor(true, true), false) + * assert.deepStrictEqual(nor(true, false), false) + * assert.deepStrictEqual(nor(false, true), false) + * assert.deepStrictEqual(nor(false, false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: boolean, that: boolean): boolean +} = dual(2, (self: boolean, that: boolean): boolean => !(self || that)) + +/** + * Combines two booleans using XOR: `(!self && that) || (self && !that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { xor } from "effect/Boolean" + * + * assert.deepStrictEqual(xor(true, true), false) + * assert.deepStrictEqual(xor(true, false), true) + * assert.deepStrictEqual(xor(false, true), true) + * assert.deepStrictEqual(xor(false, false), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const xor: { + /** + * Combines two booleans using XOR: `(!self && that) || (self && !that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { xor } from "effect/Boolean" + * + * assert.deepStrictEqual(xor(true, true), false) + * assert.deepStrictEqual(xor(true, false), true) + * assert.deepStrictEqual(xor(false, true), true) + * assert.deepStrictEqual(xor(false, false), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: boolean): (self: boolean) => boolean + /** + * Combines two booleans using XOR: `(!self && that) || (self && !that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { xor } from "effect/Boolean" + * + * assert.deepStrictEqual(xor(true, true), false) + * assert.deepStrictEqual(xor(true, false), true) + * assert.deepStrictEqual(xor(false, true), true) + * assert.deepStrictEqual(xor(false, false), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: boolean, that: boolean): boolean +} = dual(2, (self: boolean, that: boolean): boolean => (!self && that) || (self && !that)) + +/** + * Combines two booleans using EQV (aka XNOR): `!xor(self, that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { eqv } from "effect/Boolean" + * + * assert.deepStrictEqual(eqv(true, true), true) + * assert.deepStrictEqual(eqv(true, false), false) + * assert.deepStrictEqual(eqv(false, true), false) + * assert.deepStrictEqual(eqv(false, false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const eqv: { + /** + * Combines two booleans using EQV (aka XNOR): `!xor(self, that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { eqv } from "effect/Boolean" + * + * assert.deepStrictEqual(eqv(true, true), true) + * assert.deepStrictEqual(eqv(true, false), false) + * assert.deepStrictEqual(eqv(false, true), false) + * assert.deepStrictEqual(eqv(false, false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: boolean): (self: boolean) => boolean + /** + * Combines two booleans using EQV (aka XNOR): `!xor(self, that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { eqv } from "effect/Boolean" + * + * assert.deepStrictEqual(eqv(true, true), true) + * assert.deepStrictEqual(eqv(true, false), false) + * assert.deepStrictEqual(eqv(false, true), false) + * assert.deepStrictEqual(eqv(false, false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: boolean, that: boolean): boolean +} = dual(2, (self: boolean, that: boolean): boolean => !xor(self, that)) + +/** + * Combines two booleans using an implication: `(!self || that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { implies } from "effect/Boolean" + * + * assert.deepStrictEqual(implies(true, true), true) + * assert.deepStrictEqual(implies(true, false), false) + * assert.deepStrictEqual(implies(false, true), true) + * assert.deepStrictEqual(implies(false, false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const implies: { + /** + * Combines two booleans using an implication: `(!self || that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { implies } from "effect/Boolean" + * + * assert.deepStrictEqual(implies(true, true), true) + * assert.deepStrictEqual(implies(true, false), false) + * assert.deepStrictEqual(implies(false, true), true) + * assert.deepStrictEqual(implies(false, false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: boolean): (self: boolean) => boolean + /** + * Combines two booleans using an implication: `(!self || that)`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { implies } from "effect/Boolean" + * + * assert.deepStrictEqual(implies(true, true), true) + * assert.deepStrictEqual(implies(true, false), false) + * assert.deepStrictEqual(implies(false, true), true) + * assert.deepStrictEqual(implies(false, false), true) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: boolean, that: boolean): boolean +} = dual(2, (self, that) => self ? that : true) + +/** + * This utility function is used to check if all the elements in a collection of boolean values are `true`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { every } from "effect/Boolean" + * + * assert.deepStrictEqual(every([true, true, true]), true) + * assert.deepStrictEqual(every([true, false, true]), false) + * ``` + * + * @since 2.0.0 + */ +export const every = (collection: Iterable): boolean => { + for (const b of collection) { + if (!b) { + return false + } + } + return true +} + +/** + * This utility function is used to check if at least one of the elements in a collection of boolean values is `true`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { some } from "effect/Boolean" + * + * assert.deepStrictEqual(some([true, false, true]), true) + * assert.deepStrictEqual(some([false, false, false]), false) + * ``` + * + * @since 2.0.0 + */ +export const some = (collection: Iterable): boolean => { + for (const b of collection) { + if (b) { + return true + } + } + return false +} diff --git a/backend/node_modules/effect/src/Cache.ts b/backend/node_modules/effect/src/Cache.ts new file mode 100644 index 0000000000000000000000000000000000000000..876a92dfb9a83c42e983fc1b693158515afba6d0 --- /dev/null +++ b/backend/node_modules/effect/src/Cache.ts @@ -0,0 +1,281 @@ +/** + * @since 2.0.0 + */ +import type * as Duration from "./Duration.js" +import type * as Effect from "./Effect.js" +import type { Either } from "./Either.js" +import type * as Exit from "./Exit.js" +import * as internal from "./internal/cache.js" +import type * as Option from "./Option.js" +import type * as Predicate from "./Predicate.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const CacheTypeId: unique symbol = internal.CacheTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type CacheTypeId = typeof CacheTypeId + +/** + * @since 3.6.4 + * @category symbols + */ +export const ConsumerCacheTypeId: unique symbol = internal.ConsumerCacheTypeId + +/** + * @since 3.6.4 + * @category symbols + */ +export type ConsumerCacheTypeId = typeof ConsumerCacheTypeId + +/** + * A `Cache` is defined in terms of a lookup function that, given a key of + * type `Key`, can either fail with an error of type `Error` or succeed with a + * value of type `Value`. Getting a value from the cache will either return + * the previous result of the lookup function if it is available or else + * compute a new result with the lookup function, put it in the cache, and + * return it. + * + * A cache also has a specified capacity and time to live. When the cache is + * at capacity the least recently accessed values in the cache will be + * removed to make room for new values. Getting a value with a life older than + * the specified time to live will result in a new value being computed with + * the lookup function and returned when available. + * + * The cache is safe for concurrent access. If multiple fibers attempt to get + * the same key the lookup function will only be computed once and the result + * will be returned to all fibers. + * + * @since 2.0.0 + * @category models + */ +export interface Cache + extends ConsumerCache, Cache.Variance +{ + /** + * Retrieves the value associated with the specified key if it exists. + * Otherwise computes the value with the lookup function, puts it in the + * cache, and returns it. + */ + get(key: Key): Effect.Effect + + /** + * Retrieves the value associated with the specified key if it exists as a left. + * Otherwise computes the value with the lookup function, puts it in the + * cache, and returns it as a right. + */ + getEither(key: Key): Effect.Effect, Error> + + /** + * Computes the value associated with the specified key, with the lookup + * function, and puts it in the cache. The difference between this and + * `get` method is that `refresh` triggers (re)computation of the value + * without invalidating it in the cache, so any request to the associated + * key can still be served while the value is being re-computed/retrieved + * by the lookup function. Additionally, `refresh` always triggers the + * lookup function, disregarding the last `Error`. + */ + refresh(key: Key): Effect.Effect + + /** + * Associates the specified value with the specified key in the cache. + */ + set(key: Key, value: Value): Effect.Effect +} + +/** + * A ConsumerCache models a portion of a cache which is safe to share without allowing to create new values or access existing ones. + * + * It can be used safely to give over control for request management without leaking writer side details. + * + * @since 2.0.0 + * @category models + */ +export interface ConsumerCache + extends Cache.ConsumerVariance +{ + /** + * Retrieves the value associated with the specified key if it exists. + * Otherwise returns `Option.none`. + */ + getOption(key: Key): Effect.Effect, Error> + + /** + * Retrieves the value associated with the specified key if it exists and the + * lookup function has completed. Otherwise returns `Option.none`. + */ + getOptionComplete(key: Key): Effect.Effect> + + /** + * Returns statistics for this cache. + */ + readonly cacheStats: Effect.Effect + + /** + * Returns whether a value associated with the specified key exists in the + * cache. + */ + contains(key: Key): Effect.Effect + + /** + * Returns statistics for the specified entry. + */ + entryStats(key: Key): Effect.Effect> + + /** + * Invalidates the value associated with the specified key. + */ + invalidate(key: Key): Effect.Effect + + /** + * Invalidates the value associated with the specified key if the predicate holds. + */ + invalidateWhen(key: Key, predicate: Predicate.Predicate): Effect.Effect + + /** + * Invalidates all values in the cache. + */ + readonly invalidateAll: Effect.Effect + + /** + * Returns the approximate number of values in the cache. + */ + readonly size: Effect.Effect + + /** + * Returns an approximation of the values in the cache. + */ + readonly keys: Effect.Effect> + + /** + * Returns an approximation of the values in the cache. + */ + readonly values: Effect.Effect> + + /** + * Returns an approximation of the values in the cache. + */ + readonly entries: Effect.Effect> +} + +/** + * @since 2.0.0 + */ +export declare namespace Cache { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [CacheTypeId]: { + readonly _Key: Types.Invariant + readonly _Error: Types.Covariant + readonly _Value: Types.Invariant + } + } + /** + * @since 3.6.4 + * @category models + */ + export interface ConsumerVariance { + readonly [ConsumerCacheTypeId]: { + readonly _Key: Types.Invariant + readonly _Error: Types.Covariant + readonly _Value: Types.Covariant + } + } +} + +/** + * Constructs a new cache with the specified capacity, time to live, and + * lookup function. + * + * @since 2.0.0 + * @category constructors + */ +export const make: ( + options: { + readonly capacity: number + readonly timeToLive: Duration.DurationInput + readonly lookup: Lookup + } +) => Effect.Effect, never, Environment> = internal.make + +/** + * Constructs a new cache with the specified capacity, time to live, and + * lookup function, where the time to live can depend on the `Exit` value + * returned by the lookup function. + * + * @since 2.0.0 + * @category constructors + */ +export const makeWith: ( + options: { + readonly capacity: number + readonly lookup: Lookup + readonly timeToLive: (exit: Exit.Exit) => Duration.DurationInput + } +) => Effect.Effect, never, Environment> = internal.makeWith + +/** + * `CacheStats` represents a snapshot of statistics for the cache as of a + * point in time. + * + * @since 2.0.0 + * @category models + */ +export interface CacheStats { + readonly hits: number + readonly misses: number + readonly size: number +} + +/** + * Constructs a new `CacheStats` from the specified values. + * + * @since 2.0.0 + * @category constructors + */ +export const makeCacheStats: ( + options: { + readonly hits: number + readonly misses: number + readonly size: number + } +) => CacheStats = internal.makeCacheStats + +/** + * Represents a snapshot of statistics for an entry in the cache. + * + * @since 2.0.0 + * @category models + */ +export interface EntryStats { + readonly loadedMillis: number +} + +/** + * Constructs a new `EntryStats` from the specified values. + * + * @since 2.0.0 + * @category constructors + */ +export const makeEntryStats: (loadedMillis: number) => EntryStats = internal.makeEntryStats + +/** + * A `Lookup` represents a lookup function that, given a key of type `Key`, can + * return an effect that will either produce a value of type `Value` or fail + * with an error of type `Error` using an environment of type `Environment`. + * + * @since 2.0.0 + * @category models + */ +export type Lookup = ( + key: Key +) => Effect.Effect diff --git a/backend/node_modules/effect/src/Cause.ts b/backend/node_modules/effect/src/Cause.ts new file mode 100644 index 0000000000000000000000000000000000000000..8664f7bd21bf87d46ff04840bb1ee98a4d2d7467 --- /dev/null +++ b/backend/node_modules/effect/src/Cause.ts @@ -0,0 +1,2049 @@ +/** + * The `Effect` type is polymorphic in values of type `E` and we can + * work with any error type that we want. However, there is a lot of information + * that is not inside an arbitrary `E` value. So as a result, an `Effect` needs + * somewhere to store things like unexpected errors or defects, stack and + * execution traces, causes of fiber interruptions, and so forth. + * + * Effect-TS is very strict about preserving the full information related to a + * failure. It captures all type of errors into the `Cause` data type. `Effect` + * uses the `Cause` data type to store the full story of failure. So its + * error model is lossless. It doesn't throw information related to the failure + * result. So we can figure out exactly what happened during the operation of + * our effects. + * + * It is important to note that `Cause` is an underlying data type representing + * errors occuring within an `Effect` workflow. Thus, we don't usually deal with + * `Cause`s directly. Even though it is not a data type that we deal with very + * often, the `Cause` of a failing `Effect` workflow can be accessed at any + * time, which gives us total access to all parallel and sequential errors in + * occurring within our codebase. + * + * @since 2.0.0 + */ +import type * as Channel from "./Channel.js" +import type * as Chunk from "./Chunk.js" +import type * as Effect from "./Effect.js" +import type * as Either from "./Either.js" +import type * as Equal from "./Equal.js" +import type * as FiberId from "./FiberId.js" +import type * as HashSet from "./HashSet.js" +import type { Inspectable } from "./Inspectable.js" +import * as internal from "./internal/cause.js" +import * as core from "./internal/core.js" +import type * as Option from "./Option.js" +import type { Pipeable } from "./Pipeable.js" +import type { Predicate, Refinement } from "./Predicate.js" +import type * as Sink from "./Sink.js" +import type * as Stream from "./Stream.js" +import type { Span } from "./Tracer.js" +import type { Covariant, NoInfer } from "./Types.js" + +/** + * A unique symbol identifying the `Cause` type. + * + * **Details** + * + * This provides a symbol that helps identify instances of the `Cause` data + * type. This can be used for advanced operations such as refining types or + * building internal utilities that check whether an unknown value is a `Cause`. + * + * @see {@link isCause} Check if a value is a `Cause` + * + * @since 2.0.0 + * @category Symbols + */ +export const CauseTypeId: unique symbol = internal.CauseTypeId + +/** + * @since 2.0.0 + * @category Symbols + */ +export type CauseTypeId = typeof CauseTypeId + +/** + * A unique symbol identifying the `RuntimeException` type. + * + * **Details** + * + * This provides a symbol that identifies a `RuntimeException`. This is + * typically used internally by the library to recognize checked exceptions that + * occur during runtime. + * + * @see {@link RuntimeException} Create or work with a `RuntimeException` + * + * @since 2.0.0 + * @category Symbols + */ +export const RuntimeExceptionTypeId: unique symbol = core.RuntimeExceptionTypeId + +/** + * @since 2.0.0 + * @category Symbols + */ +export type RuntimeExceptionTypeId = typeof RuntimeExceptionTypeId + +/** + * A unique symbol identifying the `InterruptedException` type. + * + * **Details** + * + * This provides a symbol that identifies an `InterruptedException`. This is + * typically used internally to recognize when a fiber has been interrupted, + * helping the framework handle interruption logic correctly. + * + * @see {@link InterruptedException} Create or work with an `InterruptedException` + * + * @since 2.0.0 + * @category Symbols + */ +export const InterruptedExceptionTypeId: unique symbol = core.InterruptedExceptionTypeId + +/** + * @since 2.0.0 + * @category Symbols + */ +export type InterruptedExceptionTypeId = typeof InterruptedExceptionTypeId + +/** + * A unique symbol identifying the `IllegalArgumentException` type. + * + * **Details** + * + * This provides a symbol that identifies an `IllegalArgumentException`. This is + * often used in scenarios where invalid arguments are supplied to methods that + * expect specific input. + * + * @see {@link IllegalArgumentException} Create or work with an `IllegalArgumentException` + * + * @since 2.0.0 + * @category Symbols + */ +export const IllegalArgumentExceptionTypeId: unique symbol = core.IllegalArgumentExceptionTypeId + +/** + * @since 2.0.0 + * @category Symbols + */ +export type IllegalArgumentExceptionTypeId = typeof IllegalArgumentExceptionTypeId + +/** + * A unique symbol identifying the `NoSuchElementException` type. + * + * **Details** + * + * This provides a symbol that identifies a `NoSuchElementException`. It helps + * differentiate cases where a required element is missing within a data + * structure. + * + * @see {@link NoSuchElementException} Create or work with a `NoSuchElementException` + * + * @since 2.0.0 + * @category Symbols + */ +export const NoSuchElementExceptionTypeId: unique symbol = core.NoSuchElementExceptionTypeId + +/** + * @since 2.0.0 + * @category Symbols + */ +export type NoSuchElementExceptionTypeId = typeof NoSuchElementExceptionTypeId + +/** + * A unique symbol identifying the `InvalidPubSubCapacityException` type. + * + * **Details** + * + * This provides a symbol that identifies an `InvalidPubSubCapacityException`. + * It indicates an error related to an invalid capacity passed to a `PubSub` + * structure. + * + * @see {@link InvalidPubSubCapacityException} Create or work with an `InvalidPubSubCapacityException` + * + * @since 2.0.0 + * @category Symbols + */ +export const InvalidPubSubCapacityExceptionTypeId: unique symbol = core.InvalidPubSubCapacityExceptionTypeId + +/** + * @since 2.0.0 + * @category Symbols + */ +export type InvalidPubSubCapacityExceptionTypeId = typeof InvalidPubSubCapacityExceptionTypeId + +/** + * A unique symbol identifying the `ExceededCapacityException` type. + * + * **Details** + * + * This provides a symbol that identifies an `ExceededCapacityException`. It + * denotes situations where a resource has exceeded its configured capacity + * limit. + * + * @see {@link ExceededCapacityException} Create or work with an `ExceededCapacityException` + * + * @since 3.5.0 + * @category Symbols + */ +export const ExceededCapacityExceptionTypeId: unique symbol = core.ExceededCapacityExceptionTypeId + +/** + * @since 3.5.0 + * @category Symbols + */ +export type ExceededCapacityExceptionTypeId = typeof ExceededCapacityExceptionTypeId + +/** + * A unique symbol identifying the `TimeoutException` type. + * + * **Details** + * + * This provides a symbol that identifies a `TimeoutException`. It helps the + * framework recognize errors related to operations that fail to complete within + * a given timeframe. + * + * @see {@link TimeoutException} Create or work with a `TimeoutException` + * + * @since 2.0.0 + * @category Symbols + */ +export const TimeoutExceptionTypeId: unique symbol = core.TimeoutExceptionTypeId + +/** + * @since 2.0.0 + * @category Symbols + */ +export type TimeoutExceptionTypeId = typeof TimeoutExceptionTypeId + +/** + * A unique symbol identifying the `UnknownException` type. + * + * **Details** + * + * This provides a symbol that identifies an `UnknownException`. It is typically + * used for generic or unexpected errors that do not fit other specific + * exception categories. + * + * @see {@link UnknownException} Create or work with an `UnknownException` + * + * @since 2.0.0 + * @category Symbols + */ +export const UnknownExceptionTypeId: unique symbol = core.UnknownExceptionTypeId + +/** + * @since 2.0.0 + * @category Symbols + */ +export type UnknownExceptionTypeId = typeof UnknownExceptionTypeId + +/** + * Represents the full history of a failure within an `Effect`. + * + * **Details** + * + * This type is a data structure that captures all information about why and how + * an effect has failed, including parallel errors, sequential errors, defects, + * and interruptions. It enables a "lossless" error model: no error-related + * information is discarded, which helps in debugging and understanding the root + * cause of failures. + * + * @since 2.0.0 + * @category Models + */ +export type Cause = + | Empty + | Fail + | Die + | Interrupt + | Sequential + | Parallel + +/** + * @since 2.0.0 + */ +export declare namespace Cause { + /** + * This interface is used internally to manage the type variance of `Cause`. + * + * @since 2.0.0 + * @category Models + */ + export interface Variance { + readonly [CauseTypeId]: { + readonly _E: Covariant + } + } +} + +/** + * Describes methods for reducing a `Cause` into a value of type `Z` with + * access to contextual information. + * + * **Details** + * + * This interface is meant for advanced transformations of `Cause`. By + * implementing each method, you can define how different parts of the `Cause` + * structure (like `Fail`, `Die`, or `Interrupt`) should be transformed into a + * final type `Z`. The `context` parameter carries additional data needed during + * this reduction. + * + * @see {@link reduceWithContext} Apply a `CauseReducer` to transform a `Cause` + * + * @since 2.0.0 + * @category Models + */ +export interface CauseReducer { + emptyCase(context: C): Z + failCase(context: C, error: E): Z + dieCase(context: C, defect: unknown): Z + interruptCase(context: C, fiberId: FiberId.FiberId): Z + sequentialCase(context: C, left: Z, right: Z): Z + parallelCase(context: C, left: Z, right: Z): Z +} + +/** + * Represents an error object that can be yielded in `Effect.gen`. + * + * @since 2.0.0 + * @category Models + */ +export interface YieldableError extends Pipeable, Inspectable, Error { + readonly [Effect.EffectTypeId]: Effect.Effect.VarianceStruct + readonly [Stream.StreamTypeId]: Stream.Stream.VarianceStruct + readonly [Sink.SinkTypeId]: Sink.Sink.VarianceStruct + readonly [Channel.ChannelTypeId]: Channel.Channel.VarianceStruct + [Symbol.iterator](): Effect.EffectGenerator> +} + +/** + * Creates an error that occurs at runtime, extendable for other exception + * types. + * + * @since 2.0.0 + * @category Errors + */ +export const YieldableError: new(message?: string | undefined) => YieldableError = core.YieldableError + +/** + * An error representing a runtime error. + * + * **Details** + * + * This interface is used for errors that occur at runtime but are still + * considered recoverable or typed. + * + * @since 2.0.0 + * @category Models + */ +export interface RuntimeException extends YieldableError { + readonly _tag: "RuntimeException" + readonly [RuntimeExceptionTypeId]: RuntimeExceptionTypeId +} + +/** + * An error representing fiber interruption. + * + * **Details** + * + * This interface represents errors that occur when a fiber is forcefully + * interrupted. Interruption can happen for various reasons, including + * cancellations or system directives to halt operations. Code that deals with + * concurrency might need to catch or handle these to ensure proper cleanup. + * + * @since 2.0.0 + * @category Models + */ +export interface InterruptedException extends YieldableError { + readonly _tag: "InterruptedException" + readonly [InterruptedExceptionTypeId]: InterruptedExceptionTypeId +} + +/** + * An error representing an invalid argument passed to a method. + * + * **Details** + * + * This interface is used for signaling that a function or method received an + * argument that does not meet its preconditions. + * + * @since 2.0.0 + * @category Models + */ +export interface IllegalArgumentException extends YieldableError { + readonly _tag: "IllegalArgumentException" + readonly [IllegalArgumentExceptionTypeId]: IllegalArgumentExceptionTypeId +} + +/** + * An error that occurs when an expected element is missing. + * + * **Details** + * + * This interface indicates scenarios like looking up an item in a collection + * or searching for data that should be present but isn't. It helps your code + * signal a more specific issue rather than a general error. + * + * @since 2.0.0 + * @category Models + */ +export interface NoSuchElementException extends YieldableError { + readonly _tag: "NoSuchElementException" + readonly [NoSuchElementExceptionTypeId]: NoSuchElementExceptionTypeId +} + +/** + * An error indicating invalid capacity for a `PubSub`. + * + * @since 2.0.0 + * @category Models + */ +export interface InvalidPubSubCapacityException extends YieldableError { + readonly _tag: "InvalidPubSubCapacityException" + readonly [InvalidPubSubCapacityExceptionTypeId]: InvalidPubSubCapacityExceptionTypeId +} + +/** + * An error that occurs when resource capacity is exceeded. + * + * @since 3.5.0 + * @category Models + */ +export interface ExceededCapacityException extends YieldableError { + readonly _tag: "ExceededCapacityException" + readonly [ExceededCapacityExceptionTypeId]: ExceededCapacityExceptionTypeId +} + +/** + * An error representing a computation that timed out. + * + * @since 2.0.0 + * @category Models + */ +export interface TimeoutException extends YieldableError { + readonly _tag: "TimeoutException" + readonly [TimeoutExceptionTypeId]: TimeoutExceptionTypeId +} + +/** + * A checked exception for handling unknown or unexpected errors. + * + * **Details** + * + * This interface captures errors that don't fall under known categories. It is + * especially helpful for wrapping low-level or third-party library errors that + * might provide little or no context, such as from a rejected promise. + * + * @since 2.0.0 + * @category Models + */ +export interface UnknownException extends YieldableError { + readonly _tag: "UnknownException" + readonly [UnknownExceptionTypeId]: UnknownExceptionTypeId + readonly error: unknown +} + +/** + * Represents a lack of errors within a `Cause`. + * + * @see {@link empty} Construct a new `Empty` cause + * @see {@link isEmptyType} Check if a `Cause` is an `Empty` type + * + * @since 2.0.0 + * @category Models + */ +export interface Empty extends Cause.Variance, Equal.Equal, Pipeable, Inspectable { + readonly _tag: "Empty" +} + +/** + * Represents an expected error within a `Cause`. + * + * **Details** + * + * This interface models a `Cause` that carries an expected or known error of + * type `E`. For example, if you validate user input and find it invalid, you + * might store that error within a `Fail`. + * + * @see {@link fail} Construct a `Fail` cause + * @see {@link isFailType} Check if a `Cause` is a `Fail` + * + * @since 2.0.0 + * @category Models + */ +export interface Fail extends Cause.Variance, Equal.Equal, Pipeable, Inspectable { + readonly _tag: "Fail" + readonly error: E +} + +/** + * Represents an unexpected defect within a `Cause`. + * + * **Details** + * + * This interface models a `Cause` for errors that are typically unrecoverable or + * unanticipated—like runtime exceptions or bugs. When code "dies," it indicates a + * severe failure that wasn't accounted for. + * + * @see {@link die} Construct a `Die` cause + * @see {@link isDieType} Check if a `Cause` is a `Die` + * + * @since 2.0.0 + * @category Models + */ +export interface Die extends Cause.Variance, Equal.Equal, Pipeable, Inspectable { + readonly _tag: "Die" + readonly defect: unknown +} + +/** + * Represents fiber interruption within a `Cause`. + * + * **Details** + * + * This interface models a scenario where an effect was halted by an external + * signal, carrying a `FiberId` that identifies which fiber was interrupted. + * Interruption is a normal part of concurrency, used for cancellation or + * resource cleanup. + * + * @see {@link interrupt} Construct an `Interrupt` cause + * @see {@link isInterruptType} Check if a `Cause` is an `Interrupt` + * + * @since 2.0.0 + * @category Models + */ +export interface Interrupt extends Cause.Variance, Equal.Equal, Pipeable, Inspectable { + readonly _tag: "Interrupt" + readonly fiberId: FiberId.FiberId +} + +/** + * Represents parallel composition of two `Cause`s. + * + * **Details** + * + * This interface captures failures that happen simultaneously. In scenarios + * with concurrency, more than one operation can fail in parallel. Instead of + * losing information, this structure stores both errors together. + * + * @see {@link parallel} Combine two `Cause`s in parallel + * @see {@link isParallelType} Check if a `Cause` is a `Parallel` + * + * @since 2.0.0 + * @category Models + */ +export interface Parallel extends Cause.Variance, Equal.Equal, Pipeable, Inspectable { + readonly _tag: "Parallel" + readonly left: Cause + readonly right: Cause +} + +/** + * Represents sequential composition of two `Cause`s. + * + * **Details** + * + * This interface models the scenario where one error follows another in + * sequence, such as when a main effect fails and then a finalizer also fails. + * It ensures both errors are retained in the final `Cause`. + * + * @see {@link sequential} Combine two `Cause`s sequentially + * @see {@link isSequentialType} Check if a `Cause` is a `Sequential` + * + * @since 2.0.0 + * @category Models + */ +export interface Sequential extends Cause.Variance, Equal.Equal, Pipeable, Inspectable { + readonly _tag: "Sequential" + readonly left: Cause + readonly right: Cause +} + +/** + * Creates an `Empty` cause. + * + * **Details** + * + * This function returns a cause that signifies "no error." It's commonly used + * to represent an absence of failure conditions. + * + * @see {@link isEmpty} Check if a `Cause` is empty + * + * @since 2.0.0 + * @category Constructors + */ +export const empty: Cause = internal.empty + +/** + * Creates a `Fail` cause from an expected error. + * + * **Details** + * + * This function constructs a `Cause` carrying an error of type `E`. It's used + * when you want to represent a known or anticipated failure in your effectful + * computations. + * + * @see {@link isFailure} Check if a `Cause` contains a failure + * + * @since 2.0.0 + * @category Constructors + */ +export const fail: (error: E) => Cause = internal.fail + +/** + * Creates a `Die` cause from an unexpected error. + * + * **Details** + * + * This function wraps an unhandled or unknown defect (like a runtime crash) + * into a `Cause`. It's useful for capturing unforeseen issues in a structured + * way. + * + * @see {@link isDie} Check if a `Cause` contains a defect + * + * @since 2.0.0 + * @category Constructors + */ +export const die: (defect: unknown) => Cause = internal.die + +/** + * Creates an `Interrupt` cause from a `FiberId`. + * + * **Details** + * + * This function represents a fiber that has been interrupted. It stores the + * identifier of the interrupted fiber, enabling precise tracking of concurrent + * cancellations. + * + * @see {@link isInterrupted} Check if a `Cause` contains an interruption + * + * @since 2.0.0 + * @category Constructors + */ +export const interrupt: (fiberId: FiberId.FiberId) => Cause = internal.interrupt + +/** + * Combines two `Cause`s in parallel. + * + * **Details** + * + * This function merges two errors that occurred simultaneously. Instead of + * discarding one error, both are retained, allowing for richer error reporting + * and debugging. + * + * @see {@link isParallelType} Check if a `Cause` is a `Parallel` + * + * @since 2.0.0 + * @category Constructors + */ +export const parallel: (left: Cause, right: Cause) => Cause = internal.parallel + +/** + * Combines two `Cause`s sequentially. + * + * **Details** + * + * This function merges two errors that occurred in sequence, such as a main + * error followed by a finalization error. It preserves both errors for complete + * failure information. + * + * @see {@link isSequentialType} Check if a `Cause` is a `Sequential` + * + * @since 2.0.0 + * @category Constructors + */ +export const sequential: (left: Cause, right: Cause) => Cause = internal.sequential + +/** + * Checks if a value is a `Cause`. + * + * @since 2.0.0 + * @category Guards + */ +export const isCause: (u: unknown) => u is Cause = internal.isCause + +/** + * Checks if a `Cause` is an `Empty` type. + * + * @see {@link empty} Create a new `Empty` cause + * + * @since 2.0.0 + * @category Guards + */ +export const isEmptyType: (self: Cause) => self is Empty = internal.isEmptyType + +/** + * Checks if a `Cause` is a `Fail` type. + * + * @see {@link fail} Create a new `Fail` cause + * + * @since 2.0.0 + * @category Guards + */ +export const isFailType: (self: Cause) => self is Fail = internal.isFailType + +/** + * Checks if a `Cause` is a `Die` type. + * + * @see {@link die} Create a new `Die` cause + * + * @since 2.0.0 + * @category Guards + */ +export const isDieType: (self: Cause) => self is Die = internal.isDieType + +/** + * Checks if a `Cause` is an `Interrupt` type. + * + * @see {@link interrupt} Create an `Interrupt` cause + * + * @since 2.0.0 + * @category Guards + */ +export const isInterruptType: (self: Cause) => self is Interrupt = internal.isInterruptType + +/** + * Checks if a `Cause` is a `Sequential` type. + * + * @see {@link sequential} Combine two `Cause`s sequentially + * + * @since 2.0.0 + * @category Guards + */ +export const isSequentialType: (self: Cause) => self is Sequential = internal.isSequentialType + +/** + * Checks if a `Cause` is a `Parallel` type. + * + * @see {@link parallel} Combine two `Cause`s in parallel + * + * @since 2.0.0 + * @category Guards + */ +export const isParallelType: (self: Cause) => self is Parallel = internal.isParallelType + +/** + * Calculates the size of a `Cause`. + * + * **Details** + * + * This function returns the total number of `Cause` nodes in the semiring + * structure, reflecting how many individual error elements are recorded. + * + * @since 2.0.0 + * @category Getters + */ +export const size: (self: Cause) => number = internal.size + +/** + * Checks if a `Cause` is entirely empty. + * + * **Details** + * + * This function returns `true` if the `Cause` contains no errors, defects, or + * interruptions. It's helpful for verifying if a computation truly had no + * failures. + * + * @since 2.0.0 + * @category Getters + */ +export const isEmpty: (self: Cause) => boolean = internal.isEmpty + +/** + * Checks if a `Cause` contains a failure. + * + * **Details** + * + * This function returns `true` if the `Cause` includes any `Fail` error. It's + * commonly used to confirm whether a workflow encountered an anticipated error + * versus just defects or interruptions. + * + * @since 2.0.0 + * @category Getters + */ +export const isFailure: (self: Cause) => boolean = internal.isFailure + +/** + * Checks if a `Cause` contains a defect. + * + * **Details** + * + * This function returns `true` if the `Cause` includes any unexpected or + * unhandled errors (`Die`). It's useful for differentiating known failures from + * unexpected ones. + * + * @since 2.0.0 + * @category Getters + */ +export const isDie: (self: Cause) => boolean = internal.isDie + +/** + * Checks if a `Cause` contains an interruption. + * + * **Details** + * + * This function returns `true` if the `Cause` includes any fiber interruptions. + * + * @since 2.0.0 + * @category Getters + */ +export const isInterrupted: (self: Cause) => boolean = internal.isInterrupted + +/** + * Checks if a `Cause` contains only interruptions. + * + * **Details** + * + * This function returns `true` if the `Cause` has been interrupted but does not + * contain any other failures, such as `Fail` or `Die`. It's helpful for + * verifying purely "cancellation" scenarios. + * + * @since 2.0.0 + * @category Getters + */ +export const isInterruptedOnly: (self: Cause) => boolean = internal.isInterruptedOnly + +/** + * Extracts all recoverable errors of type `E` from a `Cause`. + * + * **Details** + * + * This function returns a chunk of errors, providing a list of all `Fail` + * values found in the cause. It's useful for collecting all known failures for + * logging or combined error handling. + * + * @since 2.0.0 + * @category Getters + */ +export const failures: (self: Cause) => Chunk.Chunk = internal.failures + +/** + * Extracts all unrecoverable defects from a `Cause`. + * + * **Details** + * + * This function returns a chunk of values representing unexpected errors + * (`Die`). It's handy for capturing or logging unanticipated failures that + * might need special handling, such as bug reports. + * + * @since 2.0.0 + * @category Getters + */ +export const defects: (self: Cause) => Chunk.Chunk = internal.defects + +/** + * Collects all `FiberId`s responsible for interrupting a fiber. + * + * **Details** + * + * This function returns a set of IDs indicating which fibers caused + * interruptions within this `Cause`. It's useful for debugging concurrency + * issues or tracing cancellations. + * + * @since 2.0.0 + * @category Getters + */ +export const interruptors: (self: Cause) => HashSet.HashSet = internal.interruptors + +/** + * Retrieves the first `Fail` error in a `Cause`, if present. + * + * **Details** + * + * This function returns an `Option` containing the first recoverable error + * (`E`) from the cause. It's often used to quickly check if there's a primary + * error to handle or display. + * + * @since 2.0.0 + * @category Getters + */ +export const failureOption: (self: Cause) => Option.Option = internal.failureOption + +/** + * Splits a `Cause` into either its first `Fail` error or the rest of the cause + * (which might only contain `Die` or `Interrupt`). + * + * **Details** + * + * This function either returns the checked error (`E`) or the remaining + * `Cause` with defects/interruptions. It helps you decide if there's a + * recoverable path or if only unhandled issues remain. + * + * @since 2.0.0 + * @category Getters + */ +export const failureOrCause: (self: Cause) => Either.Either, E> = internal.failureOrCause + +/** + * Strips out failures with an error of `None` from a `Cause>`. + * + * **Details** + * + * This function turns a `Cause>` into an `Option>`. If the + * cause only contains failures of `None`, it becomes `None`; otherwise, it + * returns a `Cause` of the remaining errors. It's helpful when working with + * optional errors and filtering out certain error paths. + * + * @since 2.0.0 + * @category Getters + */ +export const flipCauseOption: (self: Cause>) => Option.Option> = internal.flipCauseOption + +/** + * Retrieves the first `Die` defect in a `Cause`, if present. + * + * **Details** + * + * This function returns an `Option` containing the first unexpected failure + * (`Die`) discovered. It's helpful for diagnosing the primary defect in a chain + * of errors. + * + * @since 2.0.0 + * @category Getters + */ +export const dieOption: (self: Cause) => Option.Option = internal.dieOption + +/** + * Retrieves the first `Interrupt` in a `Cause`, if present. + * + * **Details** + * + * This function returns an `Option` with the first fiber interruption + * discovered. This is particularly useful for concurrency analysis or debugging + * cancellations. + * + * @since 2.0.0 + * @category Getters + */ +export const interruptOption: (self: Cause) => Option.Option = internal.interruptOption + +/** + * Removes all `Fail` and `Interrupt` nodes, keeping only defects (`Die`) in a + * `Cause`. + * + * **Details** + * + * This function strips a cause of recoverable errors and interruptions, leaving + * only unexpected failures. If no defects remain, it returns `None`. It's + * valuable for focusing only on unanticipated problems when both known errors + * and defects could occur. + * + * @since 2.0.0 + * @category Getters + */ +export const keepDefects: (self: Cause) => Option.Option> = internal.keepDefects + +// TODO(4.0): remove? what's the point of this API? +/** + * Linearizes a `Cause` into a set of parallel causes, each containing a + * sequential chain of failures. + * + * **Details** + * + * This function reorganizes the cause structure so that you can analyze each + * parallel branch separately, even if they have multiple sequential errors. + * + * @since 2.0.0 + * @category Getters + */ +export const linearize: (self: Cause) => HashSet.HashSet> = internal.linearize + +/** + * Removes `Fail` and `Interrupt` nodes from a `Cause`, keeping only defects + * (`Die`). + * + * **Details** + * + * This function is similar to `keepDefects` but returns a `Cause` + * directly, which can still store `Die` or finalizer-related defects. It's + * helpful for analyzing only the irrecoverable portion of the error. + * + * @since 2.0.0 + * @category Getters + */ +export const stripFailures: (self: Cause) => Cause = internal.stripFailures + +/** + * Removes matching defects from a `Cause` using a partial function, returning + * the remainder. + * + * **Details** + * + * This function applies a user-defined extraction function to each defect + * (`Die`). If the function matches the defect, that defect is removed. If all + * defects match, the result is `None`. Otherwise, you get a `Cause` with the + * unmatched defects. + * + * @since 2.0.0 + * @category Getters + */ +export const stripSomeDefects: { + /** + * Removes matching defects from a `Cause` using a partial function, returning + * the remainder. + * + * **Details** + * + * This function applies a user-defined extraction function to each defect + * (`Die`). If the function matches the defect, that defect is removed. If all + * defects match, the result is `None`. Otherwise, you get a `Cause` with the + * unmatched defects. + * + * @since 2.0.0 + * @category Getters + */ + (pf: (defect: unknown) => Option.Option): (self: Cause) => Option.Option> + /** + * Removes matching defects from a `Cause` using a partial function, returning + * the remainder. + * + * **Details** + * + * This function applies a user-defined extraction function to each defect + * (`Die`). If the function matches the defect, that defect is removed. If all + * defects match, the result is `None`. Otherwise, you get a `Cause` with the + * unmatched defects. + * + * @since 2.0.0 + * @category Getters + */ + (self: Cause, pf: (defect: unknown) => Option.Option): Option.Option> +} = internal.stripSomeDefects + +/** + * Replaces any errors in a `Cause` with a provided constant error. + * + * **Details** + * + * This function transforms all `Fail` errors into the specified error value, + * preserving the structure of the `Cause`. It's useful when you no longer need + * the original error details but still want to keep the cause shape. + * + * @see {@link map} Apply a custom transformation to `Fail` errors + * + * @since 2.0.0 + * @category Mapping + */ +export const as: { + /** + * Replaces any errors in a `Cause` with a provided constant error. + * + * **Details** + * + * This function transforms all `Fail` errors into the specified error value, + * preserving the structure of the `Cause`. It's useful when you no longer need + * the original error details but still want to keep the cause shape. + * + * @see {@link map} Apply a custom transformation to `Fail` errors + * + * @since 2.0.0 + * @category Mapping + */ + (error: E2): (self: Cause) => Cause + /** + * Replaces any errors in a `Cause` with a provided constant error. + * + * **Details** + * + * This function transforms all `Fail` errors into the specified error value, + * preserving the structure of the `Cause`. It's useful when you no longer need + * the original error details but still want to keep the cause shape. + * + * @see {@link map} Apply a custom transformation to `Fail` errors + * + * @since 2.0.0 + * @category Mapping + */ + (self: Cause, error: E2): Cause +} = internal.as + +/** + * Transforms the errors in a `Cause` using a user-provided function. + * + * **Details** + * + * This function applies `f` to each `Fail` error while leaving defects (`Die`) + * and interruptions untouched. It's useful for changing or simplifying error + * types in your effectful workflows. + * + * @see {@link as} Replace errors with a single constant + * + * @since 2.0.0 + * @category Mapping + */ +export const map: { + /** + * Transforms the errors in a `Cause` using a user-provided function. + * + * **Details** + * + * This function applies `f` to each `Fail` error while leaving defects (`Die`) + * and interruptions untouched. It's useful for changing or simplifying error + * types in your effectful workflows. + * + * @see {@link as} Replace errors with a single constant + * + * @since 2.0.0 + * @category Mapping + */ + (f: (e: E) => E2): (self: Cause) => Cause + /** + * Transforms the errors in a `Cause` using a user-provided function. + * + * **Details** + * + * This function applies `f` to each `Fail` error while leaving defects (`Die`) + * and interruptions untouched. It's useful for changing or simplifying error + * types in your effectful workflows. + * + * @see {@link as} Replace errors with a single constant + * + * @since 2.0.0 + * @category Mapping + */ + (self: Cause, f: (e: E) => E2): Cause +} = internal.map + +/** + * Transforms errors in a `Cause` into new causes. + * + * **Details** + * + * This function applies a function `f` to each `Fail` error, converting it into + * a new `Cause`. This is especially powerful for merging or restructuring error + * types while preserving or combining cause information. + * + * @see {@link map} Apply a simpler transformation to errors + * + * @since 2.0.0 + * @category Sequencing + */ +export const flatMap: { + /** + * Transforms errors in a `Cause` into new causes. + * + * **Details** + * + * This function applies a function `f` to each `Fail` error, converting it into + * a new `Cause`. This is especially powerful for merging or restructuring error + * types while preserving or combining cause information. + * + * @see {@link map} Apply a simpler transformation to errors + * + * @since 2.0.0 + * @category Sequencing + */ + (f: (e: E) => Cause): (self: Cause) => Cause + /** + * Transforms errors in a `Cause` into new causes. + * + * **Details** + * + * This function applies a function `f` to each `Fail` error, converting it into + * a new `Cause`. This is especially powerful for merging or restructuring error + * types while preserving or combining cause information. + * + * @see {@link map} Apply a simpler transformation to errors + * + * @since 2.0.0 + * @category Sequencing + */ + (self: Cause, f: (e: E) => Cause): Cause +} = internal.flatMap + +/** + * Sequences two `Cause`s. The second `Cause` can be dependent on the result of + * the first `Cause`. + * + * @since 2.0.0 + * @category Sequencing + */ +export const andThen: { + /** + * Sequences two `Cause`s. The second `Cause` can be dependent on the result of + * the first `Cause`. + * + * @since 2.0.0 + * @category Sequencing + */ + (f: (e: E) => Cause): (self: Cause) => Cause + /** + * Sequences two `Cause`s. The second `Cause` can be dependent on the result of + * the first `Cause`. + * + * @since 2.0.0 + * @category Sequencing + */ + (f: Cause): (self: Cause) => Cause + /** + * Sequences two `Cause`s. The second `Cause` can be dependent on the result of + * the first `Cause`. + * + * @since 2.0.0 + * @category Sequencing + */ + (self: Cause, f: (e: E) => Cause): Cause + /** + * Sequences two `Cause`s. The second `Cause` can be dependent on the result of + * the first `Cause`. + * + * @since 2.0.0 + * @category Sequencing + */ + (self: Cause, f: Cause): Cause +} = internal.andThen + +/** + * Flattens a nested `Cause` structure. + * + * **Details** + * + * This function takes a `Cause>` and merges the layers into a single + * `Cause`. It's useful for eliminating additional nesting created by + * repeated transformations or compositions. + * + * @see {@link flatMap} Compose nested causes + * + * @since 2.0.0 + * @category Sequencing + */ +export const flatten: (self: Cause>) => Cause = internal.flatten + +/** + * Checks if the current `Cause` contains or is equal to another `Cause`. + * + * **Details** + * + * This function returns `true` if `that` cause is part of or the same as + * the current `Cause`. It's useful when you need to check for specific + * error patterns or deduplicate repeated failures. + * + * @since 2.0.0 + * @category Elements + */ +export const contains: { + /** + * Checks if the current `Cause` contains or is equal to another `Cause`. + * + * **Details** + * + * This function returns `true` if `that` cause is part of or the same as + * the current `Cause`. It's useful when you need to check for specific + * error patterns or deduplicate repeated failures. + * + * @since 2.0.0 + * @category Elements + */ + (that: Cause): (self: Cause) => boolean + /** + * Checks if the current `Cause` contains or is equal to another `Cause`. + * + * **Details** + * + * This function returns `true` if `that` cause is part of or the same as + * the current `Cause`. It's useful when you need to check for specific + * error patterns or deduplicate repeated failures. + * + * @since 2.0.0 + * @category Elements + */ + (self: Cause, that: Cause): boolean +} = internal.contains + +/** + * Extracts the most "important" defect from a `Cause`. + * + * **Details** + * + * This function reduces a `Cause` to a single, prioritized defect. It evaluates + * the `Cause` in the following order of priority: + * + * 1. If the `Cause` contains a failure (e.g., from `Effect.fail`), it returns + * the raw error value. + * 2. If there is no failure, it looks for the first defect (e.g., from + * `Effect.die`). + * 3. If neither of the above is present, and the `Cause` stems from an + * interruption, it creates and returns an `InterruptedException`. + * + * This function ensures you can always extract a meaningful representation of + * the primary issue from a potentially complex `Cause` structure. + * + * **When to Use** + * + * Use this function when you need to extract the most relevant error or defect + * from a `Cause`, especially in scenarios where multiple errors or defects may + * be present. It's particularly useful for simplifying error reporting or + * logging. + * + * @see {@link squashWith} Allows transforming failures into defects when squashing. + * + * @since 2.0.0 + * @category Destructors + */ +export const squash: (self: Cause) => unknown = core.causeSquash + +/** + * Extracts the most "important" defect from a `Cause`, transforming failures + * into defects using a provided function. + * + * **Details** + * + * This function reduces a `Cause` to a single, prioritized defect, while + * allowing you to transform recoverable failures into defects through a custom + * function. It processes the `Cause` in the following order: + * + * 1. If the `Cause` contains a failure (e.g., from `Effect.fail`), it applies + * the provided function `f` to the error to transform it into a defect. + * 2. If there is no failure, it looks for the first defect (e.g., from + * `Effect.die`) and returns it. + * 3. If neither is present and the `Cause` stems from an interruption, it + * returns an `InterruptedException`. + * + * This function is particularly useful when you need custom handling or + * transformation of errors while processing a `Cause`. + * + * @see {@link squash} Extracts the most "important" defect without transforming failures. + * + * @since 2.0.0 + * @category Destructors + */ +export const squashWith: { + /** + * Extracts the most "important" defect from a `Cause`, transforming failures + * into defects using a provided function. + * + * **Details** + * + * This function reduces a `Cause` to a single, prioritized defect, while + * allowing you to transform recoverable failures into defects through a custom + * function. It processes the `Cause` in the following order: + * + * 1. If the `Cause` contains a failure (e.g., from `Effect.fail`), it applies + * the provided function `f` to the error to transform it into a defect. + * 2. If there is no failure, it looks for the first defect (e.g., from + * `Effect.die`) and returns it. + * 3. If neither is present and the `Cause` stems from an interruption, it + * returns an `InterruptedException`. + * + * This function is particularly useful when you need custom handling or + * transformation of errors while processing a `Cause`. + * + * @see {@link squash} Extracts the most "important" defect without transforming failures. + * + * @since 2.0.0 + * @category Destructors + */ + (f: (error: E) => unknown): (self: Cause) => unknown + /** + * Extracts the most "important" defect from a `Cause`, transforming failures + * into defects using a provided function. + * + * **Details** + * + * This function reduces a `Cause` to a single, prioritized defect, while + * allowing you to transform recoverable failures into defects through a custom + * function. It processes the `Cause` in the following order: + * + * 1. If the `Cause` contains a failure (e.g., from `Effect.fail`), it applies + * the provided function `f` to the error to transform it into a defect. + * 2. If there is no failure, it looks for the first defect (e.g., from + * `Effect.die`) and returns it. + * 3. If neither is present and the `Cause` stems from an interruption, it + * returns an `InterruptedException`. + * + * This function is particularly useful when you need custom handling or + * transformation of errors while processing a `Cause`. + * + * @see {@link squash} Extracts the most "important" defect without transforming failures. + * + * @since 2.0.0 + * @category Destructors + */ + (self: Cause, f: (error: E) => unknown): unknown +} = core.causeSquashWith + +/** + * Searches a `Cause` using a partial function to extract information. + * + * **Details** + * + * This function allows you to search through a `Cause` using a custom partial + * function. The partial function is applied to the `Cause`, and if it matches, + * the result is returned wrapped in a `Some`. If no match is found, the result + * is `None`. + * + * This is particularly useful when you are only interested in specific types of + * errors, defects, or interruption causes within a potentially complex `Cause` + * structure. By leveraging a partial function, you can focus on extracting only + * the relevant information you care about. + * + * The partial function should return an `Option` indicating whether it matched + * and the value it extracted. + * + * @since 2.0.0 + * @category Elements + */ +export const find: { + /** + * Searches a `Cause` using a partial function to extract information. + * + * **Details** + * + * This function allows you to search through a `Cause` using a custom partial + * function. The partial function is applied to the `Cause`, and if it matches, + * the result is returned wrapped in a `Some`. If no match is found, the result + * is `None`. + * + * This is particularly useful when you are only interested in specific types of + * errors, defects, or interruption causes within a potentially complex `Cause` + * structure. By leveraging a partial function, you can focus on extracting only + * the relevant information you care about. + * + * The partial function should return an `Option` indicating whether it matched + * and the value it extracted. + * + * @since 2.0.0 + * @category Elements + */ + (pf: (cause: Cause) => Option.Option): (self: Cause) => Option.Option + /** + * Searches a `Cause` using a partial function to extract information. + * + * **Details** + * + * This function allows you to search through a `Cause` using a custom partial + * function. The partial function is applied to the `Cause`, and if it matches, + * the result is returned wrapped in a `Some`. If no match is found, the result + * is `None`. + * + * This is particularly useful when you are only interested in specific types of + * errors, defects, or interruption causes within a potentially complex `Cause` + * structure. By leveraging a partial function, you can focus on extracting only + * the relevant information you care about. + * + * The partial function should return an `Option` indicating whether it matched + * and the value it extracted. + * + * @since 2.0.0 + * @category Elements + */ + (self: Cause, pf: (cause: Cause) => Option.Option): Option.Option +} = internal.find + +/** + * Preserves parts of a `Cause` that match a given predicate. + * + * **Details** + * + * This function allows you to retain only the parts of a `Cause` structure that + * match a specified predicate or refinement. Any parts of the `Cause` that do + * not match the provided condition are excluded from the result. + * + * You can use this function in two ways: + * - With a `Predicate`: A function that evaluates whether a `Cause` should be + * retained based on its value. + * - With a `Refinement`: A more specific predicate that can refine the type of + * the `Cause`. + * + * This is useful when you need to extract specific types of errors, defects, or + * interruptions from a `Cause` while discarding unrelated parts. + * + * @since 2.0.0 + * @category Filtering + */ +export const filter: { + /** + * Preserves parts of a `Cause` that match a given predicate. + * + * **Details** + * + * This function allows you to retain only the parts of a `Cause` structure that + * match a specified predicate or refinement. Any parts of the `Cause` that do + * not match the provided condition are excluded from the result. + * + * You can use this function in two ways: + * - With a `Predicate`: A function that evaluates whether a `Cause` should be + * retained based on its value. + * - With a `Refinement`: A more specific predicate that can refine the type of + * the `Cause`. + * + * This is useful when you need to extract specific types of errors, defects, or + * interruptions from a `Cause` while discarding unrelated parts. + * + * @since 2.0.0 + * @category Filtering + */ + (refinement: Refinement>, Cause>): (self: Cause) => Cause + /** + * Preserves parts of a `Cause` that match a given predicate. + * + * **Details** + * + * This function allows you to retain only the parts of a `Cause` structure that + * match a specified predicate or refinement. Any parts of the `Cause` that do + * not match the provided condition are excluded from the result. + * + * You can use this function in two ways: + * - With a `Predicate`: A function that evaluates whether a `Cause` should be + * retained based on its value. + * - With a `Refinement`: A more specific predicate that can refine the type of + * the `Cause`. + * + * This is useful when you need to extract specific types of errors, defects, or + * interruptions from a `Cause` while discarding unrelated parts. + * + * @since 2.0.0 + * @category Filtering + */ + (predicate: Predicate>>): (self: Cause) => Cause + /** + * Preserves parts of a `Cause` that match a given predicate. + * + * **Details** + * + * This function allows you to retain only the parts of a `Cause` structure that + * match a specified predicate or refinement. Any parts of the `Cause` that do + * not match the provided condition are excluded from the result. + * + * You can use this function in two ways: + * - With a `Predicate`: A function that evaluates whether a `Cause` should be + * retained based on its value. + * - With a `Refinement`: A more specific predicate that can refine the type of + * the `Cause`. + * + * This is useful when you need to extract specific types of errors, defects, or + * interruptions from a `Cause` while discarding unrelated parts. + * + * @since 2.0.0 + * @category Filtering + */ + (self: Cause, refinement: Refinement, Cause>): Cause + /** + * Preserves parts of a `Cause` that match a given predicate. + * + * **Details** + * + * This function allows you to retain only the parts of a `Cause` structure that + * match a specified predicate or refinement. Any parts of the `Cause` that do + * not match the provided condition are excluded from the result. + * + * You can use this function in two ways: + * - With a `Predicate`: A function that evaluates whether a `Cause` should be + * retained based on its value. + * - With a `Refinement`: A more specific predicate that can refine the type of + * the `Cause`. + * + * This is useful when you need to extract specific types of errors, defects, or + * interruptions from a `Cause` while discarding unrelated parts. + * + * @since 2.0.0 + * @category Filtering + */ + (self: Cause, predicate: Predicate>): Cause +} = internal.filter + +/** + * Transforms a `Cause` into a single value using custom handlers for each + * possible case. + * + * **Details** + * + * This function processes a `Cause` by applying a set of custom handlers to + * each possible type of cause: `Empty`, `Fail`, `Die`, `Interrupt`, + * `Sequential`, and `Parallel`. The result of this function is a single value + * of type `Z`. This function allows you to define exactly how to handle each + * part of a `Cause`, whether it's a failure, defect, interruption, or a + * combination of these. + * + * The options parameter provides handlers for: + * - `onEmpty`: Handles the case where the cause is `Empty`, meaning no errors + * occurred. + * - `onFail`: Processes a failure with an error of type `E`. + * - `onDie`: Processes a defect (unexpected error). + * - `onInterrupt`: Handles a fiber interruption, providing the `FiberId` of the + * interruption. + * - `onSequential`: Combines two sequential causes into a single value of type + * `Z`. + * - `onParallel`: Combines two parallel causes into a single value of type `Z`. + * + * @since 2.0.0 + * @category Matching + */ +export const match: { + /** + * Transforms a `Cause` into a single value using custom handlers for each + * possible case. + * + * **Details** + * + * This function processes a `Cause` by applying a set of custom handlers to + * each possible type of cause: `Empty`, `Fail`, `Die`, `Interrupt`, + * `Sequential`, and `Parallel`. The result of this function is a single value + * of type `Z`. This function allows you to define exactly how to handle each + * part of a `Cause`, whether it's a failure, defect, interruption, or a + * combination of these. + * + * The options parameter provides handlers for: + * - `onEmpty`: Handles the case where the cause is `Empty`, meaning no errors + * occurred. + * - `onFail`: Processes a failure with an error of type `E`. + * - `onDie`: Processes a defect (unexpected error). + * - `onInterrupt`: Handles a fiber interruption, providing the `FiberId` of the + * interruption. + * - `onSequential`: Combines two sequential causes into a single value of type + * `Z`. + * - `onParallel`: Combines two parallel causes into a single value of type `Z`. + * + * @since 2.0.0 + * @category Matching + */ + ( + options: { + readonly onEmpty: Z + readonly onFail: (error: E) => Z + readonly onDie: (defect: unknown) => Z + readonly onInterrupt: (fiberId: FiberId.FiberId) => Z + readonly onSequential: (left: Z, right: Z) => Z + readonly onParallel: (left: Z, right: Z) => Z + } + ): (self: Cause) => Z + /** + * Transforms a `Cause` into a single value using custom handlers for each + * possible case. + * + * **Details** + * + * This function processes a `Cause` by applying a set of custom handlers to + * each possible type of cause: `Empty`, `Fail`, `Die`, `Interrupt`, + * `Sequential`, and `Parallel`. The result of this function is a single value + * of type `Z`. This function allows you to define exactly how to handle each + * part of a `Cause`, whether it's a failure, defect, interruption, or a + * combination of these. + * + * The options parameter provides handlers for: + * - `onEmpty`: Handles the case where the cause is `Empty`, meaning no errors + * occurred. + * - `onFail`: Processes a failure with an error of type `E`. + * - `onDie`: Processes a defect (unexpected error). + * - `onInterrupt`: Handles a fiber interruption, providing the `FiberId` of the + * interruption. + * - `onSequential`: Combines two sequential causes into a single value of type + * `Z`. + * - `onParallel`: Combines two parallel causes into a single value of type `Z`. + * + * @since 2.0.0 + * @category Matching + */ + ( + self: Cause, + options: { + readonly onEmpty: Z + readonly onFail: (error: E) => Z + readonly onDie: (defect: unknown) => Z + readonly onInterrupt: (fiberId: FiberId.FiberId) => Z + readonly onSequential: (left: Z, right: Z) => Z + readonly onParallel: (left: Z, right: Z) => Z + } + ): Z +} = internal.match + +/** + * Combines all parts of a `Cause` into a single value by starting with an + * initial value. + * + * **Details** + * + * This function processes a `Cause` by starting with an initial value (`zero`) + * and applying a custom function (`pf`) to combine all elements of the `Cause` + * into a single result of type `Z`. The custom function determines how each + * part of the `Cause` contributes to the final result. The function can return + * an `Option` to either continue combining values or skip specific parts of the + * `Cause`. + * + * This function is useful for tasks such as: + * - Aggregating error messages from a `Cause` into a single string. + * - Summarizing the structure of a `Cause` into a simplified result. + * - Filtering or processing only specific parts of a `Cause`. + * + * The reduction proceeds in a top-down manner, visiting all nodes in the + * `Cause` structure. This gives you complete control over how each part of the + * `Cause` contributes to the final result. + * + * @since 2.0.0 + * @category Reducing + */ +export const reduce: { + /** + * Combines all parts of a `Cause` into a single value by starting with an + * initial value. + * + * **Details** + * + * This function processes a `Cause` by starting with an initial value (`zero`) + * and applying a custom function (`pf`) to combine all elements of the `Cause` + * into a single result of type `Z`. The custom function determines how each + * part of the `Cause` contributes to the final result. The function can return + * an `Option` to either continue combining values or skip specific parts of the + * `Cause`. + * + * This function is useful for tasks such as: + * - Aggregating error messages from a `Cause` into a single string. + * - Summarizing the structure of a `Cause` into a simplified result. + * - Filtering or processing only specific parts of a `Cause`. + * + * The reduction proceeds in a top-down manner, visiting all nodes in the + * `Cause` structure. This gives you complete control over how each part of the + * `Cause` contributes to the final result. + * + * @since 2.0.0 + * @category Reducing + */ + (zero: Z, pf: (accumulator: Z, cause: Cause) => Option.Option): (self: Cause) => Z + /** + * Combines all parts of a `Cause` into a single value by starting with an + * initial value. + * + * **Details** + * + * This function processes a `Cause` by starting with an initial value (`zero`) + * and applying a custom function (`pf`) to combine all elements of the `Cause` + * into a single result of type `Z`. The custom function determines how each + * part of the `Cause` contributes to the final result. The function can return + * an `Option` to either continue combining values or skip specific parts of the + * `Cause`. + * + * This function is useful for tasks such as: + * - Aggregating error messages from a `Cause` into a single string. + * - Summarizing the structure of a `Cause` into a simplified result. + * - Filtering or processing only specific parts of a `Cause`. + * + * The reduction proceeds in a top-down manner, visiting all nodes in the + * `Cause` structure. This gives you complete control over how each part of the + * `Cause` contributes to the final result. + * + * @since 2.0.0 + * @category Reducing + */ + ( + self: Cause, + zero: Z, + pf: (accumulator: Z, cause: Cause) => Option.Option + ): Z +} = internal.reduce + +/** + * Combines all parts of a `Cause` into a single value using a custom reducer + * and a context. + * + * **Details** + * + * This function allows you to reduce a `Cause` into a single value of type `Z` + * using a custom `CauseReducer`. A `CauseReducer` provides methods to handle + * specific parts of the `Cause`, such as failures, defects, or interruptions. + * Additionally, this function provides access to a `context` value, which can + * be used to carry information or maintain state during the reduction process. + * + * This is particularly useful when the reduction process needs additional + * context or configuration, such as: + * - Aggregating error details with dynamic formatting. + * - Collecting logs or statistics about the `Cause`. + * - Performing stateful transformations based on the `context`. + * + * @see {@link reduce} To reduce a `Cause` without additional context. + * + * @since 2.0.0 + * @category Reducing + */ +export const reduceWithContext: { + /** + * Combines all parts of a `Cause` into a single value using a custom reducer + * and a context. + * + * **Details** + * + * This function allows you to reduce a `Cause` into a single value of type `Z` + * using a custom `CauseReducer`. A `CauseReducer` provides methods to handle + * specific parts of the `Cause`, such as failures, defects, or interruptions. + * Additionally, this function provides access to a `context` value, which can + * be used to carry information or maintain state during the reduction process. + * + * This is particularly useful when the reduction process needs additional + * context or configuration, such as: + * - Aggregating error details with dynamic formatting. + * - Collecting logs or statistics about the `Cause`. + * - Performing stateful transformations based on the `context`. + * + * @see {@link reduce} To reduce a `Cause` without additional context. + * + * @since 2.0.0 + * @category Reducing + */ + (context: C, reducer: CauseReducer): (self: Cause) => Z + /** + * Combines all parts of a `Cause` into a single value using a custom reducer + * and a context. + * + * **Details** + * + * This function allows you to reduce a `Cause` into a single value of type `Z` + * using a custom `CauseReducer`. A `CauseReducer` provides methods to handle + * specific parts of the `Cause`, such as failures, defects, or interruptions. + * Additionally, this function provides access to a `context` value, which can + * be used to carry information or maintain state during the reduction process. + * + * This is particularly useful when the reduction process needs additional + * context or configuration, such as: + * - Aggregating error details with dynamic formatting. + * - Collecting logs or statistics about the `Cause`. + * - Performing stateful transformations based on the `context`. + * + * @see {@link reduce} To reduce a `Cause` without additional context. + * + * @since 2.0.0 + * @category Reducing + */ + (self: Cause, context: C, reducer: CauseReducer): Z +} = internal.reduceWithContext + +/** + * Creates an error that indicates a `Fiber` was interrupted. + * + * **Details** + * + * This function constructs an `InterruptedException` recognized by the Effect + * runtime. It is usually thrown or returned when a fiber's execution is + * interrupted by external events or by another fiber. This is particularly + * helpful in concurrent programs where fibers may halt each other before + * completion. + * + * @since 2.0.0 + * @category Errors + */ +export const InterruptedException: new(message?: string | undefined) => InterruptedException = core.InterruptedException + +/** + * Checks if a given unknown value is an `InterruptedException`. + * + * @since 2.0.0 + * @category Guards + */ +export const isInterruptedException: (u: unknown) => u is InterruptedException = core.isInterruptedException + +/** + * Creates an error indicating an invalid method argument. + * + * **Details** + * + * This function constructs an `IllegalArgumentException`. It is typically + * thrown or returned when an operation receives improper inputs, such as + * out-of-range values or invalid object states. + * + * @since 2.0.0 + * @category Errors + */ +export const IllegalArgumentException: new(message?: string | undefined) => IllegalArgumentException = + core.IllegalArgumentException + +/** + * Checks if a given unknown value is an `IllegalArgumentException`. + * + * @since 2.0.0 + * @category Guards + */ +export const isIllegalArgumentException: (u: unknown) => u is IllegalArgumentException = core.isIllegalArgumentException + +/** + * Creates an error indicating a missing element. + * + * **Details** + * + * This function constructs a `NoSuchElementException`. It helps you clearly + * communicate that a required element is unavailable. + * + * @since 2.0.0 + * @category Errors + */ +export const NoSuchElementException: new(message?: string | undefined) => NoSuchElementException = + core.NoSuchElementException + +/** + * Checks if a given unknown value is a `NoSuchElementException`. + * + * @since 2.0.0 + * @category Guards + */ +export const isNoSuchElementException: (u: unknown) => u is NoSuchElementException = core.isNoSuchElementException + +/** + * Creates an error for general runtime errors. + * + * **Details** + * + * This function constructs a `RuntimeException`, for errors that occur at + * runtime but are not specifically typed or categorized as interruptions, + * missing elements, or invalid arguments. It helps unify a wide range of + * unexpected conditions under a single, recognizable error type. + * + * @since 2.0.0 + * @category Errors + */ +export const RuntimeException: new(message?: string | undefined) => RuntimeException = core.RuntimeException + +/** + * Checks if a given unknown value is a `RuntimeException`. + * + * @since 2.0.0 + * @category Guards + */ +export const isRuntimeException: (u: unknown) => u is RuntimeException = core.isRuntimeException + +/** + * Creates an error for operations that exceed their expected time. + * + * **Details** + * + * This function constructs a `TimeoutException`. It is typically used to signal + * that an operation or fiber did not complete within a designated time limit, + * allowing you to handle slow or hanging processes. + * + * @since 2.0.0 + * @category Errors + */ +export const TimeoutException: new(message?: string | undefined) => TimeoutException = core.TimeoutException + +/** + * Checks if a given unknown value is a `TimeoutException`. + * + * @since 3.15.0 + * @category Guards + */ +export const isTimeoutException: (u: unknown) => u is TimeoutException = core.isTimeoutException + +/** + * Creates an instance of `UnknownException`, an error object used to handle + * unknown errors such as those from rejected promises. + * + * **Details** + * + * This function constructs an `UnknownException` with flexible behavior for + * managing the error message and cause. + * + * The required `error` argument is passed as the `cause` to the global `Error` + * constructor, ensuring that the original cause is preserved in the error chain + * for debugging purposes. This ensures that the origin stack trace is + * preserved. + * + * The `error` argument is always stored in the `error` property of the + * `UnknownException` instance for reference, regardless of its type. + * + * Additionally, if you provide a `message` argument, it is used as the error + * message. If no `message` is provided, the error message defaults to `"An + * unknown error occurred"`. + * + * **When to Use** + * + * Use this function when you need to handle unexpected or unknown errors in + * your application, particularly when the source of the error might not provide + * a clear message. This is useful for wrapping generic errors thrown from + * promises or external APIs. + * + * @since 2.0.0 + * @category Errors + */ +export const UnknownException: new(error: unknown, message?: string | undefined) => UnknownException = + core.UnknownException + +/** + * Checks if a given unknown value is an `UnknownException`. + * + * @since 2.0.0 + * @category Guards + */ +export const isUnknownException: (u: unknown) => u is UnknownException = core.isUnknownException + +/** + * Creates an error indicating resource capacity has been exceeded. + * + * **Details** + * + * This function constructs an `ExceededCapacityException`, signifying that an + * operation or resource usage surpassed established limits. This can be + * essential for concurrency or resource management situations, ensuring your + * application doesn't go beyond acceptable thresholds. + * + * @since 3.5.0 + * @category Errors + */ +export const ExceededCapacityException: new(message?: string | undefined) => ExceededCapacityException = + core.ExceededCapacityException + +/** + * Checks if a given unknown value is an `ExceededCapacityException`. + * + * @since 3.5.0 + * @category Guards + */ +export const isExceededCapacityException: (u: unknown) => u is ExceededCapacityException = + core.isExceededCapacityException + +/** + * Converts a `Cause` into a human-readable string. + * + * **Details** + * + * This function pretty-prints the entire `Cause`, including any failures, + * defects, and interruptions. It can be especially helpful for logging, + * debugging, or displaying structured errors to users. + * + * You can optionally pass `options` to configure how the error cause is + * rendered. By default, it includes essential details of all errors in the + * `Cause`. + * + * @see {@link prettyErrors} Get a list of `PrettyError` objects instead of a single string. + * + * @since 2.0.0 + * @category Formatting + */ +export const pretty: (cause: Cause, options?: { + readonly renderErrorCause?: boolean | undefined +}) => string = internal.pretty + +/** + * A shape for prettified errors, optionally including a source span. + * + * @since 3.2.0 + * @category Models + */ +export interface PrettyError extends Error { + readonly span: Span | undefined +} + +/** + * Returns a list of prettified errors (`PrettyError`) from a `Cause`. + * + * **Details** + * + * This function inspects the entire `Cause` and produces an array of + * `PrettyError` objects. Each object may include additional metadata, such as a + * `Span`, to provide deeper insights into where and how the error occurred. + * + * @since 3.2.0 + * @category Formatting + */ +export const prettyErrors: (cause: Cause) => Array = internal.prettyErrors + +/** + * Retrieves the original, unproxied error instance from an error object. + * + * **Details** + * + * This function returns the underlying error object without any + * library-specific wrapping or proxying that might occur during error handling. + * This can be essential if you need direct access to the error's native + * properties, such as stack traces or custom data fields, for detailed + * debugging or integration with external systems. + * + * @since 2.0.0 + * @category Errors + */ +export const originalError: (obj: E) => E = core.originalInstance diff --git a/backend/node_modules/effect/src/Channel.ts b/backend/node_modules/effect/src/Channel.ts new file mode 100644 index 0000000000000000000000000000000000000000..dda4f1fd819f012722a96a5adfad612af19e3785 --- /dev/null +++ b/backend/node_modules/effect/src/Channel.ts @@ -0,0 +1,3051 @@ +/** + * @since 2.0.0 + */ +import type * as Cause from "./Cause.js" +import type * as ChildExecutorDecision from "./ChildExecutorDecision.js" +import type * as Chunk from "./Chunk.js" +import type * as Context from "./Context.js" +import type * as Deferred from "./Deferred.js" +import type * as Effect from "./Effect.js" +import type * as Either from "./Either.js" +import type * as Exit from "./Exit.js" +import type { LazyArg } from "./Function.js" +import * as channel from "./internal/channel.js" +import * as core from "./internal/core-stream.js" +import * as sink from "./internal/sink.js" +import * as stream from "./internal/stream.js" +import type * as Layer from "./Layer.js" +import type * as MergeDecision from "./MergeDecision.js" +import type * as MergeStrategy from "./MergeStrategy.js" +import type * as Option from "./Option.js" +import type { Pipeable } from "./Pipeable.js" +import type { Predicate } from "./Predicate.js" +import type * as PubSub from "./PubSub.js" +import type * as Queue from "./Queue.js" +import type * as Ref from "./Ref.js" +import type * as Scope from "./Scope.js" +import type * as SingleProducerAsyncInput from "./SingleProducerAsyncInput.js" +import type * as Sink from "./Sink.js" +import type * as Stream from "./Stream.js" +import type * as Tracer from "./Tracer.js" +import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" +import type * as UpstreamPullRequest from "./UpstreamPullRequest.js" +import type * as UpstreamPullStrategy from "./UpstreamPullStrategy.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const ChannelTypeId: unique symbol = core.ChannelTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type ChannelTypeId = typeof ChannelTypeId + +/** + * A `Channel` is a nexus of I/O operations, which supports both reading and + * writing. A channel may read values of type `InElem` and write values of type + * `OutElem`. When the channel finishes, it yields a value of type `OutDone`. A + * channel may fail with a value of type `OutErr`. + * + * Channels are the foundation of Streams: both streams and sinks are built on + * channels. Most users shouldn't have to use channels directly, as streams and + * sinks are much more convenient and cover all common use cases. However, when + * adding new stream and sink operators, or doing something highly specialized, + * it may be useful to use channels directly. + * + * Channels compose in a variety of ways: + * + * - **Piping**: One channel can be piped to another channel, assuming the + * input type of the second is the same as the output type of the first. + * - **Sequencing**: The terminal value of one channel can be used to create + * another channel, and both the first channel and the function that makes + * the second channel can be composed into a channel. + * - **Concatenating**: The output of one channel can be used to create other + * channels, which are all concatenated together. The first channel and the + * function that makes the other channels can be composed into a channel. + * + * @since 2.0.0 + * @category models + */ +// export interface Channel +export interface Channel< + out OutElem, + in InElem = unknown, + out OutErr = never, + in InErr = unknown, + out OutDone = void, + in InDone = unknown, + out Env = never +> extends + Channel.Variance< + OutElem, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env + >, + Pipeable +{ + [Unify.typeSymbol]?: unknown + [Unify.unifySymbol]?: ChannelUnify + [Unify.ignoreSymbol]?: ChannelUnifyIgnore +} + +/** + * @since 2.0.0 + * @category models + */ +export interface ChannelUnify extends Effect.EffectUnify { + Channel?: () => A[Unify.typeSymbol] extends + | Channel< + infer OutElem, + infer InElem, + infer OutErr, + infer InErr, + infer OutDone, + infer InDone, + infer Env + > + | infer _ ? Channel + : never +} + +/** + * @category models + * @since 2.0.0 + */ +export interface ChannelUnifyIgnore extends Effect.EffectUnifyIgnore { + Channel?: true +} + +/** + * @since 2.0.0 + * @category models + */ +declare module "./Effect.js" { + interface Effect extends Channel {} + interface EffectUnifyIgnore { + Channel?: true + } +} + +/** + * @since 2.0.0 + */ +export declare namespace Channel { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [ChannelTypeId]: VarianceStruct + } + /** + * @since 2.0.0 + * @category models + */ + export interface VarianceStruct { + _Env: Types.Covariant + _InErr: Types.Contravariant + _InElem: Types.Contravariant + _InDone: Types.Contravariant + _OutErr: Types.Covariant + _OutElem: Types.Covariant + _OutDone: Types.Covariant + } +} + +/** + * @since 2.0.0 + * @category symbols + */ +export const ChannelExceptionTypeId: unique symbol = channel.ChannelExceptionTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type ChannelExceptionTypeId = typeof ChannelExceptionTypeId + +/** + * Represents a generic checked exception which occurs when a `Channel` is + * executed. + * + * @since 2.0.0 + * @category models + */ +export interface ChannelException { + readonly _tag: "ChannelException" + readonly [ChannelExceptionTypeId]: ChannelExceptionTypeId + readonly error: E +} + +/** + * @since 3.5.4 + * @category refinements + */ +export const isChannel: (u: unknown) => u is Channel< + unknown, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown +> = core.isChannel + +/** + * @since 2.0.0 + * @category constructors + */ +export const acquireUseRelease: ( + acquire: Effect.Effect, + use: (a: Acquired) => Channel, + release: (a: Acquired, exit: Exit.Exit) => Effect.Effect +) => Channel = channel.acquireUseRelease + +/** + * @since 2.0.0 + * @category constructors + */ +export const acquireReleaseOut: { + /** + * @since 2.0.0 + * @category constructors + */ + ( + release: (z: Z, e: Exit.Exit) => Effect.Effect + ): (self: Effect.Effect) => Channel + /** + * @since 2.0.0 + * @category constructors + */ + ( + self: Effect.Effect, + release: (z: Z, e: Exit.Exit) => Effect.Effect + ): Channel +} = core.acquireReleaseOut + +/** + * Returns a new channel that is the same as this one, except the terminal + * value of the channel is the specified constant value. + * + * This method produces the same result as mapping this channel to the + * specified constant value. + * + * @since 2.0.0 + * @category mapping + */ +export const as: { + /** + * Returns a new channel that is the same as this one, except the terminal + * value of the channel is the specified constant value. + * + * This method produces the same result as mapping this channel to the + * specified constant value. + * + * @since 2.0.0 + * @category mapping + */ + (value: OutDone2): ( + self: Channel + ) => Channel + /** + * Returns a new channel that is the same as this one, except the terminal + * value of the channel is the specified constant value. + * + * This method produces the same result as mapping this channel to the + * specified constant value. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Channel, + value: OutDone2 + ): Channel +} = channel.as + +/** + * @since 2.0.0 + * @category mapping + */ +export const asVoid: ( + self: Channel +) => Channel = channel.asVoid + +/** + * Creates a channel backed by a buffer. When the buffer is empty, the channel + * will simply passthrough its input as output. However, when the buffer is + * non-empty, the value inside the buffer will be passed along as output. + * + * @since 2.0.0 + * @category constructors + */ +export const buffer: ( + options: { readonly empty: InElem; readonly isEmpty: Predicate; readonly ref: Ref.Ref } +) => Channel = channel.buffer + +/** + * @since 2.0.0 + * @category constructors + */ +export const bufferChunk: ( + ref: Ref.Ref> +) => Channel, Chunk.Chunk, InErr, InErr, InDone, InDone> = channel.bufferChunk + +/** + * Returns a new channel that is the same as this one, except if this channel + * errors for any typed error, then the returned channel will switch over to + * using the fallback channel returned by the specified error handler. + * + * @since 2.0.0 + * @category error handling + */ +export const catchAll: { + /** + * Returns a new channel that is the same as this one, except if this channel + * errors for any typed error, then the returned channel will switch over to + * using the fallback channel returned by the specified error handler. + * + * @since 2.0.0 + * @category error handling + */ + ( + f: (error: OutErr) => Channel + ): ( + self: Channel + ) => Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone1 | OutDone, + InDone & InDone1, + Env1 | Env + > + /** + * Returns a new channel that is the same as this one, except if this channel + * errors for any typed error, then the returned channel will switch over to + * using the fallback channel returned by the specified error handler. + * + * @since 2.0.0 + * @category error handling + */ + ( + self: Channel, + f: (error: OutErr) => Channel + ): Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone | OutDone1, + InDone & InDone1, + Env | Env1 + > +} = channel.catchAll + +/** + * Returns a new channel that is the same as this one, except if this channel + * errors for any typed error, then the returned channel will switch over to + * using the fallback channel returned by the specified error handler. + * + * @since 2.0.0 + * @category error handling + */ +export const catchAllCause: { + /** + * Returns a new channel that is the same as this one, except if this channel + * errors for any typed error, then the returned channel will switch over to + * using the fallback channel returned by the specified error handler. + * + * @since 2.0.0 + * @category error handling + */ + ( + f: (cause: Cause.Cause) => Channel + ): ( + self: Channel + ) => Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone1 | OutDone, + InDone & InDone1, + Env1 | Env + > + /** + * Returns a new channel that is the same as this one, except if this channel + * errors for any typed error, then the returned channel will switch over to + * using the fallback channel returned by the specified error handler. + * + * @since 2.0.0 + * @category error handling + */ + ( + self: Channel, + f: (cause: Cause.Cause) => Channel + ): Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone | OutDone1, + InDone & InDone1, + Env | Env1 + > +} = core.catchAllCause + +/** + * Concat sequentially a channel of channels. + * + * @since 2.0.0 + * @category constructors + */ +export const concatAll: ( + channels: Channel, InElem, OutErr, InErr, any, InDone, Env> +) => Channel = core.concatAll + +/** + * Concat sequentially a channel of channels. + * + * @since 2.0.0 + * @category constructors + */ +export const concatAllWith: < + OutElem, + InElem2, + OutErr2, + InErr2, + OutDone, + InDone2, + Env2, + InElem, + OutErr, + InErr, + OutDone2, + InDone, + Env, + OutDone3 +>( + channels: Channel< + Channel, + InElem, + OutErr, + InErr, + OutDone2, + InDone, + Env + >, + f: (o: OutDone, o1: OutDone) => OutDone, + g: (o: OutDone, o2: OutDone2) => OutDone3 +) => Channel = + core.concatAllWith + +/** + * Returns a new channel whose outputs are fed to the specified factory + * function, which creates new channels in response. These new channels are + * sequentially concatenated together, and all their outputs appear as outputs + * of the newly returned channel. + * + * @since 2.0.0 + * @category utils + */ +export const concatMap: { + /** + * Returns a new channel whose outputs are fed to the specified factory + * function, which creates new channels in response. These new channels are + * sequentially concatenated together, and all their outputs appear as outputs + * of the newly returned channel. + * + * @since 2.0.0 + * @category utils + */ + ( + f: (o: OutElem) => Channel + ): ( + self: Channel + ) => Channel + /** + * Returns a new channel whose outputs are fed to the specified factory + * function, which creates new channels in response. These new channels are + * sequentially concatenated together, and all their outputs appear as outputs + * of the newly returned channel. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + f: (o: OutElem) => Channel + ): Channel +} = channel.concatMap + +/** + * Returns a new channel whose outputs are fed to the specified factory + * function, which creates new channels in response. These new channels are + * sequentially concatenated together, and all their outputs appear as outputs + * of the newly returned channel. The provided merging function is used to + * merge the terminal values of all channels into the single terminal value of + * the returned channel. + * + * @since 2.0.0 + * @category utils + */ +export const concatMapWith: { + /** + * Returns a new channel whose outputs are fed to the specified factory + * function, which creates new channels in response. These new channels are + * sequentially concatenated together, and all their outputs appear as outputs + * of the newly returned channel. The provided merging function is used to + * merge the terminal values of all channels into the single terminal value of + * the returned channel. + * + * @since 2.0.0 + * @category utils + */ + ( + f: (o: OutElem) => Channel, + g: (o: OutDone, o1: OutDone) => OutDone, + h: (o: OutDone, o2: OutDone2) => OutDone3 + ): ( + self: Channel + ) => Channel + /** + * Returns a new channel whose outputs are fed to the specified factory + * function, which creates new channels in response. These new channels are + * sequentially concatenated together, and all their outputs appear as outputs + * of the newly returned channel. The provided merging function is used to + * merge the terminal values of all channels into the single terminal value of + * the returned channel. + * + * @since 2.0.0 + * @category utils + */ + < + OutElem, + InElem, + OutErr, + InErr, + OutDone2, + InDone, + Env, + OutElem2, + InElem2, + OutErr2, + InErr2, + OutDone, + InDone2, + Env2, + OutDone3 + >( + self: Channel, + f: (o: OutElem) => Channel, + g: (o: OutDone, o1: OutDone) => OutDone, + h: (o: OutDone, o2: OutDone2) => OutDone3 + ): Channel +} = core.concatMapWith + +/** + * Returns a new channel whose outputs are fed to the specified factory + * function, which creates new channels in response. These new channels are + * sequentially concatenated together, and all their outputs appear as outputs + * of the newly returned channel. The provided merging function is used to + * merge the terminal values of all channels into the single terminal value of + * the returned channel. + * + * @since 2.0.0 + * @category utils + */ +export const concatMapWithCustom: { + /** + * Returns a new channel whose outputs are fed to the specified factory + * function, which creates new channels in response. These new channels are + * sequentially concatenated together, and all their outputs appear as outputs + * of the newly returned channel. The provided merging function is used to + * merge the terminal values of all channels into the single terminal value of + * the returned channel. + * + * @since 2.0.0 + * @category utils + */ + ( + f: (o: OutElem) => Channel, + g: (o: OutDone, o1: OutDone) => OutDone, + h: (o: OutDone, o2: OutDone2) => OutDone3, + onPull: ( + upstreamPullRequest: UpstreamPullRequest.UpstreamPullRequest + ) => UpstreamPullStrategy.UpstreamPullStrategy, + onEmit: (elem: OutElem2) => ChildExecutorDecision.ChildExecutorDecision + ): ( + self: Channel + ) => Channel + /** + * Returns a new channel whose outputs are fed to the specified factory + * function, which creates new channels in response. These new channels are + * sequentially concatenated together, and all their outputs appear as outputs + * of the newly returned channel. The provided merging function is used to + * merge the terminal values of all channels into the single terminal value of + * the returned channel. + * + * @since 2.0.0 + * @category utils + */ + < + OutElem, + InElem, + OutErr, + InErr, + OutDone2, + InDone, + Env, + OutElem2, + InElem2, + OutErr2, + InErr2, + OutDone, + InDone2, + Env2, + OutDone3 + >( + self: Channel, + f: (o: OutElem) => Channel, + g: (o: OutDone, o1: OutDone) => OutDone, + h: (o: OutDone, o2: OutDone2) => OutDone3, + onPull: ( + upstreamPullRequest: UpstreamPullRequest.UpstreamPullRequest + ) => UpstreamPullStrategy.UpstreamPullStrategy, + onEmit: (elem: OutElem2) => ChildExecutorDecision.ChildExecutorDecision + ): Channel +} = core.concatMapWithCustom + +/** + * Returns a new channel, which is the same as this one, except its outputs + * are filtered and transformed by the specified partial function. + * + * @since 2.0.0 + * @category utils + */ +export const collect: { + /** + * Returns a new channel, which is the same as this one, except its outputs + * are filtered and transformed by the specified partial function. + * + * @since 2.0.0 + * @category utils + */ + (pf: (o: OutElem) => Option.Option): ( + self: Channel + ) => Channel + /** + * Returns a new channel, which is the same as this one, except its outputs + * are filtered and transformed by the specified partial function. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + pf: (o: OutElem) => Option.Option + ): Channel +} = channel.collect + +/** + * Returns a new channel, which is the concatenation of all the channels that + * are written out by this channel. This method may only be called on channels + * that output other channels. + * + * @since 2.0.0 + * @category utils + */ +export const concatOut: ( + self: Channel< + Channel, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env + > +) => Channel = channel.concatOut + +/** + * Returns a new channel which is the same as this one but applies the given + * function to the input channel's done value. + * + * @since 2.0.0 + * @category utils + */ +export const mapInput: { + /** + * Returns a new channel which is the same as this one but applies the given + * function to the input channel's done value. + * + * @since 2.0.0 + * @category utils + */ + (f: (a: InDone0) => InDone): ( + self: Channel + ) => Channel + /** + * Returns a new channel which is the same as this one but applies the given + * function to the input channel's done value. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + f: (a: InDone0) => InDone + ): Channel +} = channel.mapInput + +/** + * Returns a new channel which is the same as this one but applies the given + * effectual function to the input channel's done value. + * + * @since 2.0.0 + * @category utils + */ +export const mapInputEffect: { + /** + * Returns a new channel which is the same as this one but applies the given + * effectual function to the input channel's done value. + * + * @since 2.0.0 + * @category utils + */ + (f: (i: InDone0) => Effect.Effect): ( + self: Channel + ) => Channel + /** + * Returns a new channel which is the same as this one but applies the given + * effectual function to the input channel's done value. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + f: (i: InDone0) => Effect.Effect + ): Channel +} = channel.mapInputEffect + +/** + * Returns a new channel which is the same as this one but applies the given + * function to the input channel's error value. + * + * @since 2.0.0 + * @category utils + */ +export const mapInputError: { + /** + * Returns a new channel which is the same as this one but applies the given + * function to the input channel's error value. + * + * @since 2.0.0 + * @category utils + */ + (f: (a: InErr0) => InErr): ( + self: Channel + ) => Channel + /** + * Returns a new channel which is the same as this one but applies the given + * function to the input channel's error value. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + f: (a: InErr0) => InErr + ): Channel +} = channel.mapInputError + +/** + * Returns a new channel which is the same as this one but applies the given + * effectual function to the input channel's error value. + * + * @since 2.0.0 + * @category utils + */ +export const mapInputErrorEffect: { + /** + * Returns a new channel which is the same as this one but applies the given + * effectual function to the input channel's error value. + * + * @since 2.0.0 + * @category utils + */ + (f: (error: InErr0) => Effect.Effect): ( + self: Channel + ) => Channel + /** + * Returns a new channel which is the same as this one but applies the given + * effectual function to the input channel's error value. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + f: (error: InErr0) => Effect.Effect + ): Channel +} = channel.mapInputErrorEffect + +/** + * Returns a new channel which is the same as this one but applies the given + * function to the input channel's output elements. + * + * @since 2.0.0 + * @category utils + */ +export const mapInputIn: { + /** + * Returns a new channel which is the same as this one but applies the given + * function to the input channel's output elements. + * + * @since 2.0.0 + * @category utils + */ + (f: (a: InElem0) => InElem): ( + self: Channel + ) => Channel + /** + * Returns a new channel which is the same as this one but applies the given + * function to the input channel's output elements. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + f: (a: InElem0) => InElem + ): Channel +} = channel.mapInputIn + +/** + * Returns a new channel which is the same as this one but applies the given + * effectual function to the input channel's output elements. + * + * @since 2.0.0 + * @category utils + */ +export const mapInputInEffect: { + /** + * Returns a new channel which is the same as this one but applies the given + * effectual function to the input channel's output elements. + * + * @since 2.0.0 + * @category utils + */ + (f: (a: InElem0) => Effect.Effect): ( + self: Channel + ) => Channel + /** + * Returns a new channel which is the same as this one but applies the given + * effectual function to the input channel's output elements. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + f: (a: InElem0) => Effect.Effect + ): Channel +} = channel.mapInputInEffect + +/** + * Returns a new channel, which is the same as this one, except that all the + * outputs are collected and bundled into a tuple together with the terminal + * value of this channel. + * + * As the channel returned from this channel collects all of this channel's + * output into an in- memory chunk, it is not safe to call this method on + * channels that output a large or unbounded number of values. + * + * @since 2.0.0 + * @category utils + */ +export const doneCollect: ( + self: Channel +) => Channel, OutDone], InDone, Env> = channel.doneCollect + +/** + * Returns a new channel which reads all the elements from upstream's output + * channel and ignores them, then terminates with the upstream result value. + * + * @since 2.0.0 + * @category utils + */ +export const drain: ( + self: Channel +) => Channel = channel.drain + +/** + * Returns a new channel which connects the given `AsyncInputProducer` as + * this channel's input. + * + * @since 2.0.0 + * @category utils + */ +export const embedInput: { + /** + * Returns a new channel which connects the given `AsyncInputProducer` as + * this channel's input. + * + * @since 2.0.0 + * @category utils + */ + (input: SingleProducerAsyncInput.AsyncInputProducer): ( + self: Channel + ) => Channel + /** + * Returns a new channel which connects the given `AsyncInputProducer` as + * this channel's input. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + input: SingleProducerAsyncInput.AsyncInputProducer + ): Channel +} = core.embedInput + +/** + * Returns a new channel that collects the output and terminal value of this + * channel, which it then writes as output of the returned channel. + * + * @since 2.0.0 + * @category utils + */ +export const emitCollect: ( + self: Channel +) => Channel<[Chunk.Chunk, OutDone], InElem, OutErr, InErr, void, InDone, Env> = channel.emitCollect + +/** + * Returns a new channel with an attached finalizer. The finalizer is + * guaranteed to be executed so long as the channel begins execution (and + * regardless of whether or not it completes). + * + * @since 2.0.0 + * @category utils + */ +export const ensuring: { + /** + * Returns a new channel with an attached finalizer. The finalizer is + * guaranteed to be executed so long as the channel begins execution (and + * regardless of whether or not it completes). + * + * @since 2.0.0 + * @category utils + */ + (finalizer: Effect.Effect): ( + self: Channel + ) => Channel + /** + * Returns a new channel with an attached finalizer. The finalizer is + * guaranteed to be executed so long as the channel begins execution (and + * regardless of whether or not it completes). + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + finalizer: Effect.Effect + ): Channel +} = channel.ensuring + +/** + * Returns a new channel with an attached finalizer. The finalizer is + * guaranteed to be executed so long as the channel begins execution (and + * regardless of whether or not it completes). + * + * @since 2.0.0 + * @category utils + */ +export const ensuringWith: { + /** + * Returns a new channel with an attached finalizer. The finalizer is + * guaranteed to be executed so long as the channel begins execution (and + * regardless of whether or not it completes). + * + * @since 2.0.0 + * @category utils + */ + ( + finalizer: (e: Exit.Exit) => Effect.Effect + ): ( + self: Channel + ) => Channel + /** + * Returns a new channel with an attached finalizer. The finalizer is + * guaranteed to be executed so long as the channel begins execution (and + * regardless of whether or not it completes). + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + finalizer: (e: Exit.Exit) => Effect.Effect + ): Channel +} = core.ensuringWith + +/** + * Accesses the whole context of the channel. + * + * @since 2.0.0 + * @category context + */ +export const context: () => Channel, unknown, Env> = + channel.context + +/** + * Accesses the context of the channel with the specified function. + * + * @since 2.0.0 + * @category context + */ +export const contextWith: ( + f: (env: Context.Context) => OutDone +) => Channel = channel.contextWith + +/** + * Accesses the context of the channel in the context of a channel. + * + * @since 2.0.0 + * @category context + */ +export const contextWithChannel: ( + f: (env: Context.Context) => Channel +) => Channel = channel.contextWithChannel + +/** + * Accesses the context of the channel in the context of an effect. + * + * @since 2.0.0 + * @category context + */ +export const contextWithEffect: ( + f: (env: Context.Context) => Effect.Effect +) => Channel = channel.contextWithEffect + +/** + * Constructs a channel that fails immediately with the specified error. + * + * @since 2.0.0 + * @category constructors + */ +export const fail: (error: E) => Channel = core.fail + +/** + * Constructs a channel that succeeds immediately with the specified lazily + * evaluated value. + * + * @since 2.0.0 + * @category constructors + */ +export const failSync: (evaluate: LazyArg) => Channel = core.failSync + +/** + * Constructs a channel that fails immediately with the specified `Cause`. + * + * @since 2.0.0 + * @category constructors + */ +export const failCause: (cause: Cause.Cause) => Channel = + core.failCause + +/** + * Constructs a channel that succeeds immediately with the specified lazily + * evaluated `Cause`. + * + * @since 2.0.0 + * @category constructors + */ +export const failCauseSync: ( + evaluate: LazyArg> +) => Channel = core.failCauseSync + +/** + * Returns a new channel, which sequentially combines this channel, together + * with the provided factory function, which creates a second channel based on + * the terminal value of this channel. The result is a channel that will first + * perform the functions of this channel, before performing the functions of + * the created channel (including yielding its terminal value). + * + * @since 2.0.0 + * @category sequencing + */ +export const flatMap: { + /** + * Returns a new channel, which sequentially combines this channel, together + * with the provided factory function, which creates a second channel based on + * the terminal value of this channel. The result is a channel that will first + * perform the functions of this channel, before performing the functions of + * the created channel (including yielding its terminal value). + * + * @since 2.0.0 + * @category sequencing + */ + ( + f: (d: OutDone) => Channel + ): ( + self: Channel + ) => Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + OutDone2, + InDone & InDone1, + Env1 | Env + > + /** + * Returns a new channel, which sequentially combines this channel, together + * with the provided factory function, which creates a second channel based on + * the terminal value of this channel. The result is a channel that will first + * perform the functions of this channel, before performing the functions of + * the created channel (including yielding its terminal value). + * + * @since 2.0.0 + * @category sequencing + */ + ( + self: Channel, + f: (d: OutDone) => Channel + ): Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone2, + InDone & InDone1, + Env | Env1 + > +} = core.flatMap + +/** + * Returns a new channel, which flattens the terminal value of this channel. + * This function may only be called if the terminal value of this channel is + * another channel of compatible types. + * + * @since 2.0.0 + * @category sequencing + */ +export const flatten: < + OutElem, + InElem, + OutErr, + InErr, + OutElem1, + InElem1, + OutErr1, + InErr1, + OutDone2, + InDone1, + Env1, + InDone, + Env +>( + self: Channel< + OutElem, + InElem, + OutErr, + InErr, + Channel, + InDone, + Env + > +) => Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone2, + InDone & InDone1, + Env1 | Env +> = channel.flatten + +/** + * Folds over the result of this channel. + * + * @since 2.0.0 + * @category utils + */ +export const foldChannel: { + /** + * Folds over the result of this channel. + * + * @since 2.0.0 + * @category utils + */ + < + OutErr, + OutElem1, + InElem1, + OutErr1, + InErr1, + OutDone1, + InDone1, + Env1, + OutDone, + OutElem2, + InElem2, + OutErr2, + InErr2, + OutDone2, + InDone2, + Env2 + >( + options: { + readonly onFailure: (error: OutErr) => Channel + readonly onSuccess: (done: OutDone) => Channel + } + ): ( + self: Channel + ) => Channel< + OutElem1 | OutElem2 | OutElem, + InElem & InElem1 & InElem2, + OutErr1 | OutErr2, + InErr & InErr1 & InErr2, + OutDone1 | OutDone2, + InDone & InDone1 & InDone2, + Env1 | Env2 | Env + > + /** + * Folds over the result of this channel. + * + * @since 2.0.0 + * @category utils + */ + < + OutElem, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem1, + InElem1, + OutErr1, + InErr1, + OutDone1, + InDone1, + Env1, + OutElem2, + InElem2, + OutErr2, + InErr2, + OutDone2, + InDone2, + Env2 + >( + self: Channel, + options: { + readonly onFailure: (error: OutErr) => Channel + readonly onSuccess: (done: OutDone) => Channel + } + ): Channel< + OutElem | OutElem1 | OutElem2, + InElem & InElem1 & InElem2, + OutErr1 | OutErr2, + InErr & InErr1 & InErr2, + OutDone1 | OutDone2, + InDone & InDone1 & InDone2, + Env | Env1 | Env2 + > +} = channel.foldChannel + +/** + * Folds over the result of this channel including any cause of termination. + * + * @since 2.0.0 + * @category utils + */ +export const foldCauseChannel: { + /** + * Folds over the result of this channel including any cause of termination. + * + * @since 2.0.0 + * @category utils + */ + < + OutErr, + OutElem1, + InElem1, + OutErr2, + InErr1, + OutDone2, + InDone1, + Env1, + OutDone, + OutElem2, + InElem2, + OutErr3, + InErr2, + OutDone3, + InDone2, + Env2 + >( + options: { + readonly onFailure: ( + c: Cause.Cause + ) => Channel + readonly onSuccess: (o: OutDone) => Channel + } + ): ( + self: Channel + ) => Channel< + OutElem1 | OutElem2 | OutElem, + InElem & InElem1 & InElem2, + OutErr2 | OutErr3, + InErr & InErr1 & InErr2, + OutDone2 | OutDone3, + InDone & InDone1 & InDone2, + Env1 | Env2 | Env + > + /** + * Folds over the result of this channel including any cause of termination. + * + * @since 2.0.0 + * @category utils + */ + < + OutElem, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem1, + InElem1, + OutErr2, + InErr1, + OutDone2, + InDone1, + Env1, + OutElem2, + InElem2, + OutErr3, + InErr2, + OutDone3, + InDone2, + Env2 + >( + self: Channel, + options: { + readonly onFailure: ( + c: Cause.Cause + ) => Channel + readonly onSuccess: (o: OutDone) => Channel + } + ): Channel< + OutElem | OutElem1 | OutElem2, + InElem & InElem1 & InElem2, + OutErr2 | OutErr3, + InErr & InErr1 & InErr2, + OutDone2 | OutDone3, + InDone & InDone1 & InDone2, + Env | Env1 | Env2 + > +} = core.foldCauseChannel + +/** + * Use an effect to end a channel. + * + * @since 2.0.0 + * @category constructors + */ +export const fromEffect: ( + effect: Effect.Effect +) => Channel = core.fromEffect + +/** + * Constructs a channel from an `Either`. + * + * @since 2.0.0 + * @category constructors + */ +export const fromEither: (either: Either.Either) => Channel = + channel.fromEither + +/** + * Construct a `Channel` from an `AsyncInputConsumer`. + * + * @since 2.0.0 + * @category constructors + */ +export const fromInput: ( + input: SingleProducerAsyncInput.AsyncInputConsumer +) => Channel = channel.fromInput + +/** + * Construct a `Channel` from a `PubSub`. + * + * @since 2.0.0 + * @category constructors + */ +export const fromPubSub: ( + pubsub: PubSub.PubSub>> +) => Channel = channel.fromPubSub + +/** + * Construct a `Channel` from a `PubSub` within a scoped effect. + * + * @since 2.0.0 + * @category constructors + */ +export const fromPubSubScoped: ( + pubsub: PubSub.PubSub>> +) => Effect.Effect, never, Scope.Scope> = channel.fromPubSubScoped + +/** + * Construct a `Channel` from an `Option`. + * + * @since 2.0.0 + * @category constructors + */ +export const fromOption: ( + option: Option.Option +) => Channel, unknown, A, unknown> = channel.fromOption + +/** + * Construct a `Channel` from a `Queue`. + * + * @since 2.0.0 + * @category constructors + */ +export const fromQueue: ( + queue: Queue.Dequeue>> +) => Channel = channel.fromQueue + +/** + * @since 2.0.0 + * @category constructors + */ +export const identity: () => Channel = channel.identityChannel + +/** + * Returns a new channel, which is the same as this one, except it will be + * interrupted when the specified effect completes. If the effect completes + * successfully before the underlying channel is done, then the returned + * channel will yield the success value of the effect as its terminal value. + * On the other hand, if the underlying channel finishes first, then the + * returned channel will yield the success value of the underlying channel as + * its terminal value. + * + * @since 2.0.0 + * @category utils + */ +export const interruptWhen: { + /** + * Returns a new channel, which is the same as this one, except it will be + * interrupted when the specified effect completes. If the effect completes + * successfully before the underlying channel is done, then the returned + * channel will yield the success value of the effect as its terminal value. + * On the other hand, if the underlying channel finishes first, then the + * returned channel will yield the success value of the underlying channel as + * its terminal value. + * + * @since 2.0.0 + * @category utils + */ + (effect: Effect.Effect): ( + self: Channel + ) => Channel + /** + * Returns a new channel, which is the same as this one, except it will be + * interrupted when the specified effect completes. If the effect completes + * successfully before the underlying channel is done, then the returned + * channel will yield the success value of the effect as its terminal value. + * On the other hand, if the underlying channel finishes first, then the + * returned channel will yield the success value of the underlying channel as + * its terminal value. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + effect: Effect.Effect + ): Channel +} = channel.interruptWhen + +/** + * Returns a new channel, which is the same as this one, except it will be + * interrupted when the specified deferred is completed. If the deferred is + * completed before the underlying channel is done, then the returned channel + * will yield the value of the deferred. Otherwise, if the underlying channel + * finishes first, then the returned channel will yield the value of the + * underlying channel. + * + * @since 2.0.0 + * @category utils + */ +export const interruptWhenDeferred: { + /** + * Returns a new channel, which is the same as this one, except it will be + * interrupted when the specified deferred is completed. If the deferred is + * completed before the underlying channel is done, then the returned channel + * will yield the value of the deferred. Otherwise, if the underlying channel + * finishes first, then the returned channel will yield the value of the + * underlying channel. + * + * @since 2.0.0 + * @category utils + */ + (deferred: Deferred.Deferred): ( + self: Channel + ) => Channel + /** + * Returns a new channel, which is the same as this one, except it will be + * interrupted when the specified deferred is completed. If the deferred is + * completed before the underlying channel is done, then the returned channel + * will yield the value of the deferred. Otherwise, if the underlying channel + * finishes first, then the returned channel will yield the value of the + * underlying channel. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + deferred: Deferred.Deferred + ): Channel +} = channel.interruptWhenDeferred + +/** + * Returns a new channel, which is the same as this one, except the terminal + * value of the returned channel is created by applying the specified function + * to the terminal value of this channel. + * + * @since 2.0.0 + * @category mapping + */ +export const map: { + /** + * Returns a new channel, which is the same as this one, except the terminal + * value of the returned channel is created by applying the specified function + * to the terminal value of this channel. + * + * @since 2.0.0 + * @category mapping + */ + (f: (out: OutDone) => OutDone2): ( + self: Channel + ) => Channel + /** + * Returns a new channel, which is the same as this one, except the terminal + * value of the returned channel is created by applying the specified function + * to the terminal value of this channel. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Channel, + f: (out: OutDone) => OutDone2 + ): Channel +} = channel.map + +/** + * Returns a new channel, which is the same as this one, except the terminal + * value of the returned channel is created by applying the specified + * effectful function to the terminal value of this channel. + * + * @since 2.0.0 + * @category mapping + */ +export const mapEffect: { + /** + * Returns a new channel, which is the same as this one, except the terminal + * value of the returned channel is created by applying the specified + * effectful function to the terminal value of this channel. + * + * @since 2.0.0 + * @category mapping + */ + (f: (o: OutDone) => Effect.Effect): ( + self: Channel + ) => Channel + /** + * Returns a new channel, which is the same as this one, except the terminal + * value of the returned channel is created by applying the specified + * effectful function to the terminal value of this channel. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Channel, + f: (o: OutDone) => Effect.Effect + ): Channel +} = channel.mapEffect + +/** + * Returns a new channel, which is the same as this one, except the failure + * value of the returned channel is created by applying the specified function + * to the failure value of this channel. + * + * @since 2.0.0 + * @category mapping + */ +export const mapError: { + /** + * Returns a new channel, which is the same as this one, except the failure + * value of the returned channel is created by applying the specified function + * to the failure value of this channel. + * + * @since 2.0.0 + * @category mapping + */ + (f: (err: OutErr) => OutErr2): ( + self: Channel + ) => Channel + /** + * Returns a new channel, which is the same as this one, except the failure + * value of the returned channel is created by applying the specified function + * to the failure value of this channel. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Channel, + f: (err: OutErr) => OutErr2 + ): Channel +} = channel.mapError + +/** + * A more powerful version of `mapError` which also surfaces the `Cause` + * of the channel failure. + * + * @since 2.0.0 + * @category mapping + */ +export const mapErrorCause: { + /** + * A more powerful version of `mapError` which also surfaces the `Cause` + * of the channel failure. + * + * @since 2.0.0 + * @category mapping + */ + (f: (cause: Cause.Cause) => Cause.Cause): ( + self: Channel + ) => Channel + /** + * A more powerful version of `mapError` which also surfaces the `Cause` + * of the channel failure. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Channel, + f: (cause: Cause.Cause) => Cause.Cause + ): Channel +} = channel.mapErrorCause + +/** + * Maps the output of this channel using the specified function. + * + * @since 2.0.0 + * @category mapping + */ +export const mapOut: { + /** + * Maps the output of this channel using the specified function. + * + * @since 2.0.0 + * @category mapping + */ + (f: (o: OutElem) => OutElem2): ( + self: Channel + ) => Channel + /** + * Maps the output of this channel using the specified function. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Channel, + f: (o: OutElem) => OutElem2 + ): Channel +} = channel.mapOut + +/** + * Creates a channel that is like this channel but the given effectful function + * gets applied to each emitted output element. + * + * @since 2.0.0 + * @category mapping + */ +export const mapOutEffect: { + /** + * Creates a channel that is like this channel but the given effectful function + * gets applied to each emitted output element. + * + * @since 2.0.0 + * @category mapping + */ + (f: (o: OutElem) => Effect.Effect): ( + self: Channel + ) => Channel + /** + * Creates a channel that is like this channel but the given effectful function + * gets applied to each emitted output element. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Channel, + f: (o: OutElem) => Effect.Effect + ): Channel +} = channel.mapOutEffect + +/** + * Creates a channel that is like this channel but the given Effect function gets + * applied to each emitted output element, taking `n` elements at once and + * mapping them in parallel. + * + * @since 2.0.0 + * @category mapping + */ +export const mapOutEffectPar: { + /** + * Creates a channel that is like this channel but the given Effect function gets + * applied to each emitted output element, taking `n` elements at once and + * mapping them in parallel. + * + * @since 2.0.0 + * @category mapping + */ + (f: (o: OutElem) => Effect.Effect, n: number): ( + self: Channel + ) => Channel + /** + * Creates a channel that is like this channel but the given Effect function gets + * applied to each emitted output element, taking `n` elements at once and + * mapping them in parallel. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Channel, + f: (o: OutElem) => Effect.Effect, + n: number + ): Channel +} = channel.mapOutEffectPar + +/** + * @since 2.0.0 + * @category utils + */ +export const mergeAll: ( + options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + readonly mergeStrategy?: MergeStrategy.MergeStrategy | undefined + } +) => ( + channels: Channel< + Channel, + InElem, + OutErr, + InErr, + unknown, + InDone, + Env + > +) => Channel = + channel.mergeAll + +/** + * @since 2.0.0 + * @category utils + */ +export const mergeAllUnbounded: ( + channels: Channel< + Channel, + InElem, + OutErr, + InErr, + unknown, + InDone, + Env + > +) => Channel = + channel.mergeAllUnbounded + +/** + * @since 2.0.0 + * @category utils + */ +export const mergeAllUnboundedWith: < + OutElem, + InElem1, + OutErr1, + InErr1, + OutDone, + InDone1, + Env1, + InElem, + OutErr, + InErr, + InDone, + Env +>( + channels: Channel< + Channel, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env + >, + f: (o1: OutDone, o2: OutDone) => OutDone +) => Channel = + channel.mergeAllUnboundedWith + +/** + * @since 2.0.0 + * @category utils + */ +export const mergeAllWith: ( + { bufferSize, concurrency, mergeStrategy }: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + readonly mergeStrategy?: MergeStrategy.MergeStrategy | undefined + } +) => ( + channels: Channel< + Channel, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env + >, + f: (o1: OutDone, o2: OutDone) => OutDone +) => Channel = + channel.mergeAllWith + +/** + * Returns a new channel which creates a new channel for each emitted element + * and merges some of them together. Different merge strategies control what + * happens if there are more than the given maximum number of channels gets + * created. See `Channel.mergeAll`. + * + * @since 2.0.0 + * @category mapping + */ +export const mergeMap: { + /** + * Returns a new channel which creates a new channel for each emitted element + * and merges some of them together. Different merge strategies control what + * happens if there are more than the given maximum number of channels gets + * created. See `Channel.mergeAll`. + * + * @since 2.0.0 + * @category mapping + */ + ( + f: (outElem: OutElem) => Channel, + options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + readonly mergeStrategy?: MergeStrategy.MergeStrategy | undefined + } + ): ( + self: Channel + ) => Channel + /** + * Returns a new channel which creates a new channel for each emitted element + * and merges some of them together. Different merge strategies control what + * happens if there are more than the given maximum number of channels gets + * created. See `Channel.mergeAll`. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Channel, + f: (outElem: OutElem) => Channel, + options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + readonly mergeStrategy?: MergeStrategy.MergeStrategy | undefined + } + ): Channel +} = channel.mergeMap + +/** + * Returns a new channel which merges a number of channels emitted by this + * channel using the back pressuring merge strategy. See `Channel.mergeAll`. + * + * @since 2.0.0 + * @category utils + */ +export const mergeOut: { + /** + * Returns a new channel which merges a number of channels emitted by this + * channel using the back pressuring merge strategy. See `Channel.mergeAll`. + * + * @since 2.0.0 + * @category utils + */ + (n: number): ( + self: Channel< + Channel, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env + > + ) => Channel + /** + * Returns a new channel which merges a number of channels emitted by this + * channel using the back pressuring merge strategy. See `Channel.mergeAll`. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel< + Channel, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env + >, + n: number + ): Channel +} = channel.mergeOut + +/** + * Returns a new channel which merges a number of channels emitted by this + * channel using the back pressuring merge strategy and uses a given function + * to merge each completed subchannel's result value. See + * `Channel.mergeAll`. + * + * @since 2.0.0 + * @category utils + */ +export const mergeOutWith: { + /** + * Returns a new channel which merges a number of channels emitted by this + * channel using the back pressuring merge strategy and uses a given function + * to merge each completed subchannel's result value. See + * `Channel.mergeAll`. + * + * @since 2.0.0 + * @category utils + */ + (n: number, f: (o1: OutDone1, o2: OutDone1) => OutDone1): ( + self: Channel< + Channel, + InElem, + OutErr, + InErr, + OutDone1, + InDone, + Env + > + ) => Channel + /** + * Returns a new channel which merges a number of channels emitted by this + * channel using the back pressuring merge strategy and uses a given function + * to merge each completed subchannel's result value. See + * `Channel.mergeAll`. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel< + Channel, + InElem, + OutErr, + InErr, + OutDone1, + InDone, + Env + >, + n: number, + f: (o1: OutDone1, o2: OutDone1) => OutDone1 + ): Channel +} = channel.mergeOutWith + +/** + * Returns a new channel, which is the merge of this channel and the specified + * channel, where the behavior of the returned channel on left or right early + * termination is decided by the specified `leftDone` and `rightDone` merge + * decisions. + * + * @since 2.0.0 + * @category utils + */ +export const mergeWith: { + /** + * Returns a new channel, which is the merge of this channel and the specified + * channel, where the behavior of the returned channel on left or right early + * termination is decided by the specified `leftDone` and `rightDone` merge + * decisions. + * + * @since 2.0.0 + * @category utils + */ + ( + options: { + readonly other: Channel + readonly onSelfDone: ( + exit: Exit.Exit + ) => MergeDecision.MergeDecision + readonly onOtherDone: ( + ex: Exit.Exit + ) => MergeDecision.MergeDecision + } + ): ( + self: Channel + ) => Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr2 | OutErr3, + InErr & InErr1, + OutDone2 | OutDone3, + InDone & InDone1, + Env1 | Env + > + /** + * Returns a new channel, which is the merge of this channel and the specified + * channel, where the behavior of the returned channel on left or right early + * termination is decided by the specified `leftDone` and `rightDone` merge + * decisions. + * + * @since 2.0.0 + * @category utils + */ + < + OutElem, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem1, + InElem1, + OutErr1, + InErr1, + OutDone1, + InDone1, + Env1, + OutErr2, + OutDone2, + OutErr3, + OutDone3 + >( + self: Channel, + options: { + readonly other: Channel + readonly onSelfDone: ( + exit: Exit.Exit + ) => MergeDecision.MergeDecision + readonly onOtherDone: ( + ex: Exit.Exit + ) => MergeDecision.MergeDecision + } + ): Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr2 | OutErr3, + InErr & InErr1, + OutDone2 | OutDone3, + InDone & InDone1, + Env | Env1 + > +} = channel.mergeWith + +/** + * Returns a channel that never completes + * + * @since 2.0.0 + * @category constructors + */ +export const never: Channel = channel.never + +/** + * Translates channel failure into death of the fiber, making all failures + * unchecked and not a part of the type of the channel. + * + * @since 2.0.0 + * @category error handling + */ +export const orDie: { + /** + * Translates channel failure into death of the fiber, making all failures + * unchecked and not a part of the type of the channel. + * + * @since 2.0.0 + * @category error handling + */ + (error: LazyArg): ( + self: Channel + ) => Channel + /** + * Translates channel failure into death of the fiber, making all failures + * unchecked and not a part of the type of the channel. + * + * @since 2.0.0 + * @category error handling + */ + ( + self: Channel, + error: LazyArg + ): Channel +} = channel.orDie + +/** + * Keeps none of the errors, and terminates the fiber with them, using the + * specified function to convert the `OutErr` into a defect. + * + * @since 2.0.0 + * @category error handling + */ +export const orDieWith: { + /** + * Keeps none of the errors, and terminates the fiber with them, using the + * specified function to convert the `OutErr` into a defect. + * + * @since 2.0.0 + * @category error handling + */ + (f: (e: OutErr) => unknown): ( + self: Channel + ) => Channel + /** + * Keeps none of the errors, and terminates the fiber with them, using the + * specified function to convert the `OutErr` into a defect. + * + * @since 2.0.0 + * @category error handling + */ + ( + self: Channel, + f: (e: OutErr) => unknown + ): Channel +} = channel.orDieWith + +/** + * Returns a new channel that will perform the operations of this one, until + * failure, and then it will switch over to the operations of the specified + * fallback channel. + * + * @since 2.0.0 + * @category error handling + */ +export const orElse: { + /** + * Returns a new channel that will perform the operations of this one, until + * failure, and then it will switch over to the operations of the specified + * fallback channel. + * + * @since 2.0.0 + * @category error handling + */ + ( + that: LazyArg> + ): ( + self: Channel + ) => Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone1 | OutDone, + InDone & InDone1, + Env1 | Env + > + /** + * Returns a new channel that will perform the operations of this one, until + * failure, and then it will switch over to the operations of the specified + * fallback channel. + * + * @since 2.0.0 + * @category error handling + */ + ( + self: Channel, + that: LazyArg> + ): Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone | OutDone1, + InDone & InDone1, + Env | Env1 + > +} = channel.orElse + +/** + * Returns a new channel that pipes the output of this channel into the + * specified channel. The returned channel has the input type of this channel, + * and the output type of the specified channel, terminating with the value of + * the specified channel. + * + * @since 2.0.0 + * @category utils + */ +export const pipeTo: { + /** + * Returns a new channel that pipes the output of this channel into the + * specified channel. The returned channel has the input type of this channel, + * and the output type of the specified channel, terminating with the value of + * the specified channel. + * + * @since 2.0.0 + * @category utils + */ + (that: Channel): ( + self: Channel + ) => Channel + /** + * Returns a new channel that pipes the output of this channel into the + * specified channel. The returned channel has the input type of this channel, + * and the output type of the specified channel, terminating with the value of + * the specified channel. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + that: Channel + ): Channel +} = core.pipeTo + +/** + * Returns a new channel that pipes the output of this channel into the + * specified channel and preserves this channel's failures without providing + * them to the other channel for observation. + * + * @since 2.0.0 + * @category utils + */ +export const pipeToOrFail: { + /** + * Returns a new channel that pipes the output of this channel into the + * specified channel and preserves this channel's failures without providing + * them to the other channel for observation. + * + * @since 2.0.0 + * @category utils + */ + (that: Channel): ( + self: Channel + ) => Channel + /** + * Returns a new channel that pipes the output of this channel into the + * specified channel and preserves this channel's failures without providing + * them to the other channel for observation. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Channel, + that: Channel + ): Channel +} = channel.pipeToOrFail + +/** + * Provides the channel with its required context, which eliminates its + * dependency on `Env`. + * + * @since 2.0.0 + * @category context + */ +export const provideContext: { + /** + * Provides the channel with its required context, which eliminates its + * dependency on `Env`. + * + * @since 2.0.0 + * @category context + */ + (env: Context.Context): ( + self: Channel + ) => Channel + /** + * Provides the channel with its required context, which eliminates its + * dependency on `Env`. + * + * @since 2.0.0 + * @category context + */ + ( + self: Channel, + env: Context.Context + ): Channel +} = core.provideContext + +/** + * Provides a layer to the channel, which translates it to another level. + * + * @since 2.0.0 + * @category context + */ +export const provideLayer: { + /** + * Provides a layer to the channel, which translates it to another level. + * + * @since 2.0.0 + * @category context + */ + (layer: Layer.Layer): ( + self: Channel + ) => Channel + /** + * Provides a layer to the channel, which translates it to another level. + * + * @since 2.0.0 + * @category context + */ + ( + self: Channel, + layer: Layer.Layer + ): Channel +} = channel.provideLayer + +/** + * Transforms the context being provided to the channel with the specified + * function. + * + * @since 2.0.0 + * @category context + */ +export const mapInputContext: { + /** + * Transforms the context being provided to the channel with the specified + * function. + * + * @since 2.0.0 + * @category context + */ + (f: (env: Context.Context) => Context.Context): ( + self: Channel + ) => Channel + /** + * Transforms the context being provided to the channel with the specified + * function. + * + * @since 2.0.0 + * @category context + */ + ( + self: Channel, + f: (env: Context.Context) => Context.Context + ): Channel +} = channel.mapInputContext + +/** + * Splits the context into two parts, providing one part using the + * specified layer and leaving the remainder `Env0`. + * + * @since 2.0.0 + * @category context + */ +export const provideSomeLayer: { + /** + * Splits the context into two parts, providing one part using the + * specified layer and leaving the remainder `Env0`. + * + * @since 2.0.0 + * @category context + */ + (layer: Layer.Layer): ( + self: Channel + ) => Channel> + /** + * Splits the context into two parts, providing one part using the + * specified layer and leaving the remainder `Env0`. + * + * @since 2.0.0 + * @category context + */ + ( + self: Channel, + layer: Layer.Layer + ): Channel> +} = channel.provideSomeLayer + +/** + * Provides the effect with the single service it requires. If the effect + * requires more than one service use `provideContext` instead. + * + * @since 2.0.0 + * @category context + */ +export const provideService: { + /** + * Provides the effect with the single service it requires. If the effect + * requires more than one service use `provideContext` instead. + * + * @since 2.0.0 + * @category context + */ + (tag: Context.Tag, service: Types.NoInfer): ( + self: Channel + ) => Channel> + /** + * Provides the effect with the single service it requires. If the effect + * requires more than one service use `provideContext` instead. + * + * @since 2.0.0 + * @category context + */ + ( + self: Channel, + tag: Context.Tag, + service: Types.NoInfer + ): Channel> +} = channel.provideService + +/** + * @since 2.0.0 + * @category constructors + */ +export const read: () => Channel, unknown, In, unknown> = channel.read + +/** + * @since 2.0.0 + * @category constructors + */ +export const readOrFail: (error: E) => Channel = core.readOrFail + +/** + * @since 2.0.0 + * @category constructors + */ +export const readWith: < + InElem, + OutElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem2, + OutErr2, + OutDone2, + Env2, + OutElem3, + OutErr3, + OutDone3, + Env3 +>( + options: { + readonly onInput: (input: InElem) => Channel + readonly onFailure: (error: InErr) => Channel + readonly onDone: (done: InDone) => Channel + } +) => Channel< + OutElem | OutElem2 | OutElem3, + InElem, + OutErr | OutErr2 | OutErr3, + InErr, + OutDone | OutDone2 | OutDone3, + InDone, + Env | Env2 | Env3 +> = core.readWith + +/** + * @since 2.0.0 + * @category constructors + */ +export const readWithCause: < + InElem, + OutElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem2, + OutErr2, + OutDone2, + Env2, + OutElem3, + OutErr3, + OutDone3, + Env3 +>( + options: { + readonly onInput: (input: InElem) => Channel + readonly onFailure: (cause: Cause.Cause) => Channel + readonly onDone: (done: InDone) => Channel + } +) => Channel< + OutElem | OutElem2 | OutElem3, + InElem, + OutErr | OutErr2 | OutErr3, + InErr, + OutDone | OutDone2 | OutDone3, + InDone, + Env | Env2 | Env3 +> = core.readWithCause + +/** + * Creates a channel which repeatedly runs this channel. + * + * @since 2.0.0 + * @category utils + */ +export const repeated: ( + self: Channel +) => Channel = channel.repeated + +/** + * Runs a channel until the end is received. + * + * @since 2.0.0 + * @category destructors + */ +export const run: ( + self: Channel +) => Effect.Effect = channel.run + +/** + * Run the channel until it finishes with a done value or fails with an error + * and collects its emitted output elements. + * + * The channel must not read any input. + * + * @since 2.0.0 + * @category destructors + */ +export const runCollect: ( + self: Channel +) => Effect.Effect<[Chunk.Chunk, OutDone], OutErr, Env> = channel.runCollect + +/** + * Runs a channel until the end is received. + * + * @since 2.0.0 + * @category destructors + */ +export const runDrain: ( + self: Channel +) => Effect.Effect = channel.runDrain + +/** + * Run the channel until it finishes with a done value or fails with an error. + * The channel must not read any input or write any output. + * + * Closing the channel, which includes execution of all the finalizers + * attached to the channel will be added to the current scope as a finalizer. + * + * @since 3.11.0 + * @category destructors + */ +export const runScoped: ( + self: Channel +) => Effect.Effect = channel.runScoped + +/** + * Use a scoped effect to emit an output element. + * + * @since 2.0.0 + * @category constructors + */ +export const scoped: ( + effect: Effect.Effect +) => Channel> = channel.scoped + +/** + * Use a function that receives a scope and returns an effect to emit an output + * element. The output element will be the result of the returned effect, if + * successful. + * + * @since 3.11.0 + * @category constructors + */ +export const scopedWith: ( + f: (scope: Scope.Scope) => Effect.Effect +) => Channel = channel.scopedWith + +/** + * Splits strings on newlines. Handles both Windows newlines (`\r\n`) and UNIX + * newlines (`\n`). + * + * @since 2.0.0 + * @category combinators + */ +export const splitLines: () => Channel< + Chunk.Chunk, + Chunk.Chunk, + Err, + Err, + Done, + Done, + never +> = channel.splitLines + +/** + * Constructs a channel that succeeds immediately with the specified value. + * + * @since 2.0.0 + * @category constructors + */ +export const succeed: (value: A) => Channel = core.succeed + +/** + * Lazily constructs a channel from the given side effect. + * + * @since 2.0.0 + * @category constructors + */ +export const suspend: ( + evaluate: LazyArg> +) => Channel = core.suspend + +/** + * Constructs a channel that succeeds immediately with the specified lazy value. + * + * @since 2.0.0 + * @category constructors + */ +export const sync: ( + evaluate: LazyArg +) => Channel = core.sync + +/** + * Converts a `Channel` to a `PubSub`. + * + * @since 2.0.0 + * @category destructors + */ +export const toPubSub: ( + pubsub: PubSub.PubSub>> +) => Channel = channel.toPubSub + +/** + * Returns a scoped `Effect` that can be used to repeatedly pull elements from + * the constructed `Channel`. The pull effect fails with the channel's failure + * in case the channel fails, or returns either the channel's done value or an + * emitted element. + * + * @since 2.0.0 + * @category destructors + */ +export const toPull: ( + self: Channel +) => Effect.Effect, OutErr, Env>, never, Scope.Scope | Env> = + channel.toPull + +/** + * Returns an `Effect` that can be used to repeatedly pull elements from the + * constructed `Channel` within the provided `Scope`. The pull effect fails + * with the channel's failure in case the channel fails, or returns either the + * channel's done value or an emitted element. + * + * @since 3.11.0 + * @category destructors + */ +export const toPullIn: { + /** + * Returns an `Effect` that can be used to repeatedly pull elements from the + * constructed `Channel` within the provided `Scope`. The pull effect fails + * with the channel's failure in case the channel fails, or returns either the + * channel's done value or an emitted element. + * + * @since 3.11.0 + * @category destructors + */ + (scope: Scope.Scope): ( + self: Channel + ) => Effect.Effect, OutErr, Env>, never, Env> + /** + * Returns an `Effect` that can be used to repeatedly pull elements from the + * constructed `Channel` within the provided `Scope`. The pull effect fails + * with the channel's failure in case the channel fails, or returns either the + * channel's done value or an emitted element. + * + * @since 3.11.0 + * @category destructors + */ + ( + self: Channel, + scope: Scope.Scope + ): Effect.Effect, OutErr, Env>, never, Env> +} = channel.toPullIn + +/** + * Converts a `Channel` to a `Queue`. + * + * @since 2.0.0 + * @category destructors + */ +export const toQueue: ( + queue: Queue.Enqueue>> +) => Channel = channel.toQueue + +/** Converts this channel to a `Sink`. + * + * @since 2.0.0 + * @category destructors + */ +export const toSink: ( + self: Channel, Chunk.Chunk, OutErr, InErr, OutDone, unknown, Env> +) => Sink.Sink = sink.channelToSink + +/** + * Converts this channel to a `Stream`. + * + * @since 2.0.0 + * @category destructors + */ +export const toStream: ( + self: Channel, unknown, OutErr, unknown, OutDone, unknown, Env> +) => Stream.Stream = stream.channelToStream + +const void_: Channel = core.void +export { + /** + * @since 2.0.0 + * @category constructors + */ + void_ as void +} + +/** + * Constructs a `Channel` from an effect that will result in a `Channel` if + * successful. + * + * @since 2.0.0 + * @category constructors + */ +export const unwrap: ( + channel: Effect.Effect, E, R> +) => Channel = channel.unwrap + +/** + * Constructs a `Channel` from a scoped effect that will result in a + * `Channel` if successful. + * + * @since 2.0.0 + * @category constructors + */ +export const unwrapScoped: ( + self: Effect.Effect, E, R> +) => Channel> = channel.unwrapScoped + +/** + * Constructs a `Channel` from a function which receives a `Scope` and returns + * an effect that will result in a `Channel` if successful. + * + * @since 3.11.0 + * @category constructors + */ +export const unwrapScopedWith: ( + f: (scope: Scope.Scope) => Effect.Effect, E, R> +) => Channel = channel.unwrapScopedWith + +/** + * Updates a service in the context of this channel. + * + * @since 2.0.0 + * @category context + */ +export const updateService: { + /** + * Updates a service in the context of this channel. + * + * @since 2.0.0 + * @category context + */ + ( + tag: Context.Tag, + f: (resource: Types.NoInfer) => Types.NoInfer + ): ( + self: Channel + ) => Channel + /** + * Updates a service in the context of this channel. + * + * @since 2.0.0 + * @category context + */ + ( + self: Channel, + tag: Context.Tag, + f: (resource: Types.NoInfer) => Types.NoInfer + ): Channel +} = channel.updateService + +/** + * Wraps the channel with a new span for tracing. + * + * @since 2.0.0 + * @category tracing + */ +export const withSpan: { + /** + * Wraps the channel with a new span for tracing. + * + * @since 2.0.0 + * @category tracing + */ + (name: string, options?: Tracer.SpanOptions | undefined): ( + self: Channel + ) => Channel> + /** + * Wraps the channel with a new span for tracing. + * + * @since 2.0.0 + * @category tracing + */ + ( + self: Channel, + name: string, + options?: Tracer.SpanOptions | undefined + ): Channel> +} = channel.withSpan + +/** + * Writes a single value to the channel. + * + * @since 2.0.0 + * @category constructors + */ +export const write: (out: OutElem) => Channel = core.write + +/** + * Writes a sequence of values to the channel. + * + * @since 2.0.0 + * @category constructors + */ +export const writeAll: >( + ...outs: OutElems +) => Channel = channel.writeAll + +/** + * Writes a `Chunk` of values to the channel. + * + * @since 2.0.0 + * @category constructors + */ +export const writeChunk: ( + outs: Chunk.Chunk +) => Channel = channel.writeChunk + +/** + * Returns a new channel that is the sequential composition of this channel + * and the specified channel. The returned channel terminates with a tuple of + * the terminal values of both channels. + * + * @since 2.0.0 + * @category zipping + */ +export const zip: { + /** + * Returns a new channel that is the sequential composition of this channel + * and the specified channel. The returned channel terminates with a tuple of + * the terminal values of both channels. + * + * @since 2.0.0 + * @category zipping + */ + ( + that: Channel, + options?: { readonly concurrent?: boolean | undefined } | undefined + ): ( + self: Channel + ) => Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + readonly [OutDone, OutDone1], + InDone & InDone1, + Env1 | Env + > + /** + * Returns a new channel that is the sequential composition of this channel + * and the specified channel. The returned channel terminates with a tuple of + * the terminal values of both channels. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Channel, + that: Channel, + options?: { readonly concurrent?: boolean | undefined } | undefined + ): Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + readonly [OutDone, OutDone1], + InDone & InDone1, + Env | Env1 + > +} = channel.zip + +/** + * Returns a new channel that is the sequential composition of this channel + * and the specified channel. The returned channel terminates with the + * terminal value of this channel. + * + * @since 2.0.0 + * @category zipping + */ +export const zipLeft: { + /** + * Returns a new channel that is the sequential composition of this channel + * and the specified channel. The returned channel terminates with the + * terminal value of this channel. + * + * @since 2.0.0 + * @category zipping + */ + ( + that: Channel, + options?: { readonly concurrent?: boolean | undefined } | undefined + ): ( + self: Channel + ) => Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + OutDone, + InDone & InDone1, + Env1 | Env + > + /** + * Returns a new channel that is the sequential composition of this channel + * and the specified channel. The returned channel terminates with the + * terminal value of this channel. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Channel, + that: Channel, + options?: { readonly concurrent?: boolean | undefined } | undefined + ): Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone, + InDone & InDone1, + Env | Env1 + > +} = channel.zipLeft + +/** + * Returns a new channel that is the sequential composition of this channel + * and the specified channel. The returned channel terminates with the + * terminal value of that channel. + * + * @since 2.0.0 + * @category zipping + */ +export const zipRight: { + /** + * Returns a new channel that is the sequential composition of this channel + * and the specified channel. The returned channel terminates with the + * terminal value of that channel. + * + * @since 2.0.0 + * @category zipping + */ + ( + that: Channel, + options?: { + readonly concurrent?: boolean | undefined + } + ): ( + self: Channel + ) => Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + OutDone1, + InDone & InDone1, + Env1 | Env + > + /** + * Returns a new channel that is the sequential composition of this channel + * and the specified channel. The returned channel terminates with the + * terminal value of that channel. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Channel, + that: Channel, + options?: { + readonly concurrent?: boolean | undefined + } + ): Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone1, + InDone & InDone1, + Env | Env1 + > +} = channel.zipRight + +/** + * Represents a generic checked exception which occurs when a `Channel` is + * executed. + * + * @since 2.0.0 + * @category errors + */ +export const ChannelException: (error: E) => ChannelException = channel.ChannelException + +/** + * Returns `true` if the specified value is an `ChannelException`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isChannelException: (u: unknown) => u is ChannelException = channel.isChannelException diff --git a/backend/node_modules/effect/src/ChildExecutorDecision.ts b/backend/node_modules/effect/src/ChildExecutorDecision.ts new file mode 100644 index 0000000000000000000000000000000000000000..1d9a1fce060b5ee26f0d2ca2e7ab111149965afc --- /dev/null +++ b/backend/node_modules/effect/src/ChildExecutorDecision.ts @@ -0,0 +1,158 @@ +/** + * @since 2.0.0 + */ +import * as internal from "./internal/channel/childExecutorDecision.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const ChildExecutorDecisionTypeId: unique symbol = internal.ChildExecutorDecisionTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type ChildExecutorDecisionTypeId = typeof ChildExecutorDecisionTypeId + +/** + * @since 2.0.0 + * @category models + */ +export type ChildExecutorDecision = Continue | Close | Yield + +/** + * @since 2.0.0 + */ +export declare namespace ChildExecutorDecision { + /** + * @since 2.0.0 + * @category models + */ + export interface Proto { + readonly [ChildExecutorDecisionTypeId]: ChildExecutorDecisionTypeId + } +} + +/** + * Continue executing the current substream + * + * @since 2.0.0 + * @category models + */ +export interface Continue extends ChildExecutorDecision.Proto { + readonly _tag: "Continue" +} + +/** + * Close the current substream with a given value and pass execution to the + * next substream + * + * @since 2.0.0 + * @category models + */ +export interface Close extends ChildExecutorDecision.Proto { + readonly _tag: "Close" + readonly value: unknown +} + +/** + * Pass execution to the next substream. This either pulls a new element + * from upstream, or yields to an already created active substream. + * + * @since 2.0.0 + * @category models + */ +export interface Yield extends ChildExecutorDecision.Proto { + readonly _tag: "Yield" +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const Continue: (_: void) => ChildExecutorDecision = internal.Continue + +/** + * @since 2.0.0 + * @category constructors + */ +export const Close: (value: unknown) => ChildExecutorDecision = internal.Close + +/** + * @since 2.0.0 + * @category constructors + */ +export const Yield: (_: void) => ChildExecutorDecision = internal.Yield + +/** + * Returns `true` if the specified value is a `ChildExecutorDecision`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isChildExecutorDecision: (u: unknown) => u is ChildExecutorDecision = internal.isChildExecutorDecision + +/** + * Returns `true` if the specified `ChildExecutorDecision` is a `Continue`, + * `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isContinue: (self: ChildExecutorDecision) => self is Continue = internal.isContinue + +/** + * Returns `true` if the specified `ChildExecutorDecision` is a `Close`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isClose: (self: ChildExecutorDecision) => self is Close = internal.isClose + +/** + * Returns `true` if the specified `ChildExecutorDecision` is a `Yield`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isYield: (self: ChildExecutorDecision) => self is Yield = internal.isYield + +/** + * Folds over a `ChildExecutorDecision` to produce a value of type `A`. + * + * @since 2.0.0 + * @category folding + */ +export const match: { + /** + * Folds over a `ChildExecutorDecision` to produce a value of type `A`. + * + * @since 2.0.0 + * @category folding + */ + ( + options: { + readonly onContinue: () => A + readonly onClose: (value: unknown) => A + readonly onYield: () => A + } + ): (self: ChildExecutorDecision) => A + /** + * Folds over a `ChildExecutorDecision` to produce a value of type `A`. + * + * @since 2.0.0 + * @category folding + */ + ( + self: ChildExecutorDecision, + options: { + readonly onContinue: () => A + readonly onClose: (value: unknown) => A + readonly onYield: () => A + } + ): A +} = internal.match diff --git a/backend/node_modules/effect/src/Chunk.ts b/backend/node_modules/effect/src/Chunk.ts new file mode 100644 index 0000000000000000000000000000000000000000..0377f6f92cf83b5883a1f8e1241b6df321ebad6d --- /dev/null +++ b/backend/node_modules/effect/src/Chunk.ts @@ -0,0 +1,2350 @@ +/** + * @since 2.0.0 + */ +import * as RA from "./Array.js" +import type { NonEmptyReadonlyArray } from "./Array.js" +import type { Either } from "./Either.js" +import * as Equal from "./Equal.js" +import * as Equivalence from "./Equivalence.js" +import { dual, identity, pipe } from "./Function.js" +import * as Hash from "./Hash.js" +import type { TypeLambda } from "./HKT.js" +import { format, type Inspectable, NodeInspectSymbol, toJSON } from "./Inspectable.js" +import type { NonEmptyIterable } from "./NonEmptyIterable.js" +import type { Option } from "./Option.js" +import * as O from "./Option.js" +import * as Order from "./Order.js" +import type { Pipeable } from "./Pipeable.js" +import { pipeArguments } from "./Pipeable.js" +import { hasProperty, type Predicate, type Refinement } from "./Predicate.js" +import type { Covariant, NoInfer } from "./Types.js" + +const TypeId: unique symbol = Symbol.for("effect/Chunk") as TypeId + +/** + * @category symbol + * @since 2.0.0 + */ +export type TypeId = typeof TypeId + +/** + * @category models + * @since 2.0.0 + */ +export interface Chunk extends Iterable, Equal.Equal, Pipeable, Inspectable { + readonly [TypeId]: { + readonly _A: Covariant + } + readonly length: number + /** @internal */ + right: Chunk + /** @internal */ + left: Chunk + /** @internal */ + backing: Backing + /** @internal */ + depth: number +} + +/** + * @category model + * @since 2.0.0 + */ +export interface NonEmptyChunk extends Chunk, NonEmptyIterable {} + +/** + * @category type lambdas + * @since 2.0.0 + */ +export interface ChunkTypeLambda extends TypeLambda { + readonly type: Chunk +} + +type Backing = + | IArray + | IConcat + | ISingleton + | IEmpty + | ISlice + +interface IArray { + readonly _tag: "IArray" + readonly array: ReadonlyArray +} + +interface IConcat { + readonly _tag: "IConcat" + readonly left: Chunk + readonly right: Chunk +} + +interface ISingleton { + readonly _tag: "ISingleton" + readonly a: A +} + +interface IEmpty { + readonly _tag: "IEmpty" +} + +interface ISlice { + readonly _tag: "ISlice" + readonly chunk: Chunk + readonly offset: number + readonly length: number +} + +function copy( + src: ReadonlyArray, + srcPos: number, + dest: Array, + destPos: number, + len: number +) { + for (let i = srcPos; i < Math.min(src.length, srcPos + len); i++) { + dest[destPos + i - srcPos] = src[i]! + } + return dest +} + +const emptyArray: ReadonlyArray = [] + +/** + * Compares the two chunks of equal length using the specified function + * + * @category equivalence + * @since 2.0.0 + */ +export const getEquivalence = (isEquivalent: Equivalence.Equivalence): Equivalence.Equivalence> => + Equivalence.make((self, that) => + self.length === that.length && toReadonlyArray(self).every((value, i) => isEquivalent(value, unsafeGet(that, i))) + ) + +const _equivalence = getEquivalence(Equal.equals) + +const ChunkProto: Omit, "backing" | "depth" | "left" | "length" | "right"> = { + [TypeId]: { + _A: (_: never) => _ + }, + toString(this: Chunk) { + return format(this.toJSON()) + }, + toJSON(this: Chunk) { + return { + _id: "Chunk", + values: toReadonlyArray(this).map(toJSON) + } + }, + [NodeInspectSymbol](this: Chunk) { + return this.toJSON() + }, + [Equal.symbol](this: Chunk, that: unknown): boolean { + return isChunk(that) && _equivalence(this, that) + }, + [Hash.symbol](this: Chunk): number { + return Hash.cached(this, Hash.array(toReadonlyArray(this))) + }, + [Symbol.iterator](this: Chunk): Iterator { + switch (this.backing._tag) { + case "IArray": { + return this.backing.array[Symbol.iterator]() + } + case "IEmpty": { + return emptyArray[Symbol.iterator]() + } + default: { + return toReadonlyArray(this)[Symbol.iterator]() + } + } + }, + pipe(this: Chunk) { + return pipeArguments(this, arguments) + } +} + +const makeChunk = (backing: Backing): Chunk => { + const chunk = Object.create(ChunkProto) + chunk.backing = backing + switch (backing._tag) { + case "IEmpty": { + chunk.length = 0 + chunk.depth = 0 + chunk.left = chunk + chunk.right = chunk + break + } + case "IConcat": { + chunk.length = backing.left.length + backing.right.length + chunk.depth = 1 + Math.max(backing.left.depth, backing.right.depth) + chunk.left = backing.left + chunk.right = backing.right + break + } + case "IArray": { + chunk.length = backing.array.length + chunk.depth = 0 + chunk.left = _empty + chunk.right = _empty + break + } + case "ISingleton": { + chunk.length = 1 + chunk.depth = 0 + chunk.left = _empty + chunk.right = _empty + break + } + case "ISlice": { + chunk.length = backing.length + chunk.depth = backing.chunk.depth + 1 + chunk.left = _empty + chunk.right = _empty + break + } + } + return chunk +} + +/** + * Checks if `u` is a `Chunk` + * + * @category constructors + * @since 2.0.0 + */ +export const isChunk: { + /** + * Checks if `u` is a `Chunk` + * + * @category constructors + * @since 2.0.0 + */ + (u: Iterable): u is Chunk + /** + * Checks if `u` is a `Chunk` + * + * @category constructors + * @since 2.0.0 + */ + (u: unknown): u is Chunk +} = (u: unknown): u is Chunk => hasProperty(u, TypeId) + +const _empty = makeChunk({ _tag: "IEmpty" }) + +/** + * @category constructors + * @since 2.0.0 + */ +export const empty: () => Chunk = () => _empty + +/** + * Builds a `NonEmptyChunk` from an non-empty collection of elements. + * + * @category constructors + * @since 2.0.0 + */ +export const make = ]>(...as: As): NonEmptyChunk => + unsafeFromNonEmptyArray(as) + +/** + * Builds a `NonEmptyChunk` from a single element. + * + * @category constructors + * @since 2.0.0 + */ +export const of = (a: A): NonEmptyChunk => makeChunk({ _tag: "ISingleton", a }) as any + +/** + * Creates a new `Chunk` from an iterable collection of values. + * + * @category constructors + * @since 2.0.0 + */ +export const fromIterable = (self: Iterable): Chunk => + isChunk(self) ? self : unsafeFromArray(RA.fromIterable(self)) + +const copyToArray = (self: Chunk, array: Array, initial: number): void => { + switch (self.backing._tag) { + case "IArray": { + copy(self.backing.array, 0, array, initial, self.length) + break + } + case "IConcat": { + copyToArray(self.left, array, initial) + copyToArray(self.right, array, initial + self.left.length) + break + } + case "ISingleton": { + array[initial] = self.backing.a + break + } + case "ISlice": { + let i = 0 + let j = initial + while (i < self.length) { + array[j] = unsafeGet(self, i) + i += 1 + j += 1 + } + break + } + } +} + +const toArray_ = (self: Chunk): Array => toReadonlyArray(self).slice() + +/** + * Converts a `Chunk` into an `Array`. If the provided `Chunk` is non-empty + * (`NonEmptyChunk`), the function will return a `NonEmptyArray`, ensuring the + * non-empty property is preserved. + * + * @category conversions + * @since 2.0.0 + */ +export const toArray: >( + self: S +) => S extends NonEmptyChunk ? RA.NonEmptyArray> : Array> = toArray_ as any + +const toReadonlyArray_ = (self: Chunk): ReadonlyArray => { + switch (self.backing._tag) { + case "IEmpty": { + return emptyArray + } + case "IArray": { + return self.backing.array + } + default: { + const arr = new Array(self.length) + copyToArray(self, arr, 0) + self.backing = { + _tag: "IArray", + array: arr + } + self.left = _empty + self.right = _empty + self.depth = 0 + return arr + } + } +} + +/** + * Converts a `Chunk` into a `ReadonlyArray`. If the provided `Chunk` is + * non-empty (`NonEmptyChunk`), the function will return a + * `NonEmptyReadonlyArray`, ensuring the non-empty property is preserved. + * + * @category conversions + * @since 2.0.0 + */ +export const toReadonlyArray: >( + self: S +) => S extends NonEmptyChunk ? RA.NonEmptyReadonlyArray> : ReadonlyArray> = + toReadonlyArray_ as any + +const reverseChunk = (self: Chunk): Chunk => { + switch (self.backing._tag) { + case "IEmpty": + case "ISingleton": + return self + case "IArray": { + return makeChunk({ _tag: "IArray", array: RA.reverse(self.backing.array) }) + } + case "IConcat": { + return makeChunk({ _tag: "IConcat", left: reverse(self.backing.right), right: reverse(self.backing.left) }) + } + case "ISlice": + return unsafeFromArray(RA.reverse(toReadonlyArray(self))) + } +} + +/** + * Reverses the order of elements in a `Chunk`. + * Importantly, if the input chunk is a `NonEmptyChunk`, the reversed chunk will also be a `NonEmptyChunk`. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const chunk = Chunk.make(1, 2, 3) + * const result = Chunk.reverse(chunk) + * + * console.log(result) + * // { _id: 'Chunk', values: [ 3, 2, 1 ] } + * ``` + * + * @since 2.0.0 + * @category elements + */ +export const reverse: >(self: S) => Chunk.With> = reverseChunk as any + +/** + * This function provides a safe way to read a value at a particular index from a `Chunk`. + * + * @category elements + * @since 2.0.0 + */ +export const get: { + /** + * This function provides a safe way to read a value at a particular index from a `Chunk`. + * + * @category elements + * @since 2.0.0 + */ + (index: number): (self: Chunk) => Option + /** + * This function provides a safe way to read a value at a particular index from a `Chunk`. + * + * @category elements + * @since 2.0.0 + */ + (self: Chunk, index: number): Option +} = dual( + 2, + (self: Chunk, index: number): Option => + index < 0 || index >= self.length ? O.none() : O.some(unsafeGet(self, index)) +) + +/** + * Wraps an array into a chunk without copying, unsafe on mutable arrays + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeFromArray = (self: ReadonlyArray): Chunk => + self.length === 0 ? empty() : self.length === 1 ? of(self[0]) : makeChunk({ _tag: "IArray", array: self }) + +/** + * Wraps an array into a chunk without copying, unsafe on mutable arrays + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeFromNonEmptyArray = (self: NonEmptyReadonlyArray): NonEmptyChunk => + unsafeFromArray(self) as any + +/** + * Gets an element unsafely, will throw on out of bounds + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeGet: { + /** + * Gets an element unsafely, will throw on out of bounds + * + * @since 2.0.0 + * @category unsafe + */ + (index: number): (self: Chunk) => A + /** + * Gets an element unsafely, will throw on out of bounds + * + * @since 2.0.0 + * @category unsafe + */ + (self: Chunk, index: number): A +} = dual(2, (self: Chunk, index: number): A => { + switch (self.backing._tag) { + case "IEmpty": { + throw new Error(`Index out of bounds`) + } + case "ISingleton": { + if (index !== 0) { + throw new Error(`Index out of bounds`) + } + return self.backing.a + } + case "IArray": { + if (index >= self.length || index < 0) { + throw new Error(`Index out of bounds`) + } + return self.backing.array[index]! + } + case "IConcat": { + return index < self.left.length + ? unsafeGet(self.left, index) + : unsafeGet(self.right, index - self.left.length) + } + case "ISlice": { + return unsafeGet(self.backing.chunk, index + self.backing.offset) + } + } +}) + +/** + * Appends the specified element to the end of the `Chunk`. + * + * @category concatenating + * @since 2.0.0 + */ +export const append: { + /** + * Appends the specified element to the end of the `Chunk`. + * + * @category concatenating + * @since 2.0.0 + */ + (a: A2): (self: Chunk) => NonEmptyChunk + /** + * Appends the specified element to the end of the `Chunk`. + * + * @category concatenating + * @since 2.0.0 + */ + (self: Chunk, a: A2): NonEmptyChunk +} = dual(2, (self: Chunk, a: A2): NonEmptyChunk => appendAll(self, of(a))) + +/** + * Prepend an element to the front of a `Chunk`, creating a new `NonEmptyChunk`. + * + * @category concatenating + * @since 2.0.0 + */ +export const prepend: { + /** + * Prepend an element to the front of a `Chunk`, creating a new `NonEmptyChunk`. + * + * @category concatenating + * @since 2.0.0 + */ + (elem: B): (self: Chunk) => NonEmptyChunk + /** + * Prepend an element to the front of a `Chunk`, creating a new `NonEmptyChunk`. + * + * @category concatenating + * @since 2.0.0 + */ + (self: Chunk, elem: B): NonEmptyChunk +} = dual(2, (self: Chunk, elem: B): NonEmptyChunk => appendAll(of(elem), self)) + +/** + * Takes the first up to `n` elements from the chunk + * + * @since 2.0.0 + */ +export const take: { + /** + * Takes the first up to `n` elements from the chunk + * + * @since 2.0.0 + */ + (n: number): (self: Chunk) => Chunk + /** + * Takes the first up to `n` elements from the chunk + * + * @since 2.0.0 + */ + (self: Chunk, n: number): Chunk +} = dual(2, (self: Chunk, n: number): Chunk => { + if (n <= 0) { + return _empty + } else if (n >= self.length) { + return self + } else { + switch (self.backing._tag) { + case "ISlice": { + return makeChunk({ + _tag: "ISlice", + chunk: self.backing.chunk, + length: n, + offset: self.backing.offset + }) + } + case "IConcat": { + if (n > self.left.length) { + return makeChunk({ + _tag: "IConcat", + left: self.left, + right: take(self.right, n - self.left.length) + }) + } + + return take(self.left, n) + } + default: { + return makeChunk({ + _tag: "ISlice", + chunk: self, + offset: 0, + length: n + }) + } + } + } +}) + +/** + * Drops the first up to `n` elements from the chunk + * + * @since 2.0.0 + */ +export const drop: { + /** + * Drops the first up to `n` elements from the chunk + * + * @since 2.0.0 + */ + (n: number): (self: Chunk) => Chunk + /** + * Drops the first up to `n` elements from the chunk + * + * @since 2.0.0 + */ + (self: Chunk, n: number): Chunk +} = dual(2, (self: Chunk, n: number): Chunk => { + if (n <= 0) { + return self + } else if (n >= self.length) { + return _empty + } else { + switch (self.backing._tag) { + case "ISlice": { + return makeChunk({ + _tag: "ISlice", + chunk: self.backing.chunk, + offset: self.backing.offset + n, + length: self.backing.length - n + }) + } + case "IConcat": { + if (n > self.left.length) { + return drop(self.right, n - self.left.length) + } + return makeChunk({ + _tag: "IConcat", + left: drop(self.left, n), + right: self.right + }) + } + default: { + return makeChunk({ + _tag: "ISlice", + chunk: self, + offset: n, + length: self.length - n + }) + } + } + } +}) + +/** + * Drops the last `n` elements. + * + * @since 2.0.0 + */ +export const dropRight: { + /** + * Drops the last `n` elements. + * + * @since 2.0.0 + */ + (n: number): (self: Chunk) => Chunk + /** + * Drops the last `n` elements. + * + * @since 2.0.0 + */ + (self: Chunk, n: number): Chunk +} = dual(2, (self: Chunk, n: number): Chunk => take(self, Math.max(0, self.length - n))) + +/** + * Drops all elements so long as the predicate returns true. + * + * @since 2.0.0 + */ +export const dropWhile: { + /** + * Drops all elements so long as the predicate returns true. + * + * @since 2.0.0 + */ + (predicate: Predicate>): (self: Chunk) => Chunk + /** + * Drops all elements so long as the predicate returns true. + * + * @since 2.0.0 + */ + (self: Chunk, predicate: Predicate): Chunk +} = dual(2, (self: Chunk, predicate: Predicate): Chunk => { + const arr = toReadonlyArray(self) + const len = arr.length + let i = 0 + while (i < len && predicate(arr[i]!)) { + i++ + } + return drop(self, i) +}) + +/** + * Prepends the specified prefix chunk to the beginning of the specified chunk. + * If either chunk is non-empty, the result is also a non-empty chunk. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.make(1, 2).pipe(Chunk.prependAll(Chunk.make("a", "b")), Chunk.toArray) + * + * console.log(result) + * // [ "a", "b", 1, 2 ] + * ``` + * + * @category concatenating + * @since 2.0.0 + */ +export const prependAll: { + /** + * Prepends the specified prefix chunk to the beginning of the specified chunk. + * If either chunk is non-empty, the result is also a non-empty chunk. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.make(1, 2).pipe(Chunk.prependAll(Chunk.make("a", "b")), Chunk.toArray) + * + * console.log(result) + * // [ "a", "b", 1, 2 ] + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + , T extends Chunk>(that: T): (self: S) => Chunk.OrNonEmpty | Chunk.Infer> + /** + * Prepends the specified prefix chunk to the beginning of the specified chunk. + * If either chunk is non-empty, the result is also a non-empty chunk. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.make(1, 2).pipe(Chunk.prependAll(Chunk.make("a", "b")), Chunk.toArray) + * + * console.log(result) + * // [ "a", "b", 1, 2 ] + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + (self: Chunk, that: NonEmptyChunk): NonEmptyChunk + /** + * Prepends the specified prefix chunk to the beginning of the specified chunk. + * If either chunk is non-empty, the result is also a non-empty chunk. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.make(1, 2).pipe(Chunk.prependAll(Chunk.make("a", "b")), Chunk.toArray) + * + * console.log(result) + * // [ "a", "b", 1, 2 ] + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + (self: NonEmptyChunk, that: Chunk): NonEmptyChunk + /** + * Prepends the specified prefix chunk to the beginning of the specified chunk. + * If either chunk is non-empty, the result is also a non-empty chunk. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.make(1, 2).pipe(Chunk.prependAll(Chunk.make("a", "b")), Chunk.toArray) + * + * console.log(result) + * // [ "a", "b", 1, 2 ] + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + (self: Chunk, that: Chunk): Chunk +} = dual(2, (self: NonEmptyChunk, that: Chunk): Chunk => appendAll(that, self)) + +/** + * Concatenates two chunks, combining their elements. + * If either chunk is non-empty, the result is also a non-empty chunk. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.make(1, 2).pipe(Chunk.appendAll(Chunk.make("a", "b")), Chunk.toArray) + * + * console.log(result) + * // [ 1, 2, "a", "b" ] + * ``` + * + * @category concatenating + * @since 2.0.0 + */ +export const appendAll: { + /** + * Concatenates two chunks, combining their elements. + * If either chunk is non-empty, the result is also a non-empty chunk. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.make(1, 2).pipe(Chunk.appendAll(Chunk.make("a", "b")), Chunk.toArray) + * + * console.log(result) + * // [ 1, 2, "a", "b" ] + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + , T extends Chunk>(that: T): (self: S) => Chunk.OrNonEmpty | Chunk.Infer> + /** + * Concatenates two chunks, combining their elements. + * If either chunk is non-empty, the result is also a non-empty chunk. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.make(1, 2).pipe(Chunk.appendAll(Chunk.make("a", "b")), Chunk.toArray) + * + * console.log(result) + * // [ 1, 2, "a", "b" ] + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + (self: Chunk, that: NonEmptyChunk): NonEmptyChunk + /** + * Concatenates two chunks, combining their elements. + * If either chunk is non-empty, the result is also a non-empty chunk. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.make(1, 2).pipe(Chunk.appendAll(Chunk.make("a", "b")), Chunk.toArray) + * + * console.log(result) + * // [ 1, 2, "a", "b" ] + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + (self: NonEmptyChunk, that: Chunk): NonEmptyChunk + /** + * Concatenates two chunks, combining their elements. + * If either chunk is non-empty, the result is also a non-empty chunk. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.make(1, 2).pipe(Chunk.appendAll(Chunk.make("a", "b")), Chunk.toArray) + * + * console.log(result) + * // [ 1, 2, "a", "b" ] + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + (self: Chunk, that: Chunk): Chunk +} = dual(2, (self: Chunk, that: Chunk): Chunk => { + if (self.backing._tag === "IEmpty") { + return that + } + if (that.backing._tag === "IEmpty") { + return self + } + const diff = that.depth - self.depth + if (Math.abs(diff) <= 1) { + return makeChunk({ _tag: "IConcat", left: self, right: that }); + } else if (diff < -1) { + if (self.left.depth >= self.right.depth) { + const nr = appendAll(self.right, that) + return makeChunk({ _tag: "IConcat", left: self.left, right: nr }) + } else { + const nrr = appendAll(self.right.right, that) + if (nrr.depth === self.depth - 3) { + const nr = makeChunk({ _tag: "IConcat", left: self.right.left, right: nrr }) + return makeChunk({ _tag: "IConcat", left: self.left, right: nr }) + } else { + const nl = makeChunk({ _tag: "IConcat", left: self.left, right: self.right.left }) + return makeChunk({ _tag: "IConcat", left: nl, right: nrr }) + } + } + } else { + if (that.right.depth >= that.left.depth) { + const nl = appendAll(self, that.left) + return makeChunk({ _tag: "IConcat", left: nl, right: that.right }) + } else { + const nll = appendAll(self, that.left.left) + if (nll.depth === that.depth - 3) { + const nl = makeChunk({ _tag: "IConcat", left: nll, right: that.left.right }) + return makeChunk({ _tag: "IConcat", left: nl, right: that.right }) + } else { + const nr = makeChunk({ _tag: "IConcat", left: that.left.right, right: that.right }) + return makeChunk({ _tag: "IConcat", left: nll, right: nr }) + } + } + } +}) + +/** + * Returns a filtered and mapped subset of the elements. + * + * @since 2.0.0 + * @category filtering + */ +export const filterMap: { + /** + * Returns a filtered and mapped subset of the elements. + * + * @since 2.0.0 + * @category filtering + */ + (f: (a: A, i: number) => Option): (self: Chunk) => Chunk + /** + * Returns a filtered and mapped subset of the elements. + * + * @since 2.0.0 + * @category filtering + */ + (self: Chunk, f: (a: A, i: number) => Option): Chunk +} = dual( + 2, + (self: Chunk, f: (a: A, i: number) => Option): Chunk => unsafeFromArray(RA.filterMap(self, f)) +) + +/** + * Returns a filtered and mapped subset of the elements. + * + * @since 2.0.0 + * @category filtering + */ +export const filter: { + /** + * Returns a filtered and mapped subset of the elements. + * + * @since 2.0.0 + * @category filtering + */ + (refinement: Refinement, B>): (self: Chunk) => Chunk + /** + * Returns a filtered and mapped subset of the elements. + * + * @since 2.0.0 + * @category filtering + */ + (predicate: Predicate>): (self: Chunk) => Chunk + /** + * Returns a filtered and mapped subset of the elements. + * + * @since 2.0.0 + * @category filtering + */ + (self: Chunk, refinement: Refinement): Chunk + /** + * Returns a filtered and mapped subset of the elements. + * + * @since 2.0.0 + * @category filtering + */ + (self: Chunk, predicate: Predicate): Chunk +} = dual( + 2, + (self: Chunk, predicate: Predicate): Chunk => unsafeFromArray(RA.filter(self, predicate)) +) + +/** + * Transforms all elements of the chunk for as long as the specified function returns some value + * + * @since 2.0.0 + * @category filtering + */ +export const filterMapWhile: { + /** + * Transforms all elements of the chunk for as long as the specified function returns some value + * + * @since 2.0.0 + * @category filtering + */ + (f: (a: A) => Option): (self: Chunk) => Chunk + /** + * Transforms all elements of the chunk for as long as the specified function returns some value + * + * @since 2.0.0 + * @category filtering + */ + (self: Chunk, f: (a: A) => Option): Chunk +} = dual(2, (self: Chunk, f: (a: A) => Option) => unsafeFromArray(RA.filterMapWhile(self, f))) + +/** + * Filter out optional values + * + * @since 2.0.0 + * @category filtering + */ +export const compact = (self: Chunk>): Chunk => filterMap(self, identity) + +/** + * Applies a function to each element in a chunk and returns a new chunk containing the concatenated mapped elements. + * + * @since 2.0.0 + * @category sequencing + */ +export const flatMap: { + /** + * Applies a function to each element in a chunk and returns a new chunk containing the concatenated mapped elements. + * + * @since 2.0.0 + * @category sequencing + */ + , T extends Chunk>(f: (a: Chunk.Infer, i: number) => T): (self: S) => Chunk.AndNonEmpty> + /** + * Applies a function to each element in a chunk and returns a new chunk containing the concatenated mapped elements. + * + * @since 2.0.0 + * @category sequencing + */ + (self: NonEmptyChunk, f: (a: A, i: number) => NonEmptyChunk): NonEmptyChunk + /** + * Applies a function to each element in a chunk and returns a new chunk containing the concatenated mapped elements. + * + * @since 2.0.0 + * @category sequencing + */ + (self: Chunk, f: (a: A, i: number) => Chunk): Chunk +} = dual(2, (self: Chunk, f: (a: A, i: number) => Chunk) => { + if (self.backing._tag === "ISingleton") { + return f(self.backing.a, 0) + } + let out: Chunk = _empty + let i = 0 + for (const k of self) { + out = appendAll(out, f(k, i++)) + } + return out +}) + +/** + * Iterates over each element of a `Chunk` and applies a function to it. + * + * **Details** + * + * This function processes every element of the given `Chunk`, calling the + * provided function `f` on each element. It does not return a new value; + * instead, it is primarily used for side effects, such as logging or + * accumulating data in an external variable. + * + * @since 2.0.0 + * @category combinators + */ +export const forEach: { + /** + * Iterates over each element of a `Chunk` and applies a function to it. + * + * **Details** + * + * This function processes every element of the given `Chunk`, calling the + * provided function `f` on each element. It does not return a new value; + * instead, it is primarily used for side effects, such as logging or + * accumulating data in an external variable. + * + * @since 2.0.0 + * @category combinators + */ + (f: (a: A, index: number) => B): (self: Chunk) => void + /** + * Iterates over each element of a `Chunk` and applies a function to it. + * + * **Details** + * + * This function processes every element of the given `Chunk`, calling the + * provided function `f` on each element. It does not return a new value; + * instead, it is primarily used for side effects, such as logging or + * accumulating data in an external variable. + * + * @since 2.0.0 + * @category combinators + */ + (self: Chunk, f: (a: A, index: number) => B): void +} = dual(2, (self: Chunk, f: (a: A) => B): void => toReadonlyArray(self).forEach(f)) + +/** + * Flattens a chunk of chunks into a single chunk by concatenating all chunks. + * + * @since 2.0.0 + * @category sequencing + */ +export const flatten: >>(self: S) => Chunk.Flatten = flatMap(identity) as any + +/** + * Groups elements in chunks of up to `n` elements. + * + * @since 2.0.0 + * @category elements + */ +export const chunksOf: { + /** + * Groups elements in chunks of up to `n` elements. + * + * @since 2.0.0 + * @category elements + */ + (n: number): (self: Chunk) => Chunk> + /** + * Groups elements in chunks of up to `n` elements. + * + * @since 2.0.0 + * @category elements + */ + (self: Chunk, n: number): Chunk> +} = dual(2, (self: Chunk, n: number) => { + const gr: Array> = [] + let current: Array = [] + toReadonlyArray(self).forEach((a) => { + current.push(a) + if (current.length >= n) { + gr.push(unsafeFromArray(current)) + current = [] + } + }) + if (current.length > 0) { + gr.push(unsafeFromArray(current)) + } + return unsafeFromArray(gr) +}) + +/** + * Creates a Chunk of unique values that are included in all given Chunks. + * + * The order and references of result values are determined by the Chunk. + * + * @since 2.0.0 + * @category elements + */ +export const intersection: { + /** + * Creates a Chunk of unique values that are included in all given Chunks. + * + * The order and references of result values are determined by the Chunk. + * + * @since 2.0.0 + * @category elements + */ + (that: Chunk): (self: Chunk) => Chunk + /** + * Creates a Chunk of unique values that are included in all given Chunks. + * + * The order and references of result values are determined by the Chunk. + * + * @since 2.0.0 + * @category elements + */ + (self: Chunk, that: Chunk): Chunk +} = dual( + 2, + (self: Chunk, that: Chunk): Chunk => + unsafeFromArray(RA.intersection(toReadonlyArray(self), toReadonlyArray(that))) +) + +/** + * Determines if the chunk is empty. + * + * @since 2.0.0 + * @category elements + */ +export const isEmpty = (self: Chunk): boolean => self.length === 0 + +/** + * Determines if the chunk is not empty. + * + * @since 2.0.0 + * @category elements + */ +export const isNonEmpty = (self: Chunk): self is NonEmptyChunk => self.length > 0 + +/** + * Returns the first element of this chunk if it exists. + * + * @since 2.0.0 + * @category elements + */ +export const head: (self: Chunk) => Option = get(0) + +/** + * Returns the first element of this chunk. + * + * It will throw an error if the chunk is empty. + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeHead = (self: Chunk): A => unsafeGet(self, 0) + +/** + * Returns the first element of this non empty chunk. + * + * @since 2.0.0 + * @category elements + */ +export const headNonEmpty: (self: NonEmptyChunk) => A = unsafeHead + +/** + * Returns the last element of this chunk if it exists. + * + * @since 2.0.0 + * @category elements + */ +export const last = (self: Chunk): Option => get(self, self.length - 1) + +/** + * Returns the last element of this chunk. + * + * It will throw an error if the chunk is empty. + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeLast = (self: Chunk): A => unsafeGet(self, self.length - 1) + +/** + * Returns the last element of this non empty chunk. + * + * @since 3.4.0 + * @category elements + */ +export const lastNonEmpty: (self: NonEmptyChunk) => A = unsafeLast + +/** + * @since 2.0.0 + */ +export declare namespace Chunk { + /** + * @since 2.0.0 + */ + export type Infer> = S extends Chunk ? A : never + + /** + * @since 2.0.0 + */ + export type With, A> = S extends NonEmptyChunk ? NonEmptyChunk : Chunk + + /** + * @since 2.0.0 + */ + export type OrNonEmpty, T extends Chunk, A> = S extends NonEmptyChunk ? + NonEmptyChunk + : T extends NonEmptyChunk ? NonEmptyChunk + : Chunk + + /** + * @since 2.0.0 + */ + export type AndNonEmpty, T extends Chunk, A> = S extends NonEmptyChunk ? + T extends NonEmptyChunk ? NonEmptyChunk + : Chunk : + Chunk + + /** + * @since 2.0.0 + */ + export type Flatten>> = T extends NonEmptyChunk> ? NonEmptyChunk + : T extends Chunk> ? Chunk + : never +} + +/** + * Transforms the elements of a chunk using the specified mapping function. + * If the input chunk is non-empty, the resulting chunk will also be non-empty. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.map(Chunk.make(1, 2), (n) => n + 1) + * + * console.log(result) + * // { _id: 'Chunk', values: [ 2, 3 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ +export const map: { + /** + * Transforms the elements of a chunk using the specified mapping function. + * If the input chunk is non-empty, the resulting chunk will also be non-empty. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.map(Chunk.make(1, 2), (n) => n + 1) + * + * console.log(result) + * // { _id: 'Chunk', values: [ 2, 3 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + , B>(f: (a: Chunk.Infer, i: number) => B): (self: S) => Chunk.With + /** + * Transforms the elements of a chunk using the specified mapping function. + * If the input chunk is non-empty, the resulting chunk will also be non-empty. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.map(Chunk.make(1, 2), (n) => n + 1) + * + * console.log(result) + * // { _id: 'Chunk', values: [ 2, 3 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + (self: NonEmptyChunk, f: (a: A, i: number) => B): NonEmptyChunk + /** + * Transforms the elements of a chunk using the specified mapping function. + * If the input chunk is non-empty, the resulting chunk will also be non-empty. + * + * **Example** + * + * ```ts + * import { Chunk } from "effect" + * + * const result = Chunk.map(Chunk.make(1, 2), (n) => n + 1) + * + * console.log(result) + * // { _id: 'Chunk', values: [ 2, 3 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + (self: Chunk, f: (a: A, i: number) => B): Chunk +} = dual(2, (self: Chunk, f: (a: A, i: number) => B): Chunk => + self.backing._tag === "ISingleton" ? + of(f(self.backing.a, 0)) : + unsafeFromArray(pipe(toReadonlyArray(self), RA.map((a, i) => f(a, i))))) + +/** + * Statefully maps over the chunk, producing new elements of type `B`. + * + * @since 2.0.0 + * @category folding + */ +export const mapAccum: { + /** + * Statefully maps over the chunk, producing new elements of type `B`. + * + * @since 2.0.0 + * @category folding + */ + (s: S, f: (s: S, a: A) => readonly [S, B]): (self: Chunk) => [S, Chunk] + /** + * Statefully maps over the chunk, producing new elements of type `B`. + * + * @since 2.0.0 + * @category folding + */ + (self: Chunk, s: S, f: (s: S, a: A) => readonly [S, B]): [S, Chunk] +} = dual(3, (self: Chunk, s: S, f: (s: S, a: A) => readonly [S, B]): [S, Chunk] => { + const [s1, as] = RA.mapAccum(self, s, f) + return [s1, unsafeFromArray(as)] +}) + +/** + * Separate elements based on a predicate that also exposes the index of the element. + * + * @category filtering + * @since 2.0.0 + */ +export const partition: { + /** + * Separate elements based on a predicate that also exposes the index of the element. + * + * @category filtering + * @since 2.0.0 + */ + (refinement: (a: NoInfer, i: number) => a is B): (self: Chunk) => [excluded: Chunk>, satisfying: Chunk] + /** + * Separate elements based on a predicate that also exposes the index of the element. + * + * @category filtering + * @since 2.0.0 + */ + (predicate: (a: NoInfer, i: number) => boolean): (self: Chunk) => [excluded: Chunk, satisfying: Chunk] + /** + * Separate elements based on a predicate that also exposes the index of the element. + * + * @category filtering + * @since 2.0.0 + */ + (self: Chunk, refinement: (a: A, i: number) => a is B): [excluded: Chunk>, satisfying: Chunk] + /** + * Separate elements based on a predicate that also exposes the index of the element. + * + * @category filtering + * @since 2.0.0 + */ + (self: Chunk, predicate: (a: A, i: number) => boolean): [excluded: Chunk, satisfying: Chunk] +} = dual( + 2, + (self: Chunk, predicate: (a: A, i: number) => boolean): [excluded: Chunk, satisfying: Chunk] => + pipe( + RA.partition(toReadonlyArray(self), predicate), + ([l, r]) => [unsafeFromArray(l), unsafeFromArray(r)] + ) +) + +/** + * Partitions the elements of this chunk into two chunks using f. + * + * @category filtering + * @since 2.0.0 + */ +export const partitionMap: { + /** + * Partitions the elements of this chunk into two chunks using f. + * + * @category filtering + * @since 2.0.0 + */ + (f: (a: A) => Either): (self: Chunk) => [left: Chunk, right: Chunk] + /** + * Partitions the elements of this chunk into two chunks using f. + * + * @category filtering + * @since 2.0.0 + */ + (self: Chunk, f: (a: A) => Either): [left: Chunk, right: Chunk] +} = dual(2, (self: Chunk, f: (a: A) => Either): [left: Chunk, right: Chunk] => + pipe( + RA.partitionMap(toReadonlyArray(self), f), + ([l, r]) => [unsafeFromArray(l), unsafeFromArray(r)] + )) + +/** + * Partitions the elements of this chunk into two chunks. + * + * @category filtering + * @since 2.0.0 + */ +export const separate = (self: Chunk>): [Chunk, Chunk] => + pipe( + RA.separate(toReadonlyArray(self)), + ([l, r]) => [unsafeFromArray(l), unsafeFromArray(r)] + ) + +/** + * Retireves the size of the chunk + * + * @since 2.0.0 + * @category elements + */ +export const size = (self: Chunk): number => self.length + +/** + * Sort the elements of a Chunk in increasing order, creating a new Chunk. + * + * @since 2.0.0 + * @category sorting + */ +export const sort: { + /** + * Sort the elements of a Chunk in increasing order, creating a new Chunk. + * + * @since 2.0.0 + * @category sorting + */ + (O: Order.Order): (self: Chunk) => Chunk + /** + * Sort the elements of a Chunk in increasing order, creating a new Chunk. + * + * @since 2.0.0 + * @category sorting + */ + (self: Chunk, O: Order.Order): Chunk +} = dual( + 2, + (self: Chunk, O: Order.Order): Chunk => unsafeFromArray(RA.sort(toReadonlyArray(self), O)) +) + +/** + * @since 2.0.0 + * @category sorting + */ +export const sortWith: { + /** + * @since 2.0.0 + * @category sorting + */ + (f: (a: A) => B, order: Order.Order): (self: Chunk) => Chunk + /** + * @since 2.0.0 + * @category sorting + */ + (self: Chunk, f: (a: A) => B, order: Order.Order): Chunk +} = dual( + 3, + (self: Chunk, f: (a: A) => B, order: Order.Order): Chunk => sort(self, Order.mapInput(order, f)) +) + +/** + * Returns two splits of this chunk at the specified index. + * + * @since 2.0.0 + * @category splitting + */ +export const splitAt: { + /** + * Returns two splits of this chunk at the specified index. + * + * @since 2.0.0 + * @category splitting + */ + (n: number): (self: Chunk) => [beforeIndex: Chunk, fromIndex: Chunk] + /** + * Returns two splits of this chunk at the specified index. + * + * @since 2.0.0 + * @category splitting + */ + (self: Chunk, n: number): [beforeIndex: Chunk, fromIndex: Chunk] +} = dual(2, (self: Chunk, n: number): [Chunk, Chunk] => [take(self, n), drop(self, n)]) + +/** + * Splits a `NonEmptyChunk` into two segments, with the first segment containing a maximum of `n` elements. + * The value of `n` must be `>= 1`. + * + * @category splitting + * @since 2.0.0 + */ +export const splitNonEmptyAt: { + /** + * Splits a `NonEmptyChunk` into two segments, with the first segment containing a maximum of `n` elements. + * The value of `n` must be `>= 1`. + * + * @category splitting + * @since 2.0.0 + */ + (n: number): (self: NonEmptyChunk) => [beforeIndex: NonEmptyChunk, fromIndex: Chunk] + /** + * Splits a `NonEmptyChunk` into two segments, with the first segment containing a maximum of `n` elements. + * The value of `n` must be `>= 1`. + * + * @category splitting + * @since 2.0.0 + */ + (self: NonEmptyChunk, n: number): [beforeIndex: NonEmptyChunk, fromIndex: Chunk] +} = dual(2, (self: NonEmptyChunk, n: number): [Chunk, Chunk] => { + const _n = Math.max(1, Math.floor(n)) + return _n >= self.length ? + [self, empty()] : + [take(self, _n), drop(self, _n)] +}) + +/** + * Splits this chunk into `n` equally sized chunks. + * + * @since 2.0.0 + * @category splitting + */ +export const split: { + /** + * Splits this chunk into `n` equally sized chunks. + * + * @since 2.0.0 + * @category splitting + */ + (n: number): (self: Chunk) => Chunk> + /** + * Splits this chunk into `n` equally sized chunks. + * + * @since 2.0.0 + * @category splitting + */ + (self: Chunk, n: number): Chunk> +} = dual(2, (self: Chunk, n: number) => chunksOf(self, Math.ceil(self.length / Math.floor(n)))) + +/** + * Splits this chunk on the first element that matches this predicate. + * Returns a tuple containing two chunks: the first one is before the match, and the second one is from the match onward. + * + * @category splitting + * @since 2.0.0 + */ +export const splitWhere: { + /** + * Splits this chunk on the first element that matches this predicate. + * Returns a tuple containing two chunks: the first one is before the match, and the second one is from the match onward. + * + * @category splitting + * @since 2.0.0 + */ + (predicate: Predicate>): (self: Chunk) => [beforeMatch: Chunk, fromMatch: Chunk] + /** + * Splits this chunk on the first element that matches this predicate. + * Returns a tuple containing two chunks: the first one is before the match, and the second one is from the match onward. + * + * @category splitting + * @since 2.0.0 + */ + (self: Chunk, predicate: Predicate): [beforeMatch: Chunk, fromMatch: Chunk] +} = dual(2, (self: Chunk, predicate: Predicate): [beforeMatch: Chunk, fromMatch: Chunk] => { + let i = 0 + for (const a of toReadonlyArray(self)) { + if (predicate(a)) { + break + } else { + i++ + } + } + return splitAt(self, i) +}) + +/** + * Returns every elements after the first. + * + * @since 2.0.0 + * @category elements + */ +export const tail = (self: Chunk): Option> => self.length > 0 ? O.some(drop(self, 1)) : O.none() + +/** + * Returns every elements after the first. + * + * @since 2.0.0 + * @category elements + */ +export const tailNonEmpty = (self: NonEmptyChunk): Chunk => drop(self, 1) + +/** + * Takes the last `n` elements. + * + * @since 2.0.0 + * @category elements + */ +export const takeRight: { + /** + * Takes the last `n` elements. + * + * @since 2.0.0 + * @category elements + */ + (n: number): (self: Chunk) => Chunk + /** + * Takes the last `n` elements. + * + * @since 2.0.0 + * @category elements + */ + (self: Chunk, n: number): Chunk +} = dual(2, (self: Chunk, n: number): Chunk => drop(self, self.length - n)) + +/** + * Takes all elements so long as the predicate returns true. + * + * @since 2.0.0 + * @category elements + */ +export const takeWhile: { + /** + * Takes all elements so long as the predicate returns true. + * + * @since 2.0.0 + * @category elements + */ + (refinement: Refinement, B>): (self: Chunk) => Chunk + /** + * Takes all elements so long as the predicate returns true. + * + * @since 2.0.0 + * @category elements + */ + (predicate: Predicate>): (self: Chunk) => Chunk + /** + * Takes all elements so long as the predicate returns true. + * + * @since 2.0.0 + * @category elements + */ + (self: Chunk, refinement: Refinement): Chunk + /** + * Takes all elements so long as the predicate returns true. + * + * @since 2.0.0 + * @category elements + */ + (self: Chunk, predicate: Predicate): Chunk +} = dual(2, (self: Chunk, predicate: Predicate): Chunk => { + const out: Array = [] + for (const a of toReadonlyArray(self)) { + if (predicate(a)) { + out.push(a) + } else { + break + } + } + return unsafeFromArray(out) +}) + +/** + * Creates a Chunks of unique values, in order, from all given Chunks. + * + * @since 2.0.0 + * @category elements + */ +export const union: { + /** + * Creates a Chunks of unique values, in order, from all given Chunks. + * + * @since 2.0.0 + * @category elements + */ + (that: Chunk): (self: Chunk) => Chunk + /** + * Creates a Chunks of unique values, in order, from all given Chunks. + * + * @since 2.0.0 + * @category elements + */ + (self: Chunk, that: Chunk): Chunk +} = dual( + 2, + (self: Chunk, that: Chunk) => unsafeFromArray(RA.union(toReadonlyArray(self), toReadonlyArray(that))) +) + +/** + * Remove duplicates from an array, keeping the first occurrence of an element. + * + * @since 2.0.0 + * @category elements + */ +export const dedupe = (self: Chunk): Chunk => unsafeFromArray(RA.dedupe(toReadonlyArray(self))) + +/** + * Deduplicates adjacent elements that are identical. + * + * @since 2.0.0 + * @category filtering + */ +export const dedupeAdjacent = (self: Chunk): Chunk => unsafeFromArray(RA.dedupeAdjacent(self)) + +/** + * Takes a `Chunk` of pairs and return two corresponding `Chunk`s. + * + * Note: The function is reverse of `zip`. + * + * @since 2.0.0 + * @category elements + */ +export const unzip = (self: Chunk): [Chunk, Chunk] => { + const [left, right] = RA.unzip(self) + return [unsafeFromArray(left), unsafeFromArray(right)] +} + +/** + * Zips this chunk pointwise with the specified chunk using the specified combiner. + * + * @since 2.0.0 + * @category zipping + */ +export const zipWith: { + /** + * Zips this chunk pointwise with the specified chunk using the specified combiner. + * + * @since 2.0.0 + * @category zipping + */ + (that: Chunk, f: (a: A, b: B) => C): (self: Chunk) => Chunk + /** + * Zips this chunk pointwise with the specified chunk using the specified combiner. + * + * @since 2.0.0 + * @category zipping + */ + (self: Chunk, that: Chunk, f: (a: A, b: B) => C): Chunk +} = dual( + 3, + (self: Chunk, that: Chunk, f: (a: A, b: B) => C): Chunk => + unsafeFromArray(RA.zipWith(self, that, f)) +) + +/** + * Zips this chunk pointwise with the specified chunk. + * + * @since 2.0.0 + * @category zipping + */ +export const zip: { + /** + * Zips this chunk pointwise with the specified chunk. + * + * @since 2.0.0 + * @category zipping + */ + (that: Chunk): (self: Chunk) => Chunk<[A, B]> + /** + * Zips this chunk pointwise with the specified chunk. + * + * @since 2.0.0 + * @category zipping + */ + (self: Chunk, that: Chunk): Chunk<[A, B]> +} = dual( + 2, + (self: Chunk, that: Chunk): Chunk<[A, B]> => zipWith(self, that, (a, b) => [a, b]) +) + +/** + * Delete the element at the specified index, creating a new `Chunk`. + * + * @since 2.0.0 + */ +export const remove: { + /** + * Delete the element at the specified index, creating a new `Chunk`. + * + * @since 2.0.0 + */ + (i: number): (self: Chunk) => Chunk + /** + * Delete the element at the specified index, creating a new `Chunk`. + * + * @since 2.0.0 + */ + (self: Chunk, i: number): Chunk +} = dual( + 2, + (self: Chunk, i: number): Chunk => { + if (i < 0 || i >= self.length) return self + return unsafeFromArray(RA.remove(toReadonlyArray(self), i)) + } +) + +/** + * @since 3.16.0 + */ +export const removeOption: { + /** + * @since 3.16.0 + */ + (i: number): (self: Chunk) => Option> + /** + * @since 3.16.0 + */ + (self: Chunk, i: number): Option> +} = dual( + 2, + (self: Chunk, i: number): Option> => { + if (i < 0 || i >= self.length) return O.none() + return O.some(unsafeFromArray(RA.remove(toReadonlyArray(self), i))) + } +) + +/** + * @since 2.0.0 + */ +export const modifyOption: { + /** + * @since 2.0.0 + */ + (i: number, f: (a: A) => B): (self: Chunk) => Option> + /** + * @since 2.0.0 + */ + (self: Chunk, i: number, f: (a: A) => B): Option> +} = dual( + 3, + (self: Chunk, i: number, f: (a: A) => B): Option> => { + if (i < 0 || i >= self.length) return O.none() + return O.some(unsafeFromArray(RA.modify(toReadonlyArray(self), i, f))) + } +) + +/** + * Apply a function to the element at the specified index, creating a new `Chunk`, + * or returning the input if the index is out of bounds. + * + * @since 2.0.0 + */ +export const modify: { + /** + * Apply a function to the element at the specified index, creating a new `Chunk`, + * or returning the input if the index is out of bounds. + * + * @since 2.0.0 + */ + (i: number, f: (a: A) => B): (self: Chunk) => Chunk + /** + * Apply a function to the element at the specified index, creating a new `Chunk`, + * or returning the input if the index is out of bounds. + * + * @since 2.0.0 + */ + (self: Chunk, i: number, f: (a: A) => B): Chunk +} = dual( + 3, + (self: Chunk, i: number, f: (a: A) => B): Chunk => O.getOrElse(modifyOption(self, i, f), () => self) +) + +/** + * Change the element at the specified index, creating a new `Chunk`, + * or returning the input if the index is out of bounds. + * + * @since 2.0.0 + */ +export const replace: { + /** + * Change the element at the specified index, creating a new `Chunk`, + * or returning the input if the index is out of bounds. + * + * @since 2.0.0 + */ + (i: number, b: B): (self: Chunk) => Chunk + /** + * Change the element at the specified index, creating a new `Chunk`, + * or returning the input if the index is out of bounds. + * + * @since 2.0.0 + */ + (self: Chunk, i: number, b: B): Chunk +} = dual(3, (self: Chunk, i: number, b: B): Chunk => modify(self, i, () => b)) + +/** + * @since 2.0.0 + */ +export const replaceOption: { + /** + * @since 2.0.0 + */ + (i: number, b: B): (self: Chunk) => Option> + /** + * @since 2.0.0 + */ + (self: Chunk, i: number, b: B): Option> +} = dual(3, (self: Chunk, i: number, b: B): Option> => modifyOption(self, i, () => b)) + +/** + * Return a Chunk of length n with element i initialized with f(i). + * + * **Note**. `n` is normalized to an integer >= 1. + * + * @category constructors + * @since 2.0.0 + */ +export const makeBy: { + /** + * Return a Chunk of length n with element i initialized with f(i). + * + * **Note**. `n` is normalized to an integer >= 1. + * + * @category constructors + * @since 2.0.0 + */ + (f: (i: number) => A): (n: number) => NonEmptyChunk + /** + * Return a Chunk of length n with element i initialized with f(i). + * + * **Note**. `n` is normalized to an integer >= 1. + * + * @category constructors + * @since 2.0.0 + */ + (n: number, f: (i: number) => A): NonEmptyChunk +} = dual(2, (n, f) => fromIterable(RA.makeBy(n, f))) + +/** + * Create a non empty `Chunk` containing a range of integers, including both endpoints. + * + * @category constructors + * @since 2.0.0 + */ +export const range = (start: number, end: number): NonEmptyChunk => + start <= end ? makeBy(end - start + 1, (i) => start + i) : of(start) + +// ------------------------------------------------------------------------------------- +// re-exports from ReadonlyArray +// ------------------------------------------------------------------------------------- + +/** + * Returns a function that checks if a `Chunk` contains a given value using the default `Equivalence`. + * + * @category elements + * @since 2.0.0 + */ +export const contains: { + // ------------------------------------------------------------------------------------- + // re-exports from ReadonlyArray + // ------------------------------------------------------------------------------------- + + /** + * Returns a function that checks if a `Chunk` contains a given value using the default `Equivalence`. + * + * @category elements + * @since 2.0.0 + */ + (a: A): (self: Chunk) => boolean + // ------------------------------------------------------------------------------------- + // re-exports from ReadonlyArray + // ------------------------------------------------------------------------------------- + + /** + * Returns a function that checks if a `Chunk` contains a given value using the default `Equivalence`. + * + * @category elements + * @since 2.0.0 + */ + (self: Chunk, a: A): boolean +} = RA.contains + +/** + * Returns a function that checks if a `Chunk` contains a given value using a provided `isEquivalent` function. + * + * @category elements + * @since 2.0.0 + */ +export const containsWith: ( + isEquivalent: (self: A, that: A) => boolean +) => { + (a: A): (self: Chunk) => boolean + (self: Chunk, a: A): boolean +} = RA.containsWith + +/** + * Returns the first element that satisfies the specified + * predicate, or `None` if no such element exists. + * + * @category elements + * @since 2.0.0 + */ +export const findFirst: { + /** + * Returns the first element that satisfies the specified + * predicate, or `None` if no such element exists. + * + * @category elements + * @since 2.0.0 + */ + (refinement: Refinement, B>): (self: Chunk) => Option + /** + * Returns the first element that satisfies the specified + * predicate, or `None` if no such element exists. + * + * @category elements + * @since 2.0.0 + */ + (predicate: Predicate>): (self: Chunk) => Option + /** + * Returns the first element that satisfies the specified + * predicate, or `None` if no such element exists. + * + * @category elements + * @since 2.0.0 + */ + (self: Chunk, refinement: Refinement): Option + /** + * Returns the first element that satisfies the specified + * predicate, or `None` if no such element exists. + * + * @category elements + * @since 2.0.0 + */ + (self: Chunk, predicate: Predicate): Option +} = RA.findFirst + +/** + * Return the first index for which a predicate holds. + * + * @category elements + * @since 2.0.0 + */ +export const findFirstIndex: { + /** + * Return the first index for which a predicate holds. + * + * @category elements + * @since 2.0.0 + */ + (predicate: Predicate): (self: Chunk) => Option + /** + * Return the first index for which a predicate holds. + * + * @category elements + * @since 2.0.0 + */ + (self: Chunk, predicate: Predicate): Option +} = RA.findFirstIndex + +/** + * Find the last element for which a predicate holds. + * + * @category elements + * @since 2.0.0 + */ +export const findLast: { + /** + * Find the last element for which a predicate holds. + * + * @category elements + * @since 2.0.0 + */ + (refinement: Refinement, B>): (self: Chunk) => Option + /** + * Find the last element for which a predicate holds. + * + * @category elements + * @since 2.0.0 + */ + (predicate: Predicate>): (self: Chunk) => Option + /** + * Find the last element for which a predicate holds. + * + * @category elements + * @since 2.0.0 + */ + (self: Chunk, refinement: Refinement): Option + /** + * Find the last element for which a predicate holds. + * + * @category elements + * @since 2.0.0 + */ + (self: Chunk, predicate: Predicate): Option +} = RA.findLast + +/** + * Return the last index for which a predicate holds. + * + * @category elements + * @since 2.0.0 + */ +export const findLastIndex: { + /** + * Return the last index for which a predicate holds. + * + * @category elements + * @since 2.0.0 + */ + (predicate: Predicate): (self: Chunk) => Option + /** + * Return the last index for which a predicate holds. + * + * @category elements + * @since 2.0.0 + */ + (self: Chunk, predicate: Predicate): Option +} = RA.findLastIndex + +/** + * Check if a predicate holds true for every `Chunk` element. + * + * @category elements + * @since 2.0.0 + */ +export const every: { + /** + * Check if a predicate holds true for every `Chunk` element. + * + * @category elements + * @since 2.0.0 + */ + (refinement: Refinement, B>): (self: Chunk) => self is Chunk + /** + * Check if a predicate holds true for every `Chunk` element. + * + * @category elements + * @since 2.0.0 + */ + (predicate: Predicate): (self: Chunk) => boolean + /** + * Check if a predicate holds true for every `Chunk` element. + * + * @category elements + * @since 2.0.0 + */ + (self: Chunk, refinement: Refinement): self is Chunk + /** + * Check if a predicate holds true for every `Chunk` element. + * + * @category elements + * @since 2.0.0 + */ + (self: Chunk, predicate: Predicate): boolean +} = dual( + 2, + (self: Chunk, refinement: Refinement): self is Chunk => + RA.fromIterable(self).every(refinement) +) + +/** + * Check if a predicate holds true for some `Chunk` element. + * + * @category elements + * @since 2.0.0 + */ +export const some: { + /** + * Check if a predicate holds true for some `Chunk` element. + * + * @category elements + * @since 2.0.0 + */ + (predicate: Predicate>): (self: Chunk) => self is NonEmptyChunk + /** + * Check if a predicate holds true for some `Chunk` element. + * + * @category elements + * @since 2.0.0 + */ + (self: Chunk, predicate: Predicate): self is NonEmptyChunk +} = dual( + 2, + (self: Chunk, predicate: Predicate): self is NonEmptyChunk => RA.fromIterable(self).some(predicate) +) + +/** + * Joins the elements together with "sep" in the middle. + * + * @category folding + * @since 2.0.0 + */ +export const join: { + /** + * Joins the elements together with "sep" in the middle. + * + * @category folding + * @since 2.0.0 + */ + (sep: string): (self: Chunk) => string + /** + * Joins the elements together with "sep" in the middle. + * + * @category folding + * @since 2.0.0 + */ + (self: Chunk, sep: string): string +} = RA.join + +/** + * @category folding + * @since 2.0.0 + */ +export const reduce: { + /** + * @category folding + * @since 2.0.0 + */ + (b: B, f: (b: B, a: A, i: number) => B): (self: Chunk) => B + /** + * @category folding + * @since 2.0.0 + */ + (self: Chunk, b: B, f: (b: B, a: A, i: number) => B): B +} = RA.reduce + +/** + * @category folding + * @since 2.0.0 + */ +export const reduceRight: { + /** + * @category folding + * @since 2.0.0 + */ + (b: B, f: (b: B, a: A, i: number) => B): (self: Chunk) => B + /** + * @category folding + * @since 2.0.0 + */ + (self: Chunk, b: B, f: (b: B, a: A, i: number) => B): B +} = RA.reduceRight + +/** + * Creates a `Chunk` of values not included in the other given `Chunk` using the provided `isEquivalent` function. + * The order and references of result values are determined by the first `Chunk`. + * + * @since 3.2.0 + */ +export const differenceWith = (isEquivalent: (self: A, that: A) => boolean): { + (that: Chunk): (self: Chunk) => Chunk + (self: Chunk, that: Chunk): Chunk +} => { + return dual( + 2, + (self: Chunk, that: Chunk): Chunk => unsafeFromArray(RA.differenceWith(isEquivalent)(that, self)) + ) +} + +/** + * Creates a `Chunk` of values not included in the other given `Chunk`. + * The order and references of result values are determined by the first `Chunk`. + * + * @since 3.2.0 + */ +export const difference: { + /** + * Creates a `Chunk` of values not included in the other given `Chunk`. + * The order and references of result values are determined by the first `Chunk`. + * + * @since 3.2.0 + */ + (that: Chunk): (self: Chunk) => Chunk + /** + * Creates a `Chunk` of values not included in the other given `Chunk`. + * The order and references of result values are determined by the first `Chunk`. + * + * @since 3.2.0 + */ + (self: Chunk, that: Chunk): Chunk +} = dual( + 2, + (self: Chunk, that: Chunk): Chunk => unsafeFromArray(RA.difference(that, self)) +) diff --git a/backend/node_modules/effect/src/Clock.ts b/backend/node_modules/effect/src/Clock.ts new file mode 100644 index 0000000000000000000000000000000000000000..ae42987f091263919167a7832de9a379d647561c --- /dev/null +++ b/backend/node_modules/effect/src/Clock.ts @@ -0,0 +1,111 @@ +/** + * @since 2.0.0 + */ +import type * as Context from "./Context.js" +import type * as Duration from "./Duration.js" +import type * as Effect from "./Effect.js" +import * as internal from "./internal/clock.js" +import * as defaultServices from "./internal/defaultServices.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const ClockTypeId: unique symbol = internal.ClockTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type ClockTypeId = typeof ClockTypeId + +/** + * Represents a time-based clock which provides functionality related to time + * and scheduling. + * + * @since 2.0.0 + * @category models + */ +export interface Clock { + readonly [ClockTypeId]: ClockTypeId + /** + * Unsafely returns the current time in milliseconds. + */ + unsafeCurrentTimeMillis(): number + /** + * Returns the current time in milliseconds. + */ + readonly currentTimeMillis: Effect.Effect + /** + * Unsafely returns the current time in nanoseconds. + */ + unsafeCurrentTimeNanos(): bigint + /** + * Returns the current time in nanoseconds. + */ + readonly currentTimeNanos: Effect.Effect + /** + * Asynchronously sleeps for the specified duration. + */ + sleep(duration: Duration.Duration): Effect.Effect +} + +/** + * @since 2.0.0 + * @category models + */ +export type CancelToken = () => boolean + +/** + * @since 2.0.0 + * @category models + */ +export type Task = () => void + +/** + * @since 2.0.0 + * @category models + */ +export interface ClockScheduler { + /** + * Unsafely schedules the specified task for the specified duration. + */ + unsafeSchedule(task: Task, duration: Duration.Duration): CancelToken +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const make: (_: void) => Clock = internal.make + +/** + * @since 2.0.0 + * @category constructors + */ +export const sleep: (duration: Duration.DurationInput) => Effect.Effect = defaultServices.sleep + +/** + * @since 2.0.0 + * @category constructors + */ +export const currentTimeMillis: Effect.Effect = defaultServices.currentTimeMillis + +/** + * @since 2.0.0 + * @category constructors + */ +export const currentTimeNanos: Effect.Effect = defaultServices.currentTimeNanos + +/** + * @since 2.0.0 + * @category constructors + */ +export const clockWith: (f: (clock: Clock) => Effect.Effect) => Effect.Effect = + defaultServices.clockWith + +/** + * @since 2.0.0 + * @category context + */ +export const Clock: Context.Tag = internal.clockTag diff --git a/backend/node_modules/effect/src/Config.ts b/backend/node_modules/effect/src/Config.ts new file mode 100644 index 0000000000000000000000000000000000000000..0dbaf6a5bcb7ef391e16bf3bc689a609bdff67e0 --- /dev/null +++ b/backend/node_modules/effect/src/Config.ts @@ -0,0 +1,743 @@ +/** + * @since 2.0.0 + */ +import type * as Brand from "./Brand.js" +import type * as Chunk from "./Chunk.js" +import type * as ConfigError from "./ConfigError.js" +import type * as Duration from "./Duration.js" +import type * as Effect from "./Effect.js" +import type * as Either from "./Either.js" +import type { LazyArg } from "./Function.js" +import type * as HashMap from "./HashMap.js" +import type * as HashSet from "./HashSet.js" +import * as internal from "./internal/config.js" +import type * as LogLevel from "./LogLevel.js" +import type * as Option from "./Option.js" +import type { Predicate, Refinement } from "./Predicate.js" +import type * as Redacted from "./Redacted.js" +import type * as Secret from "./Secret.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const ConfigTypeId: unique symbol = internal.ConfigTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type ConfigTypeId = typeof ConfigTypeId + +/** + * A `Config` describes the structure of some configuration data. + * + * @since 2.0.0 + * @category models + */ +export interface Config extends Config.Variance, Effect.Effect {} + +/** + * @since 2.0.0 + */ +export declare namespace Config { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [ConfigTypeId]: { + readonly _A: Types.Covariant + } + } + + /** + * @since 2.5.0 + * @category models + */ + export type Success> = [T] extends [Config] ? _A : never + + /** + * @since 2.0.0 + * @category models + */ + export interface Primitive extends Config { + readonly description: string + parse(text: string): Either.Either + } + + /** + * Wraps a nested structure, converting all primitives to a `Config`. + * + * `Config.Wrap<{ key: string }>` becomes `{ key: Config }` + * + * To create the resulting config, use the `unwrap` constructor. + * + * @since 2.0.0 + * @category models + */ + export type Wrap = [NonNullable] extends [infer T] ? [IsPlainObject] extends [true] ? + | { readonly [K in keyof A]: Wrap } + | Config + : Config + : Config + + type IsPlainObject = [A] extends [Record] + ? [keyof A] extends [never] ? false : [keyof A] extends [string] ? true : false + : false +} + +/** + * @since 2.0.0 + * @category models + */ +export type LiteralValue = string | number | boolean | null | bigint + +/** + * Constructs a config from a tuple / struct / arguments of configs. + * + * @since 2.0.0 + * @category constructors + */ +export const all: > | Record>>( + arg: Arg +) => Config< + [Arg] extends [ReadonlyArray>] ? { + -readonly [K in keyof Arg]: [Arg[K]] extends [Config] ? A : never + } + : [Arg] extends [Iterable>] ? Array + : [Arg] extends [Record>] ? { + -readonly [K in keyof Arg]: [Arg[K]] extends [Config] ? A : never + } + : never +> = internal.all + +/** + * Constructs a config for an array of values. + * + * @since 2.0.0 + * @category constructors + */ +export const array: (config: Config, name?: string) => Config> = internal.array + +/** + * Constructs a config for a boolean value. + * + * @since 2.0.0 + * @category constructors + */ +export const boolean: (name?: string) => Config = internal.boolean + +/** + * Constructs a config for a network port [1, 65535]. + * + * @since 3.16.0 + * @category constructors + */ +export const port: (name?: string) => Config = internal.port + +/** + * Constructs a config for an URL value. + * + * @since 3.11.0 + * @category constructors + */ +export const url: (name?: string) => Config = internal.url + +/** + * Constructs a config for a sequence of values. + * + * @since 2.0.0 + * @category constructors + */ +export const chunk: (config: Config, name?: string) => Config> = internal.chunk + +/** + * Constructs a config for a date value. + * + * @since 2.0.0 + * @category constructors + */ +export const date: (name?: string) => Config = internal.date + +/** + * Constructs a config that fails with the specified message. + * + * @since 2.0.0 + * @category constructors + */ +export const fail: (message: string) => Config = internal.fail + +/** + * Constructs a config for a float value. + * + * @since 2.0.0 + * @category constructors + */ +export const number: (name?: string) => Config = internal.number + +/** + * Constructs a config for a integer value. + * + * @since 2.0.0 + * @category constructors + */ +export const integer: (name?: string) => Config = internal.integer + +/** + * Constructs a config for a literal value. + * + * **Example** + * + * ```ts + * import { Config } from "effect" + * + * const config = Config.literal("http", "https")("PROTOCOL") + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const literal: >(...literals: Literals) => ( + name?: string +) => Config = internal.literal + +/** + * Constructs a config for a `LogLevel` value. + * + * @since 2.0.0 + * @category constructors + */ +export const logLevel: (name?: string) => Config = internal.logLevel + +/** + * Constructs a config for a duration value. + * + * @since 2.5.0 + * @category constructors + */ +export const duration: (name?: string) => Config = internal.duration + +/** + * This function returns `true` if the specified value is an `Config` value, + * `false` otherwise. + * + * This function can be useful for checking the type of a value before + * attempting to operate on it as an `Config` value. For example, you could + * use `isConfig` to check the type of a value before using it as an + * argument to a function that expects an `Config` value. + * + * @since 2.0.0 + * @category refinements + */ +export const isConfig: (u: unknown) => u is Config = internal.isConfig + +/** + * Returns a config whose structure is the same as this one, but which produces + * a different value, constructed using the specified function. + * + * @since 2.0.0 + * @category mapping + */ +export const map: { + /** + * Returns a config whose structure is the same as this one, but which produces + * a different value, constructed using the specified function. + * + * @since 2.0.0 + * @category mapping + */ + (f: (a: A) => B): (self: Config) => Config + /** + * Returns a config whose structure is the same as this one, but which produces + * a different value, constructed using the specified function. + * + * @since 2.0.0 + * @category mapping + */ + (self: Config, f: (a: A) => B): Config +} = internal.map + +/** + * Returns a config whose structure is the same as this one, but which may + * produce a different value, constructed using the specified function, which + * may throw exceptions that will be translated into validation errors. + * + * @since 2.0.0 + * @category utils + */ +export const mapAttempt: { + /** + * Returns a config whose structure is the same as this one, but which may + * produce a different value, constructed using the specified function, which + * may throw exceptions that will be translated into validation errors. + * + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => B): (self: Config) => Config + /** + * Returns a config whose structure is the same as this one, but which may + * produce a different value, constructed using the specified function, which + * may throw exceptions that will be translated into validation errors. + * + * @since 2.0.0 + * @category utils + */ + (self: Config, f: (a: A) => B): Config +} = internal.mapAttempt + +/** + * Returns a new config whose structure is the samea as this one, but which + * may produce a different value, constructed using the specified fallible + * function. + * + * @since 2.0.0 + * @category utils + */ +export const mapOrFail: { + /** + * Returns a new config whose structure is the samea as this one, but which + * may produce a different value, constructed using the specified fallible + * function. + * + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Either.Either): (self: Config) => Config + /** + * Returns a new config whose structure is the samea as this one, but which + * may produce a different value, constructed using the specified fallible + * function. + * + * @since 2.0.0 + * @category utils + */ + (self: Config, f: (a: A) => Either.Either): Config +} = internal.mapOrFail + +/** + * Returns a config that has this configuration nested as a property of the + * specified name. + * + * @since 2.0.0 + * @category utils + */ +export const nested: { + /** + * Returns a config that has this configuration nested as a property of the + * specified name. + * + * @since 2.0.0 + * @category utils + */ + (name: string): (self: Config) => Config + /** + * Returns a config that has this configuration nested as a property of the + * specified name. + * + * @since 2.0.0 + * @category utils + */ + (self: Config, name: string): Config +} = internal.nested + +/** + * Returns a config whose structure is preferentially described by this + * config, but which falls back to the specified config if there is an issue + * reading from this config. + * + * @since 2.0.0 + * @category utils + */ +export const orElse: { + /** + * Returns a config whose structure is preferentially described by this + * config, but which falls back to the specified config if there is an issue + * reading from this config. + * + * @since 2.0.0 + * @category utils + */ + (that: LazyArg>): (self: Config) => Config + /** + * Returns a config whose structure is preferentially described by this + * config, but which falls back to the specified config if there is an issue + * reading from this config. + * + * @since 2.0.0 + * @category utils + */ + (self: Config, that: LazyArg>): Config +} = internal.orElse + +/** + * Returns configuration which reads from this configuration, but which falls + * back to the specified configuration if reading from this configuration + * fails with an error satisfying the specified predicate. + * + * @since 2.0.0 + * @category utils + */ +export const orElseIf: { + /** + * Returns configuration which reads from this configuration, but which falls + * back to the specified configuration if reading from this configuration + * fails with an error satisfying the specified predicate. + * + * @since 2.0.0 + * @category utils + */ + ( + options: { + readonly if: Predicate + readonly orElse: LazyArg> + } + ): (self: Config) => Config + /** + * Returns configuration which reads from this configuration, but which falls + * back to the specified configuration if reading from this configuration + * fails with an error satisfying the specified predicate. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Config, + options: { + readonly if: Predicate + readonly orElse: LazyArg> + } + ): Config +} = internal.orElseIf + +/** + * Returns an optional version of this config, which will be `None` if the + * data is missing from configuration, and `Some` otherwise. + * + * @since 2.0.0 + * @category utils + */ +export const option: (self: Config) => Config> = internal.option + +/** + * Constructs a new primitive config. + * + * @since 2.0.0 + * @category constructors + */ +export const primitive: ( + description: string, + parse: (text: string) => Either.Either +) => Config = internal.primitive + +/** + * Returns a config that describes a sequence of values, each of which has the + * structure of this config. + * + * @since 2.0.0 + * @category utils + */ +export const repeat: (self: Config) => Config> = internal.repeat + +/** + * Constructs a config for a secret value. + * + * @since 2.0.0 + * @category constructors + * @deprecated + */ +export const secret: (name?: string) => Config = internal.secret + +/** + * Constructs a config for a redacted value. + * + * @since 2.0.0 + * @category constructors + */ +export const redacted: { + /** + * Constructs a config for a redacted value. + * + * @since 2.0.0 + * @category constructors + */ + (name?: string): Config + /** + * Constructs a config for a redacted value. + * + * @since 2.0.0 + * @category constructors + */ + (config: Config): Config> +} = internal.redacted + +/** + * Constructs a config for a branded value. + * + * @since 3.16.0 + * @category constructors + */ +export const branded: { + /** + * Constructs a config for a branded value. + * + * @since 3.16.0 + * @category constructors + */ + >(constructor: Brand.Brand.Constructor): (config: Config) => Config + /** + * Constructs a config for a branded value. + * + * @since 3.16.0 + * @category constructors + */ + >(name: string | undefined, constructor: Brand.Brand.Constructor): Config + /** + * Constructs a config for a branded value. + * + * @since 3.16.0 + * @category constructors + */ + >(config: Config, constructor: Brand.Brand.Constructor): Config +} = internal.branded + +/** + * Constructs a config for a sequence of values. + * + * @since 2.0.0 + * @category constructors + */ +export const hashSet: (config: Config, name?: string) => Config> = internal.hashSet + +/** + * Constructs a config for a string value. + * + * @since 2.0.0 + * @category constructors + */ +export const string: (name?: string) => Config = internal.string + +/** + * Constructs a config for a non-empty string value. + * + * @since 3.7.0 + * @category constructors + */ +export const nonEmptyString: (name?: string) => Config = internal.nonEmptyString + +/** + * Constructs a config which contains the specified value. + * + * @since 2.0.0 + * @category constructors + */ +export const succeed: (value: A) => Config = internal.succeed + +/** + * Lazily constructs a config. + * + * @since 2.0.0 + * @category constructors + */ +export const suspend: (config: LazyArg>) => Config = internal.suspend + +/** + * Constructs a config which contains the specified lazy value. + * + * @since 2.0.0 + * @category constructors + */ +export const sync: (value: LazyArg) => Config = internal.sync + +/** + * Constructs a config for a sequence of values. + * + * @since 2.0.0 + * @category constructors + */ +export const hashMap: (config: Config, name?: string) => Config> = internal.hashMap + +/** + * Constructs a config from some configuration wrapped with the `Wrap` utility type. + * + * For example: + * + * ``` + * import { Config, unwrap } from "./Config" + * + * interface Options { key: string } + * + * const makeConfig = (config: Config.Wrap): Config => unwrap(config) + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const unwrap: (wrapped: Config.Wrap) => Config = internal.unwrap + +/** + * Returns a config that describes the same structure as this one, but which + * performs validation during loading. + * + * @since 2.0.0 + * @category utils + */ +export const validate: { + /** + * Returns a config that describes the same structure as this one, but which + * performs validation during loading. + * + * @since 2.0.0 + * @category utils + */ + ( + options: { + readonly message: string + readonly validation: Refinement + } + ): (self: Config) => Config + /** + * Returns a config that describes the same structure as this one, but which + * performs validation during loading. + * + * @since 2.0.0 + * @category utils + */ + ( + options: { + readonly message: string + readonly validation: Predicate + } + ): (self: Config) => Config + /** + * Returns a config that describes the same structure as this one, but which + * performs validation during loading. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Config, + options: { + readonly message: string + readonly validation: Refinement + } + ): Config + /** + * Returns a config that describes the same structure as this one, but which + * performs validation during loading. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Config, + options: { + readonly message: string + readonly validation: Predicate + } + ): Config +} = internal.validate + +/** + * Returns a config that describes the same structure as this one, but has the + * specified default value in case the information cannot be found. + * + * @since 2.0.0 + * @category utils + */ +export const withDefault: { + /** + * Returns a config that describes the same structure as this one, but has the + * specified default value in case the information cannot be found. + * + * @since 2.0.0 + * @category utils + */ + (def: A2): (self: Config) => Config + /** + * Returns a config that describes the same structure as this one, but has the + * specified default value in case the information cannot be found. + * + * @since 2.0.0 + * @category utils + */ + (self: Config, def: A2): Config +} = internal.withDefault + +/** + * Adds a description to this configuration, which is intended for humans. + * + * @since 2.0.0 + * @category utils + */ +export const withDescription: { + /** + * Adds a description to this configuration, which is intended for humans. + * + * @since 2.0.0 + * @category utils + */ + (description: string): (self: Config) => Config + /** + * Adds a description to this configuration, which is intended for humans. + * + * @since 2.0.0 + * @category utils + */ + (self: Config, description: string): Config +} = internal.withDescription + +/** + * Returns a config that is the composition of this config and the specified + * config. + * + * @since 2.0.0 + * @category zipping + */ +export const zip: { + /** + * Returns a config that is the composition of this config and the specified + * config. + * + * @since 2.0.0 + * @category zipping + */ + (that: Config): (self: Config) => Config<[A, B]> + /** + * Returns a config that is the composition of this config and the specified + * config. + * + * @since 2.0.0 + * @category zipping + */ + (self: Config, that: Config): Config<[A, B]> +} = internal.zip + +/** + * Returns a config that is the composes this config and the specified config + * using the provided function. + * + * @since 2.0.0 + * @category zipping + */ +export const zipWith: { + /** + * Returns a config that is the composes this config and the specified config + * using the provided function. + * + * @since 2.0.0 + * @category zipping + */ + (that: Config, f: (a: A, b: B) => C): (self: Config) => Config + /** + * Returns a config that is the composes this config and the specified config + * using the provided function. + * + * @since 2.0.0 + * @category zipping + */ + (self: Config, that: Config, f: (a: A, b: B) => C): Config +} = internal.zipWith diff --git a/backend/node_modules/effect/src/ConfigError.ts b/backend/node_modules/effect/src/ConfigError.ts new file mode 100644 index 0000000000000000000000000000000000000000..482e16f37f996699832e14f3900066c885acc38b --- /dev/null +++ b/backend/node_modules/effect/src/ConfigError.ts @@ -0,0 +1,286 @@ +/** + * @since 2.0.0 + */ +import type * as Cause from "./Cause.js" +import * as internal from "./internal/configError.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const ConfigErrorTypeId: unique symbol = internal.ConfigErrorTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type ConfigErrorTypeId = typeof ConfigErrorTypeId + +/** + * The possible ways that loading configuration data may fail. + * + * @since 2.0.0 + * @category models + */ +export type ConfigError = + | And + | Or + | InvalidData + | MissingData + | SourceUnavailable + | Unsupported + +/** + * @since 2.0.0 + */ +export declare namespace ConfigError { + /** + * @since 2.0.0 + * @category models + */ + export interface Proto { + readonly _tag: "ConfigError" + readonly [ConfigErrorTypeId]: ConfigErrorTypeId + } + + /** + * @since 2.0.0 + * @category models + */ + export type Reducer = ConfigErrorReducer +} + +/** + * @since 2.0.0 + * @category models + */ +export interface ConfigErrorReducer { + andCase(context: C, left: Z, right: Z): Z + orCase(context: C, left: Z, right: Z): Z + invalidDataCase(context: C, path: Array, message: string): Z + missingDataCase(context: C, path: Array, message: string): Z + sourceUnavailableCase( + context: C, + path: Array, + message: string, + cause: Cause.Cause + ): Z + unsupportedCase(context: C, path: Array, message: string): Z +} + +/** + * @since 2.0.0 + * @category models + */ +export interface And extends ConfigError.Proto { + readonly _op: "And" + readonly left: ConfigError + readonly right: ConfigError + readonly message: string +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Or extends ConfigError.Proto { + readonly _op: "Or" + readonly left: ConfigError + readonly right: ConfigError + readonly message: string +} + +/** + * @since 2.0.0 + * @category models + */ +export interface InvalidData extends ConfigError.Proto { + readonly _op: "InvalidData" + readonly path: Array + readonly message: string +} + +/** + * @since 2.0.0 + * @category models + */ +export interface MissingData extends ConfigError.Proto { + readonly _op: "MissingData" + readonly path: Array + readonly message: string +} + +/** + * @since 2.0.0 + * @category models + */ +export interface SourceUnavailable extends ConfigError.Proto { + readonly _op: "SourceUnavailable" + readonly path: Array + readonly message: string + readonly cause: Cause.Cause +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Unsupported extends ConfigError.Proto { + readonly _op: "Unsupported" + readonly path: Array + readonly message: string +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Options { + readonly pathDelim: string +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const And: (self: ConfigError, that: ConfigError) => ConfigError = internal.And + +/** + * @since 2.0.0 + * @category constructors + */ +export const Or: (self: ConfigError, that: ConfigError) => ConfigError = internal.Or + +/** + * @since 2.0.0 + * @category constructors + */ +export const MissingData: (path: Array, message: string, options?: Options) => ConfigError = + internal.MissingData + +/** + * @since 2.0.0 + * @category constructors + */ +export const InvalidData: (path: Array, message: string, options?: Options) => ConfigError = + internal.InvalidData + +/** + * @since 2.0.0 + * @category constructors + */ +export const SourceUnavailable: ( + path: Array, + message: string, + cause: Cause.Cause, + options?: Options +) => ConfigError = internal.SourceUnavailable + +/** + * @since 2.0.0 + * @category constructors + */ +export const Unsupported: (path: Array, message: string, options?: Options) => ConfigError = + internal.Unsupported + +/** + * Returns `true` if the specified value is a `ConfigError`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isConfigError: (u: unknown) => u is ConfigError = internal.isConfigError + +/** + * Returns `true` if the specified `ConfigError` is an `And`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isAnd: (self: ConfigError) => self is And = internal.isAnd + +/** + * Returns `true` if the specified `ConfigError` is an `Or`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isOr: (self: ConfigError) => self is Or = internal.isOr + +/** + * Returns `true` if the specified `ConfigError` is an `InvalidData`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isInvalidData: (self: ConfigError) => self is InvalidData = internal.isInvalidData + +/** + * Returns `true` if the specified `ConfigError` is an `MissingData`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isMissingData: (self: ConfigError) => self is MissingData = internal.isMissingData + +/** + * Returns `true` if the specified `ConfigError` contains only `MissingData` errors, `false` otherwise. + * + * @since 2.0.0 + * @categer getters + */ +export const isMissingDataOnly: (self: ConfigError) => boolean = internal.isMissingDataOnly + +/** + * Returns `true` if the specified `ConfigError` is a `SourceUnavailable`, + * `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isSourceUnavailable: (self: ConfigError) => self is SourceUnavailable = internal.isSourceUnavailable + +/** + * Returns `true` if the specified `ConfigError` is an `Unsupported`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isUnsupported: (self: ConfigError) => self is Unsupported = internal.isUnsupported + +/** + * @since 2.0.0 + * @category utils + */ +export const prefixed: { + /** + * @since 2.0.0 + * @category utils + */ + (prefix: Array): (self: ConfigError) => ConfigError + /** + * @since 2.0.0 + * @category utils + */ + (self: ConfigError, prefix: Array): ConfigError +} = internal.prefixed + +/** + * @since 2.0.0 + * @category folding + */ +export const reduceWithContext: { + /** + * @since 2.0.0 + * @category folding + */ + (context: C, reducer: ConfigErrorReducer): (self: ConfigError) => Z + /** + * @since 2.0.0 + * @category folding + */ + (self: ConfigError, context: C, reducer: ConfigErrorReducer): Z +} = internal.reduceWithContext diff --git a/backend/node_modules/effect/src/ConfigProviderPathPatch.ts b/backend/node_modules/effect/src/ConfigProviderPathPatch.ts new file mode 100644 index 0000000000000000000000000000000000000000..3fc3c09f2585b9abb712eb6df6b0676fd066554a --- /dev/null +++ b/backend/node_modules/effect/src/ConfigProviderPathPatch.ts @@ -0,0 +1,132 @@ +/** + * @since 2.0.0 + */ +import * as internal from "./internal/configProvider/pathPatch.js" + +/** + * Represents a description of how to modify the path to a configuration + * value. + * + * @since 2.0.0 + * @category models + */ +export type PathPatch = Empty | AndThen | MapName | Nested | Unnested + +/** + * @since 2.0.0 + * @category models + */ +export interface Empty { + readonly _tag: "Empty" +} + +/** + * @since 2.0.0 + * @category models + */ +export interface AndThen { + readonly _tag: "AndThen" + readonly first: PathPatch + readonly second: PathPatch +} + +/** + * @since 2.0.0 + * @category models + */ +export interface MapName { + readonly _tag: "MapName" + f(string: string): string +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Nested { + readonly _tag: "Nested" + readonly name: string +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Unnested { + readonly _tag: "Unnested" + readonly name: string +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const empty: PathPatch = internal.empty + +/** + * @since 2.0.0 + * @category constructors + */ +export const andThen: { + /** + * @since 2.0.0 + * @category constructors + */ + (that: PathPatch): (self: PathPatch) => PathPatch + /** + * @since 2.0.0 + * @category constructors + */ + (self: PathPatch, that: PathPatch): PathPatch +} = internal.andThen + +/** + * @since 2.0.0 + * @category constructors + */ +export const mapName: { + /** + * @since 2.0.0 + * @category constructors + */ + (f: (string: string) => string): (self: PathPatch) => PathPatch + /** + * @since 2.0.0 + * @category constructors + */ + (self: PathPatch, f: (string: string) => string): PathPatch +} = internal.mapName + +/** + * @since 2.0.0 + * @category constructors + */ +export const nested: { + /** + * @since 2.0.0 + * @category constructors + */ + (name: string): (self: PathPatch) => PathPatch + /** + * @since 2.0.0 + * @category constructors + */ + (self: PathPatch, name: string): PathPatch +} = internal.nested + +/** + * @since 2.0.0 + * @category constructors + */ +export const unnested: { + /** + * @since 2.0.0 + * @category constructors + */ + (name: string): (self: PathPatch) => PathPatch + /** + * @since 2.0.0 + * @category constructors + */ + (self: PathPatch, name: string): PathPatch +} = internal.unnested diff --git a/backend/node_modules/effect/src/Context.ts b/backend/node_modules/effect/src/Context.ts new file mode 100644 index 0000000000000000000000000000000000000000..3c616b41a1b186fcf4316940e1cf8e8d6f9dbaf7 --- /dev/null +++ b/backend/node_modules/effect/src/Context.ts @@ -0,0 +1,867 @@ +/** + * This module provides a data structure called `Context` that can be used for dependency injection in effectful + * programs. It is essentially a table mapping `Tag`s to their implementations (called `Service`s), and can be used to + * manage dependencies in a type-safe way. The `Context` data structure is essentially a way of providing access to a set + * of related services that can be passed around as a single unit. This module provides functions to create, modify, and + * query the contents of a `Context`, as well as a number of utility types for working with tags and services. + * + * @since 2.0.0 + */ +import type * as Effect from "./Effect.js" +import type { Equal } from "./Equal.js" +import type { LazyArg } from "./Function.js" +import type { Inspectable } from "./Inspectable.js" +import * as internal from "./internal/context.js" +import type { Option } from "./Option.js" +import type { Pipeable } from "./Pipeable.js" +import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" + +/** + * @since 2.0.0 + * @category symbol + */ +export const TagTypeId: unique symbol = internal.TagTypeId + +/** + * @since 2.0.0 + * @category symbol + */ +export type TagTypeId = typeof TagTypeId + +/** + * @since 3.5.9 + * @category models + */ +export interface Tag extends Pipeable, Inspectable, ReadonlyTag { + readonly _op: "Tag" + readonly Service: Value + readonly Identifier: Id + readonly [TagTypeId]: { + readonly _Service: Types.Invariant + readonly _Identifier: Types.Invariant + } + of(self: Value): Value + context(self: Value): Context + readonly stack?: string | undefined + readonly key: string + [Unify.typeSymbol]?: unknown + [Unify.unifySymbol]?: TagUnify + [Unify.ignoreSymbol]?: TagUnifyIgnore +} + +/** + * @since 3.5.9 + * @category models + */ +export interface ReadonlyTag extends Pipeable, Inspectable, Effect.Effect { + readonly _op: "Tag" + readonly Service: Value + readonly Identifier: Id + readonly [TagTypeId]: { + readonly _Service: Types.Covariant + readonly _Identifier: Types.Invariant + } + readonly stack?: string | undefined + readonly key: string +} + +/** + * @since 3.11.0 + * @category symbol + */ +export const ReferenceTypeId: unique symbol = internal.ReferenceTypeId + +/** + * @since 3.11.0 + * @category symbol + */ +export type ReferenceTypeId = typeof ReferenceTypeId + +/** + * @since 3.11.0 + * @category models + */ +export interface Reference extends Pipeable, Inspectable { + readonly [ReferenceTypeId]: ReferenceTypeId + readonly defaultValue: () => Value + + readonly _op: "Tag" + readonly Service: Value + readonly Identifier: Id + readonly [TagTypeId]: { + readonly _Service: Types.Invariant + readonly _Identifier: Types.Invariant + } + of(self: Value): Value + context(self: Value): Context + readonly stack?: string | undefined + readonly key: string + [Unify.typeSymbol]?: unknown + [Unify.unifySymbol]?: TagUnify + [Unify.ignoreSymbol]?: TagUnifyIgnore +} + +/** + * @since 2.0.0 + * @category models + */ +export interface TagClassShape { + readonly [TagTypeId]: TagTypeId + readonly Type: Shape + readonly Id: Id +} + +// TODO(4.0): move key narrowing to the Tag interface +/** + * @since 2.0.0 + * @category models + */ +export interface TagClass extends Tag { + new(_: never): TagClassShape + readonly key: Id +} + +// TODO(4.0): move key narrowing to the Reference interface +/** + * @since 3.11.0 + * @category models + */ +export interface ReferenceClass extends Reference { + new(_: never): TagClassShape + readonly key: Id +} + +/** + * @category models + * @since 2.0.0 + */ +export interface TagUnify { + Tag?: () => Extract> +} + +/** + * @category models + * @since 2.0.0 + */ +export interface TagUnifyIgnore {} + +/** + * @since 2.0.0 + */ +export declare namespace Tag { + /** + * @since 2.0.0 + */ + export type Service | TagClassShape> = T extends Tag ? T["Service"] + : T extends TagClassShape ? A + : never + /** + * @since 2.0.0 + */ + export type Identifier | TagClassShape> = T extends Tag ? T["Identifier"] + : T extends TagClassShape ? T + : never +} + +/** + * Creates a new `Tag` instance with an optional key parameter. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context } from "effect" + * + * assert.strictEqual(Context.GenericTag("PORT").key === Context.GenericTag("PORT").key, true) + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const GenericTag: (key: string) => Tag = + internal.makeGenericTag + +const TypeId: unique symbol = internal.TypeId as TypeId + +/** + * @since 2.0.0 + * @category symbol + */ +export type TypeId = typeof TypeId + +/** + * @since 2.0.0 + * @category models + */ +export type ValidTagsById = R extends infer S ? Tag : never + +/** + * @since 2.0.0 + * @category models + */ +export interface Context extends Equal, Pipeable, Inspectable { + readonly [TypeId]: { + readonly _Services: Types.Contravariant + } + readonly unsafeMap: Map +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const unsafeMake: (unsafeMap: Map) => Context = internal.makeContext + +/** + * Checks if the provided argument is a `Context`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context } from "effect" + * + * assert.strictEqual(Context.isContext(Context.empty()), true) + * ``` + * + * @since 2.0.0 + * @category guards + */ +export const isContext: (input: unknown) => input is Context = internal.isContext + +/** + * Checks if the provided argument is a `Tag`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context } from "effect" + * + * assert.strictEqual(Context.isTag(Context.GenericTag("Tag")), true) + * ``` + * + * @since 2.0.0 + * @category guards + */ +export const isTag: (input: unknown) => input is Tag = internal.isTag + +/** + * Checks if the provided argument is a `Reference`. + * + * @since 3.11.0 + * @category guards + * @experimental + */ +export const isReference: (u: unknown) => u is Reference = internal.isReference + +/** + * Returns an empty `Context`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context } from "effect" + * + * assert.strictEqual(Context.isContext(Context.empty()), true) + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const empty: () => Context = internal.empty + +/** + * Creates a new `Context` with a single service associated to the tag. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * + * const Services = Context.make(Port, { PORT: 8080 }) + * + * assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 }) + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const make: (tag: Tag, service: Types.NoInfer) => Context = internal.make + +/** + * Adds a service to a given `Context`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context, pipe } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const someContext = Context.make(Port, { PORT: 8080 }) + * + * const Services = pipe( + * someContext, + * Context.add(Timeout, { TIMEOUT: 5000 }) + * ) + * + * assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 }) + * assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 }) + * ``` + * + * @since 2.0.0 + */ +export const add: { + /** + * Adds a service to a given `Context`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context, pipe } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const someContext = Context.make(Port, { PORT: 8080 }) + * + * const Services = pipe( + * someContext, + * Context.add(Timeout, { TIMEOUT: 5000 }) + * ) + * + * assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 }) + * assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 }) + * ``` + * + * @since 2.0.0 + */ + (tag: Tag, service: Types.NoInfer): (self: Context) => Context + /** + * Adds a service to a given `Context`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context, pipe } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const someContext = Context.make(Port, { PORT: 8080 }) + * + * const Services = pipe( + * someContext, + * Context.add(Timeout, { TIMEOUT: 5000 }) + * ) + * + * assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 }) + * assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 }) + * ``` + * + * @since 2.0.0 + */ + (self: Context, tag: Tag, service: Types.NoInfer): Context +} = internal.add + +/** + * Get a service from the context that corresponds to the given tag. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { pipe, Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const Services = pipe( + * Context.make(Port, { PORT: 8080 }), + * Context.add(Timeout, { TIMEOUT: 5000 }) + * ) + * + * assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 }) + * ``` + * + * @since 2.0.0 + * @category getters + */ +export const get: { + /** + * Get a service from the context that corresponds to the given tag. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { pipe, Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const Services = pipe( + * Context.make(Port, { PORT: 8080 }), + * Context.add(Timeout, { TIMEOUT: 5000 }) + * ) + * + * assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 }) + * ``` + * + * @since 2.0.0 + * @category getters + */ + (tag: Reference): (self: Context) => S + /** + * Get a service from the context that corresponds to the given tag. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { pipe, Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const Services = pipe( + * Context.make(Port, { PORT: 8080 }), + * Context.add(Timeout, { TIMEOUT: 5000 }) + * ) + * + * assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 }) + * ``` + * + * @since 2.0.0 + * @category getters + */ + (tag: Tag): (self: Context) => S + /** + * Get a service from the context that corresponds to the given tag. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { pipe, Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const Services = pipe( + * Context.make(Port, { PORT: 8080 }), + * Context.add(Timeout, { TIMEOUT: 5000 }) + * ) + * + * assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 }) + * ``` + * + * @since 2.0.0 + * @category getters + */ + (self: Context, tag: Reference): S + /** + * Get a service from the context that corresponds to the given tag. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { pipe, Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const Services = pipe( + * Context.make(Port, { PORT: 8080 }), + * Context.add(Timeout, { TIMEOUT: 5000 }) + * ) + * + * assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 }) + * ``` + * + * @since 2.0.0 + * @category getters + */ + (self: Context, tag: Tag): S +} = internal.get + +/** + * Get a service from the context that corresponds to the given tag, or + * use the fallback value. + * + * @since 3.7.0 + * @category getters + */ +export const getOrElse: { + /** + * Get a service from the context that corresponds to the given tag, or + * use the fallback value. + * + * @since 3.7.0 + * @category getters + */ + (tag: Tag, orElse: LazyArg): (self: Context) => S | B + /** + * Get a service from the context that corresponds to the given tag, or + * use the fallback value. + * + * @since 3.7.0 + * @category getters + */ + (self: Context, tag: Tag, orElse: LazyArg): S | B +} = internal.getOrElse + +/** + * Get a service from the context that corresponds to the given tag. + * This function is unsafe because if the tag is not present in the context, a runtime error will be thrown. + * + * For a safer version see {@link getOption}. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const Services = Context.make(Port, { PORT: 8080 }) + * + * assert.deepStrictEqual(Context.unsafeGet(Services, Port), { PORT: 8080 }) + * assert.throws(() => Context.unsafeGet(Services, Timeout)) + * ``` + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeGet: { + /** + * Get a service from the context that corresponds to the given tag. + * This function is unsafe because if the tag is not present in the context, a runtime error will be thrown. + * + * For a safer version see {@link getOption}. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const Services = Context.make(Port, { PORT: 8080 }) + * + * assert.deepStrictEqual(Context.unsafeGet(Services, Port), { PORT: 8080 }) + * assert.throws(() => Context.unsafeGet(Services, Timeout)) + * ``` + * + * @since 2.0.0 + * @category unsafe + */ + (tag: Tag): (self: Context) => S + /** + * Get a service from the context that corresponds to the given tag. + * This function is unsafe because if the tag is not present in the context, a runtime error will be thrown. + * + * For a safer version see {@link getOption}. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const Services = Context.make(Port, { PORT: 8080 }) + * + * assert.deepStrictEqual(Context.unsafeGet(Services, Port), { PORT: 8080 }) + * assert.throws(() => Context.unsafeGet(Services, Timeout)) + * ``` + * + * @since 2.0.0 + * @category unsafe + */ + (self: Context, tag: Tag): S +} = internal.unsafeGet + +/** + * Get the value associated with the specified tag from the context wrapped in an `Option` object. If the tag is not + * found, the `Option` object will be `None`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context, Option } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const Services = Context.make(Port, { PORT: 8080 }) + * + * assert.deepStrictEqual(Context.getOption(Services, Port), Option.some({ PORT: 8080 })) + * assert.deepStrictEqual(Context.getOption(Services, Timeout), Option.none()) + * ``` + * + * @since 2.0.0 + * @category getters + */ +export const getOption: { + /** + * Get the value associated with the specified tag from the context wrapped in an `Option` object. If the tag is not + * found, the `Option` object will be `None`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context, Option } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const Services = Context.make(Port, { PORT: 8080 }) + * + * assert.deepStrictEqual(Context.getOption(Services, Port), Option.some({ PORT: 8080 })) + * assert.deepStrictEqual(Context.getOption(Services, Timeout), Option.none()) + * ``` + * + * @since 2.0.0 + * @category getters + */ + (tag: Tag): (self: Context) => Option + /** + * Get the value associated with the specified tag from the context wrapped in an `Option` object. If the tag is not + * found, the `Option` object will be `None`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context, Option } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const Services = Context.make(Port, { PORT: 8080 }) + * + * assert.deepStrictEqual(Context.getOption(Services, Port), Option.some({ PORT: 8080 })) + * assert.deepStrictEqual(Context.getOption(Services, Timeout), Option.none()) + * ``` + * + * @since 2.0.0 + * @category getters + */ + (self: Context, tag: Tag): Option +} = internal.getOption + +/** + * Merges two `Context`s, returning a new `Context` containing the services of both. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const firstContext = Context.make(Port, { PORT: 8080 }) + * const secondContext = Context.make(Timeout, { TIMEOUT: 5000 }) + * + * const Services = Context.merge(firstContext, secondContext) + * + * assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 }) + * assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 }) + * ``` + * + * @since 2.0.0 + */ +export const merge: { + /** + * Merges two `Context`s, returning a new `Context` containing the services of both. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const firstContext = Context.make(Port, { PORT: 8080 }) + * const secondContext = Context.make(Timeout, { TIMEOUT: 5000 }) + * + * const Services = Context.merge(firstContext, secondContext) + * + * assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 }) + * assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 }) + * ``` + * + * @since 2.0.0 + */ + (that: Context): (self: Context) => Context + /** + * Merges two `Context`s, returning a new `Context` containing the services of both. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const firstContext = Context.make(Port, { PORT: 8080 }) + * const secondContext = Context.make(Timeout, { TIMEOUT: 5000 }) + * + * const Services = Context.merge(firstContext, secondContext) + * + * assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 }) + * assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 }) + * ``` + * + * @since 2.0.0 + */ + (self: Context, that: Context): Context +} = internal.merge + +/** + * Merges any number of `Context`s, returning a new `Context` containing the services of all. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * const Host = Context.GenericTag<{ HOST: string }>("Host") + * + * const firstContext = Context.make(Port, { PORT: 8080 }) + * const secondContext = Context.make(Timeout, { TIMEOUT: 5000 }) + * const thirdContext = Context.make(Host, { HOST: "localhost" }) + * + * const Services = Context.mergeAll(firstContext, secondContext, thirdContext) + * + * assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 }) + * assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 }) + * assert.deepStrictEqual(Context.get(Services, Host), { HOST: "localhost" }) + * ``` + * + * @since 3.12.0 + */ +export const mergeAll: >( + ...ctxs: [...{ [K in keyof T]: Context }] +) => Context = internal.mergeAll + +/** + * Returns a new `Context` that contains only the specified services. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { pipe, Context, Option } from "effect" + * + * const Port = Context.GenericTag<{ PORT: number }>("Port") + * const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout") + * + * const someContext = pipe( + * Context.make(Port, { PORT: 8080 }), + * Context.add(Timeout, { TIMEOUT: 5000 }) + * ) + * + * const Services = pipe(someContext, Context.pick(Port)) + * + * assert.deepStrictEqual(Context.getOption(Services, Port), Option.some({ PORT: 8080 })) + * assert.deepStrictEqual(Context.getOption(Services, Timeout), Option.none()) + * ``` + * + * @since 2.0.0 + */ +export const pick: >>( + ...tags: Tags +) => (self: Context) => Context> = internal.pick + +/** + * @since 2.0.0 + */ +export const omit: >>( + ...tags: Tags +) => (self: Context) => Context>> = internal.omit + +/** + * @example + * ```ts + * import * as assert from "node:assert" + * import { Context, Layer } from "effect" + * + * class MyTag extends Context.Tag("MyTag")< + * MyTag, + * { readonly myNum: number } + * >() { + * static Live = Layer.succeed(this, { myNum: 108 }) + * } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const Tag: (id: Id) => () => TagClass = internal.Tag + +/** + * Creates a context tag with a default value. + * + * **Details** + * + * `Context.Reference` allows you to create a tag that can hold a value. You can + * provide a default value for the service, which will automatically be used + * when the context is accessed, or override it with a custom implementation + * when needed. + * + * **Example** (Declaring a Tag with a default value) + * + * ```ts + * import * as assert from "node:assert" + * import { Context, Effect } from "effect" + * + * class SpecialNumber extends Context.Reference()( + * "SpecialNumber", + * { defaultValue: () => 2048 } + * ) {} + * + * // ┌─── Effect + * // ▼ + * const program = Effect.gen(function* () { + * const specialNumber = yield* SpecialNumber + * console.log(`The special number is ${specialNumber}`) + * }) + * + * // No need to provide the SpecialNumber implementation + * Effect.runPromise(program) + * // Output: The special number is 2048 + * ``` + * + * **Example** (Overriding the default value) + * + * ```ts + * import { Context, Effect } from "effect" + * + * class SpecialNumber extends Context.Reference()( + * "SpecialNumber", + * { defaultValue: () => 2048 } + * ) {} + * + * const program = Effect.gen(function* () { + * const specialNumber = yield* SpecialNumber + * console.log(`The special number is ${specialNumber}`) + * }) + * + * Effect.runPromise(program.pipe(Effect.provideService(SpecialNumber, -1))) + * // Output: The special number is -1 + * ``` + * + * @since 3.11.0 + * @category constructors + * @experimental + */ +export const Reference: () => ( + id: Id, + options: { readonly defaultValue: () => Service } +) => ReferenceClass = internal.Reference diff --git a/backend/node_modules/effect/src/Cron.ts b/backend/node_modules/effect/src/Cron.ts new file mode 100644 index 0000000000000000000000000000000000000000..21d8e48a8e090980fb08c60976bc1f537a2249ad --- /dev/null +++ b/backend/node_modules/effect/src/Cron.ts @@ -0,0 +1,848 @@ +/** + * @since 2.0.0 + */ +import * as Arr from "./Array.js" +import * as Data from "./Data.js" +import type * as DateTime from "./DateTime.js" +import * as Either from "./Either.js" +import * as Equal from "./Equal.js" +import * as equivalence from "./Equivalence.js" +import { constVoid, dual, identity, pipe } from "./Function.js" +import * as Hash from "./Hash.js" +import { format, type Inspectable, NodeInspectSymbol } from "./Inspectable.js" +import * as dateTime from "./internal/dateTime.js" +import * as N from "./Number.js" +import * as Option from "./Option.js" +import { type Pipeable, pipeArguments } from "./Pipeable.js" +import { hasProperty } from "./Predicate.js" +import * as String from "./String.js" +import type { Mutable } from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const TypeId: unique symbol = Symbol.for("effect/Cron") + +/** + * @since 2.0.0 + * @category symbol + */ +export type TypeId = typeof TypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface Cron extends Pipeable, Equal.Equal, Inspectable { + readonly [TypeId]: TypeId + readonly tz: Option.Option + readonly seconds: ReadonlySet + readonly minutes: ReadonlySet + readonly hours: ReadonlySet + readonly days: ReadonlySet + readonly months: ReadonlySet + readonly weekdays: ReadonlySet + /** @internal */ + readonly first: { + readonly second: number + readonly minute: number + readonly hour: number + readonly day: number + readonly month: number + readonly weekday: number + } + /** @internal */ + readonly last: { + readonly second: number + readonly minute: number + readonly hour: number + readonly day: number + readonly month: number + readonly weekday: number + } + /** @internal */ + readonly next: { + readonly second: ReadonlyArray + readonly minute: ReadonlyArray + readonly hour: ReadonlyArray + readonly day: ReadonlyArray + readonly month: ReadonlyArray + readonly weekday: ReadonlyArray + } + /** @internal */ + readonly prev: { + readonly second: ReadonlyArray + readonly minute: ReadonlyArray + readonly hour: ReadonlyArray + readonly day: ReadonlyArray + readonly month: ReadonlyArray + readonly weekday: ReadonlyArray + } +} + +const CronProto = { + [TypeId]: TypeId, + [Equal.symbol](this: Cron, that: unknown) { + return isCron(that) && equals(this, that) + }, + [Hash.symbol](this: Cron): number { + return pipe( + Hash.hash(this.tz), + Hash.combine(Hash.array(Arr.fromIterable(this.seconds))), + Hash.combine(Hash.array(Arr.fromIterable(this.minutes))), + Hash.combine(Hash.array(Arr.fromIterable(this.hours))), + Hash.combine(Hash.array(Arr.fromIterable(this.days))), + Hash.combine(Hash.array(Arr.fromIterable(this.months))), + Hash.combine(Hash.array(Arr.fromIterable(this.weekdays))), + Hash.cached(this) + ) + }, + toString(this: Cron) { + return format(this.toJSON()) + }, + toJSON(this: Cron) { + return { + _id: "Cron", + tz: this.tz, + seconds: Arr.fromIterable(this.seconds), + minutes: Arr.fromIterable(this.minutes), + hours: Arr.fromIterable(this.hours), + days: Arr.fromIterable(this.days), + months: Arr.fromIterable(this.months), + weekdays: Arr.fromIterable(this.weekdays) + } + }, + [NodeInspectSymbol](this: Cron) { + return this.toJSON() + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** + * Checks if a given value is a `Cron` instance. + * + * @since 2.0.0 + * @category guards + */ +export const isCron = (u: unknown): u is Cron => hasProperty(u, TypeId) + +/** + * Creates a `Cron` instance. + * + * @since 2.0.0 + * @category constructors + */ +export const make = (values: { + readonly seconds?: Iterable | undefined + readonly minutes: Iterable + readonly hours: Iterable + readonly days: Iterable + readonly months: Iterable + readonly weekdays: Iterable + readonly tz?: DateTime.TimeZone | undefined +}): Cron => { + const o: Mutable = Object.create(CronProto) + o.seconds = new Set(Arr.sort(values.seconds ?? [0], N.Order)) + o.minutes = new Set(Arr.sort(values.minutes, N.Order)) + o.hours = new Set(Arr.sort(values.hours, N.Order)) + o.days = new Set(Arr.sort(values.days, N.Order)) + o.months = new Set(Arr.sort(values.months, N.Order)) + o.weekdays = new Set(Arr.sort(values.weekdays, N.Order)) + o.tz = Option.fromNullable(values.tz) + + const seconds = Array.from(o.seconds) + const minutes = Array.from(o.minutes) + const hours = Array.from(o.hours) + const days = Array.from(o.days) + const months = Array.from(o.months) + const weekdays = Array.from(o.weekdays) + + o.first = { + second: seconds[0] ?? 0, + minute: minutes[0] ?? 0, + hour: hours[0] ?? 0, + day: days[0] ?? 1, + month: (months[0] ?? 1) - 1, + weekday: weekdays[0] ?? 0 + } + + o.last = { + second: seconds[seconds.length - 1] ?? 59, + minute: minutes[minutes.length - 1] ?? 59, + hour: hours[hours.length - 1] ?? 23, + day: days[days.length - 1] ?? 31, + month: (months[months.length - 1] ?? 12) - 1, + weekday: weekdays[weekdays.length - 1] ?? 6 + } + + o.next = { + second: lookupTable(seconds, 60, "next"), + minute: lookupTable(minutes, 60, "next"), + hour: lookupTable(hours, 24, "next"), + day: lookupTable(days, 32, "next"), + month: lookupTable(months, 13, "next"), + weekday: lookupTable(weekdays, 7, "next") + } + + o.prev = { + second: lookupTable(seconds, 60, "prev"), + minute: lookupTable(minutes, 60, "prev"), + hour: lookupTable(hours, 24, "prev"), + day: lookupTable(days, 32, "prev"), + month: lookupTable(months, 13, "prev"), + weekday: lookupTable(weekdays, 7, "prev") + } + + return o +} + +const lookupTable = ( + values: ReadonlyArray, + size: number, + dir: "next" | "prev" +): Array => { + const result = new Array(size).fill(undefined) + if (values.length === 0) { + return result + } + + let current: number | undefined = undefined + + if (dir === "next") { + let index = values.length - 1 + for (let i = size - 1; i >= 0; i--) { + while (index >= 0 && values[index] >= i) { + current = values[index--] + } + result[i] = current + } + } else { + let index = 0 + for (let i = 0; i < size; i++) { + while (index < values.length && values[index] <= i) { + current = values[index++] + } + result[i] = current + } + } + + return result +} + +/** + * @since 2.0.0 + * @category symbol + */ +export const ParseErrorTypeId: unique symbol = Symbol.for("effect/Cron/errors/ParseError") + +/** + * @since 2.0.0 + * @category symbols + */ +export type ParseErrorTypeId = typeof ParseErrorTypeId + +/** + * Represents a checked exception which occurs when decoding fails. + * + * @since 2.0.0 + * @category models + */ +export class ParseError extends Data.TaggedError("CronParseError")<{ + readonly message: string + readonly input?: string +}> { + /** + * @since 2.0.0 + */ + readonly [ParseErrorTypeId] = ParseErrorTypeId +} + +/** + * Returns `true` if the specified value is an `ParseError`, `false` otherwise. + * + * @since 2.0.0 + * @category guards + */ +export const isParseError = (u: unknown): u is ParseError => hasProperty(u, ParseErrorTypeId) + +/** + * Parses a cron expression into a `Cron` instance. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Cron, Either } from "effect" + * + * // At 04:00 on every day-of-month from 8 through 14. + * assert.deepStrictEqual(Cron.parse("0 0 4 8-14 * *"), Either.right(Cron.make({ + * seconds: [0], + * minutes: [0], + * hours: [4], + * days: [8, 9, 10, 11, 12, 13, 14], + * months: [], + * weekdays: [] + * }))) + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const parse = (cron: string, tz?: DateTime.TimeZone | string): Either.Either => { + const segments = cron.split(" ").filter(String.isNonEmpty) + if (segments.length !== 5 && segments.length !== 6) { + return Either.left( + new ParseError({ + message: `Invalid number of segments in cron expression`, + input: cron + }) + ) + } + + if (segments.length === 5) { + segments.unshift("0") + } + + const [seconds, minutes, hours, days, months, weekdays] = segments + const zone = tz === undefined || dateTime.isTimeZone(tz) ? + Either.right(tz) : + Either.fromOption(dateTime.zoneFromString(tz), () => + new ParseError({ + message: `Invalid time zone in cron expression`, + input: tz + })) + + return Either.all({ + tz: zone, + seconds: parseSegment(seconds, secondOptions), + minutes: parseSegment(minutes, minuteOptions), + hours: parseSegment(hours, hourOptions), + days: parseSegment(days, dayOptions), + months: parseSegment(months, monthOptions), + weekdays: parseSegment(weekdays, weekdayOptions) + }).pipe(Either.map(make)) +} + +/** + * Parses a cron expression into a `Cron` instance. + * + * **Details** + * + * This function takes a cron expression as a string and attempts to parse it + * into a `Cron` instance. If the expression is valid, the resulting `Cron` + * instance will represent the schedule defined by the cron expression. + * + * If the expression is invalid, the function throws a `ParseError`. + * + * You can optionally provide a time zone (`tz`) to interpret the cron + * expression in a specific time zone. If no time zone is provided, the cron + * expression will use the default time zone. + * + * @example + * ```ts + * import { Cron } from "effect" + * + * // At 04:00 on every day-of-month from 8 through 14. + * console.log(Cron.unsafeParse("0 4 8-14 * *")) + * // Output: + * // { + * // _id: 'Cron', + * // tz: { _id: 'Option', _tag: 'None' }, + * // seconds: [ 0 ], + * // minutes: [ 0 ], + * // hours: [ 4 ], + * // days: [ + * // 8, 9, 10, 11, + * // 12, 13, 14 + * // ], + * // months: [], + * // weekdays: [] + * // } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const unsafeParse = (cron: string, tz?: DateTime.TimeZone | string): Cron => + Either.getOrThrowWith(parse(cron, tz), identity) + +/** + * Checks if a given `Date` falls within an active `Cron` time window. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Cron, Either } from "effect" + * + * const cron = Either.getOrThrow(Cron.parse("0 4 8-14 * *")) + * assert.deepStrictEqual(Cron.match(cron, new Date("2021-01-08 04:00:00")), true) + * assert.deepStrictEqual(Cron.match(cron, new Date("2021-01-08 05:00:00")), false) + * ``` + * + * @throws `IllegalArgumentException` if the given `DateTime.Input` is invalid. + * + * @since 2.0.0 + */ +export const match = (cron: Cron, date: DateTime.DateTime.Input): boolean => { + const parts = dateTime.unsafeMakeZoned(date, { + timeZone: Option.getOrUndefined(cron.tz) + }).pipe(dateTime.toParts) + + if (cron.seconds.size !== 0 && !cron.seconds.has(parts.seconds)) { + return false + } + + if (cron.minutes.size !== 0 && !cron.minutes.has(parts.minutes)) { + return false + } + + if (cron.hours.size !== 0 && !cron.hours.has(parts.hours)) { + return false + } + + if (cron.months.size !== 0 && !cron.months.has(parts.month)) { + return false + } + + if (cron.days.size === 0 && cron.weekdays.size === 0) { + return true + } + + if (cron.weekdays.size === 0) { + return cron.days.has(parts.day) + } + + if (cron.days.size === 0) { + return cron.weekdays.has(parts.weekDay) + } + + return cron.days.has(parts.day) || cron.weekdays.has(parts.weekDay) +} + +const daysInMonth = (date: Date): number => + new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 0)).getUTCDate() + +/** + * Returns the next run `Date` for the given `Cron` instance. + * + * Uses the current time as a starting point if no value is provided for `startFrom`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Cron, Either } from "effect" + * + * const after = new Date("2021-01-01 00:00:00") + * const cron = Either.getOrThrow(Cron.parse("0 4 8-14 * *")) + * assert.deepStrictEqual(Cron.next(cron, after), new Date("2021-01-08 04:00:00")) + * ``` + * + * @throws `IllegalArgumentException` if the given `DateTime.Input` is invalid. + * @throws `Error` if the next run date cannot be found within 10,000 iterations. + * + * @since 2.0.0 + */ +export const next = (cron: Cron, startFrom?: DateTime.DateTime.Input): Date => { + return stepCron(cron, startFrom, "next") +} + +/** + * Returns the previous run `Date` for the given `Cron` instance. + * + * Uses the current time as a starting point if no value is provided for `startFrom`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Cron, Either } from "effect" + * + * const before = new Date("2021-01-15 00:00:00") + * const cron = Either.getOrThrow(Cron.parse("0 4 8-14 * *")) + * assert.deepStrictEqual(Cron.prev(cron, before), new Date("2021-01-14 04:00:00")) + * ``` + * + * @throws `IllegalArgumentException` if the given `DateTime.Input` is invalid. + * @throws `Error` if the previous run date cannot be found within 10,000 iterations. + * + * @since 3.20.0 + */ +export const prev = (cron: Cron, startFrom?: DateTime.DateTime.Input): Date => { + return stepCron(cron, startFrom, "prev") +} + +/** @internal */ +const stepCron = (cron: Cron, startFrom: DateTime.DateTime.Input | undefined, direction: "next" | "prev"): Date => { + const tz = Option.getOrUndefined(cron.tz) + const zoned = dateTime.unsafeMakeZoned(startFrom ?? new Date(), { + timeZone: tz + }) + + const prev = direction === "prev" + const tick = prev ? -1 : 1 + const table = cron[direction] + const boundary = prev ? cron.last : cron.first + + const needsStep = prev + ? (next: number, current: number) => next < current + : (next: number, current: number) => next > current + + const utc = tz !== undefined && dateTime.isTimeZoneNamed(tz) && tz.id === "UTC" + const adjustDst = utc ? constVoid : (current: Date) => { + const adjusted = dateTime.unsafeMakeZoned(current, { + timeZone: zoned.zone, + adjustForTimeZone: true, + disambiguation: prev ? "later" : undefined + }).pipe(dateTime.toDate) + + const drift = current.getTime() - adjusted.getTime() + if (prev ? drift !== 0 : drift > 0) { + current.setTime(adjusted.getTime()) + } + } + + const result = dateTime.mutate(zoned, (current) => { + current.setUTCSeconds(current.getUTCSeconds() + tick, 0) + + for (let i = 0; i < 10_000; i++) { + if (cron.seconds.size !== 0) { + const currentSecond = current.getUTCSeconds() + const nextSecond = table.second[currentSecond] + if (nextSecond === undefined) { + current.setUTCMinutes(current.getUTCMinutes() + tick, boundary.second) + adjustDst(current) + continue + } + if (needsStep(nextSecond, currentSecond)) { + current.setUTCSeconds(nextSecond) + adjustDst(current) + continue + } + } + + if (cron.minutes.size !== 0) { + const currentMinute = current.getUTCMinutes() + const nextMinute = table.minute[currentMinute] + if (nextMinute === undefined) { + current.setUTCHours(current.getUTCHours() + tick, boundary.minute, boundary.second) + adjustDst(current) + continue + } + if (needsStep(nextMinute, currentMinute)) { + current.setUTCMinutes(nextMinute, boundary.second) + adjustDst(current) + continue + } + } + + if (cron.hours.size !== 0) { + const currentHour = current.getUTCHours() + const nextHour = table.hour[currentHour] + if (nextHour === undefined) { + current.setUTCDate(current.getUTCDate() + tick) + current.setUTCHours(boundary.hour, boundary.minute, boundary.second) + adjustDst(current) + continue + } + if (needsStep(nextHour, currentHour)) { + current.setUTCHours(nextHour, boundary.minute, boundary.second) + adjustDst(current) + continue + } + } + + if (cron.weekdays.size !== 0 || cron.days.size !== 0) { + let a: number = prev ? -Infinity : Infinity + let b: number = prev ? -Infinity : Infinity + + if (cron.weekdays.size !== 0) { + const currentWeekday = current.getUTCDay() + const nextWeekday = table.weekday[currentWeekday] + if (nextWeekday === undefined) { + a = prev + ? currentWeekday - 7 + boundary.weekday + : 7 - currentWeekday + boundary.weekday + } else { + a = nextWeekday - currentWeekday + } + } + + // Only check day-of-month if weekday constraint not already satisfied (they're OR'd) + if (cron.days.size !== 0 && a !== 0) { + const currentDay = current.getUTCDate() + const nextDay = table.day[currentDay] + if (nextDay === undefined) { + if (prev) { + // When wrapping to previous month, calculate days back: + // Current day offset + gap from end of prev month to target day + // Example: June 3 → May 20 with boundary.day=20: -(3 + (31 - 20)) = -14 + const prevMonthDays = daysInMonth( + new Date(Date.UTC(current.getUTCFullYear(), current.getUTCMonth(), 0)) + ) + b = -(currentDay + (prevMonthDays - boundary.day)) + } else { + b = daysInMonth(current) - currentDay + boundary.day + } + } else { + b = nextDay - currentDay + } + } + + const addDays = prev ? Math.max(a, b) : Math.min(a, b) + if (addDays !== 0) { + current.setUTCDate(current.getUTCDate() + addDays) + current.setUTCHours(boundary.hour, boundary.minute, boundary.second) + adjustDst(current) + continue + } + } + + if (cron.months.size !== 0) { + const currentMonth = current.getUTCMonth() + 1 + const nextMonth = table.month[currentMonth] + const clampBoundaryDay = (targetMonthIndex: number): number => { + if (cron.days.size !== 0) { + return boundary.day + } + const maxDayInMonth = daysInMonth(new Date(Date.UTC(current.getUTCFullYear(), targetMonthIndex, 1))) + return Math.min(boundary.day, maxDayInMonth) + } + if (nextMonth === undefined) { + current.setUTCFullYear(current.getUTCFullYear() + tick) + current.setUTCMonth(boundary.month, clampBoundaryDay(boundary.month)) + current.setUTCHours(boundary.hour, boundary.minute, boundary.second) + adjustDst(current) + continue + } + if (needsStep(nextMonth, currentMonth)) { + const targetMonthIndex = nextMonth - 1 + current.setUTCMonth(targetMonthIndex, clampBoundaryDay(targetMonthIndex)) + current.setUTCHours(boundary.hour, boundary.minute, boundary.second) + adjustDst(current) + continue + } + } + + return + } + + throw new Error("Unable to find next cron date") + }) + + return dateTime.toDateUtc(result) +} + +/** + * Returns an `IterableIterator` which yields the sequence of `Date`s that match the `Cron` instance. + * + * @since 2.0.0 + */ +export const sequence = function*(cron: Cron, startFrom?: DateTime.DateTime.Input): IterableIterator { + while (true) { + yield startFrom = next(cron, startFrom) + } +} + +/** + * Returns an `IterableIterator` which yields the sequence of `Date`s that match the `Cron` instance, + * in reverse direction. + * + * @since 3.20.0 + */ +export const sequenceReverse = function*(cron: Cron, startFrom?: DateTime.DateTime.Input): IterableIterator { + while (true) { + yield startFrom = prev(cron, startFrom) + } +} + +/** + * @category instances + * @since 2.0.0 + */ +export const Equivalence: equivalence.Equivalence = equivalence.make((self, that) => + restrictionsEquals(self.seconds, that.seconds) && + restrictionsEquals(self.minutes, that.minutes) && + restrictionsEquals(self.hours, that.hours) && + restrictionsEquals(self.days, that.days) && + restrictionsEquals(self.months, that.months) && + restrictionsEquals(self.weekdays, that.weekdays) +) + +const restrictionsArrayEquals = equivalence.array(equivalence.number) +const restrictionsEquals = (self: ReadonlySet, that: ReadonlySet): boolean => + restrictionsArrayEquals(Arr.fromIterable(self), Arr.fromIterable(that)) + +/** + * Checks if two `Cron`s are equal. + * + * @since 2.0.0 + * @category predicates + */ +export const equals: { + /** + * Checks if two `Cron`s are equal. + * + * @since 2.0.0 + * @category predicates + */ + (that: Cron): (self: Cron) => boolean + /** + * Checks if two `Cron`s are equal. + * + * @since 2.0.0 + * @category predicates + */ + (self: Cron, that: Cron): boolean +} = dual(2, (self: Cron, that: Cron): boolean => Equivalence(self, that)) + +interface SegmentOptions { + min: number + max: number + aliases?: Record | undefined +} + +const secondOptions: SegmentOptions = { + min: 0, + max: 59 +} + +const minuteOptions: SegmentOptions = { + min: 0, + max: 59 +} + +const hourOptions: SegmentOptions = { + min: 0, + max: 23 +} + +const dayOptions: SegmentOptions = { + min: 1, + max: 31 +} + +const monthOptions: SegmentOptions = { + min: 1, + max: 12, + aliases: { + jan: 1, + feb: 2, + mar: 3, + apr: 4, + may: 5, + jun: 6, + jul: 7, + aug: 8, + sep: 9, + oct: 10, + nov: 11, + dec: 12 + } +} + +const weekdayOptions: SegmentOptions = { + min: 0, + max: 6, + aliases: { + sun: 0, + mon: 1, + tue: 2, + wed: 3, + thu: 4, + fri: 5, + sat: 6 + } +} + +const parseSegment = ( + input: string, + options: SegmentOptions +): Either.Either, ParseError> => { + const capacity = options.max - options.min + 1 + const values = new Set() + const fields = input.split(",") + + for (const field of fields) { + const [raw, step] = splitStep(field) + if (raw === "*" && step === undefined) { + return Either.right(new Set()) + } + + if (step !== undefined) { + if (!Number.isInteger(step)) { + return Either.left(new ParseError({ message: `Expected step value to be a positive integer`, input })) + } + if (step < 1) { + return Either.left(new ParseError({ message: `Expected step value to be greater than 0`, input })) + } + if (step > options.max) { + return Either.left(new ParseError({ message: `Expected step value to be less than ${options.max}`, input })) + } + } + + if (raw === "*") { + for (let i = options.min; i <= options.max; i += step ?? 1) { + values.add(i) + } + } else { + const [left, right] = splitRange(raw, options.aliases) + if (!Number.isInteger(left)) { + return Either.left(new ParseError({ message: `Expected a positive integer`, input })) + } + if (left < options.min || left > options.max) { + return Either.left( + new ParseError({ message: `Expected a value between ${options.min} and ${options.max}`, input }) + ) + } + + if (right === undefined) { + values.add(left) + } else { + if (!Number.isInteger(right)) { + return Either.left(new ParseError({ message: `Expected a positive integer`, input })) + } + if (right < options.min || right > options.max) { + return Either.left( + new ParseError({ message: `Expected a value between ${options.min} and ${options.max}`, input }) + ) + } + if (left > right) { + return Either.left(new ParseError({ message: `Invalid value range`, input })) + } + + for (let i = left; i <= right; i += step ?? 1) { + values.add(i) + } + } + } + + if (values.size >= capacity) { + return Either.right(new Set()) + } + } + + return Either.right(values) +} + +const splitStep = (input: string): [string, number | undefined] => { + const seperator = input.indexOf("/") + if (seperator !== -1) { + return [input.slice(0, seperator), Number(input.slice(seperator + 1))] + } + + return [input, undefined] +} + +const splitRange = (input: string, aliases?: Record): [number, number | undefined] => { + const seperator = input.indexOf("-") + if (seperator !== -1) { + return [aliasOrValue(input.slice(0, seperator), aliases), aliasOrValue(input.slice(seperator + 1), aliases)] + } + + return [aliasOrValue(input, aliases), undefined] +} + +function aliasOrValue(field: string, aliases?: Record): number { + return aliases?.[field.toLocaleLowerCase()] ?? Number(field) +} diff --git a/backend/node_modules/effect/src/DefaultServices.ts b/backend/node_modules/effect/src/DefaultServices.ts new file mode 100644 index 0000000000000000000000000000000000000000..f353285e476a54ac9bf23d7d684b51f7a751bae0 --- /dev/null +++ b/backend/node_modules/effect/src/DefaultServices.ts @@ -0,0 +1,34 @@ +/** + * @since 2.0.0 + */ +import type * as Clock from "./Clock.js" +import type * as ConfigProvider from "./ConfigProvider.js" +import type * as Console from "./Console.js" +import type * as Context from "./Context.js" +import type * as FiberRef from "./FiberRef.js" +import * as internal from "./internal/defaultServices.js" +import type * as Random from "./Random.js" +import type * as Tracer from "./Tracer.js" + +/** + * @since 2.0.0 + * @category models + */ +export type DefaultServices = + | Clock.Clock + | Console.Console + | Random.Random + | ConfigProvider.ConfigProvider + | Tracer.Tracer + +/** + * @since 2.0.0 + * @category constructors + */ +export const liveServices: Context.Context = internal.liveServices + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentServices: FiberRef.FiberRef> = internal.currentServices diff --git a/backend/node_modules/effect/src/Deferred.ts b/backend/node_modules/effect/src/Deferred.ts new file mode 100644 index 0000000000000000000000000000000000000000..3be0df24be582ed367e89a155df983fe8251d17c --- /dev/null +++ b/backend/node_modules/effect/src/Deferred.ts @@ -0,0 +1,471 @@ +/** + * @since 2.0.0 + */ +import type * as Cause from "./Cause.js" +import type * as Effect from "./Effect.js" +import type * as Exit from "./Exit.js" +import type * as FiberId from "./FiberId.js" +import type { LazyArg } from "./Function.js" +import * as core from "./internal/core.js" +import * as internal from "./internal/deferred.js" +import type * as MutableRef from "./MutableRef.js" +import type * as Option from "./Option.js" +import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const DeferredTypeId: unique symbol = internal.DeferredTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type DeferredTypeId = typeof DeferredTypeId + +/** + * A `Deferred` represents an asynchronous variable that can be set exactly + * once, with the ability for an arbitrary number of fibers to suspend (by + * calling `Deferred.await`) and automatically resume when the variable is set. + * + * `Deferred` can be used for building primitive actions whose completions + * require the coordinated action of multiple fibers, and for building + * higher-level concurrent or asynchronous structures. + * + * @since 2.0.0 + * @category models + */ +export interface Deferred extends Effect.Effect, Deferred.Variance { + /** @internal */ + readonly state: MutableRef.MutableRef> + /** @internal */ + readonly blockingOn: FiberId.FiberId + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: DeferredUnify + readonly [Unify.ignoreSymbol]?: DeferredUnifyIgnore +} + +/** + * @category models + * @since 3.8.0 + */ +export interface DeferredUnify extends Effect.EffectUnify { + Deferred?: () => Extract> +} + +/** + * @category models + * @since 3.8.0 + */ +export interface DeferredUnifyIgnore extends Effect.EffectUnifyIgnore { + Effect?: true +} + +/** + * @since 2.0.0 + */ +export declare namespace Deferred { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [DeferredTypeId]: { + readonly _A: Types.Invariant + readonly _E: Types.Invariant + } + } +} + +/** + * Creates a new `Deferred`. + * + * @since 2.0.0 + * @category constructors + */ +export const make: () => Effect.Effect> = core.deferredMake + +/** + * Creates a new `Deferred` from the specified `FiberId`. + * + * @since 2.0.0 + * @category constructors + */ +export const makeAs: (fiberId: FiberId.FiberId) => Effect.Effect> = core.deferredMakeAs + +const _await: (self: Deferred) => Effect.Effect = core.deferredAwait + +export { + /** + * Retrieves the value of the `Deferred`, suspending the fiber running the + * workflow until the result is available. + * + * @since 2.0.0 + * @category getters + */ + _await as await +} + +/** + * Completes the deferred with the result of the specified effect. If the + * deferred has already been completed, the method will produce false. + * + * Note that `Deferred.completeWith` will be much faster, so consider using + * that if you do not need to memoize the result of the specified effect. + * + * @since 2.0.0 + * @category utils + */ +export const complete: { + /** + * Completes the deferred with the result of the specified effect. If the + * deferred has already been completed, the method will produce false. + * + * Note that `Deferred.completeWith` will be much faster, so consider using + * that if you do not need to memoize the result of the specified effect. + * + * @since 2.0.0 + * @category utils + */ + (effect: Effect.Effect): (self: Deferred) => Effect.Effect + /** + * Completes the deferred with the result of the specified effect. If the + * deferred has already been completed, the method will produce false. + * + * Note that `Deferred.completeWith` will be much faster, so consider using + * that if you do not need to memoize the result of the specified effect. + * + * @since 2.0.0 + * @category utils + */ + (self: Deferred, effect: Effect.Effect): Effect.Effect +} = core.deferredComplete + +/** + * Completes the deferred with the result of the specified effect. If the + * deferred has already been completed, the method will produce false. + * + * @since 2.0.0 + * @category utils + */ +export const completeWith: { + /** + * Completes the deferred with the result of the specified effect. If the + * deferred has already been completed, the method will produce false. + * + * @since 2.0.0 + * @category utils + */ + (effect: Effect.Effect): (self: Deferred) => Effect.Effect + /** + * Completes the deferred with the result of the specified effect. If the + * deferred has already been completed, the method will produce false. + * + * @since 2.0.0 + * @category utils + */ + (self: Deferred, effect: Effect.Effect): Effect.Effect +} = core.deferredCompleteWith + +/** + * Exits the `Deferred` with the specified `Exit` value, which will be + * propagated to all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ +export const done: { + /** + * Exits the `Deferred` with the specified `Exit` value, which will be + * propagated to all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (exit: Exit.Exit): (self: Deferred) => Effect.Effect + /** + * Exits the `Deferred` with the specified `Exit` value, which will be + * propagated to all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (self: Deferred, exit: Exit.Exit): Effect.Effect +} = core.deferredDone + +/** + * Fails the `Deferred` with the specified error, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ +export const fail: { + /** + * Fails the `Deferred` with the specified error, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (error: E): (self: Deferred) => Effect.Effect + /** + * Fails the `Deferred` with the specified error, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (self: Deferred, error: E): Effect.Effect +} = core.deferredFail + +/** + * Fails the `Deferred` with the specified error, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ +export const failSync: { + /** + * Fails the `Deferred` with the specified error, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (evaluate: LazyArg): (self: Deferred) => Effect.Effect + /** + * Fails the `Deferred` with the specified error, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (self: Deferred, evaluate: LazyArg): Effect.Effect +} = core.deferredFailSync + +/** + * Fails the `Deferred` with the specified `Cause`, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ +export const failCause: { + /** + * Fails the `Deferred` with the specified `Cause`, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (cause: Cause.Cause): (self: Deferred) => Effect.Effect + /** + * Fails the `Deferred` with the specified `Cause`, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (self: Deferred, cause: Cause.Cause): Effect.Effect +} = core.deferredFailCause + +/** + * Fails the `Deferred` with the specified `Cause`, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ +export const failCauseSync: { + /** + * Fails the `Deferred` with the specified `Cause`, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (evaluate: LazyArg>): (self: Deferred) => Effect.Effect + /** + * Fails the `Deferred` with the specified `Cause`, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (self: Deferred, evaluate: LazyArg>): Effect.Effect +} = core.deferredFailCauseSync + +/** + * Kills the `Deferred` with the specified defect, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ +export const die: { + /** + * Kills the `Deferred` with the specified defect, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (defect: unknown): (self: Deferred) => Effect.Effect + /** + * Kills the `Deferred` with the specified defect, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (self: Deferred, defect: unknown): Effect.Effect +} = core.deferredDie + +/** + * Kills the `Deferred` with the specified defect, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ +export const dieSync: { + /** + * Kills the `Deferred` with the specified defect, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (evaluate: LazyArg): (self: Deferred) => Effect.Effect + /** + * Kills the `Deferred` with the specified defect, which will be propagated to + * all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category utils + */ + (self: Deferred, evaluate: LazyArg): Effect.Effect +} = core.deferredDieSync + +/** + * Completes the `Deferred` with interruption. This will interrupt all fibers + * waiting on the value of the `Deferred` with the `FiberId` of the fiber + * calling this method. + * + * @since 2.0.0 + * @category utils + */ +export const interrupt: (self: Deferred) => Effect.Effect = core.deferredInterrupt + +/** + * Completes the `Deferred` with interruption. This will interrupt all fibers + * waiting on the value of the `Deferred` with the specified `FiberId`. + * + * @since 2.0.0 + * @category utils + */ +export const interruptWith: { + /** + * Completes the `Deferred` with interruption. This will interrupt all fibers + * waiting on the value of the `Deferred` with the specified `FiberId`. + * + * @since 2.0.0 + * @category utils + */ + (fiberId: FiberId.FiberId): (self: Deferred) => Effect.Effect + /** + * Completes the `Deferred` with interruption. This will interrupt all fibers + * waiting on the value of the `Deferred` with the specified `FiberId`. + * + * @since 2.0.0 + * @category utils + */ + (self: Deferred, fiberId: FiberId.FiberId): Effect.Effect +} = core.deferredInterruptWith + +/** + * Returns `true` if this `Deferred` has already been completed with a value or + * an error, `false` otherwise. + * + * @since 2.0.0 + * @category getters + */ +export const isDone: (self: Deferred) => Effect.Effect = core.deferredIsDone + +/** + * Returns a `Some>` from the `Deferred` if this `Deferred` has + * already been completed, `None` otherwise. + * + * @since 2.0.0 + * @category getters + */ +export const poll: ( + self: Deferred +) => Effect.Effect>> = core.deferredPoll + +/** + * Completes the `Deferred` with the specified value. + * + * @since 2.0.0 + * @category utils + */ +export const succeed: { + /** + * Completes the `Deferred` with the specified value. + * + * @since 2.0.0 + * @category utils + */ + (value: A): (self: Deferred) => Effect.Effect + /** + * Completes the `Deferred` with the specified value. + * + * @since 2.0.0 + * @category utils + */ + (self: Deferred, value: A): Effect.Effect +} = core.deferredSucceed + +/** + * Completes the `Deferred` with the specified lazily evaluated value. + * + * @since 2.0.0 + * @category utils + */ +export const sync: { + /** + * Completes the `Deferred` with the specified lazily evaluated value. + * + * @since 2.0.0 + * @category utils + */ + (evaluate: LazyArg): (self: Deferred) => Effect.Effect + /** + * Completes the `Deferred` with the specified lazily evaluated value. + * + * @since 2.0.0 + * @category utils + */ + (self: Deferred, evaluate: LazyArg): Effect.Effect +} = core.deferredSync + +/** + * Unsafely creates a new `Deferred` from the specified `FiberId`. + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeMake: (fiberId: FiberId.FiberId) => Deferred = core.deferredUnsafeMake + +/** + * Unsafely exits the `Deferred` with the specified `Exit` value, which will be + * propagated to all fibers waiting on the value of the `Deferred`. + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeDone: (self: Deferred, effect: Effect.Effect) => void = core.deferredUnsafeDone diff --git a/backend/node_modules/effect/src/Differ.ts b/backend/node_modules/effect/src/Differ.ts new file mode 100644 index 0000000000000000000000000000000000000000..97acfe2cc7a04b9d7bfe2189ff08946220152d86 --- /dev/null +++ b/backend/node_modules/effect/src/Differ.ts @@ -0,0 +1,512 @@ +/** + * @since 2.0.0 + */ +import type { Chunk } from "./Chunk.js" +import type { Context } from "./Context.js" +import type { Either } from "./Either.js" +import type { Equal } from "./Equal.js" +import * as Dual from "./Function.js" +import type { HashMap } from "./HashMap.js" +import type { HashSet } from "./HashSet.js" +import * as internal from "./internal/differ.js" +import * as ChunkPatch from "./internal/differ/chunkPatch.js" +import * as ContextPatch from "./internal/differ/contextPatch.js" +import * as HashMapPatch from "./internal/differ/hashMapPatch.js" +import * as HashSetPatch from "./internal/differ/hashSetPatch.js" +import * as OrPatch from "./internal/differ/orPatch.js" +import * as ReadonlyArrayPatch from "./internal/differ/readonlyArrayPatch.js" +import type { Pipeable } from "./Pipeable.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbol + */ +export const TypeId: unique symbol = internal.DifferTypeId as TypeId + +/** + * @since 2.0.0 + * @category symbol + */ +export type TypeId = typeof TypeId + +/** + * A `Differ` knows how to compare an old value and new value of + * type `Value` to produce a patch of type `Patch` that describes the + * differences between those values. A `Differ` also knows how to apply a patch + * to an old value to produce a new value that represents the old value updated + * with the changes described by the patch. + * + * A `Differ` can be used to construct a `FiberRef` supporting compositional + * updates using the `FiberRef.makePatch` constructor. + * + * The `Differ` companion object contains constructors for `Differ` values for + * common data types such as `Chunk`, `HashMap`, and `HashSet``. In addition, + * `Differ`values can be transformed using the `transform` operator and combined + * using the `orElseEither` and `zip` operators. This allows creating `Differ` + * values for arbitrarily complex data types compositionally. + * + * @since 2.0.0 + * @category models + */ +export interface Differ extends Pipeable { + readonly [TypeId]: { + readonly _V: Types.Invariant + readonly _P: Types.Invariant + } + readonly empty: Patch + diff(oldValue: Value, newValue: Value): Patch + combine(first: Patch, second: Patch): Patch + patch(patch: Patch, oldValue: Value): Value +} + +const ChunkPatchTypeId: unique symbol = ChunkPatch.ChunkPatchTypeId as Differ.Chunk.TypeId +const ContextPatchTypeId: unique symbol = ContextPatch.ContextPatchTypeId as Differ.Context.TypeId +const HashMapPatchTypeId: unique symbol = HashMapPatch.HashMapPatchTypeId as Differ.HashMap.TypeId +const HashSetPatchTypeId: unique symbol = HashSetPatch.HashSetPatchTypeId as Differ.HashSet.TypeId +const OrPatchTypeId: unique symbol = OrPatch.OrPatchTypeId as Differ.Or.TypeId +const ReadonlyArrayPatchTypeId: unique symbol = ReadonlyArrayPatch + .ReadonlyArrayPatchTypeId as Differ.ReadonlyArray.TypeId + +/** + * @since 2.0.0 + */ +export declare namespace Differ { + /** + * @since 2.0.0 + */ + export namespace Context { + /** + * @since 2.0.0 + * @category symbol + */ + export type TypeId = typeof ContextPatchTypeId + /** + * A `Patch` describes an update that transforms a `Env` + * to a `Env` as a data structure. This allows combining updates to + * different services in the environment in a compositional way. + * + * @since 2.0.0 + * @category models + */ + export interface Patch extends Equal { + readonly [ContextPatchTypeId]: { + readonly _Input: Types.Contravariant + readonly _Output: Types.Covariant + } + } + } + + /** + * @since 2.0.0 + */ + export namespace Chunk { + /** + * @since 2.0.0 + * @category symbol + */ + export type TypeId = typeof ChunkPatchTypeId + /** + * A patch which describes updates to a chunk of values. + * + * @since 2.0.0 + * @category models + */ + export interface Patch extends Equal { + readonly [ChunkPatchTypeId]: { + readonly _Value: Types.Invariant + readonly _Patch: Types.Invariant + } + } + } + + /** + * @since 2.0.0 + */ + export namespace HashMap { + /** + * @since 2.0.0 + * @category symbol + */ + export type TypeId = typeof HashMapPatchTypeId + /** + * A patch which describes updates to a map of keys and values. + * + * @since 2.0.0 + * @category models + */ + export interface Patch extends Equal { + readonly [HashMapPatchTypeId]: { + readonly _Key: Types.Invariant + readonly _Value: Types.Invariant + readonly _Patch: Types.Invariant + } + } + } + + /** + * @since 2.0.0 + */ + export namespace HashSet { + /** + * @since 2.0.0 + * @category symbol + */ + export type TypeId = typeof HashSetPatchTypeId + /** + * A patch which describes updates to a set of values. + * + * @since 2.0.0 + * @category models + */ + export interface Patch extends Equal { + readonly [HashSetPatchTypeId]: { + readonly _Value: Types.Invariant + } + } + } + + /** + * @since 2.0.0 + */ + export namespace Or { + /** + * @since 2.0.0 + * @category symbol + */ + export type TypeId = typeof OrPatchTypeId + /** + * A patch which describes updates to either one value or another. + * + * @since 2.0.0 + * @category models + */ + export interface Patch extends Equal { + readonly [OrPatchTypeId]: { + readonly _Value: Types.Invariant + readonly _Value2: Types.Invariant + readonly _Patch: Types.Invariant + readonly _Patch2: Types.Invariant + } + } + } + + /** + * @since 2.0.0 + */ + export namespace ReadonlyArray { + /** + * @since 2.0.0 + * @category symbol + */ + export type TypeId = typeof ReadonlyArrayPatchTypeId + /** + * A patch which describes updates to a ReadonlyArray of values. + * + * @since 2.0.0 + * @category models + */ + export interface Patch extends Equal { + readonly [ReadonlyArrayPatchTypeId]: { + readonly _Value: Types.Invariant + readonly _Patch: Types.Invariant + } + } + } +} + +/** + * An empty patch that describes no changes. + * + * @since 2.0.0 + * @category patch + */ +export const empty: (self: Differ) => Patch = ( + self +) => self.empty + +/** + * @since 2.0.0 + * @category patch + */ +export const diff: { + /** + * @since 2.0.0 + * @category patch + */ + (oldValue: Value, newValue: Value): ( + self: Differ + ) => Patch + /** + * @since 2.0.0 + * @category patch + */ + (self: Differ, oldValue: Value, newValue: Value): Patch +} = Dual.dual( + 3, + ( + self: Differ, + oldValue: Value, + newValue: Value + ): Patch => self.diff(oldValue, newValue) +) + +/** + * Combines two patches to produce a new patch that describes the updates of + * the first patch and then the updates of the second patch. The combine + * operation should be associative. In addition, if the combine operation is + * commutative then joining multiple fibers concurrently will result in + * deterministic `FiberRef` values. + * + * @since 2.0.0 + * @category patch + */ +export const combine: { + /** + * Combines two patches to produce a new patch that describes the updates of + * the first patch and then the updates of the second patch. The combine + * operation should be associative. In addition, if the combine operation is + * commutative then joining multiple fibers concurrently will result in + * deterministic `FiberRef` values. + * + * @since 2.0.0 + * @category patch + */ + (first: Patch, second: Patch): ( + self: Differ + ) => Patch + /** + * Combines two patches to produce a new patch that describes the updates of + * the first patch and then the updates of the second patch. The combine + * operation should be associative. In addition, if the combine operation is + * commutative then joining multiple fibers concurrently will result in + * deterministic `FiberRef` values. + * + * @since 2.0.0 + * @category patch + */ + (self: Differ, first: Patch, second: Patch): Patch +} = Dual.dual( + 3, + ( + self: Differ, + first: Patch, + second: Patch + ): Patch => self.combine(first, second) +) + +/** + * Applies a patch to an old value to produce a new value that is equal to the + * old value with the updates described by the patch. + * + * @since 2.0.0 + * @category patch + */ +export const patch: { + /** + * Applies a patch to an old value to produce a new value that is equal to the + * old value with the updates described by the patch. + * + * @since 2.0.0 + * @category patch + */ + (patch: Patch, oldValue: Value): ( + self: Differ + ) => Value + /** + * Applies a patch to an old value to produce a new value that is equal to the + * old value with the updates described by the patch. + * + * @since 2.0.0 + * @category patch + */ + (self: Differ, patch: Patch, oldValue: Value): Value +} = Dual.dual( + 3, + ( + self: Differ, + patch: Patch, + oldValue: Value + ): Value => self.patch(patch, oldValue) +) + +/** + * Constructs a new `Differ`. + * + * @since 2.0.0 + * @category constructors + */ +export const make: (params: { + readonly empty: Patch + readonly diff: (oldValue: Value, newValue: Value) => Patch + readonly combine: (first: Patch, second: Patch) => Patch + readonly patch: (patch: Patch, oldValue: Value) => Value +}) => Differ = internal.make + +/** + * Constructs a differ that knows how to diff `Env` values. + * + * @since 2.0.0 + * @category constructors + */ +export const environment: () => Differ< + Context, + Differ.Context.Patch +> = internal.environment + +/** + * Constructs a differ that knows how to diff a `Chunk` of values given a + * differ that knows how to diff the values. + * + * @since 2.0.0 + * @category constructors + */ +export const chunk: ( + differ: Differ +) => Differ, Differ.Chunk.Patch> = internal.chunk + +/** + * Constructs a differ that knows how to diff a `HashMap` of keys and values given + * a differ that knows how to diff the values. + * + * @since 2.0.0 + * @category constructors + */ +export const hashMap: ( + differ: Differ +) => Differ, Differ.HashMap.Patch> = internal.hashMap + +/** + * Constructs a differ that knows how to diff a `HashSet` of values. + * + * @since 2.0.0 + * @category constructors + */ +export const hashSet: () => Differ< + HashSet, + Differ.HashSet.Patch +> = internal.hashSet + +/** + * Combines this differ and the specified differ to produce a differ that + * knows how to diff the sum of their values. + * + * @since 2.0.0 + */ +export const orElseEither: { + /** + * Combines this differ and the specified differ to produce a differ that + * knows how to diff the sum of their values. + * + * @since 2.0.0 + */ + (that: Differ): ( + self: Differ + ) => Differ< + Either, + Differ.Or.Patch + > + /** + * Combines this differ and the specified differ to produce a differ that + * knows how to diff the sum of their values. + * + * @since 2.0.0 + */ + (self: Differ, that: Differ): Differ< + Either, + Differ.Or.Patch + > +} = internal.orElseEither + +/** + * Constructs a differ that knows how to diff a `ReadonlyArray` of values. + * + * @since 2.0.0 + * @category constructors + */ +export const readonlyArray: ( + differ: Differ +) => Differ, Differ.ReadonlyArray.Patch> = internal.readonlyArray + +/** + * Transforms the type of values that this differ knows how to differ using + * the specified functions that map the new and old value types to each other. + * + * @since 2.0.0 + */ +export const transform: { + /** + * Transforms the type of values that this differ knows how to differ using + * the specified functions that map the new and old value types to each other. + * + * @since 2.0.0 + */ + ( + options: { + readonly toNew: (value: Value) => Value2 + readonly toOld: (value: Value2) => Value + } + ): (self: Differ) => Differ + /** + * Transforms the type of values that this differ knows how to differ using + * the specified functions that map the new and old value types to each other. + * + * @since 2.0.0 + */ + ( + self: Differ, + options: { + readonly toNew: (value: Value) => Value2 + readonly toOld: (value: Value2) => Value + } + ): Differ +} = internal.transform + +/** + * Constructs a differ that just diffs two values by returning a function that + * sets the value to the new value. This differ does not support combining + * multiple updates to the value compositionally and should only be used when + * there is no compositional way to update them. + * + * @since 2.0.0 + */ +export const update: () => Differ A> = internal.update + +/** + * A variant of `update` that allows specifying the function that will be used + * to combine old values with new values. + * + * @since 2.0.0 + */ +export const updateWith: (f: (x: A, y: A) => A) => Differ A> = internal.updateWith + +/** + * Combines this differ and the specified differ to produce a new differ that + * knows how to diff the product of their values. + * + * @since 2.0.0 + */ +export const zip: { + /** + * Combines this differ and the specified differ to produce a new differ that + * knows how to diff the product of their values. + * + * @since 2.0.0 + */ + (that: Differ): ( + self: Differ + ) => Differ< + readonly [Value, Value2], // readonly because invariant + readonly [Patch, Patch2] // readonly because invariant + > + /** + * Combines this differ and the specified differ to produce a new differ that + * knows how to diff the product of their values. + * + * @since 2.0.0 + */ + (self: Differ, that: Differ): Differ< + readonly [Value, Value2], // readonly because invariant + readonly [Patch, Patch2] // readonly because invariant + > +} = internal.zip diff --git a/backend/node_modules/effect/src/Duration.ts b/backend/node_modules/effect/src/Duration.ts new file mode 100644 index 0000000000000000000000000000000000000000..370fef5d1ba0e5ecd6cdc6eeb7426370148cf72f --- /dev/null +++ b/backend/node_modules/effect/src/Duration.ts @@ -0,0 +1,1140 @@ +/** + * @since 2.0.0 + */ +import * as Equal from "./Equal.js" +import type * as equivalence from "./Equivalence.js" +import { dual } from "./Function.js" +import * as Hash from "./Hash.js" +import type { Inspectable } from "./Inspectable.js" +import { NodeInspectSymbol } from "./Inspectable.js" +import * as Option from "./Option.js" +import * as order from "./Order.js" +import type { Pipeable } from "./Pipeable.js" +import { pipeArguments } from "./Pipeable.js" +import { hasProperty, isBigInt, isNumber, isString } from "./Predicate.js" + +const TypeId: unique symbol = Symbol.for("effect/Duration") + +const bigint0 = BigInt(0) +const bigint24 = BigInt(24) +const bigint60 = BigInt(60) +const bigint1e3 = BigInt(1_000) +const bigint1e6 = BigInt(1_000_000) +const bigint1e9 = BigInt(1_000_000_000) + +/** + * @since 2.0.0 + * @category symbol + */ +export type TypeId = typeof TypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface Duration extends Equal.Equal, Pipeable, Inspectable { + readonly [TypeId]: TypeId + readonly value: DurationValue +} +/** + * @since 2.0.0 + * @category models + */ +export type DurationValue = + | { + readonly _tag: "Millis" + readonly millis: number + } + | { + readonly _tag: "Nanos" + readonly nanos: bigint + } + | { + readonly _tag: "Infinity" + } + +/** + * @since 2.0.0 + * @category models + */ +export type Unit = + | "nano" + | "nanos" + | "micro" + | "micros" + | "milli" + | "millis" + | "second" + | "seconds" + | "minute" + | "minutes" + | "hour" + | "hours" + | "day" + | "days" + | "week" + | "weeks" + +/** + * @since 2.0.0 + * @category models + */ +export type DurationInput = + | Duration + | number // millis + | bigint // nanos + | readonly [seconds: number, nanos: number] + | `${number} ${Unit}` + +const DURATION_REGEX = /^(-?\d+(?:\.\d+)?)\s+(nanos?|micros?|millis?|seconds?|minutes?|hours?|days?|weeks?)$/ + +/** + * @since 2.0.0 + */ +export const decode = (input: DurationInput): Duration => { + if (isDuration(input)) { + return input + } else if (isNumber(input)) { + return millis(input) + } else if (isBigInt(input)) { + return nanos(input) + } else if (Array.isArray(input) && input.length === 2 && input.every(isNumber)) { + if (input[0] === -Infinity || input[1] === -Infinity || Number.isNaN(input[0]) || Number.isNaN(input[1])) { + return zero + } + + if (input[0] === Infinity || input[1] === Infinity) { + return infinity + } + + return nanos(BigInt(Math.round(input[0] * 1_000_000_000)) + BigInt(Math.round(input[1]))) + } else if (isString(input)) { + const match = DURATION_REGEX.exec(input) + if (match) { + const [_, valueStr, unit] = match + const value = Number(valueStr) + switch (unit) { + case "nano": + case "nanos": + return nanos(BigInt(valueStr)) + case "micro": + case "micros": + return micros(BigInt(valueStr)) + case "milli": + case "millis": + return millis(value) + case "second": + case "seconds": + return seconds(value) + case "minute": + case "minutes": + return minutes(value) + case "hour": + case "hours": + return hours(value) + case "day": + case "days": + return days(value) + case "week": + case "weeks": + return weeks(value) + } + } + } + throw new Error("Invalid DurationInput") +} + +/** + * @since 2.5.0 + */ +export const decodeUnknown: (u: unknown) => Option.Option = Option.liftThrowable(decode) as any + +const zeroValue: DurationValue = { _tag: "Millis", millis: 0 } +const infinityValue: DurationValue = { _tag: "Infinity" } + +const DurationProto: Omit = { + [TypeId]: TypeId, + [Hash.symbol](this: Duration) { + return Hash.cached(this, Hash.structure(this.value)) + }, + [Equal.symbol](this: Duration, that: unknown): boolean { + return isDuration(that) && equals(this, that) + }, + toString(this: Duration) { + return `Duration(${format(this)})` + }, + toJSON(this: Duration) { + switch (this.value._tag) { + case "Millis": + return { _id: "Duration", _tag: "Millis", millis: this.value.millis } + case "Nanos": + return { _id: "Duration", _tag: "Nanos", hrtime: toHrTime(this) } + case "Infinity": + return { _id: "Duration", _tag: "Infinity" } + } + }, + [NodeInspectSymbol]() { + return this.toJSON() + }, + pipe() { + return pipeArguments(this, arguments) + } +} as const + +const make = (input: number | bigint): Duration => { + const duration = Object.create(DurationProto) + if (isNumber(input)) { + if (isNaN(input) || input <= 0) { + duration.value = zeroValue + } else if (!Number.isFinite(input)) { + duration.value = infinityValue + } else if (!Number.isInteger(input)) { + duration.value = { _tag: "Nanos", nanos: BigInt(Math.round(input * 1_000_000)) } + } else { + duration.value = { _tag: "Millis", millis: input } + } + } else if (input <= bigint0) { + duration.value = zeroValue + } else { + duration.value = { _tag: "Nanos", nanos: input } + } + return duration +} + +/** + * @since 2.0.0 + * @category guards + */ +export const isDuration = (u: unknown): u is Duration => hasProperty(u, TypeId) + +/** + * @since 2.0.0 + * @category guards + */ +export const isFinite = (self: Duration): boolean => self.value._tag !== "Infinity" + +/** + * @since 3.5.0 + * @category guards + */ +export const isZero = (self: Duration): boolean => { + switch (self.value._tag) { + case "Millis": { + return self.value.millis === 0 + } + case "Nanos": { + return self.value.nanos === bigint0 + } + case "Infinity": { + return false + } + } +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const zero: Duration = make(0) + +/** + * @since 2.0.0 + * @category constructors + */ +export const infinity: Duration = make(Infinity) + +/** + * @since 2.0.0 + * @category constructors + */ +export const nanos = (nanos: bigint): Duration => make(nanos) + +/** + * @since 2.0.0 + * @category constructors + */ +export const micros = (micros: bigint): Duration => make(micros * bigint1e3) + +/** + * @since 2.0.0 + * @category constructors + */ +export const millis = (millis: number): Duration => make(millis) + +/** + * @since 2.0.0 + * @category constructors + */ +export const seconds = (seconds: number): Duration => make(seconds * 1000) + +/** + * @since 2.0.0 + * @category constructors + */ +export const minutes = (minutes: number): Duration => make(minutes * 60_000) + +/** + * @since 2.0.0 + * @category constructors + */ +export const hours = (hours: number): Duration => make(hours * 3_600_000) + +/** + * @since 2.0.0 + * @category constructors + */ +export const days = (days: number): Duration => make(days * 86_400_000) + +/** + * @since 2.0.0 + * @category constructors + */ +export const weeks = (weeks: number): Duration => make(weeks * 604_800_000) + +/** + * @since 2.0.0 + * @category getters + */ +export const toMillis = (self: DurationInput): number => + match(self, { + onMillis: (millis) => millis, + onNanos: (nanos) => Number(nanos) / 1_000_000 + }) + +/** + * @since 2.0.0 + * @category getters + */ +export const toSeconds = (self: DurationInput): number => + match(self, { + onMillis: (millis) => millis / 1_000, + onNanos: (nanos) => Number(nanos) / 1_000_000_000 + }) + +/** + * @since 3.8.0 + * @category getters + */ +export const toMinutes = (self: DurationInput): number => + match(self, { + onMillis: (millis) => millis / 60_000, + onNanos: (nanos) => Number(nanos) / 60_000_000_000 + }) + +/** + * @since 3.8.0 + * @category getters + */ +export const toHours = (self: DurationInput): number => + match(self, { + onMillis: (millis) => millis / 3_600_000, + onNanos: (nanos) => Number(nanos) / 3_600_000_000_000 + }) + +/** + * @since 3.8.0 + * @category getters + */ +export const toDays = (self: DurationInput): number => + match(self, { + onMillis: (millis) => millis / 86_400_000, + onNanos: (nanos) => Number(nanos) / 86_400_000_000_000 + }) + +/** + * @since 3.8.0 + * @category getters + */ +export const toWeeks = (self: DurationInput): number => + match(self, { + onMillis: (millis) => millis / 604_800_000, + onNanos: (nanos) => Number(nanos) / 604_800_000_000_000 + }) + +/** + * Get the duration in nanoseconds as a bigint. + * + * If the duration is infinite, returns `Option.none()` + * + * @since 2.0.0 + * @category getters + */ +export const toNanos = (self: DurationInput): Option.Option => { + const _self = decode(self) + switch (_self.value._tag) { + case "Infinity": + return Option.none() + case "Nanos": + return Option.some(_self.value.nanos) + case "Millis": + return Option.some(BigInt(Math.round(_self.value.millis * 1_000_000))) + } +} + +/** + * Get the duration in nanoseconds as a bigint. + * + * If the duration is infinite, it throws an error. + * + * @since 2.0.0 + * @category getters + */ +export const unsafeToNanos = (self: DurationInput): bigint => { + const _self = decode(self) + switch (_self.value._tag) { + case "Infinity": + throw new Error("Cannot convert infinite duration to nanos") + case "Nanos": + return _self.value.nanos + case "Millis": + return BigInt(Math.round(_self.value.millis * 1_000_000)) + } +} + +/** + * @since 2.0.0 + * @category getters + */ +export const toHrTime = (self: DurationInput): [seconds: number, nanos: number] => { + const _self = decode(self) + switch (_self.value._tag) { + case "Infinity": + return [Infinity, 0] + case "Nanos": + return [ + Number(_self.value.nanos / bigint1e9), + Number(_self.value.nanos % bigint1e9) + ] + case "Millis": + return [ + Math.floor(_self.value.millis / 1000), + Math.round((_self.value.millis % 1000) * 1_000_000) + ] + } +} + +/** + * @since 2.0.0 + * @category pattern matching + */ +export const match: { + /** + * @since 2.0.0 + * @category pattern matching + */ + ( + options: { + readonly onMillis: (millis: number) => A + readonly onNanos: (nanos: bigint) => B + } + ): (self: DurationInput) => A | B + /** + * @since 2.0.0 + * @category pattern matching + */ + ( + self: DurationInput, + options: { + readonly onMillis: (millis: number) => A + readonly onNanos: (nanos: bigint) => B + } + ): A | B +} = dual(2, ( + self: DurationInput, + options: { + readonly onMillis: (millis: number) => A + readonly onNanos: (nanos: bigint) => B + } +): A | B => { + const _self = decode(self) + switch (_self.value._tag) { + case "Nanos": + return options.onNanos(_self.value.nanos) + case "Infinity": + return options.onMillis(Infinity) + case "Millis": + return options.onMillis(_self.value.millis) + } +}) + +/** + * @since 2.0.0 + * @category pattern matching + */ +export const matchWith: { + /** + * @since 2.0.0 + * @category pattern matching + */ + ( + that: DurationInput, + options: { + readonly onMillis: (self: number, that: number) => A + readonly onNanos: (self: bigint, that: bigint) => B + } + ): (self: DurationInput) => A | B + /** + * @since 2.0.0 + * @category pattern matching + */ + ( + self: DurationInput, + that: DurationInput, + options: { + readonly onMillis: (self: number, that: number) => A + readonly onNanos: (self: bigint, that: bigint) => B + } + ): A | B +} = dual(3, ( + self: DurationInput, + that: DurationInput, + options: { + readonly onMillis: (self: number, that: number) => A + readonly onNanos: (self: bigint, that: bigint) => B + } +): A | B => { + const _self = decode(self) + const _that = decode(that) + if (_self.value._tag === "Infinity" || _that.value._tag === "Infinity") { + return options.onMillis( + toMillis(_self), + toMillis(_that) + ) + } else if (_self.value._tag === "Nanos" || _that.value._tag === "Nanos") { + const selfNanos = _self.value._tag === "Nanos" ? + _self.value.nanos : + BigInt(Math.round(_self.value.millis * 1_000_000)) + const thatNanos = _that.value._tag === "Nanos" ? + _that.value.nanos : + BigInt(Math.round(_that.value.millis * 1_000_000)) + return options.onNanos(selfNanos, thatNanos) + } + + return options.onMillis( + _self.value.millis, + _that.value.millis + ) +}) + +/** + * @category instances + * @since 2.0.0 + */ +export const Order: order.Order = order.make((self, that) => + matchWith(self, that, { + onMillis: (self, that) => (self < that ? -1 : self > that ? 1 : 0), + onNanos: (self, that) => (self < that ? -1 : self > that ? 1 : 0) + }) +) + +/** + * Checks if a `Duration` is between a `minimum` and `maximum` value. + * + * @category predicates + * @since 2.0.0 + */ +export const between: { + /** + * Checks if a `Duration` is between a `minimum` and `maximum` value. + * + * @category predicates + * @since 2.0.0 + */ + ( + options: { + minimum: DurationInput + maximum: DurationInput + } + ): (self: DurationInput) => boolean + /** + * Checks if a `Duration` is between a `minimum` and `maximum` value. + * + * @category predicates + * @since 2.0.0 + */ + ( + self: DurationInput, + options: { + minimum: DurationInput + maximum: DurationInput + } + ): boolean +} = order.between(order.mapInput(Order, decode)) + +/** + * @category instances + * @since 2.0.0 + */ +export const Equivalence: equivalence.Equivalence = (self, that) => + matchWith(self, that, { + onMillis: (self, that) => self === that, + onNanos: (self, that) => self === that + }) + +const _min = order.min(Order) + +/** + * @since 2.0.0 + */ +export const min: { + /** + * @since 2.0.0 + */ + (that: DurationInput): (self: DurationInput) => Duration + /** + * @since 2.0.0 + */ + (self: DurationInput, that: DurationInput): Duration +} = dual(2, (self: DurationInput, that: DurationInput): Duration => _min(decode(self), decode(that))) + +const _max = order.max(Order) + +/** + * @since 2.0.0 + * @category order + */ +export const max: { + /** + * @since 2.0.0 + * @category order + */ + (that: DurationInput): (self: DurationInput) => Duration + /** + * @since 2.0.0 + * @category order + */ + (self: DurationInput, that: DurationInput): Duration +} = dual(2, (self: DurationInput, that: DurationInput): Duration => _max(decode(self), decode(that))) + +const _clamp = order.clamp(Order) + +/** + * @since 2.0.0 + * @category order + */ +export const clamp: { + /** + * @since 2.0.0 + * @category order + */ + ( + options: { + minimum: DurationInput + maximum: DurationInput + } + ): (self: DurationInput) => Duration + /** + * @since 2.0.0 + * @category order + */ + ( + self: DurationInput, + options: { + minimum: DurationInput + maximum: DurationInput + } + ): Duration +} = dual( + 2, + (self: DurationInput, options: { + minimum: DurationInput + maximum: DurationInput + }): Duration => + _clamp(decode(self), { + minimum: decode(options.minimum), + maximum: decode(options.maximum) + }) +) + +/** + * @since 2.4.19 + * @category math + */ +export const divide: { + /** + * @since 2.4.19 + * @category math + */ + (by: number): (self: DurationInput) => Option.Option + /** + * @since 2.4.19 + * @category math + */ + (self: DurationInput, by: number): Option.Option +} = dual( + 2, + (self: DurationInput, by: number): Option.Option => + match(self, { + onMillis: (millis) => { + if (by === 0 || isNaN(by) || !Number.isFinite(by)) { + return Option.none() + } + return Option.some(make(millis / by)) + }, + onNanos: (nanos) => { + if (isNaN(by) || by <= 0 || !Number.isFinite(by)) { + return Option.none() + } + try { + return Option.some(make(nanos / BigInt(by))) + } catch { + return Option.none() + } + } + }) +) + +/** + * @since 2.4.19 + * @category math + */ +export const unsafeDivide: { + /** + * @since 2.4.19 + * @category math + */ + (by: number): (self: DurationInput) => Duration + /** + * @since 2.4.19 + * @category math + */ + (self: DurationInput, by: number): Duration +} = dual( + 2, + (self: DurationInput, by: number): Duration => + match(self, { + onMillis: (millis) => make(millis / by), + onNanos: (nanos) => { + if (isNaN(by) || by < 0 || Object.is(by, -0)) { + return zero + } else if (Object.is(by, 0) || !Number.isFinite(by)) { + return infinity + } + return make(nanos / BigInt(by)) + } + }) +) + +/** + * @since 2.0.0 + * @category math + */ +export const times: { + /** + * @since 2.0.0 + * @category math + */ + (times: number): (self: DurationInput) => Duration + /** + * @since 2.0.0 + * @category math + */ + (self: DurationInput, times: number): Duration +} = dual( + 2, + (self: DurationInput, times: number): Duration => + match(self, { + onMillis: (millis) => make(millis * times), + onNanos: (nanos) => make(nanos * BigInt(times)) + }) +) + +/** + * @since 2.0.0 + * @category math + */ +export const subtract: { + /** + * @since 2.0.0 + * @category math + */ + (that: DurationInput): (self: DurationInput) => Duration + /** + * @since 2.0.0 + * @category math + */ + (self: DurationInput, that: DurationInput): Duration +} = dual( + 2, + (self: DurationInput, that: DurationInput): Duration => + matchWith(self, that, { + onMillis: (self, that) => make(self - that), + onNanos: (self, that) => make(self - that) + }) +) + +/** + * @since 2.0.0 + * @category math + */ +export const sum: { + /** + * @since 2.0.0 + * @category math + */ + (that: DurationInput): (self: DurationInput) => Duration + /** + * @since 2.0.0 + * @category math + */ + (self: DurationInput, that: DurationInput): Duration +} = dual( + 2, + (self: DurationInput, that: DurationInput): Duration => + matchWith(self, that, { + onMillis: (self, that) => make(self + that), + onNanos: (self, that) => make(self + that) + }) +) + +/** + * @since 2.0.0 + * @category predicates + */ +export const lessThan: { + /** + * @since 2.0.0 + * @category predicates + */ + (that: DurationInput): (self: DurationInput) => boolean + /** + * @since 2.0.0 + * @category predicates + */ + (self: DurationInput, that: DurationInput): boolean +} = dual( + 2, + (self: DurationInput, that: DurationInput): boolean => + matchWith(self, that, { + onMillis: (self, that) => self < that, + onNanos: (self, that) => self < that + }) +) + +/** + * @since 2.0.0 + * @category predicates + */ +export const lessThanOrEqualTo: { + /** + * @since 2.0.0 + * @category predicates + */ + (that: DurationInput): (self: DurationInput) => boolean + /** + * @since 2.0.0 + * @category predicates + */ + (self: DurationInput, that: DurationInput): boolean +} = dual( + 2, + (self: DurationInput, that: DurationInput): boolean => + matchWith(self, that, { + onMillis: (self, that) => self <= that, + onNanos: (self, that) => self <= that + }) +) + +/** + * @since 2.0.0 + * @category predicates + */ +export const greaterThan: { + /** + * @since 2.0.0 + * @category predicates + */ + (that: DurationInput): (self: DurationInput) => boolean + /** + * @since 2.0.0 + * @category predicates + */ + (self: DurationInput, that: DurationInput): boolean +} = dual( + 2, + (self: DurationInput, that: DurationInput): boolean => + matchWith(self, that, { + onMillis: (self, that) => self > that, + onNanos: (self, that) => self > that + }) +) + +/** + * @since 2.0.0 + * @category predicates + */ +export const greaterThanOrEqualTo: { + /** + * @since 2.0.0 + * @category predicates + */ + (that: DurationInput): (self: DurationInput) => boolean + /** + * @since 2.0.0 + * @category predicates + */ + (self: DurationInput, that: DurationInput): boolean +} = dual( + 2, + (self: DurationInput, that: DurationInput): boolean => + matchWith(self, that, { + onMillis: (self, that) => self >= that, + onNanos: (self, that) => self >= that + }) +) + +/** + * @since 2.0.0 + * @category predicates + */ +export const equals: { + /** + * @since 2.0.0 + * @category predicates + */ + (that: DurationInput): (self: DurationInput) => boolean + /** + * @since 2.0.0 + * @category predicates + */ + (self: DurationInput, that: DurationInput): boolean +} = dual(2, (self: DurationInput, that: DurationInput): boolean => Equivalence(decode(self), decode(that))) + +/** + * Converts a `Duration` to its parts. + * + * @since 3.8.0 + * @category conversions + */ +export const parts = (self: DurationInput): { + days: number + hours: number + minutes: number + seconds: number + millis: number + nanos: number +} => { + const duration = decode(self) + if (duration.value._tag === "Infinity") { + return { + days: Infinity, + hours: Infinity, + minutes: Infinity, + seconds: Infinity, + millis: Infinity, + nanos: Infinity + } + } + + const nanos = unsafeToNanos(duration) + const ms = nanos / bigint1e6 + const sec = ms / bigint1e3 + const min = sec / bigint60 + const hr = min / bigint60 + const days = hr / bigint24 + + return { + days: Number(days), + hours: Number(hr % bigint24), + minutes: Number(min % bigint60), + seconds: Number(sec % bigint60), + millis: Number(ms % bigint1e3), + nanos: Number(nanos % bigint1e6) + } +} + +/** + * Converts a `Duration` to a human readable string. + * + * @since 2.0.0 + * @category conversions + * @example + * ```ts + * import { Duration } from "effect" + * + * Duration.format(Duration.millis(1000)) // "1s" + * Duration.format(Duration.millis(1001)) // "1s 1ms" + * ``` + */ +export const format = (self: DurationInput): string => { + const duration = decode(self) + if (duration.value._tag === "Infinity") { + return "Infinity" + } + if (isZero(duration)) { + return "0" + } + + const fragments = parts(duration) + const pieces = [] + if (fragments.days !== 0) { + pieces.push(`${fragments.days}d`) + } + + if (fragments.hours !== 0) { + pieces.push(`${fragments.hours}h`) + } + + if (fragments.minutes !== 0) { + pieces.push(`${fragments.minutes}m`) + } + + if (fragments.seconds !== 0) { + pieces.push(`${fragments.seconds}s`) + } + + if (fragments.millis !== 0) { + pieces.push(`${fragments.millis}ms`) + } + + if (fragments.nanos !== 0) { + pieces.push(`${fragments.nanos}ns`) + } + + return pieces.join(" ") +} + +/** + * Formats a Duration into an ISO8601 duration string. + * + * Months are assumed to be 30 days and years are assumed to be 365 days. + * + * Milliseconds and nanoseconds are expressed as fractional seconds. + * + * @example + * ```ts + * import { Duration } from "effect" + * + * Duration.unsafeFormatIso(Duration.days(1)) // => "P1D" + * Duration.unsafeFormatIso(Duration.minutes(90)) // => "PT1H30M" + * Duration.unsafeFormatIso(Duration.millis(1500)) // => "PT1.5S" + * ``` + * + * @throws `RangeError` If the duration is not finite. + * + * @since 3.13.0 + * @category conversions + */ +export const unsafeFormatIso = (self: DurationInput): string => { + const duration = decode(self) + if (!isFinite(duration)) { + throw new RangeError("Cannot format infinite duration") + } + + const fragments = [] + const { + days, + hours, + millis, + minutes, + nanos, + seconds + } = parts(duration) + + let rest = days + if (rest >= 365) { + const years = Math.floor(rest / 365) + rest %= 365 + fragments.push(`${years}Y`) + } + + if (rest >= 30) { + const months = Math.floor(rest / 30) + rest %= 30 + fragments.push(`${months}M`) + } + + if (rest >= 7) { + const weeks = Math.floor(rest / 7) + rest %= 7 + fragments.push(`${weeks}W`) + } + + if (rest > 0) { + fragments.push(`${rest}D`) + } + + if (hours !== 0 || minutes !== 0 || seconds !== 0 || millis !== 0 || nanos !== 0) { + fragments.push("T") + + if (hours !== 0) { + fragments.push(`${hours}H`) + } + + if (minutes !== 0) { + fragments.push(`${minutes}M`) + } + + if (seconds !== 0 || millis !== 0 || nanos !== 0) { + const total = BigInt(seconds) * bigint1e9 + BigInt(millis) * bigint1e6 + BigInt(nanos) + const str = (Number(total) / 1e9).toFixed(9).replace(/\.?0+$/, "") + fragments.push(`${str}S`) + } + } + + return `P${fragments.join("") || "T0S"}` +} + +/** + * Formats a Duration into an ISO8601 duration string. + * + * Months are assumed to be 30 days and years are assumed to be 365 days. + * + * Returns `Option.none()` if the duration is infinite. + * + * @example + * ```ts + * import { Duration, Option } from "effect" + * + * Duration.formatIso(Duration.days(1)) // => Option.some("P1D") + * Duration.formatIso(Duration.minutes(90)) // => Option.some("PT1H30M") + * Duration.formatIso(Duration.millis(1500)) // => Option.some("PT1.5S") + * Duration.formatIso(Duration.infinity) // => Option.none() + * ``` + * + * @since 3.13.0 + * @category conversions + */ +export const formatIso = (self: DurationInput): Option.Option => { + const duration = decode(self) + return isFinite(duration) ? Option.some(unsafeFormatIso(duration)) : Option.none() +} + +/** + * Parses an ISO8601 duration string into a `Duration`. + * + * Months are assumed to be 30 days and years are assumed to be 365 days. + * + * @example + * ```ts + * import { Duration, Option } from "effect" + * + * Duration.fromIso("P1D") // => Option.some(Duration.days(1)) + * Duration.fromIso("PT1H") // => Option.some(Duration.hours(1)) + * Duration.fromIso("PT1M") // => Option.some(Duration.minutes(1)) + * Duration.fromIso("PT1.5S") // => Option.some(Duration.seconds(1.5)) + * ``` + * + * @since 3.13.0 + * @category conversions + */ +export const fromIso = (iso: string): Option.Option => { + const result = DURATION_ISO_REGEX.exec(iso) + if (result == null) { + return Option.none() + } + + const [years, months, weeks, days, hours, mins, secs] = result.slice(1, 8).map((_) => _ ? Number(_) : 0) + const value = years * 365 * 24 * 60 * 60 + + months * 30 * 24 * 60 * 60 + + weeks * 7 * 24 * 60 * 60 + + days * 24 * 60 * 60 + + hours * 60 * 60 + + mins * 60 + + secs + + return Option.some(seconds(value)) +} + +const DURATION_ISO_REGEX = + /^P(?!$)(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?!$)(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/ diff --git a/backend/node_modules/effect/src/Effectable.ts b/backend/node_modules/effect/src/Effectable.ts new file mode 100644 index 0000000000000000000000000000000000000000..f0821135735096d1efaef432f8297517087d3be2 --- /dev/null +++ b/backend/node_modules/effect/src/Effectable.ts @@ -0,0 +1,107 @@ +/** + * @since 2.0.0 + */ +import type * as Channel from "./Channel.js" +import type * as Effect from "./Effect.js" +import * as internal from "./internal/effectable.js" +import type * as Sink from "./Sink.js" +import type * as Stream from "./Stream.js" + +/** + * @since 2.0.0 + * @category type ids + */ +export const EffectTypeId: Effect.EffectTypeId = internal.EffectTypeId + +/** + * @since 2.0.0 + * @category type ids + */ +export type EffectTypeId = Effect.EffectTypeId + +/** + * @since 2.0.0 + * @category type ids + */ +export const StreamTypeId: Stream.StreamTypeId = internal.StreamTypeId + +/** + * @since 2.0.0 + * @category type ids + */ +export type StreamTypeId = Stream.StreamTypeId + +/** + * @since 2.0.0 + * @category type ids + */ +export const SinkTypeId: Sink.SinkTypeId = internal.SinkTypeId + +/** + * @since 2.0.0 + * @category type ids + */ +export type SinkTypeId = Sink.SinkTypeId + +/** + * @since 2.0.0 + * @category type ids + */ +export const ChannelTypeId: Channel.ChannelTypeId = internal.ChannelTypeId + +/** + * @since 2.0.0 + * @category type ids + */ +export type ChannelTypeId = Channel.ChannelTypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface CommitPrimitive { + new(): Effect.Effect +} + +/** + * @since 2.0.0 + * @category prototypes + */ +export const EffectPrototype: Effect.Effect = internal.EffectPrototype + +/** + * @since 2.0.0 + * @category prototypes + */ +export const CommitPrototype: Effect.Effect = internal.CommitPrototype + +/** + * @since 2.0.0 + * @category prototypes + */ +export const StructuralCommitPrototype: Effect.Effect = internal.StructuralCommitPrototype + +const Base: CommitPrimitive = internal.Base +const StructuralBase: CommitPrimitive = internal.StructuralBase + +/** + * @since 2.0.0 + * @category constructors + */ +export abstract class Class extends Base { + /** + * @since 2.0.0 + */ + abstract commit(): Effect.Effect +} + +/** + * @since 2.0.0 + * @category constructors + */ +export abstract class StructuralClass extends StructuralBase { + /** + * @since 2.0.0 + */ + abstract commit(): Effect.Effect +} diff --git a/backend/node_modules/effect/src/Encoding.ts b/backend/node_modules/effect/src/Encoding.ts new file mode 100644 index 0000000000000000000000000000000000000000..ec2d7e17d62264e436d6cd91662872dcd5c18fe9 --- /dev/null +++ b/backend/node_modules/effect/src/Encoding.ts @@ -0,0 +1,195 @@ +/** + * This module provides encoding & decoding functionality for: + * + * - base64 (RFC4648) + * - base64 (URL) + * - hex + * + * @since 2.0.0 + */ +import * as Either from "./Either.js" +import * as Base64 from "./internal/encoding/base64.js" +import * as Base64Url from "./internal/encoding/base64Url.js" +import * as Common from "./internal/encoding/common.js" +import * as Hex from "./internal/encoding/hex.js" + +/** + * Encodes the given value into a base64 (RFC4648) `string`. + * + * @category encoding + * @since 2.0.0 + */ +export const encodeBase64: (input: Uint8Array | string) => string = (input) => + typeof input === "string" ? Base64.encode(Common.encoder.encode(input)) : Base64.encode(input) + +/** + * Decodes a base64 (RFC4648) encoded `string` into a `Uint8Array`. + * + * @category decoding + * @since 2.0.0 + */ +export const decodeBase64 = (str: string): Either.Either => Base64.decode(str) + +/** + * Decodes a base64 (RFC4648) encoded `string` into a UTF-8 `string`. + * + * @category decoding + * @since 2.0.0 + */ +export const decodeBase64String = (str: string) => Either.map(decodeBase64(str), (_) => Common.decoder.decode(_)) + +/** + * Encodes the given value into a base64 (URL) `string`. + * + * @category encoding + * @since 2.0.0 + */ +export const encodeBase64Url: (input: Uint8Array | string) => string = (input) => + typeof input === "string" ? Base64Url.encode(Common.encoder.encode(input)) : Base64Url.encode(input) + +/** + * Decodes a base64 (URL) encoded `string` into a `Uint8Array`. + * + * @category decoding + * @since 2.0.0 + */ +export const decodeBase64Url = (str: string): Either.Either => Base64Url.decode(str) + +/** + * Decodes a base64 (URL) encoded `string` into a UTF-8 `string`. + * + * @category decoding + * @since 2.0.0 + */ +export const decodeBase64UrlString = (str: string) => Either.map(decodeBase64Url(str), (_) => Common.decoder.decode(_)) + +/** + * Encodes the given value into a hex `string`. + * + * @category encoding + * @since 2.0.0 + */ +export const encodeHex: (input: Uint8Array | string) => string = (input) => + typeof input === "string" ? Hex.encode(Common.encoder.encode(input)) : Hex.encode(input) + +/** + * Decodes a hex encoded `string` into a `Uint8Array`. + * + * @category decoding + * @since 2.0.0 + */ +export const decodeHex = (str: string): Either.Either => Hex.decode(str) + +/** + * Decodes a hex encoded `string` into a UTF-8 `string`. + * + * @category decoding + * @since 2.0.0 + */ +export const decodeHexString = (str: string) => Either.map(decodeHex(str), (_) => Common.decoder.decode(_)) + +/** + * Encodes a UTF-8 `string` into a URI component `string`. + * + * @category encoding + * @since 3.12.0 + */ +export const encodeUriComponent = (str: string): Either.Either => + Either.try({ + try: () => encodeURIComponent(str), + catch: (e) => EncodeException(str, e instanceof Error ? e.message : "Invalid input") + }) + +/** + * Decodes a URI component `string` into a UTF-8 `string`. + * + * @category decoding + * @since 3.12.0 + */ +export const decodeUriComponent = (str: string): Either.Either => + Either.try({ + try: () => decodeURIComponent(str), + catch: (e) => DecodeException(str, e instanceof Error ? e.message : "Invalid input") + }) + +/** + * @since 2.0.0 + * @category symbols + */ +export const DecodeExceptionTypeId: unique symbol = Common.DecodeExceptionTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type DecodeExceptionTypeId = typeof DecodeExceptionTypeId + +/** + * Represents a checked exception which occurs when decoding fails. + * + * @since 2.0.0 + * @category models + */ +export interface DecodeException { + readonly _tag: "DecodeException" + readonly [DecodeExceptionTypeId]: DecodeExceptionTypeId + readonly input: string + readonly message?: string +} + +/** + * Creates a checked exception which occurs when decoding fails. + * + * @since 2.0.0 + * @category errors + */ +export const DecodeException: (input: string, message?: string) => DecodeException = Common.DecodeException + +/** + * Returns `true` if the specified value is an `DecodeException`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isDecodeException: (u: unknown) => u is DecodeException = Common.isDecodeException + +/** + * @since 3.12.0 + * @category symbols + */ +export const EncodeExceptionTypeId: unique symbol = Common.EncodeExceptionTypeId + +/** + * @since 3.12.0 + * @category symbols + */ +export type EncodeExceptionTypeId = typeof EncodeExceptionTypeId + +/** + * Represents a checked exception which occurs when encoding fails. + * + * @since 3.12.0 + * @category models + */ +export interface EncodeException { + readonly _tag: "EncodeException" + readonly [EncodeExceptionTypeId]: EncodeExceptionTypeId + readonly input: string + readonly message?: string +} + +/** + * Creates a checked exception which occurs when encoding fails. + * + * @since 3.12.0 + * @category errors + */ +export const EncodeException: (input: string, message?: string) => EncodeException = Common.EncodeException + +/** + * Returns `true` if the specified value is an `EncodeException`, `false` otherwise. + * + * @since 3.12.0 + * @category refinements + */ +export const isEncodeException: (u: unknown) => u is EncodeException = Common.isEncodeException diff --git a/backend/node_modules/effect/src/Equal.ts b/backend/node_modules/effect/src/Equal.ts new file mode 100644 index 0000000000000000000000000000000000000000..e6ca6d8ba231f7fd8c01d48c4dbd38226ac02a19 --- /dev/null +++ b/backend/node_modules/effect/src/Equal.ts @@ -0,0 +1,101 @@ +/** + * @since 2.0.0 + */ +import type { Equivalence } from "./Equivalence.js" +import * as Hash from "./Hash.js" +import { hasProperty } from "./Predicate.js" +import { structuralRegionState } from "./Utils.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const symbol: unique symbol = Symbol.for("effect/Equal") + +/** + * @since 2.0.0 + * @category models + */ +export interface Equal extends Hash.Hash { + [symbol](that: Equal): boolean +} + +/** + * @since 2.0.0 + * @category equality + */ +export function equals(that: B): (self: A) => boolean +export function equals(self: A, that: B): boolean +export function equals(): any { + if (arguments.length === 1) { + return (self: unknown) => compareBoth(self, arguments[0]) + } + return compareBoth(arguments[0], arguments[1]) +} + +function compareBoth(self: unknown, that: unknown): boolean { + if (self === that) { + return true + } + const selfType = typeof self + if (selfType !== typeof that) { + return false + } + if (selfType === "object" || selfType === "function") { + if (self !== null && that !== null) { + if (isEqual(self) && isEqual(that)) { + if (Hash.hash(self) === Hash.hash(that) && self[symbol](that)) { + return true + } else { + return structuralRegionState.enabled && structuralRegionState.tester + ? structuralRegionState.tester(self, that) + : false + } + } else if (self instanceof Date && that instanceof Date) { + const t1 = self.getTime() + const t2 = that.getTime() + return t1 === t2 || (Number.isNaN(t1) && Number.isNaN(t2)) + } else if (self instanceof URL && that instanceof URL) { + return self.href === that.href + } + } + if (structuralRegionState.enabled) { + if (self === null || that === null) { + return false + } + if (Array.isArray(self) && Array.isArray(that)) { + return self.length === that.length && self.every((v, i) => compareBoth(v, that[i])) + } + if (Object.getPrototypeOf(self) === Object.prototype && Object.getPrototypeOf(that) === Object.prototype) { + const keysSelf = Object.keys(self as any) + const keysThat = Object.keys(that as any) + if (keysSelf.length === keysThat.length) { + for (const key of keysSelf) { + // @ts-expect-error + if (!(key in that && compareBoth(self[key], that[key]))) { + return structuralRegionState.tester ? structuralRegionState.tester(self, that) : false + } + } + return true + } + } + return structuralRegionState.tester ? structuralRegionState.tester(self, that) : false + } + } + + return structuralRegionState.enabled && structuralRegionState.tester + ? structuralRegionState.tester(self, that) + : false +} + +/** + * @since 2.0.0 + * @category guards + */ +export const isEqual = (u: unknown): u is Equal => hasProperty(u, symbol) + +/** + * @since 2.0.0 + * @category instances + */ +export const equivalence: () => Equivalence = () => equals diff --git a/backend/node_modules/effect/src/ExecutionPlan.ts b/backend/node_modules/effect/src/ExecutionPlan.ts new file mode 100644 index 0000000000000000000000000000000000000000..d928389fbbd87a8e2b6d02038844c1b19023f980 --- /dev/null +++ b/backend/node_modules/effect/src/ExecutionPlan.ts @@ -0,0 +1,308 @@ +/** + * @since 3.16.0 + * @experimental + */ +import type { NonEmptyReadonlyArray } from "./Array.js" +import type * as Context from "./Context.js" +import * as Effect from "./Effect.js" +import * as internal from "./internal/executionPlan.js" +import * as Layer from "./Layer.js" +import type { Pipeable } from "./Pipeable.js" +import { pipeArguments } from "./Pipeable.js" +import type * as Schedule from "./Schedule.js" + +/** + * @since 3.16.0 + * @category Symbols + * @experimental + */ +export const TypeId: unique symbol = internal.TypeId + +/** + * @since 3.16.0 + * @category Symbols + * @experimental + */ +export type TypeId = typeof TypeId + +/** + * @since 3.16.0 + * @category Guards + * @experimental + */ +export const isExecutionPlan: (u: unknown) => u is ExecutionPlan = internal.isExecutionPlan + +/** + * A `ExecutionPlan` can be used with `Effect.withExecutionPlan` or `Stream.withExecutionPlan`, allowing you to provide different resources for each step of execution until the effect succeeds or the plan is exhausted. + * + * ```ts + * import type { LanguageModel } from "@effect/ai" + * import type { Layer } from "effect" + * import { Effect, ExecutionPlan, Schedule } from "effect" + * + * declare const layerBad: Layer.Layer + * declare const layerGood: Layer.Layer + * + * const ThePlan = ExecutionPlan.make( + * { + * // First try with the bad layer 2 times with a 3 second delay between attempts + * provide: layerBad, + * attempts: 2, + * schedule: Schedule.spaced(3000) + * }, + * // Then try with the bad layer 3 times with a 1 second delay between attempts + * { + * provide: layerBad, + * attempts: 3, + * schedule: Schedule.spaced(1000) + * }, + * // Finally try with the good layer. + * // + * // If `attempts` is omitted, the plan will only attempt once, unless a schedule is provided. + * { + * provide: layerGood + * } + * ) + * + * declare const effect: Effect.Effect< + * void, + * never, + * LanguageModel.LanguageModel + * > + * const withPlan: Effect.Effect = Effect.withExecutionPlan(effect, ThePlan) + * ``` + * + * @since 3.16.0 + * @category Models + * @experimental + */ +export interface ExecutionPlan< + Types extends { + provides: any + input: any + error: any + requirements: any + } +> extends Pipeable { + readonly [TypeId]: TypeId + readonly steps: NonEmptyReadonlyArray<{ + readonly provide: + | Context.Context + | Layer.Layer + readonly attempts?: number | undefined + readonly while?: + | ((input: Types["input"]) => Effect.Effect) + | undefined + readonly schedule?: Schedule.Schedule | undefined + }> + + /** + * Returns an equivalent `ExecutionPlan` with the requirements satisfied, + * using the current context. + */ + readonly withRequirements: Effect.Effect< + ExecutionPlan<{ + provides: Types["provides"] + input: Types["input"] + error: Types["error"] + requirements: never + }>, + never, + Types["requirements"] + > +} + +/** + * @since 3.16.0 + * @experimental + */ +export type TypesBase = { + provides: any + input: any + error: any + requirements: any +} + +/** + * Create an `ExecutionPlan`, which can be used with `Effect.withExecutionPlan` or `Stream.withExecutionPlan`, allowing you to provide different resources for each step of execution until the effect succeeds or the plan is exhausted. + * + * ```ts + * import type { LanguageModel } from "@effect/ai" + * import type { Layer } from "effect" + * import { Effect, ExecutionPlan, Schedule } from "effect" + * + * declare const layerBad: Layer.Layer + * declare const layerGood: Layer.Layer + * + * const ThePlan = ExecutionPlan.make( + * { + * // First try with the bad layer 2 times with a 3 second delay between attempts + * provide: layerBad, + * attempts: 2, + * schedule: Schedule.spaced(3000) + * }, + * // Then try with the bad layer 3 times with a 1 second delay between attempts + * { + * provide: layerBad, + * attempts: 3, + * schedule: Schedule.spaced(1000) + * }, + * // Finally try with the good layer. + * // + * // If `attempts` is omitted, the plan will only attempt once, unless a schedule is provided. + * { + * provide: layerGood + * } + * ) + * + * declare const effect: Effect.Effect< + * void, + * never, + * LanguageModel.LanguageModel + * > + * const withPlan: Effect.Effect = Effect.withExecutionPlan(effect, ThePlan) + * ``` + * + * @since 3.16.0 + * @category Constructors + * @experimental + */ +export const make = >( + ...steps: Steps & { [K in keyof Steps]: make.Step } +): ExecutionPlan<{ + provides: make.StepProvides + input: make.StepInput + error: + | (Steps[number]["provide"] extends Context.Context | Layer.Layer ? E + : never) + | (Steps[number]["while"] extends (input: infer _I) => Effect.Effect ? _E : never) + requirements: + | (Steps[number]["provide"] extends Layer.Layer ? R : never) + | (Steps[number]["while"] extends (input: infer _I) => Effect.Effect ? R : never) + | (Steps[number]["schedule"] extends Schedule.Schedule ? R : never) +}> => + makeProto(steps.map((options, i) => { + if (options.attempts && options.attempts < 1) { + throw new Error(`ExecutionPlan.make: step[${i}].attempts must be greater than 0`) + } + return { + schedule: options.schedule, + attempts: options.attempts, + while: options.while + ? (input: any) => + Effect.suspend(() => { + const result = options.while!(input) + return typeof result === "boolean" ? Effect.succeed(result) : result + }) + : undefined, + provide: options.provide + } + }) as any) + +/** + * @since 3.16.0 + * @experimental + */ +export declare namespace make { + /** + * @since 3.16.0 + * @experimental + */ + export type Step = { + readonly provide: Context.Context | Context.Context | Layer.Layer.Any + readonly attempts?: number | undefined + readonly while?: ((input: any) => boolean | Effect.Effect) | undefined + readonly schedule?: Schedule.Schedule | undefined + } + + /** + * @since 3.16.1 + * @experimental + */ + export type StepProvides, Out = unknown> = Steps extends + readonly [infer Step, ...infer Rest] ? StepProvides< + Rest, + & Out + & ( + (Step extends { readonly provide: Context.Context | Layer.Layer } ? P + : unknown) + ) + > : + Out + + /** + * @since 3.16.1 + * @experimental + */ + export type PlanProvides, Out = unknown> = Plans extends + readonly [infer Plan, ...infer Rest] ? + PlanProvides ? T["provides"] : unknown)> : + Out + + /** + * @since 3.16.0 + * @experimental + */ + export type StepInput, Out = unknown> = Steps extends + readonly [infer Step, ...infer Rest] ? StepInput< + Rest, + & Out + & ( + & (Step extends { readonly while: (input: infer I) => infer _ } ? I : unknown) + & (Step extends { readonly schedule: Schedule.Schedule } ? I : unknown) + ) + > : + Out + + /** + * @since 3.16.0 + * @experimental + */ + export type PlanInput, Out = unknown> = Plans extends + readonly [infer Plan, ...infer Rest] ? + PlanInput ? T["input"] : unknown)> : + Out +} + +const Proto: Omit, "steps"> = { + [TypeId]: TypeId, + get withRequirements() { + const self = this as any as ExecutionPlan + return Effect.contextWith((context: Context.Context) => + makeProto(self.steps.map((step) => ({ + ...step, + provide: Layer.isLayer(step.provide) ? Layer.provide(step.provide, Layer.succeedContext(context)) : step.provide + })) as any) + ) + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +const makeProto = ( + steps: ExecutionPlan<{ + provides: Provides + input: In + error: PlanE + requirements: PlanR + }>["steps"] +) => { + const self = Object.create(Proto) + self.steps = steps + return self +} + +/** + * @since 3.16.0 + * @category Combining + * @experimental + */ +export const merge = >>( + ...plans: Plans +): ExecutionPlan<{ + provides: make.PlanProvides + input: make.PlanInput + error: Plans[number] extends ExecutionPlan ? T["error"] : never + requirements: Plans[number] extends ExecutionPlan ? T["requirements"] : never +}> => makeProto(plans.flatMap((plan) => plan.steps) as any) diff --git a/backend/node_modules/effect/src/ExecutionStrategy.ts b/backend/node_modules/effect/src/ExecutionStrategy.ts new file mode 100644 index 0000000000000000000000000000000000000000..234698c2ef23fb446650af3c76dcd9c3c187f71f --- /dev/null +++ b/backend/node_modules/effect/src/ExecutionStrategy.ts @@ -0,0 +1,138 @@ +/** + * @since 2.0.0 + */ +import type { LazyArg } from "./Function.js" +import * as internal from "./internal/executionStrategy.js" + +/** + * Describes a strategy for evaluating multiple effects, potentially in + * parallel. + * + * There are 3 possible execution strategies: `Sequential`, `Parallel`, + * `ParallelN`. + * + * @since 2.0.0 + * @category models + */ +export type ExecutionStrategy = Sequential | Parallel | ParallelN + +/** + * Execute effects sequentially. + * + * @since 2.0.0 + * @category models + */ +export interface Sequential { + readonly _tag: "Sequential" +} + +/** + * Execute effects in parallel. + * + * @since 2.0.0 + * @category models + */ +export interface Parallel { + readonly _tag: "Parallel" +} + +/** + * Execute effects in parallel, up to the specified number of concurrent fibers. + * + * @since 2.0.0 + * @category models + */ +export interface ParallelN { + readonly _tag: "ParallelN" + readonly parallelism: number +} + +/** + * Execute effects sequentially. + * + * @since 2.0.0 + * @category constructors + */ +export const sequential: ExecutionStrategy = internal.sequential + +/** + * Execute effects in parallel. + * + * @since 2.0.0 + * @category constructors + */ +export const parallel: ExecutionStrategy = internal.parallel + +/** + * Execute effects in parallel, up to the specified number of concurrent fibers. + * + * @since 2.0.0 + * @category constructors + */ +export const parallelN: (parallelism: number) => ExecutionStrategy = internal.parallelN + +/** + * Returns `true` if the specified `ExecutionStrategy` is an instance of + * `Sequential`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isSequential: (self: ExecutionStrategy) => self is Sequential = internal.isSequential + +/** + * Returns `true` if the specified `ExecutionStrategy` is an instance of + * `Sequential`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isParallel: (self: ExecutionStrategy) => self is Parallel = internal.isParallel + +/** + * Returns `true` if the specified `ExecutionStrategy` is an instance of + * `Sequential`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isParallelN: (self: ExecutionStrategy) => self is ParallelN = internal.isParallelN + +/** + * Folds over the specified `ExecutionStrategy` using the provided case + * functions. + * + * @since 2.0.0 + * @category folding + */ +export const match: { + /** + * Folds over the specified `ExecutionStrategy` using the provided case + * functions. + * + * @since 2.0.0 + * @category folding + */ + ( + options: { + readonly onSequential: LazyArg + readonly onParallel: LazyArg + readonly onParallelN: (n: number) => A + } + ): (self: ExecutionStrategy) => A + /** + * Folds over the specified `ExecutionStrategy` using the provided case + * functions. + * + * @since 2.0.0 + * @category folding + */ + ( + self: ExecutionStrategy, + options: { + readonly onSequential: LazyArg + readonly onParallel: LazyArg + readonly onParallelN: (n: number) => A + } + ): A +} = internal.match diff --git a/backend/node_modules/effect/src/FiberHandle.ts b/backend/node_modules/effect/src/FiberHandle.ts new file mode 100644 index 0000000000000000000000000000000000000000..61a9d356e1c1f2224ead32c288c5b3008b82f3d3 --- /dev/null +++ b/backend/node_modules/effect/src/FiberHandle.ts @@ -0,0 +1,582 @@ +/** + * @since 2.0.0 + */ +import type { NoSuchElementException } from "./Cause.js" +import * as Cause from "./Cause.js" +import * as Deferred from "./Deferred.js" +import * as Effect from "./Effect.js" +import * as Exit from "./Exit.js" +import * as Fiber from "./Fiber.js" +import * as FiberId from "./FiberId.js" +import { constFalse, dual } from "./Function.js" +import * as HashSet from "./HashSet.js" +import * as Inspectable from "./Inspectable.js" +import * as Option from "./Option.js" +import { type Pipeable, pipeArguments } from "./Pipeable.js" +import * as Predicate from "./Predicate.js" +import * as Runtime from "./Runtime.js" +import type * as Scope from "./Scope.js" + +/** + * @since 2.0.0 + * @categories type ids + */ +export const TypeId: unique symbol = Symbol.for("effect/FiberHandle") + +/** + * @since 2.0.0 + * @categories type ids + */ +export type TypeId = typeof TypeId + +/** + * @since 2.0.0 + * @categories models + */ +export interface FiberHandle extends Pipeable, Inspectable.Inspectable { + readonly [TypeId]: TypeId + readonly deferred: Deferred.Deferred + /** @internal */ + state: { + readonly _tag: "Open" + fiber: Fiber.RuntimeFiber | undefined + } | { + readonly _tag: "Closed" + } +} + +/** + * @since 2.0.0 + * @categories refinements + */ +export const isFiberHandle = (u: unknown): u is FiberHandle => Predicate.hasProperty(u, TypeId) + +const Proto = { + [TypeId]: TypeId, + toString(this: FiberHandle) { + return Inspectable.format(this.toJSON()) + }, + toJSON(this: FiberHandle) { + return { + _id: "FiberHandle", + state: this.state + } + }, + [Inspectable.NodeInspectSymbol](this: FiberHandle) { + return this.toJSON() + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +const unsafeMake = ( + deferred: Deferred.Deferred +): FiberHandle => { + const self = Object.create(Proto) + self.state = { _tag: "Open", fiber: undefined } + self.deferred = deferred + return self +} + +/** + * A FiberHandle can be used to store a single fiber. + * When the associated Scope is closed, the contained fiber will be interrupted. + * + * You can add a fiber to the handle using `FiberHandle.run`, and the fiber will + * be automatically removed from the FiberHandle when it completes. + * + * @example + * ```ts + * import { Effect, FiberHandle } from "effect" + * + * Effect.gen(function*() { + * const handle = yield* FiberHandle.make() + * + * // run some effects + * yield* FiberHandle.run(handle, Effect.never) + * // this will interrupt the previous fiber + * yield* FiberHandle.run(handle, Effect.never) + * + * yield* Effect.sleep(1000) + * }).pipe( + * Effect.scoped // The fiber will be interrupted when the scope is closed + * ) + * ``` + * + * @since 2.0.0 + * @categories constructors + */ +export const make = (): Effect.Effect, never, Scope.Scope> => + Effect.acquireRelease( + Effect.map(Deferred.make(), (deferred) => unsafeMake(deferred)), + (handle) => + Effect.withFiberRuntime((parent) => { + const state = handle.state + if (state._tag === "Closed") return Effect.void + handle.state = { _tag: "Closed" } + return state.fiber ? + Effect.intoDeferred( + Effect.asVoid(Fiber.interruptAs(state.fiber, FiberId.combine(parent.id(), internalFiberId))), + handle.deferred + ) : + Deferred.done(handle.deferred, Exit.void) + }) + ) + +/** + * Create an Effect run function that is backed by a FiberHandle. + * + * @since 2.0.0 + * @categories constructors + */ +export const makeRuntime = (): Effect.Effect< + ( + effect: Effect.Effect, + options?: + | Runtime.RunForkOptions & { + readonly onlyIfMissing?: boolean | undefined + } + | undefined + ) => Fiber.RuntimeFiber, + never, + Scope.Scope | R +> => + Effect.flatMap( + make(), + (self) => runtime(self)() + ) + +/** + * Create an Effect run function that is backed by a FiberHandle. + * + * @since 3.13.0 + * @categories constructors + */ +export const makeRuntimePromise = (): Effect.Effect< + ( + effect: Effect.Effect, + options?: Runtime.RunForkOptions | undefined + ) => Promise, + never, + Scope.Scope | R +> => + Effect.flatMap( + make(), + (self) => runtimePromise(self)() + ) + +const internalFiberIdId = -1 +const internalFiberId = FiberId.make(internalFiberIdId, 0) +const isInternalInterruption = Cause.reduceWithContext(undefined, { + emptyCase: constFalse, + failCase: constFalse, + dieCase: constFalse, + interruptCase: (_, fiberId) => HashSet.has(FiberId.ids(fiberId), internalFiberIdId), + sequentialCase: (_, left, right) => left || right, + parallelCase: (_, left, right) => left || right +}) + +/** + * Set the fiber in a FiberHandle. When the fiber completes, it will be removed from the FiberHandle. + * If a fiber is already running, it will be interrupted unless `options.onlyIfMissing` is set. + * + * @since 2.0.0 + * @categories combinators + */ +export const unsafeSet: { + /** + * Set the fiber in a FiberHandle. When the fiber completes, it will be removed from the FiberHandle. + * If a fiber is already running, it will be interrupted unless `options.onlyIfMissing` is set. + * + * @since 2.0.0 + * @categories combinators + */ + ( + fiber: Fiber.RuntimeFiber, + options?: { + readonly interruptAs?: FiberId.FiberId | undefined + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } + ): (self: FiberHandle) => void + /** + * Set the fiber in a FiberHandle. When the fiber completes, it will be removed from the FiberHandle. + * If a fiber is already running, it will be interrupted unless `options.onlyIfMissing` is set. + * + * @since 2.0.0 + * @categories combinators + */ + ( + self: FiberHandle, + fiber: Fiber.RuntimeFiber, + options?: { + readonly interruptAs?: FiberId.FiberId | undefined + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } + ): void +} = dual((args) => isFiberHandle(args[0]), ( + self: FiberHandle, + fiber: Fiber.RuntimeFiber, + options?: { + readonly interruptAs?: FiberId.FiberId | undefined + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } +): void => { + if (self.state._tag === "Closed") { + fiber.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId)) + return + } else if (self.state.fiber !== undefined) { + if (options?.onlyIfMissing === true) { + fiber.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId)) + return + } else if (self.state.fiber === fiber) { + return + } + self.state.fiber.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId)) + self.state.fiber = undefined + } + + self.state.fiber = fiber + fiber.addObserver((exit) => { + if (self.state._tag === "Open" && fiber === self.state.fiber) { + self.state.fiber = undefined + } + if ( + Exit.isFailure(exit) && + ( + options?.propagateInterruption === true ? + !isInternalInterruption(exit.cause) : + !Cause.isInterruptedOnly(exit.cause) + ) + ) { + Deferred.unsafeDone(self.deferred, exit as any) + } + }) +}) + +/** + * Set the fiber in the FiberHandle. When the fiber completes, it will be removed from the FiberHandle. + * If a fiber already exists in the FiberHandle, it will be interrupted unless `options.onlyIfMissing` is set. + * + * @since 2.0.0 + * @categories combinators + */ +export const set: { + /** + * Set the fiber in the FiberHandle. When the fiber completes, it will be removed from the FiberHandle. + * If a fiber already exists in the FiberHandle, it will be interrupted unless `options.onlyIfMissing` is set. + * + * @since 2.0.0 + * @categories combinators + */ + ( + fiber: Fiber.RuntimeFiber, + options?: { + readonly onlyIfMissing?: boolean + readonly propagateInterruption?: boolean | undefined + } + ): (self: FiberHandle) => Effect.Effect + /** + * Set the fiber in the FiberHandle. When the fiber completes, it will be removed from the FiberHandle. + * If a fiber already exists in the FiberHandle, it will be interrupted unless `options.onlyIfMissing` is set. + * + * @since 2.0.0 + * @categories combinators + */ + ( + self: FiberHandle, + fiber: Fiber.RuntimeFiber, + options?: { + readonly onlyIfMissing?: boolean + readonly propagateInterruption?: boolean | undefined + } + ): Effect.Effect +} = dual((args) => isFiberHandle(args[0]), ( + self: FiberHandle, + fiber: Fiber.RuntimeFiber, + options?: { + readonly onlyIfMissing?: boolean + readonly propagateInterruption?: boolean | undefined + } +): Effect.Effect => + Effect.fiberIdWith( + (fiberId) => + Effect.sync(() => + unsafeSet(self, fiber, { + interruptAs: fiberId, + onlyIfMissing: options?.onlyIfMissing, + propagateInterruption: options?.propagateInterruption + }) + ) + )) + +/** + * Retrieve the fiber from the FiberHandle. + * + * @since 2.0.0 + * @categories combinators + */ +export const unsafeGet = (self: FiberHandle): Option.Option> => + self.state._tag === "Closed" ? Option.none() : Option.fromNullable(self.state.fiber) + +/** + * Retrieve the fiber from the FiberHandle. + * + * @since 2.0.0 + * @categories combinators + */ +export const get = (self: FiberHandle): Effect.Effect, NoSuchElementException> => + Effect.suspend(() => unsafeGet(self)) + +/** + * @since 2.0.0 + * @categories combinators + */ +export const clear = (self: FiberHandle): Effect.Effect => + Effect.uninterruptibleMask((restore) => + Effect.withFiberRuntime((fiber) => { + if (self.state._tag === "Closed" || self.state.fiber === undefined) { + return Effect.void + } + return Effect.zipRight( + restore(Fiber.interruptAs(self.state.fiber, FiberId.combine(fiber.id(), internalFiberId))), + Effect.sync(() => { + if (self.state._tag === "Open") { + self.state.fiber = undefined + } + }) + ) + }) + ) + +const constInterruptedFiber = (function() { + let fiber: Fiber.RuntimeFiber | undefined = undefined + return () => { + if (fiber === undefined) { + fiber = Effect.runFork(Effect.interrupt) + } + return fiber + } +})() + +/** + * Run an Effect and add the forked fiber to the FiberHandle. + * When the fiber completes, it will be removed from the FiberHandle. + * + * @since 2.0.0 + * @categories combinators + */ +export const run: { + /** + * Run an Effect and add the forked fiber to the FiberHandle. + * When the fiber completes, it will be removed from the FiberHandle. + * + * @since 2.0.0 + * @categories combinators + */ + ( + self: FiberHandle, + options?: { + readonly onlyIfMissing?: boolean + readonly propagateInterruption?: boolean | undefined + } + ): ( + effect: Effect.Effect + ) => Effect.Effect, never, R> + /** + * Run an Effect and add the forked fiber to the FiberHandle. + * When the fiber completes, it will be removed from the FiberHandle. + * + * @since 2.0.0 + * @categories combinators + */ + ( + self: FiberHandle, + effect: Effect.Effect, + options?: { + readonly onlyIfMissing?: boolean + readonly propagateInterruption?: boolean | undefined + } + ): Effect.Effect, never, R> +} = function() { + const self = arguments[0] as FiberHandle + if (Effect.isEffect(arguments[1])) { + return runImpl(self, arguments[1], arguments[2]) as any + } + const options = arguments[1] + return (effect: Effect.Effect) => runImpl(self, effect, options) +} + +const runImpl = ( + self: FiberHandle, + effect: Effect.Effect, + options?: { + readonly onlyIfMissing?: boolean + readonly propagateInterruption?: boolean | undefined + } +): Effect.Effect, never, R> => + Effect.withFiberRuntime((parent) => { + if (self.state._tag === "Closed") { + return Effect.interrupt + } else if (self.state.fiber !== undefined && options?.onlyIfMissing === true) { + return Effect.sync(constInterruptedFiber) + } + const runtime = Runtime.make({ + context: parent.currentContext as any, + fiberRefs: parent.getFiberRefs(), + runtimeFlags: Runtime.defaultRuntime.runtimeFlags + }) + const fiber = Runtime.runFork(runtime)(effect) + unsafeSet(self, fiber, { ...options, interruptAs: parent.id() }) + return Effect.succeed(fiber) + }) + +/** + * Capture a Runtime and use it to fork Effect's, adding the forked fibers to the FiberHandle. + * + * @example + * ```ts + * import { Context, Effect, FiberHandle } from "effect" + * + * interface Users { + * readonly _: unique symbol + * } + * const Users = Context.GenericTag> + * }>("Users") + * + * Effect.gen(function*() { + * const handle = yield* FiberHandle.make() + * const run = yield* FiberHandle.runtime(handle)() + * + * // run an effect and set the fiber in the handle + * run(Effect.andThen(Users, _ => _.getAll)) + * + * // this will interrupt the previous fiber + * run(Effect.andThen(Users, _ => _.getAll)) + * }).pipe( + * Effect.scoped // The fiber will be interrupted when the scope is closed + * ) + * ``` + * + * @since 2.0.0 + * @categories combinators + */ +export const runtime: ( + self: FiberHandle +) => () => Effect.Effect< + ( + effect: Effect.Effect, + options?: + | Runtime.RunForkOptions & { + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } + | undefined + ) => Fiber.RuntimeFiber, + never, + R +> = (self: FiberHandle) => () => + Effect.map( + Effect.runtime(), + (runtime) => { + const runFork = Runtime.runFork(runtime) + return ( + effect: Effect.Effect, + options?: + | Runtime.RunForkOptions & { + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } + | undefined + ) => { + if (self.state._tag === "Closed") { + return constInterruptedFiber() + } else if (self.state.fiber !== undefined && options?.onlyIfMissing === true) { + return constInterruptedFiber() + } + const fiber = runFork(effect, options) + unsafeSet(self, fiber, options) + return fiber + } + } + ) + +/** + * Capture a Runtime and use it to fork Effect's, adding the forked fibers to the FiberHandle. + * + * The returned run function will return Promise's that will resolve when the + * fiber completes. + * + * @since 3.13.0 + * @categories combinators + */ +export const runtimePromise = (self: FiberHandle): () => Effect.Effect< + ( + effect: Effect.Effect, + options?: + | Runtime.RunForkOptions & { readonly propagateInterruption?: boolean | undefined } + | undefined + ) => Promise, + never, + R +> => +() => + Effect.map( + runtime(self)(), + (runFork) => + ( + effect: Effect.Effect, + options?: + | Runtime.RunForkOptions & { readonly propagateInterruption?: boolean | undefined } + | undefined + ): Promise => + new Promise((resolve, reject) => + runFork(effect, options).addObserver((exit) => { + if (Exit.isSuccess(exit)) { + resolve(exit.value) + } else { + reject(Cause.squash(exit.cause)) + } + }) + ) + ) + +/** + * If any of the Fiber's in the handle terminate with a failure, + * the returned Effect will terminate with the first failure that occurred. + * + * @since 2.0.0 + * @categories combinators + * @example + * ```ts + * import { Effect, FiberHandle } from "effect"; + * + * Effect.gen(function* (_) { + * const handle = yield* _(FiberHandle.make()); + * yield* _(FiberHandle.set(handle, Effect.runFork(Effect.fail("error")))); + * + * // parent fiber will fail with "error" + * yield* _(FiberHandle.join(handle)); + * }); + * ``` + */ +export const join = (self: FiberHandle): Effect.Effect => + Deferred.await(self.deferred as Deferred.Deferred) + +/** + * Wait for the fiber in the FiberHandle to complete. + * + * @since 3.13.0 + * @categories combinators + */ +export const awaitEmpty = (self: FiberHandle): Effect.Effect => + Effect.suspend(() => { + if (self.state._tag === "Closed" || self.state.fiber === undefined) { + return Effect.void + } + return Fiber.await(self.state.fiber) + }) diff --git a/backend/node_modules/effect/src/FiberId.ts b/backend/node_modules/effect/src/FiberId.ts new file mode 100644 index 0000000000000000000000000000000000000000..9b9d1c3fdb10118e8fb1c72438b495bd86e43cf5 --- /dev/null +++ b/backend/node_modules/effect/src/FiberId.ts @@ -0,0 +1,219 @@ +/** + * @since 2.0.0 + */ +import type * as Equal from "./Equal.js" +import type * as HashSet from "./HashSet.js" +import type { Inspectable } from "./Inspectable.js" +import * as internal from "./internal/fiberId.js" +import type * as Option from "./Option.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const FiberIdTypeId: unique symbol = internal.FiberIdTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type FiberIdTypeId = typeof FiberIdTypeId + +/** + * @since 2.0.0 + * @category models + */ +export type Single = None | Runtime + +/** + * @since 2.0.0 + * @category models + */ +export type FiberId = Single | Composite + +/** + * @since 2.0.0 + * @category models + */ +export interface None extends Equal.Equal, Inspectable { + readonly [FiberIdTypeId]: FiberIdTypeId + readonly _tag: "None" + readonly id: -1 + readonly startTimeMillis: -1 +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Runtime extends Equal.Equal, Inspectable { + readonly [FiberIdTypeId]: FiberIdTypeId + readonly _tag: "Runtime" + readonly id: number + readonly startTimeMillis: number +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Composite extends Equal.Equal, Inspectable { + readonly [FiberIdTypeId]: FiberIdTypeId + readonly _tag: "Composite" + readonly left: FiberId + readonly right: FiberId +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const none: None = internal.none + +/** + * @since 2.0.0 + * @category constructors + */ +export const runtime: (id: number, startTimeMillis: number) => Runtime = internal.runtime + +/** + * @since 2.0.0 + * @category constructors + */ +export const composite: (left: FiberId, right: FiberId) => Composite = internal.composite + +/** + * Returns `true` if the specified unknown value is a `FiberId`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isFiberId: (self: unknown) => self is FiberId = internal.isFiberId + +/** + * Returns `true` if the `FiberId` is a `None`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isNone: (self: FiberId) => self is None = internal.isNone + +/** + * Returns `true` if the `FiberId` is a `Runtime`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isRuntime: (self: FiberId) => self is Runtime = internal.isRuntime + +/** + * Returns `true` if the `FiberId` is a `Composite`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isComposite: (self: FiberId) => self is Composite = internal.isComposite + +/** + * Combine two `FiberId`s. + * + * @since 2.0.0 + * @category constructors + */ +export const combine: { + /** + * Combine two `FiberId`s. + * + * @since 2.0.0 + * @category constructors + */ + (that: FiberId): (self: FiberId) => FiberId + /** + * Combine two `FiberId`s. + * + * @since 2.0.0 + * @category constructors + */ + (self: FiberId, that: FiberId): FiberId +} = internal.combine + +/** + * Combines a set of `FiberId`s into a single `FiberId`. + * + * @since 2.0.0 + * @category constructors + */ +export const combineAll: (fiberIds: HashSet.HashSet) => FiberId = internal.combineAll + +/** + * Returns this `FiberId` if it is not `None`, otherwise returns that `FiberId`. + * + * @since 2.0.0 + * @category utils + */ +export const getOrElse: { + /** + * Returns this `FiberId` if it is not `None`, otherwise returns that `FiberId`. + * + * @since 2.0.0 + * @category utils + */ + (that: FiberId): (self: FiberId) => FiberId + /** + * Returns this `FiberId` if it is not `None`, otherwise returns that `FiberId`. + * + * @since 2.0.0 + * @category utils + */ + (self: FiberId, that: FiberId): FiberId +} = internal.getOrElse + +/** + * Get the set of identifiers for this `FiberId`. + * + * @since 2.0.0 + * @category destructors + */ +export const ids: (self: FiberId) => HashSet.HashSet = internal.ids + +/** + * Creates a new `FiberId`. + * + * @since 2.0.0 + * @category constructors + */ +export const make: (id: number, startTimeSeconds: number) => FiberId = internal.make + +/** + * Creates a string representing the name of the current thread of execution + * represented by the specified `FiberId`. + * + * @since 2.0.0 + * @category destructors + */ +export const threadName: (self: FiberId) => string = internal.threadName + +/** + * Convert a `FiberId` into an `Option`. + * + * @since 2.0.0 + * @category destructors + */ +export const toOption: (self: FiberId) => Option.Option = internal.toOption + +/** + * Convert a `FiberId` into a `HashSet`. + * + * @since 2.0.0 + * @category destructors + */ +export const toSet: (self: FiberId) => HashSet.HashSet = internal.toSet + +/** + * Unsafely creates a new `FiberId`. + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeMake: (_: void) => Runtime = internal.unsafeMake diff --git a/backend/node_modules/effect/src/FiberMap.ts b/backend/node_modules/effect/src/FiberMap.ts new file mode 100644 index 0000000000000000000000000000000000000000..aeb33c83d9aa64077c363d9f2cc8db5c6fbb762e --- /dev/null +++ b/backend/node_modules/effect/src/FiberMap.ts @@ -0,0 +1,779 @@ +/** + * @since 2.0.0 + */ +import type { NoSuchElementException } from "./Cause.js" +import * as Cause from "./Cause.js" +import * as Deferred from "./Deferred.js" +import * as Effect from "./Effect.js" +import * as Exit from "./Exit.js" +import * as Fiber from "./Fiber.js" +import * as FiberId from "./FiberId.js" +import { constFalse, constVoid, dual } from "./Function.js" +import * as HashSet from "./HashSet.js" +import * as Inspectable from "./Inspectable.js" +import * as Iterable from "./Iterable.js" +import * as MutableHashMap from "./MutableHashMap.js" +import * as Option from "./Option.js" +import { type Pipeable, pipeArguments } from "./Pipeable.js" +import * as Predicate from "./Predicate.js" +import * as Runtime from "./Runtime.js" +import type * as Scope from "./Scope.js" + +/** + * @since 2.0.0 + * @categories type ids + */ +export const TypeId: unique symbol = Symbol.for("effect/FiberMap") + +/** + * @since 2.0.0 + * @categories type ids + */ +export type TypeId = typeof TypeId + +/** + * @since 2.0.0 + * @categories models + */ +export interface FiberMap + extends Pipeable, Inspectable.Inspectable, Iterable<[K, Fiber.RuntimeFiber]> +{ + readonly [TypeId]: TypeId + readonly deferred: Deferred.Deferred + /** @internal */ + state: { + readonly _tag: "Open" + readonly backing: MutableHashMap.MutableHashMap> + } | { + readonly _tag: "Closed" + } +} + +/** + * @since 2.0.0 + * @categories refinements + */ +export const isFiberMap = (u: unknown): u is FiberMap => Predicate.hasProperty(u, TypeId) + +const Proto = { + [TypeId]: TypeId, + [Symbol.iterator](this: FiberMap) { + if (this.state._tag === "Closed") { + return Iterable.empty() + } + return this.state.backing[Symbol.iterator]() + }, + toString(this: FiberMap) { + return Inspectable.format(this.toJSON()) + }, + toJSON(this: FiberMap) { + return { + _id: "FiberMap", + state: this.state + } + }, + [Inspectable.NodeInspectSymbol](this: FiberMap) { + return this.toJSON() + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +const unsafeMake = ( + backing: MutableHashMap.MutableHashMap>, + deferred: Deferred.Deferred +): FiberMap => { + const self = Object.create(Proto) + self.state = { _tag: "Open", backing } + self.deferred = deferred + return self +} + +/** + * A FiberMap can be used to store a collection of fibers, indexed by some key. + * When the associated Scope is closed, all fibers in the map will be interrupted. + * + * You can add fibers to the map using `FiberMap.set` or `FiberMap.run`, and the fibers will + * be automatically removed from the FiberMap when they complete. + * + * @example + * ```ts + * import { Effect, FiberMap } from "effect" + * + * Effect.gen(function*() { + * const map = yield* FiberMap.make() + * + * // run some effects and add the fibers to the map + * yield* FiberMap.run(map, "fiber a", Effect.never) + * yield* FiberMap.run(map, "fiber b", Effect.never) + * + * yield* Effect.sleep(1000) + * }).pipe( + * Effect.scoped // The fibers will be interrupted when the scope is closed + * ) + * ``` + * + * @since 2.0.0 + * @categories constructors + */ +export const make = (): Effect.Effect, never, Scope.Scope> => + Effect.acquireRelease( + Effect.map(Deferred.make(), (deferred) => + unsafeMake( + MutableHashMap.empty(), + deferred + )), + (map) => + Effect.withFiberRuntime((parent) => { + const state = map.state + if (state._tag === "Closed") return Effect.void + map.state = { _tag: "Closed" } + return Fiber.interruptAllAs( + Iterable.map(state.backing, ([, fiber]) => fiber), + FiberId.combine(parent.id(), internalFiberId) + ).pipe( + Effect.intoDeferred(map.deferred) + ) + }) + ) + +/** + * Create an Effect run function that is backed by a FiberMap. + * + * @since 2.0.0 + * @categories constructors + */ +export const makeRuntime = (): Effect.Effect< + ( + key: K, + effect: Effect.Effect, + options?: + | Runtime.RunForkOptions & { + readonly onlyIfMissing?: boolean | undefined + } + | undefined + ) => Fiber.RuntimeFiber, + never, + Scope.Scope | R +> => + Effect.flatMap( + make(), + (self) => runtime(self)() + ) + +/** + * Create an Effect run function that is backed by a FiberMap. + * + * @since 3.13.0 + * @categories constructors + */ +export const makeRuntimePromise = (): Effect.Effect< + ( + key: K, + effect: Effect.Effect, + options?: + | Runtime.RunForkOptions & { + readonly onlyIfMissing?: boolean | undefined + } + | undefined + ) => Promise, + never, + Scope.Scope | R +> => + Effect.flatMap( + make(), + (self) => runtimePromise(self)() + ) + +const internalFiberIdId = -1 +const internalFiberId = FiberId.make(internalFiberIdId, 0) +const isInternalInterruption = Cause.reduceWithContext(undefined, { + emptyCase: constFalse, + failCase: constFalse, + dieCase: constFalse, + interruptCase: (_, fiberId) => HashSet.has(FiberId.ids(fiberId), internalFiberIdId), + sequentialCase: (_, left, right) => left || right, + parallelCase: (_, left, right) => left || right +}) + +/** + * Add a fiber to the FiberMap. When the fiber completes, it will be removed from the FiberMap. + * If the key already exists in the FiberMap, the previous fiber will be interrupted. + * + * @since 2.0.0 + * @categories combinators + */ +export const unsafeSet: { + /** + * Add a fiber to the FiberMap. When the fiber completes, it will be removed from the FiberMap. + * If the key already exists in the FiberMap, the previous fiber will be interrupted. + * + * @since 2.0.0 + * @categories combinators + */ + ( + key: K, + fiber: Fiber.RuntimeFiber, + options?: { + readonly interruptAs?: FiberId.FiberId | undefined + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } | undefined + ): (self: FiberMap) => void + /** + * Add a fiber to the FiberMap. When the fiber completes, it will be removed from the FiberMap. + * If the key already exists in the FiberMap, the previous fiber will be interrupted. + * + * @since 2.0.0 + * @categories combinators + */ + ( + self: FiberMap, + key: K, + fiber: Fiber.RuntimeFiber, + options?: { + readonly interruptAs?: FiberId.FiberId | undefined + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } | undefined + ): void +} = dual((args) => isFiberMap(args[0]), ( + self: FiberMap, + key: K, + fiber: Fiber.RuntimeFiber, + options?: { + readonly interruptAs?: FiberId.FiberId | undefined + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } | undefined +): void => { + if (self.state._tag === "Closed") { + fiber.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId)) + return + } + + const previous = MutableHashMap.get(self.state.backing, key) + if (previous._tag === "Some") { + if (options?.onlyIfMissing === true) { + fiber.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId)) + return + } else if (previous.value === fiber) { + return + } + previous.value.unsafeInterruptAsFork(FiberId.combine(options?.interruptAs ?? FiberId.none, internalFiberId)) + } + + MutableHashMap.set(self.state.backing, key, fiber) + fiber.addObserver((exit) => { + if (self.state._tag === "Closed") { + return + } + const current = MutableHashMap.get(self.state.backing, key) + if (Option.isSome(current) && fiber === current.value) { + MutableHashMap.remove(self.state.backing, key) + } + if ( + Exit.isFailure(exit) && + ( + options?.propagateInterruption === true ? + !isInternalInterruption(exit.cause) : + !Cause.isInterruptedOnly(exit.cause) + ) + ) { + Deferred.unsafeDone(self.deferred, exit as any) + } + }) +}) + +/** + * Add a fiber to the FiberMap. When the fiber completes, it will be removed from the FiberMap. + * If the key already exists in the FiberMap, the previous fiber will be interrupted. + * + * @since 2.0.0 + * @categories combinators + */ +export const set: { + /** + * Add a fiber to the FiberMap. When the fiber completes, it will be removed from the FiberMap. + * If the key already exists in the FiberMap, the previous fiber will be interrupted. + * + * @since 2.0.0 + * @categories combinators + */ + ( + key: K, + fiber: Fiber.RuntimeFiber, + options?: { + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } | undefined + ): (self: FiberMap) => Effect.Effect + /** + * Add a fiber to the FiberMap. When the fiber completes, it will be removed from the FiberMap. + * If the key already exists in the FiberMap, the previous fiber will be interrupted. + * + * @since 2.0.0 + * @categories combinators + */ + ( + self: FiberMap, + key: K, + fiber: Fiber.RuntimeFiber, + options?: { + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } | undefined + ): Effect.Effect +} = dual((args) => isFiberMap(args[0]), ( + self: FiberMap, + key: K, + fiber: Fiber.RuntimeFiber, + options?: { + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } | undefined +): Effect.Effect => + Effect.fiberIdWith( + (fiberId) => + Effect.sync(() => + unsafeSet(self, key, fiber, { + ...options, + interruptAs: fiberId + }) + ) + )) + +/** + * Retrieve a fiber from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ +export const unsafeGet: { + /** + * Retrieve a fiber from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + (key: K): (self: FiberMap) => Option.Option> + /** + * Retrieve a fiber from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + (self: FiberMap, key: K): Option.Option> +} = dual< + /** + * Retrieve a fiber from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + (key: K) => (self: FiberMap) => Option.Option>, + /** + * Retrieve a fiber from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + (self: FiberMap, key: K) => Option.Option> +>(2, (self, key) => self.state._tag === "Closed" ? Option.none() : MutableHashMap.get(self.state.backing, key)) + +/** + * Retrieve a fiber from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ +export const get: { + /** + * Retrieve a fiber from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + (key: K): (self: FiberMap) => Effect.Effect, NoSuchElementException> + /** + * Retrieve a fiber from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + (self: FiberMap, key: K): Effect.Effect, NoSuchElementException> +} = dual< + /** + * Retrieve a fiber from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + (key: K) => (self: FiberMap) => Effect.Effect, NoSuchElementException>, + /** + * Retrieve a fiber from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + (self: FiberMap, key: K) => Effect.Effect, NoSuchElementException> +>(2, (self, key) => Effect.suspend(() => unsafeGet(self, key))) + +/** + * Check if a key exists in the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ +export const unsafeHas: { + /** + * Check if a key exists in the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + (key: K): (self: FiberMap) => boolean + /** + * Check if a key exists in the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + (self: FiberMap, key: K): boolean +} = dual( + 2, + (self: FiberMap, key: K): boolean => + self.state._tag === "Closed" ? false : MutableHashMap.has(self.state.backing, key) +) + +/** + * Check if a key exists in the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ +export const has: { + /** + * Check if a key exists in the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + (key: K): (self: FiberMap) => Effect.Effect + /** + * Check if a key exists in the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + (self: FiberMap, key: K): Effect.Effect +} = dual( + 2, + (self: FiberMap, key: K): Effect.Effect => Effect.sync(() => unsafeHas(self, key)) +) + +/** + * Remove a fiber from the FiberMap, interrupting it if it exists. + * + * @since 2.0.0 + * @categories combinators + */ +export const remove: { + /** + * Remove a fiber from the FiberMap, interrupting it if it exists. + * + * @since 2.0.0 + * @categories combinators + */ + (key: K): (self: FiberMap) => Effect.Effect + /** + * Remove a fiber from the FiberMap, interrupting it if it exists. + * + * @since 2.0.0 + * @categories combinators + */ + (self: FiberMap, key: K): Effect.Effect +} = dual< + /** + * Remove a fiber from the FiberMap, interrupting it if it exists. + * + * @since 2.0.0 + * @categories combinators + */ + (key: K) => (self: FiberMap) => Effect.Effect, + /** + * Remove a fiber from the FiberMap, interrupting it if it exists. + * + * @since 2.0.0 + * @categories combinators + */ + (self: FiberMap, key: K) => Effect.Effect +>(2, (self, key) => + Effect.withFiberRuntime((removeFiber) => { + if (self.state._tag === "Closed") { + return Effect.void + } + const fiber = MutableHashMap.get(self.state.backing, key) + if (fiber._tag === "None") { + return Effect.void + } + // will be removed by the observer + return Fiber.interruptAs(fiber.value, FiberId.combine(removeFiber.id(), internalFiberId)) + })) + +/** + * @since 2.0.0 + * @categories combinators + */ +export const clear = (self: FiberMap): Effect.Effect => + Effect.withFiberRuntime((clearFiber) => { + if (self.state._tag === "Closed") { + return Effect.void + } + + return Effect.forEach(self.state.backing, ([, fiber]) => + // will be removed by the observer + Fiber.interruptAs(fiber, FiberId.combine(clearFiber.id(), internalFiberId))) + }) + +const constInterruptedFiber = (function() { + let fiber: Fiber.RuntimeFiber | undefined = undefined + return () => { + if (fiber === undefined) { + fiber = Effect.runFork(Effect.interrupt) + } + return fiber + } +})() + +/** + * Run an Effect and add the forked fiber to the FiberMap. + * When the fiber completes, it will be removed from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ +export const run: { + /** + * Run an Effect and add the forked fiber to the FiberMap. + * When the fiber completes, it will be removed from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + ( + self: FiberMap, + key: K, + options?: { + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } | undefined + ): ( + effect: Effect.Effect + ) => Effect.Effect, never, R> + /** + * Run an Effect and add the forked fiber to the FiberMap. + * When the fiber completes, it will be removed from the FiberMap. + * + * @since 2.0.0 + * @categories combinators + */ + ( + self: FiberMap, + key: K, + effect: Effect.Effect, + options?: { + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } | undefined + ): Effect.Effect, never, R> +} = function() { + const self = arguments[0] + if (Effect.isEffect(arguments[2])) { + return runImpl(self, arguments[1], arguments[2], arguments[3]) as any + } + const key = arguments[1] + const options = arguments[2] + return (effect: Effect.Effect) => runImpl(self, key, effect, options) +} + +const runImpl = ( + self: FiberMap, + key: K, + effect: Effect.Effect, + options?: { + readonly onlyIfMissing?: boolean + readonly propagateInterruption?: boolean | undefined + } +) => + Effect.withFiberRuntime((parent) => { + if (self.state._tag === "Closed") { + return Effect.interrupt + } else if (options?.onlyIfMissing === true && unsafeHas(self, key)) { + return Effect.sync(constInterruptedFiber) + } + const runtime = Runtime.make({ + context: parent.currentContext as any, + fiberRefs: parent.getFiberRefs(), + runtimeFlags: Runtime.defaultRuntime.runtimeFlags + }) + const fiber = Runtime.runFork(runtime)(effect) + unsafeSet(self, key, fiber, { ...options, interruptAs: parent.id() }) + return Effect.succeed(fiber) + }) + +/** + * Capture a Runtime and use it to fork Effect's, adding the forked fibers to the FiberMap. + * + * @example + * ```ts + * import { Context, Effect, FiberMap } from "effect" + * + * interface Users { + * readonly _: unique symbol + * } + * const Users = Context.GenericTag> + * }>("Users") + * + * Effect.gen(function*() { + * const map = yield* FiberMap.make() + * const run = yield* FiberMap.runtime(map)() + * + * // run some effects and add the fibers to the map + * run("effect-a", Effect.andThen(Users, _ => _.getAll)) + * run("effect-b", Effect.andThen(Users, _ => _.getAll)) + * }).pipe( + * Effect.scoped // The fibers will be interrupted when the scope is closed + * ) + * ``` + * + * @since 2.0.0 + * @categories combinators + */ +export const runtime: ( + self: FiberMap +) => () => Effect.Effect< + ( + key: K, + effect: Effect.Effect, + options?: + | Runtime.RunForkOptions & { + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } + | undefined + ) => Fiber.RuntimeFiber, + never, + R +> = (self: FiberMap) => () => + Effect.map( + Effect.runtime(), + (runtime) => { + const runFork = Runtime.runFork(runtime) + return ( + key: K, + effect: Effect.Effect, + options?: + | Runtime.RunForkOptions & { + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } + | undefined + ) => { + if (self.state._tag === "Closed") { + return constInterruptedFiber() + } else if (options?.onlyIfMissing === true && unsafeHas(self, key)) { + return constInterruptedFiber() + } + const fiber = runFork(effect, options) + unsafeSet(self, key, fiber, options) + return fiber + } + } + ) + +/** + * Capture a Runtime and use it to fork Effect's, adding the forked fibers to the FiberMap. + * + * @since 3.13.0 + * @categories combinators + */ +export const runtimePromise = (self: FiberMap): () => Effect.Effect< + ( + key: K, + effect: Effect.Effect, + options?: + | Runtime.RunForkOptions & { + readonly onlyIfMissing?: boolean | undefined + readonly propagateInterruption?: boolean | undefined + } + | undefined + ) => Promise, + never, + R +> => +() => + Effect.map( + runtime(self)(), + (runFork) => + ( + key: K, + effect: Effect.Effect, + options?: + | Runtime.RunForkOptions & { readonly propagateInterruption?: boolean | undefined } + | undefined + ): Promise => + new Promise((resolve, reject) => + runFork(key, effect, options).addObserver((exit) => { + if (Exit.isSuccess(exit)) { + resolve(exit.value) + } else { + reject(Cause.squash(exit.cause)) + } + }) + ) + ) + +/** + * @since 2.0.0 + * @categories combinators + */ +export const size = (self: FiberMap): Effect.Effect => + Effect.sync(() => self.state._tag === "Closed" ? 0 : MutableHashMap.size(self.state.backing)) + +/** + * Join all fibers in the FiberMap. If any of the Fiber's in the map terminate with a failure, + * the returned Effect will terminate with the first failure that occurred. + * + * @since 2.0.0 + * @categories combinators + * @example + * ```ts + * import { Effect, FiberMap } from "effect"; + * + * Effect.gen(function* (_) { + * const map = yield* _(FiberMap.make()); + * yield* _(FiberMap.set(map, "a", Effect.runFork(Effect.fail("error")))); + * + * // parent fiber will fail with "error" + * yield* _(FiberMap.join(map)); + * }); + * ``` + */ +export const join = (self: FiberMap): Effect.Effect => + Deferred.await(self.deferred as Deferred.Deferred) + +/** + * Wait for the FiberMap to be empty. + * + * @since 3.13.0 + * @categories combinators + */ +export const awaitEmpty = (self: FiberMap): Effect.Effect => + Effect.whileLoop({ + while: () => self.state._tag === "Open" && MutableHashMap.size(self.state.backing) > 0, + body: () => Fiber.await(Iterable.unsafeHead(self)[1]), + step: constVoid + }) diff --git a/backend/node_modules/effect/src/FiberRef.ts b/backend/node_modules/effect/src/FiberRef.ts new file mode 100644 index 0000000000000000000000000000000000000000..b899d215b6d33a185c5bf5e93490bfb9689005d0 --- /dev/null +++ b/backend/node_modules/effect/src/FiberRef.ts @@ -0,0 +1,511 @@ +/** + * @since 2.0.0 + */ +import type * as Cause from "./Cause.js" +import type * as Chunk from "./Chunk.js" +import type * as Context from "./Context.js" +import type * as Differ from "./Differ.js" +import type * as Effect from "./Effect.js" +import type { LazyArg } from "./Function.js" +import type * as HashMap from "./HashMap.js" +import type * as HashSet from "./HashSet.js" +import * as core from "./internal/core.js" +import * as fiberRuntime from "./internal/fiberRuntime.js" +import * as query from "./internal/query.js" +import type * as List from "./List.js" +import type * as Logger from "./Logger.js" +import type * as LogLevel from "./LogLevel.js" +import type * as LogSpan from "./LogSpan.js" +import type * as MetricLabel from "./MetricLabel.js" +import type * as Option from "./Option.js" +import type * as Request from "./Request.js" +import type * as RuntimeFlags from "./RuntimeFlags.js" +import * as Scheduler from "./Scheduler.js" +import type * as Scope from "./Scope.js" +import type * as Supervisor from "./Supervisor.js" +import type * as Tracer from "./Tracer.js" +import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const FiberRefTypeId: unique symbol = core.FiberRefTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type FiberRefTypeId = typeof FiberRefTypeId + +/** + * @since 2.0.0 + * @category model + */ +export interface FiberRef extends Effect.Effect, Variance { + /** @internal */ + readonly initial: A + /** @internal */ + diff(oldValue: A, newValue: A): unknown + /** @internal */ + combine(first: unknown, second: unknown): unknown + /** @internal */ + patch(patch: unknown): (oldValue: A) => A + /** @internal */ + readonly fork: unknown + /** @internal */ + join(oldValue: A, newValue: A): A + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: FiberRefUnify + readonly [Unify.ignoreSymbol]?: FiberRefUnifyIgnore +} + +/** + * @category models + * @since 3.8.0 + */ +export interface FiberRefUnify extends Effect.EffectUnify { + FiberRef?: () => Extract> +} + +/** + * @category models + * @since 3.8.0 + */ +export interface FiberRefUnifyIgnore extends Effect.EffectUnifyIgnore { + Effect?: true +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Variance { + readonly [FiberRefTypeId]: { + readonly _A: Types.Invariant + } +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const make: ( + initial: A, + options?: { + readonly fork?: ((a: A) => A) | undefined + readonly join?: ((left: A, right: A) => A) | undefined + } +) => Effect.Effect, never, Scope.Scope> = fiberRuntime.fiberRefMake + +/** + * @since 2.0.0 + * @category constructors + */ +export const makeWith: (ref: LazyArg>) => Effect.Effect, never, Scope.Scope> = + fiberRuntime.fiberRefMakeWith + +/** + * @since 2.0.0 + * @category constructors + */ +export const makeContext: ( + initial: Context.Context +) => Effect.Effect>, never, Scope.Scope> = fiberRuntime.fiberRefMakeContext + +/** + * @since 2.0.0 + * @category constructors + */ +export const makeRuntimeFlags: ( + initial: RuntimeFlags.RuntimeFlags +) => Effect.Effect, never, Scope.Scope> = fiberRuntime.fiberRefMakeRuntimeFlags + +/** + * @since 2.0.0 + * @category constructors + */ +export const unsafeMake: ( + initial: Value, + options?: { + readonly fork?: ((a: Value) => Value) | undefined + readonly join?: ((left: Value, right: Value) => Value) | undefined + } +) => FiberRef = core.fiberRefUnsafeMake + +/** + * @since 2.0.0 + * @category constructors + */ +export const unsafeMakeHashSet: (initial: HashSet.HashSet) => FiberRef> = + core.fiberRefUnsafeMakeHashSet + +/** + * @since 2.0.0 + * @category constructors + */ +export const unsafeMakeContext: (initial: Context.Context) => FiberRef> = + core.fiberRefUnsafeMakeContext + +/** + * @since 2.0.0 + * @category constructors + */ +export const unsafeMakeSupervisor: (initial: Supervisor.Supervisor) => FiberRef> = + fiberRuntime.fiberRefUnsafeMakeSupervisor + +/** + * @since 2.0.0 + * @category constructors + */ +export const unsafeMakePatch: ( + initial: Value, + options: { + readonly differ: Differ.Differ + readonly fork: Patch + readonly join?: ((oldV: Value, newV: Value) => Value) | undefined + } +) => FiberRef = core.fiberRefUnsafeMakePatch + +/** + * @since 2.0.0 + * @category getters + */ +export const get: (self: FiberRef) => Effect.Effect = core.fiberRefGet + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndSet: { + /** + * @since 2.0.0 + * @category utils + */ + (value: A): (self: FiberRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: FiberRef, value: A): Effect.Effect +} = core.fiberRefGetAndSet + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndUpdate: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => A): (self: FiberRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: FiberRef, f: (a: A) => A): Effect.Effect +} = core.fiberRefGetAndUpdate + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndUpdateSome: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option): (self: FiberRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: FiberRef, pf: (a: A) => Option.Option): Effect.Effect +} = core.fiberRefGetAndUpdateSome + +/** + * @since 2.0.0 + * @category utils + */ +export const getWith: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Effect.Effect): (self: FiberRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: FiberRef, f: (a: A) => Effect.Effect): Effect.Effect +} = core.fiberRefGetWith + +/** + * @since 2.0.0 + * @category utils + */ +export const set: { + /** + * @since 2.0.0 + * @category utils + */ + (value: A): (self: FiberRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: FiberRef, value: A): Effect.Effect +} = core.fiberRefSet + +const _delete: (self: FiberRef) => Effect.Effect = core.fiberRefDelete + +export { + /** + * @since 2.0.0 + * @category utils + */ + _delete as delete +} + +/** + * @since 2.0.0 + * @category utils + */ +export const reset: (self: FiberRef) => Effect.Effect = core.fiberRefReset + +/** + * @since 2.0.0 + * @category utils + */ +export const modify: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => readonly [B, A]): (self: FiberRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: FiberRef, f: (a: A) => readonly [B, A]): Effect.Effect +} = core.fiberRefModify + +/** + * @since 2.0.0 + * @category utils + */ +export const modifySome: ( + self: FiberRef, + def: B, + f: (a: A) => Option.Option +) => Effect.Effect = core.fiberRefModifySome + +/** + * @since 2.0.0 + * @category utils + */ +export const update: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => A): (self: FiberRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: FiberRef, f: (a: A) => A): Effect.Effect +} = core.fiberRefUpdate + +/** + * @since 2.0.0 + * @category utils + */ +export const updateSome: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option): (self: FiberRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: FiberRef, pf: (a: A) => Option.Option): Effect.Effect +} = core.fiberRefUpdateSome + +/** + * @since 2.0.0 + * @category utils + */ +export const updateAndGet: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => A): (self: FiberRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: FiberRef, f: (a: A) => A): Effect.Effect +} = core.fiberRefUpdateAndGet + +/** + * @since 2.0.0 + * @category utils + */ +export const updateSomeAndGet: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option): (self: FiberRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: FiberRef, pf: (a: A) => Option.Option): Effect.Effect +} = core.fiberRefUpdateSomeAndGet + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentConcurrency: FiberRef = core.currentConcurrency + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentRequestBatchingEnabled: FiberRef = core.currentRequestBatching + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentRequestCache: FiberRef = query.currentCache as any + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentRequestCacheEnabled: FiberRef = query.currentCacheEnabled + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentContext: FiberRef> = core.currentContext + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentSchedulingPriority: FiberRef = core.currentSchedulingPriority + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentMaxOpsBeforeYield: FiberRef = core.currentMaxOpsBeforeYield + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const unhandledErrorLogLevel: FiberRef> = core.currentUnhandledErrorLogLevel + +/** + * @since 3.17.0 + * @category fiberRefs + */ +export const versionMismatchErrorLogLevel: FiberRef> = + core.currentVersionMismatchErrorLogLevel + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentLogAnnotations: FiberRef> = core.currentLogAnnotations + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentLoggers: FiberRef>> = fiberRuntime.currentLoggers + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentLogLevel: FiberRef = core.currentLogLevel + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentMinimumLogLevel: FiberRef = fiberRuntime.currentMinimumLogLevel + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentLogSpan: FiberRef> = core.currentLogSpan + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentRuntimeFlags: FiberRef = fiberRuntime.currentRuntimeFlags + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentScheduler: FiberRef = Scheduler.currentScheduler + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentSupervisor: FiberRef> = fiberRuntime.currentSupervisor + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentMetricLabels: FiberRef> = core.currentMetricLabels + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentTracerEnabled: FiberRef = core.currentTracerEnabled + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentTracerTimingEnabled: FiberRef = core.currentTracerTimingEnabled + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentTracerSpanAnnotations: FiberRef> = + core.currentTracerSpanAnnotations + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const currentTracerSpanLinks: FiberRef> = core.currentTracerSpanLinks + +/** + * @since 2.0.0 + * @category fiberRefs + */ +export const interruptedCause: FiberRef> = core.currentInterruptedCause diff --git a/backend/node_modules/effect/src/Graph.ts b/backend/node_modules/effect/src/Graph.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d3253dc68d0673d97d6e019d0dbb0c943e84490 --- /dev/null +++ b/backend/node_modules/effect/src/Graph.ts @@ -0,0 +1,3764 @@ +/** + * @experimental + * @since 3.18.0 + */ + +import * as Data from "./Data.js" +import * as Equal from "./Equal.js" +import { dual } from "./Function.js" +import * as Hash from "./Hash.js" +import type { Inspectable } from "./Inspectable.js" +import { format, NodeInspectSymbol } from "./Inspectable.js" +import * as Option from "./Option.js" +import type { Pipeable } from "./Pipeable.js" +import { pipeArguments } from "./Pipeable.js" +import type { Mutable } from "./Types.js" + +/** + * Unique identifier for Graph instances. + * + * @since 3.18.0 + * @category symbol + */ +export const TypeId: "~effect/Graph" = "~effect/Graph" as const + +/** + * Type identifier for Graph instances. + * + * @since 3.18.0 + * @category symbol + */ +export type TypeId = typeof TypeId + +/** + * Node index for node identification using plain numbers. + * + * @since 3.18.0 + * @category models + */ +export type NodeIndex = number + +/** + * Edge index for edge identification using plain numbers. + * + * @since 3.18.0 + * @category models + */ +export type EdgeIndex = number + +/** + * Edge data containing source, target, and user data. + * + * @since 3.18.0 + * @category models + */ +export class Edge extends Data.Class<{ + readonly source: NodeIndex + readonly target: NodeIndex + readonly data: E +}> {} + +/** + * Graph type for distinguishing directed and undirected graphs. + * + * @since 3.18.0 + * @category models + */ +export type Kind = "directed" | "undirected" + +/** + * Graph prototype interface. + * + * @since 3.18.0 + * @category models + */ +export interface Proto extends Iterable, Equal.Equal, Pipeable, Inspectable { + readonly [TypeId]: TypeId + readonly nodes: Map + readonly edges: Map> + readonly adjacency: Map> + readonly reverseAdjacency: Map> + nextNodeIndex: NodeIndex + nextEdgeIndex: EdgeIndex + isAcyclic: Option.Option +} + +/** + * Immutable graph interface. + * + * @since 3.18.0 + * @category models + */ +export interface Graph extends Proto { + readonly type: T + readonly mutable: false +} + +/** + * Mutable graph interface. + * + * @since 3.18.0 + * @category models + */ +export interface MutableGraph extends Proto { + readonly type: T + readonly mutable: true +} + +/** + * Directed graph type alias. + * + * @since 3.18.0 + * @category models + */ +export type DirectedGraph = Graph + +/** + * Undirected graph type alias. + * + * @since 3.18.0 + * @category models + */ +export type UndirectedGraph = Graph + +/** + * Mutable directed graph type alias. + * + * @since 3.18.0 + * @category models + */ +export type MutableDirectedGraph = MutableGraph + +/** + * Mutable undirected graph type alias. + * + * @since 3.18.0 + * @category models + */ +export type MutableUndirectedGraph = MutableGraph + +// ============================================================================= +// Proto Objects +// ============================================================================= + +/** @internal */ +const ProtoGraph = { + [TypeId]: TypeId, + [Symbol.iterator](this: Graph) { + return this.nodes[Symbol.iterator]() + }, + [NodeInspectSymbol](this: Graph) { + return this.toJSON() + }, + [Equal.symbol](this: Graph, that: Equal.Equal): boolean { + if (isGraph(that)) { + if ( + this.nodes.size !== that.nodes.size || + this.edges.size !== that.edges.size || + this.type !== that.type + ) { + return false + } + // Compare nodes + for (const [nodeIndex, nodeData] of this.nodes) { + if (!that.nodes.has(nodeIndex)) { + return false + } + const otherNodeData = that.nodes.get(nodeIndex)! + if (!Equal.equals(nodeData, otherNodeData)) { + return false + } + } + // Compare edges + for (const [edgeIndex, edgeData] of this.edges) { + if (!that.edges.has(edgeIndex)) { + return false + } + const otherEdge = that.edges.get(edgeIndex)! + if (!Equal.equals(edgeData, otherEdge)) { + return false + } + } + return true + } + return false + }, + [Hash.symbol](this: Graph): number { + let hash = Hash.string("Graph") + hash = hash ^ Hash.string(this.type) + hash = hash ^ Hash.number(this.nodes.size) + hash = hash ^ Hash.number(this.edges.size) + for (const [nodeIndex, nodeData] of this.nodes) { + hash = hash ^ (Hash.hash(nodeIndex) + Hash.hash(nodeData)) + } + for (const [edgeIndex, edgeData] of this.edges) { + hash = hash ^ (Hash.hash(edgeIndex) + Hash.hash(edgeData)) + } + return hash + }, + toJSON(this: Graph) { + return { + _id: "Graph", + nodeCount: this.nodes.size, + edgeCount: this.edges.size, + type: this.type + } + }, + toString(this: Graph) { + return format(this) + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +// ============================================================================= +// Errors +// ============================================================================= + +/** + * Error thrown when a graph operation fails. + * + * @since 3.18.0 + * @category errors + */ +export class GraphError extends Data.TaggedError("GraphError")<{ + readonly message: string +}> {} + +/** @internal */ +const missingNode = (node: number) => new GraphError({ message: `Node ${node} does not exist` }) + +// ============================================================================= +// Constructors +// ============================================================================= + +/** @internal */ +export const isGraph = (u: unknown): u is Graph => typeof u === "object" && u !== null && TypeId in u + +/** + * Creates a directed graph, optionally with initial mutations. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * // Directed graph with initial nodes and edges + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, "A->B") + * Graph.addEdge(mutable, b, c, "B->C") + * }) + * ``` + * + * @since 3.18.0 + * @category constructors + */ +export const directed = (mutate?: (mutable: MutableDirectedGraph) => void): DirectedGraph => { + const graph: Mutable> = Object.create(ProtoGraph) + graph.type = "directed" + graph.nodes = new Map() + graph.edges = new Map() + graph.adjacency = new Map() + graph.reverseAdjacency = new Map() + graph.nextNodeIndex = 0 + graph.nextEdgeIndex = 0 + graph.isAcyclic = Option.some(true) + graph.mutable = false + + if (mutate) { + const mutable = beginMutation(graph as DirectedGraph) + mutate(mutable as MutableDirectedGraph) + return endMutation(mutable) + } + + return graph +} + +/** + * Creates an undirected graph, optionally with initial mutations. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * // Undirected graph with initial nodes and edges + * const graph = Graph.undirected((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, "A-B") + * Graph.addEdge(mutable, b, c, "B-C") + * }) + * ``` + * + * @since 3.18.0 + * @category constructors + */ +export const undirected = (mutate?: (mutable: MutableUndirectedGraph) => void): UndirectedGraph => { + const graph: Mutable> = Object.create(ProtoGraph) + graph.type = "undirected" + graph.nodes = new Map() + graph.edges = new Map() + graph.adjacency = new Map() + graph.reverseAdjacency = new Map() + graph.nextNodeIndex = 0 + graph.nextEdgeIndex = 0 + graph.isAcyclic = Option.some(true) + graph.mutable = false + + if (mutate) { + const mutable = beginMutation(graph) + mutate(mutable as MutableUndirectedGraph) + return endMutation(mutable) + } + + return graph +} + +// ============================================================================= +// Scoped Mutable API +// ============================================================================= + +/** + * Creates a mutable scope for safe graph mutations by copying the data structure. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed() + * const mutable = Graph.beginMutation(graph) + * // Now mutable can be safely modified without affecting original graph + * ``` + * + * @since 3.18.0 + * @category mutations + */ +export const beginMutation = ( + graph: Graph +): MutableGraph => { + // Copy adjacency maps with deep cloned arrays + const adjacency = new Map>() + const reverseAdjacency = new Map>() + + for (const [nodeIndex, edges] of graph.adjacency) { + adjacency.set(nodeIndex, [...edges]) + } + + for (const [nodeIndex, edges] of graph.reverseAdjacency) { + reverseAdjacency.set(nodeIndex, [...edges]) + } + + const mutable: Mutable> = Object.create(ProtoGraph) + mutable.type = graph.type + mutable.nodes = new Map(graph.nodes) + mutable.edges = new Map(graph.edges) + mutable.adjacency = adjacency + mutable.reverseAdjacency = reverseAdjacency + mutable.nextNodeIndex = graph.nextNodeIndex + mutable.nextEdgeIndex = graph.nextEdgeIndex + mutable.isAcyclic = graph.isAcyclic + mutable.mutable = true + + return mutable +} + +/** + * Converts a mutable graph back to an immutable graph, ending the mutation scope. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed() + * const mutable = Graph.beginMutation(graph) + * // ... perform mutations on mutable ... + * const newGraph = Graph.endMutation(mutable) + * ``` + * + * @since 3.18.0 + * @category mutations + */ +export const endMutation = ( + mutable: MutableGraph +): Graph => { + const graph: Mutable> = Object.create(ProtoGraph) + graph.type = mutable.type + graph.nodes = new Map(mutable.nodes) + graph.edges = new Map(mutable.edges) + graph.adjacency = mutable.adjacency + graph.reverseAdjacency = mutable.reverseAdjacency + graph.nextNodeIndex = mutable.nextNodeIndex + graph.nextEdgeIndex = mutable.nextEdgeIndex + graph.isAcyclic = mutable.isAcyclic + graph.mutable = false + + return graph +} + +/** + * Performs scoped mutations on a graph, automatically managing the mutation lifecycle. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed() + * const newGraph = Graph.mutate(graph, (mutable) => { + * // Safe mutations go here + * // mutable gets automatically converted back to immutable + * }) + * ``` + * + * @since 3.18.0 + * @category mutations + */ +export const mutate: { + /** + * Performs scoped mutations on a graph, automatically managing the mutation lifecycle. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed() + * const newGraph = Graph.mutate(graph, (mutable) => { + * // Safe mutations go here + * // mutable gets automatically converted back to immutable + * }) + * ``` + * + * @since 3.18.0 + * @category mutations + */ + (f: (mutable: MutableGraph) => void): (graph: Graph) => Graph + /** + * Performs scoped mutations on a graph, automatically managing the mutation lifecycle. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed() + * const newGraph = Graph.mutate(graph, (mutable) => { + * // Safe mutations go here + * // mutable gets automatically converted back to immutable + * }) + * ``` + * + * @since 3.18.0 + * @category mutations + */ + (graph: Graph, f: (mutable: MutableGraph) => void): Graph +} = dual(2, ( + graph: Graph, + f: (mutable: MutableGraph) => void +): Graph => { + const mutable = beginMutation(graph) + f(mutable) + return endMutation(mutable) +}) + +// ============================================================================= +// Basic Node Operations +// ============================================================================= + +/** + * Adds a new node to a mutable graph and returns its index. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const result = Graph.mutate(Graph.directed(), (mutable) => { + * const nodeA = Graph.addNode(mutable, "Node A") + * const nodeB = Graph.addNode(mutable, "Node B") + * console.log(nodeA) // NodeIndex with value 0 + * console.log(nodeB) // NodeIndex with value 1 + * }) + * ``` + * + * @since 3.18.0 + * @category mutations + */ +export const addNode = ( + mutable: MutableGraph, + data: N +): NodeIndex => { + const nodeIndex = mutable.nextNodeIndex + + // Add node data + mutable.nodes.set(nodeIndex, data) + + // Initialize empty adjacency lists + mutable.adjacency.set(nodeIndex, []) + mutable.reverseAdjacency.set(nodeIndex, []) + + // Update graph allocators + mutable.nextNodeIndex = mutable.nextNodeIndex + 1 + + return nodeIndex +} + +/** + * Gets the data associated with a node index, if it exists. + * + * @example + * ```ts + * import { Graph, Option } from "effect" + * + * const graph = Graph.mutate(Graph.directed(), (mutable) => { + * Graph.addNode(mutable, "Node A") + * }) + * + * const nodeIndex = 0 + * const nodeData = Graph.getNode(graph, nodeIndex) + * + * if (Option.isSome(nodeData)) { + * console.log(nodeData.value) // "Node A" + * } + * ``` + * + * @since 3.18.0 + * @category getters + */ +export const getNode = ( + graph: Graph | MutableGraph, + nodeIndex: NodeIndex +): Option.Option => graph.nodes.has(nodeIndex) ? Option.some(graph.nodes.get(nodeIndex)!) : Option.none() + +/** + * Checks if a node with the given index exists in the graph. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.mutate(Graph.directed(), (mutable) => { + * Graph.addNode(mutable, "Node A") + * }) + * + * const nodeIndex = 0 + * const exists = Graph.hasNode(graph, nodeIndex) + * console.log(exists) // true + * + * const nonExistentIndex = 999 + * const notExists = Graph.hasNode(graph, nonExistentIndex) + * console.log(notExists) // false + * ``` + * + * @since 3.18.0 + * @category getters + */ +export const hasNode = ( + graph: Graph | MutableGraph, + nodeIndex: NodeIndex +): boolean => graph.nodes.has(nodeIndex) + +/** + * Returns the number of nodes in the graph. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const emptyGraph = Graph.directed() + * console.log(Graph.nodeCount(emptyGraph)) // 0 + * + * const graphWithNodes = Graph.mutate(emptyGraph, (mutable) => { + * Graph.addNode(mutable, "Node A") + * Graph.addNode(mutable, "Node B") + * Graph.addNode(mutable, "Node C") + * }) + * + * console.log(Graph.nodeCount(graphWithNodes)) // 3 + * ``` + * + * @since 3.18.0 + * @category getters + */ +export const nodeCount = ( + graph: Graph | MutableGraph +): number => graph.nodes.size + +/** + * Finds the first node that matches the given predicate. + * + * @example + * ```ts + * import { Graph, Option } from "effect" + * + * const graph = Graph.mutate(Graph.directed(), (mutable) => { + * Graph.addNode(mutable, "Node A") + * Graph.addNode(mutable, "Node B") + * Graph.addNode(mutable, "Node C") + * }) + * + * const result = Graph.findNode(graph, (data) => data.startsWith("Node B")) + * console.log(result) // Option.some(1) + * + * const notFound = Graph.findNode(graph, (data) => data === "Node D") + * console.log(notFound) // Option.none() + * ``` + * + * @since 3.18.0 + * @category getters + */ +export const findNode = ( + graph: Graph | MutableGraph, + predicate: (data: N) => boolean +): Option.Option => { + for (const [index, data] of graph.nodes) { + if (predicate(data)) { + return Option.some(index) + } + } + return Option.none() +} + +/** + * Finds all nodes that match the given predicate. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.mutate(Graph.directed(), (mutable) => { + * Graph.addNode(mutable, "Start A") + * Graph.addNode(mutable, "Node B") + * Graph.addNode(mutable, "Start C") + * }) + * + * const result = Graph.findNodes(graph, (data) => data.startsWith("Start")) + * console.log(result) // [0, 2] + * + * const empty = Graph.findNodes(graph, (data) => data === "Not Found") + * console.log(empty) // [] + * ``` + * + * @since 3.18.0 + * @category getters + */ +export const findNodes = ( + graph: Graph | MutableGraph, + predicate: (data: N) => boolean +): Array => { + const results: Array = [] + for (const [index, data] of graph.nodes) { + if (predicate(data)) { + results.push(index) + } + } + return results +} + +/** + * Finds the first edge that matches the given predicate. + * + * @example + * ```ts + * import { Graph, Option } from "effect" + * + * const graph = Graph.mutate(Graph.directed(), (mutable) => { + * const nodeA = Graph.addNode(mutable, "Node A") + * const nodeB = Graph.addNode(mutable, "Node B") + * const nodeC = Graph.addNode(mutable, "Node C") + * Graph.addEdge(mutable, nodeA, nodeB, 10) + * Graph.addEdge(mutable, nodeB, nodeC, 20) + * }) + * + * const result = Graph.findEdge(graph, (data) => data > 15) + * console.log(result) // Option.some(1) + * + * const notFound = Graph.findEdge(graph, (data) => data > 100) + * console.log(notFound) // Option.none() + * ``` + * + * @since 3.18.0 + * @category getters + */ +export const findEdge = ( + graph: Graph | MutableGraph, + predicate: (data: E, source: NodeIndex, target: NodeIndex) => boolean +): Option.Option => { + for (const [edgeIndex, edgeData] of graph.edges) { + if (predicate(edgeData.data, edgeData.source, edgeData.target)) { + return Option.some(edgeIndex) + } + } + return Option.none() +} + +/** + * Finds all edges that match the given predicate. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.mutate(Graph.directed(), (mutable) => { + * const nodeA = Graph.addNode(mutable, "Node A") + * const nodeB = Graph.addNode(mutable, "Node B") + * const nodeC = Graph.addNode(mutable, "Node C") + * Graph.addEdge(mutable, nodeA, nodeB, 10) + * Graph.addEdge(mutable, nodeB, nodeC, 20) + * Graph.addEdge(mutable, nodeC, nodeA, 30) + * }) + * + * const result = Graph.findEdges(graph, (data) => data >= 20) + * console.log(result) // [1, 2] + * + * const empty = Graph.findEdges(graph, (data) => data > 100) + * console.log(empty) // [] + * ``` + * + * @since 3.18.0 + * @category getters + */ +export const findEdges = ( + graph: Graph | MutableGraph, + predicate: (data: E, source: NodeIndex, target: NodeIndex) => boolean +): Array => { + const results: Array = [] + for (const [edgeIndex, edgeData] of graph.edges) { + if (predicate(edgeData.data, edgeData.source, edgeData.target)) { + results.push(edgeIndex) + } + } + return results +} + +/** + * Updates a single node's data by applying a transformation function. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * Graph.addNode(mutable, "Node A") + * Graph.addNode(mutable, "Node B") + * Graph.updateNode(mutable, 0, (data) => data.toUpperCase()) + * }) + * + * const nodeData = Graph.getNode(graph, 0) + * console.log(nodeData) // Option.some("NODE A") + * ``` + * + * @since 3.18.0 + * @category transformations + */ +export const updateNode = ( + mutable: MutableGraph, + index: NodeIndex, + f: (data: N) => N +): void => { + if (!mutable.nodes.has(index)) { + return + } + + const currentData = mutable.nodes.get(index)! + const newData = f(currentData) + mutable.nodes.set(index, newData) +} + +/** + * Updates a single edge's data by applying a transformation function. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const result = Graph.mutate(Graph.directed(), (mutable) => { + * const nodeA = Graph.addNode(mutable, "Node A") + * const nodeB = Graph.addNode(mutable, "Node B") + * const edgeIndex = Graph.addEdge(mutable, nodeA, nodeB, 10) + * Graph.updateEdge(mutable, edgeIndex, (data) => data * 2) + * }) + * + * const edgeData = Graph.getEdge(result, 0) + * console.log(edgeData) // Option.some({ source: 0, target: 1, data: 20 }) + * ``` + * + * @since 3.18.0 + * @category mutations + */ +export const updateEdge = ( + mutable: MutableGraph, + edgeIndex: EdgeIndex, + f: (data: E) => E +): void => { + if (!mutable.edges.has(edgeIndex)) { + return + } + + const currentEdge = mutable.edges.get(edgeIndex)! + const newData = f(currentEdge.data) + mutable.edges.set(edgeIndex, { + ...currentEdge, + data: newData + }) +} + +/** + * Creates a new graph with transformed node data using the provided mapping function. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * Graph.addNode(mutable, "node a") + * Graph.addNode(mutable, "node b") + * Graph.addNode(mutable, "node c") + * Graph.mapNodes(mutable, (data) => data.toUpperCase()) + * }) + * + * const nodeData = Graph.getNode(graph, 0) + * console.log(nodeData) // Option.some("NODE A") + * ``` + * + * @since 3.18.0 + * @category transformations + */ +export const mapNodes = ( + mutable: MutableGraph, + f: (data: N) => N +): void => { + // Transform existing node data in place + for (const [index, data] of mutable.nodes) { + const newData = f(data) + mutable.nodes.set(index, newData) + } +} + +/** + * Transforms all edge data in a mutable graph using the provided mapping function. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, 10) + * Graph.addEdge(mutable, b, c, 20) + * Graph.mapEdges(mutable, (data) => data * 2) + * }) + * + * const edgeData = Graph.getEdge(graph, 0) + * console.log(edgeData) // Option.some({ source: 0, target: 1, data: 20 }) + * ``` + * + * @since 3.18.0 + * @category transformations + */ +export const mapEdges = ( + mutable: MutableGraph, + f: (data: E) => E +): void => { + // Transform existing edge data in place + for (const [index, edgeData] of mutable.edges) { + const newData = f(edgeData.data) + mutable.edges.set(index, { + ...edgeData, + data: newData + }) + } +} + +/** + * Reverses all edge directions in a mutable graph by swapping source and target nodes. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, 1) // A -> B + * Graph.addEdge(mutable, b, c, 2) // B -> C + * Graph.reverse(mutable) // Now B -> A, C -> B + * }) + * + * const edge0 = Graph.getEdge(graph, 0) + * console.log(edge0) // Option.some({ source: 1, target: 0, data: 1 }) - B -> A + * ``` + * + * @since 3.18.0 + * @category transformations + */ +export const reverse = ( + mutable: MutableGraph +): void => { + // Reverse all edges by swapping source and target + for (const [index, edgeData] of mutable.edges) { + mutable.edges.set(index, { + source: edgeData.target, + target: edgeData.source, + data: edgeData.data + }) + } + + // Clear and rebuild adjacency lists with reversed directions + mutable.adjacency.clear() + mutable.reverseAdjacency.clear() + + // Rebuild adjacency lists with reversed directions + for (const [edgeIndex, edgeData] of mutable.edges) { + // Add to forward adjacency (source -> target) + const sourceEdges = mutable.adjacency.get(edgeData.source) || [] + sourceEdges.push(edgeIndex) + mutable.adjacency.set(edgeData.source, sourceEdges) + + // Add to reverse adjacency (target <- source) + const targetEdges = mutable.reverseAdjacency.get(edgeData.target) || [] + targetEdges.push(edgeIndex) + mutable.reverseAdjacency.set(edgeData.target, targetEdges) + } + + // Invalidate cycle flag since edge directions changed + mutable.isAcyclic = Option.none() +} + +/** + * Filters and optionally transforms nodes in a mutable graph using a predicate function. + * Nodes that return Option.none are removed along with all their connected edges. + * + * @example + * ```ts + * import { Graph, Option } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "active") + * const b = Graph.addNode(mutable, "inactive") + * const c = Graph.addNode(mutable, "active") + * Graph.addEdge(mutable, a, b, 1) + * Graph.addEdge(mutable, b, c, 2) + * + * // Keep only "active" nodes and transform to uppercase + * Graph.filterMapNodes(mutable, (data) => + * data === "active" ? Option.some(data.toUpperCase()) : Option.none() + * ) + * }) + * + * console.log(Graph.nodeCount(graph)) // 2 (only "active" nodes remain) + * ``` + * + * @since 3.18.0 + * @category transformations + */ +export const filterMapNodes = ( + mutable: MutableGraph, + f: (data: N) => Option.Option +): void => { + const nodesToRemove: Array = [] + + // First pass: identify nodes to remove and transform data for nodes to keep + for (const [index, data] of mutable.nodes) { + const result = f(data) + if (Option.isSome(result)) { + // Transform node data + mutable.nodes.set(index, result.value) + } else { + // Mark for removal + nodesToRemove.push(index) + } + } + + // Second pass: remove filtered out nodes and their edges + for (const nodeIndex of nodesToRemove) { + removeNode(mutable, nodeIndex) + } +} + +/** + * Filters and optionally transforms edges in a mutable graph using a predicate function. + * Edges that return Option.none are removed from the graph. + * + * @example + * ```ts + * import { Graph, Option } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, 5) + * Graph.addEdge(mutable, b, c, 15) + * Graph.addEdge(mutable, c, a, 25) + * + * // Keep only edges with weight >= 10 and double their weight + * Graph.filterMapEdges(mutable, (data) => + * data >= 10 ? Option.some(data * 2) : Option.none() + * ) + * }) + * + * console.log(Graph.edgeCount(graph)) // 2 (edges with weight 5 removed) + * ``` + * + * @since 3.18.0 + * @category transformations + */ +export const filterMapEdges = ( + mutable: MutableGraph, + f: (data: E) => Option.Option +): void => { + const edgesToRemove: Array = [] + + // First pass: identify edges to remove and transform data for edges to keep + for (const [index, edgeData] of mutable.edges) { + const result = f(edgeData.data) + if (Option.isSome(result)) { + // Transform edge data + mutable.edges.set(index, { + ...edgeData, + data: result.value + }) + } else { + // Mark for removal + edgesToRemove.push(index) + } + } + + // Second pass: remove filtered out edges + for (const edgeIndex of edgesToRemove) { + removeEdge(mutable, edgeIndex) + } +} + +/** + * Filters nodes by removing those that don't match the predicate. + * This function modifies the mutable graph in place. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * Graph.addNode(mutable, "active") + * Graph.addNode(mutable, "inactive") + * Graph.addNode(mutable, "pending") + * Graph.addNode(mutable, "active") + * + * // Keep only "active" nodes + * Graph.filterNodes(mutable, (data) => data === "active") + * }) + * + * console.log(Graph.nodeCount(graph)) // 2 (only "active" nodes remain) + * ``` + * + * @since 3.18.0 + * @category transformations + */ +export const filterNodes = ( + mutable: MutableGraph, + predicate: (data: N) => boolean +): void => { + const nodesToRemove: Array = [] + + // Identify nodes to remove + for (const [index, data] of mutable.nodes) { + if (!predicate(data)) { + nodesToRemove.push(index) + } + } + + // Remove filtered out nodes (this also removes connected edges) + for (const nodeIndex of nodesToRemove) { + removeNode(mutable, nodeIndex) + } +} + +/** + * Filters edges by removing those that don't match the predicate. + * This function modifies the mutable graph in place. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * + * Graph.addEdge(mutable, a, b, 5) + * Graph.addEdge(mutable, b, c, 15) + * Graph.addEdge(mutable, c, a, 25) + * + * // Keep only edges with weight >= 10 + * Graph.filterEdges(mutable, (data) => data >= 10) + * }) + * + * console.log(Graph.edgeCount(graph)) // 2 (edge with weight 5 removed) + * ``` + * + * @since 3.18.0 + * @category transformations + */ +export const filterEdges = ( + mutable: MutableGraph, + predicate: (data: E) => boolean +): void => { + const edgesToRemove: Array = [] + + // Identify edges to remove + for (const [index, edgeData] of mutable.edges) { + if (!predicate(edgeData.data)) { + edgesToRemove.push(index) + } + } + + // Remove filtered out edges + for (const edgeIndex of edgesToRemove) { + removeEdge(mutable, edgeIndex) + } +} + +// ============================================================================= +// Cycle Flag Management (Internal) +// ============================================================================= + +/** @internal */ +const invalidateCycleFlagOnRemoval = ( + mutable: MutableGraph +): void => { + // Only invalidate if the graph had cycles (removing edges/nodes cannot introduce cycles in acyclic graphs) + // If already unknown (null) or acyclic (true), no need to change + if (Option.isSome(mutable.isAcyclic) && mutable.isAcyclic.value === false) { + mutable.isAcyclic = Option.none() + } +} + +/** @internal */ +const invalidateCycleFlagOnAddition = ( + mutable: MutableGraph +): void => { + // Only invalidate if the graph was acyclic (adding edges cannot remove cycles from cyclic graphs) + // If already unknown (null) or cyclic (false), no need to change + if (Option.isSome(mutable.isAcyclic) && mutable.isAcyclic.value === true) { + mutable.isAcyclic = Option.none() + } +} + +// ============================================================================= +// Edge Operations +// ============================================================================= + +/** + * Adds a new edge to a mutable graph and returns its index. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const result = Graph.mutate(Graph.directed(), (mutable) => { + * const nodeA = Graph.addNode(mutable, "Node A") + * const nodeB = Graph.addNode(mutable, "Node B") + * const edge = Graph.addEdge(mutable, nodeA, nodeB, 42) + * console.log(edge) // EdgeIndex with value 0 + * }) + * ``` + * + * @since 3.18.0 + * @category mutations + */ +export const addEdge = ( + mutable: MutableGraph, + source: NodeIndex, + target: NodeIndex, + data: E +): EdgeIndex => { + // Validate that both nodes exist + if (!mutable.nodes.has(source)) { + throw missingNode(source) + } + if (!mutable.nodes.has(target)) { + throw missingNode(target) + } + + const edgeIndex = mutable.nextEdgeIndex + + // Create edge data + const edgeData = new Edge({ source, target, data }) + mutable.edges.set(edgeIndex, edgeData) + + // Update adjacency lists + const sourceAdjacency = mutable.adjacency.get(source) + if (sourceAdjacency !== undefined) { + sourceAdjacency.push(edgeIndex) + } + + const targetReverseAdjacency = mutable.reverseAdjacency.get(target) + if (targetReverseAdjacency !== undefined) { + targetReverseAdjacency.push(edgeIndex) + } + + // For undirected graphs, add reverse connections + if (mutable.type === "undirected") { + const targetAdjacency = mutable.adjacency.get(target) + if (targetAdjacency !== undefined) { + targetAdjacency.push(edgeIndex) + } + + const sourceReverseAdjacency = mutable.reverseAdjacency.get(source) + if (sourceReverseAdjacency !== undefined) { + sourceReverseAdjacency.push(edgeIndex) + } + } + + // Update allocators + mutable.nextEdgeIndex = mutable.nextEdgeIndex + 1 + + // Only invalidate cycle flag if the graph was acyclic + // Adding edges cannot remove cycles from cyclic graphs + invalidateCycleFlagOnAddition(mutable) + + return edgeIndex +} + +/** + * Removes a node and all its incident edges from a mutable graph. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const result = Graph.mutate(Graph.directed(), (mutable) => { + * const nodeA = Graph.addNode(mutable, "Node A") + * const nodeB = Graph.addNode(mutable, "Node B") + * Graph.addEdge(mutable, nodeA, nodeB, 42) + * + * // Remove nodeA and all edges connected to it + * Graph.removeNode(mutable, nodeA) + * }) + * ``` + * + * @since 3.18.0 + * @category mutations + */ +export const removeNode = ( + mutable: MutableGraph, + nodeIndex: NodeIndex +): void => { + // Check if node exists + if (!mutable.nodes.has(nodeIndex)) { + return // Node doesn't exist, nothing to remove + } + + // Collect all incident edges for removal + const edgesToRemove: Array = [] + + // Get outgoing edges + const outgoingEdges = mutable.adjacency.get(nodeIndex) + if (outgoingEdges !== undefined) { + for (const edge of outgoingEdges) { + edgesToRemove.push(edge) + } + } + + // Get incoming edges + const incomingEdges = mutable.reverseAdjacency.get(nodeIndex) + if (incomingEdges !== undefined) { + for (const edge of incomingEdges) { + edgesToRemove.push(edge) + } + } + + // Remove all incident edges + for (const edgeIndex of edgesToRemove) { + removeEdgeInternal(mutable, edgeIndex) + } + + // Remove the node itself + mutable.nodes.delete(nodeIndex) + mutable.adjacency.delete(nodeIndex) + mutable.reverseAdjacency.delete(nodeIndex) + + // Only invalidate cycle flag if the graph wasn't already known to be acyclic + // Removing nodes cannot introduce cycles in an acyclic graph + invalidateCycleFlagOnRemoval(mutable) +} + +/** + * Removes an edge from a mutable graph. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const result = Graph.mutate(Graph.directed(), (mutable) => { + * const nodeA = Graph.addNode(mutable, "Node A") + * const nodeB = Graph.addNode(mutable, "Node B") + * const edge = Graph.addEdge(mutable, nodeA, nodeB, 42) + * + * // Remove the edge + * Graph.removeEdge(mutable, edge) + * }) + * ``` + * + * @since 3.18.0 + * @category mutations + */ +export const removeEdge = ( + mutable: MutableGraph, + edgeIndex: EdgeIndex +): void => { + const wasRemoved = removeEdgeInternal(mutable, edgeIndex) + + // Only invalidate cycle flag if an edge was actually removed + // and only if the graph wasn't already known to be acyclic + if (wasRemoved) { + invalidateCycleFlagOnRemoval(mutable) + } +} + +/** @internal */ +const removeEdgeInternal = ( + mutable: MutableGraph, + edgeIndex: EdgeIndex +): boolean => { + // Get edge data + const edge = mutable.edges.get(edgeIndex) + if (edge === undefined) { + return false // Edge doesn't exist, no mutation occurred + } + + const { source, target } = edge + + // Remove from adjacency lists + const sourceAdjacency = mutable.adjacency.get(source) + if (sourceAdjacency !== undefined) { + const index = sourceAdjacency.indexOf(edgeIndex) + if (index !== -1) { + sourceAdjacency.splice(index, 1) + } + } + + const targetReverseAdjacency = mutable.reverseAdjacency.get(target) + if (targetReverseAdjacency !== undefined) { + const index = targetReverseAdjacency.indexOf(edgeIndex) + if (index !== -1) { + targetReverseAdjacency.splice(index, 1) + } + } + + // For undirected graphs, remove reverse connections + if (mutable.type === "undirected") { + const targetAdjacency = mutable.adjacency.get(target) + if (targetAdjacency !== undefined) { + const index = targetAdjacency.indexOf(edgeIndex) + if (index !== -1) { + targetAdjacency.splice(index, 1) + } + } + + const sourceReverseAdjacency = mutable.reverseAdjacency.get(source) + if (sourceReverseAdjacency !== undefined) { + const index = sourceReverseAdjacency.indexOf(edgeIndex) + if (index !== -1) { + sourceReverseAdjacency.splice(index, 1) + } + } + } + + // Remove edge data + mutable.edges.delete(edgeIndex) + + return true // Edge was successfully removed +} + +// ============================================================================= +// Edge Query Operations +// ============================================================================= + +/** + * Gets the edge data associated with an edge index, if it exists. + * + * @example + * ```ts + * import { Graph, Option } from "effect" + * + * const graph = Graph.mutate(Graph.directed(), (mutable) => { + * const nodeA = Graph.addNode(mutable, "Node A") + * const nodeB = Graph.addNode(mutable, "Node B") + * Graph.addEdge(mutable, nodeA, nodeB, 42) + * }) + * + * const edgeIndex = 0 + * const edgeData = Graph.getEdge(graph, edgeIndex) + * + * if (Option.isSome(edgeData)) { + * console.log(edgeData.value.data) // 42 + * console.log(edgeData.value.source) // NodeIndex(0) + * console.log(edgeData.value.target) // NodeIndex(1) + * } + * ``` + * + * @since 3.18.0 + * @category getters + */ +export const getEdge = ( + graph: Graph | MutableGraph, + edgeIndex: EdgeIndex +): Option.Option> => graph.edges.has(edgeIndex) ? Option.some(graph.edges.get(edgeIndex)!) : Option.none() + +/** + * Checks if an edge exists between two nodes in the graph. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.mutate(Graph.directed(), (mutable) => { + * const nodeA = Graph.addNode(mutable, "Node A") + * const nodeB = Graph.addNode(mutable, "Node B") + * const nodeC = Graph.addNode(mutable, "Node C") + * Graph.addEdge(mutable, nodeA, nodeB, 42) + * }) + * + * const nodeA = 0 + * const nodeB = 1 + * const nodeC = 2 + * + * const hasAB = Graph.hasEdge(graph, nodeA, nodeB) + * console.log(hasAB) // true + * + * const hasAC = Graph.hasEdge(graph, nodeA, nodeC) + * console.log(hasAC) // false + * ``` + * + * @since 3.18.0 + * @category getters + */ +export const hasEdge = ( + graph: Graph | MutableGraph, + source: NodeIndex, + target: NodeIndex +): boolean => { + const adjacencyList = graph.adjacency.get(source) + if (adjacencyList === undefined) { + return false + } + + // Check if any edge in the adjacency list connects to the target + for (const edgeIndex of adjacencyList) { + const edge = graph.edges.get(edgeIndex) + if (edge !== undefined && edge.target === target) { + return true + } + } + + return false +} + +/** + * Returns the number of edges in the graph. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const emptyGraph = Graph.directed() + * console.log(Graph.edgeCount(emptyGraph)) // 0 + * + * const graphWithEdges = Graph.mutate(emptyGraph, (mutable) => { + * const nodeA = Graph.addNode(mutable, "Node A") + * const nodeB = Graph.addNode(mutable, "Node B") + * const nodeC = Graph.addNode(mutable, "Node C") + * Graph.addEdge(mutable, nodeA, nodeB, 1) + * Graph.addEdge(mutable, nodeB, nodeC, 2) + * Graph.addEdge(mutable, nodeC, nodeA, 3) + * }) + * + * console.log(Graph.edgeCount(graphWithEdges)) // 3 + * ``` + * + * @since 3.18.0 + * @category getters + */ +export const edgeCount = ( + graph: Graph | MutableGraph +): number => graph.edges.size + +/** + * Returns the neighboring nodes (targets of outgoing edges) for a given node. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.mutate(Graph.directed(), (mutable) => { + * const nodeA = Graph.addNode(mutable, "Node A") + * const nodeB = Graph.addNode(mutable, "Node B") + * const nodeC = Graph.addNode(mutable, "Node C") + * Graph.addEdge(mutable, nodeA, nodeB, 1) + * Graph.addEdge(mutable, nodeA, nodeC, 2) + * }) + * + * const nodeA = 0 + * const nodeB = 1 + * const nodeC = 2 + * + * const neighborsA = Graph.neighbors(graph, nodeA) + * console.log(neighborsA) // [NodeIndex(1), NodeIndex(2)] + * + * const neighborsB = Graph.neighbors(graph, nodeB) + * console.log(neighborsB) // [] + * ``` + * + * @since 3.18.0 + * @category getters + */ +export const neighbors = ( + graph: Graph | MutableGraph, + nodeIndex: NodeIndex +): Array => { + // For undirected graphs, use the specialized helper that returns the other endpoint + if (graph.type === "undirected") { + return getUndirectedNeighbors(graph as any, nodeIndex) + } + + const adjacencyList = graph.adjacency.get(nodeIndex) + if (adjacencyList === undefined) { + return [] + } + + const result: Array = [] + for (const edgeIndex of adjacencyList) { + const edge = graph.edges.get(edgeIndex) + if (edge !== undefined) { + result.push(edge.target) + } + } + + return result +} + +/** + * Get neighbors of a node in a specific direction for bidirectional traversal. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * Graph.addEdge(mutable, a, b, "A->B") + * }) + * + * const nodeA = 0 + * const nodeB = 1 + * + * // Get outgoing neighbors (nodes that nodeA points to) + * const outgoing = Graph.neighborsDirected(graph, nodeA, "outgoing") + * + * // Get incoming neighbors (nodes that point to nodeB) + * const incoming = Graph.neighborsDirected(graph, nodeB, "incoming") + * ``` + * + * @since 3.18.0 + * @category queries + */ +export const neighborsDirected = ( + graph: Graph | MutableGraph, + nodeIndex: NodeIndex, + direction: Direction +): Array => { + const adjacencyMap = direction === "incoming" + ? graph.reverseAdjacency + : graph.adjacency + + const adjacencyList = adjacencyMap.get(nodeIndex) + if (adjacencyList === undefined) { + return [] + } + + const result: Array = [] + for (const edgeIndex of adjacencyList) { + const edge = graph.edges.get(edgeIndex) + if (edge !== undefined) { + // For incoming direction, we want the source node instead of target + const neighborNode = direction === "incoming" + ? edge.source + : edge.target + result.push(neighborNode) + } + } + + return result +} + +// ============================================================================= +// GraphViz Export +// ============================================================================= + +/** + * Configuration options for GraphViz DOT format generation from graphs. + * + * @since 3.18.0 + * @category models + */ +export interface GraphVizOptions { + readonly nodeLabel?: (data: N) => string + readonly edgeLabel?: (data: E) => string + readonly graphName?: string +} + +/** + * Exports a graph to GraphViz DOT format for visualization. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.mutate(Graph.directed(), (mutable) => { + * const nodeA = Graph.addNode(mutable, "Node A") + * const nodeB = Graph.addNode(mutable, "Node B") + * const nodeC = Graph.addNode(mutable, "Node C") + * Graph.addEdge(mutable, nodeA, nodeB, 1) + * Graph.addEdge(mutable, nodeB, nodeC, 2) + * Graph.addEdge(mutable, nodeC, nodeA, 3) + * }) + * + * const dot = Graph.toGraphViz(graph) + * console.log(dot) + * // digraph G { + * // "0" [label="Node A"]; + * // "1" [label="Node B"]; + * // "2" [label="Node C"]; + * // "0" -> "1" [label="1"]; + * // "1" -> "2" [label="2"]; + * // "2" -> "0" [label="3"]; + * // } + * ``` + * + * @since 3.18.0 + * @category utils + */ +export const toGraphViz = ( + graph: Graph | MutableGraph, + options?: GraphVizOptions +): string => { + const { + edgeLabel = (data: E) => String(data), + graphName = "G", + nodeLabel = (data: N) => String(data) + } = options ?? {} + + const isDirected = graph.type === "directed" + const graphType = isDirected ? "digraph" : "graph" + const edgeOperator = isDirected ? "->" : "--" + + const lines: Array = [] + lines.push(`${graphType} ${graphName} {`) + + // Add nodes + for (const [nodeIndex, nodeData] of graph.nodes) { + const label = nodeLabel(nodeData).replace(/"/g, "\\\"") + lines.push(` "${nodeIndex}" [label="${label}"];`) + } + + // Add edges + for (const [, edgeData] of graph.edges) { + const label = edgeLabel(edgeData.data).replace(/"/g, "\\\"") + lines.push(` "${edgeData.source}" ${edgeOperator} "${edgeData.target}" [label="${label}"];`) + } + + lines.push("}") + return lines.join("\n") +} + +// ============================================================================= +// Mermaid Export +// ============================================================================= + +/** + * Mermaid node shape types. + * + * @since 3.18.0 + * @category models + */ +export type MermaidNodeShape = + | "rectangle" + | "rounded" + | "circle" + | "diamond" + | "hexagon" + | "stadium" + | "subroutine" + | "cylindrical" + +/** + * Mermaid diagram direction types. + * + * @since 3.18.0 + * @category models + */ +export type MermaidDirection = "TB" | "TD" | "BT" | "LR" | "RL" + +/** + * Mermaid diagram type. + * + * @since 3.18.0 + * @category models + */ +export type MermaidDiagramType = "flowchart" | "graph" + +/** + * Configuration options for Mermaid diagram generation. + * + * @since 3.18.0 + * @category models + */ +export interface MermaidOptions { + readonly nodeLabel?: (data: N) => string + readonly edgeLabel?: (data: E) => string + readonly diagramType?: MermaidDiagramType + readonly direction?: MermaidDirection + readonly nodeShape?: (data: N) => MermaidNodeShape +} + +/** @internal */ +const escapeMermaidLabel = (label: string): string => { + // Escape special characters for Mermaid using HTML entity codes + // According to: https://mermaid.js.org/syntax/flowchart.html#special-characters-that-break-syntax + return label + .replace(/#/g, "#35;") + .replace(/"/g, "#quot;") + .replace(//g, "#gt;") + .replace(/&/g, "#amp;") + .replace(/\[/g, "#91;") + .replace(/\]/g, "#93;") + .replace(/\{/g, "#123;") + .replace(/\}/g, "#125;") + .replace(/\(/g, "#40;") + .replace(/\)/g, "#41;") + .replace(/\|/g, "#124;") + .replace(/\\/g, "#92;") + .replace(/\n/g, "
"); +} + +/** @internal */ +const formatMermaidNode = (nodeId: string, label: string, shape: MermaidNodeShape): string => { + switch (shape) { + case "rectangle": + return `${nodeId}["${label}"]` + case "rounded": + return `${nodeId}("${label}")` + case "circle": + return `${nodeId}(("${label}"))` + case "diamond": + return `${nodeId}{"${label}"}` + case "hexagon": + return `${nodeId}{{"${label}"}}` + case "stadium": + return `${nodeId}(["${label}"])` + case "subroutine": + return `${nodeId}[["${label}"]]` + case "cylindrical": + return `${nodeId}[("${label}")]` + } +} + +/** + * Exports a graph to Mermaid diagram format for visualization. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.mutate(Graph.directed(), (mutable) => { + * const app = Graph.addNode(mutable, "App") + * const db = Graph.addNode(mutable, "Database") + * const cache = Graph.addNode(mutable, "Cache") + * Graph.addEdge(mutable, app, db, 1) + * Graph.addEdge(mutable, app, cache, 2) + * }) + * + * const mermaid = Graph.toMermaid(graph) + * console.log(mermaid) + * // flowchart TD + * // 0["App"] + * // 1["Database"] + * // 2["Cache"] + * // 0 -->|"1"| 1 + * // 0 -->|"2"| 2 + * ``` + * + * @since 3.18.0 + * @category utils + */ +export const toMermaid = ( + graph: Graph | MutableGraph, + options?: MermaidOptions +): string => { + // Extract and validate options with defaults + const { + diagramType, + direction = "TD", + edgeLabel = (data: E) => String(data), + nodeLabel = (data: N) => String(data), + nodeShape = () => "rectangle" as const + } = options ?? {} + + // Auto-detect diagram type if not specified + const finalDiagramType = diagramType ?? + (graph.type === "directed" ? "flowchart" : "graph") + + // Generate diagram header + const lines: Array = [] + lines.push(`${finalDiagramType} ${direction}`) + + // Add nodes + for (const [nodeIndex, nodeData] of graph.nodes) { + const nodeId = String(nodeIndex) + const label = escapeMermaidLabel(nodeLabel(nodeData)) + const shape = nodeShape(nodeData) + const formattedNode = formatMermaidNode(nodeId, label, shape) + lines.push(` ${formattedNode}`) + } + + // Add edges + const edgeOperator = finalDiagramType === "flowchart" ? "-->" : "---" + for (const [, edgeData] of graph.edges) { + const sourceId = String(edgeData.source) + const targetId = String(edgeData.target) + const label = escapeMermaidLabel(edgeLabel(edgeData.data)) + + if (label) { + lines.push(` ${sourceId} ${edgeOperator}|"${label}"| ${targetId}`) + } else { + lines.push(` ${sourceId} ${edgeOperator} ${targetId}`) + } + } + + return lines.join("\n") +} + +// ============================================================================= +// Direction Types for Bidirectional Traversal +// ============================================================================= + +/** + * Direction for graph traversal, indicating which edges to follow. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * Graph.addEdge(mutable, a, b, "A->B") + * }) + * + * // Follow outgoing edges (normal direction) + * const outgoingNodes = Array.from(Graph.indices(Graph.dfs(graph, { start: [0], direction: "outgoing" }))) + * + * // Follow incoming edges (reverse direction) + * const incomingNodes = Array.from(Graph.indices(Graph.dfs(graph, { start: [1], direction: "incoming" }))) + * ``` + * + * @since 3.18.0 + * @category models + */ +export type Direction = "outgoing" | "incoming" + +// ============================================================================= + +// ============================================================================= +// Graph Structure Analysis Algorithms (Phase 5A) +// ============================================================================= + +/** + * Checks if the graph is acyclic (contains no cycles). + * + * Uses depth-first search to detect back edges, which indicate cycles. + * For directed graphs, any back edge creates a cycle. For undirected graphs, + * a back edge that doesn't go to the immediate parent creates a cycle. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * // Acyclic directed graph (DAG) + * const dag = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, "A->B") + * Graph.addEdge(mutable, b, c, "B->C") + * }) + * console.log(Graph.isAcyclic(dag)) // true + * + * // Cyclic directed graph + * const cyclic = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * Graph.addEdge(mutable, a, b, "A->B") + * Graph.addEdge(mutable, b, a, "B->A") // Creates cycle + * }) + * console.log(Graph.isAcyclic(cyclic)) // false + * ``` + * + * @since 3.18.0 + * @category algorithms + */ +export const isAcyclic = ( + graph: Graph | MutableGraph +): boolean => { + // Use existing cycle flag if available + if (Option.isSome(graph.isAcyclic)) { + return graph.isAcyclic.value + } + + // Stack-safe DFS cycle detection using iterative approach + const visited = new Set() + const recursionStack = new Set() + + // Stack entry: [node, neighbors, neighborIndex, isFirstVisit] + type DfsStackEntry = [NodeIndex, Array, number, boolean] + + // Get all nodes to handle disconnected components + for (const startNode of graph.nodes.keys()) { + if (visited.has(startNode)) { + continue // Already processed this component + } + + // Iterative DFS with explicit stack + const stack: Array = [[startNode, [], 0, true]] + + while (stack.length > 0) { + const [node, neighbors, neighborIndex, isFirstVisit] = stack[stack.length - 1] + + // First visit to this node + if (isFirstVisit) { + if (recursionStack.has(node)) { + // Back edge found - cycle detected + graph.isAcyclic = Option.some(false) + return false + } + + if (visited.has(node)) { + stack.pop() + continue + } + + visited.add(node) + recursionStack.add(node) + + // Get neighbors for this node + const nodeNeighbors = Array.from(neighborsDirected(graph, node, "outgoing")) + stack[stack.length - 1] = [node, nodeNeighbors, 0, false] + continue + } + + // Process next neighbor + if (neighborIndex < neighbors.length) { + const neighbor = neighbors[neighborIndex] + stack[stack.length - 1] = [node, neighbors, neighborIndex + 1, false] + + if (recursionStack.has(neighbor)) { + // Back edge found - cycle detected + graph.isAcyclic = Option.some(false) + return false + } + + if (!visited.has(neighbor)) { + stack.push([neighbor, [], 0, true]) + } + } else { + // Done with this node - backtrack + recursionStack.delete(node) + stack.pop() + } + } + } + + // Cache the result + graph.isAcyclic = Option.some(true) + return true +} + +/** + * Checks if an undirected graph is bipartite. + * + * A bipartite graph is one whose vertices can be divided into two disjoint sets + * such that no two vertices within the same set are adjacent. Uses BFS coloring + * to determine bipartiteness. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * // Bipartite graph (alternating coloring possible) + * const bipartite = Graph.undirected((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * const d = Graph.addNode(mutable, "D") + * Graph.addEdge(mutable, a, b, "edge") // Set 1: {A, C}, Set 2: {B, D} + * Graph.addEdge(mutable, b, c, "edge") + * Graph.addEdge(mutable, c, d, "edge") + * }) + * console.log(Graph.isBipartite(bipartite)) // true + * + * // Non-bipartite graph (odd cycle) + * const triangle = Graph.undirected((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, "edge") + * Graph.addEdge(mutable, b, c, "edge") + * Graph.addEdge(mutable, c, a, "edge") // Triangle (3-cycle) + * }) + * console.log(Graph.isBipartite(triangle)) // false + * ``` + * + * @since 3.18.0 + * @category algorithms + */ +export const isBipartite = ( + graph: Graph | MutableGraph +): boolean => { + const coloring = new Map() + const discovered = new Set() + let isBipartiteGraph = true + + // Get all nodes to handle disconnected components + for (const startNode of graph.nodes.keys()) { + if (!discovered.has(startNode)) { + // Start BFS coloring from this component + const queue: Array = [startNode] + coloring.set(startNode, 0) // Color start node with 0 + discovered.add(startNode) + + while (queue.length > 0 && isBipartiteGraph) { + const current = queue.shift()! + const currentColor = coloring.get(current)! + const neighborColor: 0 | 1 = currentColor === 0 ? 1 : 0 + + // Get all neighbors for undirected graph + const nodeNeighbors = getUndirectedNeighbors(graph, current) + for (const neighbor of nodeNeighbors) { + if (!discovered.has(neighbor)) { + // Color unvisited neighbor with opposite color + coloring.set(neighbor, neighborColor) + discovered.add(neighbor) + queue.push(neighbor) + } else { + // Check if neighbor has the same color (conflict) + if (coloring.get(neighbor) === currentColor) { + isBipartiteGraph = false + break + } + } + } + } + + // Early exit if not bipartite + if (!isBipartiteGraph) { + break + } + } + } + + return isBipartiteGraph +} + +/** + * Get neighbors for undirected graphs by checking both adjacency and reverse adjacency. + * For undirected graphs, we need to find the other endpoint of each edge incident to the node. + */ +const getUndirectedNeighbors = ( + graph: Graph | MutableGraph, + nodeIndex: NodeIndex +): Array => { + const neighbors = new Set() + + // Check edges where this node is the source + const adjacencyList = graph.adjacency.get(nodeIndex) + if (adjacencyList !== undefined) { + for (const edgeIndex of adjacencyList) { + const edge = graph.edges.get(edgeIndex) + if (edge !== undefined) { + // For undirected graphs, the neighbor is the other endpoint + const otherNode = edge.source === nodeIndex ? edge.target : edge.source + neighbors.add(otherNode) + } + } + } + + return Array.from(neighbors) +} + +/** + * Find connected components in an undirected graph. + * Each component is represented as an array of node indices. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.undirected((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * const d = Graph.addNode(mutable, "D") + * Graph.addEdge(mutable, a, b, "edge") // Component 1: A-B + * Graph.addEdge(mutable, c, d, "edge") // Component 2: C-D + * }) + * + * const components = Graph.connectedComponents(graph) + * console.log(components) // [[0, 1], [2, 3]] + * ``` + * + * @since 3.18.0 + * @category algorithms + */ +export const connectedComponents = ( + graph: Graph | MutableGraph +): Array> => { + const visited = new Set() + const components: Array> = [] + for (const startNode of graph.nodes.keys()) { + if (!visited.has(startNode)) { + // DFS to find all nodes in this component + const component: Array = [] + const stack: Array = [startNode] + + while (stack.length > 0) { + const current = stack.pop()! + if (!visited.has(current)) { + visited.add(current) + component.push(current) + + // Add all unvisited neighbors to stack + const nodeNeighbors = getUndirectedNeighbors(graph, current) + for (const neighbor of nodeNeighbors) { + if (!visited.has(neighbor)) { + stack.push(neighbor) + } + } + } + } + + components.push(component) + } + } + + return components +} + +/** + * Find strongly connected components in a directed graph using Kosaraju's algorithm. + * Each SCC is represented as an array of node indices. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, "A->B") + * Graph.addEdge(mutable, b, c, "B->C") + * Graph.addEdge(mutable, c, a, "C->A") // Creates SCC: A-B-C + * }) + * + * const sccs = Graph.stronglyConnectedComponents(graph) + * console.log(sccs) // [[0, 1, 2]] + * ``` + * + * @since 3.18.0 + * @category algorithms + */ +export const stronglyConnectedComponents = ( + graph: Graph | MutableGraph +): Array> => { + const visited = new Set() + const finishOrder: Array = [] + // Iterate directly over node keys + + // Step 1: Stack-safe DFS on original graph to get finish times + // Stack entry: [node, neighbors, neighborIndex, isFirstVisit] + type DfsStackEntry = [NodeIndex, Array, number, boolean] + + for (const startNode of graph.nodes.keys()) { + if (visited.has(startNode)) { + continue + } + + const stack: Array = [[startNode, [], 0, true]] + + while (stack.length > 0) { + const [node, nodeNeighbors, neighborIndex, isFirstVisit] = stack[stack.length - 1] + + if (isFirstVisit) { + if (visited.has(node)) { + stack.pop() + continue + } + + visited.add(node) + const nodeNeighborsList = neighbors(graph, node) + stack[stack.length - 1] = [node, nodeNeighborsList, 0, false] + continue + } + + // Process next neighbor + if (neighborIndex < nodeNeighbors.length) { + const neighbor = nodeNeighbors[neighborIndex] + stack[stack.length - 1] = [node, nodeNeighbors, neighborIndex + 1, false] + + if (!visited.has(neighbor)) { + stack.push([neighbor, [], 0, true]) + } + } else { + // Done with this node - add to finish order (post-order) + finishOrder.push(node) + stack.pop() + } + } + } + + // Step 2: Stack-safe DFS on transpose graph in reverse finish order + visited.clear() + const sccs: Array> = [] + + for (let i = finishOrder.length - 1; i >= 0; i--) { + const startNode = finishOrder[i] + if (visited.has(startNode)) { + continue + } + + const scc: Array = [] + const stack: Array = [startNode] + + while (stack.length > 0) { + const node = stack.pop()! + + if (visited.has(node)) { + continue + } + + visited.add(node) + scc.push(node) + + // Use reverse adjacency (transpose graph) + const reverseAdjacency = graph.reverseAdjacency.get(node) + if (reverseAdjacency !== undefined) { + for (const edgeIndex of reverseAdjacency) { + const edge = graph.edges.get(edgeIndex) + if (edge !== undefined) { + const predecessor = edge.source + if (!visited.has(predecessor)) { + stack.push(predecessor) + } + } + } + } + } + + sccs.push(scc) + } + + return sccs +} + +// ============================================================================= +// Path Finding Algorithms (Phase 5B) +// ============================================================================= + +/** + * Result of a shortest path computation containing the path and total distance. + * + * @since 3.18.0 + * @category models + */ +export interface PathResult { + readonly path: Array + readonly distance: number + readonly costs: Array +} + +/** + * Configuration for Dijkstra's algorithm. + * + * @since 3.18.0 + * @category models + */ +export interface DijkstraConfig { + source: NodeIndex + target: NodeIndex + cost: (edgeData: E) => number +} + +/** + * Configuration for A* algorithm. + * + * @since 3.18.0 + * @category models + */ +export interface AstarConfig { + source: NodeIndex + target: NodeIndex + cost: (edgeData: E) => number + heuristic: (sourceNodeData: N, targetNodeData: N) => number +} + +/** + * Configuration for Bellman-Ford algorithm. + * + * @since 3.18.0 + * @category models + */ +export interface BellmanFordConfig { + source: NodeIndex + target: NodeIndex + cost: (edgeData: E) => number +} + +/** + * Find the shortest path between two nodes using Dijkstra's algorithm. + * + * Dijkstra's algorithm works with non-negative edge weights and finds the shortest + * path from a source node to a target node in O((V + E) log V) time complexity. + * + * @example + * ```ts + * import { Graph, Option } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, 5) + * Graph.addEdge(mutable, a, c, 10) + * Graph.addEdge(mutable, b, c, 2) + * }) + * + * const result = Graph.dijkstra(graph, { source: 0, target: 2, cost: (edgeData) => edgeData }) + * if (Option.isSome(result)) { + * console.log(result.value.path) // [0, 1, 2] - shortest path A->B->C + * console.log(result.value.distance) // 7 - total distance + * } + * ``` + * + * @since 3.18.0 + * @category algorithms + */ +export const dijkstra = ( + graph: Graph | MutableGraph, + config: DijkstraConfig +): Option.Option> => { + const { cost, source, target } = config + // Validate that source and target nodes exist + if (!graph.nodes.has(source)) { + throw missingNode(source) + } + if (!graph.nodes.has(target)) { + throw missingNode(target) + } + + // Early return if source equals target + if (source === target) { + return Option.some({ + path: [source], + distance: 0, + costs: [] + }) + } + + // Distance tracking and priority queue simulation + const distances = new Map() + const previous = new Map() + const visited = new Set() + + // Initialize distances + // Iterate directly over node keys + for (const node of graph.nodes.keys()) { + distances.set(node, node === source ? 0 : Infinity) + previous.set(node, null) + } + + // Simple priority queue using array (can be optimized with proper heap) + const priorityQueue: Array<{ node: NodeIndex; distance: number }> = [ + { node: source, distance: 0 } + ] + + while (priorityQueue.length > 0) { + // Find minimum distance node (priority queue extract-min) + let minIndex = 0 + for (let i = 1; i < priorityQueue.length; i++) { + if (priorityQueue[i].distance < priorityQueue[minIndex].distance) { + minIndex = i + } + } + + const current = priorityQueue.splice(minIndex, 1)[0] + const currentNode = current.node + + // Skip if already visited (can happen with duplicate entries) + if (visited.has(currentNode)) { + continue + } + + visited.add(currentNode) + + // Early termination if we reached the target + if (currentNode === target) { + break + } + + // Get current distance + const currentDistance = distances.get(currentNode)! + + // Examine all outgoing edges + const adjacencyList = graph.adjacency.get(currentNode) + if (adjacencyList !== undefined) { + for (const edgeIndex of adjacencyList) { + const edge = graph.edges.get(edgeIndex) + if (edge !== undefined) { + const neighbor = edge.target + const weight = cost(edge.data) + + // Validate non-negative weights + if (weight < 0) { + throw new Error(`Dijkstra's algorithm requires non-negative edge weights, found ${weight}`) + } + + const newDistance = currentDistance + weight + const neighborDistance = distances.get(neighbor)! + + // Relaxation step + if (newDistance < neighborDistance) { + distances.set(neighbor, newDistance) + previous.set(neighbor, { node: currentNode, edgeData: edge.data }) + + // Add to priority queue if not visited + if (!visited.has(neighbor)) { + priorityQueue.push({ node: neighbor, distance: newDistance }) + } + } + } + } + } + } + + // Check if target is reachable + const targetDistance = distances.get(target)! + if (targetDistance === Infinity) { + return Option.none() // No path exists + } + + // Reconstruct path + const path: Array = [] + const costs: Array = [] + let currentNode: NodeIndex | null = target + + while (currentNode !== null) { + path.unshift(currentNode) + const prev: { node: NodeIndex; edgeData: E } | null = previous.get(currentNode)! + if (prev !== null) { + costs.unshift(prev.edgeData) + currentNode = prev.node + } else { + currentNode = null + } + } + + return Option.some({ + path, + distance: targetDistance, + costs + }) +} + +/** + * Result of all-pairs shortest path computation. + * + * @since 3.18.0 + * @category models + */ +export interface AllPairsResult { + readonly distances: Map> + readonly paths: Map | null>> + readonly costs: Map>> +} + +/** + * Find shortest paths between all pairs of nodes using Floyd-Warshall algorithm. + * + * Floyd-Warshall algorithm computes shortest paths between all pairs of nodes in O(V³) time. + * It can handle negative edge weights and detect negative cycles. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, 3) + * Graph.addEdge(mutable, b, c, 2) + * Graph.addEdge(mutable, a, c, 7) + * }) + * + * const result = Graph.floydWarshall(graph, (edgeData) => edgeData) + * const distanceAToC = result.distances.get(0)?.get(2) // 5 (A->B->C) + * const pathAToC = result.paths.get(0)?.get(2) // [0, 1, 2] + * ``` + * + * @since 3.18.0 + * @category algorithms + */ +export const floydWarshall = ( + graph: Graph | MutableGraph, + cost: (edgeData: E) => number +): AllPairsResult => { + // Get all nodes for Floyd-Warshall algorithm (needs array for nested iteration) + const allNodes = Array.from(graph.nodes.keys()) + + // Initialize distance matrix + const dist = new Map>() + const next = new Map>() + const edgeMatrix = new Map>() + + // Initialize with infinity for all pairs + for (const i of allNodes) { + dist.set(i, new Map()) + next.set(i, new Map()) + edgeMatrix.set(i, new Map()) + + for (const j of allNodes) { + dist.get(i)!.set(j, i === j ? 0 : Infinity) + next.get(i)!.set(j, null) + edgeMatrix.get(i)!.set(j, null) + } + } + + // Set edge weights + for (const [, edgeData] of graph.edges) { + const weight = cost(edgeData.data) + const i = edgeData.source + const j = edgeData.target + + // Use minimum weight if multiple edges exist + const currentWeight = dist.get(i)!.get(j)! + if (weight < currentWeight) { + dist.get(i)!.set(j, weight) + next.get(i)!.set(j, j) + edgeMatrix.get(i)!.set(j, edgeData.data) + } + } + + // Floyd-Warshall main loop + for (const k of allNodes) { + for (const i of allNodes) { + for (const j of allNodes) { + const distIK = dist.get(i)!.get(k)! + const distKJ = dist.get(k)!.get(j)! + const distIJ = dist.get(i)!.get(j)! + + if (distIK !== Infinity && distKJ !== Infinity && distIK + distKJ < distIJ) { + dist.get(i)!.set(j, distIK + distKJ) + next.get(i)!.set(j, next.get(i)!.get(k)!) + } + } + } + } + + // Check for negative cycles + for (const i of allNodes) { + if (dist.get(i)!.get(i)! < 0) { + throw new Error(`Negative cycle detected involving node ${i}`) + } + } + + // Build result paths and edge weights + const paths = new Map | null>>() + const resultCosts = new Map>>() + + for (const i of allNodes) { + paths.set(i, new Map()) + resultCosts.set(i, new Map()) + + for (const j of allNodes) { + if (i === j) { + paths.get(i)!.set(j, [i]) + resultCosts.get(i)!.set(j, []) + } else if (dist.get(i)!.get(j)! === Infinity) { + paths.get(i)!.set(j, null) + resultCosts.get(i)!.set(j, []) + } else { + // Reconstruct path iteratively + const path: Array = [] + const weights: Array = [] + let current = i + + path.push(current) + while (current !== j) { + const nextNode = next.get(current)!.get(j)! + if (nextNode === null) break + + const edgeData = edgeMatrix.get(current)!.get(nextNode)! + if (edgeData !== null) { + weights.push(edgeData) + } + + current = nextNode + path.push(current) + } + + paths.get(i)!.set(j, path) + resultCosts.get(i)!.set(j, weights) + } + } + } + + return { + distances: dist, + paths, + costs: resultCosts + } +} + +/** + * Find the shortest path between two nodes using A* pathfinding algorithm. + * + * A* is an extension of Dijkstra's algorithm that uses a heuristic function to guide + * the search towards the target, potentially finding paths faster than Dijkstra's. + * The heuristic must be admissible (never overestimate the actual cost). + * + * @example + * ```ts + * import { Graph, Option } from "effect" + * + * const graph = Graph.directed<{x: number, y: number}, number>((mutable) => { + * const a = Graph.addNode(mutable, {x: 0, y: 0}) + * const b = Graph.addNode(mutable, {x: 1, y: 0}) + * const c = Graph.addNode(mutable, {x: 2, y: 0}) + * Graph.addEdge(mutable, a, b, 1) + * Graph.addEdge(mutable, b, c, 1) + * }) + * + * // Manhattan distance heuristic + * const heuristic = (nodeData: {x: number, y: number}, targetData: {x: number, y: number}) => + * Math.abs(nodeData.x - targetData.x) + Math.abs(nodeData.y - targetData.y) + * + * const result = Graph.astar(graph, { source: 0, target: 2, cost: (edgeData) => edgeData, heuristic }) + * if (Option.isSome(result)) { + * console.log(result.value.path) // [0, 1, 2] - shortest path + * console.log(result.value.distance) // 2 - total distance + * } + * ``` + * + * @since 3.18.0 + * @category algorithms + */ +export const astar = ( + graph: Graph | MutableGraph, + config: AstarConfig +): Option.Option> => { + const { cost, heuristic, source, target } = config + // Validate that source and target nodes exist + if (!graph.nodes.has(source)) { + throw missingNode(source) + } + if (!graph.nodes.has(target)) { + throw missingNode(target) + } + + // Early return if source equals target + if (source === target) { + return Option.some({ + path: [source], + distance: 0, + costs: [] + }) + } + + // Get target node data for heuristic calculations + const targetNodeData = graph.nodes.get(target) + if (targetNodeData === undefined) { + throw new Error(`Target node ${target} data not found`) + } + + // Distance tracking (g-score) and f-score (g + h) + const gScore = new Map() + const fScore = new Map() + const previous = new Map() + const visited = new Set() + + // Initialize scores + // Iterate directly over node keys + for (const node of graph.nodes.keys()) { + gScore.set(node, node === source ? 0 : Infinity) + fScore.set(node, Infinity) + previous.set(node, null) + } + + // Calculate initial f-score for source + const sourceNodeData = graph.nodes.get(source) + if (sourceNodeData !== undefined) { + const h = heuristic(sourceNodeData, targetNodeData) + fScore.set(source, h) + } + + // Priority queue using f-score (total estimated cost) + const openSet: Array<{ node: NodeIndex; fScore: number }> = [ + { node: source, fScore: fScore.get(source)! } + ] + + while (openSet.length > 0) { + // Find node with lowest f-score + let minIndex = 0 + for (let i = 1; i < openSet.length; i++) { + if (openSet[i].fScore < openSet[minIndex].fScore) { + minIndex = i + } + } + + const current = openSet.splice(minIndex, 1)[0] + const currentNode = current.node + + // Skip if already visited + if (visited.has(currentNode)) { + continue + } + + visited.add(currentNode) + + // Early termination if we reached the target + if (currentNode === target) { + break + } + + // Get current g-score + const currentGScore = gScore.get(currentNode)! + + // Examine all outgoing edges + const adjacencyList = graph.adjacency.get(currentNode) + if (adjacencyList !== undefined) { + for (const edgeIndex of adjacencyList) { + const edge = graph.edges.get(edgeIndex) + if (edge !== undefined) { + const neighbor = edge.target + const weight = cost(edge.data) + + // Validate non-negative weights + if (weight < 0) { + throw new Error(`A* algorithm requires non-negative edge weights, found ${weight}`) + } + + const tentativeGScore = currentGScore + weight + const neighborGScore = gScore.get(neighbor)! + + // If this path to neighbor is better than any previous one + if (tentativeGScore < neighborGScore) { + // Update g-score and previous + gScore.set(neighbor, tentativeGScore) + previous.set(neighbor, { node: currentNode, edgeData: edge.data }) + + // Calculate f-score using heuristic + const neighborNodeData = graph.nodes.get(neighbor) + if (neighborNodeData !== undefined) { + const h = heuristic(neighborNodeData, targetNodeData) + const f = tentativeGScore + h + fScore.set(neighbor, f) + + // Add to open set if not visited + if (!visited.has(neighbor)) { + openSet.push({ node: neighbor, fScore: f }) + } + } + } + } + } + } + } + + // Check if target is reachable + const targetGScore = gScore.get(target)! + if (targetGScore === Infinity) { + return Option.none() // No path exists + } + + // Reconstruct path + const path: Array = [] + const costs: Array = [] + let currentNode: NodeIndex | null = target + + while (currentNode !== null) { + path.unshift(currentNode) + const prev: { node: NodeIndex; edgeData: E } | null = previous.get(currentNode)! + if (prev !== null) { + costs.unshift(prev.edgeData) + currentNode = prev.node + } else { + currentNode = null + } + } + + return Option.some({ + path, + distance: targetGScore, + costs + }) +} + +/** + * Find the shortest path between two nodes using Bellman-Ford algorithm. + * + * Bellman-Ford algorithm can handle negative edge weights and detects negative cycles. + * It has O(VE) time complexity, slower than Dijkstra's but more versatile. + * Returns Option.none() if a negative cycle is detected that affects the path. + * + * @example + * ```ts + * import { Graph, Option } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, -1) // Negative weight allowed + * Graph.addEdge(mutable, b, c, 3) + * Graph.addEdge(mutable, a, c, 5) + * }) + * + * const result = Graph.bellmanFord(graph, { source: 0, target: 2, cost: (edgeData) => edgeData }) + * if (Option.isSome(result)) { + * console.log(result.value.path) // [0, 1, 2] - shortest path A->B->C + * console.log(result.value.distance) // 2 - total distance + * } + * ``` + * + * @since 3.18.0 + * @category algorithms + */ +export const bellmanFord = ( + graph: Graph | MutableGraph, + config: BellmanFordConfig +): Option.Option> => { + const { cost, source, target } = config + // Validate that source and target nodes exist + if (!graph.nodes.has(source)) { + throw missingNode(source) + } + if (!graph.nodes.has(target)) { + throw missingNode(target) + } + + // Early return if source equals target + if (source === target) { + return Option.some({ + path: [source], + distance: 0, + costs: [] + }) + } + + // Initialize distances and predecessors + const distances = new Map() + const previous = new Map() + // Iterate directly over node keys + + for (const node of graph.nodes.keys()) { + distances.set(node, node === source ? 0 : Infinity) + previous.set(node, null) + } + + // Collect all edges for relaxation + const edges: Array<{ source: NodeIndex; target: NodeIndex; weight: number; edgeData: E }> = [] + for (const [, edgeData] of graph.edges) { + const weight = cost(edgeData.data) + edges.push({ + source: edgeData.source, + target: edgeData.target, + weight, + edgeData: edgeData.data + }) + } + + // Relax edges up to V-1 times + const nodeCount = graph.nodes.size + for (let i = 0; i < nodeCount - 1; i++) { + let hasUpdate = false + + for (const edge of edges) { + const sourceDistance = distances.get(edge.source)! + const targetDistance = distances.get(edge.target)! + + // Relaxation step + if (sourceDistance !== Infinity && sourceDistance + edge.weight < targetDistance) { + distances.set(edge.target, sourceDistance + edge.weight) + previous.set(edge.target, { node: edge.source, edgeData: edge.edgeData }) + hasUpdate = true + } + } + + // Early termination if no updates + if (!hasUpdate) { + break + } + } + + // Check for negative cycles + for (const edge of edges) { + const sourceDistance = distances.get(edge.source)! + const targetDistance = distances.get(edge.target)! + + if (sourceDistance !== Infinity && sourceDistance + edge.weight < targetDistance) { + // Negative cycle detected - check if it affects the path to target + const affectedNodes = new Set() + const queue = [edge.target] + + while (queue.length > 0) { + const node = queue.shift()! + if (affectedNodes.has(node)) continue + affectedNodes.add(node) + + // Add all nodes reachable from this node + const adjacencyList = graph.adjacency.get(node) + if (adjacencyList !== undefined) { + for (const edgeIndex of adjacencyList) { + const edge = graph.edges.get(edgeIndex) + if (edge !== undefined) { + queue.push(edge.target) + } + } + } + } + + // If target is affected by negative cycle, return null + if (affectedNodes.has(target)) { + return Option.none() + } + } + } + + // Check if target is reachable + const targetDistance = distances.get(target)! + if (targetDistance === Infinity) { + return Option.none() // No path exists + } + + // Reconstruct path + const path: Array = [] + const costs: Array = [] + let currentNode: NodeIndex | null = target + + while (currentNode !== null) { + path.unshift(currentNode) + const prev: { node: NodeIndex; edgeData: E } | null = previous.get(currentNode)! + if (prev !== null) { + costs.unshift(prev.edgeData) + currentNode = prev.node + } else { + currentNode = null + } + } + + return Option.some({ + path, + distance: targetDistance, + costs + }) +} + +/** + * Concrete class for iterables that produce [NodeIndex, NodeData] tuples. + * + * This class provides a common abstraction for all iterables that return node data, + * including traversal iterators (DFS, BFS, etc.) and element iterators (nodes, externals). + * It uses a mapEntry function pattern for flexible iteration and transformation. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * Graph.addEdge(mutable, a, b, 1) + * }) + * + * // Both traversal and element iterators return NodeWalker + * const dfsNodes: Graph.NodeWalker = Graph.dfs(graph, { start: [0] }) + * const allNodes: Graph.NodeWalker = Graph.nodes(graph) + * + * // Common interface for working with node iterables + * function processNodes(nodeIterable: Graph.NodeWalker): Array { + * return Array.from(Graph.indices(nodeIterable)) + * } + * + * // Access node data using values() or entries() + * const nodeData = Array.from(Graph.values(dfsNodes)) // ["A", "B"] + * const nodeEntries = Array.from(Graph.entries(allNodes)) // [[0, "A"], [1, "B"]] + * ``` + * + * @since 3.18.0 + * @category models + */ +export class Walker implements Iterable<[T, N]> { + /** + * @since 3.18.0 + */ + // @ts-ignore + readonly [Symbol.iterator]: () => Iterator<[T, N]> + + /** + * Visits each element and maps it to a value using the provided function. + * + * Takes a function that receives the index and data, + * and returns an iterable of the mapped values. Skips elements that + * no longer exist in the graph. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * Graph.addEdge(mutable, a, b, 1) + * }) + * + * const dfs = Graph.dfs(graph, { start: [0] }) + * + * // Map to just the node data + * const values = Array.from(dfs.visit((index, data) => data)) + * console.log(values) // ["A", "B"] + * + * // Map to custom objects + * const custom = Array.from(dfs.visit((index, data) => ({ id: index, name: data }))) + * console.log(custom) // [{ id: 0, name: "A" }, { id: 1, name: "B" }] + * ``` + * + * @since 3.18.0 + * @category iterators + */ + readonly visit: (f: (index: T, data: N) => U) => Iterable + + constructor( + /** + * Visits each element and maps it to a value using the provided function. + * + * Takes a function that receives the index and data, + * and returns an iterable of the mapped values. Skips elements that + * no longer exist in the graph. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * Graph.addEdge(mutable, a, b, 1) + * }) + * + * const dfs = Graph.dfs(graph, { start: [0] }) + * + * // Map to just the node data + * const values = Array.from(dfs.visit((index, data) => data)) + * console.log(values) // ["A", "B"] + * + * // Map to custom objects + * const custom = Array.from(dfs.visit((index, data) => ({ id: index, name: data }))) + * console.log(custom) // [{ id: 0, name: "A" }, { id: 1, name: "B" }] + * ``` + * + * @since 3.18.0 + * @category iterators + */ + visit: (f: (index: T, data: N) => U) => Iterable + ) { + this.visit = visit + this[Symbol.iterator] = visit((index, data) => [index, data] as [T, N])[Symbol.iterator] + } +} + +/** + * Type alias for node iteration using Walker. + * NodeWalker is represented as Walker. + * + * @since 3.18.0 + * @category models + */ +export type NodeWalker = Walker + +/** + * Type alias for edge iteration using Walker. + * EdgeWalker is represented as Walker>. + * + * @since 3.18.0 + * @category models + */ +export type EdgeWalker = Walker> + +/** + * Returns an iterator over the indices in the walker. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * Graph.addEdge(mutable, a, b, 1) + * }) + * + * const dfs = Graph.dfs(graph, { start: [0] }) + * const indices = Array.from(Graph.indices(dfs)) + * console.log(indices) // [0, 1] + * ``` + * + * @since 3.18.0 + * @category utilities + */ +export const indices = (walker: Walker): Iterable => walker.visit((index, _) => index) + +/** + * Returns an iterator over the values (data) in the walker. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * Graph.addEdge(mutable, a, b, 1) + * }) + * + * const dfs = Graph.dfs(graph, { start: [0] }) + * const values = Array.from(Graph.values(dfs)) + * console.log(values) // ["A", "B"] + * ``` + * + * @since 3.18.0 + * @category utilities + */ +export const values = (walker: Walker): Iterable => walker.visit((_, data) => data) + +/** + * Returns an iterator over [index, data] entries in the walker. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * Graph.addEdge(mutable, a, b, 1) + * }) + * + * const dfs = Graph.dfs(graph, { start: [0] }) + * const entries = Array.from(Graph.entries(dfs)) + * console.log(entries) // [[0, "A"], [1, "B"]] + * ``` + * + * @since 3.18.0 + * @category utilities + */ +export const entries = (walker: Walker): Iterable<[T, N]> => + walker.visit((index, data) => [index, data] as [T, N]) + +/** + * Configuration for graph search iterators. + * + * @since 3.18.0 + * @category models + */ +export interface SearchConfig { + readonly start?: Array + readonly direction?: Direction +} + +/** + * Creates a new DFS iterator with optional configuration. + * + * The iterator maintains a stack of nodes to visit and tracks discovered nodes. + * It provides lazy evaluation of the depth-first search. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, 1) + * Graph.addEdge(mutable, b, c, 1) + * }) + * + * // Start from a specific node + * const dfs1 = Graph.dfs(graph, { start: [0] }) + * for (const nodeIndex of Graph.indices(dfs1)) { + * console.log(nodeIndex) // Traverses in DFS order: 0, 1, 2 + * } + * + * // Empty iterator (no starting nodes) + * const dfs2 = Graph.dfs(graph) + * // Can be used programmatically + * ``` + * + * @since 3.18.0 + * @category iterators + */ +export const dfs = ( + graph: Graph | MutableGraph, + config: SearchConfig = {} +): NodeWalker => { + const start = config.start ?? [] + const direction = config.direction ?? "outgoing" + + // Validate that all start nodes exist + for (const nodeIndex of start) { + if (!hasNode(graph, nodeIndex)) { + throw missingNode(nodeIndex) + } + } + + return new Walker((f) => ({ + [Symbol.iterator]: () => { + const stack = [...start] + const discovered = new Set() + + const nextMapped = () => { + while (stack.length > 0) { + const current = stack.pop()! + + if (discovered.has(current)) { + continue + } + + discovered.add(current) + + const nodeDataOption = graph.nodes.get(current) + if (nodeDataOption === undefined) { + continue + } + + const neighbors = neighborsDirected(graph, current, direction) + for (let i = neighbors.length - 1; i >= 0; i--) { + const neighbor = neighbors[i] + if (!discovered.has(neighbor)) { + stack.push(neighbor) + } + } + + return { done: false, value: f(current, nodeDataOption) } + } + + return { done: true, value: undefined } as const + } + + return { next: nextMapped } + } + })) +} + +/** + * Creates a new BFS iterator with optional configuration. + * + * The iterator maintains a queue of nodes to visit and tracks discovered nodes. + * It provides lazy evaluation of the breadth-first search. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, 1) + * Graph.addEdge(mutable, b, c, 1) + * }) + * + * // Start from a specific node + * const bfs1 = Graph.bfs(graph, { start: [0] }) + * for (const nodeIndex of Graph.indices(bfs1)) { + * console.log(nodeIndex) // Traverses in BFS order: 0, 1, 2 + * } + * + * // Empty iterator (no starting nodes) + * const bfs2 = Graph.bfs(graph) + * // Can be used programmatically + * ``` + * + * @since 3.18.0 + * @category iterators + */ +export const bfs = ( + graph: Graph | MutableGraph, + config: SearchConfig = {} +): NodeWalker => { + const start = config.start ?? [] + const direction = config.direction ?? "outgoing" + + // Validate that all start nodes exist + for (const nodeIndex of start) { + if (!hasNode(graph, nodeIndex)) { + throw missingNode(nodeIndex) + } + } + + return new Walker((f) => ({ + [Symbol.iterator]: () => { + const queue = [...start] + const discovered = new Set() + + const nextMapped = () => { + while (queue.length > 0) { + const current = queue.shift()! + + if (!discovered.has(current)) { + discovered.add(current) + + const neighbors = neighborsDirected(graph, current, direction) + for (const neighbor of neighbors) { + if (!discovered.has(neighbor)) { + queue.push(neighbor) + } + } + + const nodeData = getNode(graph, current) + if (Option.isSome(nodeData)) { + return { done: false, value: f(current, nodeData.value) } + } + return nextMapped() + } + } + + return { done: true, value: undefined } as const + } + + return { next: nextMapped } + } + })) +} + +/** + * Configuration options for topological sort iterator. + * + * @since 3.18.0 + * @category models + */ +export interface TopoConfig { + readonly initials?: Array +} + +/** + * Creates a new topological sort iterator with optional configuration. + * + * The iterator uses Kahn's algorithm to lazily produce nodes in topological order. + * Throws an error if the graph contains cycles. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, 1) + * Graph.addEdge(mutable, b, c, 1) + * }) + * + * // Standard topological sort + * const topo1 = Graph.topo(graph) + * for (const nodeIndex of Graph.indices(topo1)) { + * console.log(nodeIndex) // 0, 1, 2 (topological order) + * } + * + * // With initial nodes + * const topo2 = Graph.topo(graph, { initials: [0] }) + * + * // Throws error for cyclic graph + * const cyclicGraph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * Graph.addEdge(mutable, a, b, 1) + * Graph.addEdge(mutable, b, a, 2) // Creates cycle + * }) + * + * try { + * Graph.topo(cyclicGraph) // Throws: "Cannot perform topological sort on cyclic graph" + * } catch (error) { + * console.log((error as Error).message) + * } + * ``` + * + * @since 3.18.0 + * @category iterators + */ +export const topo = ( + graph: Graph | MutableGraph, + config: TopoConfig = {} +): NodeWalker => { + // Check if graph is acyclic first + if (!isAcyclic(graph)) { + throw new Error("Cannot perform topological sort on cyclic graph") + } + + const initials = config.initials ?? [] + + // Validate that all initial nodes exist + for (const nodeIndex of initials) { + if (!hasNode(graph, nodeIndex)) { + throw missingNode(nodeIndex) + } + } + + return new Walker((f) => ({ + [Symbol.iterator]: () => { + const inDegree = new Map() + const remaining = new Set() + const queue = [...initials] + + // Initialize in-degree counts + for (const [nodeIndex] of graph.nodes) { + inDegree.set(nodeIndex, 0) + remaining.add(nodeIndex) + } + + // Calculate in-degrees + for (const [, edgeData] of graph.edges) { + const currentInDegree = inDegree.get(edgeData.target) || 0 + inDegree.set(edgeData.target, currentInDegree + 1) + } + + // Add nodes with zero in-degree to queue if no initials provided + if (initials.length === 0) { + for (const [nodeIndex, degree] of inDegree) { + if (degree === 0) { + queue.push(nodeIndex) + } + } + } + + const nextMapped = () => { + while (queue.length > 0) { + const current = queue.shift()! + + if (remaining.has(current)) { + remaining.delete(current) + + // Process outgoing edges, reducing in-degree of targets + const neighbors = neighborsDirected(graph, current, "outgoing") + for (const neighbor of neighbors) { + if (remaining.has(neighbor)) { + const currentInDegree = inDegree.get(neighbor) || 0 + const newInDegree = currentInDegree - 1 + inDegree.set(neighbor, newInDegree) + + // If in-degree becomes 0, add to queue + if (newInDegree === 0) { + queue.push(neighbor) + } + } + } + + const nodeData = getNode(graph, current) + if (Option.isSome(nodeData)) { + return { done: false, value: f(current, nodeData.value) } + } + return nextMapped() + } + } + + return { done: true, value: undefined } as const + } + + return { next: nextMapped } + } + })) +} + +/** + * Creates a new DFS postorder iterator with optional configuration. + * + * The iterator maintains a stack with visit state tracking and emits nodes + * in postorder (after all descendants have been processed). Essential for + * dependency resolution and tree destruction algorithms. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const root = Graph.addNode(mutable, "root") + * const child1 = Graph.addNode(mutable, "child1") + * const child2 = Graph.addNode(mutable, "child2") + * Graph.addEdge(mutable, root, child1, 1) + * Graph.addEdge(mutable, root, child2, 1) + * }) + * + * // Postorder: children before parents + * const postOrder = Graph.dfsPostOrder(graph, { start: [0] }) + * for (const node of postOrder) { + * console.log(node) // 1, 2, 0 + * } + * ``` + * + * @since 3.18.0 + * @category iterators + */ +export const dfsPostOrder = ( + graph: Graph | MutableGraph, + config: SearchConfig = {} +): NodeWalker => { + const start = config.start ?? [] + const direction = config.direction ?? "outgoing" + + // Validate that all start nodes exist + for (const nodeIndex of start) { + if (!hasNode(graph, nodeIndex)) { + throw missingNode(nodeIndex) + } + } + + return new Walker((f) => ({ + [Symbol.iterator]: () => { + const stack: Array<{ node: NodeIndex; visitedChildren: boolean }> = [] + const discovered = new Set() + const finished = new Set() + + // Initialize stack with start nodes + for (let i = start.length - 1; i >= 0; i--) { + stack.push({ node: start[i], visitedChildren: false }) + } + + const nextMapped = () => { + while (stack.length > 0) { + const current = stack[stack.length - 1] + + if (!discovered.has(current.node)) { + discovered.add(current.node) + current.visitedChildren = false + } + + if (!current.visitedChildren) { + current.visitedChildren = true + const neighbors = neighborsDirected(graph, current.node, direction) + + for (let i = neighbors.length - 1; i >= 0; i--) { + const neighbor = neighbors[i] + if (!discovered.has(neighbor) && !finished.has(neighbor)) { + stack.push({ node: neighbor, visitedChildren: false }) + } + } + } else { + const nodeToEmit = stack.pop()!.node + + if (!finished.has(nodeToEmit)) { + finished.add(nodeToEmit) + + const nodeData = getNode(graph, nodeToEmit) + if (Option.isSome(nodeData)) { + return { done: false, value: f(nodeToEmit, nodeData.value) } + } + return nextMapped() + } + } + } + + return { done: true, value: undefined } as const + } + + return { next: nextMapped } + } + })) +} + +/** + * Creates an iterator over all node indices in the graph. + * + * The iterator produces node indices in the order they were added to the graph. + * This provides access to all nodes regardless of connectivity. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, 1) + * }) + * + * const indices = Array.from(Graph.indices(Graph.nodes(graph))) + * console.log(indices) // [0, 1, 2] + * ``` + * + * @since 3.18.0 + * @category iterators + */ +export const nodes = ( + graph: Graph | MutableGraph +): NodeWalker => + new Walker((f) => ({ + [Symbol.iterator]() { + const nodeMap = graph.nodes + const iterator = nodeMap.entries() + + return { + next() { + const result = iterator.next() + if (result.done) { + return { done: true, value: undefined } + } + const [nodeIndex, nodeData] = result.value + return { done: false, value: f(nodeIndex, nodeData) } + } + } + } + })) + +/** + * Creates an iterator over all edge indices in the graph. + * + * The iterator produces edge indices in the order they were added to the graph. + * This provides access to all edges regardless of connectivity. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const a = Graph.addNode(mutable, "A") + * const b = Graph.addNode(mutable, "B") + * const c = Graph.addNode(mutable, "C") + * Graph.addEdge(mutable, a, b, 1) + * Graph.addEdge(mutable, b, c, 2) + * }) + * + * const indices = Array.from(Graph.indices(Graph.edges(graph))) + * console.log(indices) // [0, 1] + * ``` + * + * @since 3.18.0 + * @category iterators + */ +export const edges = ( + graph: Graph | MutableGraph +): EdgeWalker => + new Walker((f) => ({ + [Symbol.iterator]() { + const edgeMap = graph.edges + const iterator = edgeMap.entries() + + return { + next() { + const result = iterator.next() + if (result.done) { + return { done: true, value: undefined } + } + const [edgeIndex, edgeData] = result.value + return { done: false, value: f(edgeIndex, edgeData) } + } + } + } + })) + +/** + * Configuration for externals iterator. + * + * @since 3.18.0 + * @category models + */ +export interface ExternalsConfig { + readonly direction?: Direction +} + +/** + * Creates an iterator over external nodes (nodes without edges in specified direction). + * + * External nodes are nodes that have no outgoing edges (direction="outgoing") or + * no incoming edges (direction="incoming"). These are useful for finding + * sources, sinks, or isolated nodes. + * + * @example + * ```ts + * import { Graph } from "effect" + * + * const graph = Graph.directed((mutable) => { + * const source = Graph.addNode(mutable, "source") // 0 - no incoming + * const middle = Graph.addNode(mutable, "middle") // 1 - has both + * const sink = Graph.addNode(mutable, "sink") // 2 - no outgoing + * const isolated = Graph.addNode(mutable, "isolated") // 3 - no edges + * + * Graph.addEdge(mutable, source, middle, 1) + * Graph.addEdge(mutable, middle, sink, 2) + * }) + * + * // Nodes with no outgoing edges (sinks + isolated) + * const sinks = Array.from(Graph.indices(Graph.externals(graph, { direction: "outgoing" }))) + * console.log(sinks) // [2, 3] + * + * // Nodes with no incoming edges (sources + isolated) + * const sources = Array.from(Graph.indices(Graph.externals(graph, { direction: "incoming" }))) + * console.log(sources) // [0, 3] + * ``` + * + * @since 3.18.0 + * @category iterators + */ +export const externals = ( + graph: Graph | MutableGraph, + config: ExternalsConfig = {} +): NodeWalker => { + const direction = config.direction ?? "outgoing" + + return new Walker((f) => ({ + [Symbol.iterator]: () => { + const nodeMap = graph.nodes + const adjacencyMap = direction === "incoming" + ? graph.reverseAdjacency + : graph.adjacency + + const nodeIterator = nodeMap.entries() + + const nextMapped = () => { + let current = nodeIterator.next() + while (!current.done) { + const [nodeIndex, nodeData] = current.value + const adjacencyList = adjacencyMap.get(nodeIndex) + + // Node is external if it has no edges in the specified direction + if (adjacencyList === undefined || adjacencyList.length === 0) { + return { done: false, value: f(nodeIndex, nodeData) } + } + current = nodeIterator.next() + } + + return { done: true, value: undefined } as const + } + + return { next: nextMapped } + } + })) +} diff --git a/backend/node_modules/effect/src/Hash.ts b/backend/node_modules/effect/src/Hash.ts new file mode 100644 index 0000000000000000000000000000000000000000..cdbbba363b06655282fc5f185b4da03b171f9d8b --- /dev/null +++ b/backend/node_modules/effect/src/Hash.ts @@ -0,0 +1,203 @@ +/** + * @since 2.0.0 + */ +import { pipe } from "./Function.js" +import { globalValue } from "./GlobalValue.js" +import { hasProperty } from "./Predicate.js" +import { structuralRegionState } from "./Utils.js" + +/** @internal */ +const randomHashCache = globalValue( + Symbol.for("effect/Hash/randomHashCache"), + () => new WeakMap() +) + +/** + * @since 2.0.0 + * @category symbols + */ +export const symbol: unique symbol = Symbol.for("effect/Hash") + +/** + * @since 2.0.0 + * @category models + */ +export interface Hash { + [symbol](): number +} + +/** + * @since 2.0.0 + * @category hashing + */ +export const hash:
(self: A) => number = (self: A) => { + if (structuralRegionState.enabled === true) { + return 0 + } + + switch (typeof self) { + case "number": + return number(self) + case "bigint": + return string(self.toString(10)) + case "boolean": + return string(String(self)) + case "symbol": + return string(String(self)) + case "string": + return string(self) + case "undefined": + return string("undefined") + case "function": + case "object": { + if (self === null) { + return string("null") + } else if (self instanceof Date) { + if (Number.isNaN(self.getTime())) { + return string("Invalid Date") + } + return hash(self.toISOString()) + } else if (self instanceof URL) { + return hash(self.href) + } else if (isHash(self)) { + return self[symbol]() + } else { + return random(self) + } + } + default: + throw new Error( + `BUG: unhandled typeof ${typeof self} - please report an issue at https://github.com/Effect-TS/effect/issues` + ) + } +} + +/** + * @since 2.0.0 + * @category hashing + */ +export const random: (self: A) => number = (self) => { + if (!randomHashCache.has(self)) { + randomHashCache.set(self, number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))) + } + return randomHashCache.get(self)! +} + +/** + * @since 2.0.0 + * @category hashing + */ +export const combine: (b: number) => (self: number) => number = (b) => (self) => (self * 53) ^ b + +/** + * @since 2.0.0 + * @category hashing + */ +export const optimize = (n: number): number => (n & 0xbfffffff) | ((n >>> 1) & 0x40000000) + +/** + * @since 2.0.0 + * @category guards + */ +export const isHash = (u: unknown): u is Hash => hasProperty(u, symbol) + +/** + * @since 2.0.0 + * @category hashing + */ +export const number = (n: number) => { + if (n !== n || n === Infinity) { + return 0 + } + let h = n | 0 + if (h !== n) { + h ^= n * 0xffffffff + } + while (n > 0xffffffff) { + h ^= n /= 0xffffffff + } + return optimize(h) +} + +/** + * @since 2.0.0 + * @category hashing + */ +export const string = (str: string) => { + let h = 5381, i = str.length + while (i) { + h = (h * 33) ^ str.charCodeAt(--i) + } + return optimize(h) +} + +/** + * @since 2.0.0 + * @category hashing + */ +export const structureKeys = (o: A, keys: ReadonlyArray) => { + let h = 12289 + for (let i = 0; i < keys.length; i++) { + h ^= pipe(string(keys[i]! as string), combine(hash((o as any)[keys[i]!]))) + } + return optimize(h) +} + +/** + * @since 2.0.0 + * @category hashing + */ +export const structure = (o: A) => + structureKeys(o, Object.keys(o) as unknown as ReadonlyArray) + +/** + * @since 2.0.0 + * @category hashing + */ +export const array = (arr: ReadonlyArray) => { + let h = 6151 + for (let i = 0; i < arr.length; i++) { + h = pipe(h, combine(hash(arr[i]))) + } + return optimize(h) +} + +/** + * @since 2.0.0 + * @category hashing + */ +export const cached: { + /** + * @since 2.0.0 + * @category hashing + */ + (self: object): (hash: number) => number + /** + * @since 2.0.0 + * @category hashing + */ + (self: object, hash: number): number +} = function() { + if (arguments.length === 1) { + const self = arguments[0] as object + return function(hash: number) { + Object.defineProperty(self, symbol, { + value() { + return hash + }, + enumerable: false + }) + return hash + } as any + } + const self = arguments[0] as object + const hash = arguments[1] as number + Object.defineProperty(self, symbol, { + value() { + return hash + }, + enumerable: false + }) + + return hash +} diff --git a/backend/node_modules/effect/src/HashRing.ts b/backend/node_modules/effect/src/HashRing.ts new file mode 100644 index 0000000000000000000000000000000000000000..7bdae0ba167580089ba11f41ab24a46c33069cd8 --- /dev/null +++ b/backend/node_modules/effect/src/HashRing.ts @@ -0,0 +1,387 @@ +/** + * @since 3.19.0 + * @experimental + */ +import { dual } from "./Function.js" +import * as Hash from "./Hash.js" +import * as Inspectable from "./Inspectable.js" +import * as Iterable from "./Iterable.js" +import { type Pipeable, pipeArguments } from "./Pipeable.js" +import { hasProperty } from "./Predicate.js" +import * as PrimaryKey from "./PrimaryKey.js" + +const TypeId = "~effect/cluster/HashRing" as const + +/** + * @since 3.19.0 + * @category Models + * @experimental + */ +export interface HashRing extends Pipeable, Iterable { + readonly [TypeId]: typeof TypeId + readonly baseWeight: number + totalWeightCache: number + readonly nodes: Map + ring: Array<[hash: number, node: string]> +} + +/** + * @since 3.19.0 + * @category Guards + * @experimental + */ +export const isHashRing = (u: unknown): u is HashRing => hasProperty(u, TypeId) + +/** + * @since 3.19.0 + * @category Constructors + * @experimental + */ +export const make = (options?: { + readonly baseWeight?: number | undefined +}): HashRing => { + const self = Object.create(Proto) + self.baseWeight = Math.max(options?.baseWeight ?? 128, 1) + self.totalWeightCache = 0 + self.nodes = new Map() + self.ring = [] + return self +} + +const Proto = { + [TypeId]: TypeId, + [Symbol.iterator](this: HashRing): Iterator { + return Iterable.map(this.nodes.values(), ([n]) => n)[Symbol.iterator]() + }, + pipe() { + return pipeArguments(this, arguments) + }, + ...Inspectable.BaseProto, + toJSON(this: HashRing) { + return { + _id: "HashRing", + baseWeight: this.baseWeight, + nodes: this.ring.map(([, n]) => this.nodes.get(n)![0]) + } + } +} + +/** + * Add new nodes to the ring. If a node already exists in the ring, it + * will be updated. For example, you can use this to update the node's weight. + * + * @since 3.19.0 + * @category Combinators + * @experimental + */ +export const addMany: { + /** + * Add new nodes to the ring. If a node already exists in the ring, it + * will be updated. For example, you can use this to update the node's weight. + * + * @since 3.19.0 + * @category Combinators + * @experimental + */ + ( + nodes: Iterable, + options?: { + readonly weight?: number | undefined + } + ): (self: HashRing) => HashRing + /** + * Add new nodes to the ring. If a node already exists in the ring, it + * will be updated. For example, you can use this to update the node's weight. + * + * @since 3.19.0 + * @category Combinators + * @experimental + */ + ( + self: HashRing, + nodes: Iterable, + options?: { + readonly weight?: number | undefined + } + ): HashRing +} = dual( + (args) => isHashRing(args[0]), + (self: HashRing, nodes: Iterable, options?: { + readonly weight?: number | undefined + }): HashRing => { + const weight = Math.max(options?.weight ?? 1, 0.1) + const keys: Array = [] + let toRemove: Set | undefined + for (const node of nodes) { + const key = PrimaryKey.value(node) + const entry = self.nodes.get(key) + if (entry) { + if (entry[1] === weight) continue + toRemove ??= new Set() + toRemove.add(key) + self.totalWeightCache -= entry[1] + self.totalWeightCache += weight + entry[1] = weight + } else { + self.nodes.set(key, [node, weight]) + self.totalWeightCache += weight + } + keys.push(key) + } + if (toRemove) { + self.ring = self.ring.filter(([, n]) => !toRemove.has(n)) + } + addNodesToRing(self, keys, Math.round(weight * self.baseWeight)) + return self + } +) + +function addNodesToRing(self: HashRing, keys: Array, weight: number) { + for (let i = weight; i > 0; i--) { + for (let j = 0; j < keys.length; j++) { + const key = keys[j] + self.ring.push([ + Hash.string(`${key}:${i}`), + key + ]) + } + } + self.ring.sort((a, b) => a[0] - b[0]) +} + +/** + * Add a new node to the ring. If the node already exists in the ring, it + * will be updated. For example, you can use this to update the node's weight. + * + * @since 3.19.0 + * @category Combinators + * @experimental + */ +export const add: { + /** + * Add a new node to the ring. If the node already exists in the ring, it + * will be updated. For example, you can use this to update the node's weight. + * + * @since 3.19.0 + * @category Combinators + * @experimental + */ + ( + node: A, + options?: { + readonly weight?: number | undefined + } + ): (self: HashRing) => HashRing + /** + * Add a new node to the ring. If the node already exists in the ring, it + * will be updated. For example, you can use this to update the node's weight. + * + * @since 3.19.0 + * @category Combinators + * @experimental + */ + ( + self: HashRing, + node: A, + options?: { + readonly weight?: number | undefined + } + ): HashRing +} = dual((args) => isHashRing(args[0]), (self: HashRing, node: A, options?: { + readonly weight?: number | undefined +}): HashRing => addMany(self, [node], options)) + +/** + * Removes the node from the ring. No-op's if the node does not exist. + * + * @since 3.19.0 + * @category Combinators + * @experimental + */ +export const remove: { + /** + * Removes the node from the ring. No-op's if the node does not exist. + * + * @since 3.19.0 + * @category Combinators + * @experimental + */ + (node: A): (self: HashRing) => HashRing + /** + * Removes the node from the ring. No-op's if the node does not exist. + * + * @since 3.19.0 + * @category Combinators + * @experimental + */ + (self: HashRing, node: A): HashRing +} = dual(2, (self: HashRing, node: A): HashRing => { + const key = PrimaryKey.value(node) + const entry = self.nodes.get(key) + if (entry) { + self.nodes.delete(key) + self.ring = self.ring.filter(([, n]) => n !== key) + self.totalWeightCache -= entry[1] + } + return self +}) + +/** + * @since 3.19.0 + * @category Combinators + * @experimental + */ +export const has: { + /** + * @since 3.19.0 + * @category Combinators + * @experimental + */ + (node: A): (self: HashRing) => boolean + /** + * @since 3.19.0 + * @category Combinators + * @experimental + */ + (self: HashRing, node: A): boolean +} = dual( + 2, + (self: HashRing, node: A): boolean => self.nodes.has(PrimaryKey.value(node)) +) + +/** + * Gets the node which should handle the given input. Returns undefined if + * the hashring has no elements with weight. + * + * @since 3.19.0 + * @category Combinators + * @experimental + */ +export const get = (self: HashRing, input: string): A | undefined => { + if (self.ring.length === 0) { + return undefined + } + const index = getIndexForInput(self, Hash.string(input))[0] + const node = self.ring[index][1]! + return self.nodes.get(node)![0] +} + +/** + * Distributes `count` shards across the nodes in the ring, attempting to + * balance the number of shards allocated to each node. Returns undefined if + * the hashring has no elements with weight. + * + * @since 3.19.0 + * @category Combinators + * @experimental + */ +export const getShards = (self: HashRing, count: number): Array | undefined => { + if (self.ring.length === 0) { + return undefined + } + + const shards = new Array(count) + + // for tracking how many shards have been allocated to each node + const allocations = new Map() + // for tracking which shards still need to be allocated + const remaining = new Set() + // for tracking which nodes have reached the max allocation + const exclude = new Set() + + // First pass - allocate the closest nodes, skipping nodes that have reached + // max + const distances = new Array<[shard: number, node: string, distance: number]>(count) + for (let shard = 0; shard < count; shard++) { + const hash = (shardHashes[shard] ??= Hash.string(`shard-${shard}`)) + const [index, distance] = getIndexForInput(self, hash) + const node = self.ring[index][1]! + distances[shard] = [shard, node, distance] + remaining.add(shard) + } + distances.sort((a, b) => a[2] - b[2]) + for (let i = 0; i < count; i++) { + const [shard, node] = distances[i] + if (exclude.has(node)) continue + const [value, weight] = self.nodes.get(node)! + shards[shard] = value + remaining.delete(shard) + const nodeCount = (allocations.get(node) ?? 0) + 1 + allocations.set(node, nodeCount) + const maxPerNode = Math.max(1, Math.floor(count * (weight / self.totalWeightCache))) + if (nodeCount >= maxPerNode) { + exclude.add(node) + } + } + + // Second pass - allocate any remaining shards, skipping nodes that have + // reached max + let allAtMax = exclude.size === self.nodes.size + remaining.forEach((shard) => { + const index = getIndexForInput(self, shardHashes[shard], allAtMax ? undefined : exclude)[0] + const node = self.ring[index][1] + const [value, weight] = self.nodes.get(node)! + shards[shard] = value + + if (allAtMax) return + const nodeCount = (allocations.get(node) ?? 0) + 1 + allocations.set(node, nodeCount) + const maxPerNode = Math.max(1, Math.floor(count * (weight / self.totalWeightCache))) + if (nodeCount >= maxPerNode) { + exclude.add(node) + if (exclude.size === self.nodes.size) { + allAtMax = true + } + } + }) + + return shards +} + +const shardHashes: Array = [] + +function getIndexForInput( + self: HashRing, + hash: number, + exclude?: ReadonlySet | undefined +): readonly [index: number, distance: number] { + const ring = self.ring + const len = ring.length + + let mid: number + let lo = 0 + let hi = len - 1 + + while (lo <= hi) { + mid = ((lo + hi) / 2) >>> 0 + if (ring[mid][0] >= hash) { + hi = mid - 1 + } else { + lo = mid + 1 + } + } + const a = lo === len ? lo - 1 : lo + const distA = Math.abs(ring[a][0] - hash) + if (exclude === undefined) { + const b = lo - 1 + if (b < 0) { + return [a, distA] + } + const distB = Math.abs(ring[b][0] - hash) + return distA <= distB ? [a, distA] : [b, distB] + } else if (!exclude.has(ring[a][1])) { + return [a, distA] + } + const range = Math.max(lo, len - lo) + for (let i = 1; i < range; i++) { + let index = lo - i + if (index >= 0 && index < len && !exclude.has(ring[index][1])) { + return [index, Math.abs(ring[index][0] - hash)] + } + index = lo + i + if (index >= 0 && index < len && !exclude.has(ring[index][1])) { + return [index, Math.abs(ring[index][0] - hash)] + } + } + return [a, distA] +} diff --git a/backend/node_modules/effect/src/HashSet.ts b/backend/node_modules/effect/src/HashSet.ts new file mode 100644 index 0000000000000000000000000000000000000000..70b70e8121603877a4fbb15e586bd7e5a00fff68 --- /dev/null +++ b/backend/node_modules/effect/src/HashSet.ts @@ -0,0 +1,2346 @@ +/** + * # HashSet + * + * An immutable `HashSet` provides a collection of unique values with efficient + * lookup, insertion and removal. Once created, a `HashSet` cannot be modified; + * any operation that would alter the set instead returns a new `HashSet` with + * the changes. This immutability offers benefits like predictable state + * management and easier reasoning about your code. + * + * ## What Problem Does It Solve? + * + * `HashSet` solves the problem of maintaining an unsorted collection where each + * value appears exactly once, with fast operations for checking membership and + * adding/removing values. + * + * ## When to Use + * + * Use `HashSet` when you need: + * + * - A collection with no duplicate values + * - Efficient membership testing (**`O(1)`** average complexity) + * - Set operations like union, intersection, and difference + * - An immutable data structure that preserves functional programming patterns + * + * ## Advanced Features + * + * HashSet provides operations for: + * + * - Transforming sets with map and flatMap + * - Filtering elements with filter + * - Combining sets with union, intersection and difference + * - Performance optimizations via mutable operations in controlled contexts + * + * ## Performance Characteristics + * + * - **Lookup** operations ({@link module:HashSet.has}): **`O(1)`** average time + * complexity + * - **Insertion** operations ({@link module:HashSet.add}): **`O(1)`** average time + * complexity + * - **Removal** operations ({@link module:HashSet.remove}): **`O(1)`** average + * time complexity + * - **Set** operations ({@link module:HashSet.union}, + * {@link module:HashSet.intersection}): **`O(n)`** where n is the size of the + * smaller set + * - **Iteration**: **`O(n)`** where n is the size of the set + * + * The HashSet data structure implements the following traits: + * + * - {@link Iterable}: allows iterating over the values in the set + * - {@link Equal}: allows comparing two sets for value-based equality + * - {@link Pipeable}: allows chaining operations with the pipe operator + * - {@link Inspectable}: allows inspecting the contents of the set + * + * ## Operations Reference + * + * | Category | Operation | Description | Complexity | + * | ------------ | ----------------------------------- | ------------------------------------------- | ---------- | + * | constructors | {@link module:HashSet.empty} | Creates an empty HashSet | O(1) | + * | constructors | {@link module:HashSet.fromIterable} | Creates a HashSet from an iterable | O(n) | + * | constructors | {@link module:HashSet.make} | Creates a HashSet from multiple values | O(n) | + * | | | | | + * | elements | {@link module:HashSet.has} | Checks if a value exists in the set | O(1) avg | + * | elements | {@link module:HashSet.some} | Checks if any element satisfies a predicate | O(n) | + * | elements | {@link module:HashSet.every} | Checks if all elements satisfy a predicate | O(n) | + * | elements | {@link module:HashSet.isSubset} | Checks if a set is a subset of another | O(n) | + * | | | | | + * | getters | {@link module:HashSet.values} | Gets an iterator of all values | O(1) | + * | getters | {@link module:HashSet.toValues} | Gets an array of all values | O(n) | + * | getters | {@link module:HashSet.size} | Gets the number of elements | O(1) | + * | | | | | + * | mutations | {@link module:HashSet.add} | Adds a value to the set | O(1) avg | + * | mutations | {@link module:HashSet.remove} | Removes a value from the set | O(1) avg | + * | mutations | {@link module:HashSet.toggle} | Toggles a value's presence | O(1) avg | + * | | | | | + * | operations | {@link module:HashSet.difference} | Computes set difference (A - B) | O(n) | + * | operations | {@link module:HashSet.intersection} | Computes set intersection (A ∩ B) | O(n) | + * | operations | {@link module:HashSet.union} | Computes set union (A ∪ B) | O(n) | + * | | | | | + * | mapping | {@link module:HashSet.map} | Transforms each element | O(n) | + * | | | | | + * | sequencing | {@link module:HashSet.flatMap} | Transforms and flattens elements | O(n) | + * | | | | | + * | traversing | {@link module:HashSet.forEach} | Applies a function to each element | O(n) | + * | | | | | + * | folding | {@link module:HashSet.reduce} | Reduces the set to a single value | O(n) | + * | | | | | + * | filtering | {@link module:HashSet.filter} | Keeps elements that satisfy a predicate | O(n) | + * | | | | | + * | partitioning | {@link module:HashSet.partition} | Splits into two sets by a predicate | O(n) | + * + * ## Notes + * + * ### Composability with the Effect Ecosystem: + * + * This `HashSet` is designed to work seamlessly within the Effect ecosystem. It + * implements the {@link Iterable}, {@link Equal}, {@link Pipeable}, and + * {@link Inspectable} traits from Effect. This ensures compatibility with other + * Effect data structures and functionalities. For example, you can easily use + * Effect's `pipe` method to chain operations on the `HashSet`. + * + * **Equality of Elements with Effect's {@link Equal `Equal`} Trait:** + * + * This `HashSet` relies on Effect's {@link Equal} trait to determine the + * uniqueness of elements within the set. The way equality is checked depends on + * the type of the elements: + * + * - **Primitive Values:** For primitive JavaScript values like strings, numbers, + * booleans, `null`, and `undefined`, equality is determined by their value + * (similar to the `===` operator). + * - **Objects and Custom Types:** For objects and other custom types, equality is + * determined by whether those types implement the {@link Equal} interface + * themselves. If an element type implements `Equal`, the `HashSet` will + * delegate to that implementation to perform the equality check. This allows + * you to define custom logic for determining when two instances of your + * objects should be considered equal based on their properties, rather than + * just their object identity. + * + * ```ts + * import { Equal, Hash, HashSet } from "effect" + * + * class Person implements Equal.Equal { + * constructor( + * readonly id: number, // Unique identifier + * readonly name: string, + * readonly age: number + * ) {} + * + * // Define equality based on id, name, and age + * [Equal.symbol](that: Equal.Equal): boolean { + * if (that instanceof Person) { + * return ( + * Equal.equals(this.id, that.id) && + * Equal.equals(this.name, that.name) && + * Equal.equals(this.age, that.age) + * ) + * } + * return false + * } + * + * // Generate a hash code based on the unique id + * [Hash.symbol](): number { + * return Hash.hash(this.id) + * } + * } + * + * // Creating a HashSet with objects that implement the Equal interface + * const set = HashSet.empty().pipe( + * HashSet.add(new Person(1, "Alice", 30)), + * HashSet.add(new Person(1, "Alice", 30)) + * ) + * + * // HashSet recognizes them as equal, so only one element is stored + * console.log(HashSet.size(set)) + * // Output: 1 + * ``` + * + * **Simplifying Equality and Hashing with `Data` and `Schema`:** + * + * Effect's {@link Data} and {@link Schema `Schema.Data`} modules offer powerful + * ways to automatically handle the implementation of both the {@link Equal} and + * {@link Hash} traits for your custom data structures. + * + * - **`Data` Module:** By using constructors like `Data.struct`, `Data.tuple`, + * `Data.array`, or `Data.case` to define your data types, Effect + * automatically generates the necessary implementations for value-based + * equality and consistent hashing. This significantly reduces boilerplate and + * ensures correctness. + * + * ```ts + * import { HashSet, Data, Equal } from "effect" + * import assert from "node:assert/strict" + * + * // Data.* implements the `Equal` traits for us + * const person1 = Data.struct({ id: 1, name: "Alice", age: 30 }) + * const person2 = Data.struct({ id: 1, name: "Alice", age: 30 }) + * + * assert(Equal.equals(person1, person2)) + * + * const set = HashSet.empty().pipe( + * HashSet.add(person1), + * HashSet.add(person2) + * ) + * + * // HashSet recognizes them as equal, so only one element is stored + * console.log(HashSet.size(set)) // Output: 1 + * ``` + * + * - **`Schema` Module:** When defining data schemas using the {@link Schema} + * module, you can use `Schema.Data` to automatically include the `Equal` and + * `Hash` traits in the decoded objects. This is particularly important when + * working with `HashSet`. **For decoded objects to be correctly recognized as + * equal within a `HashSet`, ensure that the schema for those objects is + * defined using `Schema.Data`.** + * + * ```ts + * import { Equal, HashSet, Schema } from "effect" + * import assert from "node:assert/strict" + * + * // Schema.Data implements the `Equal` traits for us + * const PersonSchema = Schema.Data( + * Schema.Struct({ + * id: Schema.Number, + * name: Schema.String, + * age: Schema.Number + * }) + * ) + * + * const Person = Schema.decode(PersonSchema) + * + * const person1 = Person({ id: 1, name: "Alice", age: 30 }) + * const person2 = Person({ id: 1, name: "Alice", age: 30 }) + * + * assert(Equal.equals(person1, person2)) // Output: true + * + * const set = HashSet.empty().pipe( + * HashSet.add(person1), + * HashSet.add(person2) + * ) + * + * // HashSet thanks to Schema.Data implementation of the `Equal` trait, recognizes the two Person as equal, so only one element is stored + * console.log(HashSet.size(set)) // Output: 1 + * ``` + * + * ### Interoperability with the JavaScript Runtime: + * + * To interoperate with the regular JavaScript runtime, Effect's `HashSet` + * provides methods to access its elements in formats readily usable by + * JavaScript APIs: {@link values `HashSet.values`}, + * {@link toValues `HashSet.toValues`} + * + * ```ts + * import { HashSet } from "effect" + * + * const hashSet: HashSet.HashSet = HashSet.make(1, 2, 3) + * + * // Using HashSet.values to convert HashSet.HashSet to IterableIterator + * const iterable: IterableIterator = HashSet.values(hashSet) + * + * console.log(...iterable) // Logs: 1 2 3 + * + * // Using HashSet.toValues to convert HashSet.HashSet to Array + * const array: Array = HashSet.toValues(hashSet) + * + * console.log(array) // Logs: [ 1, 2, 3 ] + * ``` + * + * Be mindful of performance implications (both time and space complexity) when + * frequently converting between Effect's immutable HashSet and mutable + * JavaScript data structures, especially for large collections. + * + * @module HashSet + * @since 2.0.0 + */ + +import type { Equal } from "./Equal.js" +import type { Inspectable } from "./Inspectable.js" +import * as HS from "./internal/hashSet.js" +import type { Pipeable } from "./Pipeable.js" +import type { Predicate, Refinement } from "./Predicate.js" +import type { NoInfer } from "./Types.js" + +const TypeId: unique symbol = HS.HashSetTypeId as TypeId + +/** + * @since 2.0.0 + * @category symbol + */ +export type TypeId = typeof TypeId + +/** + * @memberof HashSet + * @since 2.0.0 + * @category models + * @example + * + * ```ts + * // Syntax + * import { HashSet } from "effect" + * + * let numberSet: HashSet.HashSet + * ``` + * + * @interface + */ +export interface HashSet extends Iterable, Equal, Pipeable, Inspectable { + readonly [TypeId]: TypeId +} + +/** + * @memberof HashSet + * @since 2.0.0 + * @category refinements + */ +export const isHashSet: { + /** + * Type guard function to determine if a given iterable is a `HashSet`. + * + * This overload preserves the type of the iterable's elements. + * + * @example + * + * ```ts + * import { HashSet } from "effect" + * + * const numberIterable: Iterable<1 | 2 | 3> = [1, 2, 3] + * + * if ( + * // if passed an Iterable the type guard that preserves the type parameter + * HashSet.isHashSet(numberIterable) + * ) { + * const HashSet: HashSet.HashSet<1 | 2 | 3> = numberIterable + * } + * ``` + * + * @param u - The iterable input to be checked. + * @returns A boolean indicating whether the provided iterable is a `HashSet`. + */ + (u: Iterable): u is HashSet + + /** + * Type guard function that checks if the provided value is a `HashSet` of + * unknown type. + * + * @example + * + * ```ts + * import { HashSet } from "effect" + * import assert from "node:assert/strict" + * + * // Check if a value is a HashSet + * const set = HashSet.make(1, 2, 3) + * + * assert.equal(HashSet.isHashSet(set), true) // true + * assert.equal(HashSet.isHashSet(HashSet.empty()), true) + * + * // Works with any type + * assert.equal(HashSet.isHashSet(null), false) // false + * assert.equal(HashSet.isHashSet({}), false) // false + * assert.equal(HashSet.isHashSet([1, 2, 3]), false) // false + * ``` + * + * @param u - The value to check. + * @returns A boolean indicating whether the value is a `HashSet`. + */ + (u: unknown): u is HashSet +} = HS.isHashSet + +/** + * Creates an empty `HashSet`. + * + * Time complexity: **`O(1)`** + * + * @memberof HashSet + * @since 2.0.0 + * @category constructors + * @example + * + * ```ts + * import { HashSet, pipe } from "effect" + * + * console.log( + * pipe( + * // Provide a type argument to create a HashSet of a specific type + * HashSet.empty(), + * HashSet.add(1), + * HashSet.add(1), // Notice the duplicate + * HashSet.add(2), + * HashSet.toValues + * ) + * ) // Output: [1, 2] + * ``` + * + * @see Other `HashSet` constructors are {@link module:HashSet.make} {@link module:HashSet.fromIterable} + */ +export const empty: () => HashSet = HS.empty + +/** + * Creates a new `HashSet` from an iterable collection of values. + * + * Time complexity: **`O(n)`** where n is the number of elements in the iterable + * + * @memberof HashSet + * @since 2.0.0 + * @category constructors + * @example + * + * ```ts + * // Creating a HashSet from an Array + * import { HashSet, pipe } from "effect" + * + * console.log( + * pipe( + * [1, 2, 3, 4, 5, 1, 2, 3], // Array is an Iterable; Note the duplicates. + * HashSet.fromIterable, + * HashSet.toValues + * ) + * ) // Output: [1, 2, 3, 4, 5] + * ``` + * + * @example + * + * ```ts + * // Creating a HashSet from a Set + * import { HashSet, pipe } from "effect" + * + * console.log( + * pipe( + * new Set(["apple", "banana", "orange", "apple"]), // Set is an Iterable + * HashSet.fromIterable, + * HashSet.toValues + * ) + * ) // Output: ["apple", "banana", "orange"] + * ``` + * + * @example + * + * ```ts + * // Creating a HashSet from a Generator + * import { HashSet } from "effect" + * + * // Generator functions return iterables + * function* fibonacci(n: number): Generator { + * let [a, b] = [0, 1] + * for (let i = 0; i < n; i++) { + * yield a + * ;[a, b] = [b, a + b] + * } + * } + * + * // Create a HashSet from the first 10 Fibonacci numbers + * const fibonacciSet = HashSet.fromIterable(fibonacci(10)) + * + * console.log(HashSet.toValues(fibonacciSet)) + * // Outputs: [0, 1, 2, 3, 5, 8, 13, 21, 34] but in unsorted order + * ``` + * + * @example + * + * ```ts + * // Creating a HashSet from another HashSet + * import { HashSet, pipe } from "effect" + * + * console.log( + * pipe( + * // since HashSet implements the Iterable interface, we can use it to create a new HashSet + * HashSet.make(1, 2, 3, 4), + * HashSet.fromIterable, + * HashSet.toValues // turns the HashSet back into an array + * ) + * ) // Output: [1, 2, 3, 4] + * ``` + * + * @example + * + * ```ts + * // Creating a HashSet from other Effect's data structures like Chunk + * import { Chunk, HashSet, pipe } from "effect" + * + * console.log( + * pipe( + * Chunk.make(1, 2, 3, 4), // Iterable + * HashSet.fromIterable, + * HashSet.toValues // turns the HashSet back into an array + * ) + * ) // Outputs: [1, 2, 3, 4] + * ``` + * + * @see Other `HashSet` constructors are {@link module:HashSet.empty} {@link module:HashSet.make} + */ +export const fromIterable: (elements: Iterable) => HashSet = HS.fromIterable + +/** + * Construct a new `HashSet` from a variable number of values. + * + * Time complexity: **`O(n)`** where n is the number of elements + * + * @memberof HashSet + * @since 2.0.0 + * @category constructors + * @example + * + * ```ts + * import { Equal, Hash, HashSet, pipe } from "effect" + * import assert from "node:assert/strict" + * + * class Character implements Equal.Equal { + * readonly name: string + * readonly trait: string + * + * constructor(name: string, trait: string) { + * this.name = name + * this.trait = trait + * } + * + * // Define equality based on name, and trait + * [Equal.symbol](that: Equal.Equal): boolean { + * if (that instanceof Character) { + * return ( + * Equal.equals(this.name, that.name) && + * Equal.equals(this.trait, that.trait) + * ) + * } + * return false + * } + * + * // Generate a hash code based on the sum of the character's name and trait + * [Hash.symbol](): number { + * return Hash.hash(this.name + this.trait) + * } + * + * static readonly of = (name: string, trait: string): Character => { + * return new Character(name, trait) + * } + * } + * + * assert.strictEqual( + * Equal.equals( + * HashSet.make( + * Character.of("Alice", "Curious"), + * Character.of("Alice", "Curious"), + * Character.of("White Rabbit", "Always late"), + * Character.of("Mad Hatter", "Tea enthusiast") + * ), + * // Is the same as adding each character to an empty set + * pipe( + * HashSet.empty(), + * HashSet.add(Character.of("Alice", "Curious")), + * HashSet.add(Character.of("Alice", "Curious")), // Alice tried to attend twice! + * HashSet.add(Character.of("White Rabbit", "Always late")), + * HashSet.add(Character.of("Mad Hatter", "Tea enthusiast")) + * ) + * ), + * true, + * "`HashSet.make` and `HashSet.empty() + HashSet.add()` should be equal" + * ) + * + * assert.strictEqual( + * Equal.equals( + * HashSet.make( + * Character.of("Alice", "Curious"), + * Character.of("Alice", "Curious"), + * Character.of("White Rabbit", "Always late"), + * Character.of("Mad Hatter", "Tea enthusiast") + * ), + * HashSet.fromIterable([ + * Character.of("Alice", "Curious"), + * Character.of("Alice", "Curious"), + * Character.of("White Rabbit", "Always late"), + * Character.of("Mad Hatter", "Tea enthusiast") + * ]) + * ), + * true, + * "`HashSet.make` and `HashSet.fromIterable` should be equal" + * ) + * ``` + * + * @see Other `HashSet` constructors are {@link module:HashSet.fromIterable} {@link module:HashSet.empty} + */ +export const make: >(...elements: As) => HashSet = HS.make + +/** + * Checks if the specified value exists in the `HashSet`. + * + * Time complexity: **`O(1)`** average + * + * @memberof HashSet + * @since 2.0.0 + * @category elements + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe(HashSet.make(0, 1, 2), HashSet.has(3)) // false + * + * // or piped with the pipe function + * HashSet.make(0, 1, 2).pipe(HashSet.has(3)) // false + * + * // or with `data-first` API + * HashSet.has(HashSet.make(0, 1, 2), 3) // false + * ``` + * + * @returns A `boolean` signaling the presence of the value in the HashSet + * @see Other `HashSet` elements are {@link module:HashSet.some} {@link module:HashSet.every} {@link module:HashSet.isSubset} + */ +export const has: { + /** + * @example + * + * ```ts + * // `data-last` a.k.a. `pipeable` API + * import * as assert from "node:assert/strict" + * import { HashSet, pipe } from "effect" + * + * const set = HashSet.make(0, 1, 2) + * + * assert.equal(pipe(set, HashSet.has(0)), true) + * assert.equal(pipe(set, HashSet.has(1)), true) + * assert.equal(pipe(set, HashSet.has(2)), true) + * assert.equal(pipe(set, HashSet.has(3)), false) + * ``` + */ + (value: A): (self: HashSet) => boolean + + /** + * @example + * + * ```ts + * // `data-first` API + * import * as assert from "node:assert/strict" + * import { HashSet, pipe } from "effect" + * + * const set = HashSet.make(0, 1, 2) + * + * assert.equal(HashSet.has(set, 0), true) + * assert.equal(HashSet.has(set, 1), true) + * assert.equal(HashSet.has(set, 2), true) + * assert.equal(HashSet.has(set, 3), false) + * ``` + */ + (self: HashSet, value: A): boolean +} = HS.has + +/** + * Check if a predicate holds true for some `HashSet` element. + * + * Time complexity: **`O(n)`** where n is the number of elements in the set + * + * @memberof HashSet + * @since 2.0.0 + * @category elements + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * const set: HashSet.HashSet = HashSet.make(0, 1, 2) + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe( + * set, + * HashSet.some((n) => n > 0) + * ) // true + * + * // or piped with the pipe function + * set.pipe(HashSet.some((n) => n > 0)) // true + * + * // or with `data-first` API + * HashSet.some(set, (n) => n > 0) // true + * ``` + * + * @see Other `HashSet` elements are {@link module:HashSet.has} {@link module:HashSet.every} {@link module:HashSet.isSubset} + */ +export const some: { + /** + * @example + * + * ```ts + * // `data-last` a.k.a. `pipeable` API + * import * as assert from "node:assert/strict" + * import { HashSet, pipe } from "effect" + * + * const set = HashSet.make(0, 1, 2) + * + * assert.equal( + * pipe( + * set, + * HashSet.some((n) => n > 0) + * ), + * true + * ) + * + * assert.equal( + * pipe( + * set, + * HashSet.some((n) => n > 2) + * ), + * false + * ) + * ``` + */ + (f: Predicate): (self: HashSet) => boolean + + /** + * @example + * + * ```ts + * // `data-first` API + * import * as assert from "node:assert/strict" + * import { HashSet } from "effect" + * + * const set = HashSet.make(0, 1, 2) + * + * assert.equal( + * HashSet.some(set, (n) => n > 0), + * true + * ) + * + * assert.equal( + * HashSet.some(set, (n) => n > 2), + * false + * ) + * ``` + */ + (self: HashSet, f: Predicate): boolean +} = HS.some + +/** + * Check if a predicate holds true for every `HashSet` element. + * + * Time complexity is **`O(n)`** as it needs to traverse the whole HashSet + * collection + * + * @memberof HashSet + * @since 2.0.0 + * @category elements + * @example + * + * ```ts + * // Syntax with Refinement + * import { HashSet, pipe, Predicate } from "effect" + * + * const numberOrString = HashSet.make(1, "1", "one", "uno") + * + * // with `data-last`, a.k.a. `pipeable` API and `Refinement` + * pipe( + * numberOrString, // HashSet.HashSet + * HashSet.every(Predicate.isString) + * ) // HashSet.HashSet + * + * // or piped with the pipe function and `Refinement` + * numberOrString // HashSet.HashSet + * .pipe(HashSet.every(Predicate.isString)) // HashSet.HashSet + * + * // or with `data-first` API and `Refinement` + * HashSet.every( + * numberOrString, // HashSet.HashSet + * Predicate.isString + * ) // HashSet.HashSet + * ``` + * + * @example + * + * ```ts + * // Syntax with Predicate + * import { HashSet, pipe } from "effect" + * + * const set = HashSet.make(1, 2, 3) + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe( + * set, + * HashSet.every((n) => n >= 0) + * ) // true + * + * // or piped with the pipe function + * set.pipe(HashSet.every((n) => n >= 0)) // true + * + * // or with `data-first` API + * HashSet.every(set, (n) => n >= 0) // true + * ``` + * + * @returns A boolean once it has evaluated that whole collection fulfill the + * Predicate function + * @see Other `HashSet` elements are {@link module:HashSet.has} {@link module:HashSet.some} {@link module:HashSet.isSubset} + */ +export const every: { + /** + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { Effect, HashSet, pipe, Predicate } from "effect" + * + * const numberOrString: HashSet.HashSet = HashSet.make( + * 1, + * "1", + * "one", + * "uno" + * ) + * + * assert.equal( + * pipe( + * numberOrString, // HashSet.HashSet + * HashSet.every(Predicate.isString) + * ), // HashSet.HashSet + * false + * ) + * ``` + */ + ( + refinement: Refinement, B> + ): (self: HashSet) => self is HashSet + + /** + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { HashSet, pipe } from "effect" + * + * const set = HashSet.make(0, 1, 2) + * + * assert.equal( + * pipe( + * set, + * HashSet.every((n) => n >= 0) + * ), + * true + * ) + * ``` + */ + (predicate: Predicate): (self: HashSet) => boolean + + /** + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { Effect, HashSet, pipe, Predicate } from "effect" + * + * const numberOrString: HashSet.HashSet = HashSet.make( + * 1, + * "1", + * "one", + * "uno" + * ) + * + * assert.equal( + * HashSet.every( + * numberOrString, // HashSet.HashSet + * Predicate.isString + * ), // HashSet.HashSet + * false + * ) + * ``` + */ + ( + self: HashSet, + refinement: Refinement + ): self is HashSet + + /** + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { HashSet } from "effect" + * + * const set = HashSet.make(0, 1, 2) + * + * assert.equal( + * HashSet.every(set, (n) => n >= 0), + * true + * ) + * ``` + */ + (self: HashSet, predicate: Predicate): boolean +} = HS.every + +/** + * Returns `true` if and only if every element in the this `HashSet` is an + * element of the second set, + * + * **NOTE**: the hash and equal of both sets must be the same. + * + * Time complexity analysis is of **`O(n)`** + * + * @memberof HashSet + * @since 2.0.0 + * @category elements + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * const set1 = HashSet.make(0, 1) + * const set2 = HashSet.make(1, 2) + * const set3 = HashSet.make(0, 1, 2) + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe(set1, HashSet.isSubset(set2)) // false + * pipe(set1, HashSet.isSubset(set3)) // true + * + * // or piped with the pipe function + * set1.pipe(HashSet.isSubset(set2)) // false + * set1.pipe(HashSet.isSubset(set3)) // true + * + * // or with `data-first` API + * HashSet.isSubset(set1, set2) // false + * HashSet.isSubset(set1, set3) // true) + * ``` + * + * @see Other `HashSet` elements are {@link module:HashSet.has} {@link module:HashSet.some} {@link module:HashSet.every} + */ +export const isSubset: { + /** + * @example + * + * ```ts + * import { HashSet, pipe } from "effect" + * import * as assert from "node:assert/strict" + * + * assert.equal( + * pipe( + * HashSet.make(0, 1), // + * HashSet.isSubset(HashSet.make(1, 2)) + * ), + * false + * ) + * + * assert.equal( + * pipe( + * HashSet.make(0, 1), // + * HashSet.isSubset(HashSet.make(0, 1, 2)) + * ), + * true + * ) + * ``` + */ + (that: HashSet): (self: HashSet) => boolean + + /** + * @example + * + * ```ts + * import { HashSet } from "effect" + * import * as assert from "node:assert/strict" + * + * assert.equal(HashSet.isSubset(set1, set2), false) + * + * assert.equal(HashSet.isSubset(set1, set3), true) + * ``` + */ + (self: HashSet, that: HashSet): boolean +} = HS.isSubset + +/** + * Returns an `IterableIterator` of the values in the `HashSet`. + * + * Time complexity: **`O(1)`** + * + * @memberof HashSet + * @since 2.0.0 + * @category getters + * @example + * + * ```ts + * import { HashSet, pipe } from "effect" + * + * const numberIterable = pipe( + * HashSet.make(0, 1, 1, 2), // HashSet.HashSet + * HashSet.values // takes an HashSet and returns an IterableIterator + * ) + * + * for (const number of numberIterable) { + * console.log(number) // it will logs: 0, 1, 2 + * } + * ``` + * + * @see Other `HashSet` getters are {@link module:HashSet.toValues} {@link module:HashSet.size} + */ +export const values: (self: HashSet) => IterableIterator = HS.values + +/** + * Returns an `Array` of the values within the `HashSet`. + * + * Time complexity: **`O(n)`** where n is the number of elements in the set + * + * @memberof HashSet + * @since 3.13.0 + * @category getters + * @example + * + * ```ts + * import { HashSet, pipe } from "effect" + * import { deepStrictEqual } from "node:assert/strict" + * + * deepStrictEqual( + * pipe( + * HashSet.make(0, 1, 1, 2), // HashSet + * HashSet.toValues // takes an HashSet and returns an Array + * ), + * Array.of(0, 1, 2) + * ) + * ``` + * + * @see Other `HashSet` getters are {@link module:HashSet.values} {@link module:HashSet.size} + */ +export const toValues = (self: HashSet): Array => Array.from(values(self)) + +/** + * Calculates the number of values in the `HashSet`. + * + * Time complexity: **`O(1)`** + * + * @memberof HashSet + * @since 2.0.0 + * @category getters + * @example + * + * ```ts + * import { HashSet, pipe } from "effect" + * import assert from "node:assert/strict" + * + * assert.deepStrictEqual(pipe(HashSet.empty(), HashSet.size), 0) + * + * assert.deepStrictEqual( + * pipe(HashSet.make(1, 2, 2, 3, 4, 3), HashSet.size), + * 4 + * ) + * ``` + * + * @see Other `HashSet` getters are {@link module:HashSet.values} {@link module:HashSet.toValues} + */ +export const size: (self: HashSet) => number = HS.size + +/** + * Creates a new mutable version of the `HashSet` + * + * When a `HashSet` is mutable, operations like {@link add} and {@link remove} + * modify the data structure in place instead of creating a new one, which is + * more efficient when performing multiple operations. + * + * @memberof HashSet + * @since 2.0.0 + * @example + * + * ```ts + * import { HashSet } from "effect" + * import assert from "node:assert/strict" + * + * const UPPER_BOUND = 10_000 + * + * const immutableSet = HashSet.empty().pipe(HashSet.add(0)) + * + * // Create a mutable version of the immutableSet + * const mutableSet = HashSet.beginMutation(immutableSet) + * + * for (let i = 1; i < UPPER_BOUND; i++) { + * // Operations now modify the set in place instead of creating new instances + * // This is more efficient when making multiple changes + * const pointerToMutableSet = HashSet.add(mutableSet, i) + * + * // the two sets have the same identity, hence `add` is mutating mutableSet and not returning a new HashSet instance + * assert(Object.is(mutableSet, pointerToMutableSet)) + * assert.equal(HashSet.has(mutableSet, i), true) // `i` is in the mutableSet + * assert.equal(HashSet.has(immutableSet, i), false) // `i` is not in the immutableSet + * } + * + * const next = UPPER_BOUND + 1 + * // When done, mark the set as immutable again + * HashSet.endMutation(mutableSet).pipe( + * HashSet.add(next) // since this returns a new HashSet, it will not be logged as part of the mutableSet + * ) + * assert.equal(HashSet.has(mutableSet, next), false) + * + * console.log(HashSet.toValues(immutableSet)) // [0] + * console.log(HashSet.toValues(mutableSet).sort((a, b) => a - b)) // [0, 1, 2, 3, ...rest] + * ``` + * + * @see Other `HashSet` mutations are {@link module:HashSet.add} {@link module:HashSet.remove} {@link module:HashSet.toggle} {@link module:HashSet.endMutation} {@link module:HashSet.mutate} + */ +export const beginMutation: (self: HashSet) => HashSet = HS.beginMutation + +/** + * Makes the `HashSet` immutable again. + * + * After calling `endMutation`, operations like {@link add} and {@link remove} + * will create new instances of the `HashSet` instead of modifying the existing + * one. + * + * @memberof HashSet + * @since 2.0.0 + * @example + * + * ```ts + * import { HashSet } from "effect" + * import assert from "node:assert/strict" + * + * // Create a mutable set + * const mutableSet = HashSet.beginMutation(HashSet.empty()) + * + * // Add some elements to the mutable set + * HashSet.add(mutableSet, 1) + * HashSet.add(mutableSet, 2) + * + * // Before endMutation, operations modify the set in place + * const sameSet = HashSet.add(mutableSet, 3) + * assert(Object.is(mutableSet, sameSet)) // true - same object reference + * assert.deepStrictEqual(HashSet.toValues(mutableSet).sort(), [1, 2, 3]) + * + * // Make the set immutable again + * const immutableSet = HashSet.endMutation(mutableSet) + * + * // endMutation returns the same set instance, now made immutable + * assert(Object.is(mutableSet, immutableSet)) // true - same object reference + * + * // After endMutation, operations create new instances + * const newSet = HashSet.add(immutableSet, 4) + * assert(!Object.is(immutableSet, newSet)) // false - different object references + * + * // The original set remains unchanged + * assert.deepStrictEqual(HashSet.toValues(immutableSet).sort(), [1, 2, 3]) + * + * // The new set contains the added element + * assert.deepStrictEqual(HashSet.toValues(newSet).sort(), [1, 2, 3, 4]) + * ``` + * + * @see Other `HashSet` mutations are {@link module:HashSet.add} {@link module:HashSet.remove} {@link module:HashSet.toggle} {@link module:HashSet.beginMutation} {@link module:HashSet.mutate} + */ +export const endMutation: (self: HashSet) => HashSet = HS.endMutation + +/** + * Mutates the `HashSet` within the context of the provided function. + * + * You can consider it a functional abstraction on top of the lower-level + * mutation primitives of {@link module:HashSet.beginMutation} `->` `mutable + * context` `->` {@link HashSet.endMutation}. + * + * @memberof HashSet + * @since 2.0.0 + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * // with data-last, a.k.a. pipeable API + * pipe( + * HashSet.make(1, 2, 3), + * HashSet.mutate((set) => { + * HashSet.add(set, 4) + * HashSet.remove(set, 1) + * }) + * ) + * + * // or piped with the pipe function + * HashSet.make(1, 2, 3).pipe( + * HashSet.mutate((set) => { + * HashSet.add(set, 4) + * HashSet.remove(set, 1) + * }) + * ) + * + * // or with data-first API + * HashSet.mutate(HashSet.make(1, 2, 3), (set) => { + * HashSet.add(set, 4) + * HashSet.remove(set, 1) + * }) + * ``` + * + * @see Other `HashSet` mutations are {@link module:HashSet.add} {@link module:HashSet.remove} {@link module:HashSet.toggle} {@link module:HashSet.beginMutation} {@link module:HashSet.endMutation} + */ +export const mutate: { + /** + * @example + * + * ```ts + * // `data-last` a.k.a. `pipeable` API + * import { HashSet, pipe } from "effect" + * import assert from "node:assert/strict" + * + * // Create a set with initial values + * const immutableSet = HashSet.make(1, 2, 3) + * + * // Use mutate to perform multiple operations efficiently + * const result = pipe( + * immutableSet, + * HashSet.mutate((set) => { + * assert.equal(Object.is(immutableSet, set), false) + * + * // The set is temporarily mutable inside this function + * const mod1 = HashSet.add(set, 4) + * const mod2 = HashSet.remove(set, 1) + * assert.equal(Object.is(mod1, mod2), true) // they are the same object by reference + * }) + * ) + * + * // The original set is unchanged + * assert.equal(Object.is(immutableSet, result), false) + * assert.deepStrictEqual( + * HashSet.toValues(immutableSet).sort(), + * [1, 2, 3] + * ) + * + * // The result contains the mutations + * assert.deepStrictEqual(HashSet.toValues(result).sort(), [2, 3, 4]) + * ``` + */ + (f: (set: HashSet) => void): (self: HashSet) => HashSet + + /** + * @example + * + * ```ts + * // `data-first` API + * import { HashSet } from "effect" + * import assert from "node:assert/strict" + * + * // Create a set with initial values + * const immutableSet = HashSet.make(1, 2, 3) + * + * // Use mutate with data-first API + * const result = HashSet.mutate(immutableSet, (set) => { + * // The set is temporarily mutable inside this function + * HashSet.add(set, 4) + * HashSet.remove(set, 1) + * }) + * + * // The original set is unchanged + * assert.equal(Object.is(immutableSet, result), false) + * assert.deepStrictEqual( + * HashSet.toValues(immutableSet).sort(), + * [1, 2, 3] + * ) + * + * // The result contains the mutations + * assert.deepStrictEqual(HashSet.toValues(result).sort(), [2, 3, 4]) + * ``` + */ + (self: HashSet, f: (set: HashSet) => void): HashSet +} = HS.mutate + +/** + * Adds a value to the `HashSet`. + * + * Time complexity: **`O(1)`** average + * + * @remarks + * Remember that a `HashSet` is a collection of unique values, so adding a value + * that already exists in the `HashSet` will not add a duplicate. + * + * Remember that HashSet is an immutable data structure, so the `add` function, + * like all other functions that modify the HashSet, will return a new HashSet + * with the added value. + * @memberof HashSet + * @since 2.0.0 + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * // with data-last, a.k.a. pipeable API + * pipe(HashSet.empty(), HashSet.add(0), HashSet.add(0)) + * + * // or piped with the pipe function + * HashSet.empty().pipe(HashSet.add(0)) + * + * // or with data-first API + * HashSet.add(HashSet.empty(), 0) + * ``` + * + * @see Other `HashSet` mutations are {@link module:HashSet.remove} {@link module:HashSet.toggle} {@link module:HashSet.beginMutation} {@link module:HashSet.endMutation} {@link module:HashSet.mutate} + */ +export const add: { + /** + * @example + * + * ```ts + * // `data-last` a.k.a. `pipeable` API + * import { HashSet, pipe } from "effect" + * import assert from "node:assert/strict" + * + * assert.deepStrictEqual( + * pipe( + * HashSet.empty(), // HashSet.HashSet + * HashSet.add(0), + * HashSet.add(1), + * HashSet.add(1), + * HashSet.add(2), + * HashSet.toValues + * ), + * Array.of(0, 1, 2) + * ) + * ``` + */ + (value: A): (self: HashSet) => HashSet + + /** + * @example + * + * ```ts + * // `data-first` API + * import { HashSet, pipe } from "effect" + * import assert from "node:assert/strict" + * + * const empty = HashSet.empty() + * const withZero = HashSet.add(empty, 0) + * const withOne = HashSet.add(withZero, 1) + * const withTwo = HashSet.add(withOne, 2) + * const withTwoTwo = HashSet.add(withTwo, 2) + * + * assert.deepStrictEqual(HashSet.toValues(withTwoTwo), Array.of(0, 1, 2)) + * ``` + */ + (self: HashSet, value: A): HashSet +} = HS.add + +/** + * Removes a value from the `HashSet`. + * + * Time complexity: **`O(1)`** average + * + * @memberof HashSet + * @since 2.0.0 + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe(HashSet.make(0, 1, 2), HashSet.remove(0)) + * + * // or piped with the pipe function + * HashSet.make(0, 1, 2).pipe(HashSet.remove(0)) + * + * // or with `data-first` API + * HashSet.remove(HashSet.make(0, 1, 2), 0) + * ``` + * + * @see Other `HashSet` mutations are {@link module:HashSet.add} {@link module:HashSet.toggle} {@link module:HashSet.beginMutation} {@link module:HashSet.endMutation} {@link module:HashSet.mutate} + */ +export const remove: { + /** + * @example + * + * ```ts + * // `data-last` a.k.a. `pipeable` API + * import { HashSet, pipe } from "effect" + * import * as assert from "node:assert/strict" + * + * const set = HashSet.make(0, 1, 2) + * const result = pipe(set, HashSet.remove(0)) + * + * assert.equal(pipe(result, HashSet.has(0)), false) // it has correctly removed 0 + * assert.equal(pipe(set, HashSet.has(0)), true) // it does not mutate the original set + * assert.equal(pipe(result, HashSet.has(1)), true) + * assert.equal(pipe(result, HashSet.has(2)), true) + * ``` + */ + (value: A): (self: HashSet) => HashSet + + /** + * @example + * + * ```ts + * // `data-first` API + * import { HashSet, pipe } from "effect" + * import * as assert from "node:assert/strict" + * + * const set = HashSet.make(0, 1, 2) + * const result = HashSet.remove(set, 0) + * + * assert.equal(HashSet.has(result, 0), false) // it has correctly removed 0 + * assert.equal(HashSet.has(set, 0), true) // it does not mutate the original set + * assert.equal(HashSet.has(result, 1), true) + * assert.equal(HashSet.has(result, 2), true) + * ``` + */ + (self: HashSet, value: A): HashSet +} = HS.remove + +/** + * Computes the set difference `(A - B)` between this `HashSet` and the + * specified `Iterable`. + * + * Time complexity: **`O(n)`** where n is the number of elements in the set + * + * **NOTE**: the hash and equal of the values in both the set and the iterable + * must be the same; meaning we cannot compute a difference between a `HashSet + * of bananas` and a `HashSet of elephants` as they are not the same type and + * won't implement the Equal trait in the same way. + * + * @memberof HashSet + * @since 2.0.0 + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * // with data-last, a.k.a. pipeable API + * pipe(HashSet.make(1, 2, 3), HashSet.difference(HashSet.make(3, 4, 5))) + * + * // or piped with the pipe function + * HashSet.make(1, 2, 3).pipe(HashSet.difference(HashSet.make(3, 4, 5))) + * + * // or with data-first API + * HashSet.difference(HashSet.make(1, 2, 3), HashSet.make(3, 4, 5)) + * ``` + * + * @see Other `HashSet` operations are {@link module:HashSet.intersection} {@link module:HashSet.union} + */ +export const difference: { + /** + * @example + * + * ```ts + * // `data-last` a.k.a. `pipeable` API + * import { HashSet, pipe } from "effect" + * import * as assert from "node:assert/strict" + * + * // Create two sets with some overlapping elements + * const thisSet = HashSet.make(1, 2, 3) + * const thatIterable = HashSet.make(3, 4, 5) + * + * // Compute the difference (elements in thisSet that are not in thatIterable) + * const result = pipe(thisSet, HashSet.difference(thatIterable)) + * + * // The result contains only elements from thisSet that are not in thatIterable + * assert.deepStrictEqual(HashSet.toValues(result).sort(), [1, 2]) + * + * // The original sets are unchanged + * assert.deepStrictEqual(HashSet.toValues(thisSet).sort(), [1, 2, 3]) + * assert.deepStrictEqual( + * HashSet.toValues(thatIterable).sort(), + * [3, 4, 5] + * ) + * + * // You can also use arrays or other iterables + * const diffWithArray = pipe(thisSet, HashSet.difference([3, 4])) + * assert.deepStrictEqual(HashSet.toValues(diffWithArray).sort(), [1, 2]) + * ``` + */ + (that: Iterable): (self: HashSet) => HashSet + + /** + * @example + * + * ```ts + * // `data-first` API + * import { HashSet } from "effect" + * import * as assert from "node:assert/strict" + * + * // Create two sets with some overlapping elements + * const thisSet = HashSet.make(1, 2, 3) + * const thatIterable = HashSet.make(3, 4, 5) + * + * // Compute the difference using data-first API + * const result = HashSet.difference(thisSet, thatIterable) + * + * // The result contains only elements from thisSet that are not in thatIterable + * assert.deepStrictEqual(HashSet.toValues(result).sort(), [1, 2]) + * + * // The original sets are unchanged + * assert.deepStrictEqual(HashSet.toValues(thisSet).sort(), [1, 2, 3]) + * assert.deepStrictEqual( + * HashSet.toValues(thatIterable).sort(), + * [3, 4, 5] + * ) + * + * // You can also compute the difference in the other direction + * const reverseResult = HashSet.difference(thatIterable, thisSet) + * assert.deepStrictEqual(HashSet.toValues(reverseResult).sort(), [4, 5]) + * ``` + */ + (self: HashSet, that: Iterable): HashSet +} = HS.difference + +/** + * Returns a `HashSet` of values which are present in both this set and that + * `Iterable`. Computes set intersection (A ∩ B) + * + * Time complexity: **`O(n)`** where n is the number of elements in the smaller + * set + * + * **NOTE**: the hash and equal of the values in both the set and the iterable + * must be the same. + * + * @memberof HashSet + * @since 2.0.0 + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * // with data-last, a.k.a. pipeable API + * pipe(HashSet.make(1, 2, 3), HashSet.intersection(HashSet.make(2, 3, 4))) + * + * // or piped with the pipe function + * HashSet.make(1, 2, 3).pipe(HashSet.intersection(HashSet.make(2, 3, 4))) + * + * // or with data-first API + * HashSet.intersection(HashSet.make(1, 2, 3), HashSet.make(2, 3, 4)) + * ``` + * + * @see Other `HashSet` operations are {@link module:HashSet.difference} {@link module:HashSet.union} + */ +export const intersection: { + /** + * @example + * + * ```ts + * // `data-last` a.k.a. `pipeable` API + * import { HashSet, pipe } from "effect" + * import * as assert from "node:assert/strict" + * + * // Create two sets with some overlapping elements + * const set1 = HashSet.make(1, 2, 3) + * const set2 = HashSet.make(2, 3, 4) + * + * // Compute the intersection (elements that are in both sets) + * const result = pipe(set1, HashSet.intersection(set2)) + * + * // The result contains only elements that are in both sets + * assert.deepStrictEqual(HashSet.toValues(result).sort(), [2, 3]) + * + * // The original sets are unchanged + * assert.deepStrictEqual(HashSet.toValues(set1).sort(), [1, 2, 3]) + * assert.deepStrictEqual(HashSet.toValues(set2).sort(), [2, 3, 4]) + * + * // You can also use arrays or other iterables + * const intersectWithArray = pipe(set1, HashSet.intersection([2, 3, 5])) + * assert.deepStrictEqual( + * HashSet.toValues(intersectWithArray).sort(), + * [2, 3] + * ) + * ``` + */ + (that: Iterable): (self: HashSet) => HashSet + + /** + * @example + * + * ```ts + * // `data-first` API + * import { HashSet } from "effect" + * import * as assert from "node:assert/strict" + * + * // Create two sets with some overlapping elements + * const set1 = HashSet.make(1, 2, 3) + * const set2 = HashSet.make(2, 3, 4) + * + * // Compute the intersection using data-first API + * const result = HashSet.intersection(set1, set2) + * + * // The result contains only elements that are in both sets + * assert.deepStrictEqual(HashSet.toValues(result).sort(), [2, 3]) + * + * // The original sets are unchanged + * assert.deepStrictEqual(HashSet.toValues(set1).sort(), [1, 2, 3]) + * assert.deepStrictEqual(HashSet.toValues(set2).sort(), [2, 3, 4]) + * + * // You can also use arrays or other iterables + * const intersectWithArray = HashSet.intersection(set1, [2, 3, 5]) + * assert.deepStrictEqual( + * HashSet.toValues(intersectWithArray).sort(), + * [2, 3] + * ) + * ``` + */ + (self: HashSet, that: Iterable): HashSet +} = HS.intersection + +/** + * Computes the set union `( self ∪ that )` between this `HashSet` and the + * specified `Iterable`. + * + * Time complexity: **`O(n)`** where n is the number of elements in the set + * + * **NOTE**: the hash and equal of the values in both the set and the iterable + * must be the same. + * + * @memberof HashSet + * @since 2.0.0 + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * // with data-last, a.k.a. pipeable API + * pipe(HashSet.make(1, 2, 3), HashSet.union(HashSet.make(3, 4, 5))) + * + * // or piped with the pipe function + * HashSet.make(1, 2, 3).pipe(HashSet.union(HashSet.make(3, 4, 5))) + * + * // or with data-first API + * HashSet.union(HashSet.make(1, 2, 3), HashSet.make(3, 4, 5)) + * ``` + * + * @see Other `HashSet` operations are {@link module:HashSet.difference} {@link module:HashSet.intersection} + */ +export const union: { + /** + * @example + * + * ```ts + * // `data-last` a.k.a. `pipeable` API + * import { HashSet, pipe } from "effect" + * import * as assert from "node:assert/strict" + * + * // Create two sets with some overlapping elements + * const selfSet = HashSet.make(1, 2, 3) + * const thatIterable = HashSet.make(3, 4, 5) + * + * // Compute the union (all elements from both sets) + * const result = pipe(selfSet, HashSet.union(thatIterable)) + * + * // The result contains all elements from both sets (without duplicates) + * assert.deepStrictEqual( + * HashSet.toValues(result).sort(), + * [1, 2, 3, 4, 5] + * ) + * + * // The original sets are unchanged + * assert.deepStrictEqual(HashSet.toValues(selfSet).sort(), [1, 2, 3]) + * assert.deepStrictEqual( + * HashSet.toValues(thatIterable).sort(), + * [3, 4, 5] + * ) + * + * // You can also use arrays or other iterables + * const unionWithArray = pipe(selfSet, HashSet.union([4, 5, 6])) + * assert.deepStrictEqual( + * HashSet.toValues(unionWithArray).sort(), + * [1, 2, 3, 4, 5, 6] + * ) + * ``` + */ + (that: Iterable): (self: HashSet) => HashSet + + /** + * @example + * + * ```ts + * // `data-first` API + * import { HashSet } from "effect" + * import * as assert from "node:assert/strict" + * + * // Create two sets with some overlapping elements + * const selfSet = HashSet.make(1, 2, 3) + * const thatIterable = HashSet.make(3, 4, 5) + * + * // Compute the union using data-first API + * const result = HashSet.union(selfSet, thatIterable) + * + * // The result contains all elements from both sets (without duplicates) + * assert.deepStrictEqual( + * HashSet.toValues(result).sort(), + * [1, 2, 3, 4, 5] + * ) + * + * // The original sets are unchanged + * assert.deepStrictEqual(HashSet.toValues(selfSet).sort(), [1, 2, 3]) + * assert.deepStrictEqual( + * HashSet.toValues(thatIterable).sort(), + * [3, 4, 5] + * ) + * + * // You can also use arrays or other iterables + * const unionWithArray = HashSet.union(selfSet, [4, 5, 6]) + * assert.deepStrictEqual( + * HashSet.toValues(unionWithArray).sort(), + * [1, 2, 3, 4, 5, 6] + * ) + * ``` + */ + (self: HashSet, that: Iterable): HashSet +} = HS.union + +/** + * Checks if a value is present in the `HashSet`. If it is present, the value + * will be removed from the `HashSet`, otherwise the value will be added to the + * `HashSet`. + * + * Time complexity: **`O(1)`** average + * + * @memberof HashSet + * @since 2.0.0 + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe(HashSet.make(0, 1, 2), HashSet.toggle(0)) + * + * // or piped with the pipe function + * HashSet.make(0, 1, 2).pipe(HashSet.toggle(0)) + * + * // or with `data-first` API + * HashSet.toggle(HashSet.make(0, 1, 2), 0) + * ``` + * + * @returns A new `HashSet` where the toggled value is being either added or + * removed based on the initial `HashSet` state. + * @see Other `HashSet` mutations are {@link module:HashSet.add} {@link module:HashSet.remove} {@link module:HashSet.beginMutation} {@link module:HashSet.endMutation} {@link module:HashSet.mutate} + */ +export const toggle: { + /** + * @example + * + * ```ts + * // `data-last` a.k.a. `pipeable` API + * import { HashSet, pipe } from "effect" + * import assert from "node:assert/strict" + * + * // arrange + * let set = HashSet.make(0, 1, 2) + * + * // assert 1: 0 is in the set + * assert.equal(pipe(set, HashSet.has(0)), true) + * + * // act 2: toggle 0 once on the set + * set = pipe(set, HashSet.toggle(0)) + * + * // assert 2: 0 is not in the set any longer + * assert.equal(pipe(set, HashSet.has(0)), false) + * + * // act 3: toggle 0 once again on the set + * set = pipe(set, HashSet.toggle(0)) + * + * // assert 3: 0 in now back in the set + * assert.equal(pipe(set, HashSet.has(0)), true) + * ``` + */ + (value: A): (self: HashSet) => HashSet + + /** + * @example + * + * ```ts + * // `data-first` API + * import { HashSet, pipe } from "effect" + * import assert from "node:assert/strict" + * + * // arrange + * let set = HashSet.make(0, 1, 2) + * + * // assert 1: 0 is in the set + * assert.equal(HashSet.has(set, 0), true) + * + * // act 2: toggle 0 once on the set + * set = HashSet.toggle(set, 0) + * + * // assert 2: 0 is not in the set any longer + * assert.equal(HashSet.has(set, 0), false) + * + * // act 3: toggle 0 once again on the set + * set = HashSet.toggle(set, 0) + * + * // assert 3: 0 in now back in the set + * assert.equal(HashSet.has(set, 0), true) + * ``` + */ + (self: HashSet, value: A): HashSet +} = HS.toggle + +/** + * Maps over the values of the `HashSet` using the specified function. + * + * The time complexity is of **`O(n)`**. + * + * @memberof HashSet + * @since 2.0.0 + * @category mapping + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe( + * HashSet.make(0, 1, 2), // HashSet.HashSet + * HashSet.map(String) // HashSet.HashSet + * ) + * + * // or piped with the pipe method + * HashSet.make(0, 1, 2).pipe(HashSet.map(String)) + * + * // or with `data-first` API + * HashSet.map(HashSet.make(0, 1, 2), String) + * ``` + */ +export const map: { + /** + * @example + * + * ```ts + * import { HashSet, pipe } from "effect" + * import * as assert from "node:assert/strict" + * + * assert.deepStrictEqual( + * pipe( + * HashSet.make(0, 1, 2), // HashSet.HashSet + * HashSet.map((n) => String(n + 1)) // HashSet.HashSet + * ), + * HashSet.make("1", "2", "3") + * ) + * ``` + */ + (f: (a: A) => B): (self: HashSet) => HashSet + + /** + * @example + * + * ```ts + * import { HashSet, pipe } from "effect" + * import * as assert from "node:assert/strict" + * + * assert.deepStrictEqual( + * HashSet.map( + * HashSet.make(0, 1, 2), // HashSet.HashSet + * (n) => String(n + 1) + * ), // HashSet.HashSet + * HashSet.make("1", "2", "3") + * ) + * ``` + */ + (self: HashSet, f: (a: A) => B): HashSet +} = HS.map + +/** + * Chains over the values of the `HashSet` using the specified function. + * + * The time complexity is of **`O(n)`**. + * + * @memberof HashSet + * @since 2.0.0 + * @category sequencing + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe( + * HashSet.make(0, 1, 2), // HashSet.HashSet + * HashSet.flatMap((n) => Array.of(String(n))) // HashSet.HashSet + * ) + * + * // or piped with the pipe method + * HashSet.make(0, 1, 2) // HashSet.HashSet + * .pipe( + * HashSet.flatMap((n) => Array.of(String(n))) // HashSet.HashSet + * ) + * + * // or with `data-first` API + * HashSet.flatMap(HashSet.make(0, 1, 2), (n) => Array.of(String(n))) + * ``` + */ +export const flatMap: { + /** + * @example + * + * ```ts + * import { HashSet, pipe, List } from "effect" + * import * as assert from "node:assert/strict" + * + * assert.deepStrictEqual( + * pipe( + * HashSet.make(0, 1, 2), + * HashSet.flatMap((n) => List.of(String(n * n))) // needs to return an Iterable + * ), + * HashSet.make("0", "1", "4") + * ) + * ``` + */ + (f: (a: A) => Iterable): (self: HashSet) => HashSet + + /** + * @example + * + * ```ts + * import { HashSet, pipe, List } from "effect" + * import * as assert from "node:assert/strict" + * + * assert.deepStrictEqual( + * HashSet.flatMap(HashSet.make(0, 1, 2), (n) => + * List.of(String(n * n * n)) + * ), // needs to return an Iterable + * HashSet.make("0", "1", "8") + * ) + * ``` + */ + (self: HashSet, f: (a: A) => Iterable): HashSet +} = HS.flatMap + +/** + * Applies the specified function to the values of the `HashSet`. + * + * The time complexity is of **`O(n)`**. + * + * @memberof HashSet + * @since 2.0.0 + * @category traversing + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe(HashSet.make(0, 1, 2), HashSet.forEach(console.log)) // logs: 0 1 2 + * + * // or piped with the pipe method + * HashSet.make(0, 1, 2).pipe(HashSet.forEach(console.log)) // logs: 0 1 2 + * + * // or with `data-first` API + * HashSet.forEach(HashSet.make(0, 1, 2), console.log) // logs: 0 1 2 + * ``` + */ +export const forEach: { + /** + * @example + * + * ```ts + * import { HashSet, pipe } from "effect" + * import * as assert from "node:assert/strict" + * + * const result: Array = [] + * + * pipe( + * HashSet.make(0, 1, 2), + * HashSet.forEach((n): void => { + * result.push(n) + * }) + * ) + * + * assert.deepStrictEqual(result, [0, 1, 2]) + * ``` + */ + (f: (value: A) => void): (self: HashSet) => void + + /** + * @example + * + * ```ts + * import { HashSet, pipe } from "effect" + * import * as assert from "node:assert/strict" + * + * const result: Array = [] + * + * HashSet.forEach(HashSet.make(0, 1, 2), (n): void => { + * result.push(n) + * }) + * + * assert.deepStrictEqual(result, [0, 1, 2]) + * ``` + */ + (self: HashSet, f: (value: A) => void): void +} = HS.forEach + +/** + * Reduces the specified state over the values of the `HashSet`. + * + * The time complexity is of **`O(n)`**. + * + * @memberof HashSet + * @since 2.0.0 + * @category folding + * @example + * + * ```ts + * // Syntax + * import { HashSet, pipe } from "effect" + * + * const sum = (a: number, b: number): number => a + b + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe(HashSet.make(0, 1, 2), HashSet.reduce(0, sum)) + * + * // or with the pipe method + * HashSet.make(0, 1, 2).pipe(HashSet.reduce(0, sum)) + * + * // or with `data-first` API + * HashSet.reduce(HashSet.make(0, 1, 2), 0, sum) + * ``` + */ +export const reduce: { + /** + * @example + * + * ```ts + * import { HashSet, pipe } from "effect" + * import * as assert from "node:assert/strict" + * + * assert.equal( + * pipe( + * HashSet.make(0, 1, 2), + * HashSet.reduce(10, (accumulator, value) => accumulator + value) + * ), + * 13 + * ) + * ``` + */ + (zero: Z, f: (accumulator: Z, value: A) => Z): (self: HashSet) => Z + + /** + * @example + * + * ```ts + * import { HashSet } from "effect" + * import * as assert from "node:assert/strict" + * + * assert.equal( + * HashSet.reduce( + * HashSet.make(0, 1, 2), + * -3, + * (accumulator, value) => accumulator + value + * ), + * 0 + * ) + * ``` + */ + (self: HashSet, zero: Z, f: (accumulator: Z, value: A) => Z): Z +} = HS.reduce + +/** + * Filters values out of a `HashSet` using the specified predicate. + * + * The time complexity is of **`O(n)`**. + * + * @memberof HashSet + * @since 2.0.0 + * @category filtering + * @example + * + * ```ts + * // Syntax with Predicate + * import { HashSet, type Predicate, pipe } from "effect" + * + * const filterPositiveNumbers: Predicate.Predicate = (n) => n > 0 + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe( + * HashSet.make(-2, -1, 0, 1, 2), + * HashSet.filter(filterPositiveNumbers) + * ) + * + * // or with the pipe method + * HashSet.make(-2, -1, 0, 1, 2).pipe(HashSet.filter(filterPositiveNumbers)) + * + * // or with `data-first` API + * HashSet.filter(HashSet.make(-2, -1, 0, 1, 2), filterPositiveNumbers) + * ``` + * + * @example + * + * ```ts + * /// Syntax with Refinement + * import { HashSet, pipe } from "effect" + * + * const stringRefinement = (value: unknown): value is string => + * typeof value === "string" + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe( + * HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier"), // // HashSet.HashSet + * HashSet.filter(stringRefinement) + * ) // HashSet.HashSet + * + * // or with the pipe method + * HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier") // HashSet.HashSet + * .pipe(HashSet.filter(stringRefinement)) // HashSet.HashSet + * + * // or with `data-first` API + * HashSet.filter( + * HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier"), // HashSet.HashSet + * stringRefinement + * ) // HashSet.HashSet + * ``` + */ +export const filter: { + /** + * @example + * + * ```ts + * import { HashSet, pipe, Predicate } from "effect" + * import * as assert from "node:assert/strict" + * + * const numbersAndStringsHashSet: HashSet.HashSet = + * HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier") + * + * const stringRefinement: Predicate.Refinement< + * string | number, + * string + * > = (value) => typeof value === "string" + * + * const stringHashSet: HashSet.HashSet = pipe( + * numbersAndStringsHashSet, + * HashSet.filter(stringRefinement) + * ) + * + * assert.equal( + * pipe(stringHashSet, HashSet.every(Predicate.isString)), + * true + * ) + * ``` + */ + ( + refinement: Refinement, B> + ): (self: HashSet) => HashSet + + /** + * @example + * + * ```ts + * import { HashSet, pipe, type Predicate } from "effect" + * import * as assert from "node:assert/strict" + * + * const filterPositiveNumbers: Predicate.Predicate = (n) => n > 0 + * + * assert.deepStrictEqual( + * pipe( + * HashSet.make(-2, -1, 0, 1, 2), + * HashSet.filter(filterPositiveNumbers) + * ), + * HashSet.make(1, 2) + * ) + * ``` + */ + (predicate: Predicate>): (self: HashSet) => HashSet + + /** + * @example + * + * ```ts + * import { HashSet, Predicate } from "effect" + * import * as assert from "node:assert/strict" + * + * const numbersAndStringsHashSet: HashSet.HashSet = + * HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier") + * + * const stringRefinement: Predicate.Refinement< + * string | number, + * string + * > = (value) => typeof value === "string" + * + * const stringHashSet: HashSet.HashSet = HashSet.filter( + * numbersAndStringsHashSet, + * stringRefinement + * ) + * + * assert.equal(HashSet.every(stringHashSet, Predicate.isString), true) + * ``` + */ + ( + self: HashSet, + refinement: Refinement + ): HashSet + + /** + * @example + * + * ```ts + * import { HashSet, pipe, type Predicate } from "effect" + * import * as assert from "node:assert/strict" + * + * const filterPositiveNumbers: Predicate.Predicate = (n) => n > 0 + * + * assert.deepStrictEqual( + * HashSet.filter(HashSet.make(-2, -1, 0, 1, 2), filterPositiveNumbers), + * HashSet.make(1, 2) + * ) + * ``` + */ + (self: HashSet, predicate: Predicate): HashSet +} = HS.filter + +/** + * Partition the values of a `HashSet` using the specified predicate. + * + * If a value matches the predicate, it will be placed into the `HashSet` on the + * right side of the resulting `Tuple`, otherwise the value will be placed into + * the left side. + * + * Time complexity is of **`O(n)`**. + * + * @memberof HashSet + * @since 2.0.0 + * @category partitioning + * @example + * + * ```ts + * // Syntax with Predicate + * import { HashSet, pipe, Predicate } from "effect" + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe( + * HashSet.make(0, 1, 2, 3, 4, 5), + * HashSet.partition((n) => n % 2 === 0) + * ) + * + * // or with the pipe method + * HashSet.make(0, 1, 2, 3, 4, 5).pipe( + * HashSet.partition((n) => n % 2 === 0) + * ) + * + * // or with `data-first` API + * HashSet.partition(HashSet.make(0, 1, 2, 3, 4, 5), (n) => n % 2 === 0) + * ``` + * + * @example + * + * ```ts + * // Syntax with Refinement + * import { HashSet, pipe, Predicate } from "effect" + * + * const stringRefinement: Predicate.Refinement = ( + * value + * ) => typeof value === "string" + * + * // with `data-last`, a.k.a. `pipeable` API + * pipe( + * HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier"), + * HashSet.partition(stringRefinement) + * ) + * + * // or with the pipe method + * HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier").pipe( + * HashSet.partition(stringRefinement) + * ) + * + * // or with `data-first` API + * HashSet.partition( + * HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier"), + * stringRefinement + * ) + * ``` + */ +export const partition: { + /** + * @example + * + * ```ts + * import { HashSet, pipe, Predicate } from "effect" + * import * as assert from "node:assert/strict" + * + * const numbersAndStringsHashSet: HashSet.HashSet = + * HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier") + * + * const stringRefinement: Predicate.Refinement< + * string | number, + * string + * > = (value) => typeof value === "string" + * + * const [ + * excluded, // HashSet.HashSet + * satisfying // HashSet.HashSet + * ] = pipe(numbersAndStringsHashSet, HashSet.partition(stringRefinement)) + * + * assert.equal(pipe(satisfying, HashSet.every(Predicate.isString)), true) + * assert.equal(pipe(excluded, HashSet.every(Predicate.isNumber)), true) + * + * assert.deepStrictEqual(excluded, HashSet.make(1, 2, 3, 4)) + * assert.deepStrictEqual( + * satisfying, + * HashSet.make("unos", "two", "trois", "vier") + * ) + * ``` + */ + ( + refinement: Refinement, B> + ): ( + self: HashSet + ) => [excluded: HashSet>, satisfying: HashSet] + + /** + * @example + * + * ```ts + * import { HashSet, pipe } from "effect" + * import * as assert from "node:assert/strict" + * + * const [excluded, satisfying] = pipe( + * HashSet.make(0, 1, 2, 3, 4, 5), + * HashSet.partition((n) => n % 2 === 0) + * ) + * + * assert.deepStrictEqual(excluded, HashSet.make(1, 3, 5)) + * assert.deepStrictEqual(satisfying, HashSet.make(0, 2, 4)) + * ``` + */ + ( + predicate: Predicate> + ): (self: HashSet) => [excluded: HashSet, satisfying: HashSet] + + /** + * @example + * + * ```ts + * import { HashSet, pipe, Predicate } from "effect" + * import * as assert from "node:assert/strict" + * + * const numbersAndStringsHashSet: HashSet.HashSet = + * HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier") + * + * const stringRefinement: Predicate.Refinement< + * string | number, + * string + * > = (value) => typeof value === "string" + * + * const [ + * excluded, // HashSet.HashSet + * satisfying // HashSet.HashSet + * ] = HashSet.partition(numbersAndStringsHashSet, stringRefinement) + * + * assert.equal(HashSet.every(satisfying, Predicate.isString), true) + * assert.equal(HashSet.every(excluded, Predicate.isNumber), true) + * + * assert.deepStrictEqual(excluded, HashSet.make(1, 2, 3, 4)) + * assert.deepStrictEqual( + * satisfying, + * HashSet.make("unos", "two", "trois", "vier") + * ) + * ``` + */ + ( + self: HashSet, + refinement: Refinement + ): [excluded: HashSet>, satisfying: HashSet] + + /** + * @example + * + * ```ts + * import { HashSet } from "effect" + * import * as assert from "node:assert/strict" + * + * const [excluded, satisfying] = HashSet.partition( + * HashSet.make(0, 1, 2, 3, 4, 5), + * (n) => n % 2 === 0 + * ) + * + * assert.deepStrictEqual(excluded, HashSet.make(1, 3, 5)) + * assert.deepStrictEqual(satisfying, HashSet.make(0, 2, 4)) + * ``` + */ + ( + self: HashSet, + predicate: Predicate + ): [excluded: HashSet, satisfying: HashSet] +} = HS.partition diff --git a/backend/node_modules/effect/src/KeyedPool.ts b/backend/node_modules/effect/src/KeyedPool.ts new file mode 100644 index 0000000000000000000000000000000000000000..460b4fa3d7dd2f4397042d8576bd8caec0756177 --- /dev/null +++ b/backend/node_modules/effect/src/KeyedPool.ts @@ -0,0 +1,201 @@ +/** + * @since 2.0.0 + */ +import type * as Duration from "./Duration.js" +import type * as Effect from "./Effect.js" +import * as internal from "./internal/keyedPool.js" +import type { Pipeable } from "./Pipeable.js" +import type * as Scope from "./Scope.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const KeyedPoolTypeId: unique symbol = internal.KeyedPoolTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type KeyedPoolTypeId = typeof KeyedPoolTypeId + +/** + * A `KeyedPool` is a pool of `Pool`s of items of type `A`. Each pool + * in the `KeyedPool` is associated with a key of type `K`. + * + * @since 2.0.0 + * @category models + */ +export interface KeyedPool extends KeyedPool.Variance, Pipeable { + /** + * Retrieves an item from the pool belonging to the given key in a scoped + * effect. Note that if acquisition fails, then the returned effect will fail + * for that same reason. Retrying a failed acquisition attempt will repeat the + * acquisition attempt. + */ + get(key: K): Effect.Effect + + /** + * Invalidates the specified item. This will cause the pool to eventually + * reallocate the item, although this reallocation may occur lazily rather + * than eagerly. + */ + invalidate(item: A): Effect.Effect +} + +/** + * @since 2.0.0 + */ +export declare namespace KeyedPool { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [KeyedPoolTypeId]: { + readonly _K: Types.Contravariant + readonly _A: Types.Invariant + readonly _E: Types.Covariant + } + } +} + +/** + * Makes a new pool of the specified fixed size. The pool is returned in a + * `Scope`, which governs the lifetime of the pool. When the pool is shutdown + * because the `Scope` is closed, the individual items allocated by the pool + * will be released in some unspecified order. + * + * @since 2.0.0 + * @category constructors + */ +export const make: ( + options: { + readonly acquire: (key: K) => Effect.Effect + readonly size: number + } +) => Effect.Effect, never, Scope.Scope | R> = internal.make + +/** + * Makes a new pool of the specified fixed size. The pool is returned in a + * `Scope`, which governs the lifetime of the pool. When the pool is shutdown + * because the `Scope` is closed, the individual items allocated by the pool + * will be released in some unspecified order. + * + * The size of the underlying pools can be configured per key. + * + * @since 2.0.0 + * @category constructors + */ +export const makeWith: ( + options: { + readonly acquire: (key: K) => Effect.Effect + readonly size: (key: K) => number + } +) => Effect.Effect, never, Scope.Scope | R> = internal.makeWith + +/** + * Makes a new pool with the specified minimum and maximum sizes and time to + * live before a pool whose excess items are not being used will be shrunk + * down to the minimum size. The pool is returned in a `Scope`, which governs + * the lifetime of the pool. When the pool is shutdown because the `Scope` is + * used, the individual items allocated by the pool will be released in some + * unspecified order. + * + * The size of the underlying pools can be configured per key. + * + * @since 2.0.0 + * @category constructors + */ +export const makeWithTTL: ( + options: { + readonly acquire: (key: K) => Effect.Effect + readonly min: (key: K) => number + readonly max: (key: K) => number + readonly timeToLive: Duration.DurationInput + } +) => Effect.Effect, never, Scope.Scope | R> = internal.makeWithTTL + +/** + * Makes a new pool with the specified minimum and maximum sizes and time to + * live before a pool whose excess items are not being used will be shrunk + * down to the minimum size. The pool is returned in a `Scope`, which governs + * the lifetime of the pool. When the pool is shutdown because the `Scope` is + * used, the individual items allocated by the pool will be released in some + * unspecified order. + * + * The size of the underlying pools can be configured per key. + * + * @since 2.0.0 + * @category constructors + */ +export const makeWithTTLBy: ( + options: { + readonly acquire: (key: K) => Effect.Effect + readonly min: (key: K) => number + readonly max: (key: K) => number + readonly timeToLive: (key: K) => Duration.DurationInput + } +) => Effect.Effect, never, Scope.Scope | R> = internal.makeWithTTLBy + +/** + * Retrieves an item from the pool belonging to the given key in a scoped + * effect. Note that if acquisition fails, then the returned effect will fail + * for that same reason. Retrying a failed acquisition attempt will repeat the + * acquisition attempt. + * + * @since 2.0.0 + * @category combinators + */ +export const get: { + /** + * Retrieves an item from the pool belonging to the given key in a scoped + * effect. Note that if acquisition fails, then the returned effect will fail + * for that same reason. Retrying a failed acquisition attempt will repeat the + * acquisition attempt. + * + * @since 2.0.0 + * @category combinators + */ + (key: K): (self: KeyedPool) => Effect.Effect + /** + * Retrieves an item from the pool belonging to the given key in a scoped + * effect. Note that if acquisition fails, then the returned effect will fail + * for that same reason. Retrying a failed acquisition attempt will repeat the + * acquisition attempt. + * + * @since 2.0.0 + * @category combinators + */ + (self: KeyedPool, key: K): Effect.Effect +} = internal.get + +/** + * Invalidates the specified item. This will cause the pool to eventually + * reallocate the item, although this reallocation may occur lazily rather + * than eagerly. + * + * @since 2.0.0 + * @category combinators + */ +export const invalidate: { + /** + * Invalidates the specified item. This will cause the pool to eventually + * reallocate the item, although this reallocation may occur lazily rather + * than eagerly. + * + * @since 2.0.0 + * @category combinators + */ + (item: A): (self: KeyedPool) => Effect.Effect + /** + * Invalidates the specified item. This will cause the pool to eventually + * reallocate the item, although this reallocation may occur lazily rather + * than eagerly. + * + * @since 2.0.0 + * @category combinators + */ + (self: KeyedPool, item: A): Effect.Effect +} = internal.invalidate diff --git a/backend/node_modules/effect/src/Layer.ts b/backend/node_modules/effect/src/Layer.ts new file mode 100644 index 0000000000000000000000000000000000000000..b32988edb910ae7c7c125022f906a27e0511a392 --- /dev/null +++ b/backend/node_modules/effect/src/Layer.ts @@ -0,0 +1,1723 @@ +/** + * A `Layer` describes how to build one or more services in your + * application. Services can be injected into effects via + * `Effect.provideService`. Effects can require services via `Effect.service`. + * + * Layer can be thought of as recipes for producing bundles of services, given + * their dependencies (other services). + * + * Construction of services can be effectful and utilize resources that must be + * acquired and safely released when the services are done being utilized. + * + * By default layers are shared, meaning that if the same layer is used twice + * the layer will only be allocated a single time. + * + * Because of their excellent composition properties, layers are the idiomatic + * way in Effect-TS to create services that depend on other services. + * + * @since 2.0.0 + */ +import type * as Cause from "./Cause.js" +import type * as Clock from "./Clock.js" +import type { ConfigProvider } from "./ConfigProvider.js" +import * as Context from "./Context.js" +import type * as Effect from "./Effect.js" +import type * as Exit from "./Exit.js" +import type { FiberRef } from "./FiberRef.js" +import { dual, type LazyArg } from "./Function.js" +import { clockTag } from "./internal/clock.js" +import * as core from "./internal/core.js" +import * as defaultServices from "./internal/defaultServices.js" +import * as fiberRuntime from "./internal/fiberRuntime.js" +import * as internal from "./internal/layer.js" +import * as circularLayer from "./internal/layer/circular.js" +import * as query from "./internal/query.js" +import { randomTag } from "./internal/random.js" +import type { LogLevel } from "./LogLevel.js" +import type * as Option from "./Option.js" +import type { Pipeable } from "./Pipeable.js" +import type * as Random from "./Random.js" +import type * as Request from "./Request.js" +import type * as Runtime from "./Runtime.js" +import type * as Schedule from "./Schedule.js" +import * as Scheduler from "./Scheduler.js" +import type * as Scope from "./Scope.js" +import type * as Stream from "./Stream.js" +import type * as Tracer from "./Tracer.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const LayerTypeId: unique symbol = internal.LayerTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type LayerTypeId = typeof LayerTypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface Layer extends Layer.Variance, Pipeable {} + +/** + * @since 2.0.0 + */ +export declare namespace Layer { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [LayerTypeId]: { + readonly _ROut: Types.Contravariant + readonly _E: Types.Covariant + readonly _RIn: Types.Covariant + } + } + /** + * @since 3.9.0 + * @category type-level + */ + export interface Any { + readonly [LayerTypeId]: { + readonly _ROut: Types.Contravariant + readonly _E: Types.Covariant + readonly _RIn: Types.Covariant + } + } + /** + * @since 2.0.0 + * @category type-level + */ + export type Context = [T] extends [Layer] ? _RIn + : never + /** + * @since 2.0.0 + * @category type-level + */ + export type Error = [T] extends [Layer] ? _E + : never + /** + * @since 2.0.0 + * @category type-level + */ + export type Success = [T] extends [Layer] ? _ROut + : never +} + +/** + * @since 2.0.0 + * @category symbols + */ +export const MemoMapTypeId: unique symbol = internal.MemoMapTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type MemoMapTypeId = typeof MemoMapTypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface MemoMap { + readonly [MemoMapTypeId]: MemoMapTypeId + + /** @internal */ + readonly getOrElseMemoize: ( + layer: Layer, + scope: Scope.Scope + ) => Effect.Effect, E, RIn> +} + +/** + * @since 3.13.0 + * @category models + */ +export interface CurrentMemoMap { + readonly _: unique symbol +} + +/** + * @since 3.13.0 + * @category models + */ +export const CurrentMemoMap: Context.Reference = internal.CurrentMemoMap + +/** + * Returns `true` if the specified value is a `Layer`, `false` otherwise. + * + * @since 2.0.0 + * @category getters + */ +export const isLayer: (u: unknown) => u is Layer = internal.isLayer + +/** + * Returns `true` if the specified `Layer` is a fresh version that will not be + * shared, `false` otherwise. + * + * @since 2.0.0 + * @category getters + */ +export const isFresh: (self: Layer) => boolean = internal.isFresh + +/** + * @since 3.3.0 + * @category tracing + */ +export const annotateLogs: { + /** + * @since 3.3.0 + * @category tracing + */ + (key: string, value: unknown): (self: Layer) => Layer + /** + * @since 3.3.0 + * @category tracing + */ + (values: Record): (self: Layer) => Layer + /** + * @since 3.3.0 + * @category tracing + */ + (self: Layer, key: string, value: unknown): Layer + /** + * @since 3.3.0 + * @category tracing + */ + (self: Layer, values: Record): Layer +} = internal.annotateLogs + +/** + * @since 3.3.0 + * @category tracing + */ +export const annotateSpans: { + /** + * @since 3.3.0 + * @category tracing + */ + (key: string, value: unknown): (self: Layer) => Layer + /** + * @since 3.3.0 + * @category tracing + */ + (values: Record): (self: Layer) => Layer + /** + * @since 3.3.0 + * @category tracing + */ + (self: Layer, key: string, value: unknown): Layer + /** + * @since 3.3.0 + * @category tracing + */ + (self: Layer, values: Record): Layer +} = internal.annotateSpans + +/** + * Builds a layer into a scoped value. + * + * @since 2.0.0 + * @category destructors + */ +export const build: ( + self: Layer +) => Effect.Effect, E, Scope.Scope | RIn> = internal.build + +/** + * Builds a layer into an `Effect` value. Any resources associated with this + * layer will be released when the specified scope is closed unless their scope + * has been extended. This allows building layers where the lifetime of some of + * the services output by the layer exceed the lifetime of the effect the + * layer is provided to. + * + * @since 2.0.0 + * @category destructors + */ +export const buildWithScope: { + /** + * Builds a layer into an `Effect` value. Any resources associated with this + * layer will be released when the specified scope is closed unless their scope + * has been extended. This allows building layers where the lifetime of some of + * the services output by the layer exceed the lifetime of the effect the + * layer is provided to. + * + * @since 2.0.0 + * @category destructors + */ + (scope: Scope.Scope): (self: Layer) => Effect.Effect, E, RIn> + /** + * Builds a layer into an `Effect` value. Any resources associated with this + * layer will be released when the specified scope is closed unless their scope + * has been extended. This allows building layers where the lifetime of some of + * the services output by the layer exceed the lifetime of the effect the + * layer is provided to. + * + * @since 2.0.0 + * @category destructors + */ + (self: Layer, scope: Scope.Scope): Effect.Effect, E, RIn> +} = internal.buildWithScope + +/** + * Recovers from all errors. + * + * @since 2.0.0 + * @category error handling + */ +export const catchAll: { + /** + * Recovers from all errors. + * + * @since 2.0.0 + * @category error handling + */ + (onError: (error: E) => Layer): (self: Layer) => Layer + /** + * Recovers from all errors. + * + * @since 2.0.0 + * @category error handling + */ + (self: Layer, onError: (error: E) => Layer): Layer +} = internal.catchAll + +/** + * Recovers from all errors. + * + * @since 2.0.0 + * @category error handling + */ +export const catchAllCause: { + /** + * Recovers from all errors. + * + * @since 2.0.0 + * @category error handling + */ + (onError: (cause: Cause.Cause) => Layer): (self: Layer) => Layer + /** + * Recovers from all errors. + * + * @since 2.0.0 + * @category error handling + */ + ( + self: Layer, + onError: (cause: Cause.Cause) => Layer + ): Layer +} = internal.catchAllCause + +/** + * Constructs a `Layer` that passes along the specified context as an + * output. + * + * @since 2.0.0 + * @category constructors + */ +export const context: () => Layer = internal.context + +/** + * Constructs a layer that dies with the specified defect. + * + * @since 2.0.0 + * @category constructors + */ +export const die: (defect: unknown) => Layer = internal.die + +/** + * Constructs a layer that dies with the specified defect. + * + * @since 2.0.0 + * @category constructors + */ +export const dieSync: (evaluate: LazyArg) => Layer = internal.dieSync + +/** + * Replaces the layer's output with `never` and includes the layer only for its + * side-effects. + * + * @since 2.0.0 + * @category mapping + */ +export const discard: (self: Layer) => Layer = internal.discard + +/** + * Constructs a layer from the specified effect. + * + * @since 2.0.0 + * @category constructors + */ +export const effect: { + /** + * Constructs a layer from the specified effect. + * + * @since 2.0.0 + * @category constructors + */ + (tag: Context.Tag): (effect: Effect.Effect, E, R>) => Layer + /** + * Constructs a layer from the specified effect. + * + * @since 2.0.0 + * @category constructors + */ + (tag: Context.Tag, effect: Effect.Effect, E, R>): Layer +} = internal.fromEffect + +/** + * Constructs a layer from the specified effect, discarding its output. + * + * @since 2.0.0 + * @category constructors + */ +export const effectDiscard: (effect: Effect.Effect) => Layer = internal.fromEffectDiscard + +/** + * Constructs a layer from the specified effect, which must return one or more + * services. + * + * @since 2.0.0 + * @category constructors + */ +export const effectContext: (effect: Effect.Effect, E, R>) => Layer = + internal.fromEffectContext + +/** + * A Layer that constructs an empty Context. + * + * @since 2.0.0 + * @category constructors + */ +export const empty: Layer = internal.empty + +/** + * Extends the scope of this layer, returning a new layer that when provided + * to an effect will not immediately release its associated resources when + * that effect completes execution but instead when the scope the resulting + * effect depends on is closed. + * + * @since 2.0.0 + * @category utils + */ +export const extendScope: (self: Layer) => Layer = + internal.extendScope + +/** + * Constructs a layer that fails with the specified error. + * + * @since 2.0.0 + * @category constructors + */ +export const fail: (error: E) => Layer = internal.fail + +/** + * Constructs a layer that fails with the specified error. + * + * @since 2.0.0 + * @category constructors + */ +export const failSync: (evaluate: LazyArg) => Layer = internal.failSync + +/** + * Constructs a layer that fails with the specified cause. + * + * @since 2.0.0 + * @category constructors + */ +export const failCause: (cause: Cause.Cause) => Layer = internal.failCause + +/** + * Constructs a layer that fails with the specified cause. + * + * @since 2.0.0 + * @category constructors + */ +export const failCauseSync: (evaluate: LazyArg>) => Layer = internal.failCauseSync + +/** + * Constructs a layer dynamically based on the output of this layer. + * + * @since 2.0.0 + * @category sequencing + */ +export const flatMap: { + /** + * Constructs a layer dynamically based on the output of this layer. + * + * @since 2.0.0 + * @category sequencing + */ + (f: (context: Context.Context) => Layer): (self: Layer) => Layer + /** + * Constructs a layer dynamically based on the output of this layer. + * + * @since 2.0.0 + * @category sequencing + */ + ( + self: Layer, + f: (context: Context.Context) => Layer + ): Layer +} = internal.flatMap + +/** + * Flattens layers nested in the context of an effect. + * + * @since 2.0.0 + * @category sequencing + */ +export const flatten: { + /** + * Flattens layers nested in the context of an effect. + * + * @since 2.0.0 + * @category sequencing + */ + (tag: Context.Tag>): (self: Layer) => Layer + /** + * Flattens layers nested in the context of an effect. + * + * @since 2.0.0 + * @category sequencing + */ + (self: Layer, tag: Context.Tag>): Layer +} = internal.flatten + +/** + * Creates a fresh version of this layer that will not be shared. + * + * @since 2.0.0 + * @category utils + */ +export const fresh: (self: Layer) => Layer = internal.fresh + +/** + * @since 3.17.0 + * @category Testing + */ +export type PartialEffectful = Types.Simplify< + & { + [ + K in keyof A as A[K] extends + | Effect.Effect + | Stream.Stream + | ((...args: any) => Effect.Effect | Stream.Stream) ? K + : never + ]?: A[K] + } + & { + [ + K in keyof A as A[K] extends + | Effect.Effect + | Stream.Stream + | ((...args: any) => Effect.Effect | Stream.Stream) ? never + : K + ]: A[K] + } +> + +/** + * Creates a mock layer for testing purposes. You can provide a partial + * implementation of the service, and any methods not provided will + * throw an `UnimplementedError` defect when called. + * + * **Example** + * + * ```ts + * import { Context, Effect, Layer } from "effect" + * + * class MyService extends Context.Tag("MyService")< + * MyService, + * { + * one: Effect.Effect + * two(): Effect.Effect + * } + * >() {} + * + * const MyServiceTest = Layer.mock(MyService, { + * two: () => Effect.succeed(2) + * }) + * ``` + * + * @since 3.17.0 + * @category Testing + */ +export const mock: { + /** + * Creates a mock layer for testing purposes. You can provide a partial + * implementation of the service, and any methods not provided will + * throw an `UnimplementedError` defect when called. + * + * **Example** + * + * ```ts + * import { Context, Effect, Layer } from "effect" + * + * class MyService extends Context.Tag("MyService")< + * MyService, + * { + * one: Effect.Effect + * two(): Effect.Effect + * } + * >() {} + * + * const MyServiceTest = Layer.mock(MyService, { + * two: () => Effect.succeed(2) + * }) + * ``` + * + * @since 3.17.0 + * @category Testing + */ + (tag: Context.Tag): (service: PartialEffectful) => Layer + /** + * Creates a mock layer for testing purposes. You can provide a partial + * implementation of the service, and any methods not provided will + * throw an `UnimplementedError` defect when called. + * + * **Example** + * + * ```ts + * import { Context, Effect, Layer } from "effect" + * + * class MyService extends Context.Tag("MyService")< + * MyService, + * { + * one: Effect.Effect + * two(): Effect.Effect + * } + * >() {} + * + * const MyServiceTest = Layer.mock(MyService, { + * two: () => Effect.succeed(2) + * }) + * ``` + * + * @since 3.17.0 + * @category Testing + */ + (tag: Context.Tag, service: PartialEffectful): Layer +} = internal.mock + +const fromFunction: ( + tagA: Context.Tag, + tagB: Context.Tag, + f: (a: Types.NoInfer) => Types.NoInfer +) => Layer = internal.fromFunction + +export { + /** + * Constructs a layer from the context using the specified function. + * + * @since 2.0.0 + * @category constructors + */ + fromFunction as function +} + +/** + * Builds this layer and uses it until it is interrupted. This is useful when + * your entire application is a layer, such as an HTTP server. + * + * @since 2.0.0 + * @category conversions + */ +export const launch: (self: Layer) => Effect.Effect = internal.launch + +/** + * Returns a new layer whose output is mapped by the specified function. + * + * @since 2.0.0 + * @category mapping + */ +export const map: { + /** + * Returns a new layer whose output is mapped by the specified function. + * + * @since 2.0.0 + * @category mapping + */ + (f: (context: Context.Context) => Context.Context): (self: Layer) => Layer + /** + * Returns a new layer whose output is mapped by the specified function. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Layer, + f: (context: Context.Context) => Context.Context + ): Layer +} = internal.map + +/** + * Returns a layer with its error channel mapped using the specified function. + * + * @since 2.0.0 + * @category mapping + */ +export const mapError: { + /** + * Returns a layer with its error channel mapped using the specified function. + * + * @since 2.0.0 + * @category mapping + */ + (f: (error: E) => E2): (self: Layer) => Layer + /** + * Returns a layer with its error channel mapped using the specified function. + * + * @since 2.0.0 + * @category mapping + */ + (self: Layer, f: (error: E) => E2): Layer +} = internal.mapError + +/** + * Feeds the error or output services of this layer into the input of either + * the specified `failure` or `success` layers, resulting in a new layer with + * the inputs of this layer, and the error or outputs of the specified layer. + * + * @since 2.0.0 + * @category folding + */ +export const match: { + /** + * Feeds the error or output services of this layer into the input of either + * the specified `failure` or `success` layers, resulting in a new layer with + * the inputs of this layer, and the error or outputs of the specified layer. + * + * @since 2.0.0 + * @category folding + */ + ( + options: { + readonly onFailure: (error: E) => Layer + readonly onSuccess: (context: Context.Context) => Layer + } + ): (self: Layer) => Layer + /** + * Feeds the error or output services of this layer into the input of either + * the specified `failure` or `success` layers, resulting in a new layer with + * the inputs of this layer, and the error or outputs of the specified layer. + * + * @since 2.0.0 + * @category folding + */ + ( + self: Layer, + options: { + readonly onFailure: (error: E) => Layer + readonly onSuccess: (context: Context.Context) => Layer + } + ): Layer +} = internal.match + +/** + * Feeds the error or output services of this layer into the input of either + * the specified `failure` or `success` layers, resulting in a new layer with + * the inputs of this layer, and the error or outputs of the specified layer. + * + * @since 2.0.0 + * @category folding + */ +export const matchCause: { + /** + * Feeds the error or output services of this layer into the input of either + * the specified `failure` or `success` layers, resulting in a new layer with + * the inputs of this layer, and the error or outputs of the specified layer. + * + * @since 2.0.0 + * @category folding + */ + ( + options: { + readonly onFailure: (cause: Cause.Cause) => Layer + readonly onSuccess: (context: Context.Context) => Layer + } + ): (self: Layer) => Layer + /** + * Feeds the error or output services of this layer into the input of either + * the specified `failure` or `success` layers, resulting in a new layer with + * the inputs of this layer, and the error or outputs of the specified layer. + * + * @since 2.0.0 + * @category folding + */ + ( + self: Layer, + options: { + readonly onFailure: (cause: Cause.Cause) => Layer + readonly onSuccess: (context: Context.Context) => Layer + } + ): Layer +} = internal.matchCause + +/** + * Returns a scoped effect that, if evaluated, will return the lazily computed + * result of this layer. + * + * @since 2.0.0 + * @category utils + */ +export const memoize: ( + self: Layer +) => Effect.Effect, never, Scope.Scope> = internal.memoize + +/** + * Merges this layer with the specified layer concurrently, producing a new layer with combined input and output types. + * + * @since 2.0.0 + * @category zipping + */ +export const merge: { + /** + * Merges this layer with the specified layer concurrently, producing a new layer with combined input and output types. + * + * @since 2.0.0 + * @category zipping + */ + (that: Layer): (self: Layer) => Layer + /** + * Merges this layer with the specified layer concurrently, producing a new layer with combined input and output types. + * + * @since 2.0.0 + * @category zipping + */ + (self: Layer, that: Layer): Layer +} = internal.merge + +/** + * Combines all the provided layers concurrently, creating a new layer with merged input, error, and output types. + * + * @since 2.0.0 + * @category zipping + */ +export const mergeAll: , ...Array>]>( + ...layers: Layers +) => Layer< + { [k in keyof Layers]: Layer.Success }[number], + { [k in keyof Layers]: Layer.Error }[number], + { [k in keyof Layers]: Layer.Context }[number] +> = internal.mergeAll + +/** + * Translates effect failure into death of the fiber, making all failures + * unchecked and not a part of the type of the layer. + * + * @since 2.0.0 + * @category error handling + */ +export const orDie: (self: Layer) => Layer = internal.orDie + +/** + * Executes this layer and returns its output, if it succeeds, but otherwise + * executes the specified layer. + * + * @since 2.0.0 + * @category error handling + */ +export const orElse: { + /** + * Executes this layer and returns its output, if it succeeds, but otherwise + * executes the specified layer. + * + * @since 2.0.0 + * @category error handling + */ + (that: LazyArg>): (self: Layer) => Layer + /** + * Executes this layer and returns its output, if it succeeds, but otherwise + * executes the specified layer. + * + * @since 2.0.0 + * @category error handling + */ + (self: Layer, that: LazyArg>): Layer +} = internal.orElse + +/** + * Returns a new layer that produces the outputs of this layer but also + * passes through the inputs. + * + * @since 2.0.0 + * @category utils + */ +export const passthrough: (self: Layer) => Layer = internal.passthrough + +/** + * Projects out part of one of the services output by this layer using the + * specified function. + * + * @since 2.0.0 + * @category utils + */ +export const project: { + /** + * Projects out part of one of the services output by this layer using the + * specified function. + * + * @since 2.0.0 + * @category utils + */ + ( + tagA: Context.Tag, + tagB: Context.Tag, + f: (a: Types.NoInfer) => Types.NoInfer + ): (self: Layer) => Layer + /** + * Projects out part of one of the services output by this layer using the + * specified function. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Layer, + tagA: Context.Tag, + tagB: Context.Tag, + f: (a: Types.NoInfer) => Types.NoInfer + ): Layer +} = internal.project + +/** + * @since 2.0.0 + * @category utils + */ +export const locallyEffect: { + /** + * @since 2.0.0 + * @category utils + */ + ( + f: (_: Effect.Effect>) => Effect.Effect> + ): (self: Layer) => Layer + /** + * @since 2.0.0 + * @category utils + */ + ( + self: Layer, + f: (_: Effect.Effect>) => Effect.Effect> + ): Layer +} = internal.locallyEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const locally: { + /** + * @since 2.0.0 + * @category utils + */ + (ref: FiberRef, value: X): (self: Layer) => Layer + /** + * @since 2.0.0 + * @category utils + */ + (self: Layer, ref: FiberRef, value: X): Layer +} = internal.fiberRefLocally + +/** + * @since 2.0.0 + * @category utils + */ +export const locallyWith: { + /** + * @since 2.0.0 + * @category utils + */ + (ref: FiberRef, value: (_: X) => X): (self: Layer) => Layer + /** + * @since 2.0.0 + * @category utils + */ + (self: Layer, ref: FiberRef, value: (_: X) => X): Layer +} = internal.fiberRefLocallyWith + +/** + * @since 2.0.0 + * @category utils + */ +export const locallyScoped: (self: FiberRef, value: A) => Layer = internal.fiberRefLocallyScoped + +/** + * @since 2.0.0 + * @category utils + */ +export const fiberRefLocallyScopedWith: (self: FiberRef, value: (_: A) => A) => Layer = + internal.fiberRefLocallyScopedWith + +/** + * Retries constructing this layer according to the specified schedule. + * + * @since 2.0.0 + * @category retrying + */ +export const retry: { + /** + * Retries constructing this layer according to the specified schedule. + * + * @since 2.0.0 + * @category retrying + */ + (schedule: Schedule.Schedule, RIn2>): (self: Layer) => Layer + /** + * Retries constructing this layer according to the specified schedule. + * + * @since 2.0.0 + * @category retrying + */ + (self: Layer, schedule: Schedule.Schedule): Layer +} = internal.retry + +/** + * A layer that constructs a scope and closes it when the workflow the layer + * is provided to completes execution, whether by success, failure, or + * interruption. This can be used to close a scope when providing a layer to a + * workflow. + * + * @since 2.0.0 + * @category constructors + */ +export const scope: Layer = internal.scope + +/** + * Constructs a layer from the specified scoped effect. + * + * @since 2.0.0 + * @category constructors + */ +export const scoped: { + /** + * Constructs a layer from the specified scoped effect. + * + * @since 2.0.0 + * @category constructors + */ + (tag: Context.Tag): (effect: Effect.Effect, E, R>) => Layer> + /** + * Constructs a layer from the specified scoped effect. + * + * @since 2.0.0 + * @category constructors + */ + (tag: Context.Tag, effect: Effect.Effect, E, R>): Layer> +} = internal.scoped + +/** + * Constructs a layer from the specified scoped effect, discarding its output. + * + * @since 2.0.0 + * @category constructors + */ +export const scopedDiscard: (effect: Effect.Effect) => Layer> = + internal.scopedDiscard + +/** + * Constructs a layer from the specified scoped effect, which must return one + * or more services. + * + * @since 2.0.0 + * @category constructors + */ +export const scopedContext: ( + effect: Effect.Effect, E, R> +) => Layer> = internal.scopedContext + +/** + * Constructs a layer that accesses and returns the specified service from the + * context. + * + * @since 2.0.0 + * @category constructors + */ +export const service: (tag: Context.Tag) => Layer = internal.service + +/** + * Constructs a layer from the specified value. + * + * @since 2.0.0 + * @category constructors + */ +export const succeed: { + /** + * Constructs a layer from the specified value. + * + * @since 2.0.0 + * @category constructors + */ + (tag: Context.Tag): (resource: Types.NoInfer) => Layer + /** + * Constructs a layer from the specified value. + * + * @since 2.0.0 + * @category constructors + */ + (tag: Context.Tag, resource: Types.NoInfer): Layer +} = internal.succeed + +/** + * Constructs a layer from the specified value, which must return one or more + * services. + * + * @since 2.0.0 + * @category constructors + */ +export const succeedContext: (context: Context.Context) => Layer = internal.succeedContext + +/** + * Lazily constructs a layer. This is useful to avoid infinite recursion when + * creating layers that refer to themselves. + * + * @since 2.0.0 + * @category constructors + */ +export const suspend: (evaluate: LazyArg>) => Layer = internal.suspend + +/** + * Lazily constructs a layer from the specified value. + * + * @since 2.0.0 + * @category constructors + */ +export const sync: { + /** + * Lazily constructs a layer from the specified value. + * + * @since 2.0.0 + * @category constructors + */ + (tag: Context.Tag): (evaluate: LazyArg>) => Layer + /** + * Lazily constructs a layer from the specified value. + * + * @since 2.0.0 + * @category constructors + */ + (tag: Context.Tag, evaluate: LazyArg>): Layer +} = internal.sync + +/** + * Lazily constructs a layer from the specified value, which must return one or more + * services. + * + * @since 2.0.0 + * @category constructors + */ +export const syncContext: (evaluate: LazyArg>) => Layer = internal.syncContext + +/** + * Performs the specified effect if this layer succeeds. + * + * @since 2.0.0 + * @category sequencing + */ +export const tap: { + /** + * Performs the specified effect if this layer succeeds. + * + * @since 2.0.0 + * @category sequencing + */ + (f: (context: Context.Context) => Effect.Effect): (self: Layer) => Layer + /** + * Performs the specified effect if this layer succeeds. + * + * @since 2.0.0 + * @category sequencing + */ + ( + self: Layer, + f: (context: Context.Context) => Effect.Effect + ): Layer +} = internal.tap + +/** + * Performs the specified effect if this layer fails. + * + * @since 2.0.0 + * @category sequencing + */ +export const tapError: { + /** + * Performs the specified effect if this layer fails. + * + * @since 2.0.0 + * @category sequencing + */ + (f: (e: XE) => Effect.Effect): (self: Layer) => Layer + /** + * Performs the specified effect if this layer fails. + * + * @since 2.0.0 + * @category sequencing + */ + (self: Layer, f: (e: XE) => Effect.Effect): Layer +} = internal.tapError + +/** + * Performs the specified effect if this layer fails. + * + * @since 2.0.0 + * @category sequencing + */ +export const tapErrorCause: { + /** + * Performs the specified effect if this layer fails. + * + * @since 2.0.0 + * @category sequencing + */ + (f: (cause: Cause.Cause) => Effect.Effect): (self: Layer) => Layer + /** + * Performs the specified effect if this layer fails. + * + * @since 2.0.0 + * @category sequencing + */ + ( + self: Layer, + f: (cause: Cause.Cause) => Effect.Effect + ): Layer +} = internal.tapErrorCause + +/** + * Converts a layer that requires no services into a scoped runtime, which can + * be used to execute effects. + * + * @since 2.0.0 + * @category conversions + */ +export const toRuntime: ( + self: Layer +) => Effect.Effect, E, Scope.Scope | RIn> = internal.toRuntime + +/** + * Converts a layer that requires no services into a scoped runtime, which can + * be used to execute effects. + * + * @since 2.0.0 + * @category conversions + */ +export const toRuntimeWithMemoMap: { + /** + * Converts a layer that requires no services into a scoped runtime, which can + * be used to execute effects. + * + * @since 2.0.0 + * @category conversions + */ + (memoMap: MemoMap): (self: Layer) => Effect.Effect, E, Scope.Scope | RIn> + /** + * Converts a layer that requires no services into a scoped runtime, which can + * be used to execute effects. + * + * @since 2.0.0 + * @category conversions + */ + (self: Layer, memoMap: MemoMap): Effect.Effect, E, Scope.Scope | RIn> +} = internal.toRuntimeWithMemoMap + +/** + * Feeds the output services of this builder into the input of the specified + * builder, resulting in a new builder with the inputs of this builder as + * well as any leftover inputs, and the outputs of the specified builder. + * + * @since 2.0.0 + * @category utils + */ +export const provide: { + /** + * Feeds the output services of this builder into the input of the specified + * builder, resulting in a new builder with the inputs of this builder as + * well as any leftover inputs, and the outputs of the specified builder. + * + * @since 2.0.0 + * @category utils + */ + (that: Layer): (self: Layer) => Layer> + /** + * Feeds the output services of this builder into the input of the specified + * builder, resulting in a new builder with the inputs of this builder as + * well as any leftover inputs, and the outputs of the specified builder. + * + * @since 2.0.0 + * @category utils + */ + ]>(that: Layers): ( + self: Layer + ) => Layer< + A, + E | { [k in keyof Layers]: Layer.Error }[number], + | { [k in keyof Layers]: Layer.Context }[number] + | Exclude }[number]> + > + /** + * Feeds the output services of this builder into the input of the specified + * builder, resulting in a new builder with the inputs of this builder as + * well as any leftover inputs, and the outputs of the specified builder. + * + * @since 2.0.0 + * @category utils + */ + (self: Layer, that: Layer): Layer> + /** + * Feeds the output services of this builder into the input of the specified + * builder, resulting in a new builder with the inputs of this builder as + * well as any leftover inputs, and the outputs of the specified builder. + * + * @since 2.0.0 + * @category utils + */ + ]>(self: Layer, that: Layers): Layer< + A, + E | { [k in keyof Layers]: Layer.Error }[number], + | { [k in keyof Layers]: Layer.Context }[number] + | Exclude }[number]> + > +} = internal.provide + +/** + * Feeds the output services of this layer into the input of the specified + * layer, resulting in a new layer with the inputs of this layer, and the + * outputs of both layers. + * + * @since 2.0.0 + * @category utils + */ +export const provideMerge: { + /** + * Feeds the output services of this layer into the input of the specified + * layer, resulting in a new layer with the inputs of this layer, and the + * outputs of both layers. + * + * @since 2.0.0 + * @category utils + */ + (self: Layer): (that: Layer) => Layer> + /** + * Feeds the output services of this layer into the input of the specified + * layer, resulting in a new layer with the inputs of this layer, and the + * outputs of both layers. + * + * @since 2.0.0 + * @category utils + */ + (that: Layer, self: Layer): Layer> +} = internal.provideMerge + +/** + * Combines this layer with the specified layer concurrently, creating a new layer with merged input types and + * combined output types using the provided function. + * + * @since 2.0.0 + * @category zipping + */ +export const zipWith: { + /** + * Combines this layer with the specified layer concurrently, creating a new layer with merged input types and + * combined output types using the provided function. + * + * @since 2.0.0 + * @category zipping + */ + ( + that: Layer, + f: (a: Context.Context, b: Context.Context) => Context.Context + ): (self: Layer) => Layer + /** + * Combines this layer with the specified layer concurrently, creating a new layer with merged input types and + * combined output types using the provided function. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Layer, + that: Layer, + f: (a: Context.Context, b: Context.Context) => Context.Context + ): Layer +} = internal.zipWith + +/** + * @since 2.0.0 + * @category utils + */ +export const unwrapEffect: (self: Effect.Effect, E, R>) => Layer = + internal.unwrapEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const unwrapScoped: ( + self: Effect.Effect, E, R> +) => Layer> = internal.unwrapScoped + +/** + * @since 2.0.0 + * @category clock + */ +export const setClock: (clock: A) => Layer = ( + clock: A +): Layer => + scopedDiscard( + fiberRuntime.fiberRefLocallyScopedWith(defaultServices.currentServices, Context.add(clockTag, clock)) + ) + +/** + * Sets the current `ConfigProvider`. + * + * @since 2.0.0 + * @category config + */ +export const setConfigProvider: (configProvider: ConfigProvider) => Layer = circularLayer.setConfigProvider + +/** + * Adds the provided span to the span stack. + * + * @since 2.0.0 + * @category tracing + */ +export const parentSpan: (span: Tracer.AnySpan) => Layer = circularLayer.parentSpan + +/** + * @since 3.15.0 + * @category Random + */ +export const setRandom = (random: A): Layer => + scopedDiscard( + fiberRuntime.fiberRefLocallyScopedWith(defaultServices.currentServices, Context.add(randomTag, random)) + ) + +/** + * @since 2.0.0 + * @category requests & batching + */ +export const setRequestBatching: (requestBatching: boolean) => Layer = ( + requestBatching: boolean +) => + scopedDiscard( + fiberRuntime.fiberRefLocallyScoped(core.currentRequestBatching, requestBatching) + ) + +/** + * @since 2.0.0 + * @category requests & batching + */ +export const setRequestCaching: (requestCaching: boolean) => Layer = ( + requestCaching: boolean +) => + scopedDiscard( + fiberRuntime.fiberRefLocallyScoped(query.currentCacheEnabled, requestCaching) + ) + +/** + * @since 2.0.0 + * @category requests & batching + */ +export const setRequestCache: { + /** + * @since 2.0.0 + * @category requests & batching + */ + (cache: Effect.Effect): Layer> + /** + * @since 2.0.0 + * @category requests & batching + */ + (cache: Request.Cache): Layer +} = ((cache: Request.Cache | Effect.Effect) => + scopedDiscard( + core.isEffect(cache) ? + core.flatMap(cache, (x) => fiberRuntime.fiberRefLocallyScoped(query.currentCache as any, x)) : + fiberRuntime.fiberRefLocallyScoped(query.currentCache as any, cache) + )) as any + +/** + * @since 2.0.0 + * @category scheduler + */ +export const setScheduler: (scheduler: Scheduler.Scheduler) => Layer = ( + scheduler: Scheduler.Scheduler +): Layer => + scopedDiscard( + fiberRuntime.fiberRefLocallyScoped(Scheduler.currentScheduler, scheduler) + ) + +/** + * Create and add a span to the current span stack. + * + * The span is ended when the Layer is released. + * + * @since 2.0.0 + * @category tracing + */ +export const span: ( + name: string, + options?: Tracer.SpanOptions & { + readonly onEnd?: + | ((span: Tracer.Span, exit: Exit.Exit) => Effect.Effect) + | undefined + } +) => Layer = circularLayer.span + +/** + * Create a Layer that sets the current Tracer + * + * @since 2.0.0 + * @category tracing + */ +export const setTracer: (tracer: Tracer.Tracer) => Layer = circularLayer.setTracer + +/** + * @since 2.0.0 + * @category tracing + */ +export const setTracerEnabled: (enabled: boolean) => Layer = (enabled: boolean) => + scopedDiscard( + fiberRuntime.fiberRefLocallyScoped(core.currentTracerEnabled, enabled) + ) + +/** + * @since 2.0.0 + * @category tracing + */ +export const setTracerTiming: (enabled: boolean) => Layer = (enabled: boolean) => + scopedDiscard( + fiberRuntime.fiberRefLocallyScoped(core.currentTracerTimingEnabled, enabled) + ) + +/** + * @since 2.0.0 + * @category logging + */ +export const setUnhandledErrorLogLevel: (level: Option.Option) => Layer = ( + level: Option.Option +): Layer => + scopedDiscard( + fiberRuntime.fiberRefLocallyScoped(core.currentUnhandledErrorLogLevel, level) + ) + +/** + * @since 3.17.0 + * @category logging + */ +export const setVersionMismatchErrorLogLevel: (level: Option.Option) => Layer = ( + level: Option.Option +): Layer => + scopedDiscard( + fiberRuntime.fiberRefLocallyScoped(core.currentVersionMismatchErrorLogLevel, level) + ) + +/** + * @since 2.0.0 + * @category tracing + */ +export const withSpan: { + /** + * @since 2.0.0 + * @category tracing + */ + ( + name: string, + options?: Tracer.SpanOptions & { + readonly onEnd?: + | ((span: Tracer.Span, exit: Exit.Exit) => Effect.Effect) + | undefined + } + ): (self: Layer) => Layer> + /** + * @since 2.0.0 + * @category tracing + */ + ( + self: Layer, + name: string, + options?: Tracer.SpanOptions & { + readonly onEnd?: + | ((span: Tracer.Span, exit: Exit.Exit) => Effect.Effect) + | undefined + } + ): Layer> +} = internal.withSpan + +/** + * @since 2.0.0 + * @category tracing + */ +export const withParentSpan: { + /** + * @since 2.0.0 + * @category tracing + */ + (span: Tracer.AnySpan): (self: Layer) => Layer> + /** + * @since 2.0.0 + * @category tracing + */ + (self: Layer, span: Tracer.AnySpan): Layer> +} = internal.withParentSpan + +// ----------------------------------------------------------------------------- +// memo map +// ----------------------------------------------------------------------------- + +/** + * Constructs a `MemoMap` that can be used to build additional layers. + * + * @since 2.0.0 + * @category memo map + */ +export const makeMemoMap: Effect.Effect = internal.makeMemoMap + +/** + * Builds a layer into an `Effect` value, using the specified `MemoMap` to memoize + * the layer construction. + * + * @since 2.0.0 + * @category memo map + */ +export const buildWithMemoMap: { + /** + * Builds a layer into an `Effect` value, using the specified `MemoMap` to memoize + * the layer construction. + * + * @since 2.0.0 + * @category memo map + */ + (memoMap: MemoMap, scope: Scope.Scope): (self: Layer) => Effect.Effect, E, RIn> + /** + * Builds a layer into an `Effect` value, using the specified `MemoMap` to memoize + * the layer construction. + * + * @since 2.0.0 + * @category memo map + */ + (self: Layer, memoMap: MemoMap, scope: Scope.Scope): Effect.Effect, E, RIn> +} = internal.buildWithMemoMap + +/** + * Updates a service in the context with a new implementation. + * + * **Details** + * + * This function modifies the existing implementation of a service in the + * context. It retrieves the current service, applies the provided + * transformation function `f`, and replaces the old service with the + * transformed one. + * + * **When to Use** + * + * This is useful for adapting or extending a service's behavior during the + * creation of a layer. + * + * @since 3.13.0 + * @category utils + */ +export const updateService = dual< + /** + * Updates a service in the context with a new implementation. + * + * **Details** + * + * This function modifies the existing implementation of a service in the + * context. It retrieves the current service, applies the provided + * transformation function `f`, and replaces the old service with the + * transformed one. + * + * **When to Use** + * + * This is useful for adapting or extending a service's behavior during the + * creation of a layer. + * + * @since 3.13.0 + * @category utils + */ + (tag: Context.Tag, f: (a: A) => A) => (layer: Layer) => Layer, + /** + * Updates a service in the context with a new implementation. + * + * **Details** + * + * This function modifies the existing implementation of a service in the + * context. It retrieves the current service, applies the provided + * transformation function `f`, and replaces the old service with the + * transformed one. + * + * **When to Use** + * + * This is useful for adapting or extending a service's behavior during the + * creation of a layer. + * + * @since 3.13.0 + * @category utils + */ + (layer: Layer, tag: Context.Tag, f: (a: A) => A) => Layer +>(3, (layer, tag, f) => + provide( + layer, + map(context(), (c) => Context.add(c, tag, f(Context.unsafeGet(c, tag)))) + )) + +// ----------------------------------------------------------------------------- +// Type constraints +// ----------------------------------------------------------------------------- + +/** + * A no-op type constraint that enforces the success channel of a Layer conforms to + * the specified success type `ROut`. + * + * @example + * import { Layer } from "effect" + * + * // Ensure that the layer produces the expected services. + * const program = Layer.succeed(MyService, new MyServiceImpl()).pipe(Layer.ensureSuccessType()) + * + * @since 3.20.0 + * @category Type constraints + */ +export const ensureSuccessType = + () => (layer: Layer): Layer => layer + +/** + * A no-op type constraint that enforces the error channel of a Layer conforms to + * the specified error type `E`. + * + * @example + * import { Layer } from "effect" + * + * // Ensure that the layer does not expose any unhandled errors. + * const program = Layer.succeed(MyService, new MyServiceImpl()).pipe(Layer.ensureErrorType()) + * + * @since 3.20.0 + * @category Type constraints + */ +export const ensureErrorType = () => (layer: Layer): Layer => + layer + +/** + * A no-op type constraint that enforces the requirements channel of a Layer conforms to + * the specified requirements type `RIn`. + * + * @example + * import { Layer } from "effect" + * + * // Ensure that the layer does not have any requirements. + * const program = Layer.succeed(MyService, new MyServiceImpl()).pipe(Layer.ensureRequirementsType()) + * + * @since 3.20.0 + * @category Type constraints + */ +export const ensureRequirementsType = + () => (layer: Layer): Layer => layer diff --git a/backend/node_modules/effect/src/List.ts b/backend/node_modules/effect/src/List.ts new file mode 100644 index 0000000000000000000000000000000000000000..21426a33969b35e7093327644d838da8150f5333 --- /dev/null +++ b/backend/node_modules/effect/src/List.ts @@ -0,0 +1,1469 @@ +/** + * A data type for immutable linked lists representing ordered collections of elements of type `A`. + * + * This data type is optimal for last-in-first-out (LIFO), stack-like access patterns. If you need another access pattern, for example, random access or FIFO, consider using a collection more suited to this than `List`. + * + * **Performance** + * + * - Time: `List` has `O(1)` prepend and head/tail access. Most other operations are `O(n)` on the number of elements in the list. This includes the index-based lookup of elements, `length`, `append` and `reverse`. + * - Space: `List` implements structural sharing of the tail list. This means that many operations are either zero- or constant-memory cost. + * + * @since 2.0.0 + */ + +/** + * This file is ported from + * + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + */ +import * as Arr from "./Array.js" +import * as Chunk from "./Chunk.js" +import * as Either from "./Either.js" +import * as Equal from "./Equal.js" +import * as Equivalence from "./Equivalence.js" +import { dual, identity, unsafeCoerce } from "./Function.js" +import * as Hash from "./Hash.js" +import { format, type Inspectable, NodeInspectSymbol, toJSON } from "./Inspectable.js" +import type { nonEmpty, NonEmptyIterable } from "./NonEmptyIterable.js" +import * as Option from "./Option.js" +import type { Pipeable } from "./Pipeable.js" +import { pipeArguments } from "./Pipeable.js" +import { hasProperty, type Predicate, type Refinement } from "./Predicate.js" +import type { NoInfer } from "./Types.js" + +/** + * Represents an immutable linked list of elements of type `A`. + * + * A `List` is optimal for last-in-first-out (LIFO), stack-like access patterns. + * If you need another access pattern, for example, random access or FIFO, + * consider using a collection more suited for that other than `List`. + * + * @since 2.0.0 + * @category models + */ +export type List = Cons | Nil + +/** + * @since 2.0.0 + * @category symbol + */ +export const TypeId: unique symbol = Symbol.for("effect/List") + +/** + * @since 2.0.0 + * @category symbol + */ +export type TypeId = typeof TypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface Nil extends Iterable, Equal.Equal, Pipeable, Inspectable { + readonly [TypeId]: TypeId + readonly _tag: "Nil" +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Cons extends NonEmptyIterable, Equal.Equal, Pipeable, Inspectable { + readonly [TypeId]: TypeId + readonly _tag: "Cons" + readonly head: A + readonly tail: List +} + +/** + * Converts the specified `List` to an `Array`. + * + * @category conversions + * @since 2.0.0 + */ +export const toArray = (self: List): Array => Arr.fromIterable(self) + +/** + * @category equivalence + * @since 2.0.0 + */ +export const getEquivalence = (isEquivalent: Equivalence.Equivalence): Equivalence.Equivalence> => + Equivalence.mapInput(Arr.getEquivalence(isEquivalent), toArray) + +const _equivalence = getEquivalence(Equal.equals) + +const ConsProto: Omit, "head" | "tail" | typeof nonEmpty> = { + [TypeId]: TypeId, + _tag: "Cons", + toString(this: Cons) { + return format(this.toJSON()) + }, + toJSON(this: Cons) { + return { + _id: "List", + _tag: "Cons", + values: toArray(this).map(toJSON) + } + }, + [NodeInspectSymbol]() { + return this.toJSON() + }, + [Equal.symbol](this: Cons, that: unknown): boolean { + return isList(that) && + this._tag === that._tag && + _equivalence(this, that) + }, + [Hash.symbol](this: Cons): number { + return Hash.cached(this, Hash.array(toArray(this))) + }, + [Symbol.iterator](this: Cons): Iterator { + let done = false + // eslint-disable-next-line @typescript-eslint/no-this-alias + let self: List = this + return { + next() { + if (done) { + return this.return!() + } + if (self._tag === "Nil") { + done = true + return this.return!() + } + const value: unknown = self.head + self = self.tail + return { done, value } + }, + return(value?: unknown) { + if (!done) { + done = true + } + return { done: true, value } + } + } + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +interface MutableCons extends Cons { + head: A + tail: List +} + +const makeCons = (head: A, tail: List): MutableCons => { + const cons = Object.create(ConsProto) + cons.head = head + cons.tail = tail + return cons +} + +const NilHash = Hash.string("Nil") +const NilProto: Nil = { + [TypeId]: TypeId, + _tag: "Nil", + toString() { + return format(this.toJSON()) + }, + toJSON() { + return { + _id: "List", + _tag: "Nil" + } + }, + [NodeInspectSymbol]() { + return this.toJSON() + }, + [Hash.symbol](): number { + return NilHash + }, + [Equal.symbol](that: unknown): boolean { + return isList(that) && this._tag === that._tag + }, + [Symbol.iterator](): Iterator { + return { + next() { + return { done: true, value: undefined } + } + } + }, + pipe() { + return pipeArguments(this, arguments) + } +} as const + +const _Nil = Object.create(NilProto) as Nil + +/** + * Returns `true` if the specified value is a `List`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isList: { + /** + * Returns `true` if the specified value is a `List`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ + (u: Iterable): u is List + /** + * Returns `true` if the specified value is a `List`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ + (u: unknown): u is List +} = (u: unknown): u is List => hasProperty(u, TypeId) + +/** + * Returns `true` if the specified value is a `List.Nil`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isNil = (self: List): self is Nil => self._tag === "Nil" + +/** + * Returns `true` if the specified value is a `List.Cons`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isCons = (self: List): self is Cons => self._tag === "Cons" + +/** + * Returns the number of elements contained in the specified `List` + * + * @since 2.0.0 + * @category getters + */ +export const size = (self: List): number => { + let these = self + let len = 0 + while (!isNil(these)) { + len += 1 + these = these.tail + } + return len +} + +/** + * Constructs a new empty `List`. + * + * @since 2.0.0 + * @category constructors + */ +export const nil = (): List => _Nil + +/** + * Constructs a new `List.Cons` from the specified `head` and `tail` values. + * + * @since 2.0.0 + * @category constructors + */ +export const cons = (head: A, tail: List): Cons => makeCons(head, tail) + +/** + * Constructs a new empty `List`. + * + * Alias of {@link nil}. + * + * @since 2.0.0 + * @category constructors + */ +export const empty = nil + +/** + * Constructs a new `List` from the specified value. + * + * @since 2.0.0 + * @category constructors + */ +export const of = (value: A): Cons => makeCons(value, _Nil) + +/** + * Creates a new `List` from an iterable collection of values. + * + * @since 2.0.0 + * @category constructors + */ +export const fromIterable = (prefix: Iterable): List => { + const iterator = prefix[Symbol.iterator]() + let next: IteratorResult + if ((next = iterator.next()) && !next.done) { + const result = makeCons(next.value, _Nil) + let curr = result + while ((next = iterator.next()) && !next.done) { + const temp = makeCons(next.value, _Nil) + curr.tail = temp + curr = temp + } + return result + } else { + return _Nil + } +} + +/** + * Constructs a new `List` from the specified values. + * + * @since 2.0.0 + * @category constructors + */ +export const make = ]>( + ...elements: Elements +): Cons => fromIterable(elements) as any + +/** + * Appends the specified element to the end of the `List`, creating a new `Cons`. + * + * @category concatenating + * @since 2.0.0 + */ +export const append: { + /** + * Appends the specified element to the end of the `List`, creating a new `Cons`. + * + * @category concatenating + * @since 2.0.0 + */ + (element: B): (self: List) => Cons + /** + * Appends the specified element to the end of the `List`, creating a new `Cons`. + * + * @category concatenating + * @since 2.0.0 + */ + (self: List, element: B): Cons +} = dual(2, (self: List, element: B): Cons => appendAll(self, of(element))) + +/** + * Concatenates two lists, combining their elements. + * If either list is non-empty, the result is also a non-empty list. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { List } from "effect" + * + * assert.deepStrictEqual( + * List.make(1, 2).pipe(List.appendAll(List.make("a", "b")), List.toArray), + * [1, 2, "a", "b"] + * ) + * ``` + * + * @category concatenating + * @since 2.0.0 + */ +export const appendAll: { + /** + * Concatenates two lists, combining their elements. + * If either list is non-empty, the result is also a non-empty list. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { List } from "effect" + * + * assert.deepStrictEqual( + * List.make(1, 2).pipe(List.appendAll(List.make("a", "b")), List.toArray), + * [1, 2, "a", "b"] + * ) + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + , T extends List>(that: T): (self: S) => List.OrNonEmpty | List.Infer> + /** + * Concatenates two lists, combining their elements. + * If either list is non-empty, the result is also a non-empty list. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { List } from "effect" + * + * assert.deepStrictEqual( + * List.make(1, 2).pipe(List.appendAll(List.make("a", "b")), List.toArray), + * [1, 2, "a", "b"] + * ) + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + (self: List, that: Cons): Cons + /** + * Concatenates two lists, combining their elements. + * If either list is non-empty, the result is also a non-empty list. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { List } from "effect" + * + * assert.deepStrictEqual( + * List.make(1, 2).pipe(List.appendAll(List.make("a", "b")), List.toArray), + * [1, 2, "a", "b"] + * ) + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + (self: Cons, that: List): Cons + /** + * Concatenates two lists, combining their elements. + * If either list is non-empty, the result is also a non-empty list. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { List } from "effect" + * + * assert.deepStrictEqual( + * List.make(1, 2).pipe(List.appendAll(List.make("a", "b")), List.toArray), + * [1, 2, "a", "b"] + * ) + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + (self: List, that: List): List +} = dual(2, (self: List, that: List): List => prependAll(that, self)) + +/** + * Prepends the specified element to the beginning of the list. + * + * @category concatenating + * @since 2.0.0 + */ +export const prepend: { + /** + * Prepends the specified element to the beginning of the list. + * + * @category concatenating + * @since 2.0.0 + */ + (element: B): (self: List) => Cons + /** + * Prepends the specified element to the beginning of the list. + * + * @category concatenating + * @since 2.0.0 + */ + (self: List, element: B): Cons +} = dual(2, (self: List, element: B): Cons => cons(element, self)) + +/** + * Prepends the specified prefix list to the beginning of the specified list. + * If either list is non-empty, the result is also a non-empty list. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { List } from "effect" + * + * assert.deepStrictEqual( + * List.make(1, 2).pipe(List.prependAll(List.make("a", "b")), List.toArray), + * ["a", "b", 1, 2] + * ) + * ``` + * + * @category concatenating + * @since 2.0.0 + */ +export const prependAll: { + /** + * Prepends the specified prefix list to the beginning of the specified list. + * If either list is non-empty, the result is also a non-empty list. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { List } from "effect" + * + * assert.deepStrictEqual( + * List.make(1, 2).pipe(List.prependAll(List.make("a", "b")), List.toArray), + * ["a", "b", 1, 2] + * ) + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + , T extends List>(that: T): (self: S) => List.OrNonEmpty | List.Infer> + /** + * Prepends the specified prefix list to the beginning of the specified list. + * If either list is non-empty, the result is also a non-empty list. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { List } from "effect" + * + * assert.deepStrictEqual( + * List.make(1, 2).pipe(List.prependAll(List.make("a", "b")), List.toArray), + * ["a", "b", 1, 2] + * ) + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + (self: List, that: Cons): Cons + /** + * Prepends the specified prefix list to the beginning of the specified list. + * If either list is non-empty, the result is also a non-empty list. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { List } from "effect" + * + * assert.deepStrictEqual( + * List.make(1, 2).pipe(List.prependAll(List.make("a", "b")), List.toArray), + * ["a", "b", 1, 2] + * ) + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + (self: Cons, that: List): Cons + /** + * Prepends the specified prefix list to the beginning of the specified list. + * If either list is non-empty, the result is also a non-empty list. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { List } from "effect" + * + * assert.deepStrictEqual( + * List.make(1, 2).pipe(List.prependAll(List.make("a", "b")), List.toArray), + * ["a", "b", 1, 2] + * ) + * ``` + * + * @category concatenating + * @since 2.0.0 + */ + (self: List, that: List): List +} = dual(2, (self: List, prefix: List): List => { + if (isNil(self)) { + return prefix + } else if (isNil(prefix)) { + return self + } else { + const result = makeCons(prefix.head, self) + let curr = result + let that = prefix.tail + while (!isNil(that)) { + const temp = makeCons(that.head, self) + curr.tail = temp + curr = temp + that = that.tail + } + return result + } +}) + +/** + * Prepends the specified prefix list (in reverse order) to the beginning of the + * specified list. + * + * @category concatenating + * @since 2.0.0 + */ +export const prependAllReversed: { + /** + * Prepends the specified prefix list (in reverse order) to the beginning of the + * specified list. + * + * @category concatenating + * @since 2.0.0 + */ + (prefix: List): (self: List) => List + /** + * Prepends the specified prefix list (in reverse order) to the beginning of the + * specified list. + * + * @category concatenating + * @since 2.0.0 + */ + (self: List, prefix: List): List +} = dual(2, (self: List, prefix: List): List => { + let out: List = self + let pres = prefix + while (isCons(pres)) { + out = makeCons(pres.head, out) + pres = pres.tail + } + return out +}) + +/** + * Drops the first `n` elements from the specified list. + * + * @since 2.0.0 + * @category combinators + */ +export const drop: { + /** + * Drops the first `n` elements from the specified list. + * + * @since 2.0.0 + * @category combinators + */ + (n: number): (self: List) => List + /** + * Drops the first `n` elements from the specified list. + * + * @since 2.0.0 + * @category combinators + */ + (self: List, n: number): List +} = dual(2, (self: List, n: number): List => { + if (n <= 0) { + return self + } + if (n >= size(self)) { + return _Nil + } + let these = self + let i = 0 + while (!isNil(these) && i < n) { + these = these.tail + i += 1 + } + return these +}) + +/** + * Check if a predicate holds true for every `List` element. + * + * @since 2.0.0 + * @category elements + */ +export const every: { + /** + * Check if a predicate holds true for every `List` element. + * + * @since 2.0.0 + * @category elements + */ + (refinement: Refinement, B>): (self: List) => self is List + /** + * Check if a predicate holds true for every `List` element. + * + * @since 2.0.0 + * @category elements + */ + (predicate: Predicate): (self: List) => boolean + /** + * Check if a predicate holds true for every `List` element. + * + * @since 2.0.0 + * @category elements + */ + (self: List, refinement: Refinement): self is List + /** + * Check if a predicate holds true for every `List` element. + * + * @since 2.0.0 + * @category elements + */ + (self: List, predicate: Predicate): boolean +} = dual(2, (self: List, refinement: Refinement): self is List => { + for (const a of self) { + if (!refinement(a)) { + return false + } + } + return true +}) + +/** + * Check if a predicate holds true for some `List` element. + * + * @since 2.0.0 + * @category elements + */ +export const some: { + /** + * Check if a predicate holds true for some `List` element. + * + * @since 2.0.0 + * @category elements + */ + (predicate: Predicate>): (self: List) => self is Cons + /** + * Check if a predicate holds true for some `List` element. + * + * @since 2.0.0 + * @category elements + */ + (self: List, predicate: Predicate): self is Cons +} = dual(2, (self: List, predicate: Predicate): self is Cons => { + let these = self + while (!isNil(these)) { + if (predicate(these.head)) { + return true + } + these = these.tail + } + return false +}) + +/** + * Filters a list using the specified predicate. + * + * @since 2.0.0 + * @category combinators + */ +export const filter: { + /** + * Filters a list using the specified predicate. + * + * @since 2.0.0 + * @category combinators + */ + (refinement: Refinement, B>): (self: List) => List + /** + * Filters a list using the specified predicate. + * + * @since 2.0.0 + * @category combinators + */ + (predicate: Predicate>): (self: List) => List + /** + * Filters a list using the specified predicate. + * + * @since 2.0.0 + * @category combinators + */ + (self: List, refinement: Refinement): List + /** + * Filters a list using the specified predicate. + * + * @since 2.0.0 + * @category combinators + */ + (self: List, predicate: Predicate): List +} = dual(2, (self: List, predicate: Predicate): List => noneIn(self, predicate, false)) + +// everything seen so far is not included +const noneIn = ( + self: List, + predicate: Predicate, + isFlipped: boolean +): List => { + while (true) { + if (isNil(self)) { + return _Nil + } else { + if (predicate(self.head) !== isFlipped) { + return allIn(self, self.tail, predicate, isFlipped) + } else { + self = self.tail + } + } + } +} + +// everything from 'start' is included, if everything from this point is in we can return the origin +// start otherwise if we discover an element that is out we must create a new partial list. +const allIn = ( + start: List, + remaining: List, + predicate: Predicate, + isFlipped: boolean +): List => { + while (true) { + if (isNil(remaining)) { + return start + } else { + if (predicate(remaining.head) !== isFlipped) { + remaining = remaining.tail + } else { + return partialFill(start, remaining, predicate, isFlipped) + } + } + } +} + +// we have seen elements that should be included then one that should be excluded, start building +const partialFill = ( + origStart: List, + firstMiss: List, + predicate: Predicate, + isFlipped: boolean +): List => { + const newHead = makeCons(unsafeHead(origStart)!, _Nil) + let toProcess = unsafeTail(origStart)! as Cons + let currentLast = newHead + + // we know that all elements are :: until at least firstMiss.tail + while (!(toProcess === firstMiss)) { + const newElem = makeCons(unsafeHead(toProcess)!, _Nil) + currentLast.tail = newElem + currentLast = unsafeCoerce(newElem) + toProcess = unsafeCoerce(toProcess.tail) + } + + // at this point newHead points to a list which is a duplicate of all the 'in' elements up to the first miss. + // currentLast is the last element in that list. + + // now we are going to try and share as much of the tail as we can, only moving elements across when we have to. + let next = firstMiss.tail + let nextToCopy: Cons = unsafeCoerce(next) // the next element we would need to copy to our list if we cant share. + while (!isNil(next)) { + // generally recommended is next.isNonEmpty but this incurs an extra method call. + const head = unsafeHead(next)! + if (predicate(head) !== isFlipped) { + next = next.tail + } else { + // its not a match - do we have outstanding elements? + while (!(nextToCopy === next)) { + const newElem = makeCons(unsafeHead(nextToCopy)!, _Nil) + currentLast.tail = newElem + currentLast = newElem + nextToCopy = unsafeCoerce(nextToCopy.tail) + } + nextToCopy = unsafeCoerce(next.tail) + next = next.tail + } + } + + // we have remaining elements - they are unchanged attach them to the end + if (!isNil(nextToCopy)) { + currentLast.tail = nextToCopy + } + return newHead +} + +/** + * Filters and maps a list using the specified partial function. The resulting + * list may be smaller than the input list due to the possibility of the partial + * function not being defined for some elements. + * + * @since 2.0.0 + * @category combinators + */ +export const filterMap: { + /** + * Filters and maps a list using the specified partial function. The resulting + * list may be smaller than the input list due to the possibility of the partial + * function not being defined for some elements. + * + * @since 2.0.0 + * @category combinators + */ + (f: (a: A) => Option.Option): (self: List) => List + /** + * Filters and maps a list using the specified partial function. The resulting + * list may be smaller than the input list due to the possibility of the partial + * function not being defined for some elements. + * + * @since 2.0.0 + * @category combinators + */ + (self: List, f: (a: A) => Option.Option): List +} = dual(2, (self: List, f: (a: A) => Option.Option): List => { + const bs: Array = [] + for (const a of self) { + const oa = f(a) + if (Option.isSome(oa)) { + bs.push(oa.value) + } + } + return fromIterable(bs) +}) + +/** + * Removes all `None` values from the specified list. + * + * @since 2.0.0 + * @category combinators + */ +export const compact = (self: List>): List => filterMap(self, identity) + +/** + * Returns the first element that satisfies the specified + * predicate, or `None` if no such element exists. + * + * @category elements + * @since 2.0.0 + */ +export const findFirst: { + /** + * Returns the first element that satisfies the specified + * predicate, or `None` if no such element exists. + * + * @category elements + * @since 2.0.0 + */ + (refinement: Refinement, B>): (self: List) => Option.Option + /** + * Returns the first element that satisfies the specified + * predicate, or `None` if no such element exists. + * + * @category elements + * @since 2.0.0 + */ + (predicate: Predicate>): (self: List) => Option.Option + /** + * Returns the first element that satisfies the specified + * predicate, or `None` if no such element exists. + * + * @category elements + * @since 2.0.0 + */ + (self: List, refinement: Refinement): Option.Option + /** + * Returns the first element that satisfies the specified + * predicate, or `None` if no such element exists. + * + * @category elements + * @since 2.0.0 + */ + (self: List, predicate: Predicate): Option.Option +} = dual(2, (self: List, predicate: Predicate): Option.Option => { + let these = self + while (!isNil(these)) { + if (predicate(these.head)) { + return Option.some(these.head) + } + these = these.tail + } + return Option.none() +}) + +/** + * Applies a function to each element in a list and returns a new list containing the concatenated mapped elements. + * + * @since 2.0.0 + * @category sequencing + */ +export const flatMap: { + /** + * Applies a function to each element in a list and returns a new list containing the concatenated mapped elements. + * + * @since 2.0.0 + * @category sequencing + */ + , T extends List>(f: (a: List.Infer, i: number) => T): (self: S) => List.AndNonEmpty> + /** + * Applies a function to each element in a list and returns a new list containing the concatenated mapped elements. + * + * @since 2.0.0 + * @category sequencing + */ + (self: Cons, f: (a: A, i: number) => Cons): Cons + /** + * Applies a function to each element in a list and returns a new list containing the concatenated mapped elements. + * + * @since 2.0.0 + * @category sequencing + */ + (self: List, f: (a: A, i: number) => List): List +} = dual(2, (self: List, f: (a: A) => List): List => { + let rest = self + let head: MutableCons | undefined = undefined + let tail: MutableCons | undefined = undefined + while (!isNil(rest)) { + let bs = f(rest.head) + while (!isNil(bs)) { + const next = makeCons(bs.head, _Nil) + if (tail === undefined) { + head = next + } else { + tail.tail = next + } + tail = next + bs = bs.tail + } + rest = rest.tail + } + if (head === undefined) { + return _Nil + } + return head +}) + +/** + * Applies the specified function to each element of the `List`. + * + * @since 2.0.0 + * @category combinators + */ +export const forEach: { + /** + * Applies the specified function to each element of the `List`. + * + * @since 2.0.0 + * @category combinators + */ + (f: (a: A) => B): (self: List) => void + /** + * Applies the specified function to each element of the `List`. + * + * @since 2.0.0 + * @category combinators + */ + (self: List, f: (a: A) => B): void +} = dual(2, (self: List, f: (a: A) => B): void => { + let these = self + while (!isNil(these)) { + f(these.head) + these = these.tail + } +}) + +/** + * Returns the first element of the specified list, or `None` if the list is + * empty. + * + * @since 2.0.0 + * @category getters + */ +export const head = (self: List): Option.Option => isNil(self) ? Option.none() : Option.some(self.head) + +/** + * Returns the last element of the specified list, or `None` if the list is + * empty. + * + * @since 2.0.0 + * @category getters + */ +export const last = (self: List): Option.Option => isNil(self) ? Option.none() : Option.some(unsafeLast(self)!) + +/** + * @since 2.0.0 + */ +export declare namespace List { + /** + * @since 2.0.0 + */ + export type Infer> = S extends List ? A : never + + /** + * @since 2.0.0 + */ + export type With, A> = S extends Cons ? Cons : List + + /** + * @since 2.0.0 + */ + export type OrNonEmpty, T extends List, A> = S extends Cons ? Cons + : T extends Cons ? Cons + : List + + /** + * @since 2.0.0 + */ + export type AndNonEmpty, T extends List, A> = S extends Cons ? + T extends Cons ? Cons + : List : + List +} + +/** + * Applies the specified mapping function to each element of the list. + * + * @since 2.0.0 + * @category mapping + */ +export const map: { + /** + * Applies the specified mapping function to each element of the list. + * + * @since 2.0.0 + * @category mapping + */ + , B>(f: (a: List.Infer, i: number) => B): (self: S) => List.With + /** + * Applies the specified mapping function to each element of the list. + * + * @since 2.0.0 + * @category mapping + */ + , B>(self: S, f: (a: List.Infer, i: number) => B): List.With +} = dual(2, (self: List, f: (a: A, i: number) => B): List => { + if (isNil(self)) { + return self as unknown as List + } else { + let i = 0 + const head = makeCons(f(self.head, i++), _Nil) + let nextHead = head + let rest = self.tail + while (!isNil(rest)) { + const next = makeCons(f(rest.head, i++), _Nil) + nextHead.tail = next + nextHead = next + rest = rest.tail + } + return head + } +}) + +/** + * Partition a list into two lists, where the first list contains all elements + * that did not satisfy the specified predicate, and the second list contains + * all elements that did satisfy the specified predicate. + * + * @since 2.0.0 + * @category combinators + */ +export const partition: { + /** + * Partition a list into two lists, where the first list contains all elements + * that did not satisfy the specified predicate, and the second list contains + * all elements that did satisfy the specified predicate. + * + * @since 2.0.0 + * @category combinators + */ + (refinement: Refinement, B>): (self: List) => [excluded: List>, satisfying: List] + /** + * Partition a list into two lists, where the first list contains all elements + * that did not satisfy the specified predicate, and the second list contains + * all elements that did satisfy the specified predicate. + * + * @since 2.0.0 + * @category combinators + */ + (predicate: Predicate>): (self: List) => [excluded: List, satisfying: List] + /** + * Partition a list into two lists, where the first list contains all elements + * that did not satisfy the specified predicate, and the second list contains + * all elements that did satisfy the specified predicate. + * + * @since 2.0.0 + * @category combinators + */ + (self: List, refinement: Refinement): [excluded: List>, satisfying: List] + /** + * Partition a list into two lists, where the first list contains all elements + * that did not satisfy the specified predicate, and the second list contains + * all elements that did satisfy the specified predicate. + * + * @since 2.0.0 + * @category combinators + */ + (self: List, predicate: Predicate): [excluded: List, satisfying: List] +} = dual(2, (self: List, predicate: Predicate): [excluded: List, satisfying: List] => { + const left: Array = [] + const right: Array = [] + for (const a of self) { + if (predicate(a)) { + right.push(a) + } else { + left.push(a) + } + } + return [fromIterable(left), fromIterable(right)] +}) + +/** + * Partition a list into two lists, where the first list contains all elements + * for which the specified function returned a `Left`, and the second list + * contains all elements for which the specified function returned a `Right`. + * + * @since 2.0.0 + * @category combinators + */ +export const partitionMap: { + /** + * Partition a list into two lists, where the first list contains all elements + * for which the specified function returned a `Left`, and the second list + * contains all elements for which the specified function returned a `Right`. + * + * @since 2.0.0 + * @category combinators + */ + (f: (a: A) => Either.Either): (self: List) => [left: List, right: List] + /** + * Partition a list into two lists, where the first list contains all elements + * for which the specified function returned a `Left`, and the second list + * contains all elements for which the specified function returned a `Right`. + * + * @since 2.0.0 + * @category combinators + */ + (self: List, f: (a: A) => Either.Either): [left: List, right: List] +} = dual(2, (self: List, f: (a: A) => Either.Either): [left: List, right: List] => { + const left: Array = [] + const right: Array = [] + for (const a of self) { + const e = f(a) + if (Either.isLeft(e)) { + left.push(e.left) + } else { + right.push(e.right) + } + } + return [fromIterable(left), fromIterable(right)] +}) + +/** + * Folds over the elements of the list using the specified function, using the + * specified initial value. + * + * @since 2.0.0 + * @category folding + */ +export const reduce: { + /** + * Folds over the elements of the list using the specified function, using the + * specified initial value. + * + * @since 2.0.0 + * @category folding + */ + (zero: Z, f: (b: Z, a: A) => Z): (self: List) => Z + /** + * Folds over the elements of the list using the specified function, using the + * specified initial value. + * + * @since 2.0.0 + * @category folding + */ + (self: List, zero: Z, f: (b: Z, a: A) => Z): Z +} = dual(3, (self: List, zero: Z, f: (b: Z, a: A) => Z): Z => { + let acc = zero + let these = self + while (!isNil(these)) { + acc = f(acc, these.head) + these = these.tail + } + return acc +}) + +/** + * Folds over the elements of the list using the specified function, beginning + * with the last element of the list, using the specified initial value. + * + * @since 2.0.0 + * @category folding + */ +export const reduceRight: { + /** + * Folds over the elements of the list using the specified function, beginning + * with the last element of the list, using the specified initial value. + * + * @since 2.0.0 + * @category folding + */ + (zero: Z, f: (accumulator: Z, value: A) => Z): (self: List) => Z + /** + * Folds over the elements of the list using the specified function, beginning + * with the last element of the list, using the specified initial value. + * + * @since 2.0.0 + * @category folding + */ + (self: List, zero: Z, f: (accumulator: Z, value: A) => Z): Z +} = dual(3, (self: List, zero: Z, f: (accumulator: Z, value: A) => Z): Z => { + let acc = zero + let these = reverse(self) + while (!isNil(these)) { + acc = f(acc, these.head) + these = these.tail + } + return acc +}) + +/** + * Returns a new list with the elements of the specified list in reverse order. + * + * @since 2.0.0 + * @category elements + */ +export const reverse = (self: List): List => { + let result = empty() + let these = self + while (!isNil(these)) { + result = prepend(result, these.head) + these = these.tail + } + return result +} + +/** + * Splits the specified list into two lists at the specified index. + * + * @since 2.0.0 + * @category combinators + */ +export const splitAt: { + /** + * Splits the specified list into two lists at the specified index. + * + * @since 2.0.0 + * @category combinators + */ + (n: number): (self: List) => [beforeIndex: List, fromIndex: List] + /** + * Splits the specified list into two lists at the specified index. + * + * @since 2.0.0 + * @category combinators + */ + (self: List, n: number): [beforeIndex: List, fromIndex: List] +} = dual(2, (self: List, n: number): [List, List] => [take(self, n), drop(self, n)]) + +/** + * Returns the tail of the specified list, or `None` if the list is empty. + * + * @since 2.0.0 + * @category getters + */ +export const tail = (self: List): Option.Option> => isNil(self) ? Option.none() : Option.some(self.tail) + +/** + * Takes the specified number of elements from the beginning of the specified + * list. + * + * @since 2.0.0 + * @category combinators + */ +export const take: { + /** + * Takes the specified number of elements from the beginning of the specified + * list. + * + * @since 2.0.0 + * @category combinators + */ + (n: number): (self: List) => List + /** + * Takes the specified number of elements from the beginning of the specified + * list. + * + * @since 2.0.0 + * @category combinators + */ + (self: List, n: number): List +} = dual(2, (self: List, n: number): List => { + if (n <= 0) { + return _Nil + } + if (n >= size(self)) { + return self + } + let these = make(unsafeHead(self)) + let current = unsafeTail(self)! + for (let i = 1; i < n; i++) { + these = makeCons(unsafeHead(current), these) + current = unsafeTail(current!) + } + return reverse(these) +}) + +/** + * Converts the specified `List` to a `Chunk`. + * + * @since 2.0.0 + * @category conversions + */ +export const toChunk = (self: List): Chunk.Chunk => Chunk.fromIterable(self) + +const getExpectedListToBeNonEmptyErrorMessage = "Expected List to be non-empty" + +/** + * Unsafely returns the first element of the specified `List`. + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeHead = (self: List): A => { + if (isNil(self)) { + throw new Error(getExpectedListToBeNonEmptyErrorMessage) + } + return self.head +} + +/** + * Unsafely returns the last element of the specified `List`. + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeLast = (self: List): A => { + if (isNil(self)) { + throw new Error(getExpectedListToBeNonEmptyErrorMessage) + } + let these = self + let scout = self.tail + while (!isNil(scout)) { + these = scout + scout = scout.tail + } + return these.head +} + +/** + * Unsafely returns the tail of the specified `List`. + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeTail = (self: List): List => { + if (isNil(self)) { + throw new Error(getExpectedListToBeNonEmptyErrorMessage) + } + return self.tail +} diff --git a/backend/node_modules/effect/src/LogLevel.ts b/backend/node_modules/effect/src/LogLevel.ts new file mode 100644 index 0000000000000000000000000000000000000000..8cec3b8f84c9fba595b5f4b17c048086b9ecf7be --- /dev/null +++ b/backend/node_modules/effect/src/LogLevel.ts @@ -0,0 +1,387 @@ +/** + * @since 2.0.0 + */ +import type * as Effect from "./Effect.js" +import { dual, pipe } from "./Function.js" +import * as core from "./internal/core.js" +import * as number from "./Number.js" +import * as order from "./Order.js" +import type { Pipeable } from "./Pipeable.js" + +/** + * A `LogLevel` represents the log level associated with an individual logging + * operation. Log levels are used both to describe the granularity (or + * importance) of individual log statements, as well as to enable tuning + * verbosity of log output. + * + * @since 2.0.0 + * @category model + * @property ordinal - The priority of the log message. Larger values indicate higher priority. + * @property label - A label associated with the log level. + * @property syslog -The syslog severity level of the log level. + */ +export type LogLevel = All | Fatal | Error | Warning | Info | Debug | Trace | None + +/** + * @since 2.0.0 + * @category model + */ +export type Literal = LogLevel["_tag"] + +/** + * @since 2.0.0 + * @category model + */ +export interface All extends Pipeable { + readonly _tag: "All" + readonly label: "ALL" + readonly syslog: 0 + readonly ordinal: number +} + +/** + * @since 2.0.0 + * @category model + */ +export interface Fatal extends Pipeable { + readonly _tag: "Fatal" + readonly label: "FATAL" + readonly syslog: 2 + readonly ordinal: number +} + +/** + * @since 2.0.0 + * @category model + */ +export interface Error extends Pipeable { + readonly _tag: "Error" + readonly label: "ERROR" + readonly syslog: 3 + readonly ordinal: number +} + +/** + * @since 2.0.0 + * @category model + */ +export interface Warning extends Pipeable { + readonly _tag: "Warning" + readonly label: "WARN" + readonly syslog: 4 + readonly ordinal: number +} + +/** + * @since 2.0.0 + * @category model + */ +export interface Info extends Pipeable { + readonly _tag: "Info" + readonly label: "INFO" + readonly syslog: 6 + readonly ordinal: number +} + +/** + * @since 2.0.0 + * @category model + */ +export interface Debug extends Pipeable { + readonly _tag: "Debug" + readonly label: "DEBUG" + readonly syslog: 7 + readonly ordinal: number +} + +/** + * @since 2.0.0 + * @category model + */ +export interface Trace extends Pipeable { + readonly _tag: "Trace" + readonly label: "TRACE" + readonly syslog: 7 + readonly ordinal: number +} + +/** + * @since 2.0.0 + * @category model + */ +export interface None extends Pipeable { + readonly _tag: "None" + readonly label: "OFF" + readonly syslog: 7 + readonly ordinal: number +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const All: LogLevel = core.logLevelAll + +/** + * @since 2.0.0 + * @category constructors + */ +export const Fatal: LogLevel = core.logLevelFatal + +/** + * @since 2.0.0 + * @category constructors + */ +export const Error: LogLevel = core.logLevelError + +/** + * @since 2.0.0 + * @category constructors + */ +export const Warning: LogLevel = core.logLevelWarning + +/** + * @since 2.0.0 + * @category constructors + */ +export const Info: LogLevel = core.logLevelInfo + +/** + * @since 2.0.0 + * @category constructors + */ +export const Debug: LogLevel = core.logLevelDebug + +/** + * @since 2.0.0 + * @category constructors + */ +export const Trace: LogLevel = core.logLevelTrace + +/** + * @since 2.0.0 + * @category constructors + */ +export const None: LogLevel = core.logLevelNone + +/** + * @since 2.0.0 + * @category constructors + */ +export const allLevels = core.allLogLevels + +/** + * Temporarily sets a `LogLevel` for an `Effect` workflow. + * + * **Details** + * + * This function allows you to apply a specific `LogLevel` locally to an + * `Effect` workflow. Once the workflow completes, the `LogLevel` reverts to its + * previous state. + * + * **When to Use** + * + * This is particularly useful when you want to adjust the verbosity of logging + * for specific parts of your program without affecting the global log level. + * + * @example + * ```ts + * import { Effect, LogLevel } from "effect" + * + * const program = Effect.gen(function*() { + * yield* Effect.log("message1") + * yield* Effect.gen(function*() { + * yield* Effect.log("message2") + * yield* Effect.log("message3") + * }).pipe(LogLevel.locally(LogLevel.Warning)) + * }) + * + * Effect.runFork(program) + * // timestamp=... level=INFO fiber=#0 message=message1 + * // timestamp=... level=WARN fiber=#0 message=message2 + * // timestamp=... level=WARN fiber=#0 message=message3 + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const locally: { + /** + * Temporarily sets a `LogLevel` for an `Effect` workflow. + * + * **Details** + * + * This function allows you to apply a specific `LogLevel` locally to an + * `Effect` workflow. Once the workflow completes, the `LogLevel` reverts to its + * previous state. + * + * **When to Use** + * + * This is particularly useful when you want to adjust the verbosity of logging + * for specific parts of your program without affecting the global log level. + * + * @example + * ```ts + * import { Effect, LogLevel } from "effect" + * + * const program = Effect.gen(function*() { + * yield* Effect.log("message1") + * yield* Effect.gen(function*() { + * yield* Effect.log("message2") + * yield* Effect.log("message3") + * }).pipe(LogLevel.locally(LogLevel.Warning)) + * }) + * + * Effect.runFork(program) + * // timestamp=... level=INFO fiber=#0 message=message1 + * // timestamp=... level=WARN fiber=#0 message=message2 + * // timestamp=... level=WARN fiber=#0 message=message3 + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: LogLevel): (use: Effect.Effect) => Effect.Effect + /** + * Temporarily sets a `LogLevel` for an `Effect` workflow. + * + * **Details** + * + * This function allows you to apply a specific `LogLevel` locally to an + * `Effect` workflow. Once the workflow completes, the `LogLevel` reverts to its + * previous state. + * + * **When to Use** + * + * This is particularly useful when you want to adjust the verbosity of logging + * for specific parts of your program without affecting the global log level. + * + * @example + * ```ts + * import { Effect, LogLevel } from "effect" + * + * const program = Effect.gen(function*() { + * yield* Effect.log("message1") + * yield* Effect.gen(function*() { + * yield* Effect.log("message2") + * yield* Effect.log("message3") + * }).pipe(LogLevel.locally(LogLevel.Warning)) + * }) + * + * Effect.runFork(program) + * // timestamp=... level=INFO fiber=#0 message=message1 + * // timestamp=... level=WARN fiber=#0 message=message2 + * // timestamp=... level=WARN fiber=#0 message=message3 + * ``` + * + * @since 2.0.0 + * @category utils + */ + (use: Effect.Effect, self: LogLevel): Effect.Effect +} = dual( + 2, + (use: Effect.Effect, self: LogLevel): Effect.Effect => + core.fiberRefLocally(use, core.currentLogLevel, self) +) + +/** + * @since 2.0.0 + * @category instances + */ +export const Order: order.Order = pipe( + number.Order, + order.mapInput((level: LogLevel) => level.ordinal) +) + +/** + * @since 2.0.0 + * @category ordering + */ +export const lessThan: { + /** + * @since 2.0.0 + * @category ordering + */ + (that: LogLevel): (self: LogLevel) => boolean + /** + * @since 2.0.0 + * @category ordering + */ + (self: LogLevel, that: LogLevel): boolean +} = order.lessThan(Order) + +/** + * @since 2.0.0 + * @category ordering + */ +export const lessThanEqual: { + /** + * @since 2.0.0 + * @category ordering + */ + (that: LogLevel): (self: LogLevel) => boolean + /** + * @since 2.0.0 + * @category ordering + */ + (self: LogLevel, that: LogLevel): boolean +} = order.lessThanOrEqualTo(Order) + +/** + * @since 2.0.0 + * @category ordering + */ +export const greaterThan: { + /** + * @since 2.0.0 + * @category ordering + */ + (that: LogLevel): (self: LogLevel) => boolean + /** + * @since 2.0.0 + * @category ordering + */ + (self: LogLevel, that: LogLevel): boolean +} = order.greaterThan(Order) + +/** + * @since 2.0.0 + * @category ordering + */ +export const greaterThanEqual: { + /** + * @since 2.0.0 + * @category ordering + */ + (that: LogLevel): (self: LogLevel) => boolean + /** + * @since 2.0.0 + * @category ordering + */ + (self: LogLevel, that: LogLevel): boolean +} = order.greaterThanOrEqualTo(Order) + +/** + * @since 2.0.0 + * @category conversions + */ +export const fromLiteral = (literal: Literal): LogLevel => { + switch (literal) { + case "All": + return All + case "Debug": + return Debug + case "Error": + return Error + case "Fatal": + return Fatal + case "Info": + return Info + case "Trace": + return Trace + case "None": + return None + case "Warning": + return Warning + } +} diff --git a/backend/node_modules/effect/src/Logger.ts b/backend/node_modules/effect/src/Logger.ts new file mode 100644 index 0000000000000000000000000000000000000000..70315533732e991a8255b6ec1a1497ddd9897f67 --- /dev/null +++ b/backend/node_modules/effect/src/Logger.ts @@ -0,0 +1,863 @@ +/** + * @since 2.0.0 + */ +import type * as Cause from "./Cause.js" +import type { DurationInput } from "./Duration.js" +import type { Effect } from "./Effect.js" +import type * as FiberId from "./FiberId.js" +import type * as FiberRefs from "./FiberRefs.js" +import type { LazyArg } from "./Function.js" +import type * as HashMap from "./HashMap.js" +import * as fiberRuntime from "./internal/fiberRuntime.js" +import * as circular from "./internal/layer/circular.js" +import * as internalCircular from "./internal/logger-circular.js" +import * as internal from "./internal/logger.js" +import type * as Layer from "./Layer.js" +import type * as List from "./List.js" +import type * as LogLevel from "./LogLevel.js" +import type * as LogSpan from "./LogSpan.js" +import type * as Option from "./Option.js" +import type { Pipeable } from "./Pipeable.js" +import type { Scope } from "./Scope.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const LoggerTypeId: unique symbol = internal.LoggerTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type LoggerTypeId = typeof LoggerTypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface Logger extends Logger.Variance, Pipeable { + log(options: Logger.Options): Output +} + +/** + * @since 2.0.0 + */ +export declare namespace Logger { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [LoggerTypeId]: { + readonly _Message: Types.Contravariant + readonly _Output: Types.Covariant + } + } + + /** + * @since 2.0.0 + * @category models + */ + export interface Options { + readonly fiberId: FiberId.FiberId + readonly logLevel: LogLevel.LogLevel + readonly message: Message + readonly cause: Cause.Cause + readonly context: FiberRefs.FiberRefs + readonly spans: List.List + readonly annotations: HashMap.HashMap + readonly date: Date + } +} + +/** + * Creates a custom logger that formats log messages according to the provided + * function. + * + * @example + * ```ts + * import { Effect, Logger, LogLevel } from "effect" + * + * const logger = Logger.make(({ logLevel, message }) => { + * globalThis.console.log(`[${logLevel.label}] ${message}`) + * }) + * + * const task1 = Effect.logDebug("task1 done") + * const task2 = Effect.logDebug("task2 done") + * + * const program = Effect.gen(function*() { + * yield* Effect.log("start") + * yield* task1 + * yield* task2 + * yield* Effect.log("done") + * }).pipe( + * Logger.withMinimumLogLevel(LogLevel.Debug), + * Effect.provide(Logger.replace(Logger.defaultLogger, logger)) + * ) + * + * Effect.runFork(program) + * // [INFO] start + * // [DEBUG] task1 done + * // [DEBUG] task2 done + * // [INFO] done + * ``` + * + * @category constructors + * @since 2.0.0 + */ +export const make: (log: (options: Logger.Options) => Output) => Logger = + internal.makeLogger + +/** + * @since 2.0.0 + * @category context + */ +export const add: (logger: Logger) => Layer.Layer = circular.addLogger + +/** + * @since 2.0.0 + * @category context + */ +export const addEffect: (effect: Effect, E, R>) => Layer.Layer = + circular.addLoggerEffect + +/** + * @since 2.0.0 + * @category context + */ +export const addScoped: ( + effect: Effect, E, R> +) => Layer.Layer> = circular.addLoggerScoped + +/** + * @since 2.0.0 + * @category mapping + */ +export const mapInput: { + /** + * @since 2.0.0 + * @category mapping + */ + (f: (message: Message2) => Message): (self: Logger) => Logger + /** + * @since 2.0.0 + * @category mapping + */ + (self: Logger, f: (message: Message2) => Message): Logger +} = internal.mapInput + +/** + * @since 2.0.0 + * @category mapping + */ +export const mapInputOptions: { + /** + * @since 2.0.0 + * @category mapping + */ + (f: (options: Logger.Options) => Logger.Options): (self: Logger) => Logger + /** + * @since 2.0.0 + * @category mapping + */ + ( + self: Logger, + f: (options: Logger.Options) => Logger.Options + ): Logger +} = internal.mapInputOptions + +/** + * Returns a version of this logger that only logs messages when the log level + * satisfies the specified predicate. + * + * @since 2.0.0 + * @category filtering + */ +export const filterLogLevel: { + /** + * Returns a version of this logger that only logs messages when the log level + * satisfies the specified predicate. + * + * @since 2.0.0 + * @category filtering + */ + (f: (logLevel: LogLevel.LogLevel) => boolean): (self: Logger) => Logger> + /** + * Returns a version of this logger that only logs messages when the log level + * satisfies the specified predicate. + * + * @since 2.0.0 + * @category filtering + */ + (self: Logger, f: (logLevel: LogLevel.LogLevel) => boolean): Logger> +} = internal.filterLogLevel + +/** + * @since 2.0.0 + * @category mapping + */ +export const map: { + /** + * @since 2.0.0 + * @category mapping + */ + (f: (output: Output) => Output2): (self: Logger) => Logger + /** + * @since 2.0.0 + * @category mapping + */ + (self: Logger, f: (output: Output) => Output2): Logger +} = internal.map + +/** + * Creates a batched logger that groups log messages together and processes them + * in intervals. + * + * @example + * ```ts + * import { Console, Effect, Logger } from "effect" + * + * const LoggerLive = Logger.replaceScoped( + * Logger.defaultLogger, + * Logger.logfmtLogger.pipe( + * Logger.batched("500 millis", (messages) => Console.log("BATCH", `[\n${messages.join("\n")}\n]`)) + * ) + * ) + * + * const program = Effect.gen(function*() { + * yield* Effect.log("one") + * yield* Effect.log("two") + * yield* Effect.log("three") + * }).pipe(Effect.provide(LoggerLive)) + * + * Effect.runFork(program) + * // BATCH [ + * // timestamp=... level=INFO fiber=#0 message=one + * // timestamp=... level=INFO fiber=#0 message=two + * // timestamp=... level=INFO fiber=#0 message=three + * // ] + * ``` + * + * @since 2.0.0 + * @category mapping + */ +export const batched: { + /** + * Creates a batched logger that groups log messages together and processes them + * in intervals. + * + * @example + * ```ts + * import { Console, Effect, Logger } from "effect" + * + * const LoggerLive = Logger.replaceScoped( + * Logger.defaultLogger, + * Logger.logfmtLogger.pipe( + * Logger.batched("500 millis", (messages) => Console.log("BATCH", `[\n${messages.join("\n")}\n]`)) + * ) + * ) + * + * const program = Effect.gen(function*() { + * yield* Effect.log("one") + * yield* Effect.log("two") + * yield* Effect.log("three") + * }).pipe(Effect.provide(LoggerLive)) + * + * Effect.runFork(program) + * // BATCH [ + * // timestamp=... level=INFO fiber=#0 message=one + * // timestamp=... level=INFO fiber=#0 message=two + * // timestamp=... level=INFO fiber=#0 message=three + * // ] + * ``` + * + * @since 2.0.0 + * @category mapping + */ + ( + window: DurationInput, + f: (messages: Array>) => Effect + ): (self: Logger) => Effect, never, R | Scope> + /** + * Creates a batched logger that groups log messages together and processes them + * in intervals. + * + * @example + * ```ts + * import { Console, Effect, Logger } from "effect" + * + * const LoggerLive = Logger.replaceScoped( + * Logger.defaultLogger, + * Logger.logfmtLogger.pipe( + * Logger.batched("500 millis", (messages) => Console.log("BATCH", `[\n${messages.join("\n")}\n]`)) + * ) + * ) + * + * const program = Effect.gen(function*() { + * yield* Effect.log("one") + * yield* Effect.log("two") + * yield* Effect.log("three") + * }).pipe(Effect.provide(LoggerLive)) + * + * Effect.runFork(program) + * // BATCH [ + * // timestamp=... level=INFO fiber=#0 message=one + * // timestamp=... level=INFO fiber=#0 message=two + * // timestamp=... level=INFO fiber=#0 message=three + * // ] + * ``` + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Logger, + window: DurationInput, + f: (messages: Array>) => Effect + ): Effect, never, Scope | R> +} = fiberRuntime.batchedLogger + +/** + * @since 2.0.0 + * @category console + */ +export const withConsoleLog: (self: Logger) => Logger = fiberRuntime.loggerWithConsoleLog + +/** + * Takes a `Logger` and returns a logger that calls the respective `Console` method + * based on the log level. + * + * @example + * ```ts + * import { Logger, Effect } from "effect" + * + * const loggerLayer = Logger.replace( + * Logger.defaultLogger, + * Logger.withLeveledConsole(Logger.stringLogger), + * ) + * + * Effect.gen(function* () { + * yield* Effect.logError("an error") + * yield* Effect.logInfo("an info") + * }).pipe(Effect.provide(loggerLayer)) + * ``` + * + * @since 3.8.0 + * @category console + */ +export const withLeveledConsole: (self: Logger) => Logger = fiberRuntime.loggerWithLeveledLog + +/** + * @since 2.0.0 + * @category console + */ +export const withConsoleError: (self: Logger) => Logger = fiberRuntime.loggerWithConsoleError + +/** + * A logger that does nothing in response to logging events. + * + * @since 2.0.0 + * @category constructors + */ +export const none: Logger = internal.none + +/** + * @since 2.0.0 + * @category context + */ +export const remove: (logger: Logger) => Layer.Layer = circular.removeLogger + +/** + * @since 2.0.0 + * @category context + */ +export const replace: { + /** + * @since 2.0.0 + * @category context + */ + (that: Logger): (self: Logger) => Layer.Layer + /** + * @since 2.0.0 + * @category context + */ + (self: Logger, that: Logger): Layer.Layer +} = circular.replaceLogger + +/** + * @since 2.0.0 + * @category context + */ +export const replaceEffect: { + /** + * @since 2.0.0 + * @category context + */ + (that: Effect, E, R>): (self: Logger) => Layer.Layer + /** + * @since 2.0.0 + * @category context + */ + (self: Logger, that: Effect, E, R>): Layer.Layer +} = circular.replaceLoggerEffect + +/** + * @since 2.0.0 + * @category context + */ +export const replaceScoped: { + /** + * @since 2.0.0 + * @category context + */ + (that: Effect, E, R>): (self: Logger) => Layer.Layer> + /** + * @since 2.0.0 + * @category context + */ + (self: Logger, that: Effect, E, R>): Layer.Layer> +} = circular.replaceLoggerScoped + +/** + * @since 2.0.0 + * @category constructors + */ +export const simple: (log: (a: A) => B) => Logger = internal.simple + +/** + * @since 2.0.0 + * @category constructors + */ +export const succeed: (value: A) => Logger = internal.succeed + +/** + * @since 2.0.0 + * @category constructors + */ +export const sync: (evaluate: LazyArg) => Logger = internal.sync + +/** + * @since 2.0.0 + * @category constructors + */ +export const test: { + /** + * @since 2.0.0 + * @category constructors + */ + (input: Message): (self: Logger) => Output + /** + * @since 2.0.0 + * @category constructors + */ + (self: Logger, input: Message): Output +} = internalCircular.test + +/** + * Sets the minimum log level for subsequent logging operations, allowing + * control over which log messages are displayed based on their severity. + * + * @example + * ```ts + * import { Effect, Logger, LogLevel } from "effect" + * + * const program = Effect.logDebug("message1").pipe(Logger.withMinimumLogLevel(LogLevel.Debug)) + * + * Effect.runFork(program) + * // timestamp=... level=DEBUG fiber=#0 message=message1 + * ``` + * + * @since 2.0.0 + * @category context + */ +export const withMinimumLogLevel: { + /** + * Sets the minimum log level for subsequent logging operations, allowing + * control over which log messages are displayed based on their severity. + * + * @example + * ```ts + * import { Effect, Logger, LogLevel } from "effect" + * + * const program = Effect.logDebug("message1").pipe(Logger.withMinimumLogLevel(LogLevel.Debug)) + * + * Effect.runFork(program) + * // timestamp=... level=DEBUG fiber=#0 message=message1 + * ``` + * + * @since 2.0.0 + * @category context + */ + (level: LogLevel.LogLevel): (self: Effect) => Effect + /** + * Sets the minimum log level for subsequent logging operations, allowing + * control over which log messages are displayed based on their severity. + * + * @example + * ```ts + * import { Effect, Logger, LogLevel } from "effect" + * + * const program = Effect.logDebug("message1").pipe(Logger.withMinimumLogLevel(LogLevel.Debug)) + * + * Effect.runFork(program) + * // timestamp=... level=DEBUG fiber=#0 message=message1 + * ``` + * + * @since 2.0.0 + * @category context + */ + (self: Effect, level: LogLevel.LogLevel): Effect +} = circular.withMinimumLogLevel + +/** + * @since 2.0.0 + * @category tracing + */ +export const withSpanAnnotations: (self: Logger) => Logger = + fiberRuntime.loggerWithSpanAnnotations + +/** + * Combines this logger with the specified logger to produce a new logger that + * logs to both this logger and that logger. + * + * @since 2.0.0 + * @category zipping + */ +export const zip: { + /** + * Combines this logger with the specified logger to produce a new logger that + * logs to both this logger and that logger. + * + * @since 2.0.0 + * @category zipping + */ + (that: Logger): (self: Logger) => Logger + /** + * Combines this logger with the specified logger to produce a new logger that + * logs to both this logger and that logger. + * + * @since 2.0.0 + * @category zipping + */ + (self: Logger, that: Logger): Logger +} = internal.zip + +/** + * @since 2.0.0 + * @category zipping + */ +export const zipLeft: { + /** + * @since 2.0.0 + * @category zipping + */ + (that: Logger): (self: Logger) => Logger + /** + * @since 2.0.0 + * @category zipping + */ + (self: Logger, that: Logger): Logger +} = internal.zipLeft + +/** + * @since 2.0.0 + * @category zipping + */ +export const zipRight: { + /** + * @since 2.0.0 + * @category zipping + */ + (that: Logger): (self: Logger) => Logger + /** + * @since 2.0.0 + * @category zipping + */ + (self: Logger, that: Logger): Logger +} = internal.zipRight + +/** + * @since 2.0.0 + * @category constructors + */ +export const defaultLogger: Logger = fiberRuntime.defaultLogger + +/** + * The `jsonLogger` logger formats log entries as JSON objects, making them easy to + * integrate with logging systems that consume JSON data. + * + * @example + * ```ts + * import { Effect, Logger } from "effect" + * + * const program = Effect.log("message1", "message2").pipe( + * Effect.annotateLogs({ key1: "value1", key2: "value2" }), + * Effect.withLogSpan("myspan") + * ) + * + * Effect.runFork(program.pipe(Effect.provide(Logger.json))) + * // {"message":["message1","message2"],"logLevel":"INFO","timestamp":"...","annotations":{"key2":"value2","key1":"value1"},"spans":{"myspan":0},"fiberId":"#0"} + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const jsonLogger: Logger = internal.jsonLogger + +/** + * This logger outputs logs in a human-readable format that is easy to read + * during development or in a production console. + * + * @example + * ```ts + * import { Effect, Logger } from "effect" + * + * const program = Effect.log("message1", "message2").pipe( + * Effect.annotateLogs({ key1: "value1", key2: "value2" }), + * Effect.withLogSpan("myspan") + * ) + * + * Effect.runFork(program.pipe(Effect.provide(Logger.logFmt))) + * // timestamp=... level=INFO fiber=#0 message=message1 message=message2 myspan=0ms key2=value2 key1=value1 + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const logfmtLogger: Logger = internal.logfmtLogger + +/** + * @since 2.0.0 + * @category constructors + */ +export const stringLogger: Logger = internal.stringLogger + +/** + * The pretty logger utilizes the capabilities of the console API to generate + * visually engaging and color-enhanced log outputs. This feature is + * particularly useful for improving the readability of log messages during + * development and debugging processes. + * + * @example + * ```ts + * import { Effect, Logger } from "effect" + * + * const program = Effect.log("message1", "message2").pipe( + * Effect.annotateLogs({ key1: "value1", key2: "value2" }), + * Effect.withLogSpan("myspan") + * ) + * + * Effect.runFork(program.pipe(Effect.provide(Logger.pretty))) + * // green --v v-- bold and cyan + * // [07:51:54.434] INFO (#0) myspan=1ms: message1 + * // message2 + * // v-- bold + * // key2: value2 + * // key1: value1 + * ``` + * + * @since 3.5.0 + * @category constructors + */ +export const prettyLogger: ( + options?: { + readonly colors?: "auto" | boolean | undefined + readonly stderr?: boolean | undefined + readonly formatDate?: ((date: Date) => string) | undefined + readonly mode?: "browser" | "tty" | "auto" | undefined + } +) => Logger = internal.prettyLogger + +/** + * A default version of the pretty logger. + * + * @since 3.8.0 + * @category constructors + */ +export const prettyLoggerDefault: Logger = internal.prettyLoggerDefault + +/** + * The structured logger provides detailed log outputs, structured in a way that + * retains comprehensive traceability of the events, suitable for deeper + * analysis and troubleshooting. + * + * @example + * ```ts + * import { Effect, Logger } from "effect" + * + * const program = Effect.log("message1", "message2").pipe( + * Effect.annotateLogs({ key1: "value1", key2: "value2" }), + * Effect.withLogSpan("myspan") + * ) + * + * Effect.runFork(program.pipe(Effect.provide(Logger.structured))) + * // { + * // message: [ 'message1', 'message2' ], + * // logLevel: 'INFO', + * // timestamp: '2024-07-09T14:05:41.623Z', + * // cause: undefined, + * // annotations: { key2: 'value2', key1: 'value1' }, + * // spans: { myspan: 0 }, + * // fiberId: '#0' + * // } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const structuredLogger: Logger< + unknown, + { + readonly logLevel: string + readonly fiberId: string + readonly timestamp: string + readonly message: unknown + readonly cause: string | undefined + readonly annotations: Record + readonly spans: Record + } +> = internal.structuredLogger + +/** + * @since 2.0.0 + * @category constructors + */ +export const tracerLogger: Logger = fiberRuntime.tracerLogger + +/** + * The `json` logger formats log entries as JSON objects, making them easy to + * integrate with logging systems that consume JSON data. + * + * @example + * ```ts + * import { Effect, Logger } from "effect" + * + * const program = Effect.log("message1", "message2").pipe( + * Effect.annotateLogs({ key1: "value1", key2: "value2" }), + * Effect.withLogSpan("myspan") + * ) + * + * Effect.runFork(program.pipe(Effect.provide(Logger.json))) + * // {"message":["message1","message2"],"logLevel":"INFO","timestamp":"...","annotations":{"key2":"value2","key1":"value1"},"spans":{"myspan":0},"fiberId":"#0"} + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const json: Layer.Layer = replace(fiberRuntime.defaultLogger, fiberRuntime.jsonLogger) + +/** + * This logger outputs logs in a human-readable format that is easy to read + * during development or in a production console. + * + * @example + * ```ts + * import { Effect, Logger } from "effect" + * + * const program = Effect.log("message1", "message2").pipe( + * Effect.annotateLogs({ key1: "value1", key2: "value2" }), + * Effect.withLogSpan("myspan") + * ) + * + * Effect.runFork(program.pipe(Effect.provide(Logger.logFmt))) + * // timestamp=... level=INFO fiber=#0 message=message1 message=message2 myspan=0ms key2=value2 key1=value1 + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const logFmt: Layer.Layer = replace(fiberRuntime.defaultLogger, fiberRuntime.logFmtLogger) + +/** + * The pretty logger utilizes the capabilities of the console API to generate + * visually engaging and color-enhanced log outputs. This feature is + * particularly useful for improving the readability of log messages during + * development and debugging processes. + * + * @example + * ```ts + * import { Effect, Logger } from "effect" + * + * const program = Effect.log("message1", "message2").pipe( + * Effect.annotateLogs({ key1: "value1", key2: "value2" }), + * Effect.withLogSpan("myspan") + * ) + * + * Effect.runFork(program.pipe(Effect.provide(Logger.pretty))) + * // green --v v-- bold and cyan + * // [07:51:54.434] INFO (#0) myspan=1ms: message1 + * // message2 + * // v-- bold + * // key2: value2 + * // key1: value1 + * ``` + * + * @since 3.5.0 + * @category constructors + */ +export const pretty: Layer.Layer = replace(fiberRuntime.defaultLogger, fiberRuntime.prettyLogger) + +/** + * The structured logger provides detailed log outputs, structured in a way that + * retains comprehensive traceability of the events, suitable for deeper + * analysis and troubleshooting. + * + * @example + * ```ts + * import { Effect, Logger } from "effect" + * + * const program = Effect.log("message1", "message2").pipe( + * Effect.annotateLogs({ key1: "value1", key2: "value2" }), + * Effect.withLogSpan("myspan") + * ) + * + * Effect.runFork(program.pipe(Effect.provide(Logger.structured))) + * // { + * // message: [ 'message1', 'message2' ], + * // logLevel: 'INFO', + * // timestamp: '2024-07-09T14:05:41.623Z', + * // cause: undefined, + * // annotations: { key2: 'value2', key1: 'value1' }, + * // spans: { myspan: 0 }, + * // fiberId: '#0' + * // } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const structured: Layer.Layer = replace(fiberRuntime.defaultLogger, fiberRuntime.structuredLogger) + +/** + * Sets the minimum log level for logging operations, allowing control over + * which log messages are displayed based on their severity. + * + * @example + * ```ts + * import { Effect, Logger, LogLevel } from "effect" + * + * const program = Effect.gen(function*() { + * yield* Effect.log("Executing task...") + * yield* Effect.sleep("100 millis") + * console.log("task done") + * }) + * + * // Logging disabled using a layer + * Effect.runFork(program.pipe(Effect.provide(Logger.minimumLogLevel(LogLevel.None)))) + * // task done + * ``` + * + * @since 2.0.0 + * @category context + */ +export const minimumLogLevel: (level: LogLevel.LogLevel) => Layer.Layer = circular.minimumLogLevel + +/** + * Returns `true` if the specified value is a `Logger`, otherwise returns `false`. + * + * @since 1.0.0 + * @category guards + */ +export const isLogger: (u: unknown) => u is Logger = internal.isLogger diff --git a/backend/node_modules/effect/src/Mailbox.ts b/backend/node_modules/effect/src/Mailbox.ts new file mode 100644 index 0000000000000000000000000000000000000000..78c9eeebb042586ce51dd44354fac8c1d649d576 --- /dev/null +++ b/backend/node_modules/effect/src/Mailbox.ts @@ -0,0 +1,298 @@ +/** + * @since 3.8.0 + * @experimental + */ +import type { Cause, NoSuchElementException } from "./Cause.js" +import type { Channel } from "./Channel.js" +import type { Chunk } from "./Chunk.js" +import type { Effect } from "./Effect.js" +import type { Exit } from "./Exit.js" +import type { Inspectable } from "./Inspectable.js" +import * as internal from "./internal/mailbox.js" +import type { Option } from "./Option.js" +import { hasProperty } from "./Predicate.js" +import type { Scope } from "./Scope.js" +import type { Stream } from "./Stream.js" + +/** + * @since 3.8.0 + * @experimental + * @category type ids + */ +export const TypeId: unique symbol = internal.TypeId + +/** + * @since 3.8.0 + * @experimental + * @category type ids + */ +export type TypeId = typeof TypeId + +/** + * @since 3.8.0 + * @experimental + * @category type ids + */ +export const ReadonlyTypeId: unique symbol = internal.ReadonlyTypeId + +/** + * @since 3.8.0 + * @experimental + * @category type ids + */ +export type ReadonlyTypeId = typeof ReadonlyTypeId + +/** + * @since 3.8.0 + * @experimental + * @category guards + */ +export const isMailbox = (u: unknown): u is Mailbox => hasProperty(u, TypeId) + +/** + * @since 3.8.0 + * @experimental + * @category guards + */ +export const isReadonlyMailbox = (u: unknown): u is ReadonlyMailbox => + hasProperty(u, ReadonlyTypeId) + +/** + * A `Mailbox` is a queue that can be signaled to be done or failed. + * + * @since 3.8.0 + * @experimental + * @category models + */ +export interface Mailbox extends ReadonlyMailbox { + readonly [TypeId]: TypeId + /** + * Add a message to the mailbox. Returns `false` if the mailbox is done. + */ + readonly offer: (message: A) => Effect + /** + * Add a message to the mailbox. Returns `false` if the mailbox is done. + */ + readonly unsafeOffer: (message: A) => boolean + /** + * Add multiple messages to the mailbox. Returns the remaining messages that + * were not added. + */ + readonly offerAll: (messages: Iterable) => Effect> + /** + * Add multiple messages to the mailbox. Returns the remaining messages that + * were not added. + */ + readonly unsafeOfferAll: (messages: Iterable) => Chunk + /** + * Fail the mailbox with an error. If the mailbox is already done, `false` is + * returned. + */ + readonly fail: (error: E) => Effect + /** + * Fail the mailbox with a cause. If the mailbox is already done, `false` is + * returned. + */ + readonly failCause: (cause: Cause) => Effect + /** + * Signal that the mailbox is complete. If the mailbox is already done, `false` is + * returned. + */ + readonly end: Effect + /** + * Signal that the mailbox is done. If the mailbox is already done, `false` is + * returned. + */ + readonly done: (exit: Exit) => Effect + /** + * Signal that the mailbox is done. If the mailbox is already done, `false` is + * returned. + */ + readonly unsafeDone: (exit: Exit) => boolean + /** + * Shutdown the mailbox, canceling any pending operations. + * If the mailbox is already done, `false` is returned. + */ + readonly shutdown: Effect +} + +/** + * A `ReadonlyMailbox` represents a mailbox that can only be read from. + * + * @since 3.8.0 + * @experimental + * @category models + */ +export interface ReadonlyMailbox + extends Effect, done: boolean], E>, Inspectable +{ + readonly [ReadonlyTypeId]: ReadonlyTypeId + /** + * Take all messages from the mailbox, returning an empty Chunk if the mailbox + * is empty or done. + */ + readonly clear: Effect, E> + /** + * Take all messages from the mailbox, or wait for messages to be available. + * + * If the mailbox is done, the `done` flag will be `true`. If the mailbox + * fails, the Effect will fail with the error. + */ + readonly takeAll: Effect, done: boolean], E> + /** + * Take a specified number of messages from the mailbox. It will only take + * up to the capacity of the mailbox. + * + * If the mailbox is done, the `done` flag will be `true`. If the mailbox + * fails, the Effect will fail with the error. + */ + readonly takeN: (n: number) => Effect, done: boolean], E> + /** + * Take a single message from the mailbox, or wait for a message to be + * available. + * + * If the mailbox is done, it will fail with `NoSuchElementException`. If the + * mailbox fails, the Effect will fail with the error. + */ + readonly take: Effect + /** Wait for the mailbox to be done. */ + readonly await: Effect + /** + * Check the size of the mailbox. + * + * If the mailbox is complete, it will return `None`. + */ + readonly size: Effect> + /** + * Check the size of the mailbox. + * + * If the mailbox is complete, it will return `None`. + */ + readonly unsafeSize: () => Option +} + +/** + * A `Mailbox` is a queue that can be signaled to be done or failed. + * + * @since 3.8.0 + * @experimental + * @category constructors + * @example + * ```ts + * import * as assert from "node:assert" + * import { Effect, Mailbox } from "effect" + * + * Effect.gen(function*() { + * const mailbox = yield* Mailbox.make() + * + * // add messages to the mailbox + * yield* mailbox.offer(1) + * yield* mailbox.offer(2) + * yield* mailbox.offerAll([3, 4, 5]) + * + * // take messages from the mailbox + * const [messages, done] = yield* mailbox.takeAll + * assert.deepStrictEqual(messages, [1, 2, 3, 4, 5]) + * assert.strictEqual(done, false) + * + * // signal that the mailbox is done + * yield* mailbox.end + * const [messages2, done2] = yield* mailbox.takeAll + * assert.deepStrictEqual(messages2, []) + * assert.strictEqual(done2, true) + * + * // signal that the mailbox has failed + * yield* mailbox.fail("boom") + * }) + * ``` + */ +export const make: ( + capacity?: number | { + readonly capacity?: number + readonly strategy?: "suspend" | "dropping" | "sliding" + } | undefined +) => Effect> = internal.make + +/** + * Run an `Effect` into a `Mailbox`, where success ends the mailbox and failure + * fails the mailbox. + * + * @since 3.8.0 + * @experimental + * @category combinators + */ +export const into: { + /** + * Run an `Effect` into a `Mailbox`, where success ends the mailbox and failure + * fails the mailbox. + * + * @since 3.8.0 + * @experimental + * @category combinators + */ + (self: Mailbox): (effect: Effect) => Effect + /** + * Run an `Effect` into a `Mailbox`, where success ends the mailbox and failure + * fails the mailbox. + * + * @since 3.8.0 + * @experimental + * @category combinators + */ + (effect: Effect, self: Mailbox): Effect +} = internal.into + +/** + * Create a `Channel` from a `Mailbox`. + * + * @since 3.8.0 + * @experimental + * @category conversions + */ +export const toChannel: (self: ReadonlyMailbox) => Channel, unknown, E> = internal.toChannel + +/** + * Create a `Stream` from a `Mailbox`. + * + * @since 3.8.0 + * @experimental + * @category conversions + */ +export const toStream: (self: ReadonlyMailbox) => Stream = internal.toStream + +/** + * Create a `ReadonlyMailbox` from a `Stream`. + * + * @since 3.11.0 + * @experimental + * @category conversions + */ +export const fromStream: { + /** + * Create a `ReadonlyMailbox` from a `Stream`. + * + * @since 3.11.0 + * @experimental + * @category conversions + */ + ( + options?: { + readonly capacity?: number | undefined + readonly strategy?: "suspend" | "dropping" | "sliding" | undefined + } + ): (self: Stream) => Effect, never, R | Scope> + /** + * Create a `ReadonlyMailbox` from a `Stream`. + * + * @since 3.11.0 + * @experimental + * @category conversions + */ + ( + self: Stream, + options?: { + readonly capacity?: number | undefined + readonly strategy?: "suspend" | "dropping" | "sliding" | undefined + } + ): Effect, never, R | Scope> +} = internal.fromStream diff --git a/backend/node_modules/effect/src/ManagedRuntime.ts b/backend/node_modules/effect/src/ManagedRuntime.ts new file mode 100644 index 0000000000000000000000000000000000000000..97e4a46ae071c2b2ecd522e644436a22b70ebfa6 --- /dev/null +++ b/backend/node_modules/effect/src/ManagedRuntime.ts @@ -0,0 +1,180 @@ +/** + * @since 2.0.0 + */ +import type * as Effect from "./Effect.js" +import type * as Exit from "./Exit.js" +import type * as Fiber from "./Fiber.js" +import * as internal from "./internal/managedRuntime.js" +import * as circular from "./internal/managedRuntime/circular.js" +import type * as Layer from "./Layer.js" +import type * as Runtime from "./Runtime.js" +import type * as Unify from "./Unify.js" + +/** + * @since 3.9.0 + * @category symbol + */ +export const TypeId: unique symbol = circular.TypeId as TypeId + +/** + * @since 3.9.0 + * @category symbol + */ +export type TypeId = typeof TypeId + +/** + * Checks if the provided argument is a `ManagedRuntime`. + * + * @since 3.9.0 + * @category guards + */ +export const isManagedRuntime: (input: unknown) => input is ManagedRuntime = internal.isManagedRuntime + +/** + * @since 3.4.0 + */ +export declare namespace ManagedRuntime { + /** + * @category type-level + * @since 3.4.0 + */ + export type Context> = [T] extends [ManagedRuntime] ? R + : never + /** + * @category type-level + * @since 3.4.0 + */ + export type Error> = [T] extends [ManagedRuntime] ? E : never +} + +/** + * @since 2.0.0 + * @category models + */ +export interface ManagedRuntime extends Effect.Effect, ER> { + readonly [TypeId]: TypeId + readonly memoMap: Layer.MemoMap + readonly runtimeEffect: Effect.Effect, ER> + readonly runtime: () => Promise> + + /** + * Executes the effect using the provided Scheduler or using the global + * Scheduler if not provided + */ + readonly runFork: ( + self: Effect.Effect, + options?: Runtime.RunForkOptions + ) => Fiber.RuntimeFiber + + /** + * Executes the effect synchronously returning the exit. + * + * This method is effectful and should only be invoked at the edges of your + * program. + */ + readonly runSyncExit: (effect: Effect.Effect) => Exit.Exit + + /** + * Executes the effect synchronously throwing in case of errors or async boundaries. + * + * This method is effectful and should only be invoked at the edges of your + * program. + */ + readonly runSync: (effect: Effect.Effect) => A + + /** + * Executes the effect asynchronously, eventually passing the exit value to + * the specified callback. + * + * This method is effectful and should only be invoked at the edges of your + * program. + */ + readonly runCallback: ( + effect: Effect.Effect, + options?: Runtime.RunCallbackOptions | undefined + ) => Runtime.Cancel + + /** + * Runs the `Effect`, returning a JavaScript `Promise` that will be resolved + * with the value of the effect once the effect has been executed, or will be + * rejected with the first error or exception throw by the effect. + * + * This method is effectful and should only be used at the edges of your + * program. + */ + readonly runPromise: (effect: Effect.Effect, options?: { + readonly signal?: AbortSignal | undefined + }) => Promise + + /** + * Runs the `Effect`, returning a JavaScript `Promise` that will be resolved + * with the `Exit` state of the effect once the effect has been executed. + * + * This method is effectful and should only be used at the edges of your + * program. + */ + readonly runPromiseExit: (effect: Effect.Effect, options?: { + readonly signal?: AbortSignal | undefined + }) => Promise> + + /** + * Dispose of the resources associated with the runtime. + */ + readonly dispose: () => Promise + + /** + * Dispose of the resources associated with the runtime. + */ + readonly disposeEffect: Effect.Effect + + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: ManagedRuntimeUnify + readonly [Unify.ignoreSymbol]?: ManagedRuntimeUnifyIgnore +} + +/** + * @category models + * @since 3.9.0 + */ +export interface ManagedRuntimeUnify extends Effect.EffectUnify { + ManagedRuntime?: () => Extract> +} + +/** + * @category models + * @since 3.9.0 + */ +export interface ManagedRuntimeUnifyIgnore extends Effect.EffectUnifyIgnore { + Effect?: true +} + +/** + * Convert a Layer into an ManagedRuntime, that can be used to run Effect's using + * your services. + * + * @since 2.0.0 + * @category runtime class + * @example + * ```ts + * import { Console, Effect, Layer, ManagedRuntime } from "effect" + * + * class Notifications extends Effect.Tag("Notifications")< + * Notifications, + * { readonly notify: (message: string) => Effect.Effect } + * >() { + * static Live = Layer.succeed(this, { notify: (message) => Console.log(message) }) + * } + * + * async function main() { + * const runtime = ManagedRuntime.make(Notifications.Live) + * await runtime.runPromise(Notifications.notify("Hello, world!")) + * await runtime.dispose() + * } + * + * main() + * ``` + */ +export const make: ( + layer: Layer.Layer, + memoMap?: Layer.MemoMap | undefined +) => ManagedRuntime = internal.make diff --git a/backend/node_modules/effect/src/MergeDecision.ts b/backend/node_modules/effect/src/MergeDecision.ts new file mode 100644 index 0000000000000000000000000000000000000000..a4d2740d216ff53a3056f97406b9710f256dab58 --- /dev/null +++ b/backend/node_modules/effect/src/MergeDecision.ts @@ -0,0 +1,103 @@ +/** + * @since 2.0.0 + */ +import type * as Effect from "./Effect.js" +import type * as Exit from "./Exit.js" +import * as internal from "./internal/channel/mergeDecision.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const MergeDecisionTypeId: unique symbol = internal.MergeDecisionTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type MergeDecisionTypeId = typeof MergeDecisionTypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface MergeDecision extends MergeDecision.Variance {} + +/** + * @since 2.0.0 + */ +export declare namespace MergeDecision { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [MergeDecisionTypeId]: { + _R: Types.Covariant + _E0: Types.Contravariant + _Z0: Types.Contravariant + _E: Types.Covariant + _Z: Types.Covariant + } + } +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const Done: (effect: Effect.Effect) => MergeDecision = internal.Done + +/** + * @since 2.0.0 + * @category constructors + */ +export const Await: ( + f: (exit: Exit.Exit) => Effect.Effect +) => MergeDecision = internal.Await + +/** + * @since 2.0.0 + * @category constructors + */ +export const AwaitConst: (effect: Effect.Effect) => MergeDecision = + internal.AwaitConst + +/** + * Returns `true` if the specified value is a `MergeDecision`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isMergeDecision: (u: unknown) => u is MergeDecision = + internal.isMergeDecision + +/** + * @since 2.0.0 + * @category folding + */ +export const match: { + /** + * @since 2.0.0 + * @category folding + */ + ( + options: { + readonly onDone: (effect: Effect.Effect) => Z2 + readonly onAwait: (f: (exit: Exit.Exit) => Effect.Effect) => Z2 + } + ): (self: MergeDecision) => Z2 + /** + * @since 2.0.0 + * @category folding + */ + ( + self: MergeDecision, + options: { + readonly onDone: (effect: Effect.Effect) => Z2 + readonly onAwait: (f: (exit: Exit.Exit) => Effect.Effect) => Z2 + } + ): Z2 +} = internal.match diff --git a/backend/node_modules/effect/src/MergeState.ts b/backend/node_modules/effect/src/MergeState.ts new file mode 100644 index 0000000000000000000000000000000000000000..e694860ca6d58e942b451009713da211682a3941 --- /dev/null +++ b/backend/node_modules/effect/src/MergeState.ts @@ -0,0 +1,180 @@ +/** + * @since 2.0.0 + */ +import type * as Effect from "./Effect.js" +import type * as Either from "./Either.js" +import type * as Exit from "./Exit.js" +import type * as Fiber from "./Fiber.js" +import * as internal from "./internal/channel/mergeState.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const MergeStateTypeId: unique symbol = internal.MergeStateTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type MergeStateTypeId = typeof MergeStateTypeId + +/** + * @since 2.0.0 + * @category models + */ +export type MergeState = + | BothRunning + | LeftDone + | RightDone + +/** + * @since 2.0.0 + */ +export declare namespace MergeState { + /** + * @since 2.0.0 + * @category models + */ + export interface Proto { + readonly [MergeStateTypeId]: MergeStateTypeId + } +} + +/** + * @since 2.0.0 + * @category models + */ +export interface BothRunning<_Env, out Err, out Err1, _Err2, out Elem, out Done, out Done1, _Done2> + extends MergeState.Proto +{ + readonly _tag: "BothRunning" + readonly left: Fiber.Fiber, Err> + readonly right: Fiber.Fiber, Err1> +} + +/** + * @since 2.0.0 + * @category models + */ +export interface LeftDone + extends MergeState.Proto +{ + readonly _tag: "LeftDone" + f(exit: Exit.Exit): Effect.Effect +} + +/** + * @since 2.0.0 + * @category models + */ +export interface RightDone + extends MergeState.Proto +{ + readonly _tag: "RightDone" + f(exit: Exit.Exit): Effect.Effect +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const BothRunning: ( + left: Fiber.Fiber, Err>, + right: Fiber.Fiber, Err1> +) => MergeState = internal.BothRunning + +/** + * @since 2.0.0 + * @category constructors + */ +export const LeftDone: ( + f: (exit: Exit.Exit) => Effect.Effect +) => MergeState = internal.LeftDone + +/** + * @since 2.0.0 + * @category constructors + */ +export const RightDone: ( + f: (exit: Exit.Exit) => Effect.Effect +) => MergeState = internal.RightDone + +/** + * Returns `true` if the specified value is a `MergeState`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isMergeState: ( + u: unknown +) => u is MergeState = internal.isMergeState + +/** + * Returns `true` if the specified `MergeState` is a `BothRunning`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isBothRunning: ( + self: MergeState +) => self is BothRunning = internal.isBothRunning + +/** + * Returns `true` if the specified `MergeState` is a `LeftDone`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isLeftDone: ( + self: MergeState +) => self is LeftDone = internal.isLeftDone + +/** + * Returns `true` if the specified `MergeState` is a `RightDone`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isRightDone: ( + self: MergeState +) => self is RightDone = internal.isRightDone + +/** + * @since 2.0.0 + * @category folding + */ +export const match: { + /** + * @since 2.0.0 + * @category folding + */ + ( + options: { + readonly onBothRunning: ( + left: Fiber.Fiber, Err>, + right: Fiber.Fiber, Err1> + ) => Z + readonly onLeftDone: (f: (exit: Exit.Exit) => Effect.Effect) => Z + readonly onRightDone: (f: (exit: Exit.Exit) => Effect.Effect) => Z + } + ): (self: MergeState) => Z + /** + * @since 2.0.0 + * @category folding + */ + ( + self: MergeState, + options: { + readonly onBothRunning: ( + left: Fiber.Fiber, Err>, + right: Fiber.Fiber, Err1> + ) => Z + readonly onLeftDone: (f: (exit: Exit.Exit) => Effect.Effect) => Z + readonly onRightDone: (f: (exit: Exit.Exit) => Effect.Effect) => Z + } + ): Z +} = internal.match diff --git a/backend/node_modules/effect/src/MergeStrategy.ts b/backend/node_modules/effect/src/MergeStrategy.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f57629333f241f0702e7601ed601f059dc3a764 --- /dev/null +++ b/backend/node_modules/effect/src/MergeStrategy.ts @@ -0,0 +1,124 @@ +/** + * @since 2.0.0 + */ +import * as internal from "./internal/channel/mergeStrategy.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const MergeStrategyTypeId: unique symbol = internal.MergeStrategyTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type MergeStrategyTypeId = typeof MergeStrategyTypeId + +/** + * @since 2.0.0 + * @category models + */ +export type MergeStrategy = BackPressure | BufferSliding + +/** + * @since 2.0.0 + */ +export declare namespace MergeStrategy { + /** + * @since 2.0.0 + * @category models + */ + export interface Proto { + readonly [MergeStrategyTypeId]: MergeStrategyTypeId + } +} + +/** + * @since 2.0.0 + * @category models + */ +export interface BackPressure extends MergeStrategy.Proto { + readonly _tag: "BackPressure" +} + +/** + * @since 2.0.0 + * @category models + */ +export interface BufferSliding extends MergeStrategy.Proto { + readonly _tag: "BufferSliding" +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const BackPressure: (_: void) => MergeStrategy = internal.BackPressure + +/** + * @since 2.0.0 + * @category constructors + */ +export const BufferSliding: (_: void) => MergeStrategy = internal.BufferSliding + +/** + * Returns `true` if the specified value is a `MergeStrategy`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isMergeStrategy: (u: unknown) => u is MergeStrategy = internal.isMergeStrategy + +/** + * Returns `true` if the specified `MergeStrategy` is a `BackPressure`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isBackPressure: (self: MergeStrategy) => self is BackPressure = internal.isBackPressure + +/** + * Returns `true` if the specified `MergeStrategy` is a `BufferSliding`, `false` + * otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isBufferSliding: (self: MergeStrategy) => self is BufferSliding = internal.isBufferSliding + +/** + * Folds an `MergeStrategy` into a value of type `A`. + * + * @since 2.0.0 + * @category folding + */ +export const match: { + /** + * Folds an `MergeStrategy` into a value of type `A`. + * + * @since 2.0.0 + * @category folding + */ + ( + options: { + readonly onBackPressure: () => A + readonly onBufferSliding: () => A + } + ): (self: MergeStrategy) => A + /** + * Folds an `MergeStrategy` into a value of type `A`. + * + * @since 2.0.0 + * @category folding + */ + ( + self: MergeStrategy, + options: { + readonly onBackPressure: () => A + readonly onBufferSliding: () => A + } + ): A +} = internal.match diff --git a/backend/node_modules/effect/src/Metric.ts b/backend/node_modules/effect/src/Metric.ts new file mode 100644 index 0000000000000000000000000000000000000000..e25397f3a14f03a04a6fe852c08ae2725d8b9e13 --- /dev/null +++ b/backend/node_modules/effect/src/Metric.ts @@ -0,0 +1,1165 @@ +/** + * @since 2.0.0 + */ +import type * as Duration from "./Duration.js" +import type * as Effect from "./Effect.js" +import type { LazyArg } from "./Function.js" +import * as fiberRuntime from "./internal/fiberRuntime.js" +import * as internal from "./internal/metric.js" +import type * as MetricBoundaries from "./MetricBoundaries.js" +import type * as MetricKey from "./MetricKey.js" +import type * as MetricKeyType from "./MetricKeyType.js" +import type * as MetricLabel from "./MetricLabel.js" +import type * as MetricPair from "./MetricPair.js" +import type * as MetricRegistry from "./MetricRegistry.js" +import type * as MetricState from "./MetricState.js" +import type { Pipeable } from "./Pipeable.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const MetricTypeId: unique symbol = internal.MetricTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type MetricTypeId = typeof MetricTypeId + +/** + * A `Metric` represents a concurrent metric which accepts + * updates of type `In` and are aggregated to a stateful value of type `Out`. + * + * For example, a counter metric would have type `Metric`, + * representing the fact that the metric can be updated with numbers (the amount + * to increment or decrement the counter by), and the state of the counter is a + * number. + * + * There are five primitive metric types supported by Effect: + * + * - Counters + * - Frequencies + * - Gauges + * - Histograms + * - Summaries + * + * @since 2.0.0 + * @category models + */ +export interface Metric extends Metric.Variance, Pipeable { + /** + * The type of the underlying primitive metric. For example, this could be + * `MetricKeyType.Counter` or `MetricKeyType.Gauge`. + */ + readonly keyType: Type + unsafeUpdate(input: In, extraTags: ReadonlyArray): void + unsafeValue(extraTags: ReadonlyArray): Out + unsafeModify(input: In, extraTags: ReadonlyArray): void + register(): this + (effect: Effect.Effect): Effect.Effect +} + +/** + * @since 2.0.0 + * @category models + */ +export interface MetricApply { + ( + keyType: Type, + unsafeUpdate: (input: In, extraTags: ReadonlyArray) => void, + unsafeValue: (extraTags: ReadonlyArray) => Out, + unsafeModify: (input: In, extraTags: ReadonlyArray) => void + ): Metric +} + +/** + * @since 2.0.0 + */ +export declare namespace Metric { + /** + * @since 2.0.0 + * @category models + */ + export interface Counter + extends Metric, In, MetricState.MetricState.Counter> + {} + + /** + * @since 2.0.0 + * @category models + */ + export interface Gauge + extends Metric, In, MetricState.MetricState.Gauge> + {} + + /** + * @since 2.0.0 + * @category models + */ + export interface Frequency + extends Metric + {} + + /** + * @since 2.0.0 + * @category models + */ + export interface Histogram + extends Metric + {} + + /** + * @since 2.0.0 + * @category models + */ + export interface Summary + extends Metric + {} + + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [MetricTypeId]: { + readonly _Type: Types.Invariant + readonly _In: Types.Contravariant + readonly _Out: Types.Covariant + } + } +} + +/** + * @since 2.0.0 + * @category globals + */ +export const globalMetricRegistry: MetricRegistry.MetricRegistry = internal.globalMetricRegistry + +/** + * @since 2.0.0 + * @category constructors + */ +export const make: MetricApply = internal.make + +/** + * Returns a new metric that is powered by this one, but which accepts updates + * of the specified new type, which must be transformable to the input type of + * this metric. + * + * @since 2.0.0 + * @category mapping + */ +export const mapInput: { + /** + * Returns a new metric that is powered by this one, but which accepts updates + * of the specified new type, which must be transformable to the input type of + * this metric. + * + * @since 2.0.0 + * @category mapping + */ + (f: (input: In2) => In): (self: Metric) => Metric + /** + * Returns a new metric that is powered by this one, but which accepts updates + * of the specified new type, which must be transformable to the input type of + * this metric. + * + * @since 2.0.0 + * @category mapping + */ + (self: Metric, f: (input: In2) => In): Metric +} = internal.mapInput + +/** + * Represents a Counter metric that tracks cumulative numerical values over time. + * Counters can be incremented and decremented and provide a running total of changes. + * + * **Options** + * + * - description - A description of the counter. + * - bigint - Indicates if the counter uses 'bigint' data type. + * - incremental - Set to 'true' for a counter that only increases. With this configuration, Effect ensures that non-incremental updates have no impact on the counter, making it exclusively suitable for counting upwards. + * + * @example + * ```ts + * import { Metric } from "effect" + * + * const numberCounter = Metric.counter("count", { + * description: "A number counter" + * }); + * + * const bigintCounter = Metric.counter("count", { + * description: "A bigint counter", + * bigint: true + * }); + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const counter: { + /** + * Represents a Counter metric that tracks cumulative numerical values over time. + * Counters can be incremented and decremented and provide a running total of changes. + * + * **Options** + * + * - description - A description of the counter. + * - bigint - Indicates if the counter uses 'bigint' data type. + * - incremental - Set to 'true' for a counter that only increases. With this configuration, Effect ensures that non-incremental updates have no impact on the counter, making it exclusively suitable for counting upwards. + * + * @example + * ```ts + * import { Metric } from "effect" + * + * const numberCounter = Metric.counter("count", { + * description: "A number counter" + * }); + * + * const bigintCounter = Metric.counter("count", { + * description: "A bigint counter", + * bigint: true + * }); + * ``` + * + * @since 2.0.0 + * @category constructors + */ + ( + name: string, + options?: { + readonly description?: string | undefined + readonly bigint?: false | undefined + readonly incremental?: boolean | undefined + } + ): Metric.Counter + /** + * Represents a Counter metric that tracks cumulative numerical values over time. + * Counters can be incremented and decremented and provide a running total of changes. + * + * **Options** + * + * - description - A description of the counter. + * - bigint - Indicates if the counter uses 'bigint' data type. + * - incremental - Set to 'true' for a counter that only increases. With this configuration, Effect ensures that non-incremental updates have no impact on the counter, making it exclusively suitable for counting upwards. + * + * @example + * ```ts + * import { Metric } from "effect" + * + * const numberCounter = Metric.counter("count", { + * description: "A number counter" + * }); + * + * const bigintCounter = Metric.counter("count", { + * description: "A bigint counter", + * bigint: true + * }); + * ``` + * + * @since 2.0.0 + * @category constructors + */ + ( + name: string, + options: { + readonly description?: string | undefined + readonly bigint: true + readonly incremental?: boolean | undefined + } + ): Metric.Counter +} = internal.counter + +/** + * Creates a Frequency metric to count occurrences of events. + * Frequency metrics are used to count the number of times specific events or incidents occur. + * + * @example + * ```ts + * import { Metric } from "effect" + * + * const errorFrequency = Metric.frequency("error_frequency", { + * description: "Counts the occurrences of errors." + * }); + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const frequency: ( + name: string, + options?: + | { readonly description?: string | undefined; readonly preregisteredWords?: ReadonlyArray | undefined } + | undefined +) => Metric.Frequency = internal.frequency + +/** + * Returns a new metric that is powered by this one, but which accepts updates + * of any type, and translates them to updates with the specified constant + * update value. + * + * @since 2.0.0 + * @category constructors + */ +export const withConstantInput: { + /** + * Returns a new metric that is powered by this one, but which accepts updates + * of any type, and translates them to updates with the specified constant + * update value. + * + * @since 2.0.0 + * @category constructors + */ + (input: In): (self: Metric) => Metric + /** + * Returns a new metric that is powered by this one, but which accepts updates + * of any type, and translates them to updates with the specified constant + * update value. + * + * @since 2.0.0 + * @category constructors + */ + (self: Metric, input: In): Metric +} = internal.withConstantInput + +/** + * @since 2.0.0 + * @category constructors + */ +export const fromMetricKey: >( + key: MetricKey.MetricKey +) => Metric, MetricKeyType.MetricKeyType.OutType> = + internal.fromMetricKey + +/** + * Represents a Gauge metric that tracks and reports a single numerical value at a specific moment. + * Gauges are suitable for metrics that represent instantaneous values, such as memory usage or CPU load. + * + * **Options** + * + * - description - A description of the gauge metric. + * - bigint - Indicates if the counter uses 'bigint' data type. + * + * @example + * ```ts + * import { Metric } from "effect" + * + * const numberGauge = Metric.gauge("memory_usage", { + * description: "A gauge for memory usage" + * }); + * + * const bigintGauge = Metric.gauge("cpu_load", { + * description: "A gauge for CPU load", + * bigint: true + * }); + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const gauge: { + /** + * Represents a Gauge metric that tracks and reports a single numerical value at a specific moment. + * Gauges are suitable for metrics that represent instantaneous values, such as memory usage or CPU load. + * + * **Options** + * + * - description - A description of the gauge metric. + * - bigint - Indicates if the counter uses 'bigint' data type. + * + * @example + * ```ts + * import { Metric } from "effect" + * + * const numberGauge = Metric.gauge("memory_usage", { + * description: "A gauge for memory usage" + * }); + * + * const bigintGauge = Metric.gauge("cpu_load", { + * description: "A gauge for CPU load", + * bigint: true + * }); + * ``` + * + * @since 2.0.0 + * @category constructors + */ + ( + name: string, + options?: { + readonly description?: string | undefined + readonly bigint?: false | undefined + } + ): Metric.Gauge + /** + * Represents a Gauge metric that tracks and reports a single numerical value at a specific moment. + * Gauges are suitable for metrics that represent instantaneous values, such as memory usage or CPU load. + * + * **Options** + * + * - description - A description of the gauge metric. + * - bigint - Indicates if the counter uses 'bigint' data type. + * + * @example + * ```ts + * import { Metric } from "effect" + * + * const numberGauge = Metric.gauge("memory_usage", { + * description: "A gauge for memory usage" + * }); + * + * const bigintGauge = Metric.gauge("cpu_load", { + * description: "A gauge for CPU load", + * bigint: true + * }); + * ``` + * + * @since 2.0.0 + * @category constructors + */ + ( + name: string, + options: { + readonly description?: string | undefined + readonly bigint: true + } + ): Metric.Gauge +} = internal.gauge + +/** + * Represents a Histogram metric that records observations in specified value boundaries. + * Histogram metrics are useful for measuring the distribution of values within a range. + * + * @example + * ```ts + * import { Metric, MetricBoundaries } from "effect" + * + * const latencyHistogram = Metric.histogram("latency_histogram", + * MetricBoundaries.linear({ start: 0, width: 10, count: 11 }), + * "Measures the distribution of request latency." + * ); + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const histogram: ( + name: string, + boundaries: MetricBoundaries.MetricBoundaries, + description?: string +) => Metric = internal.histogram + +/** + * @since 2.0.0 + * @category combinators + */ +export const increment: ( + self: Metric.Counter | Metric.Counter | Metric.Gauge | Metric.Gauge +) => Effect.Effect = internal.increment + +/** + * @since 2.0.0 + * @category combinators + */ +export const incrementBy: { + /** + * @since 2.0.0 + * @category combinators + */ + (amount: number): (self: Metric.Counter | Metric.Counter) => Effect.Effect + /** + * @since 2.0.0 + * @category combinators + */ + (amount: bigint): (self: Metric.Counter | Metric.Gauge) => Effect.Effect + /** + * @since 2.0.0 + * @category combinators + */ + (self: Metric.Counter | Metric.Gauge, amount: number): Effect.Effect + /** + * @since 2.0.0 + * @category combinators + */ + (self: Metric.Counter | Metric.Gauge, amount: bigint): Effect.Effect +} = internal.incrementBy + +/** + * Returns a new metric that is powered by this one, but which outputs a new + * state type, determined by transforming the state type of this metric by the + * specified function. + * + * @since 2.0.0 + * @category mapping + */ +export const map: { + /** + * Returns a new metric that is powered by this one, but which outputs a new + * state type, determined by transforming the state type of this metric by the + * specified function. + * + * @since 2.0.0 + * @category mapping + */ + (f: (out: Out) => Out2): (self: Metric) => Metric + /** + * Returns a new metric that is powered by this one, but which outputs a new + * state type, determined by transforming the state type of this metric by the + * specified function. + * + * @since 2.0.0 + * @category mapping + */ + (self: Metric, f: (out: Out) => Out2): Metric +} = internal.map + +/** + * @since 2.0.0 + * @category mapping + */ +export const mapType: { + /** + * @since 2.0.0 + * @category mapping + */ + (f: (type: Type) => Type2): (self: Metric) => Metric + /** + * @since 2.0.0 + * @category mapping + */ + (self: Metric, f: (type: Type) => Type2): Metric +} = internal.mapType + +/** + * Modifies the metric with the specified update message. For example, if the + * metric were a gauge, the update would increment the method by the provided + * amount. + * + * @since 3.6.5 + * @category utils + */ +export const modify: { + /** + * Modifies the metric with the specified update message. For example, if the + * metric were a gauge, the update would increment the method by the provided + * amount. + * + * @since 3.6.5 + * @category utils + */ + (input: In): (self: Metric) => Effect.Effect + /** + * Modifies the metric with the specified update message. For example, if the + * metric were a gauge, the update would increment the method by the provided + * amount. + * + * @since 3.6.5 + * @category utils + */ + (self: Metric, input: In): Effect.Effect +} = internal.modify + +/** + * @since 2.0.0 + * @category aspects + */ +export const set: { + /** + * @since 2.0.0 + * @category aspects + */ + (value: number): (self: Metric.Gauge) => Effect.Effect + /** + * @since 2.0.0 + * @category aspects + */ + (value: bigint): (self: Metric.Gauge) => Effect.Effect + /** + * @since 2.0.0 + * @category aspects + */ + (self: Metric.Gauge, value: number): Effect.Effect + /** + * @since 2.0.0 + * @category aspects + */ + (self: Metric.Gauge, value: bigint): Effect.Effect +} = internal.set + +/** + * Captures a snapshot of all metrics recorded by the application. + * + * @since 2.0.0 + * @category getters + */ +export const snapshot: Effect.Effect> = internal.snapshot + +/** + * Creates a metric that ignores input and produces constant output. + * + * @since 2.0.0 + * @category constructors + */ +export const succeed: (out: Out) => Metric = internal.succeed + +/** + * Creates a metric that ignores input and produces constant output. + * + * @since 2.0.0 + * @category constructors + */ +export const sync: (evaluate: LazyArg) => Metric = internal.sync + +/** + * Creates a Summary metric that records observations and calculates quantiles. + * Summary metrics provide statistical information about a set of values, including quantiles. + * + * **Options** + * + * - name - The name of the Summary metric. + * - maxAge - The maximum age of observations to retain. + * - maxSize - The maximum number of observations to keep. + * - error - The error percentage when calculating quantiles. + * - quantiles - An `Chunk` of quantiles to calculate (e.g., [0.5, 0.9]). + * - description - An optional description of the Summary metric. + * + * @example + * ```ts + * import { Metric, Chunk } from "effect" + * + * const responseTimesSummary = Metric.summary({ + * name: "response_times_summary", + * maxAge: "60 seconds", // Retain observations for 60 seconds. + * maxSize: 1000, // Keep a maximum of 1000 observations. + * error: 0.01, // Allow a 1% error when calculating quantiles. + * quantiles: [0.5, 0.9, 0.99], // Calculate 50th, 90th, and 99th percentiles. + * description: "Measures the distribution of response times." + * }); + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const summary: ( + options: { + readonly name: string + readonly maxAge: Duration.DurationInput + readonly maxSize: number + readonly error: number + readonly quantiles: ReadonlyArray + readonly description?: string | undefined + } +) => Metric.Summary = internal.summary + +/** + * @since 2.0.0 + * @category constructors + */ +export const summaryTimestamp: ( + options: { + readonly name: string + readonly maxAge: Duration.DurationInput + readonly maxSize: number + readonly error: number + readonly quantiles: ReadonlyArray + readonly description?: string | undefined + } +) => Metric.Summary // readonly because contravariant + = internal.summaryTimestamp + +/** + * Returns a new metric, which is identical in every way to this one, except + * the specified tags have been added to the tags of this metric. + * + * @since 2.0.0 + * @category utils + */ +export const tagged: { + /** + * Returns a new metric, which is identical in every way to this one, except + * the specified tags have been added to the tags of this metric. + * + * @since 2.0.0 + * @category utils + */ + (key: string, value: string): (self: Metric) => Metric + /** + * Returns a new metric, which is identical in every way to this one, except + * the specified tags have been added to the tags of this metric. + * + * @since 2.0.0 + * @category utils + */ + (self: Metric, key: string, value: string): Metric +} = internal.tagged + +/** + * Returns a new metric, which is identical in every way to this one, except + * dynamic tags are added based on the update values. Note that the metric + * returned by this method does not return any useful information, due to the + * dynamic nature of the added tags. + * + * @since 2.0.0 + * @category utils + */ +export const taggedWithLabelsInput: { + /** + * Returns a new metric, which is identical in every way to this one, except + * dynamic tags are added based on the update values. Note that the metric + * returned by this method does not return any useful information, due to the + * dynamic nature of the added tags. + * + * @since 2.0.0 + * @category utils + */ + (f: (input: In) => Iterable): (self: Metric) => Metric + /** + * Returns a new metric, which is identical in every way to this one, except + * dynamic tags are added based on the update values. Note that the metric + * returned by this method does not return any useful information, due to the + * dynamic nature of the added tags. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Metric, + f: (input: In) => Iterable + ): Metric +} = internal.taggedWithLabelsInput + +/** + * Returns a new metric, which is identical in every way to this one, except + * the specified tags have been added to the tags of this metric. + * + * @since 2.0.0 + * @category utils + */ +export const taggedWithLabels: { + /** + * Returns a new metric, which is identical in every way to this one, except + * the specified tags have been added to the tags of this metric. + * + * @since 2.0.0 + * @category utils + */ + (extraTags: Iterable): (self: Metric) => Metric + /** + * Returns a new metric, which is identical in every way to this one, except + * the specified tags have been added to the tags of this metric. + * + * @since 2.0.0 + * @category utils + */ + (self: Metric, extraTags: Iterable): Metric +} = internal.taggedWithLabels + +/** + * Creates a timer metric, based on a histogram, which keeps track of + * durations in milliseconds. The unit of time will automatically be added to + * the metric as a tag (i.e. `"time_unit: milliseconds"`). + * + * @since 2.0.0 + * @category constructors + */ +export const timer: ( + name: string, + description?: string +) => Metric = + internal.timer + +/** + * Creates a timer metric, based on a histogram created from the provided + * boundaries, which keeps track of durations in milliseconds. The unit of time + * will automatically be added to the metric as a tag (i.e. + * `"time_unit: milliseconds"`). + * + * @since 2.0.0 + * @category constructors + */ +export const timerWithBoundaries: ( + name: string, + boundaries: ReadonlyArray, + description?: string +) => Metric = + internal.timerWithBoundaries + +/** + * Returns an aspect that will update this metric with the specified constant + * value every time the aspect is applied to an effect, regardless of whether + * that effect fails or succeeds. + * + * @since 2.0.0 + * @category aspects + */ +export const trackAll: { + /** + * Returns an aspect that will update this metric with the specified constant + * value every time the aspect is applied to an effect, regardless of whether + * that effect fails or succeeds. + * + * @since 2.0.0 + * @category aspects + */ + (input: In): (self: Metric) => (effect: Effect.Effect) => Effect.Effect + /** + * Returns an aspect that will update this metric with the specified constant + * value every time the aspect is applied to an effect, regardless of whether + * that effect fails or succeeds. + * + * @since 2.0.0 + * @category aspects + */ + (self: Metric, input: In): (effect: Effect.Effect) => Effect.Effect +} = internal.trackAll + +/** + * Returns an aspect that will update this metric with the defects of the + * effects that it is applied to. + * + * @since 2.0.0 + * @category aspects + */ +export const trackDefect: { + /** + * Returns an aspect that will update this metric with the defects of the + * effects that it is applied to. + * + * @since 2.0.0 + * @category aspects + */ + (metric: Metric): (self: Effect.Effect) => Effect.Effect + /** + * Returns an aspect that will update this metric with the defects of the + * effects that it is applied to. + * + * @since 2.0.0 + * @category aspects + */ + (self: Effect.Effect, metric: Metric): Effect.Effect +} = internal.trackDefect + +/** + * Returns an aspect that will update this metric with the result of applying + * the specified function to the defect throwables of the effects that the + * aspect is applied to. + * + * @since 2.0.0 + * @category aspects + */ +export const trackDefectWith: { + /** + * Returns an aspect that will update this metric with the result of applying + * the specified function to the defect throwables of the effects that the + * aspect is applied to. + * + * @since 2.0.0 + * @category aspects + */ + (metric: Metric, f: (defect: unknown) => In): (self: Effect.Effect) => Effect.Effect + /** + * Returns an aspect that will update this metric with the result of applying + * the specified function to the defect throwables of the effects that the + * aspect is applied to. + * + * @since 2.0.0 + * @category aspects + */ + ( + self: Effect.Effect, + metric: Metric, + f: (defect: unknown) => In + ): Effect.Effect +} = internal.trackDefectWith + +/** + * Returns an aspect that will update this metric with the duration that the + * effect takes to execute. To call this method, the input type of the metric + * must be `Duration`. + * + * @since 2.0.0 + * @category aspects + */ +export const trackDuration: { + /** + * Returns an aspect that will update this metric with the duration that the + * effect takes to execute. To call this method, the input type of the metric + * must be `Duration`. + * + * @since 2.0.0 + * @category aspects + */ + (metric: Metric): (self: Effect.Effect) => Effect.Effect + /** + * Returns an aspect that will update this metric with the duration that the + * effect takes to execute. To call this method, the input type of the metric + * must be `Duration`. + * + * @since 2.0.0 + * @category aspects + */ + (self: Effect.Effect, metric: Metric): Effect.Effect +} = internal.trackDuration + +/** + * Returns an aspect that will update this metric with the duration that the + * effect takes to execute. To call this method, you must supply a function + * that can convert the `Duration` to the input type of this metric. + * + * @since 2.0.0 + * @category aspects + */ +export const trackDurationWith: { + /** + * Returns an aspect that will update this metric with the duration that the + * effect takes to execute. To call this method, you must supply a function + * that can convert the `Duration` to the input type of this metric. + * + * @since 2.0.0 + * @category aspects + */ + (metric: Metric, f: (duration: Duration.Duration) => In): (effect: Effect.Effect) => Effect.Effect + /** + * Returns an aspect that will update this metric with the duration that the + * effect takes to execute. To call this method, you must supply a function + * that can convert the `Duration` to the input type of this metric. + * + * @since 2.0.0 + * @category aspects + */ + ( + self: Effect.Effect, + metric: Metric, + f: (duration: Duration.Duration) => In + ): Effect.Effect +} = internal.trackDurationWith + +/** + * Returns an aspect that will update this metric with the failure value of + * the effects that it is applied to. + * + * @since 2.0.0 + * @category aspects + */ +export const trackError: { + /** + * Returns an aspect that will update this metric with the failure value of + * the effects that it is applied to. + * + * @since 2.0.0 + * @category aspects + */ + (metric: Metric): (self: Effect.Effect) => Effect.Effect + /** + * Returns an aspect that will update this metric with the failure value of + * the effects that it is applied to. + * + * @since 2.0.0 + * @category aspects + */ + (self: Effect.Effect, metric: Metric): Effect.Effect +} = internal.trackError + +/** + * Returns an aspect that will update this metric with the result of applying + * the specified function to the error value of the effects that the aspect is + * applied to. + * + * @since 2.0.0 + * @category aspects + */ +export const trackErrorWith: { + /** + * Returns an aspect that will update this metric with the result of applying + * the specified function to the error value of the effects that the aspect is + * applied to. + * + * @since 2.0.0 + * @category aspects + */ + (metric: Metric, f: (error: In2) => In): (effect: Effect.Effect) => Effect.Effect + /** + * Returns an aspect that will update this metric with the result of applying + * the specified function to the error value of the effects that the aspect is + * applied to. + * + * @since 2.0.0 + * @category aspects + */ + ( + self: Effect.Effect, + metric: Metric, + f: (error: In2) => In + ): Effect.Effect +} = internal.trackErrorWith + +/** + * Returns an aspect that will update this metric with the success value of + * the effects that it is applied to. + * + * @since 2.0.0 + * @category aspects + */ +export const trackSuccess: { + /** + * Returns an aspect that will update this metric with the success value of + * the effects that it is applied to. + * + * @since 2.0.0 + * @category aspects + */ + (metric: Metric): (self: Effect.Effect) => Effect.Effect + /** + * Returns an aspect that will update this metric with the success value of + * the effects that it is applied to. + * + * @since 2.0.0 + * @category aspects + */ + (self: Effect.Effect, metric: Metric): Effect.Effect +} = internal.trackSuccess + +/** + * Returns an aspect that will update this metric with the result of applying + * the specified function to the success value of the effects that the aspect is + * applied to. + * + * @since 2.0.0 + * @category aspects + */ +export const trackSuccessWith: { + /** + * Returns an aspect that will update this metric with the result of applying + * the specified function to the success value of the effects that the aspect is + * applied to. + * + * @since 2.0.0 + * @category aspects + */ + (metric: Metric, f: (value: Types.NoInfer) => In): (self: Effect.Effect) => Effect.Effect + /** + * Returns an aspect that will update this metric with the result of applying + * the specified function to the success value of the effects that the aspect is + * applied to. + * + * @since 2.0.0 + * @category aspects + */ + ( + self: Effect.Effect, + metric: Metric, + f: (value: Types.NoInfer) => In + ): Effect.Effect +} = internal.trackSuccessWith + +/** + * Updates the metric with the specified update message. For example, if the + * metric were a counter, the update would increment the method by the + * provided amount. + * + * @since 2.0.0 + * @category utils + */ +export const update: { + /** + * Updates the metric with the specified update message. For example, if the + * metric were a counter, the update would increment the method by the + * provided amount. + * + * @since 2.0.0 + * @category utils + */ + (input: In): (self: Metric) => Effect.Effect + /** + * Updates the metric with the specified update message. For example, if the + * metric were a counter, the update would increment the method by the + * provided amount. + * + * @since 2.0.0 + * @category utils + */ + (self: Metric, input: In): Effect.Effect +} = internal.update + +/** + * Retrieves a snapshot of the value of the metric at this moment in time. + * + * @since 2.0.0 + * @category getters + */ +export const value: (self: Metric) => Effect.Effect = internal.value + +/** + * @since 2.0.0 + * @category utils + */ +export const withNow: (self: Metric) => Metric = + internal.withNow + +/** + * @since 2.0.0 + * @category zipping + */ +export const zip: { + /** + * @since 2.0.0 + * @category zipping + */ + (that: Metric): ( + self: Metric + ) => Metric< + readonly [Type, Type2], // readonly because invariant + readonly [In, In2], // readonly because contravariant + [Out, Out2] + > + /** + * @since 2.0.0 + * @category zipping + */ + (self: Metric, that: Metric): Metric< + readonly [Type, Type2], // readonly because invariant + readonly [In, In2], // readonly because contravariant + [Out, Out2] + > +} = internal.zip + +/** + * Unsafely captures a snapshot of all metrics recorded by the application. + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeSnapshot: (_: void) => ReadonlyArray = internal.unsafeSnapshot + +/** + * @since 2.0.0 + * @category metrics + */ +export const fiberStarted: Metric.Counter = fiberRuntime.fiberStarted + +/** + * @since 2.0.0 + * @category metrics + */ +export const fiberSuccesses: Metric.Counter = fiberRuntime.fiberSuccesses + +/** + * @since 2.0.0 + * @category metrics + */ +export const fiberFailures: Metric.Counter = fiberRuntime.fiberFailures + +/** + * @since 2.0.0 + * @category metrics + */ +export const fiberLifetimes: Metric = + fiberRuntime.fiberLifetimes + +/** + * @since 2.0.0 + * @category metrics + */ +export const fiberActive: Metric.Counter = fiberRuntime.fiberActive diff --git a/backend/node_modules/effect/src/MetricKeyType.ts b/backend/node_modules/effect/src/MetricKeyType.ts new file mode 100644 index 0000000000000000000000000000000000000000..ed325d8d5dcf4301da2899b2b4f0905bbc7a9b09 --- /dev/null +++ b/backend/node_modules/effect/src/MetricKeyType.ts @@ -0,0 +1,262 @@ +/** + * @since 2.0.0 + */ +import type * as Duration from "./Duration.js" +import type * as Equal from "./Equal.js" +import * as internal from "./internal/metric/keyType.js" +import type * as MetricBoundaries from "./MetricBoundaries.js" +import type * as MetricState from "./MetricState.js" +import type { Pipeable } from "./Pipeable.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const MetricKeyTypeTypeId: unique symbol = internal.MetricKeyTypeTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type MetricKeyTypeTypeId = typeof MetricKeyTypeTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export const CounterKeyTypeTypeId: unique symbol = internal.CounterKeyTypeTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type CounterKeyTypeTypeId = typeof CounterKeyTypeTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export const FrequencyKeyTypeTypeId: unique symbol = internal.FrequencyKeyTypeTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type FrequencyKeyTypeTypeId = typeof FrequencyKeyTypeTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export const GaugeKeyTypeTypeId: unique symbol = internal.GaugeKeyTypeTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type GaugeKeyTypeTypeId = typeof GaugeKeyTypeTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export const HistogramKeyTypeTypeId: unique symbol = internal.HistogramKeyTypeTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type HistogramKeyTypeTypeId = typeof HistogramKeyTypeTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export const SummaryKeyTypeTypeId: unique symbol = internal.SummaryKeyTypeTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type SummaryKeyTypeTypeId = typeof SummaryKeyTypeTypeId + +/** + * @since 2.0.0 + * @category modelz + */ +export interface MetricKeyType extends MetricKeyType.Variance, Equal.Equal, Pipeable {} + +/** + * @since 2.0.0 + */ +export declare namespace MetricKeyType { + /** + * @since 2.0.0 + * @category models + */ + export type Untyped = MetricKeyType + + /** + * @since 2.0.0 + * @category models + */ + export type Counter = MetricKeyType> & { + readonly [CounterKeyTypeTypeId]: CounterKeyTypeTypeId + readonly incremental: boolean + readonly bigint: boolean + } + + /** + * @since 2.0.0 + * @category models + */ + export type Frequency = MetricKeyType & { + readonly [FrequencyKeyTypeTypeId]: FrequencyKeyTypeTypeId + readonly preregisteredWords: ReadonlyArray + } + + /** + * @since 2.0.0 + * @category models + */ + export type Gauge = MetricKeyType> & { + readonly [GaugeKeyTypeTypeId]: GaugeKeyTypeTypeId + readonly bigint: boolean + } + + /** + * @since 2.0.0 + * @category models + */ + export type Histogram = MetricKeyType & { + readonly [HistogramKeyTypeTypeId]: HistogramKeyTypeTypeId + readonly boundaries: MetricBoundaries.MetricBoundaries + } + + /** + * @since 2.0.0 + * @category models + */ + export type Summary = MetricKeyType & { + readonly [SummaryKeyTypeTypeId]: SummaryKeyTypeTypeId + readonly maxAge: Duration.Duration + readonly maxSize: number + readonly error: number + readonly quantiles: ReadonlyArray + } + + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [MetricKeyTypeTypeId]: { + readonly _In: Types.Contravariant + readonly _Out: Types.Covariant + } + } + + /** + * @since 2.0.0 + * @category models + */ + export type InType> = [Type] extends [ + { + readonly [MetricKeyTypeTypeId]: { + readonly _In: (_: infer In) => void + } + } + ] ? In + : never + + /** + * @since 2.0.0 + * @category models + */ + export type OutType> = [Type] extends [ + { + readonly [MetricKeyTypeTypeId]: { + readonly _Out: (_: never) => infer Out + } + } + ] ? Out + : never +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const counter: () => MetricKeyType.Counter = internal.counter + +/** + * @since 2.0.0 + * @category constructors + */ +export const frequency: ( + options?: { + readonly preregisteredWords?: ReadonlyArray | undefined + } | undefined +) => MetricKeyType.Frequency = internal.frequency + +/** + * @since 2.0.0 + * @category constructors + */ +export const gauge: () => MetricKeyType.Gauge = internal.gauge + +/** + * @since 2.0.0 + * @category constructors + */ +export const histogram: (boundaries: MetricBoundaries.MetricBoundaries) => MetricKeyType.Histogram = internal.histogram + +/** + * @since 2.0.0 + * @category constructors + */ +export const summary: ( + options: { + readonly maxAge: Duration.DurationInput + readonly maxSize: number + readonly error: number + readonly quantiles: ReadonlyArray + } +) => MetricKeyType.Summary = internal.summary + +/** + * @since 2.0.0 + * @category refinements + */ +export const isMetricKeyType: (u: unknown) => u is MetricKeyType = internal.isMetricKeyType + +/** + * @since 2.0.0 + * @category refinements + */ +export const isCounterKey: (u: unknown) => u is MetricKeyType.Counter = internal.isCounterKey + +/** + * @since 2.0.0 + * @category refinements + */ +export const isFrequencyKey: (u: unknown) => u is MetricKeyType.Frequency = internal.isFrequencyKey + +/** + * @since 2.0.0 + * @category refinements + */ +export const isGaugeKey: (u: unknown) => u is MetricKeyType.Gauge = internal.isGaugeKey + +/** + * @since 2.0.0 + * @category refinements + */ +export const isHistogramKey: (u: unknown) => u is MetricKeyType.Histogram = internal.isHistogramKey + +/** + * @since 2.0.0 + * @category refinements + */ +export const isSummaryKey: (u: unknown) => u is MetricKeyType.Summary = internal.isSummaryKey diff --git a/backend/node_modules/effect/src/MetricPair.ts b/backend/node_modules/effect/src/MetricPair.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ed12b294d757e489fecde3c27ec860a2decec5c --- /dev/null +++ b/backend/node_modules/effect/src/MetricPair.ts @@ -0,0 +1,71 @@ +/** + * @since 2.0.0 + */ +import * as internal from "./internal/metric/pair.js" +import type * as MetricKey from "./MetricKey.js" +import type * as MetricKeyType from "./MetricKeyType.js" +import type * as MetricState from "./MetricState.js" +import type { Pipeable } from "./Pipeable.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const MetricPairTypeId: unique symbol = internal.MetricPairTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type MetricPairTypeId = typeof MetricPairTypeId + +/** + * @since 2.0.0 + * @category model + */ +export interface MetricPair> + extends MetricPair.Variance, Pipeable +{ + readonly metricKey: MetricKey.MetricKey + readonly metricState: MetricState.MetricState> +} + +/** + * @since 2.0.0 + */ +export declare namespace MetricPair { + /** + * @since 2.0.0 + * @category models + */ + export interface Untyped extends MetricPair> {} + + /** + * @since 2.0.0 + * @category models + */ + export interface Variance> { + readonly [MetricPairTypeId]: { + readonly _Type: Types.Covariant + } + } +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const make: >( + metricKey: MetricKey.MetricKey, + metricState: MetricState.MetricState> +) => MetricPair.Untyped = internal.make + +/** + * @since 2.0.0 + * @category unsafe + */ +export const unsafeMake: >( + metricKey: MetricKey.MetricKey, + metricState: MetricState.MetricState.Untyped +) => MetricPair.Untyped = internal.unsafeMake diff --git a/backend/node_modules/effect/src/MetricPolling.ts b/backend/node_modules/effect/src/MetricPolling.ts new file mode 100644 index 0000000000000000000000000000000000000000..4ca12621e6cd09408100b30d05948c30865d76a5 --- /dev/null +++ b/backend/node_modules/effect/src/MetricPolling.ts @@ -0,0 +1,182 @@ +/** + * @since 2.0.0 + */ +import type * as Effect from "./Effect.js" +import type * as Fiber from "./Fiber.js" +import * as internal from "./internal/metric/polling.js" +import type * as Metric from "./Metric.js" +import type { Pipeable } from "./Pipeable.js" +import type * as Schedule from "./Schedule.js" +import type * as Scope from "./Scope.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const MetricPollingTypeId: unique symbol = internal.MetricPollingTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type MetricPollingTypeId = typeof MetricPollingTypeId + +/** + * A `MetricPolling` is a combination of a metric and an effect that polls for + * updates to the metric. + * + * @since 2.0.0 + * @category models + */ +export interface MetricPolling extends Pipeable { + readonly [MetricPollingTypeId]: MetricPollingTypeId + /** + * The metric that this `MetricPolling` polls to update. + */ + readonly metric: Metric.Metric + /** + * An effect that polls a value that may be fed to the metric. + */ + readonly poll: Effect.Effect +} + +/** + * Constructs a new polling metric from a metric and poll effect. + * + * @since 2.0.0 + * @category constructors + */ +export const make: ( + metric: Metric.Metric, + poll: Effect.Effect +) => MetricPolling = internal.make + +/** + * Collects all of the polling metrics into a single polling metric, which + * polls for, updates, and produces the outputs of all individual metrics. + * + * @since 2.0.0 + * @category constructors + */ +export const collectAll: ( + iterable: Iterable> +) => MetricPolling, Array, R, E, Array> = internal.collectAll + +/** + * Returns an effect that will launch the polling metric in a background + * fiber, using the specified schedule. + * + * @since 2.0.0 + * @category utils + */ +export const launch: { + /** + * Returns an effect that will launch the polling metric in a background + * fiber, using the specified schedule. + * + * @since 2.0.0 + * @category utils + */ + (schedule: Schedule.Schedule): ( + self: MetricPolling + ) => Effect.Effect, never, R2 | R | Scope.Scope> + /** + * Returns an effect that will launch the polling metric in a background + * fiber, using the specified schedule. + * + * @since 2.0.0 + * @category utils + */ + ( + self: MetricPolling, + schedule: Schedule.Schedule + ): Effect.Effect, never, Scope.Scope | R | R2> +} = internal.launch + +/** + * An effect that polls a value that may be fed to the metric. + * + * @since 2.0.0 + * @category utils + */ +export const poll: (self: MetricPolling) => Effect.Effect = + internal.poll + +/** + * An effect that polls for a value and uses the value to update the metric. + * + * @since 2.0.0 + * @category utils + */ +export const pollAndUpdate: ( + self: MetricPolling +) => Effect.Effect = internal.pollAndUpdate + +/** + * Returns a new polling metric whose poll function will be retried with the + * specified retry policy. + * + * @since 2.0.0 + * @category constructors + */ +export const retry: { + /** + * Returns a new polling metric whose poll function will be retried with the + * specified retry policy. + * + * @since 2.0.0 + * @category constructors + */ + (policy: Schedule.Schedule, R2>): (self: MetricPolling) => MetricPolling + /** + * Returns a new polling metric whose poll function will be retried with the + * specified retry policy. + * + * @since 2.0.0 + * @category constructors + */ + ( + self: MetricPolling, + policy: Schedule.Schedule + ): MetricPolling +} = internal.retry + +/** + * Zips this polling metric with the specified polling metric. + * + * @since 2.0.0 + * @category utils + */ +export const zip: { + /** + * Zips this polling metric with the specified polling metric. + * + * @since 2.0.0 + * @category utils + */ + (that: MetricPolling): ( + self: MetricPolling + ) => MetricPolling< + readonly [Type, Type2], // readonly because invariant + readonly [In, In2], // readonly because contravariant + R2 | R, + E2 | E, + [Out, Out2] + > + /** + * Zips this polling metric with the specified polling metric. + * + * @since 2.0.0 + * @category utils + */ + ( + self: MetricPolling, + that: MetricPolling + ): MetricPolling< + readonly [Type, Type2], // readonly because invariant + readonly [In, In2], // readonly because contravariant + R | R2, + E | E2, + [Out, Out2] + > +} = internal.zip diff --git a/backend/node_modules/effect/src/MetricState.ts b/backend/node_modules/effect/src/MetricState.ts new file mode 100644 index 0000000000000000000000000000000000000000..30fd99e961af17bc1e8a0e2ffc810e920dbabcee --- /dev/null +++ b/backend/node_modules/effect/src/MetricState.ts @@ -0,0 +1,273 @@ +/** + * @since 2.0.0 + */ +import type * as Equal from "./Equal.js" +import * as internal from "./internal/metric/state.js" +import type * as MetricKeyType from "./MetricKeyType.js" +import type * as Option from "./Option.js" +import type { Pipeable } from "./Pipeable.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const MetricStateTypeId: unique symbol = internal.MetricStateTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type MetricStateTypeId = typeof MetricStateTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export const CounterStateTypeId: unique symbol = internal.CounterStateTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type CounterStateTypeId = typeof CounterStateTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export const FrequencyStateTypeId: unique symbol = internal.FrequencyStateTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type FrequencyStateTypeId = typeof FrequencyStateTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export const GaugeStateTypeId: unique symbol = internal.GaugeStateTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type GaugeStateTypeId = typeof GaugeStateTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export const HistogramStateTypeId: unique symbol = internal.HistogramStateTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type HistogramStateTypeId = typeof HistogramStateTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export const SummaryStateTypeId: unique symbol = internal.SummaryStateTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type SummaryStateTypeId = typeof SummaryStateTypeId + +/** + * A `MetricState` describes the state of a metric. The type parameter of a + * metric state corresponds to the type of the metric key (`MetricStateType`). + * This phantom type parameter is used to tie keys to their expected states. + * + * @since 2.0.0 + * @category models + */ +export interface MetricState extends MetricState.Variance, Equal.Equal, Pipeable {} + +/** + * @since 2.0.0 + */ +export declare namespace MetricState { + /** + * @since 2.0.0 + * @category models + */ + export interface Untyped extends MetricState {} + + /** + * @since 2.0.0 + * @category models + */ + export interface Counter + extends MetricState> + { + readonly [CounterStateTypeId]: CounterStateTypeId + readonly count: A + } + + /** + * @since 2.0.0 + * @category models + */ + export interface Frequency extends MetricState { + readonly [FrequencyStateTypeId]: FrequencyStateTypeId + readonly occurrences: ReadonlyMap + } + + /** + * @since 2.0.0 + * @category models + */ + export interface Gauge extends MetricState> { + readonly [GaugeStateTypeId]: GaugeStateTypeId + readonly value: A + } + + /** + * @since 2.0.0 + * @category models + */ + export interface Histogram extends MetricState { + readonly [HistogramStateTypeId]: HistogramStateTypeId + readonly buckets: ReadonlyArray + readonly count: number + readonly min: number + readonly max: number + readonly sum: number + } + + /** + * @since 2.0.0 + * @category models + */ + export interface Summary extends MetricState { + readonly [SummaryStateTypeId]: SummaryStateTypeId + readonly error: number + readonly quantiles: ReadonlyArray]> + readonly count: number + readonly min: number + readonly max: number + readonly sum: number + } + + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [MetricStateTypeId]: { + readonly _A: Types.Contravariant + } + } +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const counter: { + /** + * @since 2.0.0 + * @category constructors + */ + (count: number): MetricState.Counter + /** + * @since 2.0.0 + * @category constructors + */ + (count: bigint): MetricState.Counter +} = internal.counter + +/** + * @since 2.0.0 + * @category constructors + */ +export const frequency: (occurrences: ReadonlyMap) => MetricState.Frequency = internal.frequency + +/** + * @since 2.0.0 + * @category constructors + */ +export const gauge: { + /** + * @since 2.0.0 + * @category constructors + */ + (count: number): MetricState.Gauge + /** + * @since 2.0.0 + * @category constructors + */ + (count: bigint): MetricState.Gauge +} = internal.gauge + +/** + * @since 2.0.0 + * @category constructors + */ +export const histogram: ( + options: { + readonly buckets: ReadonlyArray + readonly count: number + readonly min: number + readonly max: number + readonly sum: number + } +) => MetricState.Histogram = internal.histogram + +/** + * @since 2.0.0 + * @category constructors + */ +export const summary: ( + options: { + readonly error: number + readonly quantiles: ReadonlyArray]> + readonly count: number + readonly min: number + readonly max: number + readonly sum: number + } +) => MetricState.Summary = internal.summary + +/** + * @since 2.0.0 + * @category refinements + */ +export const isMetricState: (u: unknown) => u is MetricState.Counter = internal.isMetricState + +/** + * @since 2.0.0 + * @category refinements + */ +export const isCounterState: (u: unknown) => u is MetricState.Counter = internal.isCounterState + +/** + * @since 2.0.0 + * @category refinements + */ +export const isFrequencyState: (u: unknown) => u is MetricState.Frequency = internal.isFrequencyState + +/** + * @since 2.0.0 + * @category refinements + */ +export const isGaugeState: (u: unknown) => u is MetricState.Gauge = internal.isGaugeState + +/** + * @since 2.0.0 + * @category refinements + */ +export const isHistogramState: (u: unknown) => u is MetricState.Histogram = internal.isHistogramState + +/** + * @since 2.0.0 + * @category refinements + */ +export const isSummaryState: (u: unknown) => u is MetricState.Summary = internal.isSummaryState diff --git a/backend/node_modules/effect/src/Micro.ts b/backend/node_modules/effect/src/Micro.ts new file mode 100644 index 0000000000000000000000000000000000000000..4aee42a112edd85a84a345d03647522f5aa629c1 --- /dev/null +++ b/backend/node_modules/effect/src/Micro.ts @@ -0,0 +1,5556 @@ +/** + * A lightweight alternative to the `Effect` data type, with a subset of the functionality. + * + * @since 3.4.0 + * @experimental + */ +import * as Arr from "./Array.js" +import type { Channel } from "./Channel.js" +import * as Context from "./Context.js" +import type { Effect, EffectUnify, EffectUnifyIgnore } from "./Effect.js" +import * as Effectable from "./Effectable.js" +import * as Either from "./Either.js" +import * as Equal from "./Equal.js" +import type { LazyArg } from "./Function.js" +import { constTrue, constVoid, dual, identity } from "./Function.js" +import { globalValue } from "./GlobalValue.js" +import * as Hash from "./Hash.js" +import type { TypeLambda } from "./HKT.js" +import type { Inspectable } from "./Inspectable.js" +import { format, NodeInspectSymbol, toStringUnknown } from "./Inspectable.js" +import * as InternalContext from "./internal/context.js" +import * as doNotation from "./internal/doNotation.js" +import { StructuralPrototype } from "./internal/effectable.js" +import * as Option from "./Option.js" +import type { Pipeable } from "./Pipeable.js" +import { pipeArguments } from "./Pipeable.js" +import type { Predicate, Refinement } from "./Predicate.js" +import { hasProperty, isIterable, isTagged } from "./Predicate.js" +import type { Sink } from "./Sink.js" +import type { Stream } from "./Stream.js" +import type { Concurrency, Covariant, Equals, NoExcessProperties, NotFunction, Simplify } from "./Types.js" +import type * as Unify from "./Unify.js" +import { SingleShotGen, YieldWrap, yieldWrapGet } from "./Utils.js" + +/** + * @since 3.4.0 + * @experimental + * @category type ids + */ +export const TypeId: unique symbol = Symbol.for("effect/Micro") + +/** + * @since 3.4.0 + * @experimental + * @category type ids + */ +export type TypeId = typeof TypeId + +/** + * @since 3.4.0 + * @experimental + * @category MicroExit + */ +export const MicroExitTypeId: unique symbol = Symbol.for( + "effect/Micro/MicroExit" +) + +/** + * @since 3.4.0 + * @experimental + * @category MicroExit + */ +export type MicroExitTypeId = typeof TypeId + +/** + * A lightweight alternative to the `Effect` data type, with a subset of the functionality. + * + * @since 3.4.0 + * @experimental + * @category models + */ +export interface Micro extends Effect { + readonly [TypeId]: Micro.Variance + [Symbol.iterator](): MicroIterator> + [Unify.typeSymbol]?: unknown + [Unify.unifySymbol]?: MicroUnify + [Unify.ignoreSymbol]?: MicroUnifyIgnore +} + +/** + * @category models + * @since 3.4.3 + */ +export interface MicroUnify extends EffectUnify { + Micro?: () => A[Unify.typeSymbol] extends Micro | infer _ ? Micro : never +} + +/** + * @category models + * @since 3.4.3 + */ +export interface MicroUnifyIgnore extends EffectUnifyIgnore { + Effect?: true +} +/** + * @category type lambdas + * @since 3.4.1 + */ +export interface MicroTypeLambda extends TypeLambda { + readonly type: Micro +} + +/** + * @since 3.4.0 + * @experimental + */ +export declare namespace Micro { + /** + * @since 3.4.0 + * @experimental + */ + export interface Variance { + _A: Covariant + _E: Covariant + _R: Covariant + } + + /** + * @since 3.4.0 + * @experimental + */ + export type Success = T extends Micro ? _A : never + + /** + * @since 3.4.0 + * @experimental + */ + export type Error = T extends Micro ? _E : never + + /** + * @since 3.4.0 + * @experimental + */ + export type Context = T extends Micro ? _R : never +} + +/** + * @since 3.4.0 + * @experimental + * @category guards + */ +export const isMicro = (u: unknown): u is Micro => typeof u === "object" && u !== null && TypeId in u + +/** + * @since 3.4.0 + * @experimental + * @category models + */ +export interface MicroIterator> { + next(...args: ReadonlyArray): IteratorResult, Micro.Success> +} + +// ---------------------------------------------------------------------------- +// MicroCause +// ---------------------------------------------------------------------------- + +/** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ +export const MicroCauseTypeId = Symbol.for("effect/Micro/MicroCause") + +/** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ +export type MicroCauseTypeId = typeof MicroCauseTypeId + +/** + * A `MicroCause` is a data type that represents the different ways a `Micro` can fail. + * + * **Details** + * + * `MicroCause` comes in three forms: + * + * - `Die`: Indicates an unforeseen defect that wasn't planned for in the system's logic. + * - `Fail`: Covers anticipated errors that are recognized and typically handled within the application. + * - `Interrupt`: Signifies an operation that has been purposefully stopped. + * + * @since 3.4.6 + * @experimental + * @category MicroCause + */ +export type MicroCause = + | MicroCause.Die + | MicroCause.Fail + | MicroCause.Interrupt + +/** + * @since 3.6.6 + * @experimental + * @category guards + */ +export const isMicroCause = (self: unknown): self is MicroCause => hasProperty(self, MicroCauseTypeId) + +/** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ +export declare namespace MicroCause { + /** + * @since 3.4.6 + * @experimental + */ + export type Error = T extends MicroCause.Fail ? E : never + + /** + * @since 3.4.0 + * @experimental + */ + export interface Proto extends Pipeable, globalThis.Error { + readonly [MicroCauseTypeId]: { + _E: Covariant + } + readonly _tag: Tag + readonly traces: ReadonlyArray + } + + /** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ + export interface Die extends Proto<"Die", never> { + readonly defect: unknown + } + + /** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ + export interface Fail extends Proto<"Fail", E> { + readonly error: E + } + + /** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ + export interface Interrupt extends Proto<"Interrupt", never> {} +} + +const microCauseVariance = { + _E: identity +} + +abstract class MicroCauseImpl extends globalThis.Error implements MicroCause.Proto { + readonly [MicroCauseTypeId]: { + _E: Covariant + } + constructor( + readonly _tag: Tag, + originalError: unknown, + readonly traces: ReadonlyArray + ) { + const causeName = `MicroCause.${_tag}` + let name: string + let message: string + let stack: string + if (originalError instanceof globalThis.Error) { + name = `(${causeName}) ${originalError.name}` + message = originalError.message as string + const messageLines = message.split("\n").length + stack = originalError.stack + ? `(${causeName}) ${ + originalError.stack + .split("\n") + .slice(0, messageLines + 3) + .join("\n") + }` + : `${name}: ${message}` + } else { + name = causeName + message = toStringUnknown(originalError, 0) + stack = `${name}: ${message}` + } + if (traces.length > 0) { + stack += `\n ${traces.join("\n ")}` + } + super(message) + this[MicroCauseTypeId] = microCauseVariance + this.name = name + this.stack = stack + } + pipe() { + return pipeArguments(this, arguments) + } + toString() { + return this.stack + } + [NodeInspectSymbol]() { + return this.stack + } +} + +class Fail extends MicroCauseImpl<"Fail", E> implements MicroCause.Fail { + constructor( + readonly error: E, + traces: ReadonlyArray = [] + ) { + super("Fail", error, traces) + } +} + +/** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ +export const causeFail = ( + error: E, + traces: ReadonlyArray = [] +): MicroCause => new Fail(error, traces) + +class Die extends MicroCauseImpl<"Die", never> implements MicroCause.Die { + constructor( + readonly defect: unknown, + traces: ReadonlyArray = [] + ) { + super("Die", defect, traces) + } +} + +/** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ +export const causeDie = ( + defect: unknown, + traces: ReadonlyArray = [] +): MicroCause => new Die(defect, traces) + +class Interrupt extends MicroCauseImpl<"Interrupt", never> implements MicroCause.Interrupt { + constructor(traces: ReadonlyArray = []) { + super("Interrupt", "interrupted", traces) + } +} + +/** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ +export const causeInterrupt = ( + traces: ReadonlyArray = [] +): MicroCause => new Interrupt(traces) + +/** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ +export const causeIsFail = ( + self: MicroCause +): self is MicroCause.Fail => self._tag === "Fail" + +/** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ +export const causeIsDie = (self: MicroCause): self is MicroCause.Die => self._tag === "Die" + +/** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ +export const causeIsInterrupt = ( + self: MicroCause +): self is MicroCause.Interrupt => self._tag === "Interrupt" + +/** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ +export const causeSquash = (self: MicroCause): unknown => + self._tag === "Fail" ? self.error : self._tag === "Die" ? self.defect : self + +/** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ +export const causeWithTrace: { + /** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ + (trace: string): (self: MicroCause) => MicroCause + /** + * @since 3.4.6 + * @experimental + * @category MicroCause + */ + (self: MicroCause, trace: string): MicroCause +} = dual(2, (self: MicroCause, trace: string): MicroCause => { + const traces = [...self.traces, trace] + switch (self._tag) { + case "Die": + return causeDie(self.defect, traces) + case "Interrupt": + return causeInterrupt(traces) + case "Fail": + return causeFail(self.error, traces) + } +}) + +// ---------------------------------------------------------------------------- +// MicroFiber +// ---------------------------------------------------------------------------- + +/** + * @since 3.11.0 + * @experimental + * @category MicroFiber + */ +export const MicroFiberTypeId = Symbol.for("effect/Micro/MicroFiber") + +/** + * @since 3.11.0 + * @experimental + * @category MicroFiber + */ +export type MicroFiberTypeId = typeof MicroFiberTypeId + +/** + * @since 3.11.0 + * @experimental + * @category MicroFiber + */ +export interface MicroFiber { + readonly [MicroFiberTypeId]: MicroFiber.Variance + + readonly currentOpCount: number + readonly getRef: (ref: Context.Reference) => A + readonly context: Context.Context + readonly addObserver: (cb: (exit: MicroExit) => void) => () => void + readonly unsafeInterrupt: () => void + readonly unsafePoll: () => MicroExit | undefined +} + +/** + * @since 3.11.0 + * @experimental + * @category MicroFiber + */ +export declare namespace MicroFiber { + /** + * @since 3.11.0 + * @experimental + * @category MicroFiber + */ + export interface Variance { + readonly _A: Covariant + readonly _E: Covariant + } +} + +const fiberVariance = { + _A: identity, + _E: identity +} + +class MicroFiberImpl implements MicroFiber { + readonly [MicroFiberTypeId]: MicroFiber.Variance + + readonly _stack: Array = [] + readonly _observers: Array<(exit: MicroExit) => void> = [] + _exit: MicroExit | undefined + public _children: Set> | undefined + + public currentOpCount = 0 + + constructor( + public context: Context.Context, + public interruptible = true + ) { + this[MicroFiberTypeId] = fiberVariance + } + + getRef(ref: Context.Reference): A { + return InternalContext.unsafeGetReference(this.context, ref) + } + + addObserver(cb: (exit: MicroExit) => void): () => void { + if (this._exit) { + cb(this._exit) + return constVoid + } + this._observers.push(cb) + return () => { + const index = this._observers.indexOf(cb) + if (index >= 0) { + this._observers.splice(index, 1) + } + } + } + + _interrupted = false + unsafeInterrupt(): void { + if (this._exit) { + return + } + this._interrupted = true + if (this.interruptible) { + this.evaluate(exitInterrupt as any) + } + } + + unsafePoll(): MicroExit | undefined { + return this._exit + } + + evaluate(effect: Primitive): void { + if (this._exit) { + return + } else if (this._yielded !== undefined) { + const yielded = this._yielded as () => void + this._yielded = undefined + yielded() + } + const exit = this.runLoop(effect) + if (exit === Yield) { + return + } + + // the interruptChildren middlware is added in Micro.fork, so it can be + // tree-shaken if not used + const interruptChildren = fiberMiddleware.interruptChildren && fiberMiddleware.interruptChildren(this) + if (interruptChildren !== undefined) { + return this.evaluate(flatMap(interruptChildren, () => exit) as any) + } + + this._exit = exit + for (let i = 0; i < this._observers.length; i++) { + this._observers[i](exit) + } + this._observers.length = 0 + } + + runLoop(effect: Primitive): MicroExit | Yield { + let yielding = false + let current: Primitive | Yield = effect + this.currentOpCount = 0 + try { + while (true) { + this.currentOpCount++ + if (!yielding && this.getRef(CurrentScheduler).shouldYield(this as any)) { + yielding = true + const prev = current + current = flatMap(yieldNow, () => prev as any) as any + } + current = (current as any)[evaluate](this) + if (current === Yield) { + const yielded = this._yielded! + if (MicroExitTypeId in yielded) { + this._yielded = undefined + return yielded + } + return Yield + } + } + } catch (error) { + if (!hasProperty(current, evaluate)) { + return exitDie(`MicroFiber.runLoop: Not a valid effect: ${String(current)}`) + } + return exitDie(error) + } + } + + getCont( + symbol: S + ): Primitive & Record Primitive> | undefined { + while (true) { + const op = this._stack.pop() + if (!op) return undefined + const cont = op[ensureCont] && op[ensureCont](this) + if (cont) return { [symbol]: cont } as any + if (op[symbol]) return op as any + } + } + + // cancel the yielded operation, or for the yielded exit value + _yielded: MicroExit | (() => void) | undefined = undefined + yieldWith(value: MicroExit | (() => void)): Yield { + this._yielded = value + return Yield + } + + children(): Set> { + return this._children ??= new Set() + } +} + +const fiberMiddleware = globalValue("effect/Micro/fiberMiddleware", () => ({ + interruptChildren: undefined as ((fiber: MicroFiberImpl) => Micro | undefined) | undefined +})) + +const fiberInterruptChildren = (fiber: MicroFiberImpl) => { + if (fiber._children === undefined || fiber._children.size === 0) { + return undefined + } + return fiberInterruptAll(fiber._children) +} + +/** + * @since 3.11.0 + * @experimental + * @category MicroFiber + */ +export const fiberAwait = (self: MicroFiber): Micro> => + async((resume) => sync(self.addObserver((exit) => resume(succeed(exit))))) + +/** + * @since 3.11.2 + * @experimental + * @category MicroFiber + */ +export const fiberJoin = (self: MicroFiber): Micro => flatten(fiberAwait(self)) + +/** + * @since 3.11.0 + * @experimental + * @category MicroFiber + */ +export const fiberInterrupt = (self: MicroFiber): Micro => + suspend(() => { + self.unsafeInterrupt() + return asVoid(fiberAwait(self)) + }) + +/** + * @since 3.11.0 + * @experimental + * @category MicroFiber + */ +export const fiberInterruptAll = >>(fibers: A): Micro => + suspend(() => { + for (const fiber of fibers) fiber.unsafeInterrupt() + const iter = fibers[Symbol.iterator]() + const wait: Micro = suspend(() => { + let result = iter.next() + while (!result.done) { + if (result.value.unsafePoll()) { + result = iter.next() + continue + } + const fiber = result.value + return async((resume) => { + fiber.addObserver((_) => { + resume(wait) + }) + }) + } + return exitVoid + }) + return wait + }) + +const identifier = Symbol.for("effect/Micro/identifier") +type identifier = typeof identifier + +const args = Symbol.for("effect/Micro/args") +type args = typeof args + +const evaluate = Symbol.for("effect/Micro/evaluate") +type evaluate = typeof evaluate + +const successCont = Symbol.for("effect/Micro/successCont") +type successCont = typeof successCont + +const failureCont = Symbol.for("effect/Micro/failureCont") +type failureCont = typeof failureCont + +const ensureCont = Symbol.for("effect/Micro/ensureCont") +type ensureCont = typeof ensureCont + +const Yield = Symbol.for("effect/Micro/Yield") +type Yield = typeof Yield + +interface Primitive { + readonly [identifier]: string + readonly [successCont]: ((value: unknown, fiber: MicroFiberImpl) => Primitive | Yield) | undefined + readonly [failureCont]: + | ((cause: MicroCause, fiber: MicroFiberImpl) => Primitive | Yield) + | undefined + readonly [ensureCont]: + | ((fiber: MicroFiberImpl) => + | ((value: unknown, fiber: MicroFiberImpl) => Primitive | Yield) + | undefined) + | undefined + [evaluate](fiber: MicroFiberImpl): Primitive | Yield +} + +const microVariance = { + _A: identity, + _E: identity, + _R: identity +} + +const MicroProto = { + ...Effectable.EffectPrototype, + _op: "Micro", + [TypeId]: microVariance, + pipe() { + return pipeArguments(this, arguments) + }, + [Symbol.iterator]() { + return new SingleShotGen(new YieldWrap(this)) as any + }, + toJSON(this: Primitive) { + return { + _id: "Micro", + op: this[identifier], + ...(args in this ? { args: this[args] } : undefined) + } + }, + toString() { + return format(this) + }, + [NodeInspectSymbol]() { + return format(this) + } +} + +function defaultEvaluate(_fiber: MicroFiberImpl): Primitive | Yield { + return exitDie(`Micro.evaluate: Not implemented`) as any +} + +const makePrimitiveProto = (options: { + readonly op: Op + readonly eval?: (fiber: MicroFiberImpl) => Primitive | Micro | Yield + readonly contA?: (this: Primitive, value: any, fiber: MicroFiberImpl) => Primitive | Micro | Yield + readonly contE?: ( + this: Primitive, + cause: MicroCause, + fiber: MicroFiberImpl + ) => Primitive | Micro | Yield + readonly ensure?: (this: Primitive, fiber: MicroFiberImpl) => void | ((value: any, fiber: MicroFiberImpl) => void) +}): Primitive => ({ + ...MicroProto, + [identifier]: options.op, + [evaluate]: options.eval ?? defaultEvaluate, + [successCont]: options.contA, + [failureCont]: options.contE, + [ensureCont]: options.ensure +} as any) + +const makePrimitive = ) => any, Single extends boolean = true>(options: { + readonly op: string + readonly single?: Single + readonly eval?: ( + this: Primitive & { readonly [args]: Single extends true ? Parameters[0] : Parameters }, + fiber: MicroFiberImpl + ) => Primitive | Micro | Yield + readonly contA?: ( + this: Primitive & { readonly [args]: Single extends true ? Parameters[0] : Parameters }, + value: any, + fiber: MicroFiberImpl + ) => Primitive | Micro | Yield + readonly contE?: ( + this: Primitive & { readonly [args]: Single extends true ? Parameters[0] : Parameters }, + cause: MicroCause, + fiber: MicroFiberImpl + ) => Primitive | Micro | Yield + readonly ensure?: ( + this: Primitive & { readonly [args]: Single extends true ? Parameters[0] : Parameters }, + fiber: MicroFiberImpl + ) => void | ((value: any, fiber: MicroFiberImpl) => void) +}): Fn => { + const Proto = makePrimitiveProto(options as any) + return function() { + const self = Object.create(Proto) + self[args] = options.single === false ? arguments : arguments[0] + return self + } as Fn +} + +const makeExit = ) => any, Prop extends string>(options: { + readonly op: "Success" | "Failure" + readonly prop: Prop + readonly eval: ( + this: + & MicroExit + & { [args]: Parameters[0] }, + fiber: MicroFiberImpl + ) => Primitive | Yield +}): Fn => { + const Proto = { + ...makePrimitiveProto(options), + [MicroExitTypeId]: MicroExitTypeId, + _tag: options.op, + get [options.prop](): any { + return (this as any)[args] + }, + toJSON(this: any) { + return { + _id: "MicroExit", + _tag: options.op, + [options.prop]: this[args] + } + }, + [Equal.symbol](this: any, that: any): boolean { + return isMicroExit(that) && that._tag === options.op && + Equal.equals(this[args], (that as any)[args]) + }, + [Hash.symbol](this: any): number { + return Hash.cached(this, Hash.combine(Hash.string(options.op))(Hash.hash(this[args]))) + } + } + return function(value: unknown) { + const self = Object.create(Proto) + self[args] = value + self[successCont] = undefined + self[failureCont] = undefined + self[ensureCont] = undefined + return self + } as Fn +} + +/** + * Creates a `Micro` effect that will succeed with the specified constant value. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const succeed: (value: A) => Micro = makeExit({ + op: "Success", + prop: "value", + eval(fiber) { + const cont = fiber.getCont(successCont) + return cont ? cont[successCont](this[args], fiber) : fiber.yieldWith(this) + } +}) + +/** + * Creates a `Micro` effect that will fail with the specified `MicroCause`. + * + * @since 3.4.6 + * @experimental + * @category constructors + */ +export const failCause: (cause: MicroCause) => Micro = makeExit({ + op: "Failure", + prop: "cause", + eval(fiber) { + let cont = fiber.getCont(failureCont) + while (causeIsInterrupt(this[args]) && cont && fiber.interruptible) { + cont = fiber.getCont(failureCont) + } + return cont ? cont[failureCont](this[args], fiber) : fiber.yieldWith(this) + } +}) + +/** + * Creates a `Micro` effect that fails with the given error. + * + * This results in a `Fail` variant of the `MicroCause` type, where the error is + * tracked at the type level. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const fail = (error: E): Micro => failCause(causeFail(error)) + +/** + * Creates a `Micro` effect that succeeds with a lazily evaluated value. + * + * If the evaluation of the value throws an error, the effect will fail with a + * `Die` variant of the `MicroCause` type. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const sync: (evaluate: LazyArg) => Micro = makePrimitive({ + op: "Sync", + eval(fiber): Primitive | Yield { + const value = this[args]() + const cont = fiber.getCont(successCont) + return cont ? cont[successCont](value, fiber) : fiber.yieldWith(exitSucceed(value)) + } +}) + +/** + * Lazily creates a `Micro` effect from the given side-effect. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const suspend: (evaluate: LazyArg>) => Micro = makePrimitive({ + op: "Suspend", + eval(_fiber) { + return this[args]() + } +}) + +/** + * Pause the execution of the current `Micro` effect, and resume it on the next + * scheduler tick. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const yieldNowWith: (priority?: number) => Micro = makePrimitive({ + op: "Yield", + eval(fiber) { + let resumed = false + fiber.getRef(CurrentScheduler).scheduleTask(() => { + if (resumed) return + fiber.evaluate(exitVoid as any) + }, this[args] ?? 0) + return fiber.yieldWith(() => { + resumed = true + }) + } +}) + +/** + * Pause the execution of the current `Micro` effect, and resume it on the next + * scheduler tick. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const yieldNow: Micro = yieldNowWith(0) + +/** + * Creates a `Micro` effect that will succeed with the value wrapped in `Some`. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const succeedSome = (a: A): Micro> => succeed(Option.some(a)) + +/** + * Creates a `Micro` effect that succeeds with `None`. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const succeedNone: Micro> = succeed(Option.none()) + +/** + * Creates a `Micro` effect that will fail with the lazily evaluated `MicroCause`. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const failCauseSync = (evaluate: LazyArg>): Micro => + suspend(() => failCause(evaluate())) + +/** + * Creates a `Micro` effect that will die with the specified error. + * + * This results in a `Die` variant of the `MicroCause` type, where the error is + * not tracked at the type level. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const die = (defect: unknown): Micro => exitDie(defect) + +/** + * Creates a `Micro` effect that will fail with the lazily evaluated error. + * + * This results in a `Fail` variant of the `MicroCause` type, where the error is + * tracked at the type level. + * + * @since 3.4.6 + * @experimental + * @category constructors + */ +export const failSync = (error: LazyArg): Micro => suspend(() => fail(error())) + +/** + * Converts an `Option` into a `Micro` effect, that will fail with + * `NoSuchElementException` if the option is `None`. Otherwise, it will succeed with the + * value of the option. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const fromOption = (option: Option.Option): Micro => + option._tag === "Some" ? succeed(option.value) : fail(new NoSuchElementException({})) + +/** + * Converts an `Either` into a `Micro` effect, that will fail with the left side + * of the either if it is a `Left`. Otherwise, it will succeed with the right + * side of the either. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const fromEither = (either: Either.Either): Micro => + either._tag === "Right" ? succeed(either.right) : fail(either.left) + +const void_: Micro = succeed(void 0) +export { + /** + * A `Micro` effect that will succeed with `void` (`undefined`). + * + * @since 3.4.0 + * @experimental + * @category constructors + */ + void_ as void +} + +const try_ = (options: { + try: LazyArg + catch: (error: unknown) => E +}): Micro => + suspend(() => { + try { + return succeed(options.try()) + } catch (err) { + return fail(options.catch(err)) + } + }) +export { + /** + * The `Micro` equivalent of a try / catch block, which allows you to map + * thrown errors to a specific error type. + * + * @example + * ```ts + * import { Micro } from "effect" + * + * Micro.try({ + * try: () => { throw new Error("boom") }, + * catch: (cause) => new Error("caught", { cause }) + * }) + * ``` + * + * @since 3.4.0 + * @experimental + * @category constructors + */ + try_ as try +} + +/** + * Wrap a `Promise` into a `Micro` effect. + * + * Any errors will result in a `Die` variant of the `MicroCause` type, where the + * error is not tracked at the type level. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const promise = (evaluate: (signal: AbortSignal) => PromiseLike): Micro => + asyncOptions(function(resume, signal) { + evaluate(signal!).then( + (a) => resume(succeed(a)), + (e) => resume(die(e)) + ) + }, evaluate.length !== 0) + +/** + * Wrap a `Promise` into a `Micro` effect. Any errors will be caught and + * converted into a specific error type. + * + * @example + * ```ts + * import { Micro } from "effect" + * + * Micro.tryPromise({ + * try: () => Promise.resolve("success"), + * catch: (cause) => new Error("caught", { cause }) + * }) + * ``` + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const tryPromise = (options: { + readonly try: (signal: AbortSignal) => PromiseLike + readonly catch: (error: unknown) => E +}): Micro => + asyncOptions(function(resume, signal) { + try { + options.try(signal!).then( + (a) => resume(succeed(a)), + (e) => resume(fail(options.catch(e))) + ) + } catch (err) { + resume(fail(options.catch(err))) + } + }, options.try.length !== 0) + +/** + * Create a `Micro` effect using the current `MicroFiber`. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const withMicroFiber: ( + evaluate: (fiber: MicroFiberImpl) => Micro +) => Micro = makePrimitive({ + op: "WithMicroFiber", + eval(fiber) { + return this[args](fiber) + } +}) + +/** + * Flush any yielded effects that are waiting to be executed. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const yieldFlush: Micro = withMicroFiber((fiber) => { + fiber.getRef(CurrentScheduler).flush() + return exitVoid +}) + +const asyncOptions: ( + register: ( + resume: (effect: Micro) => void, + signal?: AbortSignal + ) => void | Micro, + withSignal: boolean +) => Micro = makePrimitive({ + op: "Async", + single: false, + eval(fiber) { + const register = this[args][0] + let resumed = false + let yielded: boolean | Primitive = false + const controller = this[args][1] ? new AbortController() : undefined + const onCancel = register((effect) => { + if (resumed) return + resumed = true + if (yielded) { + fiber.evaluate(effect as any) + } else { + yielded = effect as any + } + }, controller?.signal) + if (yielded !== false) return yielded + yielded = true + fiber._yielded = () => { + resumed = true + } + if (controller === undefined && onCancel === undefined) { + return Yield + } + fiber._stack.push(asyncFinalizer(() => { + resumed = true + controller?.abort() + return onCancel ?? exitVoid + })) + return Yield + } +}) +const asyncFinalizer: (onInterrupt: () => Micro) => Primitive = makePrimitive({ + op: "AsyncFinalizer", + ensure(fiber) { + if (fiber.interruptible) { + fiber.interruptible = false + fiber._stack.push(setInterruptible(true)) + } + }, + contE(cause, _fiber) { + return causeIsInterrupt(cause) + ? flatMap(this[args](), () => failCause(cause)) + : failCause(cause) + } +}) + +/** + * Create a `Micro` effect from an asynchronous computation. + * + * You can return a cleanup effect that will be run when the effect is aborted. + * It is also passed an `AbortSignal` that is triggered when the effect is + * aborted. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const async = ( + register: ( + resume: (effect: Micro) => void, + signal: AbortSignal + ) => void | Micro +): Micro => asyncOptions(register as any, register.length >= 2) + +/** + * A `Micro` that will never succeed or fail. It wraps `setInterval` to prevent + * the Javascript runtime from exiting. + * + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const never: Micro = async(function() { + const interval = setInterval(constVoid, 2147483646) + return sync(() => clearInterval(interval)) +}) + +/** + * @since 3.4.0 + * @experimental + * @category constructors + */ +export const gen = >, AEff>( + ...args: + | [self: Self, body: (this: Self) => Generator] + | [body: () => Generator] +): Micro< + AEff, + [Eff] extends [never] ? never : [Eff] extends [YieldWrap>] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [YieldWrap>] ? R : never +> => suspend(() => fromIterator(args.length === 1 ? args[0]() : args[1].call(args[0]) as any)) + +const fromIterator: ( + iterator: Iterator>> +) => Micro = makePrimitive({ + op: "Iterator", + contA(value, fiber) { + const state = this[args].next(value) + if (state.done) return succeed(state.value) + fiber._stack.push(this) + return yieldWrapGet(state.value) + }, + eval(this: any, fiber: MicroFiberImpl) { + return this[successCont](undefined, fiber) + } +}) + +// ---------------------------------------------------------------------------- +// mapping & sequencing +// ---------------------------------------------------------------------------- + +/** + * Create a `Micro` effect that will replace the success value of the given + * effect. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ +export const as: { + // ---------------------------------------------------------------------------- + // mapping & sequencing + // ---------------------------------------------------------------------------- + + /** + * Create a `Micro` effect that will replace the success value of the given + * effect. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (value: B): (self: Micro) => Micro + // ---------------------------------------------------------------------------- + // mapping & sequencing + // ---------------------------------------------------------------------------- + + /** + * Create a `Micro` effect that will replace the success value of the given + * effect. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (self: Micro, value: B): Micro +} = dual(2, (self: Micro, value: B): Micro => map(self, (_) => value)) + +/** + * Wrap the success value of this `Micro` effect in a `Some`. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ +export const asSome = (self: Micro): Micro, E, R> => map(self, Option.some) + +/** + * Swap the error and success types of the `Micro` effect. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ +export const flip = (self: Micro): Micro => + matchEffect(self, { + onFailure: succeed, + onSuccess: fail + }) + +/** + * A more flexible version of `flatMap` that combines `map` and `flatMap` into a + * single API. + * + * It also lets you directly pass a `Micro` effect, which will be executed after + * the current effect. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ +export const andThen: { + /** + * A more flexible version of `flatMap` that combines `map` and `flatMap` into a + * single API. + * + * It also lets you directly pass a `Micro` effect, which will be executed after + * the current effect. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (f: (a: A) => X): ( + self: Micro + ) => [X] extends [Micro] ? Micro + : Micro + /** + * A more flexible version of `flatMap` that combines `map` and `flatMap` into a + * single API. + * + * It also lets you directly pass a `Micro` effect, which will be executed after + * the current effect. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (f: NotFunction): ( + self: Micro + ) => [X] extends [Micro] ? Micro + : Micro + /** + * A more flexible version of `flatMap` that combines `map` and `flatMap` into a + * single API. + * + * It also lets you directly pass a `Micro` effect, which will be executed after + * the current effect. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (self: Micro, f: (a: A) => X): [X] extends [Micro] ? Micro + : Micro + /** + * A more flexible version of `flatMap` that combines `map` and `flatMap` into a + * single API. + * + * It also lets you directly pass a `Micro` effect, which will be executed after + * the current effect. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (self: Micro, f: NotFunction): [X] extends [Micro] ? Micro + : Micro +} = dual( + 2, + (self: Micro, f: any): Micro => + flatMap(self, (a) => { + const value = isMicro(f) ? f : typeof f === "function" ? f(a) : f + return isMicro(value) ? value : succeed(value) + }) +) + +/** + * Execute a side effect from the success value of the `Micro` effect. + * + * It is similar to the `andThen` api, but the success value is ignored. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ +export const tap: { + /** + * Execute a side effect from the success value of the `Micro` effect. + * + * It is similar to the `andThen` api, but the success value is ignored. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (f: (a: NoInfer) => X): ( + self: Micro + ) => [X] extends [Micro] ? Micro + : Micro + /** + * Execute a side effect from the success value of the `Micro` effect. + * + * It is similar to the `andThen` api, but the success value is ignored. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (f: NotFunction): ( + self: Micro + ) => [X] extends [Micro] ? Micro + : Micro + /** + * Execute a side effect from the success value of the `Micro` effect. + * + * It is similar to the `andThen` api, but the success value is ignored. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (self: Micro, f: (a: NoInfer) => X): [X] extends [Micro] ? Micro + : Micro + /** + * Execute a side effect from the success value of the `Micro` effect. + * + * It is similar to the `andThen` api, but the success value is ignored. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (self: Micro, f: NotFunction): [X] extends [Micro] ? Micro + : Micro +} = dual( + 2, + (self: Micro, f: (a: A) => Micro): Micro => + flatMap(self, (a) => { + const value = isMicro(f) ? f : typeof f === "function" ? f(a) : f + return isMicro(value) ? as(value, a) : succeed(a) + }) +) + +/** + * Replace the success value of the `Micro` effect with `void`. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ +export const asVoid = (self: Micro): Micro => flatMap(self, (_) => exitVoid) + +/** + * Access the `MicroExit` of the given `Micro` effect. + * + * @since 3.4.6 + * @experimental + * @category mapping & sequencing + */ +export const exit = (self: Micro): Micro, never, R> => + matchCause(self, { + onFailure: exitFailCause, + onSuccess: exitSucceed + }) + +/** + * Replace the error type of the given `Micro` with the full `MicroCause` object. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ +export const sandbox = (self: Micro): Micro, R> => catchAllCause(self, fail) + +/** + * Returns an effect that races all the specified effects, + * yielding the value of the first effect to succeed with a value. Losers of + * the race will be interrupted immediately + * + * @since 3.4.0 + * @experimental + * @category sequencing + */ +export const raceAll = >( + all: Iterable +): Micro, Micro.Error, Micro.Context> => + withMicroFiber((parent) => + async((resume) => { + const effects = Arr.fromIterable(all) + const len = effects.length + let doneCount = 0 + let done = false + const fibers = new Set>() + const causes: Array> = [] + const onExit = (exit: MicroExit) => { + doneCount++ + if (exit._tag === "Failure") { + causes.push(exit.cause) + if (doneCount >= len) { + resume(failCause(causes[0])) + } + return + } + done = true + resume(fibers.size === 0 ? exit : flatMap(uninterruptible(fiberInterruptAll(fibers)), () => exit)) + } + + for (let i = 0; i < len; i++) { + if (done) break + const fiber = unsafeFork(parent, interruptible(effects[i]), true, true) + fibers.add(fiber) + fiber.addObserver((exit) => { + fibers.delete(fiber) + onExit(exit) + }) + } + + return fiberInterruptAll(fibers) + }) + ) + +/** + * Returns an effect that races all the specified effects, + * yielding the value of the first effect to succeed or fail. Losers of + * the race will be interrupted immediately. + * + * @since 3.4.0 + * @experimental + * @category sequencing + */ +export const raceAllFirst = >( + all: Iterable +): Micro, Micro.Error, Micro.Context> => + withMicroFiber((parent) => + async((resume) => { + let done = false + const fibers = new Set>() + const onExit = (exit: MicroExit) => { + done = true + resume(fibers.size === 0 ? exit : flatMap(fiberInterruptAll(fibers), () => exit)) + } + + for (const effect of all) { + if (done) break + const fiber = unsafeFork(parent, interruptible(effect), true, true) + fibers.add(fiber) + fiber.addObserver((exit) => { + fibers.delete(fiber) + onExit(exit) + }) + } + + return fiberInterruptAll(fibers) + }) + ) + +/** + * Returns an effect that races two effects, yielding the value of the first + * effect to succeed. Losers of the race will be interrupted immediately. + * + * @since 3.4.0 + * @experimental + * @category sequencing + */ +export const race: { + /** + * Returns an effect that races two effects, yielding the value of the first + * effect to succeed. Losers of the race will be interrupted immediately. + * + * @since 3.4.0 + * @experimental + * @category sequencing + */ + (that: Micro): (self: Micro) => Micro + /** + * Returns an effect that races two effects, yielding the value of the first + * effect to succeed. Losers of the race will be interrupted immediately. + * + * @since 3.4.0 + * @experimental + * @category sequencing + */ + (self: Micro, that: Micro): Micro +} = dual( + 2, + (self: Micro, that: Micro): Micro => + raceAll([self, that]) +) + +/** + * Returns an effect that races two effects, yielding the value of the first + * effect to succeed *or* fail. Losers of the race will be interrupted immediately. + * + * @since 3.4.0 + * @experimental + * @category sequencing + */ +export const raceFirst: { + /** + * Returns an effect that races two effects, yielding the value of the first + * effect to succeed *or* fail. Losers of the race will be interrupted immediately. + * + * @since 3.4.0 + * @experimental + * @category sequencing + */ + (that: Micro): (self: Micro) => Micro + /** + * Returns an effect that races two effects, yielding the value of the first + * effect to succeed *or* fail. Losers of the race will be interrupted immediately. + * + * @since 3.4.0 + * @experimental + * @category sequencing + */ + (self: Micro, that: Micro): Micro +} = dual( + 2, + (self: Micro, that: Micro): Micro => + raceAllFirst([self, that]) +) + +/** + * Map the success value of this `Micro` effect to another `Micro` effect, then + * flatten the result. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ +export const flatMap: { + /** + * Map the success value of this `Micro` effect to another `Micro` effect, then + * flatten the result. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (f: (a: A) => Micro): (self: Micro) => Micro + /** + * Map the success value of this `Micro` effect to another `Micro` effect, then + * flatten the result. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (self: Micro, f: (a: A) => Micro): Micro +} = dual( + 2, + ( + self: Micro, + f: (a: A) => Micro + ): Micro => { + const onSuccess = Object.create(OnSuccessProto) + onSuccess[args] = self + onSuccess[successCont] = f + return onSuccess + } +) +const OnSuccessProto = makePrimitiveProto({ + op: "OnSuccess", + eval(this: any, fiber: MicroFiberImpl): Primitive { + fiber._stack.push(this) + return this[args] + } +}) + +// ---------------------------------------------------------------------------- +// mapping & sequencing +// ---------------------------------------------------------------------------- + +/** + * Flattens any nested `Micro` effects, merging the error and requirement types. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ +export const flatten = ( + self: Micro, E2, R2> +): Micro => flatMap(self, identity) + +/** + * Transforms the success value of the `Micro` effect with the specified + * function. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ +export const map: { + /** + * Transforms the success value of the `Micro` effect with the specified + * function. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (f: (a: A) => B): (self: Micro) => Micro + /** + * Transforms the success value of the `Micro` effect with the specified + * function. + * + * @since 3.4.0 + * @experimental + * @category mapping & sequencing + */ + (self: Micro, f: (a: A) => B): Micro +} = dual( + 2, + (self: Micro, f: (a: A) => B): Micro => flatMap(self, (a) => succeed(f(a))) +) + +// ---------------------------------------------------------------------------- +// MicroExit +// ---------------------------------------------------------------------------- + +/** + * The `MicroExit` type is used to represent the result of a `Micro` computation. It + * can either be successful, containing a value of type `A`, or it can fail, + * containing an error of type `E` wrapped in a `MicroCause`. + * + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export type MicroExit = + | MicroExit.Success + | MicroExit.Failure + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export declare namespace MicroExit { + /** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ + export interface Proto extends Micro { + readonly [MicroExitTypeId]: MicroExitTypeId + } + + /** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ + export interface Success extends Proto { + readonly _tag: "Success" + readonly value: A + } + + /** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ + export interface Failure extends Proto { + readonly _tag: "Failure" + readonly cause: MicroCause + } +} + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export const isMicroExit = (u: unknown): u is MicroExit => hasProperty(u, MicroExitTypeId) + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export const exitSucceed: (a: A) => MicroExit = succeed as any + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export const exitFailCause: (cause: MicroCause) => MicroExit = failCause as any + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export const exitInterrupt: MicroExit = exitFailCause(causeInterrupt()) + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export const exitFail = (e: E): MicroExit => exitFailCause(causeFail(e)) + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export const exitDie = (defect: unknown): MicroExit => exitFailCause(causeDie(defect)) + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export const exitIsSuccess = ( + self: MicroExit +): self is MicroExit.Success => self._tag === "Success" + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export const exitIsFailure = ( + self: MicroExit +): self is MicroExit.Failure => self._tag === "Failure" + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export const exitIsInterrupt = ( + self: MicroExit +): self is MicroExit.Failure & { + readonly cause: MicroCause.Interrupt +} => exitIsFailure(self) && self.cause._tag === "Interrupt" + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export const exitIsFail = ( + self: MicroExit +): self is MicroExit.Failure & { + readonly cause: MicroCause.Fail +} => exitIsFailure(self) && self.cause._tag === "Fail" + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export const exitIsDie = ( + self: MicroExit +): self is MicroExit.Failure & { + readonly cause: MicroCause.Die +} => exitIsFailure(self) && self.cause._tag === "Die" + +/** + * @since 3.4.6 + * @experimental + * @category MicroExit + */ +export const exitVoid: MicroExit = exitSucceed(void 0) + +/** + * @since 3.11.0 + * @experimental + * @category MicroExit + */ +export const exitVoidAll = >>( + exits: I +): MicroExit> ? _E : never> => { + for (const exit of exits) { + if (exit._tag === "Failure") { + return exit + } + } + return exitVoid +} + +// ---------------------------------------------------------------------------- +// scheduler +// ---------------------------------------------------------------------------- + +/** + * @since 3.5.9 + * @experimental + * @category scheduler + */ +export interface MicroScheduler { + readonly scheduleTask: (task: () => void, priority: number) => void + readonly shouldYield: (fiber: MicroFiber) => boolean + readonly flush: () => void +} + +const setImmediate = "setImmediate" in globalThis + ? globalThis.setImmediate + : (f: () => void) => setTimeout(f, 0) + +/** + * @since 3.5.9 + * @experimental + * @category scheduler + */ +export class MicroSchedulerDefault implements MicroScheduler { + private tasks: Array<() => void> = [] + private running = false + + /** + * @since 3.5.9 + */ + scheduleTask(task: () => void, _priority: number) { + this.tasks.push(task) + if (!this.running) { + this.running = true + setImmediate(this.afterScheduled) + } + } + + /** + * @since 3.5.9 + */ + afterScheduled = () => { + this.running = false + this.runTasks() + } + + /** + * @since 3.5.9 + */ + runTasks() { + const tasks = this.tasks + this.tasks = [] + for (let i = 0, len = tasks.length; i < len; i++) { + tasks[i]() + } + } + + /** + * @since 3.5.9 + */ + shouldYield(fiber: MicroFiber) { + return fiber.currentOpCount >= fiber.getRef(MaxOpsBeforeYield) + } + + /** + * @since 3.5.9 + */ + flush() { + while (this.tasks.length > 0) { + this.runTasks() + } + } +} + +/** + * Access the given `Context.Tag` from the environment. + * + * @since 3.4.0 + * @experimental + * @category environment + */ +export const service: { + /** + * Access the given `Context.Tag` from the environment. + * + * @since 3.4.0 + * @experimental + * @category environment + */ + (tag: Context.Reference): Micro + /** + * Access the given `Context.Tag` from the environment. + * + * @since 3.4.0 + * @experimental + * @category environment + */ + (tag: Context.Tag): Micro +} = + ((tag: Context.Tag): Micro => + withMicroFiber((fiber) => succeed(Context.unsafeGet(fiber.context, tag)))) as any + +/** + * Access the given `Context.Tag` from the environment, without tracking the + * dependency at the type level. + * + * It will return an `Option` of the service, depending on whether it is + * available in the environment or not. + * + * @since 3.4.0 + * @experimental + * @category environment + */ +export const serviceOption = ( + tag: Context.Tag +): Micro> => withMicroFiber((fiber) => succeed(Context.getOption(fiber.context, tag))) + +/** + * Update the Context with the given mapping function. + * + * @since 3.11.0 + * @experimental + * @category environment + */ +export const updateContext: { + /** + * Update the Context with the given mapping function. + * + * @since 3.11.0 + * @experimental + * @category environment + */ + (f: (context: Context.Context) => Context.Context>): (self: Micro) => Micro + /** + * Update the Context with the given mapping function. + * + * @since 3.11.0 + * @experimental + * @category environment + */ + ( + self: Micro, + f: (context: Context.Context) => Context.Context> + ): Micro +} = dual( + 2, + ( + self: Micro, + f: (context: Context.Context) => Context.Context> + ): Micro => + withMicroFiber((fiber) => { + const prev = fiber.context as Context.Context + fiber.context = f(prev) + return onExit( + self as any, + () => { + fiber.context = prev + return void_ + } + ) + }) +) + +/** + * Update the service for the given `Context.Tag` in the environment. + * + * @since 3.11.0 + * @experimental + * @category environment + */ +export const updateService: { + /** + * Update the service for the given `Context.Tag` in the environment. + * + * @since 3.11.0 + * @experimental + * @category environment + */ + (tag: Context.Reference, f: (value: A) => A): (self: Micro) => Micro + /** + * Update the service for the given `Context.Tag` in the environment. + * + * @since 3.11.0 + * @experimental + * @category environment + */ + (tag: Context.Tag, f: (value: A) => A): (self: Micro) => Micro + /** + * Update the service for the given `Context.Tag` in the environment. + * + * @since 3.11.0 + * @experimental + * @category environment + */ + (self: Micro, tag: Context.Reference, f: (value: A) => A): Micro + /** + * Update the service for the given `Context.Tag` in the environment. + * + * @since 3.11.0 + * @experimental + * @category environment + */ + (self: Micro, tag: Context.Tag, f: (value: A) => A): Micro +} = dual( + 3, + ( + self: Micro, + tag: Context.Reference, + f: (value: A) => A + ): Micro => + withMicroFiber((fiber) => { + const prev = Context.unsafeGet(fiber.context, tag) + fiber.context = Context.add(fiber.context, tag, f(prev)) + return onExit( + self, + () => { + fiber.context = Context.add(fiber.context, tag, prev) + return void_ + } + ) + }) +) + +/** + * Access the current `Context` from the environment. + * + * @since 3.4.0 + * @experimental + * @category environment + */ +export const context = (): Micro> => getContext as any +const getContext = withMicroFiber((fiber) => succeed(fiber.context)) + +/** + * Merge the given `Context` with the current context. + * + * @since 3.4.0 + * @experimental + * @category environment + */ +export const provideContext: { + /** + * Merge the given `Context` with the current context. + * + * @since 3.4.0 + * @experimental + * @category environment + */ + (context: Context.Context): (self: Micro) => Micro> + /** + * Merge the given `Context` with the current context. + * + * @since 3.4.0 + * @experimental + * @category environment + */ + (self: Micro, context: Context.Context): Micro> +} = dual( + 2, + ( + self: Micro, + provided: Context.Context + ): Micro> => updateContext(self, Context.merge(provided)) as any +) + +/** + * Add the provided service to the current context. + * + * @since 3.4.0 + * @experimental + * @category environment + */ +export const provideService: { + /** + * Add the provided service to the current context. + * + * @since 3.4.0 + * @experimental + * @category environment + */ + (tag: Context.Tag, service: S): (self: Micro) => Micro> + /** + * Add the provided service to the current context. + * + * @since 3.4.0 + * @experimental + * @category environment + */ + (self: Micro, tag: Context.Tag, service: S): Micro> +} = dual( + 3, + ( + self: Micro, + tag: Context.Tag, + service: S + ): Micro> => updateContext(self, Context.add(tag, service)) as any +) + +/** + * Create a service using the provided `Micro` effect, and add it to the + * current context. + * + * @since 3.4.6 + * @experimental + * @category environment + */ +export const provideServiceEffect: { + /** + * Create a service using the provided `Micro` effect, and add it to the + * current context. + * + * @since 3.4.6 + * @experimental + * @category environment + */ + (tag: Context.Tag, acquire: Micro): (self: Micro) => Micro | R2> + /** + * Create a service using the provided `Micro` effect, and add it to the + * current context. + * + * @since 3.4.6 + * @experimental + * @category environment + */ + (self: Micro, tag: Context.Tag, acquire: Micro): Micro | R2> +} = dual( + 3, + ( + self: Micro, + tag: Context.Tag, + acquire: Micro + ): Micro | R2> => flatMap(acquire, (service) => provideService(self, tag, service)) +) + +// ======================================================================== +// References +// ======================================================================== + +/** + * @since 3.11.0 + * @experimental + * @category references + */ +export class MaxOpsBeforeYield extends Context.Reference()< + "effect/Micro/currentMaxOpsBeforeYield", + number +>( + "effect/Micro/currentMaxOpsBeforeYield", + { defaultValue: () => 2048 } +) {} + +/** + * @since 3.11.0 + * @experimental + * @category environment refs + */ +export class CurrentConcurrency extends Context.Reference()< + "effect/Micro/currentConcurrency", + "unbounded" | number +>( + "effect/Micro/currentConcurrency", + { defaultValue: () => "unbounded" } +) {} + +/** + * @since 3.11.0 + * @experimental + * @category environment refs + */ +export class CurrentScheduler extends Context.Reference()< + "effect/Micro/currentScheduler", + MicroScheduler +>( + "effect/Micro/currentScheduler", + { defaultValue: () => new MicroSchedulerDefault() } +) {} + +/** + * If you have a `Micro` that uses `concurrency: "inherit"`, you can use this + * api to control the concurrency of that `Micro` when it is run. + * + * @example + * ```ts + * import * as Micro from "effect/Micro" + * + * Micro.forEach([1, 2, 3], (n) => Micro.succeed(n), { + * concurrency: "inherit" + * }).pipe( + * Micro.withConcurrency(2) // use a concurrency of 2 + * ) + * ``` + * + * @since 3.4.0 + * @experimental + * @category environment refs + */ +export const withConcurrency: { + /** + * If you have a `Micro` that uses `concurrency: "inherit"`, you can use this + * api to control the concurrency of that `Micro` when it is run. + * + * @example + * ```ts + * import * as Micro from "effect/Micro" + * + * Micro.forEach([1, 2, 3], (n) => Micro.succeed(n), { + * concurrency: "inherit" + * }).pipe( + * Micro.withConcurrency(2) // use a concurrency of 2 + * ) + * ``` + * + * @since 3.4.0 + * @experimental + * @category environment refs + */ + (concurrency: "unbounded" | number): (self: Micro) => Micro + /** + * If you have a `Micro` that uses `concurrency: "inherit"`, you can use this + * api to control the concurrency of that `Micro` when it is run. + * + * @example + * ```ts + * import * as Micro from "effect/Micro" + * + * Micro.forEach([1, 2, 3], (n) => Micro.succeed(n), { + * concurrency: "inherit" + * }).pipe( + * Micro.withConcurrency(2) // use a concurrency of 2 + * ) + * ``` + * + * @since 3.4.0 + * @experimental + * @category environment refs + */ + (self: Micro, concurrency: "unbounded" | number): Micro +} = dual( + 2, + ( + self: Micro, + concurrency: "unbounded" | number + ): Micro => provideService(self, CurrentConcurrency, concurrency) +) + +// ---------------------------------------------------------------------------- +// zipping +// ---------------------------------------------------------------------------- + +/** + * Combine two `Micro` effects into a single effect that produces a tuple of + * their results. + * + * @since 3.4.0 + * @experimental + * @category zipping + */ +export const zip: { + // ---------------------------------------------------------------------------- + // zipping + // ---------------------------------------------------------------------------- + + /** + * Combine two `Micro` effects into a single effect that produces a tuple of + * their results. + * + * @since 3.4.0 + * @experimental + * @category zipping + */ + ( + that: Micro, + options?: + | { readonly concurrent?: boolean | undefined } + | undefined + ): (self: Micro) => Micro<[A, A2], E2 | E, R2 | R> + // ---------------------------------------------------------------------------- + // zipping + // ---------------------------------------------------------------------------- + + /** + * Combine two `Micro` effects into a single effect that produces a tuple of + * their results. + * + * @since 3.4.0 + * @experimental + * @category zipping + */ + ( + self: Micro, + that: Micro, + options?: { readonly concurrent?: boolean | undefined } + ): Micro<[A, A2], E | E2, R | R2> +} = dual((args) => isMicro(args[1]), ( + self: Micro, + that: Micro, + options?: { readonly concurrent?: boolean | undefined } +): Micro<[A, A2], E | E2, R | R2> => zipWith(self, that, (a, a2) => [a, a2], options)) + +/** + * The `Micro.zipWith` function combines two `Micro` effects and allows you to + * apply a function to the results of the combined effects, transforming them + * into a single value. + * + * @since 3.4.3 + * @experimental + * @category zipping + */ +export const zipWith: { + /** + * The `Micro.zipWith` function combines two `Micro` effects and allows you to + * apply a function to the results of the combined effects, transforming them + * into a single value. + * + * @since 3.4.3 + * @experimental + * @category zipping + */ + ( + that: Micro, + f: (a: A, b: A2) => B, + options?: { readonly concurrent?: boolean | undefined } + ): (self: Micro) => Micro + /** + * The `Micro.zipWith` function combines two `Micro` effects and allows you to + * apply a function to the results of the combined effects, transforming them + * into a single value. + * + * @since 3.4.3 + * @experimental + * @category zipping + */ + ( + self: Micro, + that: Micro, + f: (a: A, b: A2) => B, + options?: { readonly concurrent?: boolean | undefined } + ): Micro +} = dual((args) => isMicro(args[1]), ( + self: Micro, + that: Micro, + f: (a: A, b: A2) => B, + options?: { readonly concurrent?: boolean | undefined } +): Micro => + options?.concurrent + // Use `all` exclusively for concurrent cases, as it introduces additional overhead due to the management of concurrency + ? map(all([self, that], { concurrency: 2 }), ([a, a2]) => f(a, a2)) + : flatMap(self, (a) => map(that, (a2) => f(a, a2)))) + +// ---------------------------------------------------------------------------- +// filtering & conditionals +// ---------------------------------------------------------------------------- + +/** + * Filter the specified effect with the provided function, failing with specified + * `MicroCause` if the predicate fails. + * + * In addition to the filtering capabilities discussed earlier, you have the option to further + * refine and narrow down the type of the success channel by providing a + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ +export const filterOrFailCause: { + // ---------------------------------------------------------------------------- + // filtering & conditionals + // ---------------------------------------------------------------------------- + + /** + * Filter the specified effect with the provided function, failing with specified + * `MicroCause` if the predicate fails. + * + * In addition to the filtering capabilities discussed earlier, you have the option to further + * refine and narrow down the type of the success channel by providing a + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ + ( + refinement: Refinement, + orFailWith: (a: NoInfer) => MicroCause + ): (self: Micro) => Micro + // ---------------------------------------------------------------------------- + // filtering & conditionals + // ---------------------------------------------------------------------------- + + /** + * Filter the specified effect with the provided function, failing with specified + * `MicroCause` if the predicate fails. + * + * In addition to the filtering capabilities discussed earlier, you have the option to further + * refine and narrow down the type of the success channel by providing a + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ + ( + predicate: Predicate>, + orFailWith: (a: NoInfer) => MicroCause + ): (self: Micro) => Micro + // ---------------------------------------------------------------------------- + // filtering & conditionals + // ---------------------------------------------------------------------------- + + /** + * Filter the specified effect with the provided function, failing with specified + * `MicroCause` if the predicate fails. + * + * In addition to the filtering capabilities discussed earlier, you have the option to further + * refine and narrow down the type of the success channel by providing a + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ + ( + self: Micro, + refinement: Refinement, + orFailWith: (a: A) => MicroCause + ): Micro + // ---------------------------------------------------------------------------- + // filtering & conditionals + // ---------------------------------------------------------------------------- + + /** + * Filter the specified effect with the provided function, failing with specified + * `MicroCause` if the predicate fails. + * + * In addition to the filtering capabilities discussed earlier, you have the option to further + * refine and narrow down the type of the success channel by providing a + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ + ( + self: Micro, + predicate: Predicate, + orFailWith: (a: A) => MicroCause + ): Micro +} = dual((args) => isMicro(args[0]), ( + self: Micro, + refinement: Refinement, + orFailWith: (a: A) => MicroCause +): Micro => flatMap(self, (a) => refinement(a) ? succeed(a) : failCause(orFailWith(a)))) + +/** + * Filter the specified effect with the provided function, failing with specified + * error if the predicate fails. + * + * In addition to the filtering capabilities discussed earlier, you have the option to further + * refine and narrow down the type of the success channel by providing a + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ +export const filterOrFail: { + /** + * Filter the specified effect with the provided function, failing with specified + * error if the predicate fails. + * + * In addition to the filtering capabilities discussed earlier, you have the option to further + * refine and narrow down the type of the success channel by providing a + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ + (refinement: Refinement, orFailWith: (a: NoInfer) => E2): (self: Micro) => Micro + /** + * Filter the specified effect with the provided function, failing with specified + * error if the predicate fails. + * + * In addition to the filtering capabilities discussed earlier, you have the option to further + * refine and narrow down the type of the success channel by providing a + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ + (predicate: Predicate>, orFailWith: (a: NoInfer) => E2): (self: Micro) => Micro + /** + * Filter the specified effect with the provided function, failing with specified + * error if the predicate fails. + * + * In addition to the filtering capabilities discussed earlier, you have the option to further + * refine and narrow down the type of the success channel by providing a + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ + ( + self: Micro, + refinement: Refinement, + orFailWith: (a: A) => E2 + ): Micro + /** + * Filter the specified effect with the provided function, failing with specified + * error if the predicate fails. + * + * In addition to the filtering capabilities discussed earlier, you have the option to further + * refine and narrow down the type of the success channel by providing a + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ + (self: Micro, predicate: Predicate, orFailWith: (a: A) => E2): Micro +} = dual((args) => isMicro(args[0]), ( + self: Micro, + refinement: Refinement, + orFailWith: (a: A) => E2 +): Micro => flatMap(self, (a) => refinement(a) ? succeed(a) : fail(orFailWith(a)))) + +/** + * The moral equivalent of `if (p) exp`. + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ +export const when: { + /** + * The moral equivalent of `if (p) exp`. + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ + (condition: LazyArg | Micro): (self: Micro) => Micro, E | E2, R | R2> + /** + * The moral equivalent of `if (p) exp`. + * + * @since 3.4.0 + * @experimental + * @category filtering & conditionals + */ + (self: Micro, condition: LazyArg | Micro): Micro, E | E2, R | R2> +} = dual( + 2, + ( + self: Micro, + condition: LazyArg | Micro + ): Micro, E | E2, R | R2> => + flatMap(isMicro(condition) ? condition : sync(condition), (pass) => pass ? asSome(self) : succeedNone) +) + +// ---------------------------------------------------------------------------- +// repetition +// ---------------------------------------------------------------------------- + +/** + * Repeat the given `Micro` using the provided options. + * + * The `while` predicate will be checked after each iteration, and can use the + * fall `MicroExit` of the effect to determine if the repetition should continue. + * + * @since 3.4.6 + * @experimental + * @category repetition + */ +export const repeatExit: { + // ---------------------------------------------------------------------------- + // repetition + // ---------------------------------------------------------------------------- + + /** + * Repeat the given `Micro` using the provided options. + * + * The `while` predicate will be checked after each iteration, and can use the + * fall `MicroExit` of the effect to determine if the repetition should continue. + * + * @since 3.4.6 + * @experimental + * @category repetition + */ + ( + options: { + while: Predicate> + times?: number | undefined + schedule?: MicroSchedule | undefined + } + ): (self: Micro) => Micro + // ---------------------------------------------------------------------------- + // repetition + // ---------------------------------------------------------------------------- + + /** + * Repeat the given `Micro` using the provided options. + * + * The `while` predicate will be checked after each iteration, and can use the + * fall `MicroExit` of the effect to determine if the repetition should continue. + * + * @since 3.4.6 + * @experimental + * @category repetition + */ + ( + self: Micro, + options: { + while: Predicate> + times?: number | undefined + schedule?: MicroSchedule | undefined + } + ): Micro +} = dual(2, (self: Micro, options: { + while: Predicate> + times?: number | undefined + schedule?: MicroSchedule | undefined +}): Micro => + suspend(() => { + const startedAt = options.schedule ? Date.now() : 0 + let attempt = 0 + + const loop: Micro = flatMap(exit(self), (exit) => { + if (options.while !== undefined && !options.while(exit)) { + return exit + } else if (options.times !== undefined && attempt >= options.times) { + return exit + } + attempt++ + let delayEffect = yieldNow + if (options.schedule !== undefined) { + const elapsed = Date.now() - startedAt + const duration = options.schedule(attempt, elapsed) + if (Option.isNone(duration)) { + return exit + } + delayEffect = sleep(duration.value) + } + return flatMap(delayEffect, () => loop) + }) + + return loop + })) + +/** + * Repeat the given `Micro` effect using the provided options. Only successful + * results will be repeated. + * + * @since 3.4.0 + * @experimental + * @category repetition + */ +export const repeat: { + /** + * Repeat the given `Micro` effect using the provided options. Only successful + * results will be repeated. + * + * @since 3.4.0 + * @experimental + * @category repetition + */ + ( + options?: { + while?: Predicate | undefined + times?: number | undefined + schedule?: MicroSchedule | undefined + } | undefined + ): (self: Micro) => Micro + /** + * Repeat the given `Micro` effect using the provided options. Only successful + * results will be repeated. + * + * @since 3.4.0 + * @experimental + * @category repetition + */ + ( + self: Micro, + options?: { + while?: Predicate | undefined + times?: number | undefined + schedule?: MicroSchedule | undefined + } | undefined + ): Micro +} = dual((args) => isMicro(args[0]), ( + self: Micro, + options?: { + while?: Predicate | undefined + times?: number | undefined + schedule?: MicroSchedule | undefined + } | undefined +): Micro => + repeatExit(self, { + ...options, + while: (exit) => exit._tag === "Success" && (options?.while === undefined || options.while(exit.value)) + })) + +/** + * Replicates the given effect `n` times. + * + * @since 3.11.0 + * @experimental + * @category repetition + */ +export const replicate: { + /** + * Replicates the given effect `n` times. + * + * @since 3.11.0 + * @experimental + * @category repetition + */ + (n: number): (self: Micro) => Array> + /** + * Replicates the given effect `n` times. + * + * @since 3.11.0 + * @experimental + * @category repetition + */ + (self: Micro, n: number): Array> +} = dual( + 2, + (self: Micro, n: number): Array> => Array.from({ length: n }, () => self) +) + +/** + * Performs this effect the specified number of times and collects the + * results. + * + * @since 3.11.0 + * @category repetition + */ +export const replicateEffect: { + /** + * Performs this effect the specified number of times and collects the + * results. + * + * @since 3.11.0 + * @category repetition + */ + ( + n: number, + options?: { + readonly concurrency?: Concurrency | undefined + readonly discard?: false | undefined + } + ): (self: Micro) => Micro, E, R> + /** + * Performs this effect the specified number of times and collects the + * results. + * + * @since 3.11.0 + * @category repetition + */ + ( + n: number, + options: { + readonly concurrency?: Concurrency | undefined + readonly discard: true + } + ): (self: Micro) => Micro + /** + * Performs this effect the specified number of times and collects the + * results. + * + * @since 3.11.0 + * @category repetition + */ + ( + self: Micro, + n: number, + options?: { + readonly concurrency?: Concurrency | undefined + readonly discard?: false | undefined + } + ): Micro, E, R> + /** + * Performs this effect the specified number of times and collects the + * results. + * + * @since 3.11.0 + * @category repetition + */ + ( + self: Micro, + n: number, + options: { + readonly concurrency?: Concurrency | undefined + readonly discard: true + } + ): Micro +} = dual( + (args) => isMicro(args[0]), + ( + self: Micro, + n: number, + options: { + readonly concurrency?: Concurrency | undefined + readonly discard: true + } + ): Micro => all(replicate(self, n), options) +) + +/** + * Repeat the given `Micro` effect forever, only stopping if the effect fails. + * + * @since 3.4.0 + * @experimental + * @category repetition + */ +export const forever = (self: Micro): Micro => repeat(self) as any + +// ---------------------------------------------------------------------------- +// scheduling +// ---------------------------------------------------------------------------- + +/** + * The `MicroSchedule` type represents a function that can be used to calculate + * the delay between repeats. + * + * The function takes the current attempt number and the elapsed time since the + * first attempt, and returns the delay for the next attempt. If the function + * returns `None`, the repetition will stop. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ +export type MicroSchedule = (attempt: number, elapsed: number) => Option.Option + +/** + * Create a `MicroSchedule` that will stop repeating after the specified number + * of attempts. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ +export const scheduleRecurs = (n: number): MicroSchedule => (attempt) => attempt <= n ? Option.some(0) : Option.none() + +/** + * Create a `MicroSchedule` that will generate a constant delay. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ +export const scheduleSpaced = (millis: number): MicroSchedule => () => Option.some(millis) + +/** + * Create a `MicroSchedule` that will generate a delay with an exponential backoff. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ +export const scheduleExponential = (baseMillis: number, factor = 2): MicroSchedule => (attempt) => + Option.some(Math.pow(factor, attempt) * baseMillis) + +/** + * Returns a new `MicroSchedule` with an added calculated delay to each delay + * returned by this schedule. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ +export const scheduleAddDelay: { + /** + * Returns a new `MicroSchedule` with an added calculated delay to each delay + * returned by this schedule. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ + (f: () => number): (self: MicroSchedule) => MicroSchedule + /** + * Returns a new `MicroSchedule` with an added calculated delay to each delay + * returned by this schedule. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ + (self: MicroSchedule, f: () => number): MicroSchedule +} = dual( + 2, + (self: MicroSchedule, f: () => number): MicroSchedule => (attempt, elapsed) => + Option.map(self(attempt, elapsed), (duration) => duration + f()) +) + +/** + * Transform a `MicroSchedule` to one that will have a delay that will never exceed + * the specified maximum. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ +export const scheduleWithMaxDelay: { + /** + * Transform a `MicroSchedule` to one that will have a delay that will never exceed + * the specified maximum. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ + (max: number): (self: MicroSchedule) => MicroSchedule + /** + * Transform a `MicroSchedule` to one that will have a delay that will never exceed + * the specified maximum. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ + (self: MicroSchedule, max: number): MicroSchedule +} = dual( + 2, + (self: MicroSchedule, max: number): MicroSchedule => (attempt, elapsed) => + Option.map(self(attempt, elapsed), (duration) => Math.min(duration, max)) +) + +/** + * Transform a `MicroSchedule` to one that will stop repeating after the specified + * amount of time. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ +export const scheduleWithMaxElapsed: { + /** + * Transform a `MicroSchedule` to one that will stop repeating after the specified + * amount of time. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ + (max: number): (self: MicroSchedule) => MicroSchedule + /** + * Transform a `MicroSchedule` to one that will stop repeating after the specified + * amount of time. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ + (self: MicroSchedule, max: number): MicroSchedule +} = dual( + 2, + (self: MicroSchedule, max: number): MicroSchedule => (attempt, elapsed) => + elapsed < max ? self(attempt, elapsed) : Option.none() +) + +/** + * Combines two `MicroSchedule`s, by recurring if either schedule wants to + * recur, using the minimum of the two durations between recurrences. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ +export const scheduleUnion: { + /** + * Combines two `MicroSchedule`s, by recurring if either schedule wants to + * recur, using the minimum of the two durations between recurrences. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ + (that: MicroSchedule): (self: MicroSchedule) => MicroSchedule + /** + * Combines two `MicroSchedule`s, by recurring if either schedule wants to + * recur, using the minimum of the two durations between recurrences. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ + (self: MicroSchedule, that: MicroSchedule): MicroSchedule +} = dual( + 2, + (self: MicroSchedule, that: MicroSchedule): MicroSchedule => (attempt, elapsed) => + Option.zipWith(self(attempt, elapsed), that(attempt, elapsed), (d1, d2) => Math.min(d1, d2)) +) + +/** + * Combines two `MicroSchedule`s, by recurring only if both schedules want to + * recur, using the maximum of the two durations between recurrences. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ +export const scheduleIntersect: { + /** + * Combines two `MicroSchedule`s, by recurring only if both schedules want to + * recur, using the maximum of the two durations between recurrences. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ + (that: MicroSchedule): (self: MicroSchedule) => MicroSchedule + /** + * Combines two `MicroSchedule`s, by recurring only if both schedules want to + * recur, using the maximum of the two durations between recurrences. + * + * @since 3.4.6 + * @experimental + * @category scheduling + */ + (self: MicroSchedule, that: MicroSchedule): MicroSchedule +} = dual( + 2, + (self: MicroSchedule, that: MicroSchedule): MicroSchedule => (attempt, elapsed) => + Option.zipWith(self(attempt, elapsed), that(attempt, elapsed), (d1, d2) => Math.max(d1, d2)) +) + +// ---------------------------------------------------------------------------- +// error handling +// ---------------------------------------------------------------------------- + +/** + * Catch the full `MicroCause` object of the given `Micro` effect, allowing you to + * recover from any kind of cause. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ +export const catchAllCause: { + // ---------------------------------------------------------------------------- + // error handling + // ---------------------------------------------------------------------------- + + /** + * Catch the full `MicroCause` object of the given `Micro` effect, allowing you to + * recover from any kind of cause. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + (f: (cause: NoInfer>) => Micro): (self: Micro) => Micro + // ---------------------------------------------------------------------------- + // error handling + // ---------------------------------------------------------------------------- + + /** + * Catch the full `MicroCause` object of the given `Micro` effect, allowing you to + * recover from any kind of cause. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + ( + self: Micro, + f: (cause: NoInfer>) => Micro + ): Micro +} = dual( + 2, + ( + self: Micro, + f: (cause: NoInfer>) => Micro + ): Micro => { + const onFailure = Object.create(OnFailureProto) + onFailure[args] = self + onFailure[failureCont] = f + return onFailure + } +) +const OnFailureProto = makePrimitiveProto({ + op: "OnFailure", + eval(this: any, fiber: MicroFiberImpl): Primitive { + fiber._stack.push(this as any) + return this[args] + } +}) + +/** + * Selectively catch a `MicroCause` object of the given `Micro` effect, + * using the provided predicate to determine if the failure should be caught. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ +export const catchCauseIf: { + /** + * Selectively catch a `MicroCause` object of the given `Micro` effect, + * using the provided predicate to determine if the failure should be caught. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + >( + refinement: Refinement, EB>, + f: (cause: EB) => Micro + ): ( + self: Micro + ) => Micro> | E2, R | R2> + /** + * Selectively catch a `MicroCause` object of the given `Micro` effect, + * using the provided predicate to determine if the failure should be caught. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + ( + predicate: Predicate>>, + f: (cause: NoInfer>) => Micro + ): (self: Micro) => Micro + /** + * Selectively catch a `MicroCause` object of the given `Micro` effect, + * using the provided predicate to determine if the failure should be caught. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + >( + self: Micro, + refinement: Refinement, EB>, + f: (cause: EB) => Micro + ): Micro> | E2, R | R2> + /** + * Selectively catch a `MicroCause` object of the given `Micro` effect, + * using the provided predicate to determine if the failure should be caught. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + ( + self: Micro, + predicate: Predicate>>, + f: (cause: NoInfer>) => Micro + ): Micro +} = dual( + 3, + ( + self: Micro, + predicate: Predicate>, + f: (cause: MicroCause) => Micro + ): Micro => + catchAllCause(self, (cause) => predicate(cause) ? f(cause) : failCause(cause) as any) +) + +/** + * Catch the error of the given `Micro` effect, allowing you to recover from it. + * + * It only catches expected errors. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ +export const catchAll: { + /** + * Catch the error of the given `Micro` effect, allowing you to recover from it. + * + * It only catches expected errors. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + (f: (e: NoInfer) => Micro): (self: Micro) => Micro + /** + * Catch the error of the given `Micro` effect, allowing you to recover from it. + * + * It only catches expected errors. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + (self: Micro, f: (e: NoInfer) => Micro): Micro +} = dual( + 2, + ( + self: Micro, + f: (a: NoInfer) => Micro + ): Micro => catchCauseIf(self, causeIsFail, (cause) => f(cause.error)) +) + +/** + * Catch any unexpected errors of the given `Micro` effect, allowing you to recover from them. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ +export const catchAllDefect: { + /** + * Catch any unexpected errors of the given `Micro` effect, allowing you to recover from them. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + (f: (defect: unknown) => Micro): (self: Micro) => Micro + /** + * Catch any unexpected errors of the given `Micro` effect, allowing you to recover from them. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + (self: Micro, f: (defect: unknown) => Micro): Micro +} = dual( + 2, + (self: Micro, f: (defect: unknown) => Micro): Micro => + catchCauseIf(self, causeIsDie, (die) => f(die.defect)) +) + +/** + * Perform a side effect using the full `MicroCause` object of the given `Micro`. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ +export const tapErrorCause: { + /** + * Perform a side effect using the full `MicroCause` object of the given `Micro`. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + (f: (cause: NoInfer>) => Micro): (self: Micro) => Micro + /** + * Perform a side effect using the full `MicroCause` object of the given `Micro`. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + ( + self: Micro, + f: (cause: NoInfer>) => Micro + ): Micro +} = dual( + 2, + ( + self: Micro, + f: (cause: NoInfer>) => Micro + ): Micro => tapErrorCauseIf(self, constTrue, f) +) + +/** + * Perform a side effect using if a `MicroCause` object matches the specified + * predicate. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ +export const tapErrorCauseIf: { + /** + * Perform a side effect using if a `MicroCause` object matches the specified + * predicate. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + >(refinement: Refinement, EB>, f: (a: EB) => Micro): (self: Micro) => Micro + /** + * Perform a side effect using if a `MicroCause` object matches the specified + * predicate. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + ( + predicate: (cause: NoInfer>) => boolean, + f: (a: NoInfer>) => Micro + ): (self: Micro) => Micro + /** + * Perform a side effect using if a `MicroCause` object matches the specified + * predicate. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + >( + self: Micro, + refinement: Refinement, EB>, + f: (a: EB) => Micro + ): Micro + /** + * Perform a side effect using if a `MicroCause` object matches the specified + * predicate. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + ( + self: Micro, + predicate: (cause: NoInfer>) => boolean, + f: (a: NoInfer>) => Micro + ): Micro +} = dual( + 3, + >( + self: Micro, + refinement: Refinement, EB>, + f: (a: EB) => Micro + ): Micro => catchCauseIf(self, refinement, (cause) => andThen(f(cause), failCause(cause))) +) + +/** + * Perform a side effect from expected errors of the given `Micro`. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ +export const tapError: { + /** + * Perform a side effect from expected errors of the given `Micro`. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + (f: (e: NoInfer) => Micro): (self: Micro) => Micro + /** + * Perform a side effect from expected errors of the given `Micro`. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + (self: Micro, f: (e: NoInfer) => Micro): Micro +} = dual( + 2, + (self: Micro, f: (e: NoInfer) => Micro): Micro => + tapErrorCauseIf(self, causeIsFail, (fail) => f(fail.error)) +) + +/** + * Perform a side effect from unexpected errors of the given `Micro`. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ +export const tapDefect: { + /** + * Perform a side effect from unexpected errors of the given `Micro`. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + (f: (defect: unknown) => Micro): (self: Micro) => Micro + /** + * Perform a side effect from unexpected errors of the given `Micro`. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + (self: Micro, f: (defect: unknown) => Micro): Micro +} = dual( + 2, + (self: Micro, f: (defect: unknown) => Micro): Micro => + tapErrorCauseIf(self, causeIsDie, (die) => f(die.defect)) +) + +/** + * Catch any expected errors that match the specified predicate. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ +export const catchIf: { + /** + * Catch any expected errors that match the specified predicate. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + (refinement: Refinement, EB>, f: (e: EB) => Micro): (self: Micro) => Micro, R2 | R> + /** + * Catch any expected errors that match the specified predicate. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + (predicate: Predicate>, f: (e: NoInfer) => Micro): (self: Micro) => Micro + /** + * Catch any expected errors that match the specified predicate. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + ( + self: Micro, + refinement: Refinement, + f: (e: EB) => Micro + ): Micro, R | R2> + /** + * Catch any expected errors that match the specified predicate. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + ( + self: Micro, + predicate: Predicate, + f: (e: E) => Micro + ): Micro +} = dual( + 3, + ( + self: Micro, + predicate: Predicate, + f: (e: E) => Micro + ): Micro => + catchCauseIf( + self, + (f): f is MicroCause.Fail => causeIsFail(f) && predicate(f.error), + (fail) => f(fail.error) + ) +) + +/** + * Recovers from the specified tagged error. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ +export const catchTag: { + /** + * Recovers from the specified tagged error. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + (k: K, f: (e: Extract) => Micro): (self: Micro) => Micro, R1 | R> + /** + * Recovers from the specified tagged error. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + ( + self: Micro, + k: K, + f: (e: Extract) => Micro + ): Micro, R | R1> +} = dual(3, ( + self: Micro, + k: K, + f: (e: Extract) => Micro +): Micro, R | R1> => + catchIf(self, isTagged(k) as Refinement>, f) as any) + +/** + * Transform the full `MicroCause` object of the given `Micro` effect. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ +export const mapErrorCause: { + /** + * Transform the full `MicroCause` object of the given `Micro` effect. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + (f: (e: MicroCause) => MicroCause): (self: Micro) => Micro + /** + * Transform the full `MicroCause` object of the given `Micro` effect. + * + * @since 3.4.6 + * @experimental + * @category error handling + */ + (self: Micro, f: (e: MicroCause) => MicroCause): Micro +} = dual( + 2, + (self: Micro, f: (e: MicroCause) => MicroCause): Micro => + catchAllCause(self, (cause) => failCause(f(cause))) +) + +/** + * Transform any expected errors of the given `Micro` effect. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ +export const mapError: { + /** + * Transform any expected errors of the given `Micro` effect. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + (f: (e: E) => E2): (self: Micro) => Micro + /** + * Transform any expected errors of the given `Micro` effect. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + (self: Micro, f: (e: E) => E2): Micro +} = dual( + 2, + (self: Micro, f: (e: E) => E2): Micro => catchAll(self, (error) => fail(f(error))) +) + +/** + * Elevate any expected errors of the given `Micro` effect to unexpected errors, + * resulting in an error type of `never`. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ +export const orDie = (self: Micro): Micro => catchAll(self, die) + +/** + * Recover from all errors by succeeding with the given value. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ +export const orElseSucceed: { + /** + * Recover from all errors by succeeding with the given value. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + (f: LazyArg): (self: Micro) => Micro + /** + * Recover from all errors by succeeding with the given value. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + (self: Micro, f: LazyArg): Micro +} = dual( + 2, + (self: Micro, f: LazyArg): Micro => catchAll(self, (_) => sync(f)) +) + +/** + * Ignore any expected errors of the given `Micro` effect, returning `void`. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ +export const ignore = (self: Micro): Micro => + matchEffect(self, { onFailure: (_) => void_, onSuccess: (_) => void_ }) + +/** + * Ignore any expected errors of the given `Micro` effect, returning `void`. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ +export const ignoreLogged = (self: Micro): Micro => + matchEffect(self, { + // eslint-disable-next-line no-console + onFailure: (error) => sync(() => console.error(error)), + onSuccess: (_) => void_ + }) + +/** + * Replace the success value of the given `Micro` effect with an `Option`, + * wrapping the success value in `Some` and returning `None` if the effect fails + * with an expected error. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ +export const option = (self: Micro): Micro, never, R> => + match(self, { onFailure: Option.none, onSuccess: Option.some }) + +/** + * Replace the success value of the given `Micro` effect with an `Either`, + * wrapping the success value in `Right` and wrapping any expected errors with + * a `Left`. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ +export const either = (self: Micro): Micro, never, R> => + match(self, { onFailure: Either.left, onSuccess: Either.right }) + +/** + * Retry the given `Micro` effect using the provided options. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ +export const retry: { + /** + * Retry the given `Micro` effect using the provided options. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + ( + options?: { + while?: Predicate | undefined + times?: number | undefined + schedule?: MicroSchedule | undefined + } | undefined + ): (self: Micro) => Micro + /** + * Retry the given `Micro` effect using the provided options. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + ( + self: Micro, + options?: { + while?: Predicate | undefined + times?: number | undefined + schedule?: MicroSchedule | undefined + } | undefined + ): Micro +} = dual((args) => isMicro(args[0]), ( + self: Micro, + options?: { + while?: Predicate | undefined + times?: number | undefined + schedule?: MicroSchedule | undefined + } | undefined +): Micro => + repeatExit(self, { + ...options, + while: (exit) => + exit._tag === "Failure" && exit.cause._tag === "Fail" && + (options?.while === undefined || options.while(exit.cause.error)) + })) + +/** + * Add a stack trace to any failures that occur in the effect. The trace will be + * added to the `traces` field of the `MicroCause` object. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ +export const withTrace: { + /** + * Add a stack trace to any failures that occur in the effect. The trace will be + * added to the `traces` field of the `MicroCause` object. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + (name: string): (self: Micro) => Micro + /** + * Add a stack trace to any failures that occur in the effect. The trace will be + * added to the `traces` field of the `MicroCause` object. + * + * @since 3.4.0 + * @experimental + * @category error handling + */ + (self: Micro, name: string): Micro +} = function() { + const prevLimit = globalThis.Error.stackTraceLimit + globalThis.Error.stackTraceLimit = 2 + const error = new globalThis.Error() + globalThis.Error.stackTraceLimit = prevLimit + function generate(name: string, cause: MicroCause) { + const stack = error.stack + if (!stack) { + return cause + } + const line = stack.split("\n")[2]?.trim().replace(/^at /, "") + if (!line) { + return cause + } + const lineMatch = line.match(/\((.*)\)$/) + return causeWithTrace(cause, `at ${name} (${lineMatch ? lineMatch[1] : line})`) + } + const f = (name: string) => (self: Micro) => onError(self, (cause) => failCause(generate(name, cause))) + if (arguments.length === 2) { + return f(arguments[1])(arguments[0]) + } + return f(arguments[0]) +} as any + +// ---------------------------------------------------------------------------- +// pattern matching +// ---------------------------------------------------------------------------- + +/** + * @since 3.4.6 + * @experimental + * @category pattern matching + */ +export const matchCauseEffect: { + // ---------------------------------------------------------------------------- + // pattern matching + // ---------------------------------------------------------------------------- + + /** + * @since 3.4.6 + * @experimental + * @category pattern matching + */ + ( + options: { + readonly onFailure: (cause: MicroCause) => Micro + readonly onSuccess: (a: A) => Micro + } + ): (self: Micro) => Micro + // ---------------------------------------------------------------------------- + // pattern matching + // ---------------------------------------------------------------------------- + + /** + * @since 3.4.6 + * @experimental + * @category pattern matching + */ + ( + self: Micro, + options: { + readonly onFailure: (cause: MicroCause) => Micro + readonly onSuccess: (a: A) => Micro + } + ): Micro +} = dual( + 2, + ( + self: Micro, + options: { + readonly onFailure: (cause: MicroCause) => Micro + readonly onSuccess: (a: A) => Micro + } + ): Micro => { + const primitive = Object.create(OnSuccessAndFailureProto) + primitive[args] = self + primitive[successCont] = options.onSuccess + primitive[failureCont] = options.onFailure + return primitive + } +) +const OnSuccessAndFailureProto = makePrimitiveProto({ + op: "OnSuccessAndFailure", + eval(this: any, fiber: MicroFiberImpl): Primitive { + fiber._stack.push(this) + return this[args] + } +}) + +/** + * @since 3.4.6 + * @experimental + * @category pattern matching + */ +export const matchCause: { + /** + * @since 3.4.6 + * @experimental + * @category pattern matching + */ + ( + options: { + readonly onFailure: (cause: MicroCause) => A2 + readonly onSuccess: (a: A) => A3 + } + ): (self: Micro) => Micro + /** + * @since 3.4.6 + * @experimental + * @category pattern matching + */ + ( + self: Micro, + options: { + readonly onFailure: (cause: MicroCause) => A2 + readonly onSuccess: (a: A) => A3 + } + ): Micro +} = dual( + 2, + ( + self: Micro, + options: { + readonly onFailure: (cause: MicroCause) => A2 + readonly onSuccess: (a: A) => A3 + } + ): Micro => + matchCauseEffect(self, { + onFailure: (cause) => sync(() => options.onFailure(cause)), + onSuccess: (value) => sync(() => options.onSuccess(value)) + }) +) + +/** + * @since 3.4.6 + * @experimental + * @category pattern matching + */ +export const matchEffect: { + /** + * @since 3.4.6 + * @experimental + * @category pattern matching + */ + ( + options: { + readonly onFailure: (e: E) => Micro + readonly onSuccess: (a: A) => Micro + } + ): (self: Micro) => Micro + /** + * @since 3.4.6 + * @experimental + * @category pattern matching + */ + ( + self: Micro, + options: { + readonly onFailure: (e: E) => Micro + readonly onSuccess: (a: A) => Micro + } + ): Micro +} = dual( + 2, + ( + self: Micro, + options: { + readonly onFailure: (e: E) => Micro + readonly onSuccess: (a: A) => Micro + } + ): Micro => + matchCauseEffect(self, { + onFailure: (cause) => cause._tag === "Fail" ? options.onFailure(cause.error) : failCause(cause), + onSuccess: options.onSuccess + }) +) + +/** + * @since 3.4.0 + * @experimental + * @category pattern matching + */ +export const match: { + /** + * @since 3.4.0 + * @experimental + * @category pattern matching + */ + ( + options: { + readonly onFailure: (error: E) => A2 + readonly onSuccess: (value: A) => A3 + } + ): (self: Micro) => Micro + /** + * @since 3.4.0 + * @experimental + * @category pattern matching + */ + ( + self: Micro, + options: { + readonly onFailure: (error: E) => A2 + readonly onSuccess: (value: A) => A3 + } + ): Micro +} = dual( + 2, + ( + self: Micro, + options: { + readonly onFailure: (error: E) => A2 + readonly onSuccess: (value: A) => A3 + } + ): Micro => + matchEffect(self, { + onFailure: (error) => sync(() => options.onFailure(error)), + onSuccess: (value) => sync(() => options.onSuccess(value)) + }) +) + +// ---------------------------------------------------------------------------- +// delays & timeouts +// ---------------------------------------------------------------------------- + +/** + * Create a `Micro` effect that will sleep for the specified duration. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ +export const sleep = (millis: number): Micro => + async((resume) => { + const timeout = setTimeout(() => { + resume(void_) + }, millis) + return sync(() => { + clearTimeout(timeout) + }) + }) + +/** + * Returns an effect that will delay the execution of this effect by the + * specified duration. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ +export const delay: { + /** + * Returns an effect that will delay the execution of this effect by the + * specified duration. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ + (millis: number): (self: Micro) => Micro + /** + * Returns an effect that will delay the execution of this effect by the + * specified duration. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ + (self: Micro, millis: number): Micro +} = dual( + 2, + (self: Micro, millis: number): Micro => andThen(sleep(millis), self) +) + +/** + * Returns an effect that will timeout this effect, that will execute the + * fallback effect if the timeout elapses before the effect has produced a value. + * + * If the timeout elapses, the running effect will be safely interrupted. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ +export const timeoutOrElse: { + /** + * Returns an effect that will timeout this effect, that will execute the + * fallback effect if the timeout elapses before the effect has produced a value. + * + * If the timeout elapses, the running effect will be safely interrupted. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ + ( + options: { + readonly duration: number + readonly onTimeout: LazyArg> + } + ): (self: Micro) => Micro + /** + * Returns an effect that will timeout this effect, that will execute the + * fallback effect if the timeout elapses before the effect has produced a value. + * + * If the timeout elapses, the running effect will be safely interrupted. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ + ( + self: Micro, + options: { + readonly duration: number + readonly onTimeout: LazyArg> + } + ): Micro +} = dual( + 2, + (self: Micro, options: { + readonly duration: number + readonly onTimeout: LazyArg> + }): Micro => + raceFirst(self, andThen(interruptible(sleep(options.duration)), options.onTimeout)) +) + +/** + * Returns an effect that will timeout this effect, that will fail with a + * `TimeoutException` if the timeout elapses before the effect has produced a + * value. + * + * If the timeout elapses, the running effect will be safely interrupted. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ +export const timeout: { + /** + * Returns an effect that will timeout this effect, that will fail with a + * `TimeoutException` if the timeout elapses before the effect has produced a + * value. + * + * If the timeout elapses, the running effect will be safely interrupted. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ + (millis: number): (self: Micro) => Micro + /** + * Returns an effect that will timeout this effect, that will fail with a + * `TimeoutException` if the timeout elapses before the effect has produced a + * value. + * + * If the timeout elapses, the running effect will be safely interrupted. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ + (self: Micro, millis: number): Micro +} = dual( + 2, + (self: Micro, millis: number): Micro => + timeoutOrElse(self, { duration: millis, onTimeout: () => fail(new TimeoutException()) }) +) + +/** + * Returns an effect that will timeout this effect, succeeding with a `None` + * if the timeout elapses before the effect has produced a value; and `Some` of + * the produced value otherwise. + * + * If the timeout elapses, the running effect will be safely interrupted. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ +export const timeoutOption: { + /** + * Returns an effect that will timeout this effect, succeeding with a `None` + * if the timeout elapses before the effect has produced a value; and `Some` of + * the produced value otherwise. + * + * If the timeout elapses, the running effect will be safely interrupted. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ + (millis: number): (self: Micro) => Micro, E, R> + /** + * Returns an effect that will timeout this effect, succeeding with a `None` + * if the timeout elapses before the effect has produced a value; and `Some` of + * the produced value otherwise. + * + * If the timeout elapses, the running effect will be safely interrupted. + * + * @since 3.4.0 + * @experimental + * @category delays & timeouts + */ + (self: Micro, millis: number): Micro, E, R> +} = dual( + 2, + (self: Micro, millis: number): Micro, E, R> => + raceFirst( + asSome(self), + as(interruptible(sleep(millis)), Option.none()) + ) +) + +// ---------------------------------------------------------------------------- +// resources & finalization +// ---------------------------------------------------------------------------- + +/** + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export const MicroScopeTypeId: unique symbol = Symbol.for("effect/Micro/MicroScope") + +/** + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export type MicroScopeTypeId = typeof MicroScopeTypeId + +/** + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export interface MicroScope { + readonly [MicroScopeTypeId]: MicroScopeTypeId + readonly addFinalizer: (finalizer: (exit: MicroExit) => Micro) => Micro + readonly fork: Micro +} + +/** + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export declare namespace MicroScope { + /** + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ + export interface Closeable extends MicroScope { + readonly close: (exit: MicroExit) => Micro + } +} + +/** + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export const MicroScope: Context.Tag = Context.GenericTag("effect/Micro/MicroScope") + +class MicroScopeImpl implements MicroScope.Closeable { + readonly [MicroScopeTypeId]: MicroScopeTypeId + state: { + readonly _tag: "Open" + readonly finalizers: Set<(exit: MicroExit) => Micro> + } | { + readonly _tag: "Closed" + readonly exit: MicroExit + } = { _tag: "Open", finalizers: new Set() } + + constructor() { + this[MicroScopeTypeId] = MicroScopeTypeId + } + + unsafeAddFinalizer(finalizer: (exit: MicroExit) => Micro): void { + if (this.state._tag === "Open") { + this.state.finalizers.add(finalizer) + } + } + addFinalizer(finalizer: (exit: MicroExit) => Micro): Micro { + return suspend(() => { + if (this.state._tag === "Open") { + this.state.finalizers.add(finalizer) + return void_ + } + return finalizer(this.state.exit) + }) + } + unsafeRemoveFinalizer(finalizer: (exit: MicroExit) => Micro): void { + if (this.state._tag === "Open") { + this.state.finalizers.delete(finalizer) + } + } + close(microExit: MicroExit): Micro { + return suspend(() => { + if (this.state._tag === "Open") { + const finalizers = Array.from(this.state.finalizers).reverse() + this.state = { _tag: "Closed", exit: microExit } + return flatMap( + forEach(finalizers, (finalizer) => exit(finalizer(microExit))), + exitVoidAll + ) + } + return void_ + }) + } + get fork() { + return sync(() => { + const newScope = new MicroScopeImpl() + if (this.state._tag === "Closed") { + newScope.state = this.state + return newScope + } + function fin(exit: MicroExit) { + return newScope.close(exit) + } + this.state.finalizers.add(fin) + newScope.unsafeAddFinalizer((_) => sync(() => this.unsafeRemoveFinalizer(fin))) + return newScope + }) + } +} + +/** + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export const scopeMake: Micro = sync(() => new MicroScopeImpl()) + +/** + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export const scopeUnsafeMake = (): MicroScope.Closeable => new MicroScopeImpl() + +/** + * Access the current `MicroScope`. + * + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export const scope: Micro = service(MicroScope) + +/** + * Provide a `MicroScope` to an effect. + * + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export const provideScope: { + /** + * Provide a `MicroScope` to an effect. + * + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ + (scope: MicroScope): (self: Micro) => Micro> + /** + * Provide a `MicroScope` to an effect. + * + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ + (self: Micro, scope: MicroScope): Micro> +} = dual( + 2, + (self: Micro, scope: MicroScope): Micro> => + provideService(self, MicroScope, scope) +) + +/** + * Provide a `MicroScope` to the given effect, closing it after the effect has + * finished executing. + * + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export const scoped = (self: Micro): Micro> => + suspend(() => { + const scope = new MicroScopeImpl() + return onExit(provideService(self, MicroScope, scope), (exit) => scope.close(exit)) + }) + +/** + * Create a resource with a cleanup `Micro` effect, ensuring the cleanup is + * executed when the `MicroScope` is closed. + * + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export const acquireRelease = ( + acquire: Micro, + release: (a: A, exit: MicroExit) => Micro +): Micro => + uninterruptible(flatMap( + scope, + (scope) => tap(acquire, (a) => scope.addFinalizer((exit) => release(a, exit))) + )) + +/** + * Add a finalizer to the current `MicroScope`. + * + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export const addFinalizer = ( + finalizer: (exit: MicroExit) => Micro +): Micro => flatMap(scope, (scope) => scope.addFinalizer(finalizer)) + +/** + * When the `Micro` effect is completed, run the given finalizer effect with the + * `MicroExit` of the executed effect. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ +export const onExit: { + /** + * When the `Micro` effect is completed, run the given finalizer effect with the + * `MicroExit` of the executed effect. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ + (f: (exit: MicroExit) => Micro): (self: Micro) => Micro + /** + * When the `Micro` effect is completed, run the given finalizer effect with the + * `MicroExit` of the executed effect. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ + (self: Micro, f: (exit: MicroExit) => Micro): Micro +} = dual( + 2, + ( + self: Micro, + f: (exit: MicroExit) => Micro + ): Micro => + uninterruptibleMask((restore) => + matchCauseEffect(restore(self), { + onFailure: (cause) => flatMap(f(exitFailCause(cause)), () => failCause(cause)), + onSuccess: (a) => flatMap(f(exitSucceed(a)), () => succeed(a)) + }) + ) +) + +/** + * Regardless of the result of the this `Micro` effect, run the finalizer effect. + * + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export const ensuring: { + /** + * Regardless of the result of the this `Micro` effect, run the finalizer effect. + * + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ + (finalizer: Micro): (self: Micro) => Micro + /** + * Regardless of the result of the this `Micro` effect, run the finalizer effect. + * + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ + (self: Micro, finalizer: Micro): Micro +} = dual( + 2, + ( + self: Micro, + finalizer: Micro + ): Micro => onExit(self, (_) => finalizer) +) + +/** + * When the `Micro` effect is completed, run the given finalizer effect if it + * matches the specified predicate. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ +export const onExitIf: { + /** + * When the `Micro` effect is completed, run the given finalizer effect if it + * matches the specified predicate. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ + >( + refinement: Refinement, B>, + f: (exit: B) => Micro + ): (self: Micro) => Micro + /** + * When the `Micro` effect is completed, run the given finalizer effect if it + * matches the specified predicate. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ + ( + predicate: Predicate, NoInfer>>, + f: (exit: MicroExit, NoInfer>) => Micro + ): (self: Micro) => Micro + /** + * When the `Micro` effect is completed, run the given finalizer effect if it + * matches the specified predicate. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ + >( + self: Micro, + refinement: Refinement, B>, + f: (exit: B) => Micro + ): Micro + /** + * When the `Micro` effect is completed, run the given finalizer effect if it + * matches the specified predicate. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ + ( + self: Micro, + predicate: Predicate, NoInfer>>, + f: (exit: MicroExit, NoInfer>) => Micro + ): Micro +} = dual( + 3, + >( + self: Micro, + refinement: Refinement, B>, + f: (exit: B) => Micro + ): Micro => onExit(self, (exit) => (refinement(exit) ? f(exit) : exitVoid)) +) + +/** + * When the `Micro` effect fails, run the given finalizer effect with the + * `MicroCause` of the executed effect. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ +export const onError: { + /** + * When the `Micro` effect fails, run the given finalizer effect with the + * `MicroCause` of the executed effect. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ + (f: (cause: MicroCause>) => Micro): (self: Micro) => Micro + /** + * When the `Micro` effect fails, run the given finalizer effect with the + * `MicroCause` of the executed effect. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ + ( + self: Micro, + f: (cause: MicroCause>) => Micro + ): Micro +} = dual( + 2, + ( + self: Micro, + f: (cause: MicroCause>) => Micro + ): Micro => onExitIf(self, exitIsFailure, (exit) => f(exit.cause)) +) + +/** + * If this `Micro` effect is aborted, run the finalizer effect. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ +export const onInterrupt: { + /** + * If this `Micro` effect is aborted, run the finalizer effect. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ + (finalizer: Micro): (self: Micro) => Micro + /** + * If this `Micro` effect is aborted, run the finalizer effect. + * + * @since 3.4.6 + * @experimental + * @category resources & finalization + */ + (self: Micro, finalizer: Micro): Micro +} = dual( + 2, + (self: Micro, finalizer: Micro): Micro => + onExitIf(self, exitIsInterrupt, (_) => finalizer) +) + +/** + * Acquire a resource, use it, and then release the resource when the `use` + * effect has completed. + * + * @since 3.4.0 + * @experimental + * @category resources & finalization + */ +export const acquireUseRelease = ( + acquire: Micro, + use: (a: Resource) => Micro, + release: (a: Resource, exit: MicroExit) => Micro +): Micro => + uninterruptibleMask((restore) => + flatMap( + acquire, + (a) => + flatMap( + exit(restore(use(a))), + (exit) => andThen(release(a, exit), exit) + ) + ) + ) + +// ---------------------------------------------------------------------------- +// interruption +// ---------------------------------------------------------------------------- + +/** + * Abort the current `Micro` effect. + * + * @since 3.4.6 + * @experimental + * @category interruption + */ +export const interrupt: Micro = failCause(causeInterrupt()) + +/** + * Flag the effect as uninterruptible, which means that when the effect is + * interrupted, it will be allowed to continue running until completion. + * + * @since 3.4.0 + * @experimental + * @category flags + */ +export const uninterruptible = ( + self: Micro +): Micro => + withMicroFiber((fiber) => { + if (!fiber.interruptible) return self + fiber.interruptible = false + fiber._stack.push(setInterruptible(true)) + return self + }) + +const setInterruptible: (interruptible: boolean) => Primitive = makePrimitive({ + op: "SetInterruptible", + ensure(fiber) { + fiber.interruptible = this[args] + if (fiber._interrupted && fiber.interruptible) { + return () => exitInterrupt + } + } +}) + +/** + * Flag the effect as interruptible, which means that when the effect is + * interrupted, it will be interrupted immediately. + * + * @since 3.4.0 + * @experimental + * @category flags + */ +export const interruptible = ( + self: Micro +): Micro => + withMicroFiber((fiber) => { + if (fiber.interruptible) return self + fiber.interruptible = true + fiber._stack.push(setInterruptible(false)) + if (fiber._interrupted) return exitInterrupt + return self + }) + +/** + * Wrap the given `Micro` effect in an uninterruptible region, preventing the + * effect from being aborted. + * + * You can use the `restore` function to restore a `Micro` effect to the + * interruptibility state before the `uninterruptibleMask` was applied. + * + * @example + * ```ts + * import * as Micro from "effect/Micro" + * + * Micro.uninterruptibleMask((restore) => + * Micro.sleep(1000).pipe( // uninterruptible + * Micro.andThen(restore(Micro.sleep(1000))) // interruptible + * ) + * ) + * ``` + * + * @since 3.4.0 + * @experimental + * @category interruption + */ +export const uninterruptibleMask = ( + f: ( + restore: (effect: Micro) => Micro + ) => Micro +): Micro => + withMicroFiber((fiber) => { + if (!fiber.interruptible) return f(identity) + fiber.interruptible = false + fiber._stack.push(setInterruptible(true)) + return f(interruptible) + }) + +// ======================================================================== +// collecting & elements +// ======================================================================== + +/** + * @since 3.4.0 + * @experimental + */ +export declare namespace All { + /** + * @since 3.4.0 + * @experimental + */ + export type MicroAny = Micro + + /** + * @since 3.4.0 + * @experimental + */ + export type ReturnIterable, Discard extends boolean> = [T] extends + [Iterable>] ? Micro< + Discard extends true ? void : Array, + E, + R + > + : never + + /** + * @since 3.4.0 + * @experimental + */ + export type ReturnTuple, Discard extends boolean> = Micro< + Discard extends true ? void + : T[number] extends never ? [] + : { -readonly [K in keyof T]: T[K] extends Micro ? _A : never }, + T[number] extends never ? never + : T[number] extends Micro ? _E + : never, + T[number] extends never ? never + : T[number] extends Micro ? _R + : never + > extends infer X ? X : never + + /** + * @since 3.4.0 + * @experimental + */ + export type ReturnObject = [T] extends [{ [K: string]: MicroAny }] ? Micro< + Discard extends true ? void : + { -readonly [K in keyof T]: [T[K]] extends [Micro] ? _A : never }, + keyof T extends never ? never + : T[keyof T] extends Micro ? _E + : never, + keyof T extends never ? never + : T[keyof T] extends Micro ? _R + : never + > + : never + + /** + * @since 3.4.0 + * @experimental + */ + export type IsDiscard = [Extract] extends [never] ? false : true + + /** + * @since 3.4.0 + * @experimental + */ + export type Return< + Arg extends Iterable | Record, + O extends NoExcessProperties<{ + readonly concurrency?: Concurrency | undefined + readonly discard?: boolean | undefined + }, O> + > = [Arg] extends [ReadonlyArray] ? ReturnTuple> + : [Arg] extends [Iterable] ? ReturnIterable> + : [Arg] extends [Record] ? ReturnObject> + : never +} + +/** + * Runs all the provided effects in sequence respecting the structure provided in input. + * + * Supports multiple arguments, a single argument tuple / array or record / struct. + * + * @since 3.4.0 + * @experimental + * @category collecting & elements + */ +export const all = < + const Arg extends Iterable> | Record>, + O extends NoExcessProperties<{ + readonly concurrency?: Concurrency | undefined + readonly discard?: boolean | undefined + }, O> +>(arg: Arg, options?: O): All.Return => { + if (Array.isArray(arg) || isIterable(arg)) { + return (forEach as any)(arg, identity, options) + } else if (options?.discard) { + return (forEach as any)(Object.values(arg), identity, options) + } + return suspend(() => { + const out: Record = {} + return as( + forEach(Object.entries(arg), ([key, effect]) => + map(effect, (value) => { + out[key] = value + }), { + discard: true, + concurrency: options?.concurrency + }), + out + ) + }) as any +} + +/** + * @since 3.11.0 + * @experimental + * @category collecting & elements + */ +export const whileLoop: (options: { + readonly while: LazyArg + readonly body: LazyArg> + readonly step: (a: A) => void +}) => Micro = makePrimitive({ + op: "While", + contA(value, fiber) { + this[args].step(value) + if (this[args].while()) { + fiber._stack.push(this) + return this[args].body() + } + return exitVoid + }, + eval(fiber) { + if (this[args].while()) { + fiber._stack.push(this) + return this[args].body() + } + return exitVoid + } +}) + +/** + * For each element of the provided iterable, run the effect and collect the + * results. + * + * If the `discard` option is set to `true`, the results will be discarded and + * the effect will return `void`. + * + * The `concurrency` option can be set to control how many effects are run + * concurrently. By default, the effects are run sequentially. + * + * @since 3.4.0 + * @experimental + * @category collecting & elements + */ +export const forEach: { + /** + * For each element of the provided iterable, run the effect and collect the + * results. + * + * If the `discard` option is set to `true`, the results will be discarded and + * the effect will return `void`. + * + * The `concurrency` option can be set to control how many effects are run + * concurrently. By default, the effects are run sequentially. + * + * @since 3.4.0 + * @experimental + * @category collecting & elements + */ + ( + iterable: Iterable, + f: (a: A, index: number) => Micro, + options?: { + readonly concurrency?: Concurrency | undefined + readonly discard?: false | undefined + } + ): Micro, E, R> + /** + * For each element of the provided iterable, run the effect and collect the + * results. + * + * If the `discard` option is set to `true`, the results will be discarded and + * the effect will return `void`. + * + * The `concurrency` option can be set to control how many effects are run + * concurrently. By default, the effects are run sequentially. + * + * @since 3.4.0 + * @experimental + * @category collecting & elements + */ + ( + iterable: Iterable, + f: (a: A, index: number) => Micro, + options: { + readonly concurrency?: Concurrency | undefined + readonly discard: true + } + ): Micro +} = < + A, + B, + E, + R +>(iterable: Iterable, f: (a: A, index: number) => Micro, options?: { + readonly concurrency?: Concurrency | undefined + readonly discard?: boolean | undefined +}): Micro => + withMicroFiber((parent) => { + const concurrencyOption = options?.concurrency === "inherit" + ? parent.getRef(CurrentConcurrency) + : options?.concurrency ?? 1 + const concurrency = concurrencyOption === "unbounded" + ? Number.POSITIVE_INFINITY + : Math.max(1, concurrencyOption) + + const items = Arr.fromIterable(iterable) + let length = items.length + if (length === 0) { + return options?.discard ? void_ : succeed([]) + } + + const out: Array | undefined = options?.discard ? undefined : new Array(length) + let index = 0 + + if (concurrency === 1) { + return as( + whileLoop({ + while: () => index < items.length, + body: () => f(items[index], index), + step: out ? + (b) => out[index++] = b : + (_) => index++ + }), + out as any + ) + } + return async((resume) => { + const fibers = new Set>() + let result: MicroExit | undefined = undefined + let inProgress = 0 + let doneCount = 0 + let pumping = false + let interrupted = false + function pump() { + pumping = true + while (inProgress < concurrency && index < length) { + const currentIndex = index + const item = items[currentIndex] + index++ + inProgress++ + try { + const child = unsafeFork(parent, f(item, currentIndex), true, true) + fibers.add(child) + child.addObserver((exit) => { + fibers.delete(child) + if (interrupted) { + return + } else if (exit._tag === "Failure") { + if (result === undefined) { + result = exit + length = index + fibers.forEach((fiber) => fiber.unsafeInterrupt()) + } + } else if (out !== undefined) { + out[currentIndex] = exit.value + } + doneCount++ + inProgress-- + if (doneCount === length) { + resume(result ?? succeed(out)) + } else if (!pumping && inProgress < concurrency) { + pump() + } + }) + } catch (err) { + result = exitDie(err) + length = index + fibers.forEach((fiber) => fiber.unsafeInterrupt()) + } + } + pumping = false + } + pump() + + return suspend(() => { + interrupted = true + index = length + return fiberInterruptAll(fibers) + }) + }) + }) + +/** + * Effectfully filter the elements of the provided iterable. + * + * Use the `concurrency` option to control how many elements are processed + * concurrently. + * + * @since 3.4.0 + * @experimental + * @category collecting & elements + */ +export const filter = (iterable: Iterable, f: (a: NoInfer) => Micro, options?: { + readonly concurrency?: Concurrency | undefined + readonly negate?: boolean | undefined +}): Micro, E, R> => + filterMap(iterable, (a) => + map(f(a), (pass) => { + pass = options?.negate ? !pass : pass + return pass ? Option.some(a) : Option.none() + }), options) + +/** + * Effectfully filter the elements of the provided iterable. + * + * Use the `concurrency` option to control how many elements are processed + * concurrently. + * + * @since 3.4.0 + * @experimental + * @category collecting & elements + */ +export const filterMap = ( + iterable: Iterable, + f: (a: NoInfer) => Micro, E, R>, + options?: { + readonly concurrency?: Concurrency | undefined + } +): Micro, E, R> => + suspend(() => { + const out: Array = [] + return as( + forEach(iterable, (a) => + map(f(a), (o) => { + if (o._tag === "Some") { + out.push(o.value) + } + }), { + discard: true, + concurrency: options?.concurrency + }), + out + ) + }) + +// ---------------------------------------------------------------------------- +// do notation +// ---------------------------------------------------------------------------- + +/** + * Start a do notation block. + * + * @since 3.4.0 + * @experimental + * @category do notation + */ +export const Do: Micro<{}> = succeed({}) + +/** + * Bind the success value of this `Micro` effect to the provided name. + * + * @since 3.4.0 + * @experimental + * @category do notation + */ +export const bindTo: { + /** + * Bind the success value of this `Micro` effect to the provided name. + * + * @since 3.4.0 + * @experimental + * @category do notation + */ + (name: N): (self: Micro) => Micro<{ [K in N]: A }, E, R> + /** + * Bind the success value of this `Micro` effect to the provided name. + * + * @since 3.4.0 + * @experimental + * @category do notation + */ + (self: Micro, name: N): Micro<{ [K in N]: A }, E, R> +} = doNotation.bindTo(map) + +/** + * Bind the success value of this `Micro` effect to the provided name. + * + * @since 3.4.0 + * @experimental + * @category do notation + */ +export const bind: { + /** + * Bind the success value of this `Micro` effect to the provided name. + * + * @since 3.4.0 + * @experimental + * @category do notation + */ + , B, E2, R2>(name: N, f: (a: NoInfer) => Micro): (self: Micro) => Micro & { [K in N]: B }>, E | E2, R | R2> + /** + * Bind the success value of this `Micro` effect to the provided name. + * + * @since 3.4.0 + * @experimental + * @category do notation + */ + , E, R, B, E2, R2, N extends string>(self: Micro, name: N, f: (a: NoInfer) => Micro): Micro & { [K in N]: B }>, E | E2, R | R2> +} = doNotation.bind(map, flatMap) + +const let_: { + , B>( + name: N, + f: (a: NoInfer) => B + ): (self: Micro) => Micro & { [K in N]: B }>, E, R> + , E, R, B, N extends string>( + self: Micro, + name: N, + f: (a: NoInfer) => B + ): Micro & { [K in N]: B }>, E, R> +} = doNotation.let_(map) + +export { + /** + * Bind the result of a synchronous computation to the given name. + * + * @since 3.4.0 + * @experimental + * @category do notation + */ + let_ as let +} + +// ---------------------------------------------------------------------------- +// fibers & forking +// ---------------------------------------------------------------------------- + +/** + * Run the `Micro` effect in a new `MicroFiber` that can be awaited, joined, or + * aborted. + * + * When the parent `Micro` finishes, this `Micro` will be aborted. + * + * @since 3.4.0 + * @experimental + * @category fiber & forking + */ +export const fork = ( + self: Micro +): Micro, never, R> => + withMicroFiber((fiber) => { + fiberMiddleware.interruptChildren ??= fiberInterruptChildren + return succeed(unsafeFork(fiber, self)) + }) + +const unsafeFork = ( + parent: MicroFiberImpl, + effect: Micro, + immediate = false, + daemon = false +): MicroFiber => { + const child = new MicroFiberImpl(parent.context, parent.interruptible) + if (!daemon) { + parent.children().add(child) + child.addObserver(() => parent.children().delete(child)) + } + if (immediate) { + child.evaluate(effect as any) + } else { + parent.getRef(CurrentScheduler).scheduleTask(() => child.evaluate(effect as any), 0) + } + return child +} + +/** + * Run the `Micro` effect in a new `MicroFiber` that can be awaited, joined, or + * aborted. + * + * It will not be aborted when the parent `Micro` finishes. + * + * @since 3.4.0 + * @experimental + * @category fiber & forking + */ +export const forkDaemon = ( + self: Micro +): Micro, never, R> => withMicroFiber((fiber) => succeed(unsafeFork(fiber, self, false, true))) + +/** + * Run the `Micro` effect in a new `MicroFiber` that can be awaited, joined, or + * aborted. + * + * The lifetime of the handle will be attached to the provided `MicroScope`. + * + * @since 3.4.0 + * @experimental + * @category fiber & forking + */ +export const forkIn: { + /** + * Run the `Micro` effect in a new `MicroFiber` that can be awaited, joined, or + * aborted. + * + * The lifetime of the handle will be attached to the provided `MicroScope`. + * + * @since 3.4.0 + * @experimental + * @category fiber & forking + */ + (scope: MicroScope): (self: Micro) => Micro, never, R> + /** + * Run the `Micro` effect in a new `MicroFiber` that can be awaited, joined, or + * aborted. + * + * The lifetime of the handle will be attached to the provided `MicroScope`. + * + * @since 3.4.0 + * @experimental + * @category fiber & forking + */ + (self: Micro, scope: MicroScope): Micro, never, R> +} = dual( + 2, + (self: Micro, scope: MicroScope): Micro, never, R> => + uninterruptibleMask((restore) => + flatMap(scope.fork, (scope) => + tap( + restore(forkDaemon(onExit(self, (exit) => scope.close(exit)))), + (fiber) => scope.addFinalizer((_) => fiberInterrupt(fiber)) + )) + ) +) + +/** + * Run the `Micro` effect in a new `MicroFiber` that can be awaited, joined, or + * aborted. + * + * The lifetime of the handle will be attached to the current `MicroScope`. + * + * @since 3.4.0 + * @experimental + * @category fiber & forking + */ +export const forkScoped = (self: Micro): Micro, never, R | MicroScope> => + flatMap(scope, (scope) => forkIn(self, scope)) + +// ---------------------------------------------------------------------------- +// execution +// ---------------------------------------------------------------------------- + +/** + * Execute the `Micro` effect and return a `MicroFiber` that can be awaited, joined, + * or aborted. + * + * You can listen for the result by adding an observer using the handle's + * `addObserver` method. + * + * @example + * ```ts + * import * as Micro from "effect/Micro" + * + * const handle = Micro.succeed(42).pipe( + * Micro.delay(1000), + * Micro.runFork + * ) + * + * handle.addObserver((exit) => { + * console.log(exit) + * }) + * ``` + * + * @since 3.4.0 + * @experimental + * @category execution + */ +export const runFork = ( + effect: Micro, + options?: { + readonly signal?: AbortSignal | undefined + readonly scheduler?: MicroScheduler | undefined + } | undefined +): MicroFiberImpl => { + const fiber = new MicroFiberImpl(CurrentScheduler.context( + options?.scheduler ?? new MicroSchedulerDefault() + )) + fiber.evaluate(effect as any) + if (options?.signal) { + if (options.signal.aborted) { + fiber.unsafeInterrupt() + } else { + const abort = () => fiber.unsafeInterrupt() + options.signal.addEventListener("abort", abort, { once: true }) + fiber.addObserver(() => options.signal!.removeEventListener("abort", abort)) + } + } + return fiber +} + +/** + * Execute the `Micro` effect and return a `Promise` that resolves with the + * `MicroExit` of the computation. + * + * @since 3.4.6 + * @experimental + * @category execution + */ +export const runPromiseExit = ( + effect: Micro, + options?: { + readonly signal?: AbortSignal | undefined + readonly scheduler?: MicroScheduler | undefined + } | undefined +): Promise> => + new Promise((resolve, _reject) => { + const handle = runFork(effect, options) + handle.addObserver(resolve) + }) + +/** + * Execute the `Micro` effect and return a `Promise` that resolves with the + * successful value of the computation. + * + * @since 3.4.0 + * @experimental + * @category execution + */ +export const runPromise = ( + effect: Micro, + options?: { + readonly signal?: AbortSignal | undefined + readonly scheduler?: MicroScheduler | undefined + } | undefined +): Promise => + runPromiseExit(effect, options).then((exit) => { + if (exit._tag === "Failure") { + throw exit.cause + } + return exit.value + }) + +/** + * Attempt to execute the `Micro` effect synchronously and return the `MicroExit`. + * + * If any asynchronous effects are encountered, the function will return a + * `CauseDie` containing the `MicroFiber`. + * + * @since 3.4.6 + * @experimental + * @category execution + */ +export const runSyncExit = (effect: Micro): MicroExit => { + const scheduler = new MicroSchedulerDefault() + const fiber = runFork(effect, { scheduler }) + scheduler.flush() + return fiber._exit ?? exitDie(fiber) +} + +/** + * Attempt to execute the `Micro` effect synchronously and return the success + * value. + * + * @since 3.4.0 + * @experimental + * @category execution + */ +export const runSync = (effect: Micro): A => { + const exit = runSyncExit(effect) + if (exit._tag === "Failure") throw exit.cause + return exit.value +} + +// ---------------------------------------------------------------------------- +// Errors +// ---------------------------------------------------------------------------- + +/** + * @since 3.4.0 + * @experimental + * @category errors + */ +export interface YieldableError extends Pipeable, Inspectable, Readonly { + readonly [Effectable.EffectTypeId]: Effect.VarianceStruct + readonly [Effectable.StreamTypeId]: Stream.VarianceStruct + readonly [Effectable.SinkTypeId]: Sink.VarianceStruct + readonly [Effectable.ChannelTypeId]: Channel.VarianceStruct + readonly [TypeId]: Micro.Variance + [Symbol.iterator](): MicroIterator> +} + +const YieldableError: new(message?: string) => YieldableError = (function() { + class YieldableError extends globalThis.Error {} + // @effect-diagnostics-next-line floatingEffect:off + Object.assign(YieldableError.prototype, MicroProto, StructuralPrototype, { + [identifier]: "Failure", + [evaluate]() { + return fail(this) + }, + toString(this: Error) { + return this.message ? `${this.name}: ${this.message}` : this.name + }, + toJSON() { + return { ...this } + }, + [NodeInspectSymbol](this: Error): string { + const stack = this.stack + if (stack) { + return `${this.toString()}\n${stack.split("\n").slice(1).join("\n")}` + } + return this.toString() + } + }) + return YieldableError as any +})() + +/** + * @since 3.4.0 + * @experimental + * @category errors + */ +export const Error: new = {}>( + args: Equals extends true ? void + : { readonly [P in keyof A]: A[P] } +) => YieldableError & Readonly = (function() { + return class extends YieldableError { + constructor(args: any) { + super() + if (args) { + Object.assign(this, args) + } + } + } as any +})() + +/** + * @since 3.4.0 + * @experimental + * @category errors + */ +export const TaggedError = (tag: Tag): new = {}>( + args: Equals extends true ? void + : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] } +) => YieldableError & { readonly _tag: Tag } & Readonly => { + class Base extends Error<{}> { + readonly _tag = tag + } + ;(Base.prototype as any).name = tag + return Base as any +} + +/** + * Represents a checked exception which occurs when an expected element was + * unable to be found. + * + * @since 3.4.4 + * @experimental + * @category errors + */ +export class NoSuchElementException extends TaggedError("NoSuchElementException")<{ message?: string | undefined }> {} + +/** + * Represents a checked exception which occurs when a timeout occurs. + * + * @since 3.4.4 + * @experimental + * @category errors + */ +export class TimeoutException extends TaggedError("TimeoutException") {} diff --git a/backend/node_modules/effect/src/ModuleVersion.ts b/backend/node_modules/effect/src/ModuleVersion.ts new file mode 100644 index 0000000000000000000000000000000000000000..a8e5d2514036df405fab253e01a32efa041050bf --- /dev/null +++ b/backend/node_modules/effect/src/ModuleVersion.ts @@ -0,0 +1,18 @@ +/** + * @since 2.0.0 + * + * Enables low level framework authors to run on their own isolated effect version + */ +import * as internal from "./internal/version.js" + +/** + * @since 2.0.0 + * @category version + */ +export const getCurrentVersion: () => string = internal.getCurrentVersion + +/** + * @since 2.0.0 + * @category version + */ +export const setCurrentVersion: (version: string) => void = internal.setCurrentVersion diff --git a/backend/node_modules/effect/src/MutableHashMap.ts b/backend/node_modules/effect/src/MutableHashMap.ts new file mode 100644 index 0000000000000000000000000000000000000000..05d645d22a8939036dd4c7d494503fda2dddea3a --- /dev/null +++ b/backend/node_modules/effect/src/MutableHashMap.ts @@ -0,0 +1,518 @@ +/** + * @since 2.0.0 + */ +import type { NonEmptyArray } from "./Array.js" +import * as Equal from "./Equal.js" +import { dual } from "./Function.js" +import * as Hash from "./Hash.js" +import { format, type Inspectable, NodeInspectSymbol, toJSON } from "./Inspectable.js" +import * as Option from "./Option.js" +import type { Pipeable } from "./Pipeable.js" +import { pipeArguments } from "./Pipeable.js" + +const TypeId: unique symbol = Symbol.for("effect/MutableHashMap") as TypeId + +/** + * @since 2.0.0 + * @category symbol + */ +export type TypeId = typeof TypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface MutableHashMap extends Iterable<[K, V]>, Pipeable, Inspectable { + readonly [TypeId]: TypeId + /** @internal */ + readonly referential: Map + /** @internal */ + readonly buckets: Map> + /** @internal */ + bucketsSize: number +} + +const MutableHashMapProto: Omit, "referential" | "buckets" | "bucketsSize"> = { + [TypeId]: TypeId, + [Symbol.iterator](this: MutableHashMap): Iterator<[unknown, unknown]> { + return new MutableHashMapIterator(this) + }, + toString() { + return format(this.toJSON()) + }, + toJSON() { + return { + _id: "MutableHashMap", + values: Array.from(this).map(toJSON) + } + }, + [NodeInspectSymbol]() { + return this.toJSON() + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +class MutableHashMapIterator implements IterableIterator<[K, V]> { + readonly referentialIterator: Iterator<[K, V]> + bucketIterator: Iterator<[K, V]> | undefined + + constructor(readonly self: MutableHashMap) { + this.referentialIterator = self.referential[Symbol.iterator]() + } + next(): IteratorResult<[K, V]> { + if (this.bucketIterator !== undefined) { + return this.bucketIterator.next() + } + const result = this.referentialIterator.next() + if (result.done) { + this.bucketIterator = new BucketIterator(this.self.buckets.values()) + return this.next() + } + return result + } + + [Symbol.iterator](): IterableIterator<[K, V]> { + return new MutableHashMapIterator(this.self) + } +} + +class BucketIterator implements Iterator<[K, V]> { + constructor(readonly backing: Iterator>) {} + currentBucket: Iterator | undefined + next(): IteratorResult<[K, V]> { + if (this.currentBucket === undefined) { + const result = this.backing.next() + if (result.done) { + return result + } + this.currentBucket = result.value[Symbol.iterator]() + } + const result = this.currentBucket.next() + if (result.done) { + this.currentBucket = undefined + return this.next() + } + return result as IteratorResult<[K, V]> + } +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const empty = (): MutableHashMap => { + const self = Object.create(MutableHashMapProto) + self.referential = new Map() + self.buckets = new Map() + self.bucketsSize = 0 + return self +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const make: >( + ...entries: Entries +) => MutableHashMap< + Entries[number] extends readonly [infer K, any] ? K : never, + Entries[number] extends readonly [any, infer V] ? V : never +> = (...entries) => fromIterable(entries) + +/** + * Creates a new `MutableHashMap` from an iterable collection of key/value pairs. + * + * @since 2.0.0 + * @category constructors + */ +export const fromIterable = (entries: Iterable): MutableHashMap => { + const self = empty() + for (const [key, value] of entries) { + set(self, key, value) + } + return self +} + +/** + * @since 2.0.0 + * @category elements + */ +export const get: { + /** + * @since 2.0.0 + * @category elements + */ + (key: K): (self: MutableHashMap) => Option.Option + /** + * @since 2.0.0 + * @category elements + */ + (self: MutableHashMap, key: K): Option.Option +} = dual< + /** + * @since 2.0.0 + * @category elements + */ + (key: K) => (self: MutableHashMap) => Option.Option, + /** + * @since 2.0.0 + * @category elements + */ + (self: MutableHashMap, key: K) => Option.Option +>(2, (self: MutableHashMap, key: K): Option.Option => { + if (Equal.isEqual(key) === false) { + return self.referential.has(key) ? Option.some(self.referential.get(key)!) : Option.none() + } + + const hash = key[Hash.symbol]() + const bucket = self.buckets.get(hash) + if (bucket === undefined) { + return Option.none() + } + + return getFromBucket(self, bucket, key) +}) + +/** + * @since 3.8.0 + * @category elements + */ +export const keys = (self: MutableHashMap): Array => { + const keys = Array.from(self.referential.keys()) + for (const bucket of self.buckets.values()) { + for (let i = 0, len = bucket.length; i < len; i++) { + keys.push(bucket[i][0]) + } + } + return keys +} + +/** + * @since 3.8.0 + * @category elements + */ +export const values = (self: MutableHashMap): Array => { + const values = Array.from(self.referential.values()) + for (const bucket of self.buckets.values()) { + for (let i = 0, len = bucket.length; i < len; i++) { + values.push(bucket[i][1]) + } + } + return values +} + +const getFromBucket = ( + self: MutableHashMap, + bucket: NonEmptyArray, + key: K & Equal.Equal, + remove = false +): Option.Option => { + for (let i = 0, len = bucket.length; i < len; i++) { + if (key[Equal.symbol](bucket[i][0])) { + const value = bucket[i][1] + if (remove) { + bucket.splice(i, 1) + self.bucketsSize-- + } + return Option.some(value) + } + } + + return Option.none() +} + +/** + * @since 2.0.0 + * @category elements + */ +export const has: { + /** + * @since 2.0.0 + * @category elements + */ + (key: K): (self: MutableHashMap) => boolean + /** + * @since 2.0.0 + * @category elements + */ + (self: MutableHashMap, key: K): boolean +} = dual< + /** + * @since 2.0.0 + * @category elements + */ + (key: K) => (self: MutableHashMap) => boolean, + /** + * @since 2.0.0 + * @category elements + */ + (self: MutableHashMap, key: K) => boolean +>(2, (self, key) => Option.isSome(get(self, key))) + +/** + * @since 2.0.0 + */ +export const set: { + /** + * @since 2.0.0 + */ + (key: K, value: V): (self: MutableHashMap) => MutableHashMap + /** + * @since 2.0.0 + */ + (self: MutableHashMap, key: K, value: V): MutableHashMap +} = dual< + /** + * @since 2.0.0 + */ + (key: K, value: V) => (self: MutableHashMap) => MutableHashMap, + /** + * @since 2.0.0 + */ + (self: MutableHashMap, key: K, value: V) => MutableHashMap +>(3, (self: MutableHashMap, key: K, value: V) => { + if (Equal.isEqual(key) === false) { + self.referential.set(key, value) + return self + } + + const hash = key[Hash.symbol]() + const bucket = self.buckets.get(hash) + if (bucket === undefined) { + self.buckets.set(hash, [[key, value]]) + self.bucketsSize++ + return self + } + + removeFromBucket(self, bucket, key) + bucket.push([key, value]) + self.bucketsSize++ + return self +}) + +const removeFromBucket = ( + self: MutableHashMap, + bucket: NonEmptyArray, + key: K & Equal.Equal +) => { + for (let i = 0, len = bucket.length; i < len; i++) { + if (key[Equal.symbol](bucket[i][0])) { + bucket.splice(i, 1) + self.bucketsSize-- + return + } + } +} + +/** + * Updates the value of the specified key within the `MutableHashMap` if it exists. + * + * @since 2.0.0 + */ +export const modify: { + /** + * Updates the value of the specified key within the `MutableHashMap` if it exists. + * + * @since 2.0.0 + */ + (key: K, f: (v: V) => V): (self: MutableHashMap) => MutableHashMap + /** + * Updates the value of the specified key within the `MutableHashMap` if it exists. + * + * @since 2.0.0 + */ + (self: MutableHashMap, key: K, f: (v: V) => V): MutableHashMap +} = dual< + /** + * Updates the value of the specified key within the `MutableHashMap` if it exists. + * + * @since 2.0.0 + */ + (key: K, f: (v: V) => V) => (self: MutableHashMap) => MutableHashMap, + /** + * Updates the value of the specified key within the `MutableHashMap` if it exists. + * + * @since 2.0.0 + */ + (self: MutableHashMap, key: K, f: (v: V) => V) => MutableHashMap +>(3, (self: MutableHashMap, key: K, f: (v: V) => V) => { + if (Equal.isEqual(key) === false) { + if (self.referential.has(key)) { + self.referential.set(key, f(self.referential.get(key)!)) + } + return self + } + + const hash = key[Hash.symbol]() + const bucket = self.buckets.get(hash) + if (bucket === undefined) { + return self + } + + const value = getFromBucket(self, bucket, key, true) + if (Option.isNone(value)) { + return self + } + bucket.push([key, f(value.value)]) + self.bucketsSize++ + return self +}) + +/** + * Set or remove the specified key in the `MutableHashMap` using the specified + * update function. + * + * @since 2.0.0 + */ +export const modifyAt: { + /** + * Set or remove the specified key in the `MutableHashMap` using the specified + * update function. + * + * @since 2.0.0 + */ + (key: K, f: (value: Option.Option) => Option.Option): (self: MutableHashMap) => MutableHashMap + /** + * Set or remove the specified key in the `MutableHashMap` using the specified + * update function. + * + * @since 2.0.0 + */ + ( + self: MutableHashMap, + key: K, + f: (value: Option.Option) => Option.Option + ): MutableHashMap +} = dual< + /** + * Set or remove the specified key in the `MutableHashMap` using the specified + * update function. + * + * @since 2.0.0 + */ + (key: K, f: (value: Option.Option) => Option.Option) => (self: MutableHashMap) => MutableHashMap, + /** + * Set or remove the specified key in the `MutableHashMap` using the specified + * update function. + * + * @since 2.0.0 + */ + ( + self: MutableHashMap, + key: K, + f: (value: Option.Option) => Option.Option + ) => MutableHashMap +>(3, (self, key, f) => { + if (Equal.isEqual(key) === false) { + const result = f(get(self, key)) + if (Option.isSome(result)) { + set(self, key, result.value) + } else { + remove(self, key) + } + return self + } + + const hash = key[Hash.symbol]() + const bucket = self.buckets.get(hash) + if (bucket === undefined) { + const result = f(Option.none()) + return Option.isSome(result) ? set(self, key, result.value) : self + } + + const result = f(getFromBucket(self, bucket, key, true)) + if (Option.isNone(result)) { + if (bucket.length === 0) { + self.buckets.delete(hash) + } + return self + } + bucket.push([key, result.value]) + self.bucketsSize++ + return self +}) + +/** + * @since 2.0.0 + */ +export const remove: { + /** + * @since 2.0.0 + */ + (key: K): (self: MutableHashMap) => MutableHashMap + /** + * @since 2.0.0 + */ + (self: MutableHashMap, key: K): MutableHashMap +} = dual< + /** + * @since 2.0.0 + */ + (key: K) => (self: MutableHashMap) => MutableHashMap, + /** + * @since 2.0.0 + */ + (self: MutableHashMap, key: K) => MutableHashMap +>(2, (self: MutableHashMap, key: K) => { + if (Equal.isEqual(key) === false) { + self.referential.delete(key) + return self + } + + const hash = key[Hash.symbol]() + const bucket = self.buckets.get(hash) + if (bucket === undefined) { + return self + } + removeFromBucket(self, bucket, key) + if (bucket.length === 0) { + self.buckets.delete(hash) + } + return self +}) + +/** + * @since 2.0.0 + */ +export const clear = (self: MutableHashMap) => { + self.referential.clear() + self.buckets.clear() + self.bucketsSize = 0 + return self +} + +/** + * @since 2.0.0 + * @category elements + */ +export const size = (self: MutableHashMap): number => { + return self.referential.size + self.bucketsSize +} + +/** + * @since 2.0.0 + */ +export const isEmpty = (self: MutableHashMap): boolean => size(self) === 0 + +/** + * @since 2.0.0 + */ +export const forEach: { + /** + * @since 2.0.0 + */ + (f: (value: V, key: K) => void): (self: MutableHashMap) => void + /** + * @since 2.0.0 + */ + (self: MutableHashMap, f: (value: V, key: K) => void): void +} = dual(2, (self: MutableHashMap, f: (value: V, key: K) => void) => { + self.referential.forEach(f) + for (const bucket of self.buckets.values()) { + for (const [key, value] of bucket) { + f(value, key) + } + } +}) diff --git a/backend/node_modules/effect/src/NonEmptyIterable.ts b/backend/node_modules/effect/src/NonEmptyIterable.ts new file mode 100644 index 0000000000000000000000000000000000000000..c798360172c7f4e803da2725a487f101e7c516ce --- /dev/null +++ b/backend/node_modules/effect/src/NonEmptyIterable.ts @@ -0,0 +1,32 @@ +/** + * @since 2.0.0 + */ + +/** + * @category symbol + * @since 2.0.0 + */ +export declare const nonEmpty: unique symbol + +/** + * @category model + * @since 2.0.0 + */ +export interface NonEmptyIterable extends Iterable { + readonly [nonEmpty]: A +} + +/** + * @category getters + * @since 2.0.0 + */ +export const unprepend = (self: NonEmptyIterable): [firstElement: A, remainingElements: Iterator] => { + const iterator = self[Symbol.iterator]() + const next = iterator.next() + if (next.done) { + throw new Error( + "BUG: NonEmptyIterator should not be empty - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + } + return [next.value, iterator] +} diff --git a/backend/node_modules/effect/src/Number.ts b/backend/node_modules/effect/src/Number.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d2b25945bcecff6deba475a0376a2b5eebadbce --- /dev/null +++ b/backend/node_modules/effect/src/Number.ts @@ -0,0 +1,1440 @@ +/** + * # Number + * + * This module provides utility functions and type class instances for working + * with the `number` type in TypeScript. It includes functions for basic + * arithmetic operations, as well as type class instances for `Equivalence` and + * `Order`. + * + * ## Operations Reference + * + * | Category | Operation | Description | Domain | Co-domain | + * | ------------ | ------------------------------------------ | ------------------------------------------------------- | ------------------------------ | --------------------- | + * | constructors | {@link module:Number.parse} | Safely parses a string to a number | `string` | `Option` | + * | | | | | | + * | math | {@link module:Number.sum} | Adds two numbers | `number`, `number` | `number` | + * | math | {@link module:Number.sumAll} | Sums all numbers in a collection | `Iterable` | `number` | + * | math | {@link module:Number.subtract} | Subtracts one number from another | `number`, `number` | `number` | + * | math | {@link module:Number.multiply} | Multiplies two numbers | `number`, `number` | `number` | + * | math | {@link module:Number.multiplyAll} | Multiplies all numbers in a collection | `Iterable` | `number` | + * | math | {@link module:Number.divide} | Safely divides handling division by zero | `number`, `number` | `Option` | + * | math | {@link module:Number.unsafeDivide} | Divides but misbehaves for division by zero | `number`, `number` | `number` | + * | math | {@link module:Number.remainder} | Calculates remainder of division | `number`, `number` | `number` | + * | math | {@link module:Number.increment} | Adds 1 to a number | `number` | `number` | + * | math | {@link module:Number.decrement} | Subtracts 1 from a number | `number` | `number` | + * | math | {@link module:Number.sign} | Determines the sign of a number | `number` | `Ordering` | + * | math | {@link module:Number.nextPow2} | Finds the next power of 2 | `number` | `number` | + * | math | {@link module:Number.round} | Rounds a number with specified precision | `number`, `number` | `number` | + * | | | | | | + * | predicates | {@link module:Number.between} | Checks if a number is in a range | `number`, `{minimum, maximum}` | `boolean` | + * | predicates | {@link module:Number.lessThan} | Checks if one number is less than another | `number`, `number` | `boolean` | + * | predicates | {@link module:Number.lessThanOrEqualTo} | Checks if one number is less than or equal | `number`, `number` | `boolean` | + * | predicates | {@link module:Number.greaterThan} | Checks if one number is greater than another | `number`, `number` | `boolean` | + * | predicates | {@link module:Number.greaterThanOrEqualTo} | Checks if one number is greater or equal | `number`, `number` | `boolean` | + * | | | | | | + * | guards | {@link module:Number.isNumber} | Type guard for JavaScript numbers | `unknown` | `boolean` | + * | | | | | | + * | comparison | {@link module:Number.min} | Returns the minimum of two numbers | `number`, `number` | `number` | + * | comparison | {@link module:Number.max} | Returns the maximum of two numbers | `number`, `number` | `number` | + * | comparison | {@link module:Number.clamp} | Restricts a number to a range | `number`, `{minimum, maximum}` | `number` | + * | | | | | | + * | instances | {@link module:Number.Equivalence} | Equivalence instance for numbers | | `Equivalence` | + * | instances | {@link module:Number.Order} | Order instance for numbers | | `Order` | + * | | | | | | + * | errors | {@link module:Number.DivisionByZeroError} | Error thrown by unsafeDivide | | | + * + * ## Composition Patterns and Type Safety + * + * When building function pipelines, understanding how types flow through + * operations is critical: + * + * ### Composing with type-preserving operations + * + * Most operations in this module are type-preserving (`number → number`), + * making them easily composable in pipelines: + * + * ```ts + * import { pipe } from "effect" + * import * as Number from "effect/Number" + * + * const result = pipe( + * 10, + * Number.increment, // number → number + * Number.multiply(2), // number → number + * Number.round(1) // number → number + * ) // Result: number (21) + * ``` + * + * ### Working with Option results + * + * Operations that might fail (like division by zero) return Option types and + * require Option combinators: + * + * ```ts + * import { pipe, Option } from "effect" + * import * as Number from "effect/Number" + * + * const result = pipe( + * 10, + * Number.divide(0), // number → Option + * Option.getOrElse(() => 0) // Option → number + * ) // Result: number (0) + * ``` + * + * ### Composition best practices + * + * - Chain type-preserving operations for maximum composability + * - Use Option combinators when working with potentially failing operations + * - Consider using Effect for operations that might fail with specific errors + * - Remember that all operations maintain JavaScript's floating-point precision + * limitations + * + * @module Number + * @since 2.0.0 + * @see {@link module:BigInt} for more similar operations on `bigint` types + * @see {@link module:BigDecimal} for more similar operations on `BigDecimal` types + */ + +import * as equivalence from "./Equivalence.js" +import { dual } from "./Function.js" +import * as option from "./internal/option.js" +import * as _Iterable from "./Iterable.js" +import type { Option } from "./Option.js" +import * as order from "./Order.js" +import type { Ordering } from "./Ordering.js" +import * as predicate from "./Predicate.js" + +/** + * Type guard that tests if a value is a member of the set of JavaScript + * numbers. + * + * @memberof Number + * @since 2.0.0 + * @category guards + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import * as Number from "effect/Number" + * + * // Regular numbers + * assert.equal(Number.isNumber(2), true) + * assert.equal(Number.isNumber(-3.14), true) + * assert.equal(Number.isNumber(0), true) + * + * // Special numeric values + * assert.equal(Number.isNumber(Infinity), true) + * assert.equal(Number.isNumber(NaN), true) + * + * // Non-number values + * assert.equal(Number.isNumber("2"), false) + * assert.equal(Number.isNumber(true), false) + * assert.equal(Number.isNumber(null), false) + * assert.equal(Number.isNumber(undefined), false) + * assert.equal(Number.isNumber({}), false) + * assert.equal(Number.isNumber([]), false) + * + * // Using as a type guard in conditionals + * function processValue(value: unknown): string { + * if (Number.isNumber(value)) { + * // TypeScript now knows 'value' is a number + * return `Numeric value: ${value.toFixed(2)}` + * } + * return "Not a number" + * } + * + * assert.strictEqual(processValue(42), "Numeric value: 42.00") + * assert.strictEqual(processValue("hello"), "Not a number") + * + * // Filtering for numbers in an array + * const mixed = [1, "two", 3, false, 5] + * const onlyNumbers = mixed.filter(Number.isNumber) + * assert.equal(onlyNumbers, [1, 3, 5]) + * ``` + * + * @param input - The value to test for membership in the set of JavaScript + * numbers + * + * @returns `true` if the input is a JavaScript number, `false` otherwise + */ +export const isNumber: (input: unknown) => input is number = predicate.isNumber + +/** + * Returns the additive inverse of a number, effectively negating it. + * + * @memberof Number + * @since 3.14.6 + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { pipe } from "effect" + * import * as Number from "effect/Number" + * + * assert.equal( + * Number.negate(5), // + * -5 + * ) + * + * assert.equal( + * Number.negate(-5), // + * 5 + * ) + * + * assert.equal( + * Number.negate(0), // + * 0 + * ) + * ``` + * + * @param n - The number value to be negated. + * + * @returns The negated number value. + */ +export const negate = (n: number): number => multiply(n, -1) + +/** + * Performs addition in the set of JavaScript numbers. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { pipe } from "effect" + * import * as Number from "effect/Number" + * + * // Data-first style (direct application) + * assert.equal(Number.sum(2, 3), 5) + * assert.equal(Number.sum(-10, 5), -5) + * assert.equal(Number.sum(0.1, 0.2), 0.30000000000000004) // Note: floating-point precision limitation + * + * // Data-last style (pipeable) + * assert.equal( + * pipe( + * 10, + * Number.sum(5) // 10 + 5 = 15 + * ), + * 15 + * ) + * + * // Chaining multiple additions + * assert.equal( + * pipe( + * 1, + * Number.sum(2), // 1 + 2 = 3 + * Number.sum(3), // 3 + 3 = 6 + * Number.sum(4) // 6 + 4 = 10 + * ), + * 10 + * ) + * + * // Identity property: a + 0 = a + * assert.equal(Number.sum(42, 0), 42) + * + * // Commutative property: a + b = b + a + * assert.equal(Number.sum(5, 3), Number.sum(3, 5)) + * ``` + */ +export const sum: { + /** + * Returns a function that adds a specified number to its argument. + * + * @param that - The number to add to the input of the resulting function + * + * @returns A function that takes a number and returns the sum of that number + * and `that` + */ + (that: number): (self: number) => number + + /** + * Adds two numbers together. + * + * @param self - The first addend + * @param that - The second addend + * + * @returns The sum of the two numbers + */ + (self: number, that: number): number +} = dual(2, (self: number, that: number): number => self + that) + +/** + * Computes the sum of all elements in an iterable collection of numbers. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import * as Number from "effect/Number" + * + * // Basic sums + * assert.equal(Number.sumAll([2, 3, 4]), 9) // 2 + 3 + 4 = 9 + * assert.equal(Number.sumAll([1.1, 2.2, 3.3]), 6.6) // 1.1 + 2.2 + 3.3 = 6.6 + * + * // Empty collection returns the additive identity (0) + * assert.equal(Number.sumAll([]), 0) + * + * // Single element collection + * assert.equal(Number.sumAll([42]), 42) + * + * // Sums with negative numbers + * assert.equal(Number.sumAll([2, -3, 4]), 3) // 2 + (-3) + 4 = 3 + * assert.equal(Number.sumAll([-2, -3, -4]), -9) // (-2) + (-3) + (-4) = -9 + * + * // Works with any iterable + * assert.equal(Number.sumAll(new Set([2, 3, 4])), 9) + * + * // Using with generated sequences + * function* range(start: number, end: number) { + * for (let i = start; i <= end; i++) yield i + * } + * + * // Compute sum of first 5 natural numbers: 1 + 2 + 3 + 4 + 5 = 15 + * assert.equal(Number.sumAll(range(1, 5)), 15) + * + * // Floating point precision example + * assert.equal( + * Number.sumAll([0.1, 0.2]), + * 0.30000000000000004 // Note IEEE 754 precision limitation + * ) + * ``` + * + * @param collection - An `iterable` containing the `numbers` to sum + * + * @returns The sum of all numbers in the collection, or 0 if the collection is + * empty + */ +export const sumAll = (collection: Iterable): number => _Iterable.reduce(collection, 0, sum) + +/** + * Performs subtraction in the set of JavaScript numbers. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { pipe } from "effect" + * import * as Number from "effect/Number" + * + * // Data-first style (direct application) + * assert.equal(Number.subtract(2, 3), -1) // 2 - 3 = -1 + * assert.equal(Number.subtract(10, 5), 5) // 10 - 5 = 5 + * assert.equal(Number.subtract(0.3, 0.1), 0.19999999999999998) // Note: floating-point precision limitation + * + * // Data-last style (pipeable) + * assert.equal( + * pipe( + * 10, + * Number.subtract(5) // 10 - 5 = 5 + * ), + * 5 + * ) + * + * // Chaining multiple subtractions + * assert.equal( + * pipe( + * 20, + * Number.subtract(5), // 20 - 5 = 15 + * Number.subtract(3), // 15 - 3 = 12 + * Number.subtract(2) // 12 - 2 = 10 + * ), + * 10 + * ) + * + * // Right identity property: a - 0 = a + * assert.equal(Number.subtract(42, 0), 42) + * + * // Self-annihilation property: a - a = 0 + * assert.equal(Number.subtract(42, 42), 0) + * + * // Non-commutative property: a - b ≠ b - a + * assert.equal(Number.subtract(5, 3), 2) // 5 - 3 = 2 + * assert.equal(Number.subtract(3, 5), -2) // 3 - 5 = -2 + * + * // Inverse relation: a - b = -(b - a) + * assert.equal(Number.subtract(5, 3), -Number.subtract(3, 5)) + * ``` + */ +export const subtract: { + /** + * Returns a function that subtracts a specified number from its argument. + * + * @param subtrahend - The number to subtract from the input of the resulting + * function + * + * @returns A function that takes a minuend and returns the difference of + * subtracting the subtrahend from it + */ + (subtrahend: number): (minuend: number) => number + + /** + * Subtracts the subtrahend from the minuend and returns the difference. + * + * @param minuend - The number from which another number is to be subtracted + * @param subtrahend - The number to subtract from the minuend + * + * @returns The difference of the minuend minus the subtrahend + */ + (minuend: number, subtrahend: number): number +} = dual( + 2, + (minuend: number, subtrahend: number): number => minuend - subtrahend +) + +/** + * Performs **multiplication** in the set of JavaScript numbers. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { pipe } from "effect" + * import * as Number from "effect/Number" + * + * // Data-first style (direct application) + * assert.equal(Number.multiply(2, 3), 6) // 2 × 3 = 6 + * assert.equal(Number.multiply(-4, 5), -20) // (-4) × 5 = -20 + * assert.equal(Number.multiply(-3, -2), 6) // (-3) × (-2) = 6 + * assert.equal(Number.multiply(0.1, 0.2), 0.020000000000000004) // Note: floating-point precision limitation + * + * // Data-last style (pipeable) + * assert.equal( + * pipe( + * 10, + * Number.multiply(5) // 10 × 5 = 50 + * ), + * 50 + * ) + * + * // Chaining multiple multiplications + * assert.equal( + * pipe( + * 2, + * Number.multiply(3), // 2 × 3 = 6 + * Number.multiply(4), // 6 × 4 = 24 + * Number.multiply(0.5) // 24 × 0.5 = 12 + * ), + * 12 + * ) + * + * // Identity property: a × 1 = a + * assert.equal(Number.multiply(42, 1), 42) + * + * // Zero property: a × 0 = 0 + * assert.equal(Number.multiply(42, 0), 0) + * + * // Commutative property: a × b = b × a + * assert.equal(Number.multiply(5, 3), Number.multiply(3, 5)) + * + * // Associative property: (a × b) × c = a × (b × c) + * const a = 2, + * b = 3, + * c = 4 + * assert.equal( + * Number.multiply(Number.multiply(a, b), c), + * Number.multiply(a, Number.multiply(b, c)) + * ) + * ``` + */ +export const multiply: { + /** + * Returns a function that multiplies a specified number with its argument. + * + * @param multiplicand - The number to multiply with the input of the + * resulting function + * + * @returns A function that takes a multiplier and returns the product of that + * multiplier and the multiplicand + */ + (multiplicand: number): (multiplier: number) => number + + /** + * Multiplies two numbers together. + * + * @param multiplier - The first factor + * @param multiplicand - The second factor + * + * @returns The product of the two numbers + */ + (multiplier: number, multiplicand: number): number +} = dual( + 2, + (multiplier: number, multiplicand: number): number => multiplier * multiplicand +) + +/** + * Computes the product of all elements in an iterable collection of numbers. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import * as Number from "effect/Number" + * + * // Basic products + * assert.equal(Number.multiplyAll([2, 3, 4]), 24) // 2 × 3 × 4 = 24 + * assert.equal(Number.multiplyAll([1.5, 2, 3]), 9) // 1.5 × 2 × 3 = 9 + * + * // Empty collection returns the multiplicative identity (1) + * assert.equal(Number.multiplyAll([]), 1) + * + * // Single element collection + * assert.equal(Number.multiplyAll([42]), 42) + * + * // Products with negative numbers + * assert.equal(Number.multiplyAll([2, -3, 4]), -24) // 2 × (-3) × 4 = -24 + * assert.equal(Number.multiplyAll([-2, -3]), 6) // (-2) × (-3) = 6 + * + * // Zero property - if any element is zero, product is zero + * assert.equal(Number.multiplyAll([2, 0, 3]), 0) + * + * // Works with any iterable + * assert.equal(Number.multiplyAll(new Set([2, 3, 4])), 24) + * + * // Using with generated sequences + * function* range(start: number, end: number) { + * for (let i = start; i <= end; i++) yield i + * } + * + * // Compute factorial: 5! = 5 × 4 × 3 × 2 × 1 = 120 + * assert.equal(Number.multiplyAll(range(1, 5)), 120) + * ``` + * + * @param collection - An `iterable` containing the `numbers` to multiply + * + * @returns The product of all numbers in the collection, or 1 if the collection + * is empty + */ +export const multiplyAll = (collection: Iterable): number => { + let out = 1 + for (const n of collection) { + if (n === 0) { + return 0 + } + out *= n + } + return out +} + +/** + * Performs division in the set of JavaScript numbers, returning the result + * wrapped in an `Option` to handle division by zero. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { pipe, Option } from "effect" + * import * as Number from "effect/Number" + * + * // Data-first style (direct application) + * assert.equal(Number.divide(6, 3), Option.some(2)) // 6 ÷ 3 = 2 + * assert.equal(Number.divide(-8, 4), Option.some(-2)) // (-8) ÷ 4 = -2 + * assert.equal(Number.divide(-10, -5), Option.some(2)) // (-10) ÷ (-5) = 2 + * assert.equal(Number.divide(1, 3), Option.some(0.3333333333333333)) // Note: floating-point approximation + * + * // Handling division by zero + * assert.equal(Number.divide(6, 0), Option.none()) // 6 ÷ 0 is undefined + * + * // Data-last style (pipeable) + * assert.equal( + * pipe( + * 10, + * Number.divide(2) // 10 ÷ 2 = 5 + * ), + * Option.some(5) + * ) + * + * // Chaining multiple divisions using Option combinators + * assert.equal( + * pipe( + * Option.some(24), + * Option.flatMap((n) => Number.divide(n, 2)), // 24 ÷ 2 = 12 + * Option.flatMap(Number.divide(3)), // 12 ÷ 3 = 4 + * Option.flatMap(Number.divide(2)) // 4 ÷ 2 = 2 + * ), + * Option.some(2) + * ) + * + * // Division-by-one property: a ÷ 1 = a + * assert.equal(Number.divide(42, 1), Option.some(42)) + * + * // Self-division property: a ÷ a = 1 (for a ≠ 0) + * assert.equal(Number.divide(42, 42), Option.some(1)) + * + * // Non-commutative property: a ÷ b ≠ b ÷ a + * assert.notDeepStrictEqual( + * Number.divide(6, 3), // 6 ÷ 3 = 2 + * Number.divide(3, 6) // 3 ÷ 6 = 0.5 + * ) + * ``` + */ +export const divide: { + /** + * Returns a function that divides its input by a specified divisor. + * + * @param divisor - The number to divide by + * + * @returns A function that takes a dividend and returns the quotient wrapped + * in an Option (Option.none() if divisor is 0) + */ + (divisor: number): (dividend: number) => Option + + /** + * Divides the dividend by the divisor and returns the quotient wrapped in an + * Option. + * + * @param dividend - The number to be divided + * @param divisor - The number to divide by + * + * @returns Some(quotient) if the divisor is not 0, None otherwise + */ + (dividend: number, divisor: number): Option +} = dual(2, (dividend: number, divisor: number) => divisor === 0 ? option.none : option.some(dividend / divisor)) + +/** + * Performs division in the set of JavaScript numbers, but misbehaves for + * division by zero. + * + * Unlike {@link module:Number.divide} which returns an Option, this function + * directly returns a number or `Infinity` or `NaN`. + * + * - If the `divisor` is zero, it returns `Infinity`. + * - If both the `dividend` and the `divisor` are zero, then it returns `NaN`. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { pipe } from "effect" + * import * as Number from "effect/Number" + * + * // Data-first style (direct application) + * assert.equal(Number.unsafeDivide(6, 3), 2) // 6 ÷ 3 = 2 + * assert.equal(Number.unsafeDivide(-8, 4), -2) // (-8) ÷ 4 = -2 + * assert.equal(Number.unsafeDivide(-10, -5), 2) // (-10) ÷ (-5) = 2 + * assert.equal(Number.unsafeDivide(1, 3), 0.3333333333333333) + * + * // Data-last style (pipeable) + * assert.equal( + * pipe( + * 10, + * Number.unsafeDivide(2) // 10 ÷ 2 = 5 + * ), + * 5 + * ) + * + * // Chaining multiple divisions + * assert.equal( + * pipe( + * 24, + * Number.unsafeDivide(2), // 24 ÷ 2 = 12 + * Number.unsafeDivide(3), // 12 ÷ 3 = 4 + * Number.unsafeDivide(2) // 4 ÷ 2 = 2 + * ), + * 2 + * ) + * + * assert.equal(Number.unsafeDivide(6, 0), Infinity) + * + * assert.equal(Number.unsafeDivide(0, 0), NaN) + * + * // Compare with safe division + * const safeResult = Number.divide(6, 3) // Option.some(2) + * const unsafeResult = Number.unsafeDivide(6, 3) // 2 directly + * ``` + * + * @throws - An {@link module:Number.DivisionByZeroError} if the divisor is zero. + * @see {@link module:Number.divide} - Safe division returning an Option + */ +export const unsafeDivide: { + /** + * Returns a function that divides its input by a specified divisor. + * + * @param divisor - The number to divide by + * + * @returns A function that takes a dividend and returns the quotient + * @throws - An {@link module:Number.DivisionByZeroError} if the divisor is + * zero + */ + (divisor: number): (dividend: number) => number + + /** + * Divides the dividend by the divisor and returns the quotient. + * + * If the divisor is zero, it returns Infinity. + * + * @param dividend - The number to be divided + * @param divisor - The number to divide by + * + * @returns The quotient of the division + */ + (dividend: number, divisor: number): number +} = dual(2, (dividend: number, divisor: number): number => dividend / divisor) + +/** + * Returns the result of adding `1` to a given number. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { increment } from "effect/Number" + * + * assert.equal(increment(2), 3) + * ``` + */ +export const increment = (n: number): number => sum(n, 1) + +/** + * Decrements a number by `1`. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { decrement } from "effect/Number" + * + * assert.equal(decrement(3), 2) + * ``` + */ +export const decrement = (n: number): number => subtract(n, 1) + +/** + * @memberof Number + * @since 2.0.0 + * @category instances + */ +export const Equivalence: equivalence.Equivalence = equivalence.number + +/** + * @memberof Number + * @since 2.0.0 + * @category instances + */ +export const Order: order.Order = order.number + +/** + * Returns `true` if the first argument is less than the second, otherwise + * `false`. + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { lessThan } from "effect/Number" + * + * assert.equal(lessThan(2, 3), true) + * assert.equal(lessThan(3, 3), false) + * assert.equal(lessThan(4, 3), false) + * ``` + */ +export const lessThan: { + /** + * Returns `true` if the first argument is less than the second, otherwise + * `false`. + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { lessThan } from "effect/Number" + * + * assert.equal(lessThan(2, 3), true) + * assert.equal(lessThan(3, 3), false) + * assert.equal(lessThan(4, 3), false) + * ``` + */ + (that: number): (self: number) => boolean + /** + * Returns `true` if the first argument is less than the second, otherwise + * `false`. + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { lessThan } from "effect/Number" + * + * assert.equal(lessThan(2, 3), true) + * assert.equal(lessThan(3, 3), false) + * assert.equal(lessThan(4, 3), false) + * ``` + */ + (self: number, that: number): boolean +} = order.lessThan(Order) + +/** + * Returns a function that checks if a given `number` is less than or equal to + * the provided one. + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { lessThanOrEqualTo } from "effect/Number" + * + * assert.equal(lessThanOrEqualTo(2, 3), true) + * assert.equal(lessThanOrEqualTo(3, 3), true) + * assert.equal(lessThanOrEqualTo(4, 3), false) + * ``` + */ +export const lessThanOrEqualTo: { + /** + * Returns a function that checks if a given `number` is less than or equal to + * the provided one. + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { lessThanOrEqualTo } from "effect/Number" + * + * assert.equal(lessThanOrEqualTo(2, 3), true) + * assert.equal(lessThanOrEqualTo(3, 3), true) + * assert.equal(lessThanOrEqualTo(4, 3), false) + * ``` + */ + (that: number): (self: number) => boolean + /** + * Returns a function that checks if a given `number` is less than or equal to + * the provided one. + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { lessThanOrEqualTo } from "effect/Number" + * + * assert.equal(lessThanOrEqualTo(2, 3), true) + * assert.equal(lessThanOrEqualTo(3, 3), true) + * assert.equal(lessThanOrEqualTo(4, 3), false) + * ``` + */ + (self: number, that: number): boolean +} = order.lessThanOrEqualTo(Order) + +/** + * Returns `true` if the first argument is greater than the second, otherwise + * `false`. + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { greaterThan } from "effect/Number" + * + * assert.equal(greaterThan(2, 3), false) + * assert.equal(greaterThan(3, 3), false) + * assert.equal(greaterThan(4, 3), true) + * ``` + */ +export const greaterThan: { + /** + * Returns `true` if the first argument is greater than the second, otherwise + * `false`. + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { greaterThan } from "effect/Number" + * + * assert.equal(greaterThan(2, 3), false) + * assert.equal(greaterThan(3, 3), false) + * assert.equal(greaterThan(4, 3), true) + * ``` + */ + (that: number): (self: number) => boolean + /** + * Returns `true` if the first argument is greater than the second, otherwise + * `false`. + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { greaterThan } from "effect/Number" + * + * assert.equal(greaterThan(2, 3), false) + * assert.equal(greaterThan(3, 3), false) + * assert.equal(greaterThan(4, 3), true) + * ``` + */ + (self: number, that: number): boolean +} = order.greaterThan(Order) + +/** + * Returns a function that checks if a given `number` is greater than or equal + * to the provided one. + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { greaterThanOrEqualTo } from "effect/Number" + * + * assert.equal(greaterThanOrEqualTo(2, 3), false) + * assert.equal(greaterThanOrEqualTo(3, 3), true) + * assert.equal(greaterThanOrEqualTo(4, 3), true) + * ``` + */ +export const greaterThanOrEqualTo: { + /** + * Returns a function that checks if a given `number` is greater than or equal + * to the provided one. + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { greaterThanOrEqualTo } from "effect/Number" + * + * assert.equal(greaterThanOrEqualTo(2, 3), false) + * assert.equal(greaterThanOrEqualTo(3, 3), true) + * assert.equal(greaterThanOrEqualTo(4, 3), true) + * ``` + */ + (that: number): (self: number) => boolean + /** + * Returns a function that checks if a given `number` is greater than or equal + * to the provided one. + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { greaterThanOrEqualTo } from "effect/Number" + * + * assert.equal(greaterThanOrEqualTo(2, 3), false) + * assert.equal(greaterThanOrEqualTo(3, 3), true) + * assert.equal(greaterThanOrEqualTo(4, 3), true) + * ``` + */ + (self: number, that: number): boolean +} = order.greaterThanOrEqualTo(Order) + +/** + * Checks if a `number` is between a `minimum` and `maximum` value (inclusive). + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { Number } from "effect" + * + * const between = Number.between({ minimum: 0, maximum: 5 }) + * + * assert.equal(between(3), true) + * assert.equal(between(-1), false) + * assert.equal(between(6), false) + * ``` + */ +export const between: { + /** + * Checks if a `number` is between a `minimum` and `maximum` value (inclusive). + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { Number } from "effect" + * + * const between = Number.between({ minimum: 0, maximum: 5 }) + * + * assert.equal(between(3), true) + * assert.equal(between(-1), false) + * assert.equal(between(6), false) + * ``` + */ + (options: { minimum: number; maximum: number }): (self: number) => boolean + /** + * Checks if a `number` is between a `minimum` and `maximum` value (inclusive). + * + * @memberof Number + * @since 2.0.0 + * @category predicates + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { Number } from "effect" + * + * const between = Number.between({ minimum: 0, maximum: 5 }) + * + * assert.equal(between(3), true) + * assert.equal(between(-1), false) + * assert.equal(between(6), false) + * ``` + */ + ( + self: number, + options: { + minimum: number + maximum: number + } + ): boolean +} = order.between(Order) + +/** + * Restricts the given `number` to be within the range specified by the + * `minimum` and `maximum` values. + * + * - If the `number` is less than the `minimum` value, the function returns the + * `minimum` value. + * - If the `number` is greater than the `maximum` value, the function returns the + * `maximum` value. + * - Otherwise, it returns the original `number`. + * + * @memberof Number + * @since 2.0.0 + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { Number } from "effect" + * + * const clamp = Number.clamp({ minimum: 1, maximum: 5 }) + * + * assert.equal(clamp(3), 3) + * assert.equal(clamp(0), 1) + * assert.equal(clamp(6), 5) + * ``` + */ +export const clamp: { + /** + * Restricts the given `number` to be within the range specified by the + * `minimum` and `maximum` values. + * + * - If the `number` is less than the `minimum` value, the function returns the + * `minimum` value. + * - If the `number` is greater than the `maximum` value, the function returns the + * `maximum` value. + * - Otherwise, it returns the original `number`. + * + * @memberof Number + * @since 2.0.0 + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { Number } from "effect" + * + * const clamp = Number.clamp({ minimum: 1, maximum: 5 }) + * + * assert.equal(clamp(3), 3) + * assert.equal(clamp(0), 1) + * assert.equal(clamp(6), 5) + * ``` + */ + (options: { minimum: number; maximum: number }): (self: number) => number + /** + * Restricts the given `number` to be within the range specified by the + * `minimum` and `maximum` values. + * + * - If the `number` is less than the `minimum` value, the function returns the + * `minimum` value. + * - If the `number` is greater than the `maximum` value, the function returns the + * `maximum` value. + * - Otherwise, it returns the original `number`. + * + * @memberof Number + * @since 2.0.0 + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { Number } from "effect" + * + * const clamp = Number.clamp({ minimum: 1, maximum: 5 }) + * + * assert.equal(clamp(3), 3) + * assert.equal(clamp(0), 1) + * assert.equal(clamp(6), 5) + * ``` + */ + ( + self: number, + options: { + minimum: number + maximum: number + } + ): number +} = order.clamp(Order) + +/** + * Returns the minimum between two `number`s. + * + * @memberof Number + * @since 2.0.0 + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { min } from "effect/Number" + * + * assert.equal(min(2, 3), 2) + * ``` + */ +export const min: { + /** + * Returns the minimum between two `number`s. + * + * @memberof Number + * @since 2.0.0 + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { min } from "effect/Number" + * + * assert.equal(min(2, 3), 2) + * ``` + */ + (that: number): (self: number) => number + /** + * Returns the minimum between two `number`s. + * + * @memberof Number + * @since 2.0.0 + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { min } from "effect/Number" + * + * assert.equal(min(2, 3), 2) + * ``` + */ + (self: number, that: number): number +} = order.min(Order) + +/** + * Returns the maximum between two `number`s. + * + * @memberof Number + * @since 2.0.0 + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { max } from "effect/Number" + * + * assert.equal(max(2, 3), 3) + * ``` + */ +export const max: { + /** + * Returns the maximum between two `number`s. + * + * @memberof Number + * @since 2.0.0 + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { max } from "effect/Number" + * + * assert.equal(max(2, 3), 3) + * ``` + */ + (that: number): (self: number) => number + /** + * Returns the maximum between two `number`s. + * + * @memberof Number + * @since 2.0.0 + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { max } from "effect/Number" + * + * assert.equal(max(2, 3), 3) + * ``` + */ + (self: number, that: number): number +} = order.max(Order) + +/** + * Determines the sign of a given `number`. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { sign } from "effect/Number" + * + * assert.equal(sign(-5), -1) + * assert.equal(sign(0), 0) + * assert.equal(sign(5), 1) + * ``` + */ +export const sign = (n: number): Ordering => Order(n, 0) + +/** + * Returns the remainder left over when one operand is divided by a second + * operand. + * + * It always takes the sign of the dividend. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { remainder } from "effect/Number" + * + * assert.equal(remainder(2, 2), 0) + * assert.equal(remainder(3, 2), 1) + * assert.equal(remainder(-4, 2), -0) + * ``` + */ +export const remainder: { + /** + * Returns the remainder left over when one operand is divided by a second + * operand. + * + * It always takes the sign of the dividend. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { remainder } from "effect/Number" + * + * assert.equal(remainder(2, 2), 0) + * assert.equal(remainder(3, 2), 1) + * assert.equal(remainder(-4, 2), -0) + * ``` + */ + (divisor: number): (dividend: number) => number + /** + * Returns the remainder left over when one operand is divided by a second + * operand. + * + * It always takes the sign of the dividend. + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { remainder } from "effect/Number" + * + * assert.equal(remainder(2, 2), 0) + * assert.equal(remainder(3, 2), 1) + * assert.equal(remainder(-4, 2), -0) + * ``` + */ + (dividend: number, divisor: number): number +} = dual(2, (dividend: number, divisor: number): number => { + // https://stackoverflow.com/questions/3966484/why-does-modulus-operator-return-fractional-number-in-javascript/31711034#31711034 + const selfDecCount = (dividend.toString().split(".")[1] || "").length + const divisorDecCount = (divisor.toString().split(".")[1] || "").length + const decCount = selfDecCount > divisorDecCount ? selfDecCount : divisorDecCount + const selfInt = parseInt(dividend.toFixed(decCount).replace(".", "")) + const divisorInt = parseInt(divisor.toFixed(decCount).replace(".", "")) + return (selfInt % divisorInt) / Math.pow(10, decCount) +}) + +/** + * Returns the next power of 2 greater than or equal to the given number. + * + * - For `positive` inputs, returns the smallest power of 2 that is >= the input + * - For `zero`, returns 2 + * - For `negative` inputs, returns NaN (as logarithms of negative numbers are + * undefined) + * - For `NaN` input, returns NaN + * - For `Infinity`, returns Infinity + * + * @memberof Number + * @since 2.0.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { nextPow2 } from "effect/Number" + * + * assert.equal(nextPow2(5), 8) + * assert.equal(nextPow2(17), 32) + * assert.equal(nextPow2(0), 2) + * assert.equal(Number.isNaN(nextPow2(-1)), true) // Negative inputs result in NaN + * ``` + */ +export const nextPow2 = (n: number): number => { + const nextPow = Math.ceil(Math.log(n) / Math.log(2)) + return Math.max(Math.pow(2, nextPow), 2) +} + +/** + * Tries to parse a `number` from a `string` using the `Number()` function. The + * following special string values are supported: "NaN", "Infinity", + * "-Infinity". + * + * @memberof Number + * @since 2.0.0 + * @category constructors + */ +export const parse: { + /** + * Tries to parse a `number` from a `string` using the `Number()` function. The + * following special string values are supported: "NaN", "Infinity", + * "-Infinity". + * + * @memberof Number + * @since 2.0.0 + * @category constructors + */ + (s: string): Option +} = (s) => { + if (s === "NaN") { + return option.some(NaN) + } + if (s === "Infinity") { + return option.some(Infinity) + } + if (s === "-Infinity") { + return option.some(-Infinity) + } + if (s.trim() === "") { + return option.none + } + const n = Number(s) + return Number.isNaN(n) ? option.none : option.some(n) +} + +/** + * Returns the number rounded with the given precision. + * + * @memberof Number + * @since 3.8.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { round } from "effect/Number" + * + * assert.equal(round(1.1234, 2), 1.12) + * assert.equal(round(1.567, 2), 1.57) + * ``` + */ +export const round: { + /** + * Returns the number rounded with the given precision. + * + * @memberof Number + * @since 3.8.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { round } from "effect/Number" + * + * assert.equal(round(1.1234, 2), 1.12) + * assert.equal(round(1.567, 2), 1.57) + * ``` + */ + (precision: number): (self: number) => number + /** + * Returns the number rounded with the given precision. + * + * @memberof Number + * @since 3.8.0 + * @category math + * @example + * + * ```ts + * import * as assert from "node:assert/strict" + * import { round } from "effect/Number" + * + * assert.equal(round(1.1234, 2), 1.12) + * assert.equal(round(1.567, 2), 1.57) + * ``` + */ + (self: number, precision: number): number +} = dual(2, (self: number, precision: number): number => { + const factor = Math.pow(10, precision) + return Math.round(self * factor) / factor +}) diff --git a/backend/node_modules/effect/src/Option.ts b/backend/node_modules/effect/src/Option.ts new file mode 100644 index 0000000000000000000000000000000000000000..56ad289203443ebd00325735a1bf4d2c9377758d --- /dev/null +++ b/backend/node_modules/effect/src/Option.ts @@ -0,0 +1,4028 @@ +/** + * @since 2.0.0 + */ +import type { Either } from "./Either.js" +import * as Equal from "./Equal.js" +import * as Equivalence from "./Equivalence.js" +import type { LazyArg } from "./Function.js" +import { constNull, constUndefined, dual, identity, isFunction } from "./Function.js" +import type { TypeLambda } from "./HKT.js" +import type { Inspectable } from "./Inspectable.js" +import * as doNotation from "./internal/doNotation.js" +import * as either from "./internal/either.js" +import * as option from "./internal/option.js" +import type { Order } from "./Order.js" +import * as order from "./Order.js" +import type { Pipeable } from "./Pipeable.js" +import type { Predicate, Refinement } from "./Predicate.js" +import type { Covariant, NoInfer, NotFunction } from "./Types.js" +import type * as Unify from "./Unify.js" +import * as Gen from "./Utils.js" + +/** + * The `Option` data type represents optional values. An `Option` can either + * be `Some`, containing a value of type `A`, or `None`, representing the + * absence of a value. + * + * **When to Use** + * + * You can use `Option` in scenarios like: + * + * - Using it for initial values + * - Returning values from functions that are not defined for all possible + * inputs (referred to as “partial functions”) + * - Managing optional fields in data structures + * - Handling optional function arguments + * + * @category Models + * @since 2.0.0 + */ +export type Option = None | Some + +/** + * @category Symbols + * @since 2.0.0 + */ +export const TypeId: unique symbol = Symbol.for("effect/Option") + +/** + * @category Symbols + * @since 2.0.0 + */ +export type TypeId = typeof TypeId + +/** + * @category Models + * @since 2.0.0 + */ +export interface None extends Pipeable, Inspectable { + readonly _tag: "None" + readonly _op: "None" + readonly [TypeId]: { + readonly _A: Covariant + } + [Unify.typeSymbol]?: unknown + [Unify.unifySymbol]?: OptionUnify + [Unify.ignoreSymbol]?: OptionUnifyIgnore +} + +/** + * @category Models + * @since 2.0.0 + */ +export interface Some extends Pipeable, Inspectable { + readonly _tag: "Some" + readonly _op: "Some" + readonly value: A + readonly [TypeId]: { + readonly _A: Covariant + } + [Unify.typeSymbol]?: unknown + [Unify.unifySymbol]?: OptionUnify + [Unify.ignoreSymbol]?: OptionUnifyIgnore +} + +/** + * @category Models + * @since 2.0.0 + */ +export interface OptionUnify { + Option?: () => A[Unify.typeSymbol] extends Option | infer _ ? Option : never +} + +/** + * @since 2.0.0 + */ +export declare namespace Option { + /** + * Extracts the type of the value contained in an `Option`. + * + * **Example** (Getting the Value Type of an Option) + * + * ```ts + * import { Option } from "effect" + * + * // Declare an Option holding a string + * declare const myOption: Option.Option + * + * // Extract the type of the value within the Option + * // + * // ┌─── string + * // ▼ + * type MyType = Option.Option.Value + * ``` + * + * @since 2.0.0 + * @category Type-level Utils + */ + export type Value> = [T] extends [Option] ? _A : never +} + +/** + * @category Models + * @since 2.0.0 + */ +export interface OptionUnifyIgnore {} + +/** + * @category Type Lambdas + * @since 2.0.0 + */ +export interface OptionTypeLambda extends TypeLambda { + readonly type: Option +} + +/** + * Represents the absence of a value by creating an empty `Option`. + * + * `Option.none` returns an `Option`, which is a subtype of `Option`. + * This means you can use it in place of any `Option` regardless of the type + * `A`. + * + * **Example** (Creating an Option with No Value) + * + * ```ts + * import { Option } from "effect" + * + * // An Option holding no value + * // + * // ┌─── Option + * // ▼ + * const noValue = Option.none() + * + * console.log(noValue) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @see {@link some} for the opposite operation. + * + * @category Constructors + * @since 2.0.0 + */ +export const none = (): Option => option.none + +/** + * Wraps the given value into an `Option` to represent its presence. + * + * **Example** (Creating an Option with a Value) + * + * ```ts + * import { Option } from "effect" + * + * // An Option holding the number 1 + * // + * // ┌─── Option + * // ▼ + * const value = Option.some(1) + * + * console.log(value) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * ``` + * + * @see {@link none} for the opposite operation. + * + * @category Constructors + * @since 2.0.0 + */ +export const some: (value: A) => Option = option.some + +/** + * Determines whether the given value is an `Option`. + * + * **Details** + * + * This function checks if a value is an instance of `Option`. It returns `true` + * if the value is either `Option.some` or `Option.none`, and `false` otherwise. + * This is particularly useful when working with unknown values or when you need + * to ensure type safety in your code. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.isOption(Option.some(1))) + * // Output: true + * + * console.log(Option.isOption(Option.none())) + * // Output: true + * + * console.log(Option.isOption({})) + * // Output: false + * ``` + * + * @category Guards + * @since 2.0.0 + */ +export const isOption: (input: unknown) => input is Option = option.isOption + +/** + * Checks whether an `Option` represents the absence of a value (`None`). + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.isNone(Option.some(1))) + * // Output: false + * + * console.log(Option.isNone(Option.none())) + * // Output: true + * ``` + * + * @see {@link isSome} for the opposite check. + * + * @category Guards + * @since 2.0.0 + */ +export const isNone: (self: Option) => self is None = option.isNone + +/** + * Checks whether an `Option` contains a value (`Some`). + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.isSome(Option.some(1))) + * // Output: true + * + * console.log(Option.isSome(Option.none())) + * // Output: false + * ``` + * + * @see {@link isNone} for the opposite check. + * + * @category Guards + * @since 2.0.0 + */ +export const isSome: (self: Option) => self is Some = option.isSome + +/** + * Performs pattern matching on an `Option` to handle both `Some` and `None` + * cases. + * + * **Details** + * + * This function allows you to match against an `Option` and handle both + * scenarios: when the `Option` is `None` (i.e., contains no value), and when + * the `Option` is `Some` (i.e., contains a value). It executes one of the + * provided functions based on the case: + * + * - If the `Option` is `None`, the `onNone` function is executed and its result + * is returned. + * - If the `Option` is `Some`, the `onSome` function is executed with the + * contained value, and its result is returned. + * + * This function provides a concise and functional way to handle optional values + * without resorting to `if` or manual checks, making your code more declarative + * and readable. + * + * **Example** (Pattern Matching with Option) + * + * ```ts + * import { Option } from "effect" + * + * const foo = Option.some(1) + * + * const message = Option.match(foo, { + * onNone: () => "Option is empty", + * onSome: (value) => `Option has a value: ${value}` + * }) + * + * console.log(message) + * // Output: "Option has a value: 1" + * ``` + * + * @category Pattern matching + * @since 2.0.0 + */ +export const match: { + /** + * Performs pattern matching on an `Option` to handle both `Some` and `None` + * cases. + * + * **Details** + * + * This function allows you to match against an `Option` and handle both + * scenarios: when the `Option` is `None` (i.e., contains no value), and when + * the `Option` is `Some` (i.e., contains a value). It executes one of the + * provided functions based on the case: + * + * - If the `Option` is `None`, the `onNone` function is executed and its result + * is returned. + * - If the `Option` is `Some`, the `onSome` function is executed with the + * contained value, and its result is returned. + * + * This function provides a concise and functional way to handle optional values + * without resorting to `if` or manual checks, making your code more declarative + * and readable. + * + * **Example** (Pattern Matching with Option) + * + * ```ts + * import { Option } from "effect" + * + * const foo = Option.some(1) + * + * const message = Option.match(foo, { + * onNone: () => "Option is empty", + * onSome: (value) => `Option has a value: ${value}` + * }) + * + * console.log(message) + * // Output: "Option has a value: 1" + * ``` + * + * @category Pattern matching + * @since 2.0.0 + */ + ( + options: { + readonly onNone: LazyArg + readonly onSome: (a: A) => C + } + ): (self: Option) => B | C + /** + * Performs pattern matching on an `Option` to handle both `Some` and `None` + * cases. + * + * **Details** + * + * This function allows you to match against an `Option` and handle both + * scenarios: when the `Option` is `None` (i.e., contains no value), and when + * the `Option` is `Some` (i.e., contains a value). It executes one of the + * provided functions based on the case: + * + * - If the `Option` is `None`, the `onNone` function is executed and its result + * is returned. + * - If the `Option` is `Some`, the `onSome` function is executed with the + * contained value, and its result is returned. + * + * This function provides a concise and functional way to handle optional values + * without resorting to `if` or manual checks, making your code more declarative + * and readable. + * + * **Example** (Pattern Matching with Option) + * + * ```ts + * import { Option } from "effect" + * + * const foo = Option.some(1) + * + * const message = Option.match(foo, { + * onNone: () => "Option is empty", + * onSome: (value) => `Option has a value: ${value}` + * }) + * + * console.log(message) + * // Output: "Option has a value: 1" + * ``` + * + * @category Pattern matching + * @since 2.0.0 + */ + ( + self: Option, + options: { + readonly onNone: LazyArg + readonly onSome: (a: A) => C + } + ): B | C +} = dual( + 2, + (self: Option, { onNone, onSome }: { + readonly onNone: LazyArg + readonly onSome: (a: A) => C + }): B | C => isNone(self) ? onNone() : onSome(self.value) +) + +/** + * Converts an `Option`-returning function into a type guard. + * + * **Details** + * + * This function transforms a function that returns an `Option` into a type + * guard, ensuring type safety when validating or narrowing types. The returned + * type guard function checks whether the input satisfies the condition defined + * in the original `Option`-returning function. + * + * If the original function returns `Option.some`, the type guard evaluates to + * `true`, confirming the input is of the desired type. If the function returns + * `Option.none`, the type guard evaluates to `false`. + * + * This utility is especially useful for validating types in union types, + * filtering arrays, or ensuring safe handling of specific subtypes. + * + * @example + * ```ts + * import { Option } from "effect" + * + * type MyData = string | number + * + * const parseString = (data: MyData): Option.Option => + * typeof data === "string" ? Option.some(data) : Option.none() + * + * // ┌─── (a: MyData) => a is string + * // ▼ + * const isString = Option.toRefinement(parseString) + * + * console.log(isString("a")) + * // Output: true + * + * console.log(isString(1)) + * // Output: false + * ``` + * + * @category Conversions + * @since 2.0.0 + */ +export const toRefinement = (f: (a: A) => Option): (a: A) => a is B => (a: A): a is B => isSome(f(a)) + +/** + * Converts an `Iterable` into an `Option`, wrapping the first element if it + * exists. + * + * **Details** + * + * This function takes an `Iterable` (e.g., an array, a generator, or any object + * implementing the `Iterable` interface) and returns an `Option` based on its + * content: + * + * - If the `Iterable` contains at least one element, the first element is + * wrapped in a `Some` and returned. + * - If the `Iterable` is empty, `None` is returned, representing the absence of + * a value. + * + * This utility is useful for safely handling collections that might be empty, + * ensuring you explicitly handle both cases where a value exists or doesn't. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.fromIterable([1, 2, 3])) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * + * console.log(Option.fromIterable([])) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Constructors + * @since 2.0.0 + */ +export const fromIterable = (collection: Iterable): Option => { + for (const a of collection) { + return some(a) + } + return none() +} + +/** + * Converts an `Either` into an `Option` by discarding the error and extracting + * the right value. + * + * **Details** + * + * This function takes an `Either` and returns an `Option` based on its value: + * + * - If the `Either` is a `Right`, its value is wrapped in a `Some` and + * returned. + * - If the `Either` is a `Left`, the error is discarded, and `None` is + * returned. + * + * This is particularly useful when you only care about the success case + * (`Right`) of an `Either` and want to handle the result using `Option`. By + * using this function, you can convert `Either` into a simpler structure for + * cases where error handling is not required. + * + * @example + * ```ts + * import { Either, Option } from "effect" + * + * console.log(Option.getRight(Either.right("ok"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'ok' } + * + * console.log(Option.getRight(Either.left("err"))) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @see {@link getLeft} for the opposite operation. + * + * @category Conversions + * @since 2.0.0 + */ +export const getRight: (self: Either) => Option = either.getRight + +/** + * Converts an `Either` into an `Option` by discarding the right value and + * extracting the left value. + * + * **Details** + * + * This function transforms an `Either` into an `Option` as follows: + * + * - If the `Either` is a `Left`, its value is wrapped in a `Some` and returned. + * - If the `Either` is a `Right`, the value is discarded, and `None` is + * returned. + * + * This utility is useful when you only care about the error case (`Left`) of an + * `Either` and want to handle it as an `Option`. By discarding the right value, + * it simplifies error-focused workflows. + * + * @example + * ```ts + * import { Either, Option } from "effect" + * + * console.log(Option.getLeft(Either.right("ok"))) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(Option.getLeft(Either.left("err"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'err' } + * ``` + * + * @see {@link getRight} for the opposite operation. + * + * @category Conversions + * @since 2.0.0 + */ +export const getLeft: (self: Either) => Option = either.getLeft + +/** + * Returns the value contained in the `Option` if it is `Some`, otherwise + * evaluates and returns the result of `onNone`. + * + * **Details** + * + * This function allows you to provide a fallback value or computation for when + * an `Option` is `None`. If the `Option` contains a value (`Some`), that value + * is returned. If it is empty (`None`), the `onNone` function is executed, and + * its result is returned instead. + * + * This utility is helpful for safely handling `Option` values by ensuring you + * always receive a meaningful result, whether or not the `Option` contains a + * value. It is particularly useful for providing default values or alternative + * logic when working with optional values. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.some(1).pipe(Option.getOrElse(() => 0))) + * // Output: 1 + * + * console.log(Option.none().pipe(Option.getOrElse(() => 0))) + * // Output: 0 + * ``` + * + * @see {@link getOrNull} for a version that returns `null` instead of executing a function. + * @see {@link getOrUndefined} for a version that returns `undefined` instead of executing a function. + * + * @category Getters + * @since 2.0.0 + */ +export const getOrElse: { + /** + * Returns the value contained in the `Option` if it is `Some`, otherwise + * evaluates and returns the result of `onNone`. + * + * **Details** + * + * This function allows you to provide a fallback value or computation for when + * an `Option` is `None`. If the `Option` contains a value (`Some`), that value + * is returned. If it is empty (`None`), the `onNone` function is executed, and + * its result is returned instead. + * + * This utility is helpful for safely handling `Option` values by ensuring you + * always receive a meaningful result, whether or not the `Option` contains a + * value. It is particularly useful for providing default values or alternative + * logic when working with optional values. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.some(1).pipe(Option.getOrElse(() => 0))) + * // Output: 1 + * + * console.log(Option.none().pipe(Option.getOrElse(() => 0))) + * // Output: 0 + * ``` + * + * @see {@link getOrNull} for a version that returns `null` instead of executing a function. + * @see {@link getOrUndefined} for a version that returns `undefined` instead of executing a function. + * + * @category Getters + * @since 2.0.0 + */ + (onNone: LazyArg): (self: Option) => B | A + /** + * Returns the value contained in the `Option` if it is `Some`, otherwise + * evaluates and returns the result of `onNone`. + * + * **Details** + * + * This function allows you to provide a fallback value or computation for when + * an `Option` is `None`. If the `Option` contains a value (`Some`), that value + * is returned. If it is empty (`None`), the `onNone` function is executed, and + * its result is returned instead. + * + * This utility is helpful for safely handling `Option` values by ensuring you + * always receive a meaningful result, whether or not the `Option` contains a + * value. It is particularly useful for providing default values or alternative + * logic when working with optional values. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.some(1).pipe(Option.getOrElse(() => 0))) + * // Output: 1 + * + * console.log(Option.none().pipe(Option.getOrElse(() => 0))) + * // Output: 0 + * ``` + * + * @see {@link getOrNull} for a version that returns `null` instead of executing a function. + * @see {@link getOrUndefined} for a version that returns `undefined` instead of executing a function. + * + * @category Getters + * @since 2.0.0 + */ + (self: Option, onNone: LazyArg): A | B +} = dual( + 2, + (self: Option, onNone: LazyArg): A | B => isNone(self) ? onNone() : self.value +) + +/** + * Returns the provided `Option` `that` if the current `Option` (`self`) is + * `None`; otherwise, it returns `self`. + * + * **Details** + * + * This function provides a fallback mechanism for `Option` values. If the + * current `Option` is `None` (i.e., it contains no value), the `that` function + * is evaluated, and its resulting `Option` is returned. If the current `Option` + * is `Some` (i.e., it contains a value), the original `Option` is returned + * unchanged. + * + * This is particularly useful for chaining fallback values or computations, + * allowing you to provide alternative `Option` values when the first one is + * empty. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.none().pipe(Option.orElse(() => Option.none()))) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(Option.some("a").pipe(Option.orElse(() => Option.none()))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * + * console.log(Option.none().pipe(Option.orElse(() => Option.some("b")))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'b' } + * + * console.log(Option.some("a").pipe(Option.orElse(() => Option.some("b")))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * ``` + * + * @category Error handling + * @since 2.0.0 + */ +export const orElse: { + /** + * Returns the provided `Option` `that` if the current `Option` (`self`) is + * `None`; otherwise, it returns `self`. + * + * **Details** + * + * This function provides a fallback mechanism for `Option` values. If the + * current `Option` is `None` (i.e., it contains no value), the `that` function + * is evaluated, and its resulting `Option` is returned. If the current `Option` + * is `Some` (i.e., it contains a value), the original `Option` is returned + * unchanged. + * + * This is particularly useful for chaining fallback values or computations, + * allowing you to provide alternative `Option` values when the first one is + * empty. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.none().pipe(Option.orElse(() => Option.none()))) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(Option.some("a").pipe(Option.orElse(() => Option.none()))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * + * console.log(Option.none().pipe(Option.orElse(() => Option.some("b")))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'b' } + * + * console.log(Option.some("a").pipe(Option.orElse(() => Option.some("b")))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * ``` + * + * @category Error handling + * @since 2.0.0 + */ + (that: LazyArg>): (self: Option) => Option + /** + * Returns the provided `Option` `that` if the current `Option` (`self`) is + * `None`; otherwise, it returns `self`. + * + * **Details** + * + * This function provides a fallback mechanism for `Option` values. If the + * current `Option` is `None` (i.e., it contains no value), the `that` function + * is evaluated, and its resulting `Option` is returned. If the current `Option` + * is `Some` (i.e., it contains a value), the original `Option` is returned + * unchanged. + * + * This is particularly useful for chaining fallback values or computations, + * allowing you to provide alternative `Option` values when the first one is + * empty. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.none().pipe(Option.orElse(() => Option.none()))) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(Option.some("a").pipe(Option.orElse(() => Option.none()))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * + * console.log(Option.none().pipe(Option.orElse(() => Option.some("b")))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'b' } + * + * console.log(Option.some("a").pipe(Option.orElse(() => Option.some("b")))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * ``` + * + * @category Error handling + * @since 2.0.0 + */ + (self: Option, that: LazyArg>): Option +} = dual( + 2, + (self: Option, that: LazyArg>): Option => isNone(self) ? that() : self +) + +/** + * Returns the provided default value wrapped in `Some` if the current `Option` + * (`self`) is `None`; otherwise, returns `self`. + * + * **Details** + * + * This function provides a way to supply a default value for cases where an + * `Option` is `None`. If the current `Option` is empty (`None`), the `onNone` + * function is executed to compute the default value, which is then wrapped in a + * `Some`. If the current `Option` contains a value (`Some`), it is returned as + * is. + * + * This is particularly useful for handling optional values where a fallback + * default needs to be provided explicitly in case of absence. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.none().pipe(Option.orElseSome(() => "b"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'b' } + * + * console.log(Option.some("a").pipe(Option.orElseSome(() => "b"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * ``` + * + * @category Error handling + * @since 2.0.0 + */ +export const orElseSome: { + /** + * Returns the provided default value wrapped in `Some` if the current `Option` + * (`self`) is `None`; otherwise, returns `self`. + * + * **Details** + * + * This function provides a way to supply a default value for cases where an + * `Option` is `None`. If the current `Option` is empty (`None`), the `onNone` + * function is executed to compute the default value, which is then wrapped in a + * `Some`. If the current `Option` contains a value (`Some`), it is returned as + * is. + * + * This is particularly useful for handling optional values where a fallback + * default needs to be provided explicitly in case of absence. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.none().pipe(Option.orElseSome(() => "b"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'b' } + * + * console.log(Option.some("a").pipe(Option.orElseSome(() => "b"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * ``` + * + * @category Error handling + * @since 2.0.0 + */ + (onNone: LazyArg): (self: Option) => Option + /** + * Returns the provided default value wrapped in `Some` if the current `Option` + * (`self`) is `None`; otherwise, returns `self`. + * + * **Details** + * + * This function provides a way to supply a default value for cases where an + * `Option` is `None`. If the current `Option` is empty (`None`), the `onNone` + * function is executed to compute the default value, which is then wrapped in a + * `Some`. If the current `Option` contains a value (`Some`), it is returned as + * is. + * + * This is particularly useful for handling optional values where a fallback + * default needs to be provided explicitly in case of absence. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.none().pipe(Option.orElseSome(() => "b"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'b' } + * + * console.log(Option.some("a").pipe(Option.orElseSome(() => "b"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * ``` + * + * @category Error handling + * @since 2.0.0 + */ + (self: Option, onNone: LazyArg): Option +} = dual( + 2, + (self: Option, onNone: LazyArg): Option => isNone(self) ? some(onNone()) : self +) + +/** + * Similar to {@link orElse}, but returns an `Either` wrapped in an `Option` to + * indicate the source of the value. + * + * **Details** + * + * This function allows you to provide a fallback `Option` in case the current + * `Option` (`self`) is `None`. However, unlike `orElse`, it returns the value + * wrapped in an `Either` object, providing additional information about where + * the value came from: + * + * - If the value is from the fallback `Option` (`that`), it is wrapped in an + * `Either.right`. + * - If the value is from the original `Option` (`self`), it is wrapped in an + * `Either.left`. + * + * This is especially useful when you need to differentiate between values + * originating from the primary `Option` and those coming from the fallback, + * while still maintaining the `Option`-style handling. + * + * @category Error handling + * @since 2.0.0 + */ +export const orElseEither: { + /** + * Similar to {@link orElse}, but returns an `Either` wrapped in an `Option` to + * indicate the source of the value. + * + * **Details** + * + * This function allows you to provide a fallback `Option` in case the current + * `Option` (`self`) is `None`. However, unlike `orElse`, it returns the value + * wrapped in an `Either` object, providing additional information about where + * the value came from: + * + * - If the value is from the fallback `Option` (`that`), it is wrapped in an + * `Either.right`. + * - If the value is from the original `Option` (`self`), it is wrapped in an + * `Either.left`. + * + * This is especially useful when you need to differentiate between values + * originating from the primary `Option` and those coming from the fallback, + * while still maintaining the `Option`-style handling. + * + * @category Error handling + * @since 2.0.0 + */ + (that: LazyArg>): (self: Option) => Option> + /** + * Similar to {@link orElse}, but returns an `Either` wrapped in an `Option` to + * indicate the source of the value. + * + * **Details** + * + * This function allows you to provide a fallback `Option` in case the current + * `Option` (`self`) is `None`. However, unlike `orElse`, it returns the value + * wrapped in an `Either` object, providing additional information about where + * the value came from: + * + * - If the value is from the fallback `Option` (`that`), it is wrapped in an + * `Either.right`. + * - If the value is from the original `Option` (`self`), it is wrapped in an + * `Either.left`. + * + * This is especially useful when you need to differentiate between values + * originating from the primary `Option` and those coming from the fallback, + * while still maintaining the `Option`-style handling. + * + * @category Error handling + * @since 2.0.0 + */ + (self: Option, that: LazyArg>): Option> +} = dual( + 2, + (self: Option, that: LazyArg>): Option> => + isNone(self) ? map(that(), either.right) : map(self, either.left) +) + +/** + * Returns the first `Some` value found in an `Iterable` collection of + * `Option`s, or `None` if no `Some` is found. + * + * **Details** + * + * This function iterates over a collection of `Option` values and returns the + * first `Some` it encounters. If the collection contains only `None` values, + * the result will also be `None`. This utility is useful for efficiently + * finding the first valid value in a sequence of potentially empty or invalid + * options. + * + * The iteration stops as soon as a `Some` is found, making this function + * efficient for large collections. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.firstSomeOf([ + * Option.none(), + * Option.some(1), + * Option.some(2) + * ])) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * ``` + * + * @category Error handling + * @since 2.0.0 + */ +export const firstSomeOf = > = Iterable>>( + collection: C +): [C] extends [Iterable>] ? Option : never => { + let out: Option = none() + for (out of collection) { + if (isSome(out)) { + return out as any + } + } + return out as any +} + +/** + * Converts a nullable value into an `Option`. Returns `None` if the value is + * `null` or `undefined`, otherwise wraps the value in a `Some`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.fromNullable(undefined)) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(Option.fromNullable(null)) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(Option.fromNullable(1)) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * ``` + * + * @category Conversions + * @since 2.0.0 + */ +export const fromNullable = ( + nullableValue: A +): Option> => (nullableValue == null ? none() : some(nullableValue as NonNullable)) + +/** + * Lifts a function that returns `null` or `undefined` into the `Option` + * context. + * + * **Details** + * + * This function takes a function `f` that might return `null` or `undefined` + * and transforms it into a function that returns an `Option`. The resulting + * function will return: + * - `Some` if the original function produces a non-null, non-undefined value. + * - `None` if the original function produces `null` or `undefined`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const parse = (s: string): number | undefined => { + * const n = parseFloat(s) + * return isNaN(n) ? undefined : n + * } + * + * const parseOption = Option.liftNullable(parse) + * + * console.log(parseOption("1")) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * + * console.log(parseOption("not a number")) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Conversions + * @since 2.0.0 + */ +export const liftNullable = , B>( + f: (...a: A) => B | null | undefined +): (...a: A) => Option> => +(...a) => fromNullable(f(...a)) + +/** + * Returns the value contained in the `Option` if it is `Some`; otherwise, + * returns `null`. + * + * **Details** + * + * This function provides a way to extract the value of an `Option` while + * falling back to `null` if the `Option` is `None`. + * + * It is particularly useful in scenarios where `null` is an acceptable + * placeholder for the absence of a value, such as when interacting with APIs or + * systems that use `null` as a default for missing values. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.getOrNull(Option.some(1))) + * // Output: 1 + * + * console.log(Option.getOrNull(Option.none())) + * // Output: null + * ``` + * + * @category Getters + * @since 2.0.0 + */ +export const getOrNull: (self: Option) => A | null = getOrElse(constNull) + +/** + * Returns the value contained in the `Option` if it is `Some`; otherwise, + * returns `undefined`. + * + * **Details** + * + * This function provides a way to extract the value of an `Option` while + * falling back to `undefined` if the `Option` is `None`. + * + * It is particularly useful in scenarios where `undefined` is an acceptable + * placeholder for the absence of a value, such as when interacting with APIs or + * systems that use `undefined` as a default for missing values. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.getOrUndefined(Option.some(1))) + * // Output: 1 + * + * console.log(Option.getOrUndefined(Option.none())) + * // Output: undefined + * ``` + * + * @category Getters + * @since 2.0.0 + */ +export const getOrUndefined: (self: Option) => A | undefined = getOrElse(constUndefined) + +/** + * Lifts a function that throws exceptions into a function that returns an + * `Option`. + * + * **Details** + * + * This utility function takes a function `f` that might throw an exception and + * transforms it into a safer function that returns an `Option`. If the original + * function executes successfully, the result is wrapped in a `Some`. If an + * exception is thrown, the result is `None`, allowing the developer to handle + * errors in a functional, type-safe way. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const parse = Option.liftThrowable(JSON.parse) + * + * console.log(parse("1")) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * + * console.log(parse("")) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Conversions + * @since 2.0.0 + */ +export const liftThrowable = , B>( + f: (...a: A) => B +): (...a: A) => Option => +(...a) => { + try { + return some(f(...a)) + } catch { + return none() + } +} + +/** + * Extracts the value of an `Option` or throws an error if the `Option` is + * `None`, using a custom error factory. + * + * **Details** + * + * This function allows you to extract the value of an `Option` when it is + * `Some`. If the `Option` is `None`, it throws an error generated by the + * provided `onNone` function. This utility is particularly useful when you need + * a fail-fast behavior for empty `Option` values and want to provide a custom + * error message or object. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Option } from "effect" + * + * assert.deepStrictEqual( + * Option.getOrThrowWith(Option.some(1), () => new Error('Unexpected None')), + * 1 + * ) + * assert.throws(() => Option.getOrThrowWith(Option.none(), () => new Error('Unexpected None'))) + * ``` + * + * @see {@link getOrThrow} for a version that throws a default error. + * + * @category Conversions + * @since 2.0.0 + */ +export const getOrThrowWith: { + /** + * Extracts the value of an `Option` or throws an error if the `Option` is + * `None`, using a custom error factory. + * + * **Details** + * + * This function allows you to extract the value of an `Option` when it is + * `Some`. If the `Option` is `None`, it throws an error generated by the + * provided `onNone` function. This utility is particularly useful when you need + * a fail-fast behavior for empty `Option` values and want to provide a custom + * error message or object. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Option } from "effect" + * + * assert.deepStrictEqual( + * Option.getOrThrowWith(Option.some(1), () => new Error('Unexpected None')), + * 1 + * ) + * assert.throws(() => Option.getOrThrowWith(Option.none(), () => new Error('Unexpected None'))) + * ``` + * + * @see {@link getOrThrow} for a version that throws a default error. + * + * @category Conversions + * @since 2.0.0 + */ + (onNone: () => unknown): (self: Option) => A + /** + * Extracts the value of an `Option` or throws an error if the `Option` is + * `None`, using a custom error factory. + * + * **Details** + * + * This function allows you to extract the value of an `Option` when it is + * `Some`. If the `Option` is `None`, it throws an error generated by the + * provided `onNone` function. This utility is particularly useful when you need + * a fail-fast behavior for empty `Option` values and want to provide a custom + * error message or object. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Option } from "effect" + * + * assert.deepStrictEqual( + * Option.getOrThrowWith(Option.some(1), () => new Error('Unexpected None')), + * 1 + * ) + * assert.throws(() => Option.getOrThrowWith(Option.none(), () => new Error('Unexpected None'))) + * ``` + * + * @see {@link getOrThrow} for a version that throws a default error. + * + * @category Conversions + * @since 2.0.0 + */ + (self: Option, onNone: () => unknown): A +} = dual(2, (self: Option, onNone: () => unknown): A => { + if (isSome(self)) { + return self.value + } + throw onNone() +}) + +/** + * Extracts the value of an `Option` or throws a default error if the `Option` + * is `None`. + * + * **Details** + * + * This function extracts the value from an `Option` if it is `Some`. If the + * `Option` is `None`, it throws a default error. It is useful for fail-fast + * scenarios where the absence of a value is treated as an exceptional case and + * a default error is sufficient. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Option } from "effect" + * + * assert.deepStrictEqual(Option.getOrThrow(Option.some(1)), 1) + * assert.throws(() => Option.getOrThrow(Option.none())) + * ``` + * + * @see {@link getOrThrowWith} for a version that allows you to provide a custom error. + * + * @category Conversions + * @since 2.0.0 + */ +export const getOrThrow: (self: Option) => A = getOrThrowWith(() => new Error("getOrThrow called on a None")) + +/** + * Transforms the value inside a `Some` to a new value using the provided + * function, while leaving `None` unchanged. + * + * **Details** + * + * This function applies a mapping function `f` to the value inside an `Option` + * if it is a `Some`. If the `Option` is `None`, it remains unchanged. The + * result is a new `Option` with the transformed value (if it was a `Some`) or + * still `None`. + * + * This utility is particularly useful for chaining transformations in a + * functional way without needing to manually handle `None` cases. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Mapping over a `Some` + * const someValue = Option.some(2) + * + * console.log(Option.map(someValue, (n) => n * 2)) + * // Output: { _id: 'Option', _tag: 'Some', value: 4 } + * + * // Mapping over a `None` + * const noneValue = Option.none() + * + * console.log(Option.map(noneValue, (n) => n * 2)) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Mapping + * @since 2.0.0 + */ +export const map: { + /** + * Transforms the value inside a `Some` to a new value using the provided + * function, while leaving `None` unchanged. + * + * **Details** + * + * This function applies a mapping function `f` to the value inside an `Option` + * if it is a `Some`. If the `Option` is `None`, it remains unchanged. The + * result is a new `Option` with the transformed value (if it was a `Some`) or + * still `None`. + * + * This utility is particularly useful for chaining transformations in a + * functional way without needing to manually handle `None` cases. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Mapping over a `Some` + * const someValue = Option.some(2) + * + * console.log(Option.map(someValue, (n) => n * 2)) + * // Output: { _id: 'Option', _tag: 'Some', value: 4 } + * + * // Mapping over a `None` + * const noneValue = Option.none() + * + * console.log(Option.map(noneValue, (n) => n * 2)) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Mapping + * @since 2.0.0 + */ + (f: (a: A) => B): (self: Option) => Option + /** + * Transforms the value inside a `Some` to a new value using the provided + * function, while leaving `None` unchanged. + * + * **Details** + * + * This function applies a mapping function `f` to the value inside an `Option` + * if it is a `Some`. If the `Option` is `None`, it remains unchanged. The + * result is a new `Option` with the transformed value (if it was a `Some`) or + * still `None`. + * + * This utility is particularly useful for chaining transformations in a + * functional way without needing to manually handle `None` cases. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Mapping over a `Some` + * const someValue = Option.some(2) + * + * console.log(Option.map(someValue, (n) => n * 2)) + * // Output: { _id: 'Option', _tag: 'Some', value: 4 } + * + * // Mapping over a `None` + * const noneValue = Option.none() + * + * console.log(Option.map(noneValue, (n) => n * 2)) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Mapping + * @since 2.0.0 + */ + (self: Option, f: (a: A) => B): Option +} = dual( + 2, + (self: Option, f: (a: A) => B): Option => isNone(self) ? none() : some(f(self.value)) +) + +/** + * Replaces the value inside a `Some` with the specified constant value, leaving + * `None` unchanged. + * + * **Details** + * + * This function transforms an `Option` by replacing the value inside a `Some` + * with the given constant value `b`. If the `Option` is `None`, it remains + * unchanged. + * + * This is useful when you want to preserve the presence of a value (`Some`) but + * replace its content with a fixed value. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Replacing the value of a `Some` + * const someValue = Option.some(42) + * + * console.log(Option.as(someValue, "new value")) + * // Output: { _id: 'Option', _tag: 'Some', value: 'new value' } + * + * // Replacing a `None` (no effect) + * const noneValue = Option.none() + * + * console.log(Option.as(noneValue, "new value")) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Mapping + * @since 2.0.0 + */ +export const as: { + /** + * Replaces the value inside a `Some` with the specified constant value, leaving + * `None` unchanged. + * + * **Details** + * + * This function transforms an `Option` by replacing the value inside a `Some` + * with the given constant value `b`. If the `Option` is `None`, it remains + * unchanged. + * + * This is useful when you want to preserve the presence of a value (`Some`) but + * replace its content with a fixed value. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Replacing the value of a `Some` + * const someValue = Option.some(42) + * + * console.log(Option.as(someValue, "new value")) + * // Output: { _id: 'Option', _tag: 'Some', value: 'new value' } + * + * // Replacing a `None` (no effect) + * const noneValue = Option.none() + * + * console.log(Option.as(noneValue, "new value")) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Mapping + * @since 2.0.0 + */ + (b: B): (self: Option) => Option + /** + * Replaces the value inside a `Some` with the specified constant value, leaving + * `None` unchanged. + * + * **Details** + * + * This function transforms an `Option` by replacing the value inside a `Some` + * with the given constant value `b`. If the `Option` is `None`, it remains + * unchanged. + * + * This is useful when you want to preserve the presence of a value (`Some`) but + * replace its content with a fixed value. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Replacing the value of a `Some` + * const someValue = Option.some(42) + * + * console.log(Option.as(someValue, "new value")) + * // Output: { _id: 'Option', _tag: 'Some', value: 'new value' } + * + * // Replacing a `None` (no effect) + * const noneValue = Option.none() + * + * console.log(Option.as(noneValue, "new value")) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Mapping + * @since 2.0.0 + */ + (self: Option, b: B): Option +} = dual(2, (self: Option, b: B): Option => map(self, () => b)) + +/** + * Replaces the value inside a `Some` with the constant value `void`, leaving + * `None` unchanged. + * + * **Details** + * + * This function transforms an `Option` by replacing the value inside a `Some` + * with `void`. If the `Option` is `None`, it remains unchanged. + * + * This is particularly useful in scenarios where the presence or absence of a + * value is significant, but the actual content of the value is irrelevant. + * + * @category Mapping + * @since 2.0.0 + */ +export const asVoid: <_>(self: Option<_>) => Option = as(undefined) + +const void_: Option = some(undefined) +export { + /** + * @since 2.0.0 + */ + void_ as void +} + +/** + * Applies a function to the value of a `Some` and flattens the resulting + * `Option`. If the input is `None`, it remains `None`. + * + * **Details** + * + * This function allows you to chain computations that return `Option` values. + * If the input `Option` is `Some`, the provided function `f` is applied to the + * contained value, and the resulting `Option` is returned. If the input is + * `None`, the function is not applied, and the result remains `None`. + * + * This utility is particularly useful for sequencing operations that may fail + * or produce optional results, enabling clean and concise workflows for + * handling such cases. + * + * @example + * ```ts + * import { Option } from "effect" + * + * interface Address { + * readonly city: string + * readonly street: Option.Option + * } + * + * interface User { + * readonly id: number + * readonly username: string + * readonly email: Option.Option + * readonly address: Option.Option
+ * } + * + * const user: User = { + * id: 1, + * username: "john_doe", + * email: Option.some("john.doe@example.com"), + * address: Option.some({ + * city: "New York", + * street: Option.some("123 Main St") + * }) + * } + * + * // Use flatMap to extract the street value + * const street = user.address.pipe( + * Option.flatMap((address) => address.street) + * ) + * + * console.log(street) + * // Output: { _id: 'Option', _tag: 'Some', value: '123 Main St' } + * ``` + * + * @category Sequencing + * @since 2.0.0 + */ +export const flatMap: { + /** + * Applies a function to the value of a `Some` and flattens the resulting + * `Option`. If the input is `None`, it remains `None`. + * + * **Details** + * + * This function allows you to chain computations that return `Option` values. + * If the input `Option` is `Some`, the provided function `f` is applied to the + * contained value, and the resulting `Option` is returned. If the input is + * `None`, the function is not applied, and the result remains `None`. + * + * This utility is particularly useful for sequencing operations that may fail + * or produce optional results, enabling clean and concise workflows for + * handling such cases. + * + * @example + * ```ts + * import { Option } from "effect" + * + * interface Address { + * readonly city: string + * readonly street: Option.Option + * } + * + * interface User { + * readonly id: number + * readonly username: string + * readonly email: Option.Option + * readonly address: Option.Option
+ * } + * + * const user: User = { + * id: 1, + * username: "john_doe", + * email: Option.some("john.doe@example.com"), + * address: Option.some({ + * city: "New York", + * street: Option.some("123 Main St") + * }) + * } + * + * // Use flatMap to extract the street value + * const street = user.address.pipe( + * Option.flatMap((address) => address.street) + * ) + * + * console.log(street) + * // Output: { _id: 'Option', _tag: 'Some', value: '123 Main St' } + * ``` + * + * @category Sequencing + * @since 2.0.0 + */ + (f: (a: A) => Option): (self: Option) => Option + /** + * Applies a function to the value of a `Some` and flattens the resulting + * `Option`. If the input is `None`, it remains `None`. + * + * **Details** + * + * This function allows you to chain computations that return `Option` values. + * If the input `Option` is `Some`, the provided function `f` is applied to the + * contained value, and the resulting `Option` is returned. If the input is + * `None`, the function is not applied, and the result remains `None`. + * + * This utility is particularly useful for sequencing operations that may fail + * or produce optional results, enabling clean and concise workflows for + * handling such cases. + * + * @example + * ```ts + * import { Option } from "effect" + * + * interface Address { + * readonly city: string + * readonly street: Option.Option + * } + * + * interface User { + * readonly id: number + * readonly username: string + * readonly email: Option.Option + * readonly address: Option.Option
+ * } + * + * const user: User = { + * id: 1, + * username: "john_doe", + * email: Option.some("john.doe@example.com"), + * address: Option.some({ + * city: "New York", + * street: Option.some("123 Main St") + * }) + * } + * + * // Use flatMap to extract the street value + * const street = user.address.pipe( + * Option.flatMap((address) => address.street) + * ) + * + * console.log(street) + * // Output: { _id: 'Option', _tag: 'Some', value: '123 Main St' } + * ``` + * + * @category Sequencing + * @since 2.0.0 + */ + (self: Option, f: (a: A) => Option): Option +} = dual( + 2, + (self: Option, f: (a: A) => Option): Option => isNone(self) ? none() : f(self.value) +) + +/** + * Chains two `Option`s together. The second `Option` can either be a static + * value or depend on the result of the first `Option`. + * + * **Details** + * + * This function enables sequencing of two `Option` computations. If the first + * `Option` is `Some`, the second `Option` is evaluated. The second `Option` can + * either: + * + * - Be a static `Option` value. + * - Be a function that produces an `Option`, optionally based on the value of + * the first `Option`. + * + * If the first `Option` is `None`, the function skips the evaluation of the + * second `Option` and directly returns `None`. + * + * @category Sequencing + * @since 2.0.0 + */ +export const andThen: { + /** + * Chains two `Option`s together. The second `Option` can either be a static + * value or depend on the result of the first `Option`. + * + * **Details** + * + * This function enables sequencing of two `Option` computations. If the first + * `Option` is `Some`, the second `Option` is evaluated. The second `Option` can + * either: + * + * - Be a static `Option` value. + * - Be a function that produces an `Option`, optionally based on the value of + * the first `Option`. + * + * If the first `Option` is `None`, the function skips the evaluation of the + * second `Option` and directly returns `None`. + * + * @category Sequencing + * @since 2.0.0 + */ + (f: (a: A) => Option): (self: Option) => Option + /** + * Chains two `Option`s together. The second `Option` can either be a static + * value or depend on the result of the first `Option`. + * + * **Details** + * + * This function enables sequencing of two `Option` computations. If the first + * `Option` is `Some`, the second `Option` is evaluated. The second `Option` can + * either: + * + * - Be a static `Option` value. + * - Be a function that produces an `Option`, optionally based on the value of + * the first `Option`. + * + * If the first `Option` is `None`, the function skips the evaluation of the + * second `Option` and directly returns `None`. + * + * @category Sequencing + * @since 2.0.0 + */ + (f: Option): (self: Option) => Option + /** + * Chains two `Option`s together. The second `Option` can either be a static + * value or depend on the result of the first `Option`. + * + * **Details** + * + * This function enables sequencing of two `Option` computations. If the first + * `Option` is `Some`, the second `Option` is evaluated. The second `Option` can + * either: + * + * - Be a static `Option` value. + * - Be a function that produces an `Option`, optionally based on the value of + * the first `Option`. + * + * If the first `Option` is `None`, the function skips the evaluation of the + * second `Option` and directly returns `None`. + * + * @category Sequencing + * @since 2.0.0 + */ + (f: (a: A) => B): (self: Option) => Option + /** + * Chains two `Option`s together. The second `Option` can either be a static + * value or depend on the result of the first `Option`. + * + * **Details** + * + * This function enables sequencing of two `Option` computations. If the first + * `Option` is `Some`, the second `Option` is evaluated. The second `Option` can + * either: + * + * - Be a static `Option` value. + * - Be a function that produces an `Option`, optionally based on the value of + * the first `Option`. + * + * If the first `Option` is `None`, the function skips the evaluation of the + * second `Option` and directly returns `None`. + * + * @category Sequencing + * @since 2.0.0 + */ + (f: NotFunction): (self: Option) => Option + /** + * Chains two `Option`s together. The second `Option` can either be a static + * value or depend on the result of the first `Option`. + * + * **Details** + * + * This function enables sequencing of two `Option` computations. If the first + * `Option` is `Some`, the second `Option` is evaluated. The second `Option` can + * either: + * + * - Be a static `Option` value. + * - Be a function that produces an `Option`, optionally based on the value of + * the first `Option`. + * + * If the first `Option` is `None`, the function skips the evaluation of the + * second `Option` and directly returns `None`. + * + * @category Sequencing + * @since 2.0.0 + */ + (self: Option, f: (a: A) => Option): Option + /** + * Chains two `Option`s together. The second `Option` can either be a static + * value or depend on the result of the first `Option`. + * + * **Details** + * + * This function enables sequencing of two `Option` computations. If the first + * `Option` is `Some`, the second `Option` is evaluated. The second `Option` can + * either: + * + * - Be a static `Option` value. + * - Be a function that produces an `Option`, optionally based on the value of + * the first `Option`. + * + * If the first `Option` is `None`, the function skips the evaluation of the + * second `Option` and directly returns `None`. + * + * @category Sequencing + * @since 2.0.0 + */ + (self: Option, f: Option): Option + /** + * Chains two `Option`s together. The second `Option` can either be a static + * value or depend on the result of the first `Option`. + * + * **Details** + * + * This function enables sequencing of two `Option` computations. If the first + * `Option` is `Some`, the second `Option` is evaluated. The second `Option` can + * either: + * + * - Be a static `Option` value. + * - Be a function that produces an `Option`, optionally based on the value of + * the first `Option`. + * + * If the first `Option` is `None`, the function skips the evaluation of the + * second `Option` and directly returns `None`. + * + * @category Sequencing + * @since 2.0.0 + */ + (self: Option, f: (a: A) => B): Option + /** + * Chains two `Option`s together. The second `Option` can either be a static + * value or depend on the result of the first `Option`. + * + * **Details** + * + * This function enables sequencing of two `Option` computations. If the first + * `Option` is `Some`, the second `Option` is evaluated. The second `Option` can + * either: + * + * - Be a static `Option` value. + * - Be a function that produces an `Option`, optionally based on the value of + * the first `Option`. + * + * If the first `Option` is `None`, the function skips the evaluation of the + * second `Option` and directly returns `None`. + * + * @category Sequencing + * @since 2.0.0 + */ + (self: Option, f: NotFunction): Option +} = dual( + 2, + (self: Option, f: (a: A) => Option | Option): Option => + flatMap(self, (a) => { + const b = isFunction(f) ? f(a) : f + return isOption(b) ? b : some(b) + }) +) + +/** + * Combines `flatMap` and `fromNullable`, transforming the value inside a `Some` + * using a function that may return `null` or `undefined`. + * + * **Details** + * + * This function applies a transformation function `f` to the value inside a + * `Some`. The function `f` may return a value, `null`, or `undefined`. If `f` + * returns a value, it is wrapped in a `Some`. If `f` returns `null` or + * `undefined`, the result is `None`. If the input `Option` is `None`, the + * function is not applied, and `None` is returned. + * + * This utility is particularly useful when working with deeply nested optional + * values or chaining computations that may result in `null` or `undefined` at + * some point. + * + * @example + * ```ts + * import { Option } from "effect" + * + * interface Employee { + * company?: { + * address?: { + * street?: { + * name?: string + * } + * } + * } + * } + * + * const employee1: Employee = { company: { address: { street: { name: "high street" } } } } + * + * // Extracting a deeply nested property + * console.log( + * Option.some(employee1) + * .pipe(Option.flatMapNullable((employee) => employee.company?.address?.street?.name)) + * ) + * // Output: { _id: 'Option', _tag: 'Some', value: 'high street' } + * + * const employee2: Employee = { company: { address: { street: {} } } } + * + * // Property does not exist + * console.log( + * Option.some(employee2) + * .pipe(Option.flatMapNullable((employee) => employee.company?.address?.street?.name)) + * ) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Sequencing + * @since 2.0.0 + */ +export const flatMapNullable: { + /** + * Combines `flatMap` and `fromNullable`, transforming the value inside a `Some` + * using a function that may return `null` or `undefined`. + * + * **Details** + * + * This function applies a transformation function `f` to the value inside a + * `Some`. The function `f` may return a value, `null`, or `undefined`. If `f` + * returns a value, it is wrapped in a `Some`. If `f` returns `null` or + * `undefined`, the result is `None`. If the input `Option` is `None`, the + * function is not applied, and `None` is returned. + * + * This utility is particularly useful when working with deeply nested optional + * values or chaining computations that may result in `null` or `undefined` at + * some point. + * + * @example + * ```ts + * import { Option } from "effect" + * + * interface Employee { + * company?: { + * address?: { + * street?: { + * name?: string + * } + * } + * } + * } + * + * const employee1: Employee = { company: { address: { street: { name: "high street" } } } } + * + * // Extracting a deeply nested property + * console.log( + * Option.some(employee1) + * .pipe(Option.flatMapNullable((employee) => employee.company?.address?.street?.name)) + * ) + * // Output: { _id: 'Option', _tag: 'Some', value: 'high street' } + * + * const employee2: Employee = { company: { address: { street: {} } } } + * + * // Property does not exist + * console.log( + * Option.some(employee2) + * .pipe(Option.flatMapNullable((employee) => employee.company?.address?.street?.name)) + * ) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Sequencing + * @since 2.0.0 + */ + (f: (a: A) => B | null | undefined): (self: Option) => Option> + /** + * Combines `flatMap` and `fromNullable`, transforming the value inside a `Some` + * using a function that may return `null` or `undefined`. + * + * **Details** + * + * This function applies a transformation function `f` to the value inside a + * `Some`. The function `f` may return a value, `null`, or `undefined`. If `f` + * returns a value, it is wrapped in a `Some`. If `f` returns `null` or + * `undefined`, the result is `None`. If the input `Option` is `None`, the + * function is not applied, and `None` is returned. + * + * This utility is particularly useful when working with deeply nested optional + * values or chaining computations that may result in `null` or `undefined` at + * some point. + * + * @example + * ```ts + * import { Option } from "effect" + * + * interface Employee { + * company?: { + * address?: { + * street?: { + * name?: string + * } + * } + * } + * } + * + * const employee1: Employee = { company: { address: { street: { name: "high street" } } } } + * + * // Extracting a deeply nested property + * console.log( + * Option.some(employee1) + * .pipe(Option.flatMapNullable((employee) => employee.company?.address?.street?.name)) + * ) + * // Output: { _id: 'Option', _tag: 'Some', value: 'high street' } + * + * const employee2: Employee = { company: { address: { street: {} } } } + * + * // Property does not exist + * console.log( + * Option.some(employee2) + * .pipe(Option.flatMapNullable((employee) => employee.company?.address?.street?.name)) + * ) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Sequencing + * @since 2.0.0 + */ + (self: Option, f: (a: A) => B | null | undefined): Option> +} = dual( + 2, + (self: Option, f: (a: A) => B | null | undefined): Option> => + isNone(self) ? none() : fromNullable(f(self.value)) +) + +/** + * Flattens an `Option` of `Option` into a single `Option`. + * + * **Details** + * + * This function takes an `Option` that wraps another `Option` and flattens it + * into a single `Option`. If the outer `Option` is `Some`, the function + * extracts the inner `Option`. If the outer `Option` is `None`, the result + * remains `None`. + * + * This is useful for simplifying nested `Option` structures that may arise + * during functional operations. + * + * @category Sequencing + * @since 2.0.0 + */ +export const flatten: (self: Option>) => Option = flatMap(identity) + +/** + * Combines two `Option`s, keeping the value from the second `Option` if both + * are `Some`. + * + * **Details** + * + * This function takes two `Option`s and returns the second one if the first is + * `Some`. If the first `Option` is `None`, the result will also be `None`, + * regardless of the second `Option`. It effectively "zips" the two `Option`s + * while discarding the value from the first `Option`. + * + * This is particularly useful when sequencing computations where the result of + * the first computation is not needed, and you only care about the result of + * the second computation. + * + * @category Zipping + * @since 2.0.0 + */ +export const zipRight: { + /** + * Combines two `Option`s, keeping the value from the second `Option` if both + * are `Some`. + * + * **Details** + * + * This function takes two `Option`s and returns the second one if the first is + * `Some`. If the first `Option` is `None`, the result will also be `None`, + * regardless of the second `Option`. It effectively "zips" the two `Option`s + * while discarding the value from the first `Option`. + * + * This is particularly useful when sequencing computations where the result of + * the first computation is not needed, and you only care about the result of + * the second computation. + * + * @category Zipping + * @since 2.0.0 + */ + (that: Option): <_>(self: Option<_>) => Option + /** + * Combines two `Option`s, keeping the value from the second `Option` if both + * are `Some`. + * + * **Details** + * + * This function takes two `Option`s and returns the second one if the first is + * `Some`. If the first `Option` is `None`, the result will also be `None`, + * regardless of the second `Option`. It effectively "zips" the two `Option`s + * while discarding the value from the first `Option`. + * + * This is particularly useful when sequencing computations where the result of + * the first computation is not needed, and you only care about the result of + * the second computation. + * + * @category Zipping + * @since 2.0.0 + */ + (self: Option, that: Option): Option +} = dual(2, (self: Option, that: Option): Option => flatMap(self, () => that)) + +/** + * Combines two `Option`s, keeping the value from the first `Option` if both are + * `Some`. + * + * **Details** + * + * This function takes two `Option`s and returns the first one if it is `Some`. + * If either the first `Option` or the second `Option` is `None`, the result + * will be `None`. This operation "zips" the two `Option`s while discarding the + * value from the second `Option`. + * + * This is useful when sequencing computations where the second `Option` + * represents a dependency or condition that must hold, but its value is + * irrelevant. + * + * @category Zipping + * @since 2.0.0 + */ +export const zipLeft: { + /** + * Combines two `Option`s, keeping the value from the first `Option` if both are + * `Some`. + * + * **Details** + * + * This function takes two `Option`s and returns the first one if it is `Some`. + * If either the first `Option` or the second `Option` is `None`, the result + * will be `None`. This operation "zips" the two `Option`s while discarding the + * value from the second `Option`. + * + * This is useful when sequencing computations where the second `Option` + * represents a dependency or condition that must hold, but its value is + * irrelevant. + * + * @category Zipping + * @since 2.0.0 + */ + <_>(that: Option<_>): (self: Option) => Option + /** + * Combines two `Option`s, keeping the value from the first `Option` if both are + * `Some`. + * + * **Details** + * + * This function takes two `Option`s and returns the first one if it is `Some`. + * If either the first `Option` or the second `Option` is `None`, the result + * will be `None`. This operation "zips" the two `Option`s while discarding the + * value from the second `Option`. + * + * This is useful when sequencing computations where the second `Option` + * represents a dependency or condition that must hold, but its value is + * irrelevant. + * + * @category Zipping + * @since 2.0.0 + */ + (self: Option, that: Option): Option +} = dual(2, (self: Option, that: Option): Option => tap(self, () => that)) + +/** + * Composes two functions that return `Option` values, creating a new function + * that chains them together. + * + * **Details** + * + * This function allows you to compose two computations, each represented by a + * function that returns an `Option`. The result of the first function is passed + * to the second function if it is `Some`. If the first function returns `None`, + * the composed function short-circuits and returns `None` without invoking the + * second function. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const parse = (s: string): Option.Option => isNaN(Number(s)) ? Option.none() : Option.some(Number(s)) + * + * const double = (n: number): Option.Option => n > 0 ? Option.some(n * 2) : Option.none() + * + * const parseAndDouble = Option.composeK(parse, double) + * + * console.log(parseAndDouble("42")) + * // Output: { _id: 'Option', _tag: 'Some', value: 84 } + * + * console.log(parseAndDouble("not a number")) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Sequencing + * @since 2.0.0 + */ +export const composeK: { + /** + * Composes two functions that return `Option` values, creating a new function + * that chains them together. + * + * **Details** + * + * This function allows you to compose two computations, each represented by a + * function that returns an `Option`. The result of the first function is passed + * to the second function if it is `Some`. If the first function returns `None`, + * the composed function short-circuits and returns `None` without invoking the + * second function. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const parse = (s: string): Option.Option => isNaN(Number(s)) ? Option.none() : Option.some(Number(s)) + * + * const double = (n: number): Option.Option => n > 0 ? Option.some(n * 2) : Option.none() + * + * const parseAndDouble = Option.composeK(parse, double) + * + * console.log(parseAndDouble("42")) + * // Output: { _id: 'Option', _tag: 'Some', value: 84 } + * + * console.log(parseAndDouble("not a number")) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Sequencing + * @since 2.0.0 + */ + (bfc: (b: B) => Option): (afb: (a: A) => Option) => (a: A) => Option + /** + * Composes two functions that return `Option` values, creating a new function + * that chains them together. + * + * **Details** + * + * This function allows you to compose two computations, each represented by a + * function that returns an `Option`. The result of the first function is passed + * to the second function if it is `Some`. If the first function returns `None`, + * the composed function short-circuits and returns `None` without invoking the + * second function. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const parse = (s: string): Option.Option => isNaN(Number(s)) ? Option.none() : Option.some(Number(s)) + * + * const double = (n: number): Option.Option => n > 0 ? Option.some(n * 2) : Option.none() + * + * const parseAndDouble = Option.composeK(parse, double) + * + * console.log(parseAndDouble("42")) + * // Output: { _id: 'Option', _tag: 'Some', value: 84 } + * + * console.log(parseAndDouble("not a number")) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Sequencing + * @since 2.0.0 + */ + (afb: (a: A) => Option, bfc: (b: B) => Option): (a: A) => Option +} = dual(2, (afb: (a: A) => Option, bfc: (b: B) => Option) => (a: A): Option => flatMap(afb(a), bfc)) + +/** + * Applies the provided function `f` to the value of the `Option` if it is + * `Some` and returns the original `Option`, unless `f` returns `None`, in which + * case it returns `None`. + * + * **Details** + * + * This function allows you to perform additional computations on the value of + * an `Option` without modifying its original value. If the `Option` is `Some`, + * the provided function `f` is executed with the value, and its result + * determines whether the original `Option` is returned (`Some`) or the result + * is `None` if `f` returns `None`. If the input `Option` is `None`, the + * function is not executed, and `None` is returned. + * + * This is particularly useful for applying side conditions or performing + * validation checks while retaining the original `Option`'s value. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const getInteger = (n: number) => Number.isInteger(n) ? Option.some(n) : Option.none() + * + * console.log(Option.tap(Option.none(), getInteger)) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(Option.tap(Option.some(1), getInteger)) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * + * console.log(Option.tap(Option.some(1.14), getInteger)) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Sequencing + * @since 2.0.0 + */ +export const tap: { + /** + * Applies the provided function `f` to the value of the `Option` if it is + * `Some` and returns the original `Option`, unless `f` returns `None`, in which + * case it returns `None`. + * + * **Details** + * + * This function allows you to perform additional computations on the value of + * an `Option` without modifying its original value. If the `Option` is `Some`, + * the provided function `f` is executed with the value, and its result + * determines whether the original `Option` is returned (`Some`) or the result + * is `None` if `f` returns `None`. If the input `Option` is `None`, the + * function is not executed, and `None` is returned. + * + * This is particularly useful for applying side conditions or performing + * validation checks while retaining the original `Option`'s value. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const getInteger = (n: number) => Number.isInteger(n) ? Option.some(n) : Option.none() + * + * console.log(Option.tap(Option.none(), getInteger)) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(Option.tap(Option.some(1), getInteger)) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * + * console.log(Option.tap(Option.some(1.14), getInteger)) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Sequencing + * @since 2.0.0 + */ + (f: (a: A) => Option): (self: Option) => Option + /** + * Applies the provided function `f` to the value of the `Option` if it is + * `Some` and returns the original `Option`, unless `f` returns `None`, in which + * case it returns `None`. + * + * **Details** + * + * This function allows you to perform additional computations on the value of + * an `Option` without modifying its original value. If the `Option` is `Some`, + * the provided function `f` is executed with the value, and its result + * determines whether the original `Option` is returned (`Some`) or the result + * is `None` if `f` returns `None`. If the input `Option` is `None`, the + * function is not executed, and `None` is returned. + * + * This is particularly useful for applying side conditions or performing + * validation checks while retaining the original `Option`'s value. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const getInteger = (n: number) => Number.isInteger(n) ? Option.some(n) : Option.none() + * + * console.log(Option.tap(Option.none(), getInteger)) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(Option.tap(Option.some(1), getInteger)) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * + * console.log(Option.tap(Option.some(1.14), getInteger)) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Sequencing + * @since 2.0.0 + */ + (self: Option, f: (a: A) => Option): Option +} = dual(2, (self: Option, f: (a: A) => Option): Option => flatMap(self, (a) => map(f(a), () => a))) + +/** + * Combines two `Option` values into a single `Option` containing a tuple of + * their values if both are `Some`. + * + * **Details** + * + * This function takes two `Option`s and combines their values into a tuple `[A, + * B]` if both are `Some`. If either of the `Option`s is `None`, the result is + * `None`. This is particularly useful for combining multiple `Option` values + * into a single one, ensuring both contain valid values. + * + * @category Combining + * @since 2.0.0 + */ +export const product = (self: Option, that: Option): Option<[A, B]> => + isSome(self) && isSome(that) ? some([self.value, that.value]) : none() + +/** + * Combines an `Option` with a collection of `Option`s into a single `Option` + * containing a tuple of their values if all are `Some`. + * + * **Details** + * + * This function takes a primary `Option` and a collection of `Option`s and + * combines their values into a tuple `[A, ...Array]` if all are `Some`. If + * the primary `Option` or any `Option` in the collection is `None`, the result + * is `None`. + * + * @category Combining + * @since 2.0.0 + */ +export const productMany = ( + self: Option, + collection: Iterable> +): Option<[A, ...Array]> => { + if (isNone(self)) { + return none() + } + const out: [A, ...Array] = [self.value] + for (const o of collection) { + if (isNone(o)) { + return none() + } + out.push(o.value) + } + return some(out) +} + +/** + * Combines a structure of `Option`s into a single `Option` containing the + * values with the same structure. + * + * **Details** + * + * This function takes a structure of `Option`s (a tuple, struct, or iterable) + * and produces a single `Option` that contains the values from the input + * structure if all `Option`s are `Some`. If any `Option` in the input is + * `None`, the result is `None`. The structure of the input is preserved in the + * output. + * + * - If the input is a tuple (e.g., an array), the result will be an `Option` + * containing a tuple with the same length. + * - If the input is a struct (e.g., an object), the result will be an `Option` + * containing a struct with the same keys. + * - If the input is an iterable, the result will be an `Option` containing an + * array. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const maybeName: Option.Option = Option.some("John") + * const maybeAge: Option.Option = Option.some(25) + * + * // ┌─── Option<[string, number]> + * // ▼ + * const tuple = Option.all([maybeName, maybeAge]) + * console.log(tuple) + * // Output: + * // { _id: 'Option', _tag: 'Some', value: [ 'John', 25 ] } + * + * // ┌─── Option<{ name: string; age: number; }> + * // ▼ + * const struct = Option.all({ name: maybeName, age: maybeAge }) + * console.log(struct) + * // Output: + * // { _id: 'Option', _tag: 'Some', value: { name: 'John', age: 25 } } + * ``` + * + * @category Combining + * @since 2.0.0 + */ +// @ts-expect-error +export const all: > | Record>>( + input: I +) => [I] extends [ReadonlyArray>] ? Option< + { -readonly [K in keyof I]: [I[K]] extends [Option] ? A : never } + > + : [I] extends [Iterable>] ? Option> + : Option<{ -readonly [K in keyof I]: [I[K]] extends [Option] ? A : never }> = ( + input: Iterable> | Record> + ): Option => { + if (Symbol.iterator in input) { + const out: Array> = [] + for (const o of (input as Iterable>)) { + if (isNone(o)) { + return none() + } + out.push(o.value) + } + return some(out) + } + + const out: Record = {} + for (const key of Object.keys(input)) { + const o = input[key] + if (isNone(o)) { + return none() + } + out[key] = o.value + } + return some(out) + } + +/** + * Combines two `Option` values into a new `Option` by applying a provided + * function to their values. + * + * **Details** + * + * This function takes two `Option` values (`self` and `that`) and a combining + * function `f`. If both `Option` values are `Some`, the function `f` is applied + * to their values, and the result is wrapped in a new `Some`. If either + * `Option` is `None`, the result is `None`. + * + * This utility is useful for combining two optional computations into a single + * result while maintaining type safety and avoiding explicit checks for `None`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const maybeName: Option.Option = Option.some("John") + * const maybeAge: Option.Option = Option.some(25) + * + * // Combine the name and age into a person object + * const person = Option.zipWith(maybeName, maybeAge, (name, age) => ({ + * name: name.toUpperCase(), + * age + * })) + * + * console.log(person) + * // Output: + * // { _id: 'Option', _tag: 'Some', value: { name: 'JOHN', age: 25 } } + * ``` + * + * @category Zipping + * @since 2.0.0 + */ +export const zipWith: { + /** + * Combines two `Option` values into a new `Option` by applying a provided + * function to their values. + * + * **Details** + * + * This function takes two `Option` values (`self` and `that`) and a combining + * function `f`. If both `Option` values are `Some`, the function `f` is applied + * to their values, and the result is wrapped in a new `Some`. If either + * `Option` is `None`, the result is `None`. + * + * This utility is useful for combining two optional computations into a single + * result while maintaining type safety and avoiding explicit checks for `None`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const maybeName: Option.Option = Option.some("John") + * const maybeAge: Option.Option = Option.some(25) + * + * // Combine the name and age into a person object + * const person = Option.zipWith(maybeName, maybeAge, (name, age) => ({ + * name: name.toUpperCase(), + * age + * })) + * + * console.log(person) + * // Output: + * // { _id: 'Option', _tag: 'Some', value: { name: 'JOHN', age: 25 } } + * ``` + * + * @category Zipping + * @since 2.0.0 + */ + (that: Option, f: (a: A, b: B) => C): (self: Option) => Option + /** + * Combines two `Option` values into a new `Option` by applying a provided + * function to their values. + * + * **Details** + * + * This function takes two `Option` values (`self` and `that`) and a combining + * function `f`. If both `Option` values are `Some`, the function `f` is applied + * to their values, and the result is wrapped in a new `Some`. If either + * `Option` is `None`, the result is `None`. + * + * This utility is useful for combining two optional computations into a single + * result while maintaining type safety and avoiding explicit checks for `None`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const maybeName: Option.Option = Option.some("John") + * const maybeAge: Option.Option = Option.some(25) + * + * // Combine the name and age into a person object + * const person = Option.zipWith(maybeName, maybeAge, (name, age) => ({ + * name: name.toUpperCase(), + * age + * })) + * + * console.log(person) + * // Output: + * // { _id: 'Option', _tag: 'Some', value: { name: 'JOHN', age: 25 } } + * ``` + * + * @category Zipping + * @since 2.0.0 + */ + (self: Option, that: Option, f: (a: A, b: B) => C): Option +} = dual( + 3, + (self: Option, that: Option, f: (a: A, b: B) => C): Option => + map(product(self, that), ([a, b]) => f(a, b)) +) + +/** + * Applies a function inside a `Some` to a value inside another `Some`, + * combining them into a new `Option`. + * + * **Details** + * + * This function allows you to apply a function wrapped in an `Option` (`self`) + * to a value wrapped in another `Option` (`that`). If both `Option`s are + * `Some`, the function is applied to the value, and the result is wrapped in a + * new `Some`. If either `Option` is `None`, the result is `None`. + * + * @category Combining + * @since 2.0.0 + */ +export const ap: { + /** + * Applies a function inside a `Some` to a value inside another `Some`, + * combining them into a new `Option`. + * + * **Details** + * + * This function allows you to apply a function wrapped in an `Option` (`self`) + * to a value wrapped in another `Option` (`that`). If both `Option`s are + * `Some`, the function is applied to the value, and the result is wrapped in a + * new `Some`. If either `Option` is `None`, the result is `None`. + * + * @category Combining + * @since 2.0.0 + */ + (that: Option): (self: Option<(a: A) => B>) => Option + /** + * Applies a function inside a `Some` to a value inside another `Some`, + * combining them into a new `Option`. + * + * **Details** + * + * This function allows you to apply a function wrapped in an `Option` (`self`) + * to a value wrapped in another `Option` (`that`). If both `Option`s are + * `Some`, the function is applied to the value, and the result is wrapped in a + * new `Some`. If either `Option` is `None`, the result is `None`. + * + * @category Combining + * @since 2.0.0 + */ + (self: Option<(a: A) => B>, that: Option): Option +} = dual(2, (self: Option<(a: A) => B>, that: Option): Option => zipWith(self, that, (f, a) => f(a))) + +/** + * Reduces an `Iterable` of `Option` to a single value of type `B`, ignoring + * elements that are `None`. + * + * **Details** + * + * This function takes an initial value of type `B` and a reducing function `f` + * that combines the accumulator with values of type `A`. It processes an + * iterable of `Option`, applying `f` only to the `Some` values while + * ignoring the `None` values. The result is a single value of type `B`. + * + * This utility is particularly useful for aggregating values from an iterable + * of `Option`s while skipping the absent (`None`) values. + * + * @example + * ```ts + * import { Option, pipe } from "effect" + * + * const iterable = [Option.some(1), Option.none(), Option.some(2), Option.none()] + * + * console.log(pipe(iterable, Option.reduceCompact(0, (b, a) => b + a))) + * // Output: 3 + * ``` + * + * @category Reducing + * @since 2.0.0 + */ +export const reduceCompact: { + /** + * Reduces an `Iterable` of `Option` to a single value of type `B`, ignoring + * elements that are `None`. + * + * **Details** + * + * This function takes an initial value of type `B` and a reducing function `f` + * that combines the accumulator with values of type `A`. It processes an + * iterable of `Option`, applying `f` only to the `Some` values while + * ignoring the `None` values. The result is a single value of type `B`. + * + * This utility is particularly useful for aggregating values from an iterable + * of `Option`s while skipping the absent (`None`) values. + * + * @example + * ```ts + * import { Option, pipe } from "effect" + * + * const iterable = [Option.some(1), Option.none(), Option.some(2), Option.none()] + * + * console.log(pipe(iterable, Option.reduceCompact(0, (b, a) => b + a))) + * // Output: 3 + * ``` + * + * @category Reducing + * @since 2.0.0 + */ + (b: B, f: (b: B, a: A) => B): (self: Iterable>) => B + /** + * Reduces an `Iterable` of `Option` to a single value of type `B`, ignoring + * elements that are `None`. + * + * **Details** + * + * This function takes an initial value of type `B` and a reducing function `f` + * that combines the accumulator with values of type `A`. It processes an + * iterable of `Option`, applying `f` only to the `Some` values while + * ignoring the `None` values. The result is a single value of type `B`. + * + * This utility is particularly useful for aggregating values from an iterable + * of `Option`s while skipping the absent (`None`) values. + * + * @example + * ```ts + * import { Option, pipe } from "effect" + * + * const iterable = [Option.some(1), Option.none(), Option.some(2), Option.none()] + * + * console.log(pipe(iterable, Option.reduceCompact(0, (b, a) => b + a))) + * // Output: 3 + * ``` + * + * @category Reducing + * @since 2.0.0 + */ + (self: Iterable>, b: B, f: (b: B, a: A) => B): B +} = dual( + 3, + (self: Iterable>, b: B, f: (b: B, a: A) => B): B => { + let out: B = b + for (const oa of self) { + if (isSome(oa)) { + out = f(out, oa.value) + } + } + return out + } +) + +/** + * Converts an `Option` into an `Array`. + * If the input is `None`, an empty array is returned. + * If the input is `Some`, its value is wrapped in a single-element array. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.toArray(Option.some(1))) + * // Output: [1] + * + * console.log(Option.toArray(Option.none())) + * // Output: [] + * ``` + * + * @category Conversions + * @since 2.0.0 + */ +export const toArray = (self: Option): Array => isNone(self) ? [] : [self.value] + +/** + * Splits an `Option` into two `Option`s based on the result of a mapping + * function that produces an `Either`. + * + * **Details** + * + * This function takes an `Option` and a mapping function `f` that converts its + * value into an `Either`. It returns a tuple of two `Option`s: + * + * - The first `Option` (`left`) contains the value from the `Left` side of the + * `Either` if it exists, otherwise `None`. + * - The second `Option` (`right`) contains the value from the `Right` side of + * the `Either` if it exists, otherwise `None`. + * + * If the input `Option` is `None`, both returned `Option`s are `None`. + * + * This utility is useful for filtering and categorizing the contents of an + * `Option` based on a bifurcating computation. + * + * @category Filtering + * @since 2.0.0 + */ +export const partitionMap: { + /** + * Splits an `Option` into two `Option`s based on the result of a mapping + * function that produces an `Either`. + * + * **Details** + * + * This function takes an `Option` and a mapping function `f` that converts its + * value into an `Either`. It returns a tuple of two `Option`s: + * + * - The first `Option` (`left`) contains the value from the `Left` side of the + * `Either` if it exists, otherwise `None`. + * - The second `Option` (`right`) contains the value from the `Right` side of + * the `Either` if it exists, otherwise `None`. + * + * If the input `Option` is `None`, both returned `Option`s are `None`. + * + * This utility is useful for filtering and categorizing the contents of an + * `Option` based on a bifurcating computation. + * + * @category Filtering + * @since 2.0.0 + */ + (f: (a: A) => Either): (self: Option) => [left: Option, right: Option] + /** + * Splits an `Option` into two `Option`s based on the result of a mapping + * function that produces an `Either`. + * + * **Details** + * + * This function takes an `Option` and a mapping function `f` that converts its + * value into an `Either`. It returns a tuple of two `Option`s: + * + * - The first `Option` (`left`) contains the value from the `Left` side of the + * `Either` if it exists, otherwise `None`. + * - The second `Option` (`right`) contains the value from the `Right` side of + * the `Either` if it exists, otherwise `None`. + * + * If the input `Option` is `None`, both returned `Option`s are `None`. + * + * This utility is useful for filtering and categorizing the contents of an + * `Option` based on a bifurcating computation. + * + * @category Filtering + * @since 2.0.0 + */ + (self: Option, f: (a: A) => Either): [left: Option, right: Option] +} = dual(2, ( + self: Option, + f: (a: A) => Either +): [excluded: Option, satisfying: Option] => { + if (isNone(self)) { + return [none(), none()] + } + const e = f(self.value) + return either.isLeft(e) ? [some(e.left), none()] : [none(), some(e.right)] +}) + +// TODO(4.0): remove? +/** + * Alias of {@link flatMap}. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Transform and filter numbers + * const transformEven = (n: Option.Option): Option.Option => + * Option.filterMap(n, (n) => (n % 2 === 0 ? Option.some(`Even: ${n}`) : Option.none())) + * + * console.log(transformEven(Option.none())) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(transformEven(Option.some(1))) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(transformEven(Option.some(2))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'Even: 2' } + * ``` + * + * @category Filtering + * @since 2.0.0 + */ +export const filterMap: { + // TODO(4.0): remove? + /** + * Alias of {@link flatMap}. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Transform and filter numbers + * const transformEven = (n: Option.Option): Option.Option => + * Option.filterMap(n, (n) => (n % 2 === 0 ? Option.some(`Even: ${n}`) : Option.none())) + * + * console.log(transformEven(Option.none())) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(transformEven(Option.some(1))) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(transformEven(Option.some(2))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'Even: 2' } + * ``` + * + * @category Filtering + * @since 2.0.0 + */ + (f: (a: A) => Option): (self: Option) => Option + // TODO(4.0): remove? + /** + * Alias of {@link flatMap}. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Transform and filter numbers + * const transformEven = (n: Option.Option): Option.Option => + * Option.filterMap(n, (n) => (n % 2 === 0 ? Option.some(`Even: ${n}`) : Option.none())) + * + * console.log(transformEven(Option.none())) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(transformEven(Option.some(1))) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(transformEven(Option.some(2))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'Even: 2' } + * ``` + * + * @category Filtering + * @since 2.0.0 + */ + (self: Option, f: (a: A) => Option): Option +} = flatMap + +/** + * Filters an `Option` using a predicate. If the predicate is not satisfied or the `Option` is `None` returns `None`. + * + * If you need to change the type of the `Option` in addition to filtering, see `filterMap`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const removeEmptyString = (input: Option.Option) => + * Option.filter(input, (value) => value !== "") + * + * console.log(removeEmptyString(Option.none())) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(removeEmptyString(Option.some(""))) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(removeEmptyString(Option.some("a"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * ``` + * + * @category Filtering + * @since 2.0.0 + */ +export const filter: { + /** + * Filters an `Option` using a predicate. If the predicate is not satisfied or the `Option` is `None` returns `None`. + * + * If you need to change the type of the `Option` in addition to filtering, see `filterMap`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const removeEmptyString = (input: Option.Option) => + * Option.filter(input, (value) => value !== "") + * + * console.log(removeEmptyString(Option.none())) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(removeEmptyString(Option.some(""))) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(removeEmptyString(Option.some("a"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * ``` + * + * @category Filtering + * @since 2.0.0 + */ + (refinement: Refinement, B>): (self: Option) => Option + /** + * Filters an `Option` using a predicate. If the predicate is not satisfied or the `Option` is `None` returns `None`. + * + * If you need to change the type of the `Option` in addition to filtering, see `filterMap`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const removeEmptyString = (input: Option.Option) => + * Option.filter(input, (value) => value !== "") + * + * console.log(removeEmptyString(Option.none())) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(removeEmptyString(Option.some(""))) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(removeEmptyString(Option.some("a"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * ``` + * + * @category Filtering + * @since 2.0.0 + */ + (predicate: Predicate>): (self: Option) => Option + /** + * Filters an `Option` using a predicate. If the predicate is not satisfied or the `Option` is `None` returns `None`. + * + * If you need to change the type of the `Option` in addition to filtering, see `filterMap`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const removeEmptyString = (input: Option.Option) => + * Option.filter(input, (value) => value !== "") + * + * console.log(removeEmptyString(Option.none())) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(removeEmptyString(Option.some(""))) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(removeEmptyString(Option.some("a"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * ``` + * + * @category Filtering + * @since 2.0.0 + */ + (self: Option, refinement: Refinement): Option + /** + * Filters an `Option` using a predicate. If the predicate is not satisfied or the `Option` is `None` returns `None`. + * + * If you need to change the type of the `Option` in addition to filtering, see `filterMap`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const removeEmptyString = (input: Option.Option) => + * Option.filter(input, (value) => value !== "") + * + * console.log(removeEmptyString(Option.none())) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(removeEmptyString(Option.some(""))) + * // Output: { _id: 'Option', _tag: 'None' } + * + * console.log(removeEmptyString(Option.some("a"))) + * // Output: { _id: 'Option', _tag: 'Some', value: 'a' } + * ``` + * + * @category Filtering + * @since 2.0.0 + */ + (self: Option, predicate: Predicate): Option +} = dual( + 2, + (self: Option, predicate: Predicate): Option => + filterMap(self, (b) => (predicate(b) ? option.some(b) : option.none)) +) + +/** + * Creates an `Equivalence` instance for comparing `Option` values, using a + * provided `Equivalence` for the inner type. + * + * **Details** + * + * This function takes an `Equivalence` instance for a specific type `A` and + * produces an `Equivalence` instance for `Option`. The resulting + * `Equivalence` determines whether two `Option` values are equivalent: + * + * - Two `None`s are considered equivalent. + * - A `Some` and a `None` are not equivalent. + * - Two `Some` values are equivalent if their inner values are equivalent + * according to the provided `Equivalence`. + * + * **Example** (Comparing Optional Numbers for Equivalence) + * + * ```ts + * import { Number, Option } from "effect" + * + * const isEquivalent = Option.getEquivalence(Number.Equivalence) + * + * console.log(isEquivalent(Option.none(), Option.none())) + * // Output: true + * + * console.log(isEquivalent(Option.none(), Option.some(1))) + * // Output: false + * + * console.log(isEquivalent(Option.some(1), Option.none())) + * // Output: false + * + * console.log(isEquivalent(Option.some(1), Option.some(2))) + * // Output: false + * + * console.log(isEquivalent(Option.some(1), Option.some(1))) + * // Output: true + * ``` + * + * @category Equivalence + * @since 2.0.0 + */ +export const getEquivalence = (isEquivalent: Equivalence.Equivalence): Equivalence.Equivalence> => + Equivalence.make((x, y) => isNone(x) ? isNone(y) : isNone(y) ? false : isEquivalent(x.value, y.value)) + +/** + * Creates an `Order` instance for comparing `Option` values, using a provided + * `Order` for the inner type. + * + * **Details** + * + * This function produces an `Order` instance for `Option`, allowing `Option` + * values to be compared: + * + * - `None` is always considered less than any `Some` value. + * - If both are `Some`, their inner values are compared using the provided + * `Order` instance. + * + * @example + * ```ts + * import { Number, Option } from "effect" + * + * const order = Option.getOrder(Number.Order) + * + * console.log(order(Option.none(), Option.none())) + * // Output: 0 + * + * console.log(order(Option.none(), Option.some(1))) + * // Output: -1 + * + * console.log(order(Option.some(1), Option.none())) + * // Output: 1 + * + * console.log(order(Option.some(1), Option.some(2))) + * // Output: -1 + * + * console.log(order(Option.some(1), Option.some(1))) + * // Output: 0 + * ``` + * + * @category Sorting + * @since 2.0.0 + */ +export const getOrder = (O: Order): Order> => + order.make((self, that) => isSome(self) ? (isSome(that) ? O(self.value, that.value) : 1) : -1) + +/** + * Lifts a binary function to work with `Option` values, allowing the function + * to operate on two `Option`s. + * + * **Details** + * + * This function takes a binary function `f` and returns a new function that + * applies `f` to the values of two `Option`s (`self` and `that`). If both + * `Option`s are `Some`, the binary function `f` is applied to their values, and + * the result is wrapped in a new `Some`. If either `Option` is `None`, the + * result is `None`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // A binary function to add two numbers + * const add = (a: number, b: number): number => a + b + * + * // Lift the `add` function to work with `Option` values + * const addOptions = Option.lift2(add) + * + * // Both `Option`s are `Some` + * console.log(addOptions(Option.some(2), Option.some(3))) + * // Output: { _id: 'Option', _tag: 'Some', value: 5 } + * + * // One `Option` is `None` + * console.log(addOptions(Option.some(2), Option.none())) + * // Output: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Lifting + * @since 2.0.0 + */ +export const lift2 = (f: (a: A, b: B) => C): { + (that: Option): (self: Option) => Option + (self: Option, that: Option): Option +} => dual(2, (self: Option, that: Option): Option => zipWith(self, that, f)) + +/** + * Lifts a `Predicate` or `Refinement` into the `Option` context, returning a + * `Some` of the input value if the predicate is satisfied, or `None` otherwise. + * + * **Details** + * + * This function transforms a `Predicate` (or a more specific `Refinement`) into + * a function that produces an `Option`. If the predicate evaluates to `true`, + * the input value is wrapped in a `Some`. If the predicate evaluates to + * `false`, the result is `None`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Check if a number is positive + * const isPositive = (n: number) => n > 0 + * + * // ┌─── (b: number) => Option + * // ▼ + * const parsePositive = Option.liftPredicate(isPositive) + * + * console.log(parsePositive(1)) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * + * console.log(parsePositive(-1)) + * // OUtput: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Lifting + * @since 2.0.0 + */ +export const liftPredicate: { // Note: I intentionally avoid using the NoInfer pattern here. + (refinement: Refinement): (a: A) => Option + /** + * Lifts a `Predicate` or `Refinement` into the `Option` context, returning a + * `Some` of the input value if the predicate is satisfied, or `None` otherwise. + * + * **Details** + * + * This function transforms a `Predicate` (or a more specific `Refinement`) into + * a function that produces an `Option`. If the predicate evaluates to `true`, + * the input value is wrapped in a `Some`. If the predicate evaluates to + * `false`, the result is `None`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Check if a number is positive + * const isPositive = (n: number) => n > 0 + * + * // ┌─── (b: number) => Option + * // ▼ + * const parsePositive = Option.liftPredicate(isPositive) + * + * console.log(parsePositive(1)) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * + * console.log(parsePositive(-1)) + * // OUtput: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Lifting + * @since 2.0.0 + */ + (predicate: Predicate): (b: B) => Option + /** + * Lifts a `Predicate` or `Refinement` into the `Option` context, returning a + * `Some` of the input value if the predicate is satisfied, or `None` otherwise. + * + * **Details** + * + * This function transforms a `Predicate` (or a more specific `Refinement`) into + * a function that produces an `Option`. If the predicate evaluates to `true`, + * the input value is wrapped in a `Some`. If the predicate evaluates to + * `false`, the result is `None`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Check if a number is positive + * const isPositive = (n: number) => n > 0 + * + * // ┌─── (b: number) => Option + * // ▼ + * const parsePositive = Option.liftPredicate(isPositive) + * + * console.log(parsePositive(1)) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * + * console.log(parsePositive(-1)) + * // OUtput: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Lifting + * @since 2.0.0 + */ + (self: A, refinement: Refinement): Option + /** + * Lifts a `Predicate` or `Refinement` into the `Option` context, returning a + * `Some` of the input value if the predicate is satisfied, or `None` otherwise. + * + * **Details** + * + * This function transforms a `Predicate` (or a more specific `Refinement`) into + * a function that produces an `Option`. If the predicate evaluates to `true`, + * the input value is wrapped in a `Some`. If the predicate evaluates to + * `false`, the result is `None`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * // Check if a number is positive + * const isPositive = (n: number) => n > 0 + * + * // ┌─── (b: number) => Option + * // ▼ + * const parsePositive = Option.liftPredicate(isPositive) + * + * console.log(parsePositive(1)) + * // Output: { _id: 'Option', _tag: 'Some', value: 1 } + * + * console.log(parsePositive(-1)) + * // OUtput: { _id: 'Option', _tag: 'None' } + * ``` + * + * @category Lifting + * @since 2.0.0 + */ + (self: B, predicate: Predicate): Option +} = dual( + 2, + (b: B, predicate: Predicate): Option => predicate(b) ? some(b) : none() +) + +/** + * Returns a function that checks if an `Option` contains a specified value, + * using a provided equivalence function. + * + * **Details** + * + * This function allows you to check whether an `Option` contains a specific + * value. It uses an equivalence function `isEquivalent` to compare the value + * inside the `Option` to the provided value. If the `Option` is `Some` and the + * equivalence function returns `true`, the result is `true`. If the `Option` is + * `None` or the values are not equivalent, the result is `false`. + * + * @example + * ```ts + * import { Number, Option } from "effect" + * + * const contains = Option.containsWith(Number.Equivalence) + * + * console.log(Option.some(2).pipe(contains(2))) + * // Output: true + * + * console.log(Option.some(1).pipe(contains(2))) + * // Output: false + * + * console.log(Option.none().pipe(contains(2))) + * // Output: false + * ``` + * + * @see {@link contains} for a version that uses the default `Equivalence`. + * + * @category Elements + * @since 2.0.0 + */ +export const containsWith = (isEquivalent: (self: A, that: A) => boolean): { + (a: A): (self: Option) => boolean + (self: Option, a: A): boolean +} => dual(2, (self: Option, a: A): boolean => isNone(self) ? false : isEquivalent(self.value, a)) + +const _equivalence = Equal.equivalence() + +/** + * Returns a function that checks if an `Option` contains a specified value + * using the default `Equivalence`. + * + * **Details** + * + * This function allows you to check whether an `Option` contains a specific + * value. It uses the default `Equivalence` for equality comparison. If the + * `Option` is `Some` and its value is equivalent to the provided value, the + * result is `true`. If the `Option` is `None` or the values are not equivalent, + * the result is `false`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.some(2).pipe(Option.contains(2))) + * // Output: true + * + * console.log(Option.some(1).pipe(Option.contains(2))) + * // Output: false + * + * console.log(Option.none().pipe(Option.contains(2))) + * // Output: false + * ``` + * + * @see {@link containsWith} for a version that allows you to specify a custom equivalence function. + * + * @category Elements + * @since 2.0.0 + */ +export const contains: { + /** + * Returns a function that checks if an `Option` contains a specified value + * using the default `Equivalence`. + * + * **Details** + * + * This function allows you to check whether an `Option` contains a specific + * value. It uses the default `Equivalence` for equality comparison. If the + * `Option` is `Some` and its value is equivalent to the provided value, the + * result is `true`. If the `Option` is `None` or the values are not equivalent, + * the result is `false`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.some(2).pipe(Option.contains(2))) + * // Output: true + * + * console.log(Option.some(1).pipe(Option.contains(2))) + * // Output: false + * + * console.log(Option.none().pipe(Option.contains(2))) + * // Output: false + * ``` + * + * @see {@link containsWith} for a version that allows you to specify a custom equivalence function. + * + * @category Elements + * @since 2.0.0 + */ + (a: A): (self: Option) => boolean + /** + * Returns a function that checks if an `Option` contains a specified value + * using the default `Equivalence`. + * + * **Details** + * + * This function allows you to check whether an `Option` contains a specific + * value. It uses the default `Equivalence` for equality comparison. If the + * `Option` is `Some` and its value is equivalent to the provided value, the + * result is `true`. If the `Option` is `None` or the values are not equivalent, + * the result is `false`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * console.log(Option.some(2).pipe(Option.contains(2))) + * // Output: true + * + * console.log(Option.some(1).pipe(Option.contains(2))) + * // Output: false + * + * console.log(Option.none().pipe(Option.contains(2))) + * // Output: false + * ``` + * + * @see {@link containsWith} for a version that allows you to specify a custom equivalence function. + * + * @category Elements + * @since 2.0.0 + */ + (self: Option, a: A): boolean +} = containsWith(_equivalence) + +/** + * Checks if a value in an `Option` satisfies a given predicate or refinement. + * + * **Details** + * + * This function allows you to check if a value inside a `Some` meets a + * specified condition. If the `Option` is `None`, the result is `false`. If the + * `Option` is `Some`, the provided predicate or refinement is applied to the + * value: + * + * - If the condition is met, the result is `true`. + * - If the condition is not met, the result is `false`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const isEven = (n: number) => n % 2 === 0 + * + * console.log(Option.some(2).pipe(Option.exists(isEven))) + * // Output: true + * + * console.log(Option.some(1).pipe(Option.exists(isEven))) + * // Output: false + * + * console.log(Option.none().pipe(Option.exists(isEven))) + * // Output: false + * ``` + * + * @category Elements + * @since 2.0.0 + */ +export const exists: { + /** + * Checks if a value in an `Option` satisfies a given predicate or refinement. + * + * **Details** + * + * This function allows you to check if a value inside a `Some` meets a + * specified condition. If the `Option` is `None`, the result is `false`. If the + * `Option` is `Some`, the provided predicate or refinement is applied to the + * value: + * + * - If the condition is met, the result is `true`. + * - If the condition is not met, the result is `false`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const isEven = (n: number) => n % 2 === 0 + * + * console.log(Option.some(2).pipe(Option.exists(isEven))) + * // Output: true + * + * console.log(Option.some(1).pipe(Option.exists(isEven))) + * // Output: false + * + * console.log(Option.none().pipe(Option.exists(isEven))) + * // Output: false + * ``` + * + * @category Elements + * @since 2.0.0 + */ + (refinement: Refinement, B>): (self: Option) => self is Option + /** + * Checks if a value in an `Option` satisfies a given predicate or refinement. + * + * **Details** + * + * This function allows you to check if a value inside a `Some` meets a + * specified condition. If the `Option` is `None`, the result is `false`. If the + * `Option` is `Some`, the provided predicate or refinement is applied to the + * value: + * + * - If the condition is met, the result is `true`. + * - If the condition is not met, the result is `false`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const isEven = (n: number) => n % 2 === 0 + * + * console.log(Option.some(2).pipe(Option.exists(isEven))) + * // Output: true + * + * console.log(Option.some(1).pipe(Option.exists(isEven))) + * // Output: false + * + * console.log(Option.none().pipe(Option.exists(isEven))) + * // Output: false + * ``` + * + * @category Elements + * @since 2.0.0 + */ + (predicate: Predicate>): (self: Option) => boolean + /** + * Checks if a value in an `Option` satisfies a given predicate or refinement. + * + * **Details** + * + * This function allows you to check if a value inside a `Some` meets a + * specified condition. If the `Option` is `None`, the result is `false`. If the + * `Option` is `Some`, the provided predicate or refinement is applied to the + * value: + * + * - If the condition is met, the result is `true`. + * - If the condition is not met, the result is `false`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const isEven = (n: number) => n % 2 === 0 + * + * console.log(Option.some(2).pipe(Option.exists(isEven))) + * // Output: true + * + * console.log(Option.some(1).pipe(Option.exists(isEven))) + * // Output: false + * + * console.log(Option.none().pipe(Option.exists(isEven))) + * // Output: false + * ``` + * + * @category Elements + * @since 2.0.0 + */ + (self: Option, refinement: Refinement): self is Option + /** + * Checks if a value in an `Option` satisfies a given predicate or refinement. + * + * **Details** + * + * This function allows you to check if a value inside a `Some` meets a + * specified condition. If the `Option` is `None`, the result is `false`. If the + * `Option` is `Some`, the provided predicate or refinement is applied to the + * value: + * + * - If the condition is met, the result is `true`. + * - If the condition is not met, the result is `false`. + * + * @example + * ```ts + * import { Option } from "effect" + * + * const isEven = (n: number) => n % 2 === 0 + * + * console.log(Option.some(2).pipe(Option.exists(isEven))) + * // Output: true + * + * console.log(Option.some(1).pipe(Option.exists(isEven))) + * // Output: false + * + * console.log(Option.none().pipe(Option.exists(isEven))) + * // Output: false + * ``` + * + * @category Elements + * @since 2.0.0 + */ + (self: Option, predicate: Predicate): boolean +} = dual( + 2, + (self: Option, refinement: Refinement): self is Option => + isNone(self) ? false : refinement(self.value) +) + +// ------------------------------------------------------------------------------------- +// do notation +// ------------------------------------------------------------------------------------- + +/** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Option` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * 5. Regular `Option` functions like `map` and `filter` can still be used within the do simulation. These functions will receive the accumulated variables as arguments within the scope + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Option, pipe } from "effect" + * + * const result = pipe( + * Option.Do, + * Option.bind("x", () => Option.some(2)), + * Option.bind("y", () => Option.some(3)), + * Option.let("sum", ({ x, y }) => x + y), + * Option.filter(({ x, y }) => x * y > 5) + * ) + * assert.deepStrictEqual(result, Option.some({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bind} + * @see {@link let_ let} + * + * @category Do notation + * @since 2.0.0 + */ +export const bindTo: { + // ------------------------------------------------------------------------------------- + // do notation + // ------------------------------------------------------------------------------------- + + /** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Option` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * 5. Regular `Option` functions like `map` and `filter` can still be used within the do simulation. These functions will receive the accumulated variables as arguments within the scope + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Option, pipe } from "effect" + * + * const result = pipe( + * Option.Do, + * Option.bind("x", () => Option.some(2)), + * Option.bind("y", () => Option.some(3)), + * Option.let("sum", ({ x, y }) => x + y), + * Option.filter(({ x, y }) => x * y > 5) + * ) + * assert.deepStrictEqual(result, Option.some({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bind} + * @see {@link let_ let} + * + * @category Do notation + * @since 2.0.0 + */ + (name: N): (self: Option) => Option<{ [K in N]: A }> + // ------------------------------------------------------------------------------------- + // do notation + // ------------------------------------------------------------------------------------- + + /** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Option` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * 5. Regular `Option` functions like `map` and `filter` can still be used within the do simulation. These functions will receive the accumulated variables as arguments within the scope + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Option, pipe } from "effect" + * + * const result = pipe( + * Option.Do, + * Option.bind("x", () => Option.some(2)), + * Option.bind("y", () => Option.some(3)), + * Option.let("sum", ({ x, y }) => x + y), + * Option.filter(({ x, y }) => x * y > 5) + * ) + * assert.deepStrictEqual(result, Option.some({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bind} + * @see {@link let_ let} + * + * @category Do notation + * @since 2.0.0 + */ + (self: Option, name: N): Option<{ [K in N]: A }> +} = doNotation.bindTo(map) + +const let_: { + ( + name: Exclude, + f: (a: NoInfer) => B + ): (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> + ( + self: Option, + name: Exclude, + f: (a: NoInfer) => B + ): Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> +} = doNotation.let_(map) + +export { + /** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Option` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * 5. Regular `Option` functions like `map` and `filter` can still be used within the do simulation. These functions will receive the accumulated variables as arguments within the scope + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Option, pipe } from "effect" + * + * const result = pipe( + * Option.Do, + * Option.bind("x", () => Option.some(2)), + * Option.bind("y", () => Option.some(3)), + * Option.let("sum", ({ x, y }) => x + y), + * Option.filter(({ x, y }) => x * y > 5) + * ) + * assert.deepStrictEqual(result, Option.some({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bind} + * @see {@link bindTo} + * + * @category Do notation + * @since 2.0.0 + */ + let_ as let +} + +/** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Option` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * 5. Regular `Option` functions like `map` and `filter` can still be used within the do simulation. These functions will receive the accumulated variables as arguments within the scope + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Option, pipe } from "effect" + * + * const result = pipe( + * Option.Do, + * Option.bind("x", () => Option.some(2)), + * Option.bind("y", () => Option.some(3)), + * Option.let("sum", ({ x, y }) => x + y), + * Option.filter(({ x, y }) => x * y > 5) + * ) + * assert.deepStrictEqual(result, Option.some({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bindTo} + * @see {@link let_ let} + * + * @category Do notation + * @since 2.0.0 + */ +export const bind: { + /** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Option` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * 5. Regular `Option` functions like `map` and `filter` can still be used within the do simulation. These functions will receive the accumulated variables as arguments within the scope + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Option, pipe } from "effect" + * + * const result = pipe( + * Option.Do, + * Option.bind("x", () => Option.some(2)), + * Option.bind("y", () => Option.some(3)), + * Option.let("sum", ({ x, y }) => x + y), + * Option.filter(({ x, y }) => x * y > 5) + * ) + * assert.deepStrictEqual(result, Option.some({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bindTo} + * @see {@link let_ let} + * + * @category Do notation + * @since 2.0.0 + */ + (name: Exclude, f: (a: NoInfer) => Option): (self: Option) => Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> + /** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Option` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * 5. Regular `Option` functions like `map` and `filter` can still be used within the do simulation. These functions will receive the accumulated variables as arguments within the scope + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Option, pipe } from "effect" + * + * const result = pipe( + * Option.Do, + * Option.bind("x", () => Option.some(2)), + * Option.bind("y", () => Option.some(3)), + * Option.let("sum", ({ x, y }) => x + y), + * Option.filter(({ x, y }) => x * y > 5) + * ) + * assert.deepStrictEqual(result, Option.some({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bindTo} + * @see {@link let_ let} + * + * @category Do notation + * @since 2.0.0 + */ + ( + self: Option, + name: Exclude, + f: (a: NoInfer) => Option + ): Option<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }> +} = doNotation.bind(map, flatMap) + +/** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Option` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * 5. Regular `Option` functions like `map` and `filter` can still be used within the do simulation. These functions will receive the accumulated variables as arguments within the scope + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Option, pipe } from "effect" + * + * const result = pipe( + * Option.Do, + * Option.bind("x", () => Option.some(2)), + * Option.bind("y", () => Option.some(3)), + * Option.let("sum", ({ x, y }) => x + y), + * Option.filter(({ x, y }) => x * y > 5) + * ) + * assert.deepStrictEqual(result, Option.some({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link bindTo} + * @see {@link bind} + * @see {@link let_ let} + * + * @category Do notation + * @since 2.0.0 + */ +export const Do: Option<{}> = some({}) + +const adapter = Gen.adapter() + +/** + * Similar to `Effect.gen`, `Option.gen` provides a more readable, + * generator-based syntax for working with `Option` values, making code that + * involves `Option` easier to write and understand. This approach is similar to + * using `async/await` but tailored for `Option`. + * + * **Example** (Using `Option.gen` to Create a Combined Value) + * + * ```ts + * import { Option } from "effect" + * + * const maybeName: Option.Option = Option.some("John") + * const maybeAge: Option.Option = Option.some(25) + * + * const person = Option.gen(function* () { + * const name = (yield* maybeName).toUpperCase() + * const age = yield* maybeAge + * return { name, age } + * }) + * + * console.log(person) + * // Output: + * // { _id: 'Option', _tag: 'Some', value: { name: 'JOHN', age: 25 } } + * ``` + * + * @category Generators + * @since 2.0.0 + */ +export const gen: Gen.Gen> = (...args) => { + const f = args.length === 1 ? args[0] : args[1].bind(args[0]) + const iterator = f(adapter) + let state: IteratorResult = iterator.next() + while (!state.done) { + const current = Gen.isGenKind(state.value) + ? state.value.value + : Gen.yieldWrapGet(state.value) + if (isNone(current)) { + return current + } + state = iterator.next(current.value as never) + } + return some(state.value) +} + +/** + * Merges two optional values, applying a function if both exist. + * Unlike {@link zipWith}, this function returns `None` only if both inputs are `None`. + * + * @internal + */ +export const mergeWith = (f: (a1: A, a2: A) => A) => (o1: Option, o2: Option): Option => { + if (isNone(o1)) { + return o2 + } else if (isNone(o2)) { + return o1 + } + return some(f(o1.value, o2.value)) +} diff --git a/backend/node_modules/effect/src/Order.ts b/backend/node_modules/effect/src/Order.ts new file mode 100644 index 0000000000000000000000000000000000000000..06e4a7baa60d8d768f2ac85a266a5c068f55a990 --- /dev/null +++ b/backend/node_modules/effect/src/Order.ts @@ -0,0 +1,397 @@ +/** + * This module provides an implementation of the `Order` type class which is used to define a total ordering on some type `A`. + * An order is defined by a relation `<=`, which obeys the following laws: + * + * - either `x <= y` or `y <= x` (totality) + * - if `x <= y` and `y <= x`, then `x == y` (antisymmetry) + * - if `x <= y` and `y <= z`, then `x <= z` (transitivity) + * + * The truth table for compare is defined as follows: + * + * | `x <= y` | `x >= y` | Ordering | | + * | -------- | -------- | -------- | --------------------- | + * | `true` | `true` | `0` | corresponds to x == y | + * | `true` | `false` | `< 0` | corresponds to x < y | + * | `false` | `true` | `> 0` | corresponds to x > y | + * + * @since 2.0.0 + */ +import { dual } from "./Function.js" +import type { TypeLambda } from "./HKT.js" + +/** + * @category type class + * @since 2.0.0 + */ +export interface Order { + (self: A, that: A): -1 | 0 | 1 +} + +/** + * @category type lambdas + * @since 2.0.0 + */ +export interface OrderTypeLambda extends TypeLambda { + readonly type: Order +} + +/** + * @category constructors + * @since 2.0.0 + */ +export const make = ( + compare: (self: A, that: A) => -1 | 0 | 1 +): Order => +(self, that) => self === that ? 0 : compare(self, that) + +/** + * @category instances + * @since 2.0.0 + */ +export const string: Order = make((self, that) => self < that ? -1 : 1) + +/** + * @category instances + * @since 2.0.0 + */ +export const number: Order = make((self, that) => self < that ? -1 : 1) + +/** + * @category instances + * @since 2.0.0 + */ +export const boolean: Order = make((self, that) => self < that ? -1 : 1) + +/** + * @category instances + * @since 2.0.0 + */ +export const bigint: Order = make((self, that) => self < that ? -1 : 1) + +/** + * @since 2.0.0 + */ +export const reverse = (O: Order): Order => make((self, that) => O(that, self)) + +/** + * @category combining + * @since 2.0.0 + */ +export const combine: { + /** + * @category combining + * @since 2.0.0 + */ + (that: Order): (self: Order) => Order + /** + * @category combining + * @since 2.0.0 + */ + (self: Order, that: Order): Order +} = dual(2, (self: Order, that: Order): Order => + make((a1, a2) => { + const out = self(a1, a2) + if (out !== 0) { + return out + } + return that(a1, a2) + })) + +/** + * @category combining + * @since 2.0.0 + */ +export const combineMany: { + /** + * @category combining + * @since 2.0.0 + */ + (collection: Iterable>): (self: Order) => Order + /** + * @category combining + * @since 2.0.0 + */ + (self: Order, collection: Iterable>): Order +} = dual(2, (self: Order, collection: Iterable>): Order => + make((a1, a2) => { + let out = self(a1, a2) + if (out !== 0) { + return out + } + for (const O of collection) { + out = O(a1, a2) + if (out !== 0) { + return out + } + } + return out + })) + +/** + * @since 2.0.0 + */ +export const empty = (): Order => make(() => 0) + +/** + * @category combining + * @since 2.0.0 + */ +export const combineAll = (collection: Iterable>): Order => combineMany(empty(), collection) + +/** + * @category mapping + * @since 2.0.0 + */ +export const mapInput: { + /** + * @category mapping + * @since 2.0.0 + */ + (f: (b: B) => A): (self: Order) => Order + /** + * @category mapping + * @since 2.0.0 + */ + (self: Order, f: (b: B) => A): Order +} = dual( + 2, + (self: Order, f: (b: B) => A): Order => make((b1, b2) => self(f(b1), f(b2))) +) + +/** + * @category instances + * @since 2.0.0 + */ +export const Date: Order = mapInput(number, (date) => date.getTime()) + +/** + * @category combining + * @since 2.0.0 + */ +export const product: { + (that: Order): (self: Order) => Order // readonly because invariant + (self: Order, that: Order): Order // readonly because invariant +} = dual(2, (self: Order, that: Order): Order => + make(([xa, xb], [ya, yb]) => { + const o = self(xa, ya) + return o !== 0 ? o : that(xb, yb) + })) + +/** + * @category combining + * @since 2.0.0 + */ +export const all = (collection: Iterable>): Order> => { + return make((x, y) => { + const len = Math.min(x.length, y.length) + let collectionLength = 0 + for (const O of collection) { + if (collectionLength >= len) { + break + } + const o = O(x[collectionLength], y[collectionLength]) + if (o !== 0) { + return o + } + collectionLength++ + } + return 0 + }) +} + +/** + * @category combining + * @since 2.0.0 + */ +export const productMany: { + (collection: Iterable>): (self: Order) => Order]> // readonly because invariant + (self: Order, collection: Iterable>): Order]> // readonly because invariant +} = dual(2, (self: Order, collection: Iterable>): Order]> => { + const O = all(collection) + return make((x, y) => { + const o = self(x[0], y[0]) + return o !== 0 ? o : O(x.slice(1), y.slice(1)) + }) +}) + +/** + * Similar to `Promise.all` but operates on `Order`s. + * + * ``` + * [Order, Order, ...] -> Order<[A, B, ...]> + * ``` + * + * This function creates and returns a new `Order` for a tuple of values based on the given `Order`s for each element in the tuple. + * The returned `Order` compares two tuples of the same type by applying the corresponding `Order` to each element in the tuple. + * It is useful when you need to compare two tuples of the same type and you have a specific way of comparing each element + * of the tuple. + * + * @category combinators + * @since 2.0.0 + */ +export const tuple = >>( + ...elements: T +): Order] ? A : never }>> => all(elements) as any + +/** + * This function creates and returns a new `Order` for an array of values based on a given `Order` for the elements of the array. + * The returned `Order` compares two arrays by applying the given `Order` to each element in the arrays. + * If all elements are equal, the arrays are then compared based on their length. + * It is useful when you need to compare two arrays of the same type and you have a specific way of comparing each element of the array. + * + * @category combinators + * @since 2.0.0 + */ +export const array = (O: Order): Order> => + make((self, that) => { + const aLen = self.length + const bLen = that.length + const len = Math.min(aLen, bLen) + for (let i = 0; i < len; i++) { + const o = O(self[i], that[i]) + if (o !== 0) { + return o + } + } + return number(aLen, bLen) + }) + +/** + * This function creates and returns a new `Order` for a struct of values based on the given `Order`s + * for each property in the struct. + * + * @category combinators + * @since 2.0.0 + */ +export const struct = }>( + fields: R +): Order<{ [K in keyof R]: [R[K]] extends [Order] ? A : never }> => { + const keys = Object.keys(fields) + return make((self, that) => { + for (const key of keys) { + const o = fields[key](self[key], that[key]) + if (o !== 0) { + return o + } + } + return 0 + }) +} + +/** + * Test whether one value is _strictly less than_ another. + * + * @since 2.0.0 + */ +export const lessThan = (O: Order): { + (that: A): (self: A) => boolean + (self: A, that: A): boolean +} => dual(2, (self: A, that: A) => O(self, that) === -1) + +/** + * Test whether one value is _strictly greater than_ another. + * + * @since 2.0.0 + */ +export const greaterThan = (O: Order): { + (that: A): (self: A) => boolean + (self: A, that: A): boolean +} => dual(2, (self: A, that: A) => O(self, that) === 1) + +/** + * Test whether one value is _non-strictly less than_ another. + * + * @since 2.0.0 + */ +export const lessThanOrEqualTo = (O: Order): { + (that: A): (self: A) => boolean + (self: A, that: A): boolean +} => dual(2, (self: A, that: A) => O(self, that) !== 1) + +/** + * Test whether one value is _non-strictly greater than_ another. + * + * @since 2.0.0 + */ +export const greaterThanOrEqualTo = (O: Order): { + (that: A): (self: A) => boolean + (self: A, that: A): boolean +} => dual(2, (self: A, that: A) => O(self, that) !== -1) + +/** + * Take the minimum of two values. If they are considered equal, the first argument is chosen. + * + * @since 2.0.0 + */ +export const min = (O: Order): { + (that: A): (self: A) => A + (self: A, that: A): A +} => dual(2, (self: A, that: A) => self === that || O(self, that) < 1 ? self : that) + +/** + * Take the maximum of two values. If they are considered equal, the first argument is chosen. + * + * @since 2.0.0 + */ +export const max = (O: Order): { + (that: A): (self: A) => A + (self: A, that: A): A +} => dual(2, (self: A, that: A) => self === that || O(self, that) > -1 ? self : that) + +/** + * Clamp a value between a minimum and a maximum. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Order, Number } from "effect" + * + * const clamp = Order.clamp(Number.Order)({ minimum: 1, maximum: 5 }) + * + * assert.equal(clamp(3), 3) + * assert.equal(clamp(0), 1) + * assert.equal(clamp(6), 5) + * ``` + * + * @since 2.0.0 + */ +export const clamp = (O: Order): { + (options: { + minimum: A + maximum: A + }): (self: A) => A + (self: A, options: { + minimum: A + maximum: A + }): A +} => + dual( + 2, + (self: A, options: { + minimum: A + maximum: A + }): A => min(O)(options.maximum, max(O)(options.minimum, self)) + ) + +/** + * Test whether a value is between a minimum and a maximum (inclusive). + * + * @since 2.0.0 + */ +export const between = (O: Order): { + (options: { + minimum: A + maximum: A + }): (self: A) => boolean + (self: A, options: { + minimum: A + maximum: A + }): boolean +} => + dual( + 2, + (self: A, options: { + minimum: A + maximum: A + }): boolean => !lessThan(O)(self, options.minimum) && !greaterThan(O)(self, options.maximum) + ) diff --git a/backend/node_modules/effect/src/Ordering.ts b/backend/node_modules/effect/src/Ordering.ts new file mode 100644 index 0000000000000000000000000000000000000000..f0fe4b45028c674c3aaf789ee9cabd475972d6d6 --- /dev/null +++ b/backend/node_modules/effect/src/Ordering.ts @@ -0,0 +1,173 @@ +/** + * @since 2.0.0 + */ +import type { LazyArg } from "./Function.js" +import { dual } from "./Function.js" + +/** + * @category model + * @since 2.0.0 + */ +export type Ordering = -1 | 0 | 1 + +/** + * Inverts the ordering of the input `Ordering`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { reverse } from "effect/Ordering" + * + * assert.deepStrictEqual(reverse(1), -1) + * assert.deepStrictEqual(reverse(-1), 1) + * assert.deepStrictEqual(reverse(0), 0) + * ``` + * + * @since 2.0.0 + */ +export const reverse = (o: Ordering): Ordering => (o === -1 ? 1 : o === 1 ? -1 : 0) + +/** + * Depending on the `Ordering` parameter given to it, returns a value produced by one of the 3 functions provided as parameters. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Ordering } from "effect" + * import { constant } from "effect/Function" + * + * const toMessage = Ordering.match({ + * onLessThan: constant('less than'), + * onEqual: constant('equal'), + * onGreaterThan: constant('greater than') + * }) + * + * assert.deepStrictEqual(toMessage(-1), "less than") + * assert.deepStrictEqual(toMessage(0), "equal") + * assert.deepStrictEqual(toMessage(1), "greater than") + * ``` + * + * @category pattern matching + * @since 2.0.0 + */ +export const match: { + /** + * Depending on the `Ordering` parameter given to it, returns a value produced by one of the 3 functions provided as parameters. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Ordering } from "effect" + * import { constant } from "effect/Function" + * + * const toMessage = Ordering.match({ + * onLessThan: constant('less than'), + * onEqual: constant('equal'), + * onGreaterThan: constant('greater than') + * }) + * + * assert.deepStrictEqual(toMessage(-1), "less than") + * assert.deepStrictEqual(toMessage(0), "equal") + * assert.deepStrictEqual(toMessage(1), "greater than") + * ``` + * + * @category pattern matching + * @since 2.0.0 + */ + ( + options: { + readonly onLessThan: LazyArg + readonly onEqual: LazyArg + readonly onGreaterThan: LazyArg + } + ): (self: Ordering) => A | B | C + /** + * Depending on the `Ordering` parameter given to it, returns a value produced by one of the 3 functions provided as parameters. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Ordering } from "effect" + * import { constant } from "effect/Function" + * + * const toMessage = Ordering.match({ + * onLessThan: constant('less than'), + * onEqual: constant('equal'), + * onGreaterThan: constant('greater than') + * }) + * + * assert.deepStrictEqual(toMessage(-1), "less than") + * assert.deepStrictEqual(toMessage(0), "equal") + * assert.deepStrictEqual(toMessage(1), "greater than") + * ``` + * + * @category pattern matching + * @since 2.0.0 + */ + ( + o: Ordering, + options: { + readonly onLessThan: LazyArg + readonly onEqual: LazyArg + readonly onGreaterThan: LazyArg + } + ): A | B | C +} = dual(2, ( + self: Ordering, + { onEqual, onGreaterThan, onLessThan }: { + readonly onLessThan: LazyArg + readonly onEqual: LazyArg + readonly onGreaterThan: LazyArg + } +): A | B | C => self === -1 ? onLessThan() : self === 0 ? onEqual() : onGreaterThan()) + +/** + * @category combining + * @since 2.0.0 + */ +export const combine: { + /** + * @category combining + * @since 2.0.0 + */ + (that: Ordering): (self: Ordering) => Ordering + /** + * @category combining + * @since 2.0.0 + */ + (self: Ordering, that: Ordering): Ordering +} = dual(2, (self: Ordering, that: Ordering): Ordering => self !== 0 ? self : that) + +/** + * @category combining + * @since 2.0.0 + */ +export const combineMany: { + /** + * @category combining + * @since 2.0.0 + */ + (collection: Iterable): (self: Ordering) => Ordering + /** + * @category combining + * @since 2.0.0 + */ + (self: Ordering, collection: Iterable): Ordering +} = dual(2, (self: Ordering, collection: Iterable): Ordering => { + let ordering = self + if (ordering !== 0) { + return ordering + } + for (ordering of collection) { + if (ordering !== 0) { + return ordering + } + } + return ordering +}) + +/** + * @category combining + * @since 2.0.0 + */ +export const combineAll = (collection: Iterable): Ordering => combineMany(0, collection) diff --git a/backend/node_modules/effect/src/Pipeable.ts b/backend/node_modules/effect/src/Pipeable.ts new file mode 100644 index 0000000000000000000000000000000000000000..98818f21a92a1d7f79616bb74cef361035fe09b7 --- /dev/null +++ b/backend/node_modules/effect/src/Pipeable.ts @@ -0,0 +1,574 @@ +/** + * @since 2.0.0 + */ + +import type { Ctor } from "./Types.js" + +/** + * @since 2.0.0 + * @category Models + */ +export interface Pipeable { + pipe(this: A): A + pipe(this: A, ab: (_: A) => B): B + pipe(this: A, ab: (_: A) => B, bc: (_: B) => C): C + pipe(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D + pipe( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E + ): E + pipe( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F + ): F + pipe( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G + ): G + pipe( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H + ): H + pipe( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I + ): I + pipe( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J + ): J + pipe( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J, + jk: (_: J) => K + ): K + pipe< + A, + B = never, + C = never, + D = never, + E = never, + F = never, + G = never, + H = never, + I = never, + J = never, + K = never, + L = never + >( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J, + jk: (_: J) => K, + kl: (_: K) => L + ): L + pipe< + A, + B = never, + C = never, + D = never, + E = never, + F = never, + G = never, + H = never, + I = never, + J = never, + K = never, + L = never, + M = never + >( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J, + jk: (_: J) => K, + kl: (_: K) => L, + lm: (_: L) => M + ): M + pipe< + A, + B = never, + C = never, + D = never, + E = never, + F = never, + G = never, + H = never, + I = never, + J = never, + K = never, + L = never, + M = never, + N = never + >( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J, + jk: (_: J) => K, + kl: (_: K) => L, + lm: (_: L) => M, + mn: (_: M) => N + ): N + pipe< + A, + B = never, + C = never, + D = never, + E = never, + F = never, + G = never, + H = never, + I = never, + J = never, + K = never, + L = never, + M = never, + N = never, + O = never + >( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J, + jk: (_: J) => K, + kl: (_: K) => L, + lm: (_: L) => M, + mn: (_: M) => N, + no: (_: N) => O + ): O + pipe< + A, + B = never, + C = never, + D = never, + E = never, + F = never, + G = never, + H = never, + I = never, + J = never, + K = never, + L = never, + M = never, + N = never, + O = never, + P = never + >( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J, + jk: (_: J) => K, + kl: (_: K) => L, + lm: (_: L) => M, + mn: (_: M) => N, + no: (_: N) => O, + op: (_: O) => P + ): P + pipe< + A, + B = never, + C = never, + D = never, + E = never, + F = never, + G = never, + H = never, + I = never, + J = never, + K = never, + L = never, + M = never, + N = never, + O = never, + P = never, + Q = never + >( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J, + jk: (_: J) => K, + kl: (_: K) => L, + lm: (_: L) => M, + mn: (_: M) => N, + no: (_: N) => O, + op: (_: O) => P, + pq: (_: P) => Q + ): Q + pipe< + A, + B = never, + C = never, + D = never, + E = never, + F = never, + G = never, + H = never, + I = never, + J = never, + K = never, + L = never, + M = never, + N = never, + O = never, + P = never, + Q = never, + R = never + >( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J, + jk: (_: J) => K, + kl: (_: K) => L, + lm: (_: L) => M, + mn: (_: M) => N, + no: (_: N) => O, + op: (_: O) => P, + pq: (_: P) => Q, + qr: (_: Q) => R + ): R + pipe< + A, + B = never, + C = never, + D = never, + E = never, + F = never, + G = never, + H = never, + I = never, + J = never, + K = never, + L = never, + M = never, + N = never, + O = never, + P = never, + Q = never, + R = never, + S = never + >( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J, + jk: (_: J) => K, + kl: (_: K) => L, + lm: (_: L) => M, + mn: (_: M) => N, + no: (_: N) => O, + op: (_: O) => P, + pq: (_: P) => Q, + qr: (_: Q) => R, + rs: (_: R) => S + ): S + pipe< + A, + B = never, + C = never, + D = never, + E = never, + F = never, + G = never, + H = never, + I = never, + J = never, + K = never, + L = never, + M = never, + N = never, + O = never, + P = never, + Q = never, + R = never, + S = never, + T = never + >( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J, + jk: (_: J) => K, + kl: (_: K) => L, + lm: (_: L) => M, + mn: (_: M) => N, + no: (_: N) => O, + op: (_: O) => P, + pq: (_: P) => Q, + qr: (_: Q) => R, + rs: (_: R) => S, + st: (_: S) => T + ): T + pipe< + A, + B = never, + C = never, + D = never, + E = never, + F = never, + G = never, + H = never, + I = never, + J = never, + K = never, + L = never, + M = never, + N = never, + O = never, + P = never, + Q = never, + R = never, + S = never, + T = never, + U = never + >( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J, + jk: (_: J) => K, + kl: (_: K) => L, + lm: (_: L) => M, + mn: (_: M) => N, + no: (_: N) => O, + op: (_: O) => P, + pq: (_: P) => Q, + qr: (_: Q) => R, + rs: (_: R) => S, + st: (_: S) => T, + tu: (_: T) => U + ): U + pipe< + A, + B = never, + C = never, + D = never, + E = never, + F = never, + G = never, + H = never, + I = never, + J = never, + K = never, + L = never, + M = never, + N = never, + O = never, + P = never, + Q = never, + R = never, + S = never, + T = never, + U = never + >( + this: A, + ab: (_: A) => B, + bc: (_: B) => C, + cd: (_: C) => D, + de: (_: D) => E, + ef: (_: E) => F, + fg: (_: F) => G, + gh: (_: G) => H, + hi: (_: H) => I, + ij: (_: I) => J, + jk: (_: J) => K, + kl: (_: K) => L, + lm: (_: L) => M, + mn: (_: M) => N, + no: (_: N) => O, + op: (_: O) => P, + pq: (_: P) => Q, + qr: (_: Q) => R, + rs: (_: R) => S, + st: (_: S) => T, + tu: (_: T) => U + ): U +} + +/** + * @since 2.0.0 + */ +export const pipeArguments = (self: A, args: IArguments): unknown => { + switch (args.length) { + case 0: + return self + case 1: + return args[0](self) + case 2: + return args[1](args[0](self)) + case 3: + return args[2](args[1](args[0](self))) + case 4: + return args[3](args[2](args[1](args[0](self)))) + case 5: + return args[4](args[3](args[2](args[1](args[0](self))))) + case 6: + return args[5](args[4](args[3](args[2](args[1](args[0](self)))))) + case 7: + return args[6](args[5](args[4](args[3](args[2](args[1](args[0](self))))))) + case 8: + return args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))))) + case 9: + return args[8](args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self))))))))) + default: { + let ret = self + for (let i = 0, len = args.length; i < len; i++) { + ret = args[i](ret) + } + return ret + } + } +} + +/** + * @since 3.15.0 + * @category Models + */ +export interface PipeableConstructor { + new(...args: Array): Pipeable +} + +/** + * @since 3.15.0 + * @category Prototypes + */ +export const Prototype: Pipeable = { + pipe() { + return pipeArguments(this, arguments) + } +} + +const Base: PipeableConstructor = (function() { + function PipeableBase() {} + PipeableBase.prototype = Prototype + return PipeableBase as any +})() + +/** + * @since 3.15.0 + * @category Constructors + */ +export const Class: { + /** + * @since 3.15.0 + * @category Constructors + */ + (): PipeableConstructor + /** + * @since 3.15.0 + * @category Constructors + */ + (klass: TBase): TBase & PipeableConstructor +} = (klass?: Ctor) => + klass ? + class extends klass { + pipe() { + return pipeArguments(this, arguments) + } + } + : Base diff --git a/backend/node_modules/effect/src/Predicate.ts b/backend/node_modules/effect/src/Predicate.ts new file mode 100644 index 0000000000000000000000000000000000000000..3cdc191813ab6743cb76f967a40a77b1538c4c97 --- /dev/null +++ b/backend/node_modules/effect/src/Predicate.ts @@ -0,0 +1,2344 @@ +/** + * This module provides a collection of functions for working with predicates and refinements. + * + * A `Predicate` is a function that takes a value of type `A` and returns a boolean. + * It is used to check if a value satisfies a certain condition. + * + * A `Refinement` is a special type of predicate that not only checks a condition + * but also provides a type guard, allowing TypeScript to narrow the type of the input + * value from `A` to a more specific type `B` within a conditional block. + * + * The module includes: + * - Basic predicates and refinements for common types (e.g., `isString`, `isNumber`). + * - Combinators to create new predicates from existing ones (e.g., `and`, `or`, `not`). + * - Advanced combinators for working with data structures (e.g., `tuple`, `struct`). + * - Type-level utilities for inspecting predicate and refinement types. + * + * @since 2.0.0 + */ +import { dual, isFunction as isFunction_ } from "./Function.js" +import type { TypeLambda } from "./HKT.js" +import type { TupleOf, TupleOfAtLeast } from "./Types.js" + +/** + * Represents a function that takes a value of type `A` and returns `true` if the value + * satisfies some condition, `false` otherwise. + * + * @example + * ```ts + * import { Predicate } from "effect" + * import * as assert from "node:assert" + * + * const isEven: Predicate.Predicate = (n) => n % 2 === 0 + * + * assert.strictEqual(isEven(2), true) + * assert.strictEqual(isEven(3), false) + * ``` + * + * @category models + * @since 2.0.0 + */ +export interface Predicate { + (a: A): boolean +} + +/** + * A `TypeLambda` for `Predicate`. This is used to support higher-kinded types + * and allows `Predicate` to be used in generic contexts within the `effect` ecosystem. + * + * @category type lambdas + * @since 2.0.0 + */ +export interface PredicateTypeLambda extends TypeLambda { + readonly type: Predicate +} + +/** + * Represents a function that serves as a type guard. + * + * A `Refinement` is a function that takes a value of type `A` and returns a + * type predicate `a is B`, where `B` is a subtype of `A`. If the function returns + * `true`, TypeScript will narrow the type of the input variable to `B`. + * + * @example + * ```ts + * import { Predicate } from "effect" + * import * as assert from "node:assert" + * + * const isString: Predicate.Refinement = (u): u is string => typeof u === "string" + * + * const value: unknown = "hello" + * + * if (isString(value)) { + * // value is now known to be a string + * assert.strictEqual(value.toUpperCase(), "HELLO") + * } + * ``` + * + * @category models + * @since 2.0.0 + */ +export interface Refinement { + (a: A): a is B +} + +/** + * A namespace for type-level utilities for `Predicate`. + * + * @since 3.6.0 + * @category type-level + */ +export declare namespace Predicate { + /** + * Extracts the input type `A` from a `Predicate`. + * + * @example + * ```ts + * import { type Predicate } from "effect" + * + * type T = Predicate.Predicate.In> // T is string + * ``` + * + * @since 3.6.0 + * @category type-level + */ + export type In = [T] extends [Predicate] ? _A : never + /** + * A type representing any `Predicate`. + * + * @since 3.6.0 + * @category type-level + */ + export type Any = Predicate +} + +/** + * A namespace for type-level utilities for `Refinement`. + * + * @since 3.6.0 + * @category type-level + */ +export declare namespace Refinement { + /** + * Extracts the input type `A` from a `Refinement`. + * + * @example + * ```ts + * import { type Predicate } from "effect" + * + * type IsString = Predicate.Refinement + * type T = Predicate.Refinement.In // T is unknown + * ``` + * + * @since 3.6.0 + * @category type-level + */ + export type In = [T] extends [Refinement] ? _A : never + /** + * Extracts the output (refined) type `B` from a `Refinement`. + * + * @example + * ```ts + * import { type Predicate } from "effect" + * + * type IsString = Predicate.Refinement + * type T = Predicate.Refinement.Out // T is string + * ``` + * + * @since 3.6.0 + * @category type-level + */ + export type Out = [T] extends [Refinement] ? _B : never + /** + * A type representing any `Refinement`. + * + * @since 3.6.0 + * @category type-level + */ + export type Any = Refinement +} + +/** + * Transforms a `Predicate` into a `Predicate` by applying a function `(b: B) => A` + * to the input before passing it to the predicate. This is also known as "contramap" or + * "pre-composition". + * + * @example + * ```ts + * import { Predicate, Number } from "effect" + * import * as assert from "node:assert" + * + * // A predicate on numbers + * const isPositive: Predicate.Predicate = Number.greaterThan(0) + * + * // A function from `string` to `number` + * const stringLength = (s: string): number => s.length + * + * // Create a new predicate on strings by mapping the input + * const hasPositiveLength = Predicate.mapInput(isPositive, stringLength) + * + * assert.strictEqual(hasPositiveLength("hello"), true) + * assert.strictEqual(hasPositiveLength(""), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const mapInput: { + /** + * Transforms a `Predicate` into a `Predicate` by applying a function `(b: B) => A` + * to the input before passing it to the predicate. This is also known as "contramap" or + * "pre-composition". + * + * @example + * ```ts + * import { Predicate, Number } from "effect" + * import * as assert from "node:assert" + * + * // A predicate on numbers + * const isPositive: Predicate.Predicate = Number.greaterThan(0) + * + * // A function from `string` to `number` + * const stringLength = (s: string): number => s.length + * + * // Create a new predicate on strings by mapping the input + * const hasPositiveLength = Predicate.mapInput(isPositive, stringLength) + * + * assert.strictEqual(hasPositiveLength("hello"), true) + * assert.strictEqual(hasPositiveLength(""), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (f: (b: B) => A): (self: Predicate) => Predicate + /** + * Transforms a `Predicate` into a `Predicate` by applying a function `(b: B) => A` + * to the input before passing it to the predicate. This is also known as "contramap" or + * "pre-composition". + * + * @example + * ```ts + * import { Predicate, Number } from "effect" + * import * as assert from "node:assert" + * + * // A predicate on numbers + * const isPositive: Predicate.Predicate = Number.greaterThan(0) + * + * // A function from `string` to `number` + * const stringLength = (s: string): number => s.length + * + * // Create a new predicate on strings by mapping the input + * const hasPositiveLength = Predicate.mapInput(isPositive, stringLength) + * + * assert.strictEqual(hasPositiveLength("hello"), true) + * assert.strictEqual(hasPositiveLength(""), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: Predicate, f: (b: B) => A): Predicate +} = dual(2, (self: Predicate, f: (b: B) => A): Predicate => (b) => self(f(b))) + +/** + * A refinement that checks if a `ReadonlyArray` is a tuple with exactly `N` elements. + * If the check is successful, the type is narrowed to `TupleOf`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isTupleOf } from "effect/Predicate" + * + * const isTupleOf3 = isTupleOf(3) + * + * assert.strictEqual(isTupleOf3([1, 2, 3]), true); + * assert.strictEqual(isTupleOf3([1, 2]), false); + * + * const arr: number[] = [1, 2, 3]; + * if (isTupleOf(arr, 3)) { + * // The type of arr is now [number, number, number] + * const [a, b, c] = arr; + * assert.deepStrictEqual([a, b, c], [1, 2, 3]) + * } + * ``` + * + * @category guards + * @since 3.3.0 + */ +export const isTupleOf: { + /** + * A refinement that checks if a `ReadonlyArray` is a tuple with exactly `N` elements. + * If the check is successful, the type is narrowed to `TupleOf`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isTupleOf } from "effect/Predicate" + * + * const isTupleOf3 = isTupleOf(3) + * + * assert.strictEqual(isTupleOf3([1, 2, 3]), true); + * assert.strictEqual(isTupleOf3([1, 2]), false); + * + * const arr: number[] = [1, 2, 3]; + * if (isTupleOf(arr, 3)) { + * // The type of arr is now [number, number, number] + * const [a, b, c] = arr; + * assert.deepStrictEqual([a, b, c], [1, 2, 3]) + * } + * ``` + * + * @category guards + * @since 3.3.0 + */ + (n: N): (self: ReadonlyArray) => self is TupleOf + /** + * A refinement that checks if a `ReadonlyArray` is a tuple with exactly `N` elements. + * If the check is successful, the type is narrowed to `TupleOf`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isTupleOf } from "effect/Predicate" + * + * const isTupleOf3 = isTupleOf(3) + * + * assert.strictEqual(isTupleOf3([1, 2, 3]), true); + * assert.strictEqual(isTupleOf3([1, 2]), false); + * + * const arr: number[] = [1, 2, 3]; + * if (isTupleOf(arr, 3)) { + * // The type of arr is now [number, number, number] + * const [a, b, c] = arr; + * assert.deepStrictEqual([a, b, c], [1, 2, 3]) + * } + * ``` + * + * @category guards + * @since 3.3.0 + */ + (self: ReadonlyArray, n: N): self is TupleOf +} = dual(2, (self: ReadonlyArray, n: N): self is TupleOf => self.length === n) + +/** + * A refinement that checks if a `ReadonlyArray` is a tuple with at least `N` elements. + * If the check is successful, the type is narrowed to `TupleOfAtLeast`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isTupleOfAtLeast } from "effect/Predicate" + * + * const isTupleOfAtLeast3 = isTupleOfAtLeast(3) + * + * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3]), true); + * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3, 4]), true); + * assert.strictEqual(isTupleOfAtLeast3([1, 2]), false); + * + * const arr: number[] = [1, 2, 3, 4]; + * if (isTupleOfAtLeast(arr, 3)) { + * // The type of arr is now [number, number, number, ...number[]] + * const [a, b, c] = arr; + * assert.deepStrictEqual([a, b, c], [1, 2, 3]) + * } + * ``` + * + * @category guards + * @since 3.3.0 + */ +export const isTupleOfAtLeast: { + /** + * A refinement that checks if a `ReadonlyArray` is a tuple with at least `N` elements. + * If the check is successful, the type is narrowed to `TupleOfAtLeast`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isTupleOfAtLeast } from "effect/Predicate" + * + * const isTupleOfAtLeast3 = isTupleOfAtLeast(3) + * + * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3]), true); + * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3, 4]), true); + * assert.strictEqual(isTupleOfAtLeast3([1, 2]), false); + * + * const arr: number[] = [1, 2, 3, 4]; + * if (isTupleOfAtLeast(arr, 3)) { + * // The type of arr is now [number, number, number, ...number[]] + * const [a, b, c] = arr; + * assert.deepStrictEqual([a, b, c], [1, 2, 3]) + * } + * ``` + * + * @category guards + * @since 3.3.0 + */ + (n: N): (self: ReadonlyArray) => self is TupleOfAtLeast + /** + * A refinement that checks if a `ReadonlyArray` is a tuple with at least `N` elements. + * If the check is successful, the type is narrowed to `TupleOfAtLeast`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isTupleOfAtLeast } from "effect/Predicate" + * + * const isTupleOfAtLeast3 = isTupleOfAtLeast(3) + * + * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3]), true); + * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3, 4]), true); + * assert.strictEqual(isTupleOfAtLeast3([1, 2]), false); + * + * const arr: number[] = [1, 2, 3, 4]; + * if (isTupleOfAtLeast(arr, 3)) { + * // The type of arr is now [number, number, number, ...number[]] + * const [a, b, c] = arr; + * assert.deepStrictEqual([a, b, c], [1, 2, 3]) + * } + * ``` + * + * @category guards + * @since 3.3.0 + */ + (self: ReadonlyArray, n: N): self is TupleOfAtLeast +} = dual(2, (self: ReadonlyArray, n: N): self is TupleOfAtLeast => self.length >= n) + +/** + * A predicate that checks if a value is "truthy" in JavaScript. + * Fails for `false`, `0`, `-0`, `0n`, `""`, `null`, `undefined`, and `NaN`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isTruthy } from "effect/Predicate" + * + * assert.strictEqual(isTruthy(1), true) + * assert.strictEqual(isTruthy("hello"), true) + * assert.strictEqual(isTruthy({}), true) + * + * assert.strictEqual(isTruthy(0), false) + * assert.strictEqual(isTruthy(""), false) + * assert.strictEqual(isTruthy(null), false) + * assert.strictEqual(isTruthy(undefined), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isTruthy = (input: unknown) => !!input + +/** + * A refinement that checks if a value is a `Set`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isSet } from "effect/Predicate" + * + * assert.strictEqual(isSet(new Set([1, 2])), true) + * assert.strictEqual(isSet(new Set()), true) + * + * assert.strictEqual(isSet({}), false) + * assert.strictEqual(isSet([1, 2]), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isSet = (input: unknown): input is Set => input instanceof Set + +/** + * A refinement that checks if a value is a `Map`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isMap } from "effect/Predicate" + * + * assert.strictEqual(isMap(new Map()), true) + * + * assert.strictEqual(isMap({}), false) + * assert.strictEqual(isMap(new Set()), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isMap = (input: unknown): input is Map => input instanceof Map + +/** + * A refinement that checks if a value is a `string`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isString } from "effect/Predicate" + * + * assert.strictEqual(isString("hello"), true) + * assert.strictEqual(isString(""), true) + * + * assert.strictEqual(isString(123), false) + * assert.strictEqual(isString(null), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isString = (input: unknown): input is string => typeof input === "string" + +/** + * A refinement that checks if a value is a `number`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isNumber } from "effect/Predicate" + * + * assert.strictEqual(isNumber(123), true) + * assert.strictEqual(isNumber(0), true) + * assert.strictEqual(isNumber(-1.5), true) + * assert.strictEqual(isNumber(NaN), true) + * + * assert.strictEqual(isNumber("123"), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isNumber = (input: unknown): input is number => typeof input === "number" + +/** + * A refinement that checks if a value is a `boolean`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isBoolean } from "effect/Predicate" + * + * assert.strictEqual(isBoolean(true), true) + * assert.strictEqual(isBoolean(false), true) + * + * assert.strictEqual(isBoolean("true"), false) + * assert.strictEqual(isBoolean(0), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isBoolean = (input: unknown): input is boolean => typeof input === "boolean" + +/** + * A refinement that checks if a value is a `bigint`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isBigInt } from "effect/Predicate" + * + * assert.strictEqual(isBigInt(1n), true) + * + * assert.strictEqual(isBigInt(1), false) + * assert.strictEqual(isBigInt("1"), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isBigInt = (input: unknown): input is bigint => typeof input === "bigint" + +/** + * A refinement that checks if a value is a `symbol`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isSymbol } from "effect/Predicate" + * + * assert.strictEqual(isSymbol(Symbol.for("a")), true) + * + * assert.strictEqual(isSymbol("a"), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isSymbol = (input: unknown): input is symbol => typeof input === "symbol" + +// TODO: make public +/** + * A refinement that checks if a value is a valid `PropertyKey` (a `string`, `number`, or `symbol`). + * @internal + */ +export const isPropertyKey = (u: unknown): u is PropertyKey => isString(u) || isNumber(u) || isSymbol(u) + +/** + * A refinement that checks if a value is a `Function`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isFunction } from "effect/Predicate" + * + * assert.strictEqual(isFunction(() => {}), true) + * assert.strictEqual(isFunction(isFunction), true) + * + * assert.strictEqual(isFunction("function"), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isFunction: (input: unknown) => input is Function = isFunction_ + +/** + * A refinement that checks if a value is `undefined`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isUndefined } from "effect/Predicate" + * + * assert.strictEqual(isUndefined(undefined), true) + * + * assert.strictEqual(isUndefined(null), false) + * assert.strictEqual(isUndefined("undefined"), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isUndefined = (input: unknown): input is undefined => input === undefined + +/** + * A refinement that checks if a value is not `undefined`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isNotUndefined } from "effect/Predicate" + * + * assert.strictEqual(isNotUndefined(null), true) + * assert.strictEqual(isNotUndefined("value"), true) + * + * assert.strictEqual(isNotUndefined(undefined), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isNotUndefined = (input: A): input is Exclude => input !== undefined + +/** + * A refinement that checks if a value is `null`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isNull } from "effect/Predicate" + * + * assert.strictEqual(isNull(null), true) + * + * assert.strictEqual(isNull(undefined), false) + * assert.strictEqual(isNull("null"), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isNull = (input: unknown): input is null => input === null + +/** + * A refinement that checks if a value is not `null`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isNotNull } from "effect/Predicate" + * + * assert.strictEqual(isNotNull(undefined), true) + * assert.strictEqual(isNotNull("value"), true) + * + * assert.strictEqual(isNotNull(null), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isNotNull = (input: A): input is Exclude => input !== null + +/** + * A refinement that always returns `false`. The type is narrowed to `never`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isNever } from "effect/Predicate" + * + * assert.strictEqual(isNever(1), false) + * assert.strictEqual(isNever(null), false) + * assert.strictEqual(isNever({}), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isNever: (input: unknown) => input is never = (_: unknown): _ is never => false + +/** + * A refinement that always returns `true`. The type is narrowed to `unknown`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isUnknown } from "effect/Predicate" + * + * assert.strictEqual(isUnknown(1), true) + * assert.strictEqual(isUnknown(null), true) + * assert.strictEqual(isUnknown({}), true) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isUnknown: (input: unknown) => input is unknown = (_): _ is unknown => true + +/** + * Checks if the input is an object or an array. + * @internal + */ +export const isRecordOrArray = (input: unknown): input is { [x: PropertyKey]: unknown } => + typeof input === "object" && input !== null + +/** + * A refinement that checks if a value is an `object`. Note that in JavaScript, + * arrays and functions are also considered objects. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isObject } from "effect/Predicate" + * + * assert.strictEqual(isObject({}), true) + * assert.strictEqual(isObject([]), true) + * assert.strictEqual(isObject(() => {}), true) + * + * assert.strictEqual(isObject(null), false) + * assert.strictEqual(isObject("hello"), false) + * ``` + * + * @category guards + * @since 2.0.0 + * @see isRecord to check for plain objects (excluding arrays and functions). + */ +export const isObject = (input: unknown): input is object => isRecordOrArray(input) || isFunction(input) + +/** + * A refinement that checks if a value is an object-like value and has a specific property key. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { hasProperty } from "effect/Predicate" + * + * assert.strictEqual(hasProperty({ a: 1 }, "a"), true) + * assert.strictEqual(hasProperty({ a: 1 }, "b"), false) + * + * const value: unknown = { name: "Alice" }; + * if (hasProperty(value, "name")) { + * // The type of `value` is narrowed to `{ name: unknown }` + * // and we can safely access `value.name` + * console.log(value.name) + * } + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const hasProperty: { + /** + * A refinement that checks if a value is an object-like value and has a specific property key. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { hasProperty } from "effect/Predicate" + * + * assert.strictEqual(hasProperty({ a: 1 }, "a"), true) + * assert.strictEqual(hasProperty({ a: 1 }, "b"), false) + * + * const value: unknown = { name: "Alice" }; + * if (hasProperty(value, "name")) { + * // The type of `value` is narrowed to `{ name: unknown }` + * // and we can safely access `value.name` + * console.log(value.name) + * } + * ``` + * + * @category guards + * @since 2.0.0 + */ +

(property: P): (self: unknown) => self is { [K in P]: unknown } + /** + * A refinement that checks if a value is an object-like value and has a specific property key. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { hasProperty } from "effect/Predicate" + * + * assert.strictEqual(hasProperty({ a: 1 }, "a"), true) + * assert.strictEqual(hasProperty({ a: 1 }, "b"), false) + * + * const value: unknown = { name: "Alice" }; + * if (hasProperty(value, "name")) { + * // The type of `value` is narrowed to `{ name: unknown }` + * // and we can safely access `value.name` + * console.log(value.name) + * } + * ``` + * + * @category guards + * @since 2.0.0 + */ +

(self: unknown, property: P): self is { [K in P]: unknown } +} = dual( + 2, +

(self: unknown, property: P): self is { [K in P]: unknown } => + isObject(self) && (property in self) +) + +/** + * A refinement that checks if a value is an object with a `_tag` property + * that matches the given tag. This is a powerful tool for working with + * discriminated union types. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isTagged } from "effect/Predicate" + * + * type Shape = { _tag: "circle"; radius: number } | { _tag: "square"; side: number } + * + * const isCircle = isTagged("circle") + * + * const shape1: Shape = { _tag: "circle", radius: 10 } + * const shape2: Shape = { _tag: "square", side: 5 } + * + * assert.strictEqual(isCircle(shape1), true) + * assert.strictEqual(isCircle(shape2), false) + * + * if (isCircle(shape1)) { + * // shape1 is now narrowed to { _tag: "circle"; radius: number } + * assert.strictEqual(shape1.radius, 10) + * } + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isTagged: { + /** + * A refinement that checks if a value is an object with a `_tag` property + * that matches the given tag. This is a powerful tool for working with + * discriminated union types. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isTagged } from "effect/Predicate" + * + * type Shape = { _tag: "circle"; radius: number } | { _tag: "square"; side: number } + * + * const isCircle = isTagged("circle") + * + * const shape1: Shape = { _tag: "circle", radius: 10 } + * const shape2: Shape = { _tag: "square", side: 5 } + * + * assert.strictEqual(isCircle(shape1), true) + * assert.strictEqual(isCircle(shape2), false) + * + * if (isCircle(shape1)) { + * // shape1 is now narrowed to { _tag: "circle"; radius: number } + * assert.strictEqual(shape1.radius, 10) + * } + * ``` + * + * @category guards + * @since 2.0.0 + */ + (tag: K): (self: unknown) => self is { _tag: K } + /** + * A refinement that checks if a value is an object with a `_tag` property + * that matches the given tag. This is a powerful tool for working with + * discriminated union types. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isTagged } from "effect/Predicate" + * + * type Shape = { _tag: "circle"; radius: number } | { _tag: "square"; side: number } + * + * const isCircle = isTagged("circle") + * + * const shape1: Shape = { _tag: "circle", radius: 10 } + * const shape2: Shape = { _tag: "square", side: 5 } + * + * assert.strictEqual(isCircle(shape1), true) + * assert.strictEqual(isCircle(shape2), false) + * + * if (isCircle(shape1)) { + * // shape1 is now narrowed to { _tag: "circle"; radius: number } + * assert.strictEqual(shape1.radius, 10) + * } + * ``` + * + * @category guards + * @since 2.0.0 + */ + (self: unknown, tag: K): self is { _tag: K } +} = dual( + 2, + (self: unknown, tag: K): self is { _tag: K } => hasProperty(self, "_tag") && self["_tag"] === tag +) + +/** + * A refinement that checks if a value is either `null` or `undefined`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isNullable } from "effect/Predicate" + * + * assert.strictEqual(isNullable(null), true) + * assert.strictEqual(isNullable(undefined), true) + * + * assert.strictEqual(isNullable(0), false) + * assert.strictEqual(isNullable(""), false) + * ``` + * + * @category guards + * @since 2.0.0 + * @see isNotNullable + */ +export const isNullable = (input: A): input is Extract => input === null || input === undefined + +/** + * A refinement that checks if a value is neither `null` nor `undefined`. + * The type is narrowed to `NonNullable`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isNotNullable } from "effect/Predicate" + * + * assert.strictEqual(isNotNullable(0), true) + * assert.strictEqual(isNotNullable("hello"), true) + * + * assert.strictEqual(isNotNullable(null), false) + * assert.strictEqual(isNotNullable(undefined), false) + * ``` + * + * @category guards + * @since 2.0.0 + * @see isNullable + */ +export const isNotNullable = (input: A): input is NonNullable => input !== null && input !== undefined + +/** + * A refinement that checks if a value is an instance of `Error`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isError } from "effect/Predicate" + * + * assert.strictEqual(isError(new Error("boom")), true) + * assert.strictEqual(isError(new TypeError("boom")), true) + * + * assert.strictEqual(isError({ message: "boom" }), false) + * assert.strictEqual(isError("boom"), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isError = (input: unknown): input is Error => input instanceof Error + +/** + * A refinement that checks if a value is a `Uint8Array`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isUint8Array } from "effect/Predicate" + * + * assert.strictEqual(isUint8Array(new Uint8Array()), true) + * + * assert.strictEqual(isUint8Array(new Uint16Array()), false) + * assert.strictEqual(isUint8Array([1, 2, 3]), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isUint8Array = (input: unknown): input is Uint8Array => input instanceof Uint8Array + +/** + * A refinement that checks if a value is a `Date` object. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isDate } from "effect/Predicate" + * + * assert.strictEqual(isDate(new Date()), true) + * + * assert.strictEqual(isDate(Date.now()), false) // `Date.now()` returns a number + * assert.strictEqual(isDate("2023-01-01"), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isDate = (input: unknown): input is Date => input instanceof Date + +/** + * A refinement that checks if a value is an `Iterable`. + * Many built-in types are iterable, such as `Array`, `string`, `Map`, and `Set`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isIterable } from "effect/Predicate" + * + * assert.strictEqual(isIterable([]), true) + * assert.strictEqual(isIterable("hello"), true) + * assert.strictEqual(isIterable(new Set()), true) + * + * assert.strictEqual(isIterable({}), false) + * assert.strictEqual(isIterable(123), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isIterable = (input: unknown): input is Iterable => + typeof input === "string" || hasProperty(input, Symbol.iterator) + +/** + * A refinement that checks if a value is a record (i.e., a plain object). + * This check returns `false` for arrays, `null`, and functions. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isRecord } from "effect/Predicate" + * + * assert.strictEqual(isRecord({}), true) + * assert.strictEqual(isRecord({ a: 1 }), true) + * + * assert.strictEqual(isRecord([]), false) + * assert.strictEqual(isRecord(new Date()), false) + * assert.strictEqual(isRecord(null), false) + * assert.strictEqual(isRecord(() => null), false) + * ``` + * + * @category guards + * @since 2.0.0 + * @see isObject + */ +export const isRecord = (input: unknown): input is { [x: string | symbol]: unknown } => + isRecordOrArray(input) && !Array.isArray(input) + +/** + * A refinement that checks if a value is a readonly record (i.e., a plain object). + * This check returns `false` for arrays, `null`, and functions. + * + * This is an alias for `isRecord`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isReadonlyRecord } from "effect/Predicate" + * + * assert.strictEqual(isReadonlyRecord({}), true) + * assert.strictEqual(isReadonlyRecord({ a: 1 }), true) + * + * assert.strictEqual(isReadonlyRecord([]), false) + * assert.strictEqual(isReadonlyRecord(null), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isReadonlyRecord: ( + input: unknown +) => input is { readonly [x: string | symbol]: unknown } = isRecord + +/** + * A refinement that checks if a value is a `Promise`. It performs a duck-typing check + * for `.then` and `.catch` methods. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isPromise } from "effect/Predicate" + * + * assert.strictEqual(isPromise(Promise.resolve(1)), true) + * assert.strictEqual(isPromise(new Promise(() => {})), true) + * + * assert.strictEqual(isPromise({ then() {} }), false) // Missing .catch + * assert.strictEqual(isPromise({}), false) + * ``` + * + * @category guards + * @since 2.0.0 + * @see isPromiseLike + */ +export const isPromise = ( + input: unknown +): input is Promise => + hasProperty(input, "then") && "catch" in input && isFunction(input.then) && isFunction(input.catch) + +/** + * A refinement that checks if a value is `PromiseLike`. It performs a duck-typing + * check for a `.then` method. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isPromiseLike } from "effect/Predicate" + * + * assert.strictEqual(isPromiseLike(Promise.resolve(1)), true) + * assert.strictEqual(isPromiseLike({ then: () => {} }), true) + * + * assert.strictEqual(isPromiseLike({}), false) + * ``` + * + * @category guards + * @since 2.0.0 + * @see isPromise + */ +export const isPromiseLike = ( + input: unknown +): input is PromiseLike => hasProperty(input, "then") && isFunction(input.then) + +/** + * A refinement that checks if a value is a `RegExp`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * assert.strictEqual(Predicate.isRegExp(/a/), true) + * assert.strictEqual(Predicate.isRegExp(new RegExp("a")), true) + * + * assert.strictEqual(Predicate.isRegExp("/a/"), false) + * ``` + * + * @category guards + * @since 3.9.0 + */ +export const isRegExp = (input: unknown): input is RegExp => input instanceof RegExp + +/** + * Composes a `Refinement` with another `Refinement` or `Predicate`. + * + * This can be used to chain checks. The first refinement is applied, and if it + * passes, the second check is applied to the same value, potentially refining + * the type further. + * + * @example + * ```ts + * import { Predicate } from "effect" + * import * as assert from "node:assert" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const minLength = (n: number) => (s: string): boolean => s.length >= n + * + * // Create a refinement that checks for a string with a minimum length of 3 + * const isLongString = Predicate.compose(isString, minLength(3)) + * + * let value: unknown = "hello" + * + * assert.strictEqual(isLongString(value), true) + * if (isLongString(value)) { + * // value is narrowed to string + * assert.strictEqual(value.toUpperCase(), "HELLO") + * } + * assert.strictEqual(isLongString("hi"), false) + * ``` + * + * @since 2.0.0 + */ +export const compose: { + /** + * Composes a `Refinement` with another `Refinement` or `Predicate`. + * + * This can be used to chain checks. The first refinement is applied, and if it + * passes, the second check is applied to the same value, potentially refining + * the type further. + * + * @example + * ```ts + * import { Predicate } from "effect" + * import * as assert from "node:assert" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const minLength = (n: number) => (s: string): boolean => s.length >= n + * + * // Create a refinement that checks for a string with a minimum length of 3 + * const isLongString = Predicate.compose(isString, minLength(3)) + * + * let value: unknown = "hello" + * + * assert.strictEqual(isLongString(value), true) + * if (isLongString(value)) { + * // value is narrowed to string + * assert.strictEqual(value.toUpperCase(), "HELLO") + * } + * assert.strictEqual(isLongString("hi"), false) + * ``` + * + * @since 2.0.0 + */ + (bc: Refinement): (ab: Refinement) => Refinement + /** + * Composes a `Refinement` with another `Refinement` or `Predicate`. + * + * This can be used to chain checks. The first refinement is applied, and if it + * passes, the second check is applied to the same value, potentially refining + * the type further. + * + * @example + * ```ts + * import { Predicate } from "effect" + * import * as assert from "node:assert" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const minLength = (n: number) => (s: string): boolean => s.length >= n + * + * // Create a refinement that checks for a string with a minimum length of 3 + * const isLongString = Predicate.compose(isString, minLength(3)) + * + * let value: unknown = "hello" + * + * assert.strictEqual(isLongString(value), true) + * if (isLongString(value)) { + * // value is narrowed to string + * assert.strictEqual(value.toUpperCase(), "HELLO") + * } + * assert.strictEqual(isLongString("hi"), false) + * ``` + * + * @since 2.0.0 + */ + (bc: Predicate>): (ab: Refinement) => Refinement + /** + * Composes a `Refinement` with another `Refinement` or `Predicate`. + * + * This can be used to chain checks. The first refinement is applied, and if it + * passes, the second check is applied to the same value, potentially refining + * the type further. + * + * @example + * ```ts + * import { Predicate } from "effect" + * import * as assert from "node:assert" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const minLength = (n: number) => (s: string): boolean => s.length >= n + * + * // Create a refinement that checks for a string with a minimum length of 3 + * const isLongString = Predicate.compose(isString, minLength(3)) + * + * let value: unknown = "hello" + * + * assert.strictEqual(isLongString(value), true) + * if (isLongString(value)) { + * // value is narrowed to string + * assert.strictEqual(value.toUpperCase(), "HELLO") + * } + * assert.strictEqual(isLongString("hi"), false) + * ``` + * + * @since 2.0.0 + */ + (ab: Refinement, bc: Refinement): Refinement + /** + * Composes a `Refinement` with another `Refinement` or `Predicate`. + * + * This can be used to chain checks. The first refinement is applied, and if it + * passes, the second check is applied to the same value, potentially refining + * the type further. + * + * @example + * ```ts + * import { Predicate } from "effect" + * import * as assert from "node:assert" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const minLength = (n: number) => (s: string): boolean => s.length >= n + * + * // Create a refinement that checks for a string with a minimum length of 3 + * const isLongString = Predicate.compose(isString, minLength(3)) + * + * let value: unknown = "hello" + * + * assert.strictEqual(isLongString(value), true) + * if (isLongString(value)) { + * // value is narrowed to string + * assert.strictEqual(value.toUpperCase(), "HELLO") + * } + * assert.strictEqual(isLongString("hi"), false) + * ``` + * + * @since 2.0.0 + */ + (ab: Refinement, bc: Predicate>): Refinement +} = dual( + 2, + (ab: Refinement, bc: Refinement): Refinement => + (a): a is D => ab(a) && bc(a as C) +) + +/** + * Combines two predicates to test a tuple of two values. The first predicate tests the + * first element of the tuple, and the second predicate tests the second element. + * + * @category combining + * @since 2.0.0 + */ +export const product = + (self: Predicate, that: Predicate): Predicate /* readonly because contravariant */ => + ([a, b]) => self(a) && that(b) + +/** + * Takes an iterable of predicates and returns a new predicate that tests an array of values. + * The new predicate returns `true` if each predicate at a given index is satisfied by the + * value at the same index in the array. The check stops at the length of the shorter of + * the two iterables (predicates or values). + * + * @category combining + * @since 2.0.0 + * @see tuple for a more powerful, variadic version. + */ +export const all = ( + collection: Iterable> +): Predicate> => { + return (as) => { + let collectionIndex = 0 + for (const p of collection) { + if (collectionIndex >= as.length) { + break + } + if (p(as[collectionIndex]) === false) { + return false + } + collectionIndex++ + } + return true + } +} + +/** + * Combines a predicate for a single value and an iterable of predicates for the rest of an array. + * Useful for checking the head and tail of an array separately. + * + * @category combining + * @since 2.0.0 + */ +export const productMany = ( + self: Predicate, + collection: Iterable> +): Predicate]> /* readonly because contravariant */ => { + const rest = all(collection) + return ([head, ...tail]) => self(head) === false ? false : rest(tail) +} + +/** + * Combines an array of predicates into a single predicate that tests an array of values. + * This function is highly type-aware and will produce a `Refinement` if any of the provided + * predicates are `Refinement`s, allowing for powerful type-narrowing of tuples. + * + * - If all predicates are `Predicate`, the result is `Predicate<[T, T, ...]>`. + * - If any predicate is a `Refinement`, the result is a `Refinement` that narrows + * the input tuple type to a more specific tuple type. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const isNumber = (u: unknown): u is number => typeof u === "number" + * + * // Create a refinement for a [string, number] tuple + * const isStringNumberTuple = Predicate.tuple(isString, isNumber) + * + * const value: [unknown, unknown] = ["hello", 123] + * if (isStringNumberTuple(value)) { + * // value is narrowed to [string, number] + * const [s, n] = value + * assert.strictEqual(s.toUpperCase(), "HELLO") + * assert.strictEqual(n.toFixed(2), "123.00") + * } + * assert.strictEqual(isStringNumberTuple(["hello", "123"]), false) + * ``` + * + * @since 2.0.0 + */ +export const tuple: { + /** + * Combines an array of predicates into a single predicate that tests an array of values. + * This function is highly type-aware and will produce a `Refinement` if any of the provided + * predicates are `Refinement`s, allowing for powerful type-narrowing of tuples. + * + * - If all predicates are `Predicate`, the result is `Predicate<[T, T, ...]>`. + * - If any predicate is a `Refinement`, the result is a `Refinement` that narrows + * the input tuple type to a more specific tuple type. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const isNumber = (u: unknown): u is number => typeof u === "number" + * + * // Create a refinement for a [string, number] tuple + * const isStringNumberTuple = Predicate.tuple(isString, isNumber) + * + * const value: [unknown, unknown] = ["hello", 123] + * if (isStringNumberTuple(value)) { + * // value is narrowed to [string, number] + * const [s, n] = value + * assert.strictEqual(s.toUpperCase(), "HELLO") + * assert.strictEqual(n.toFixed(2), "123.00") + * } + * assert.strictEqual(isStringNumberTuple(["hello", "123"]), false) + * ``` + * + * @since 2.0.0 + */ + >(...elements: T): [Extract] extends [never] ? Predicate<{ readonly [I in keyof T]: Predicate.In }> + : Refinement< + { readonly [I in keyof T]: T[I] extends Refinement.Any ? Refinement.In : Predicate.In }, + { readonly [I in keyof T]: T[I] extends Refinement.Any ? Refinement.Out : Predicate.In } + > +} = (...elements: ReadonlyArray) => all(elements) as any + +/** + * Combines a record of predicates into a single predicate that tests a record of values. + * This function is highly type-aware and will produce a `Refinement` if any of the provided + * predicates are `Refinement`s, allowing for powerful type-narrowing of structs. + * + * - If all predicates are `Predicate`, the result is `Predicate<{ k: T, ... }>`. + * - If any predicate is a `Refinement`, the result is a `Refinement` that narrows + * the input record type to a more specific record type. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const isNumber = (u: unknown): u is number => typeof u === "number" + * + * const personPredicate = Predicate.struct({ + * name: isString, + * age: isNumber + * }) + * + * const value: { name: unknown; age: unknown } = { name: "Alice", age: 30 } + * if (personPredicate(value)) { + * // value is narrowed to { name: string; age: number } + * assert.strictEqual(value.name.toUpperCase(), "ALICE") + * assert.strictEqual(value.age.toFixed(0), "30") + * } + * assert.strictEqual(personPredicate({ name: "Bob", age: "40" }), false) + * ``` + * + * @since 2.0.0 + */ +export const struct: { + /** + * Combines a record of predicates into a single predicate that tests a record of values. + * This function is highly type-aware and will produce a `Refinement` if any of the provided + * predicates are `Refinement`s, allowing for powerful type-narrowing of structs. + * + * - If all predicates are `Predicate`, the result is `Predicate<{ k: T, ... }>`. + * - If any predicate is a `Refinement`, the result is a `Refinement` that narrows + * the input record type to a more specific record type. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const isNumber = (u: unknown): u is number => typeof u === "number" + * + * const personPredicate = Predicate.struct({ + * name: isString, + * age: isNumber + * }) + * + * const value: { name: unknown; age: unknown } = { name: "Alice", age: 30 } + * if (personPredicate(value)) { + * // value is narrowed to { name: string; age: number } + * assert.strictEqual(value.name.toUpperCase(), "ALICE") + * assert.strictEqual(value.age.toFixed(0), "30") + * } + * assert.strictEqual(personPredicate({ name: "Bob", age: "40" }), false) + * ``` + * + * @since 2.0.0 + */ + >(fields: R): [Extract] extends [never] ? + Predicate<{ readonly [K in keyof R]: Predicate.In }> : + Refinement< + { readonly [K in keyof R]: R[K] extends Refinement.Any ? Refinement.In : Predicate.In }, + { readonly [K in keyof R]: R[K] extends Refinement.Any ? Refinement.Out : Predicate.In } + > +} = (>(fields: R) => { + const keys = Object.keys(fields) + return (a: Record) => { + for (const key of keys) { + if (!fields[key](a[key] as never)) { + return false + } + } + return true + } +}) as any + +/** + * Returns a new predicate that is the logical negation of the given predicate. + * + * **Note**: If the input is a `Refinement`, the resulting predicate will be a + * simple `Predicate`, as TypeScript cannot infer the negative type. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate, Number } from "effect" + * + * const isNonPositive = Predicate.not(Number.greaterThan(0)) + * + * assert.strictEqual(isNonPositive(-1), true) + * assert.strictEqual(isNonPositive(0), true) + * assert.strictEqual(isNonPositive(1), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const not = (self: Predicate): Predicate => (a) => !self(a) + +/** + * Combines two predicates with a logical "OR". The resulting predicate returns `true` + * if at least one of the predicates returns `true`. + * + * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the + * union of their target types (`B | C`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const isNumber = (u: unknown): u is number => typeof u === "number" + * + * const isStringOrNumber = Predicate.or(isString, isNumber) + * + * assert.strictEqual(isStringOrNumber("hello"), true) + * assert.strictEqual(isStringOrNumber(123), true) + * assert.strictEqual(isStringOrNumber(null), false) + * + * const value: unknown = "world" + * if (isStringOrNumber(value)) { + * // value is narrowed to string | number + * console.log(value) + * } + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const or: { + /** + * Combines two predicates with a logical "OR". The resulting predicate returns `true` + * if at least one of the predicates returns `true`. + * + * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the + * union of their target types (`B | C`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const isNumber = (u: unknown): u is number => typeof u === "number" + * + * const isStringOrNumber = Predicate.or(isString, isNumber) + * + * assert.strictEqual(isStringOrNumber("hello"), true) + * assert.strictEqual(isStringOrNumber(123), true) + * assert.strictEqual(isStringOrNumber(null), false) + * + * const value: unknown = "world" + * if (isStringOrNumber(value)) { + * // value is narrowed to string | number + * console.log(value) + * } + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: Refinement): (self: Refinement) => Refinement + /** + * Combines two predicates with a logical "OR". The resulting predicate returns `true` + * if at least one of the predicates returns `true`. + * + * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the + * union of their target types (`B | C`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const isNumber = (u: unknown): u is number => typeof u === "number" + * + * const isStringOrNumber = Predicate.or(isString, isNumber) + * + * assert.strictEqual(isStringOrNumber("hello"), true) + * assert.strictEqual(isStringOrNumber(123), true) + * assert.strictEqual(isStringOrNumber(null), false) + * + * const value: unknown = "world" + * if (isStringOrNumber(value)) { + * // value is narrowed to string | number + * console.log(value) + * } + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: Refinement, that: Refinement): Refinement + /** + * Combines two predicates with a logical "OR". The resulting predicate returns `true` + * if at least one of the predicates returns `true`. + * + * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the + * union of their target types (`B | C`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const isNumber = (u: unknown): u is number => typeof u === "number" + * + * const isStringOrNumber = Predicate.or(isString, isNumber) + * + * assert.strictEqual(isStringOrNumber("hello"), true) + * assert.strictEqual(isStringOrNumber(123), true) + * assert.strictEqual(isStringOrNumber(null), false) + * + * const value: unknown = "world" + * if (isStringOrNumber(value)) { + * // value is narrowed to string | number + * console.log(value) + * } + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: Predicate): (self: Predicate) => Predicate + /** + * Combines two predicates with a logical "OR". The resulting predicate returns `true` + * if at least one of the predicates returns `true`. + * + * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the + * union of their target types (`B | C`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isString = (u: unknown): u is string => typeof u === "string" + * const isNumber = (u: unknown): u is number => typeof u === "number" + * + * const isStringOrNumber = Predicate.or(isString, isNumber) + * + * assert.strictEqual(isStringOrNumber("hello"), true) + * assert.strictEqual(isStringOrNumber(123), true) + * assert.strictEqual(isStringOrNumber(null), false) + * + * const value: unknown = "world" + * if (isStringOrNumber(value)) { + * // value is narrowed to string | number + * console.log(value) + * } + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: Predicate, that: Predicate): Predicate +} = dual(2, (self: Predicate, that: Predicate): Predicate => (a) => self(a) || that(a)) + +/** + * Combines two predicates with a logical "AND". The resulting predicate returns `true` + * only if both of the predicates return `true`. + * + * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the + * intersection of their target types (`B & C`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * type Person = { name: string } + * type Employee = { id: number } + * + * const hasName = (u: unknown): u is Person => Predicate.hasProperty(u, "name") && typeof (u as any).name === "string" + * const hasId = (u: unknown): u is Employee => Predicate.hasProperty(u, "id") && typeof (u as any).id === "number" + * + * const isPersonAndEmployee = Predicate.and(hasName, hasId) + * + * const val: unknown = { name: "Alice", id: 123 } + * if (isPersonAndEmployee(val)) { + * // val is narrowed to Person & Employee + * assert.strictEqual(val.name, "Alice") + * assert.strictEqual(val.id, 123) + * } + * + * assert.strictEqual(isPersonAndEmployee({ name: "Bob" }), false) // Missing id + * assert.strictEqual(isPersonAndEmployee({ id: 456 }), false) // Missing name + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const and: { + /** + * Combines two predicates with a logical "AND". The resulting predicate returns `true` + * only if both of the predicates return `true`. + * + * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the + * intersection of their target types (`B & C`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * type Person = { name: string } + * type Employee = { id: number } + * + * const hasName = (u: unknown): u is Person => Predicate.hasProperty(u, "name") && typeof (u as any).name === "string" + * const hasId = (u: unknown): u is Employee => Predicate.hasProperty(u, "id") && typeof (u as any).id === "number" + * + * const isPersonAndEmployee = Predicate.and(hasName, hasId) + * + * const val: unknown = { name: "Alice", id: 123 } + * if (isPersonAndEmployee(val)) { + * // val is narrowed to Person & Employee + * assert.strictEqual(val.name, "Alice") + * assert.strictEqual(val.id, 123) + * } + * + * assert.strictEqual(isPersonAndEmployee({ name: "Bob" }), false) // Missing id + * assert.strictEqual(isPersonAndEmployee({ id: 456 }), false) // Missing name + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: Refinement): (self: Refinement) => Refinement + /** + * Combines two predicates with a logical "AND". The resulting predicate returns `true` + * only if both of the predicates return `true`. + * + * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the + * intersection of their target types (`B & C`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * type Person = { name: string } + * type Employee = { id: number } + * + * const hasName = (u: unknown): u is Person => Predicate.hasProperty(u, "name") && typeof (u as any).name === "string" + * const hasId = (u: unknown): u is Employee => Predicate.hasProperty(u, "id") && typeof (u as any).id === "number" + * + * const isPersonAndEmployee = Predicate.and(hasName, hasId) + * + * const val: unknown = { name: "Alice", id: 123 } + * if (isPersonAndEmployee(val)) { + * // val is narrowed to Person & Employee + * assert.strictEqual(val.name, "Alice") + * assert.strictEqual(val.id, 123) + * } + * + * assert.strictEqual(isPersonAndEmployee({ name: "Bob" }), false) // Missing id + * assert.strictEqual(isPersonAndEmployee({ id: 456 }), false) // Missing name + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: Refinement, that: Refinement): Refinement + /** + * Combines two predicates with a logical "AND". The resulting predicate returns `true` + * only if both of the predicates return `true`. + * + * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the + * intersection of their target types (`B & C`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * type Person = { name: string } + * type Employee = { id: number } + * + * const hasName = (u: unknown): u is Person => Predicate.hasProperty(u, "name") && typeof (u as any).name === "string" + * const hasId = (u: unknown): u is Employee => Predicate.hasProperty(u, "id") && typeof (u as any).id === "number" + * + * const isPersonAndEmployee = Predicate.and(hasName, hasId) + * + * const val: unknown = { name: "Alice", id: 123 } + * if (isPersonAndEmployee(val)) { + * // val is narrowed to Person & Employee + * assert.strictEqual(val.name, "Alice") + * assert.strictEqual(val.id, 123) + * } + * + * assert.strictEqual(isPersonAndEmployee({ name: "Bob" }), false) // Missing id + * assert.strictEqual(isPersonAndEmployee({ id: 456 }), false) // Missing name + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: Predicate): (self: Predicate) => Predicate + /** + * Combines two predicates with a logical "AND". The resulting predicate returns `true` + * only if both of the predicates return `true`. + * + * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the + * intersection of their target types (`B & C`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * type Person = { name: string } + * type Employee = { id: number } + * + * const hasName = (u: unknown): u is Person => Predicate.hasProperty(u, "name") && typeof (u as any).name === "string" + * const hasId = (u: unknown): u is Employee => Predicate.hasProperty(u, "id") && typeof (u as any).id === "number" + * + * const isPersonAndEmployee = Predicate.and(hasName, hasId) + * + * const val: unknown = { name: "Alice", id: 123 } + * if (isPersonAndEmployee(val)) { + * // val is narrowed to Person & Employee + * assert.strictEqual(val.name, "Alice") + * assert.strictEqual(val.id, 123) + * } + * + * assert.strictEqual(isPersonAndEmployee({ name: "Bob" }), false) // Missing id + * assert.strictEqual(isPersonAndEmployee({ id: 456 }), false) // Missing name + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: Predicate, that: Predicate): Predicate +} = dual(2, (self: Predicate, that: Predicate): Predicate => (a) => self(a) && that(a)) + +/** + * Combines two predicates with a logical "XOR" (exclusive OR). The resulting predicate + * returns `true` if one of the predicates returns `true`, but not both. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isPositive = (n: number) => n > 0 + * const isEven = (n: number) => n % 2 === 0 + * + * const isPositiveXorEven = Predicate.xor(isPositive, isEven) + * + * assert.strictEqual(isPositiveXorEven(4), false) // both true -> false + * assert.strictEqual(isPositiveXorEven(3), true) // one true -> true + * assert.strictEqual(isPositiveXorEven(-2), true) // one true -> true + * assert.strictEqual(isPositiveXorEven(-1), false) // both false -> false + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const xor: { + /** + * Combines two predicates with a logical "XOR" (exclusive OR). The resulting predicate + * returns `true` if one of the predicates returns `true`, but not both. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isPositive = (n: number) => n > 0 + * const isEven = (n: number) => n % 2 === 0 + * + * const isPositiveXorEven = Predicate.xor(isPositive, isEven) + * + * assert.strictEqual(isPositiveXorEven(4), false) // both true -> false + * assert.strictEqual(isPositiveXorEven(3), true) // one true -> true + * assert.strictEqual(isPositiveXorEven(-2), true) // one true -> true + * assert.strictEqual(isPositiveXorEven(-1), false) // both false -> false + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: Predicate): (self: Predicate) => Predicate + /** + * Combines two predicates with a logical "XOR" (exclusive OR). The resulting predicate + * returns `true` if one of the predicates returns `true`, but not both. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isPositive = (n: number) => n > 0 + * const isEven = (n: number) => n % 2 === 0 + * + * const isPositiveXorEven = Predicate.xor(isPositive, isEven) + * + * assert.strictEqual(isPositiveXorEven(4), false) // both true -> false + * assert.strictEqual(isPositiveXorEven(3), true) // one true -> true + * assert.strictEqual(isPositiveXorEven(-2), true) // one true -> true + * assert.strictEqual(isPositiveXorEven(-1), false) // both false -> false + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: Predicate, that: Predicate): Predicate +} = dual(2, (self: Predicate, that: Predicate): Predicate => (a) => self(a) !== that(a)) + +/** + * Combines two predicates with a logical "EQV" (equivalence). The resulting predicate + * returns `true` if both predicates return the same boolean value (both `true` or both `false`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isPositive = (n: number) => n > 0 + * const isEven = (n: number) => n % 2 === 0 + * + * const isPositiveEqvEven = Predicate.eqv(isPositive, isEven) + * + * assert.strictEqual(isPositiveEqvEven(4), true) // both true -> true + * assert.strictEqual(isPositiveEqvEven(3), false) // different -> false + * assert.strictEqual(isPositiveEqvEven(-2), false) // different -> false + * assert.strictEqual(isPositiveEqvEven(-1), true) // both false -> true + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const eqv: { + /** + * Combines two predicates with a logical "EQV" (equivalence). The resulting predicate + * returns `true` if both predicates return the same boolean value (both `true` or both `false`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isPositive = (n: number) => n > 0 + * const isEven = (n: number) => n % 2 === 0 + * + * const isPositiveEqvEven = Predicate.eqv(isPositive, isEven) + * + * assert.strictEqual(isPositiveEqvEven(4), true) // both true -> true + * assert.strictEqual(isPositiveEqvEven(3), false) // different -> false + * assert.strictEqual(isPositiveEqvEven(-2), false) // different -> false + * assert.strictEqual(isPositiveEqvEven(-1), true) // both false -> true + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (that: Predicate): (self: Predicate) => Predicate + /** + * Combines two predicates with a logical "EQV" (equivalence). The resulting predicate + * returns `true` if both predicates return the same boolean value (both `true` or both `false`). + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isPositive = (n: number) => n > 0 + * const isEven = (n: number) => n % 2 === 0 + * + * const isPositiveEqvEven = Predicate.eqv(isPositive, isEven) + * + * assert.strictEqual(isPositiveEqvEven(4), true) // both true -> true + * assert.strictEqual(isPositiveEqvEven(3), false) // different -> false + * assert.strictEqual(isPositiveEqvEven(-2), false) // different -> false + * assert.strictEqual(isPositiveEqvEven(-1), true) // both false -> true + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (self: Predicate, that: Predicate): Predicate +} = dual(2, (self: Predicate, that: Predicate): Predicate => (a) => self(a) === that(a)) + +/** + * Creates a predicate that represents a logical "if-then" rule. + * + * Think of it as a conditional promise: **"If `antecedent` holds true, then I promise `consequent` will also be true."** + * + * This function is invaluable for defining complex validation logic where one condition dictates another. + * + * ### How It Works + * + * The rule only fails (returns `false`) when the "if" part is `true`, but the "then" part is `false`. + * In all other cases, the promise is considered kept, and the result is `true`. + * + * This includes the concept of **"vacuous truth"**: if the "if" part is `false`, the rule doesn't apply, + * so the promise isn't broken, and the result is `true`. (e.g., "If it rains, I'll bring an umbrella." + * If it doesn't rain, you haven't broken your promise, no matter what). + * + * ### Key Details + * + * - **Logical Equivalence**: `implies(p, q)` is the same as `not(p).or(q)`, or simply `!p || q` + * in plain JavaScript. This can be a helpful way to reason about its behavior. + * + * - **Type-Safety Warning**: This function always returns a `Predicate`, never a type-narrowing + * `Refinement`. A `true` result doesn't guarantee the `consequent` passed (it could be `true` + * simply because the `antecedent` was `false`), so it cannot be used to safely narrow a type. + * + * @example + * ```ts + * // Rule: A user can only be an admin if they also belong to the "staff" group. + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * type User = { + * isStaff: boolean + * isAdmin: boolean + * } + * + * const isValidUserPermission = Predicate.implies( + * // antecedent: "if" the user is an admin... + * (user: User) => user.isAdmin, + * // consequent: "then" they must be staff. + * (user: User) => user.isStaff + * ) + * + * // A non-admin who is not staff. Rule doesn't apply (antecedent is false). + * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: false }), true) + * + * // A staff member who is not an admin. Rule doesn't apply (antecedent is false). + * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: false }), true) + * + * // An admin who is also staff. The rule was followed. + * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: true }), true) + * + * // An admin who is NOT staff. The rule was broken! + * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: true }), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ +export const implies: { + /** + * Creates a predicate that represents a logical "if-then" rule. + * + * Think of it as a conditional promise: **"If `antecedent` holds true, then I promise `consequent` will also be true."** + * + * This function is invaluable for defining complex validation logic where one condition dictates another. + * + * ### How It Works + * + * The rule only fails (returns `false`) when the "if" part is `true`, but the "then" part is `false`. + * In all other cases, the promise is considered kept, and the result is `true`. + * + * This includes the concept of **"vacuous truth"**: if the "if" part is `false`, the rule doesn't apply, + * so the promise isn't broken, and the result is `true`. (e.g., "If it rains, I'll bring an umbrella." + * If it doesn't rain, you haven't broken your promise, no matter what). + * + * ### Key Details + * + * - **Logical Equivalence**: `implies(p, q)` is the same as `not(p).or(q)`, or simply `!p || q` + * in plain JavaScript. This can be a helpful way to reason about its behavior. + * + * - **Type-Safety Warning**: This function always returns a `Predicate`, never a type-narrowing + * `Refinement`. A `true` result doesn't guarantee the `consequent` passed (it could be `true` + * simply because the `antecedent` was `false`), so it cannot be used to safely narrow a type. + * + * @example + * ```ts + * // Rule: A user can only be an admin if they also belong to the "staff" group. + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * type User = { + * isStaff: boolean + * isAdmin: boolean + * } + * + * const isValidUserPermission = Predicate.implies( + * // antecedent: "if" the user is an admin... + * (user: User) => user.isAdmin, + * // consequent: "then" they must be staff. + * (user: User) => user.isStaff + * ) + * + * // A non-admin who is not staff. Rule doesn't apply (antecedent is false). + * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: false }), true) + * + * // A staff member who is not an admin. Rule doesn't apply (antecedent is false). + * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: false }), true) + * + * // An admin who is also staff. The rule was followed. + * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: true }), true) + * + * // An admin who is NOT staff. The rule was broken! + * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: true }), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (consequent: Predicate): (antecedent: Predicate) => Predicate + /** + * Creates a predicate that represents a logical "if-then" rule. + * + * Think of it as a conditional promise: **"If `antecedent` holds true, then I promise `consequent` will also be true."** + * + * This function is invaluable for defining complex validation logic where one condition dictates another. + * + * ### How It Works + * + * The rule only fails (returns `false`) when the "if" part is `true`, but the "then" part is `false`. + * In all other cases, the promise is considered kept, and the result is `true`. + * + * This includes the concept of **"vacuous truth"**: if the "if" part is `false`, the rule doesn't apply, + * so the promise isn't broken, and the result is `true`. (e.g., "If it rains, I'll bring an umbrella." + * If it doesn't rain, you haven't broken your promise, no matter what). + * + * ### Key Details + * + * - **Logical Equivalence**: `implies(p, q)` is the same as `not(p).or(q)`, or simply `!p || q` + * in plain JavaScript. This can be a helpful way to reason about its behavior. + * + * - **Type-Safety Warning**: This function always returns a `Predicate`, never a type-narrowing + * `Refinement`. A `true` result doesn't guarantee the `consequent` passed (it could be `true` + * simply because the `antecedent` was `false`), so it cannot be used to safely narrow a type. + * + * @example + * ```ts + * // Rule: A user can only be an admin if they also belong to the "staff" group. + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * type User = { + * isStaff: boolean + * isAdmin: boolean + * } + * + * const isValidUserPermission = Predicate.implies( + * // antecedent: "if" the user is an admin... + * (user: User) => user.isAdmin, + * // consequent: "then" they must be staff. + * (user: User) => user.isStaff + * ) + * + * // A non-admin who is not staff. Rule doesn't apply (antecedent is false). + * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: false }), true) + * + * // A staff member who is not an admin. Rule doesn't apply (antecedent is false). + * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: false }), true) + * + * // An admin who is also staff. The rule was followed. + * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: true }), true) + * + * // An admin who is NOT staff. The rule was broken! + * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: true }), false) + * ``` + * + * @category combinators + * @since 2.0.0 + */ + (antecedent: Predicate, consequent: Predicate): Predicate +} = dual( + 2, + (antecedent: Predicate, consequent: Predicate): Predicate => (a) => antecedent(a) ? consequent(a) : true +) + +/** + * Combines two predicates with a logical "NOR" (negated OR). The resulting predicate + * returns `true` only if both predicates return `false`. + * This is equivalent to `not(or(p, q))`. + * + * @category combinators + * @since 2.0.0 + */ +export const nor: { + /** + * Combines two predicates with a logical "NOR" (negated OR). The resulting predicate + * returns `true` only if both predicates return `false`. + * This is equivalent to `not(or(p, q))`. + * + * @category combinators + * @since 2.0.0 + */ + (that: Predicate): (self: Predicate) => Predicate + /** + * Combines two predicates with a logical "NOR" (negated OR). The resulting predicate + * returns `true` only if both predicates return `false`. + * This is equivalent to `not(or(p, q))`. + * + * @category combinators + * @since 2.0.0 + */ + (self: Predicate, that: Predicate): Predicate +} = dual( + 2, + (self: Predicate, that: Predicate): Predicate => (a) => !(self(a) || that(a)) +) + +/** + * Combines two predicates with a logical "NAND" (negated AND). The resulting predicate + * returns `true` if at least one of the predicates returns `false`. + * This is equivalent to `not(and(p, q))`. + * + * @category combinators + * @since 2.0.0 + */ +export const nand: { + /** + * Combines two predicates with a logical "NAND" (negated AND). The resulting predicate + * returns `true` if at least one of the predicates returns `false`. + * This is equivalent to `not(and(p, q))`. + * + * @category combinators + * @since 2.0.0 + */ + (that: Predicate): (self: Predicate) => Predicate + /** + * Combines two predicates with a logical "NAND" (negated AND). The resulting predicate + * returns `true` if at least one of the predicates returns `false`. + * This is equivalent to `not(and(p, q))`. + * + * @category combinators + * @since 2.0.0 + */ + (self: Predicate, that: Predicate): Predicate +} = dual( + 2, + (self: Predicate, that: Predicate): Predicate => (a) => !(self(a) && that(a)) +) + +/** + * Takes an iterable of predicates and returns a new predicate. The new predicate + * returns `true` if all predicates in the collection return `true` for a given value. + * + * This is like `Array.prototype.every` but for a collection of predicates. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isPositive = (n: number) => n > 0 + * const isEven = (n: number) => n % 2 === 0 + * + * const isPositiveAndEven = Predicate.every([isPositive, isEven]) + * + * assert.strictEqual(isPositiveAndEven(4), true) + * assert.strictEqual(isPositiveAndEven(3), false) + * assert.strictEqual(isPositiveAndEven(-2), false) + * ``` + * + * @category elements + * @since 2.0.0 + * @see some + */ +export const every = (collection: Iterable>): Predicate => (a: A) => { + for (const p of collection) { + if (!p(a)) { + return false + } + } + return true +} + +/** + * Takes an iterable of predicates and returns a new predicate. The new predicate + * returns `true` if at least one predicate in the collection returns `true` for a given value. + * + * This is like `Array.prototype.some` but for a collection of predicates. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * const isNegative = (n: number) => n < 0 + * const isOdd = (n: number) => n % 2 !== 0 + * + * const isNegativeOrOdd = Predicate.some([isNegative, isOdd]) + * + * assert.strictEqual(isNegativeOrOdd(-2), true) // isNegative is true + * assert.strictEqual(isNegativeOrOdd(3), true) // isOdd is true + * assert.strictEqual(isNegativeOrOdd(4), false) // both are false + * ``` + * + * @category elements + * @since 2.0.0 + * @see every + */ +export const some = (collection: Iterable>): Predicate => (a) => { + for (const p of collection) { + if (p(a)) { + return true + } + } + return false +} diff --git a/backend/node_modules/effect/src/Pretty.ts b/backend/node_modules/effect/src/Pretty.ts new file mode 100644 index 0000000000000000000000000000000000000000..a772b7b9ba12575cd02c1dc0b2313ef11b499904 --- /dev/null +++ b/backend/node_modules/effect/src/Pretty.ts @@ -0,0 +1,205 @@ +/** + * @since 3.10.0 + */ +import * as Arr from "./Array.js" +import * as Inspectable from "./Inspectable.js" +import * as errors_ from "./internal/schema/errors.js" +import * as util_ from "./internal/schema/util.js" +import * as Option from "./Option.js" +import * as ParseResult from "./ParseResult.js" +import type * as Schema from "./Schema.js" +import * as AST from "./SchemaAST.js" + +/** + * @category model + * @since 3.10.0 + */ +export interface Pretty { + (a: To): string +} + +/** + * @category annotations + * @since 3.10.0 + */ +export type PrettyAnnotation = readonly []> = ( + ...pretties: { readonly [K in keyof TypeParameters]: Pretty } +) => Pretty + +/** + * @category prettify + * @since 3.10.0 + */ +export const make = (schema: Schema.Schema): (a: A) => string => compile(schema.ast, []) + +const getPrettyAnnotation = AST.getAnnotation>(AST.PrettyAnnotationId) + +const getMatcher = (defaultPretty: Pretty) => (ast: AST.AST): Pretty => + Option.match(getPrettyAnnotation(ast), { + onNone: () => defaultPretty, + onSome: (handler) => handler() + }) + +const toString = getMatcher((a) => String(a)) + +const stringify = getMatcher((a) => JSON.stringify(a)) + +const formatUnknown = getMatcher(Inspectable.formatUnknown) + +/** + * @since 3.10.0 + */ +export const match: AST.Match> = { + "Declaration": (ast, go, path) => { + const annotation = getPrettyAnnotation(ast) + if (Option.isSome(annotation)) { + return annotation.value(...ast.typeParameters.map((tp) => go(tp, path))) + } + throw new Error(errors_.getPrettyMissingAnnotationErrorMessage(path, ast)) + }, + "VoidKeyword": getMatcher(() => "void(0)"), + "NeverKeyword": getMatcher(() => { + throw new Error(errors_.getPrettyNeverErrorMessage) + }), + "Literal": getMatcher((literal: AST.LiteralValue): string => + typeof literal === "bigint" ? + `${String(literal)}n` : + JSON.stringify(literal) + ), + "SymbolKeyword": toString, + "UniqueSymbol": toString, + "TemplateLiteral": stringify, + "UndefinedKeyword": toString, + "UnknownKeyword": formatUnknown, + "AnyKeyword": formatUnknown, + "ObjectKeyword": formatUnknown, + "StringKeyword": stringify, + "NumberKeyword": toString, + "BooleanKeyword": toString, + "BigIntKeyword": getMatcher((a) => `${String(a)}n`), + "Enums": stringify, + "TupleType": (ast, go, path) => { + const hook = getPrettyAnnotation(ast) + if (Option.isSome(hook)) { + return hook.value() + } + const elements = ast.elements.map((e, i) => go(e.type, path.concat(i))) + const rest = ast.rest.map((annotatedAST) => go(annotatedAST.type, path)) + return (input: ReadonlyArray) => { + const output: Array = [] + let i = 0 + // --------------------------------------------- + // handle elements + // --------------------------------------------- + for (; i < elements.length; i++) { + if (input.length < i + 1) { + if (ast.elements[i].isOptional) { + continue + } + } else { + output.push(elements[i](input[i])) + } + } + // --------------------------------------------- + // handle rest element + // --------------------------------------------- + if (Arr.isNonEmptyReadonlyArray(rest)) { + const [head, ...tail] = rest + for (; i < input.length - tail.length; i++) { + output.push(head(input[i])) + } + // --------------------------------------------- + // handle post rest elements + // --------------------------------------------- + for (let j = 0; j < tail.length; j++) { + i += j + output.push(tail[j](input[i])) + } + } + + return "[" + output.join(", ") + "]" + } + }, + "TypeLiteral": (ast, go, path) => { + const hook = getPrettyAnnotation(ast) + if (Option.isSome(hook)) { + return hook.value() + } + const propertySignaturesTypes = ast.propertySignatures.map((ps) => go(ps.type, path.concat(ps.name))) + const indexSignatureTypes = ast.indexSignatures.map((is) => go(is.type, path)) + const expectedKeys: any = {} + for (let i = 0; i < propertySignaturesTypes.length; i++) { + expectedKeys[ast.propertySignatures[i].name] = null + } + return (input: { readonly [x: PropertyKey]: unknown }) => { + const output: Array = [] + // --------------------------------------------- + // handle property signatures + // --------------------------------------------- + for (let i = 0; i < propertySignaturesTypes.length; i++) { + const ps = ast.propertySignatures[i] + const name = ps.name + if (ps.isOptional && !Object.prototype.hasOwnProperty.call(input, name)) { + continue + } + output.push( + `${Inspectable.formatPropertyKey(name)}: ${propertySignaturesTypes[i](input[name])}` + ) + } + // --------------------------------------------- + // handle index signatures + // --------------------------------------------- + if (indexSignatureTypes.length > 0) { + for (let i = 0; i < indexSignatureTypes.length; i++) { + const type = indexSignatureTypes[i] + const keys = util_.getKeysForIndexSignature(input, ast.indexSignatures[i].parameter) + for (const key of keys) { + if (Object.prototype.hasOwnProperty.call(expectedKeys, key)) { + continue + } + output.push(`${Inspectable.formatPropertyKey(key)}: ${type(input[key])}`) + } + } + } + + return Arr.isNonEmptyReadonlyArray(output) ? "{ " + output.join(", ") + " }" : "{}" + } + }, + "Union": (ast, go, path) => { + const hook = getPrettyAnnotation(ast) + if (Option.isSome(hook)) { + return hook.value() + } + const types = ast.types.map((ast) => [ParseResult.is({ ast } as any), go(ast, path)] as const) + return (a) => { + const index = types.findIndex(([is]) => is(a)) + if (index === -1) { + throw new Error(errors_.getPrettyNoMatchingSchemaErrorMessage(a, path, ast)) + } + return types[index][1](a) + } + }, + "Suspend": (ast, go, path) => { + return Option.match(getPrettyAnnotation(ast), { + onNone: () => { + const get = util_.memoizeThunk(() => go(ast.f(), path)) + return (a) => get()(a) + }, + onSome: (handler) => handler() + }) + }, + "Refinement": (ast, go, path) => { + return Option.match(getPrettyAnnotation(ast), { + onNone: () => go(ast.from, path), + onSome: (handler) => handler() + }) + }, + "Transformation": (ast, go, path) => { + return Option.match(getPrettyAnnotation(ast), { + onNone: () => go(ast.to, path), + onSome: (handler) => handler() + }) + } +} + +const compile = AST.getCompiler(match) diff --git a/backend/node_modules/effect/src/PrimaryKey.ts b/backend/node_modules/effect/src/PrimaryKey.ts new file mode 100644 index 0000000000000000000000000000000000000000..dda1d117958c473792d425a08c2f742a2cb3402a --- /dev/null +++ b/backend/node_modules/effect/src/PrimaryKey.ts @@ -0,0 +1,23 @@ +/** + * @since 2.0.0 + */ + +/** + * @since 2.0.0 + * @category symbols + */ +export const symbol: unique symbol = Symbol.for("effect/PrimaryKey") + +/** + * @since 2.0.0 + * @category models + */ +export interface PrimaryKey { + [symbol](): string +} + +/** + * @since 2.0.0 + * @category accessors + */ +export const value = (self: PrimaryKey): string => self[symbol]() diff --git a/backend/node_modules/effect/src/PubSub.ts b/backend/node_modules/effect/src/PubSub.ts new file mode 100644 index 0000000000000000000000000000000000000000..53aab9c131cf1e27bd976ce2314d116a1ef2efd7 --- /dev/null +++ b/backend/node_modules/effect/src/PubSub.ts @@ -0,0 +1,210 @@ +/** + * @since 2.0.0 + */ +import type * as Effect from "./Effect.js" +import * as internal from "./internal/pubsub.js" +import type { Pipeable } from "./Pipeable.js" +import type * as Queue from "./Queue.js" +import type * as Scope from "./Scope.js" + +/** + * A `PubSub` is an asynchronous message hub into which publishers can publish + * messages of type `A` and subscribers can subscribe to take messages of type + * `A`. + * + * @since 2.0.0 + * @category models + */ +export interface PubSub extends Queue.Enqueue, Pipeable { + /** + * Publishes a message to the `PubSub`, returning whether the message was published + * to the `PubSub`. + */ + publish(value: A): Effect.Effect + + /** + * Publishes all of the specified messages to the `PubSub`, returning whether they + * were published to the `PubSub`. + */ + publishAll(elements: Iterable): Effect.Effect + + /** + * Subscribes to receive messages from the `PubSub`. The resulting subscription can + * be evaluated multiple times within the scope to take a message from the `PubSub` + * each time. + */ + readonly subscribe: Effect.Effect, never, Scope.Scope> +} + +/** + * Creates a bounded `PubSub` with the back pressure strategy. The `PubSub` will retain + * messages until they have been taken by all subscribers, applying back + * pressure to publishers if the `PubSub` is at capacity. + * + * For best performance use capacities that are powers of two. + * + * @since 2.0.0 + * @category constructors + */ +export const bounded: ( + capacity: number | { readonly capacity: number; readonly replay?: number | undefined } +) => Effect.Effect> = internal.bounded + +/** + * Creates a bounded `PubSub` with the dropping strategy. The `PubSub` will drop new + * messages if the `PubSub` is at capacity. + * + * For best performance use capacities that are powers of two. + * + * @since 2.0.0 + * @category constructors + */ +export const dropping: ( + capacity: number | { readonly capacity: number; readonly replay?: number | undefined } +) => Effect.Effect> = internal.dropping + +/** + * Creates a bounded `PubSub` with the sliding strategy. The `PubSub` will add new + * messages and drop old messages if the `PubSub` is at capacity. + * + * For best performance use capacities that are powers of two. + * + * @since 2.0.0 + * @category constructors + */ +export const sliding: ( + capacity: number | { readonly capacity: number; readonly replay?: number | undefined } +) => Effect.Effect> = internal.sliding + +/** + * Creates an unbounded `PubSub`. + * + * @since 2.0.0 + * @category constructors + */ +export const unbounded: (options?: { readonly replay?: number | undefined }) => Effect.Effect> = + internal.unbounded + +/** + * Returns the number of elements the queue can hold. + * + * @since 2.0.0 + * @category getters + */ +export const capacity: (self: PubSub) => number = internal.capacity + +/** + * Retrieves the size of the queue, which is equal to the number of elements + * in the queue. This may be negative if fibers are suspended waiting for + * elements to be added to the queue. + * + * @since 2.0.0 + * @category getters + */ +export const size: (self: PubSub) => Effect.Effect = internal.size + +/** + * Returns `true` if the `Queue` contains at least one element, `false` + * otherwise. + * + * @since 2.0.0 + * @category getters + */ +export const isFull: (self: PubSub) => Effect.Effect = internal.isFull + +/** + * Returns `true` if the `Queue` contains zero elements, `false` otherwise. + * + * @since 2.0.0 + * @category getters + */ +export const isEmpty: (self: PubSub) => Effect.Effect = internal.isEmpty + +/** + * Interrupts any fibers that are suspended on `offer` or `take`. Future calls + * to `offer*` and `take*` will be interrupted immediately. + * + * @since 2.0.0 + * @category utils + */ +export const shutdown: (self: PubSub) => Effect.Effect = internal.shutdown + +/** + * Returns `true` if `shutdown` has been called, otherwise returns `false`. + * + * @since 2.0.0 + * @category getters + */ +export const isShutdown: (self: PubSub) => Effect.Effect = internal.isShutdown + +/** + * Waits until the queue is shutdown. The `Effect` returned by this method will + * not resume until the queue has been shutdown. If the queue is already + * shutdown, the `Effect` will resume right away. + * + * @since 2.0.0 + * @category utils + */ +export const awaitShutdown: (self: PubSub) => Effect.Effect = internal.awaitShutdown + +/** + * Publishes a message to the `PubSub`, returning whether the message was published + * to the `PubSub`. + * + * @since 2.0.0 + * @category utils + */ +export const publish: { + /** + * Publishes a message to the `PubSub`, returning whether the message was published + * to the `PubSub`. + * + * @since 2.0.0 + * @category utils + */ + (value: A): (self: PubSub) => Effect.Effect + /** + * Publishes a message to the `PubSub`, returning whether the message was published + * to the `PubSub`. + * + * @since 2.0.0 + * @category utils + */ + (self: PubSub, value: A): Effect.Effect +} = internal.publish + +/** + * Publishes all of the specified messages to the `PubSub`, returning whether they + * were published to the `PubSub`. + * + * @since 2.0.0 + * @category utils + */ +export const publishAll: { + /** + * Publishes all of the specified messages to the `PubSub`, returning whether they + * were published to the `PubSub`. + * + * @since 2.0.0 + * @category utils + */ + (elements: Iterable): (self: PubSub) => Effect.Effect + /** + * Publishes all of the specified messages to the `PubSub`, returning whether they + * were published to the `PubSub`. + * + * @since 2.0.0 + * @category utils + */ + (self: PubSub, elements: Iterable): Effect.Effect +} = internal.publishAll + +/** + * Subscribes to receive messages from the `PubSub`. The resulting subscription can + * be evaluated multiple times within the scope to take a message from the `PubSub` + * each time. + * + * @since 2.0.0 + * @category utils + */ +export const subscribe: (self: PubSub) => Effect.Effect, never, Scope.Scope> = internal.subscribe diff --git a/backend/node_modules/effect/src/Random.ts b/backend/node_modules/effect/src/Random.ts new file mode 100644 index 0000000000000000000000000000000000000000..5ebdbb5ea8655079039dc823d1395f110a2422bc --- /dev/null +++ b/backend/node_modules/effect/src/Random.ts @@ -0,0 +1,204 @@ +/** + * @since 2.0.0 + */ +import type * as Array from "./Array.js" +import type * as Cause from "./Cause.js" +import type * as Chunk from "./Chunk.js" +import type * as Context from "./Context.js" +import type * as Effect from "./Effect.js" +import * as defaultServices from "./internal/defaultServices.js" +import * as internal from "./internal/random.js" +import type * as NonEmptyIterable from "./NonEmptyIterable.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const RandomTypeId: unique symbol = internal.RandomTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type RandomTypeId = typeof RandomTypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface Random { + readonly [RandomTypeId]: RandomTypeId + /** + * Returns the next numeric value from the pseudo-random number generator. + */ + readonly next: Effect.Effect + /** + * Returns the next boolean value from the pseudo-random number generator. + */ + readonly nextBoolean: Effect.Effect + /** + * Returns the next integer value from the pseudo-random number generator. + */ + readonly nextInt: Effect.Effect + /** + * Returns the next numeric value in the specified range from the + * pseudo-random number generator. + */ + nextRange(min: number, max: number): Effect.Effect + /** + * Returns the next integer value in the specified range from the + * pseudo-random number generator. + */ + nextIntBetween(min: number, max: number): Effect.Effect + /** + * Uses the pseudo-random number generator to shuffle the specified iterable. + */ + shuffle(elements: Iterable): Effect.Effect> +} + +/** + * Returns the next numeric value from the pseudo-random number generator. + * + * @since 2.0.0 + * @category constructors + */ +export const next: Effect.Effect = defaultServices.next + +/** + * Returns the next integer value from the pseudo-random number generator. + * + * @since 2.0.0 + * @category constructors + */ +export const nextInt: Effect.Effect = defaultServices.nextInt + +/** + * Returns the next boolean value from the pseudo-random number generator. + * + * @since 2.0.0 + * @category constructors + */ +export const nextBoolean: Effect.Effect = defaultServices.nextBoolean + +/** + * Returns the next numeric value in the specified range from the + * pseudo-random number generator. + * + * @since 2.0.0 + * @category constructors + */ +export const nextRange: (min: number, max: number) => Effect.Effect = defaultServices.nextRange + +/** + * Returns the next integer value in the specified range from the + * pseudo-random number generator. + * + * @since 2.0.0 + * @category constructors + */ +export const nextIntBetween: (min: number, max: number) => Effect.Effect = defaultServices.nextIntBetween + +/** + * Uses the pseudo-random number generator to shuffle the specified iterable. + * + * @since 2.0.0 + * @category constructors + */ +export const shuffle: (elements: Iterable) => Effect.Effect> = defaultServices.shuffle + +/** + * Get a random element from an iterable. + * + * @example + * ```ts + * import { Effect, Random } from "effect" + * + * Effect.gen(function* () { + * const randomItem = yield* Random.choice([1, 2, 3]) + * console.log(randomItem) + * }) + * ``` + * + * @since 3.6.0 + * @category constructors + */ +export const choice: >( + elements: Self +) => Self extends NonEmptyIterable.NonEmptyIterable ? Effect.Effect + : Self extends Array.NonEmptyReadonlyArray ? Effect.Effect + : Self extends Iterable ? Effect.Effect + : never = defaultServices.choice + +/** + * Retreives the `Random` service from the context and uses it to run the + * specified workflow. + * + * @since 2.0.0 + * @category constructors + */ +export const randomWith: (f: (random: Random) => Effect.Effect) => Effect.Effect = + defaultServices.randomWith + +/** + * @since 2.0.0 + * @category context + */ +export const Random: Context.Tag = internal.randomTag + +/** + * Constructs the `Random` service, seeding the pseudo-random number generator + * with an hash of the specified seed. + * This constructor is useful for generating predictable sequences of random values for specific use cases. + * + * Example uses: + * - Generating random UI data for visual tests. + * - Creating data that needs to change daily but remain the same throughout a single day, such as using a date as the seed. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Effect, Random } from "effect" + * + * const random1 = Random.make("myseed") + * const random2 = Random.make("myseed") + * + * assert.equal(Effect.runSync(random1.next), Effect.runSync(random2.next)) + * ``` + * + * @since 3.5.0 + * @category constructors + */ +export const make: (seed: A) => Random = internal.make + +/** + * Constructs the `Random` service from an array of literal values. + * The service will cycle through the provided values in order when generating random values. + * This constructor is useful for creating deterministic sequences for testing or when specific values need to be returned. + * + * @example + * ```ts + * import { Effect, Random } from "effect" + * + * Effect.gen(function* () { + * console.log(yield* Random.next) // 0.2 + * console.log(yield* Random.next) // 0.5 + * console.log(yield* Random.next) // 0.8 + * console.log(yield* Random.next) // 0.2 (cycles back) + * }).pipe(Effect.withRandom(Random.fixed([0.2, 0.5, 0.8]))) + * ``` + * + * @example + * ```ts + * import { Effect, Random } from "effect" + * + * Effect.gen(function* () { + * console.log(yield* Random.nextBoolean) // true + * console.log(yield* Random.nextBoolean) // false + * console.log(yield* Random.nextBoolean) // true + * }).pipe(Effect.withRandom(Random.fixed([true, false, true]))) + * ``` + * + * @since 3.11.0 + * @category constructors + */ +export const fixed: >(values: T) => Random = internal.fixed diff --git a/backend/node_modules/effect/src/Record.ts b/backend/node_modules/effect/src/Record.ts new file mode 100644 index 0000000000000000000000000000000000000000..246562a7efc19985cdcfab3252db2767dc302332 --- /dev/null +++ b/backend/node_modules/effect/src/Record.ts @@ -0,0 +1,1990 @@ +/** + * This module provides utility functions for working with records in TypeScript. + * + * @since 2.0.0 + */ + +import type { Either } from "./Either.js" +import * as E from "./Either.js" +import * as Equal from "./Equal.js" +import type { Equivalence } from "./Equivalence.js" +import { dual, identity } from "./Function.js" +import type { TypeLambda } from "./HKT.js" +import * as Option from "./Option.js" +import type { NoInfer } from "./Types.js" + +/** + * @category models + * @since 2.0.0 + */ +export type ReadonlyRecord = { + readonly [P in K]: A +} + +/** + * @since 2.0.0 + */ +export declare namespace ReadonlyRecord { + type IsFiniteString = T extends "" ? true : + [T] extends [`${infer Head}${infer Rest}`] + ? string extends Head ? false : `${number}` extends Head ? false : Rest extends "" ? true : IsFiniteString + : false + + /** + * @since 2.0.0 + */ + export type NonLiteralKey = K extends string ? IsFiniteString extends true ? string : K + : symbol + + /** + * @since 2.0.0 + */ + export type IntersectKeys = [string] extends [K1 | K2] ? + NonLiteralKey & NonLiteralKey + : K1 & K2 +} + +/** + * @category type lambdas + * @since 2.0.0 + */ +export interface ReadonlyRecordTypeLambda extends TypeLambda { + readonly type: ReadonlyRecord +} + +/** + * Creates a new, empty record. + * + * @category constructors + * @since 2.0.0 + */ +export const empty = (): Record< + ReadonlyRecord.NonLiteralKey, + V +> => ({} as any) + +/** + * Determine if a record is empty. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isEmptyRecord } from "effect/Record" + * + * assert.deepStrictEqual(isEmptyRecord({}), true); + * assert.deepStrictEqual(isEmptyRecord({ a: 3 }), false); + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isEmptyRecord = (self: Record): self is Record => + keys(self).length === 0 + +/** + * Determine if a record is empty. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { isEmptyReadonlyRecord } from "effect/Record" + * + * assert.deepStrictEqual(isEmptyReadonlyRecord({}), true); + * assert.deepStrictEqual(isEmptyReadonlyRecord({ a: 3 }), false); + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isEmptyReadonlyRecord: ( + self: ReadonlyRecord +) => self is ReadonlyRecord = isEmptyRecord + +/** + * Takes an iterable and a projection function and returns a record. + * The projection function maps each value of the iterable to a tuple of a key and a value, which is then added to the resulting record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { fromIterableWith } from "effect/Record" + * + * const input = [1, 2, 3, 4] + * + * assert.deepStrictEqual( + * fromIterableWith(input, a => [String(a), a * 2]), + * { '1': 2, '2': 4, '3': 6, '4': 8 } + * ) + * ``` + * + * @category constructors + * @since 2.0.0 + */ +export const fromIterableWith: { + /** + * Takes an iterable and a projection function and returns a record. + * The projection function maps each value of the iterable to a tuple of a key and a value, which is then added to the resulting record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { fromIterableWith } from "effect/Record" + * + * const input = [1, 2, 3, 4] + * + * assert.deepStrictEqual( + * fromIterableWith(input, a => [String(a), a * 2]), + * { '1': 2, '2': 4, '3': 6, '4': 8 } + * ) + * ``` + * + * @category constructors + * @since 2.0.0 + */ + (f: (a: A) => readonly [K, B]): (self: Iterable) => Record, B> + /** + * Takes an iterable and a projection function and returns a record. + * The projection function maps each value of the iterable to a tuple of a key and a value, which is then added to the resulting record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { fromIterableWith } from "effect/Record" + * + * const input = [1, 2, 3, 4] + * + * assert.deepStrictEqual( + * fromIterableWith(input, a => [String(a), a * 2]), + * { '1': 2, '2': 4, '3': 6, '4': 8 } + * ) + * ``` + * + * @category constructors + * @since 2.0.0 + */ + (self: Iterable, f: (a: A) => readonly [K, B]): Record, B> +} = dual( + 2, + ( + self: Iterable, + f: (a: A) => readonly [K, B] + ): Record, B> => { + const out: Record = empty() + for (const a of self) { + const [k, b] = f(a) + out[k] = b + } + return out + } +) + +/** + * Creates a new record from an iterable, utilizing the provided function to determine the key for each element. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { fromIterableBy } from "effect/Record" + * + * const users = [ + * { id: "2", name: "name2" }, + * { id: "1", name: "name1" } + * ] + * + * assert.deepStrictEqual( + * fromIterableBy(users, user => user.id), + * { + * "2": { id: "2", name: "name2" }, + * "1": { id: "1", name: "name1" } + * } + * ) + * ``` + * + * @category constructors + * @since 2.0.0 + */ +export const fromIterableBy = ( + items: Iterable, + f: (a: A) => K +): Record, A> => fromIterableWith(items, (a) => [f(a), a]) + +/** + * Builds a record from an iterable of key-value pairs. + * + * If there are conflicting keys when using `fromEntries`, the last occurrence of the key/value pair will overwrite the + * previous ones. So the resulting record will only have the value of the last occurrence of each key. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { fromEntries } from "effect/Record" + * + * const input: Array<[string, number]> = [["a", 1], ["b", 2]] + * + * assert.deepStrictEqual(fromEntries(input), { a: 1, b: 2 }) + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const fromEntries: ( + entries: Iterable +) => Record, Entry[1]> = Object.fromEntries + +/** + * Transforms the values of a record into an `Array` with a custom mapping function. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { collect } from "effect/Record" + * + * const x = { a: 1, b: 2, c: 3 } + * assert.deepStrictEqual(collect(x, (key, n) => [key, n]), [["a", 1], ["b", 2], ["c", 3]]) + * ``` + * + * @category conversions + * @since 2.0.0 + */ +export const collect: { + /** + * Transforms the values of a record into an `Array` with a custom mapping function. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { collect } from "effect/Record" + * + * const x = { a: 1, b: 2, c: 3 } + * assert.deepStrictEqual(collect(x, (key, n) => [key, n]), [["a", 1], ["b", 2], ["c", 3]]) + * ``` + * + * @category conversions + * @since 2.0.0 + */ + (f: (key: K, a: A) => B): (self: ReadonlyRecord) => Array + /** + * Transforms the values of a record into an `Array` with a custom mapping function. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { collect } from "effect/Record" + * + * const x = { a: 1, b: 2, c: 3 } + * assert.deepStrictEqual(collect(x, (key, n) => [key, n]), [["a", 1], ["b", 2], ["c", 3]]) + * ``` + * + * @category conversions + * @since 2.0.0 + */ + (self: ReadonlyRecord, f: (key: K, a: A) => B): Array +} = dual( + 2, + (self: ReadonlyRecord, f: (key: K, a: A) => B): Array => { + const out: Array = [] + for (const key of keys(self)) { + out.push(f(key, self[key])) + } + return out + } +) + +/** + * Takes a record and returns an array of tuples containing its keys and values. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { toEntries } from "effect/Record" + * + * const x = { a: 1, b: 2, c: 3 } + * assert.deepStrictEqual(toEntries(x), [["a", 1], ["b", 2], ["c", 3]]) + * ``` + * + * @category conversions + * @since 2.0.0 + */ +export const toEntries: (self: ReadonlyRecord) => Array<[K, A]> = collect(( + key, + value +) => [key, value]) + +/** + * Returns the number of key/value pairs in a record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { size } from "effect/Record"; + * + * assert.deepStrictEqual(size({ a: "a", b: 1, c: true }), 3); + * ``` + * + * @since 2.0.0 + */ +export const size = (self: ReadonlyRecord): number => keys(self).length + +/** + * Check if a given `key` exists in a record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { empty, has } from "effect/Record" + * + * assert.deepStrictEqual(has({ a: 1, b: 2 }, "a"), true); + * assert.deepStrictEqual(has(empty(), "c"), false); + * ``` + * + * @since 2.0.0 + */ +export const has: { + /** + * Check if a given `key` exists in a record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { empty, has } from "effect/Record" + * + * assert.deepStrictEqual(has({ a: 1, b: 2 }, "a"), true); + * assert.deepStrictEqual(has(empty(), "c"), false); + * ``` + * + * @since 2.0.0 + */ + (key: NoInfer): (self: ReadonlyRecord) => boolean + /** + * Check if a given `key` exists in a record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { empty, has } from "effect/Record" + * + * assert.deepStrictEqual(has({ a: 1, b: 2 }, "a"), true); + * assert.deepStrictEqual(has(empty(), "c"), false); + * ``` + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, key: NoInfer): boolean +} = dual( + 2, + ( + self: ReadonlyRecord, + key: NoInfer + ): boolean => Object.prototype.hasOwnProperty.call(self, key) +) + +/** + * Retrieve a value at a particular key from a record, returning it wrapped in an `Option`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record as R, Option } from "effect" + * + * const person: Record = { name: "John Doe", age: 35 } + * + * assert.deepStrictEqual(R.get(person, "name"), Option.some("John Doe")) + * assert.deepStrictEqual(R.get(person, "email"), Option.none()) + * ``` + * + * @since 2.0.0 + */ +export const get: { + /** + * Retrieve a value at a particular key from a record, returning it wrapped in an `Option`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record as R, Option } from "effect" + * + * const person: Record = { name: "John Doe", age: 35 } + * + * assert.deepStrictEqual(R.get(person, "name"), Option.some("John Doe")) + * assert.deepStrictEqual(R.get(person, "email"), Option.none()) + * ``` + * + * @since 2.0.0 + */ + (key: NoInfer): (self: ReadonlyRecord) => Option.Option + /** + * Retrieve a value at a particular key from a record, returning it wrapped in an `Option`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record as R, Option } from "effect" + * + * const person: Record = { name: "John Doe", age: 35 } + * + * assert.deepStrictEqual(R.get(person, "name"), Option.some("John Doe")) + * assert.deepStrictEqual(R.get(person, "email"), Option.none()) + * ``` + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, key: NoInfer): Option.Option +} = dual( + 2, + (self: ReadonlyRecord, key: NoInfer): Option.Option => + has(self, key) ? Option.some(self[key]) : Option.none() +) + +/** + * Apply a function to the element at the specified key, creating a new record. + * If the key does not exist, the record is returned unchanged. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record as R } from "effect" + * + * const f = (x: number) => x * 2 + * + * assert.deepStrictEqual( + * R.modify({ a: 3 }, 'a', f), + * { a: 6 } + * ) + * assert.deepStrictEqual( + * R.modify({ a: 3 } as Record, 'b', f), + * { a: 3 } + * ) + * ``` + * + * @since 2.0.0 + */ +export const modify: { + /** + * Apply a function to the element at the specified key, creating a new record. + * If the key does not exist, the record is returned unchanged. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record as R } from "effect" + * + * const f = (x: number) => x * 2 + * + * assert.deepStrictEqual( + * R.modify({ a: 3 }, 'a', f), + * { a: 6 } + * ) + * assert.deepStrictEqual( + * R.modify({ a: 3 } as Record, 'b', f), + * { a: 3 } + * ) + * ``` + * + * @since 2.0.0 + */ + (key: NoInfer, f: (a: A) => B): (self: ReadonlyRecord) => Record + /** + * Apply a function to the element at the specified key, creating a new record. + * If the key does not exist, the record is returned unchanged. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record as R } from "effect" + * + * const f = (x: number) => x * 2 + * + * assert.deepStrictEqual( + * R.modify({ a: 3 }, 'a', f), + * { a: 6 } + * ) + * assert.deepStrictEqual( + * R.modify({ a: 3 } as Record, 'b', f), + * { a: 3 } + * ) + * ``` + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, key: NoInfer, f: (a: A) => B): Record +} = dual( + 3, + (self: ReadonlyRecord, key: NoInfer, f: (a: A) => B): Record => { + if (!has(self, key)) { + return { ...self } + } + return { ...self, [key]: f(self[key]) } + } +) + +/** + * Apply a function to the element at the specified key, creating a new record, + * or return `None` if the key doesn't exist. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record as R, Option } from "effect" + * + * const f = (x: number) => x * 2 + * + * assert.deepStrictEqual( + * R.modifyOption({ a: 3 }, 'a', f), + * Option.some({ a: 6 }) + * ) + * assert.deepStrictEqual( + * R.modifyOption({ a: 3 } as Record, 'b', f), + * Option.none() + * ) + * ``` + * + * @since 2.0.0 + */ +export const modifyOption: { + /** + * Apply a function to the element at the specified key, creating a new record, + * or return `None` if the key doesn't exist. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record as R, Option } from "effect" + * + * const f = (x: number) => x * 2 + * + * assert.deepStrictEqual( + * R.modifyOption({ a: 3 }, 'a', f), + * Option.some({ a: 6 }) + * ) + * assert.deepStrictEqual( + * R.modifyOption({ a: 3 } as Record, 'b', f), + * Option.none() + * ) + * ``` + * + * @since 2.0.0 + */ + (key: NoInfer, f: (a: A) => B): (self: ReadonlyRecord) => Option.Option> + /** + * Apply a function to the element at the specified key, creating a new record, + * or return `None` if the key doesn't exist. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record as R, Option } from "effect" + * + * const f = (x: number) => x * 2 + * + * assert.deepStrictEqual( + * R.modifyOption({ a: 3 }, 'a', f), + * Option.some({ a: 6 }) + * ) + * assert.deepStrictEqual( + * R.modifyOption({ a: 3 } as Record, 'b', f), + * Option.none() + * ) + * ``` + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, key: NoInfer, f: (a: A) => B): Option.Option> +} = dual( + 3, + ( + self: ReadonlyRecord, + key: NoInfer, + f: (a: A) => B + ): Option.Option> => { + if (!has(self, key)) { + return Option.none() + } + return Option.some({ ...self, [key]: f(self[key]) }) + } +) + +/** + * Replaces a value in the record with the new value passed as parameter. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Option } from "effect" + * + * assert.deepStrictEqual( + * Record.replaceOption({ a: 1, b: 2, c: 3 }, 'a', 10), + * Option.some({ a: 10, b: 2, c: 3 }) + * ) + * assert.deepStrictEqual(Record.replaceOption(Record.empty(), 'a', 10), Option.none()) + * ``` + * + * @since 2.0.0 + */ +export const replaceOption: { + /** + * Replaces a value in the record with the new value passed as parameter. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Option } from "effect" + * + * assert.deepStrictEqual( + * Record.replaceOption({ a: 1, b: 2, c: 3 }, 'a', 10), + * Option.some({ a: 10, b: 2, c: 3 }) + * ) + * assert.deepStrictEqual(Record.replaceOption(Record.empty(), 'a', 10), Option.none()) + * ``` + * + * @since 2.0.0 + */ + (key: NoInfer, b: B): (self: ReadonlyRecord) => Option.Option> + /** + * Replaces a value in the record with the new value passed as parameter. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Option } from "effect" + * + * assert.deepStrictEqual( + * Record.replaceOption({ a: 1, b: 2, c: 3 }, 'a', 10), + * Option.some({ a: 10, b: 2, c: 3 }) + * ) + * assert.deepStrictEqual(Record.replaceOption(Record.empty(), 'a', 10), Option.none()) + * ``` + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, key: NoInfer, b: B): Option.Option> +} = dual( + 3, + ( + self: ReadonlyRecord, + key: NoInfer, + b: B + ): Option.Option> => modifyOption(self, key, () => b) +) + +/** + * If the given key exists in the record, returns a new record with the key removed, + * otherwise returns a copy of the original record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { remove } from "effect/Record" + * + * assert.deepStrictEqual(remove({ a: 1, b: 2 }, "a"), { b: 2 }) + * ``` + * + * @since 2.0.0 + */ +export const remove: { + /** + * If the given key exists in the record, returns a new record with the key removed, + * otherwise returns a copy of the original record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { remove } from "effect/Record" + * + * assert.deepStrictEqual(remove({ a: 1, b: 2 }, "a"), { b: 2 }) + * ``` + * + * @since 2.0.0 + */ + (key: X): (self: ReadonlyRecord) => Record, A> + /** + * If the given key exists in the record, returns a new record with the key removed, + * otherwise returns a copy of the original record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { remove } from "effect/Record" + * + * assert.deepStrictEqual(remove({ a: 1, b: 2 }, "a"), { b: 2 }) + * ``` + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, key: X): Record, A> +} = dual( + 2, + (self: ReadonlyRecord, key: X): Record, A> => { + if (!has(self, key)) { + return { ...self } + } + const out = { ...self } + delete out[key] + return out + } +) + +/** + * Retrieves the value of the property with the given `key` from a record and returns an `Option` + * of a tuple with the value and the record with the removed property. + * If the key is not present, returns `O.none`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record as R, Option } from "effect" + * + * assert.deepStrictEqual(R.pop({ a: 1, b: 2 }, "a"), Option.some([1, { b: 2 }])) + * assert.deepStrictEqual(R.pop({ a: 1, b: 2 } as Record, "c"), Option.none()) + * ``` + * + * @category record + * @since 2.0.0 + */ +export const pop: { + /** + * Retrieves the value of the property with the given `key` from a record and returns an `Option` + * of a tuple with the value and the record with the removed property. + * If the key is not present, returns `O.none`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record as R, Option } from "effect" + * + * assert.deepStrictEqual(R.pop({ a: 1, b: 2 }, "a"), Option.some([1, { b: 2 }])) + * assert.deepStrictEqual(R.pop({ a: 1, b: 2 } as Record, "c"), Option.none()) + * ``` + * + * @category record + * @since 2.0.0 + */ + (key: X): (self: ReadonlyRecord) => Option.Option<[A, Record, A>]> + /** + * Retrieves the value of the property with the given `key` from a record and returns an `Option` + * of a tuple with the value and the record with the removed property. + * If the key is not present, returns `O.none`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record as R, Option } from "effect" + * + * assert.deepStrictEqual(R.pop({ a: 1, b: 2 }, "a"), Option.some([1, { b: 2 }])) + * assert.deepStrictEqual(R.pop({ a: 1, b: 2 } as Record, "c"), Option.none()) + * ``` + * + * @category record + * @since 2.0.0 + */ + (self: ReadonlyRecord, key: X): Option.Option<[A, Record, A>]> +} = dual(2, ( + self: ReadonlyRecord, + key: X +): Option.Option<[A, Record, A>]> => + has(self, key) ? Option.some([self[key], remove(self, key)]) : Option.none()) + +/** + * Maps a record into another record by applying a transformation function to each of its values. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { map } from "effect/Record" + * + * const f = (n: number) => `-${n}` + * + * assert.deepStrictEqual(map({ a: 3, b: 5 }, f), { a: "-3", b: "-5" }) + * + * const g = (n: number, key: string) => `${key.toUpperCase()}-${n}` + * + * assert.deepStrictEqual(map({ a: 3, b: 5 }, g), { a: "A-3", b: "B-5" }) + * ``` + * + * @category mapping + * @since 2.0.0 + */ +export const map: { + /** + * Maps a record into another record by applying a transformation function to each of its values. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { map } from "effect/Record" + * + * const f = (n: number) => `-${n}` + * + * assert.deepStrictEqual(map({ a: 3, b: 5 }, f), { a: "-3", b: "-5" }) + * + * const g = (n: number, key: string) => `${key.toUpperCase()}-${n}` + * + * assert.deepStrictEqual(map({ a: 3, b: 5 }, g), { a: "A-3", b: "B-5" }) + * ``` + * + * @category mapping + * @since 2.0.0 + */ + (f: (a: A, key: NoInfer) => B): (self: ReadonlyRecord) => Record + /** + * Maps a record into another record by applying a transformation function to each of its values. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { map } from "effect/Record" + * + * const f = (n: number) => `-${n}` + * + * assert.deepStrictEqual(map({ a: 3, b: 5 }, f), { a: "-3", b: "-5" }) + * + * const g = (n: number, key: string) => `${key.toUpperCase()}-${n}` + * + * assert.deepStrictEqual(map({ a: 3, b: 5 }, g), { a: "A-3", b: "B-5" }) + * ``` + * + * @category mapping + * @since 2.0.0 + */ + (self: ReadonlyRecord, f: (a: A, key: NoInfer) => B): Record +} = dual( + 2, + (self: ReadonlyRecord, f: (a: A, key: NoInfer) => B): Record => { + const out: Record = { ...self } as any + for (const key of keys(self)) { + out[key] = f(self[key], key) + } + return out + } +) + +/** + * Maps the keys of a `ReadonlyRecord` while preserving the corresponding values. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { mapKeys } from "effect/Record" + * + * assert.deepStrictEqual(mapKeys({ a: 3, b: 5 }, (key) => key.toUpperCase()), { A: 3, B: 5 }) + * ``` + * + * @category mapping + * @since 2.0.0 + */ +export const mapKeys: { + /** + * Maps the keys of a `ReadonlyRecord` while preserving the corresponding values. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { mapKeys } from "effect/Record" + * + * assert.deepStrictEqual(mapKeys({ a: 3, b: 5 }, (key) => key.toUpperCase()), { A: 3, B: 5 }) + * ``` + * + * @category mapping + * @since 2.0.0 + */ + (f: (key: K, a: A) => K2): (self: ReadonlyRecord) => Record + /** + * Maps the keys of a `ReadonlyRecord` while preserving the corresponding values. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { mapKeys } from "effect/Record" + * + * assert.deepStrictEqual(mapKeys({ a: 3, b: 5 }, (key) => key.toUpperCase()), { A: 3, B: 5 }) + * ``` + * + * @category mapping + * @since 2.0.0 + */ + (self: ReadonlyRecord, f: (key: K, a: A) => K2): Record +} = dual( + 2, + ( + self: ReadonlyRecord, + f: (key: K, a: A) => K2 + ): Record => { + const out: Record = {} as any + for (const key of keys(self)) { + const a = self[key] + out[f(key, a)] = a + } + return out + } +) + +/** + * Maps entries of a `ReadonlyRecord` using the provided function, allowing modification of both keys and corresponding values. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { mapEntries } from "effect/Record" + * + * assert.deepStrictEqual(mapEntries({ a: 3, b: 5 }, (a, key) => [key.toUpperCase(), a + 1]), { A: 4, B: 6 }) + * ``` + * + * @category mapping + * @since 2.0.0 + */ +export const mapEntries: { + /** + * Maps entries of a `ReadonlyRecord` using the provided function, allowing modification of both keys and corresponding values. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { mapEntries } from "effect/Record" + * + * assert.deepStrictEqual(mapEntries({ a: 3, b: 5 }, (a, key) => [key.toUpperCase(), a + 1]), { A: 4, B: 6 }) + * ``` + * + * @category mapping + * @since 2.0.0 + */ + (f: (a: A, key: K) => readonly [K2, B]): (self: ReadonlyRecord) => Record + /** + * Maps entries of a `ReadonlyRecord` using the provided function, allowing modification of both keys and corresponding values. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { mapEntries } from "effect/Record" + * + * assert.deepStrictEqual(mapEntries({ a: 3, b: 5 }, (a, key) => [key.toUpperCase(), a + 1]), { A: 4, B: 6 }) + * ``` + * + * @category mapping + * @since 2.0.0 + */ + (self: ReadonlyRecord, f: (a: A, key: K) => [K2, B]): Record +} = dual( + 2, + ( + self: ReadonlyRecord, + f: (a: A, key: K) => [K2, B] + ): Record => { + const out = > {} + for (const key of keys(self)) { + const [k, b] = f(self[key], key) + out[k] = b + } + return out + } +) + +/** + * Transforms a record into a record by applying the function `f` to each key and value in the original record. + * If the function returns `Some`, the key-value pair is included in the output record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Option } from "effect" + * + * const x = { a: 1, b: 2, c: 3 } + * const f = (a: number, key: string) => a > 2 ? Option.some(a * 2) : Option.none() + * assert.deepStrictEqual(Record.filterMap(x, f), { c: 6 }) + * ``` + * + * @since 2.0.0 + */ +export const filterMap: { + /** + * Transforms a record into a record by applying the function `f` to each key and value in the original record. + * If the function returns `Some`, the key-value pair is included in the output record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Option } from "effect" + * + * const x = { a: 1, b: 2, c: 3 } + * const f = (a: number, key: string) => a > 2 ? Option.some(a * 2) : Option.none() + * assert.deepStrictEqual(Record.filterMap(x, f), { c: 6 }) + * ``` + * + * @since 2.0.0 + */ + (f: (a: A, key: K) => Option.Option): (self: ReadonlyRecord) => Record, B> + /** + * Transforms a record into a record by applying the function `f` to each key and value in the original record. + * If the function returns `Some`, the key-value pair is included in the output record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Option } from "effect" + * + * const x = { a: 1, b: 2, c: 3 } + * const f = (a: number, key: string) => a > 2 ? Option.some(a * 2) : Option.none() + * assert.deepStrictEqual(Record.filterMap(x, f), { c: 6 }) + * ``` + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, f: (a: A, key: K) => Option.Option): Record, B> +} = dual( + 2, + ( + self: ReadonlyRecord, + f: (a: A, key: K) => Option.Option + ): Record, B> => { + const out: Record = empty() + for (const key of keys(self)) { + const o = f(self[key], key) + if (Option.isSome(o)) { + out[key] = o.value + } + } + return out + } +) + +/** + * Selects properties from a record whose values match the given predicate. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { filter } from "effect/Record" + * + * const x = { a: 1, b: 2, c: 3, d: 4 } + * assert.deepStrictEqual(filter(x, (n) => n > 2), { c: 3, d: 4 }) + * ``` + * + * @category filtering + * @since 2.0.0 + */ +export const filter: { + /** + * Selects properties from a record whose values match the given predicate. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { filter } from "effect/Record" + * + * const x = { a: 1, b: 2, c: 3, d: 4 } + * assert.deepStrictEqual(filter(x, (n) => n > 2), { c: 3, d: 4 }) + * ``` + * + * @category filtering + * @since 2.0.0 + */ + (refinement: (a: NoInfer, key: K) => a is B): (self: ReadonlyRecord) => Record, B> + /** + * Selects properties from a record whose values match the given predicate. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { filter } from "effect/Record" + * + * const x = { a: 1, b: 2, c: 3, d: 4 } + * assert.deepStrictEqual(filter(x, (n) => n > 2), { c: 3, d: 4 }) + * ``` + * + * @category filtering + * @since 2.0.0 + */ + (predicate: (A: NoInfer, key: K) => boolean): (self: ReadonlyRecord) => Record, A> + /** + * Selects properties from a record whose values match the given predicate. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { filter } from "effect/Record" + * + * const x = { a: 1, b: 2, c: 3, d: 4 } + * assert.deepStrictEqual(filter(x, (n) => n > 2), { c: 3, d: 4 }) + * ``` + * + * @category filtering + * @since 2.0.0 + */ + (self: ReadonlyRecord, refinement: (a: A, key: K) => a is B): Record, B> + /** + * Selects properties from a record whose values match the given predicate. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { filter } from "effect/Record" + * + * const x = { a: 1, b: 2, c: 3, d: 4 } + * assert.deepStrictEqual(filter(x, (n) => n > 2), { c: 3, d: 4 }) + * ``` + * + * @category filtering + * @since 2.0.0 + */ + (self: ReadonlyRecord, predicate: (a: A, key: K) => boolean): Record, A> +} = dual( + 2, + ( + self: ReadonlyRecord, + predicate: (a: A, key: K) => boolean + ): Record, A> => { + const out: Record = empty() + for (const key of keys(self)) { + if (predicate(self[key], key)) { + out[key] = self[key] + } + } + return out + } +) + +/** + * Given a record with `Option` values, returns a new record containing only the `Some` values, preserving the original keys. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Option } from "effect" + * + * assert.deepStrictEqual( + * Record.getSomes({ a: Option.some(1), b: Option.none(), c: Option.some(2) }), + * { a: 1, c: 2 } + * ) + * ``` + * + * @category filtering + * @since 2.0.0 + */ +export const getSomes: ( + self: ReadonlyRecord> +) => Record, A> = filterMap( + identity +) + +/** + * Given a record with `Either` values, returns a new record containing only the `Left` values, preserving the original keys. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Either } from "effect" + * + * assert.deepStrictEqual( + * Record.getLefts({ a: Either.right(1), b: Either.left("err"), c: Either.right(2) }), + * { b: "err" } + * ) + * ``` + * + * @category filtering + * @since 2.0.0 + */ +export const getLefts = ( + self: ReadonlyRecord> +): Record, L> => { + const out: Record = empty() + for (const key of keys(self)) { + const value = self[key] + if (E.isLeft(value)) { + out[key] = value.left + } + } + + return out +} + +/** + * Given a record with `Either` values, returns a new record containing only the `Right` values, preserving the original keys. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Either } from "effect" + * + * assert.deepStrictEqual( + * Record.getRights({ a: Either.right(1), b: Either.left("err"), c: Either.right(2) }), + * { a: 1, c: 2 } + * ) + * ``` + * + * @category filtering + * @since 2.0.0 + */ +export const getRights = ( + self: ReadonlyRecord> +): Record => { + const out: Record = empty() + for (const key of keys(self)) { + const value = self[key] + if (E.isRight(value)) { + out[key] = value.right + } + } + + return out +} + +/** + * Partitions the elements of a record into two groups: those that match a predicate, and those that don't. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Either } from "effect" + * + * const x = { a: 1, b: 2, c: 3 } + * const f = (n: number) => (n % 2 === 0 ? Either.right(n) : Either.left(n)) + * assert.deepStrictEqual(Record.partitionMap(x, f), [{ a: 1, c: 3 }, { b: 2}]) + * ``` + * + * @category filtering + * @since 2.0.0 + */ +export const partitionMap: { + /** + * Partitions the elements of a record into two groups: those that match a predicate, and those that don't. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Either } from "effect" + * + * const x = { a: 1, b: 2, c: 3 } + * const f = (n: number) => (n % 2 === 0 ? Either.right(n) : Either.left(n)) + * assert.deepStrictEqual(Record.partitionMap(x, f), [{ a: 1, c: 3 }, { b: 2}]) + * ``` + * + * @category filtering + * @since 2.0.0 + */ + (f: (a: A, key: K) => Either): ( + self: ReadonlyRecord + ) => [left: Record, B>, right: Record, C>] + /** + * Partitions the elements of a record into two groups: those that match a predicate, and those that don't. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Either } from "effect" + * + * const x = { a: 1, b: 2, c: 3 } + * const f = (n: number) => (n % 2 === 0 ? Either.right(n) : Either.left(n)) + * assert.deepStrictEqual(Record.partitionMap(x, f), [{ a: 1, c: 3 }, { b: 2}]) + * ``` + * + * @category filtering + * @since 2.0.0 + */ + (self: ReadonlyRecord, f: (a: A, key: K) => Either): [left: Record, B>, right: Record, C>] +} = dual( + 2, + ( + self: ReadonlyRecord, + f: (a: A, key: K) => Either + ): [left: Record, B>, right: Record, C>] => { + const left: Record = empty() + const right: Record = empty() + for (const key of keys(self)) { + const e = f(self[key], key) + if (E.isLeft(e)) { + left[key] = e.left + } else { + right[key] = e.right + } + } + return [left, right] + } +) + +/** + * Partitions a record of `Either` values into two separate records, + * one with the `Left` values and one with the `Right` values. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record, Either } from "effect" + * + * assert.deepStrictEqual( + * Record.separate({ a: Either.left("e"), b: Either.right(1) }), + * [{ a: "e" }, { b: 1 }] + * ) + * ``` + * + * @category filtering + * @since 2.0.0 + */ +export const separate: ( + self: ReadonlyRecord> +) => [Record, A>, Record, B>] = partitionMap(identity) + +/** + * Partitions a record into two separate records based on the result of a predicate function. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { partition } from "effect/Record" + * + * assert.deepStrictEqual( + * partition({ a: 1, b: 3 }, (n) => n > 2), + * [{ a: 1 }, { b: 3 }] + * ) + * ``` + * + * @category filtering + * @since 2.0.0 + */ +export const partition: { + /** + * Partitions a record into two separate records based on the result of a predicate function. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { partition } from "effect/Record" + * + * assert.deepStrictEqual( + * partition({ a: 1, b: 3 }, (n) => n > 2), + * [{ a: 1 }, { b: 3 }] + * ) + * ``` + * + * @category filtering + * @since 2.0.0 + */ + (refinement: (a: NoInfer, key: K) => a is B): ( + self: ReadonlyRecord + ) => [ + excluded: Record, Exclude>, + satisfying: Record, B> + ] + /** + * Partitions a record into two separate records based on the result of a predicate function. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { partition } from "effect/Record" + * + * assert.deepStrictEqual( + * partition({ a: 1, b: 3 }, (n) => n > 2), + * [{ a: 1 }, { b: 3 }] + * ) + * ``` + * + * @category filtering + * @since 2.0.0 + */ + (predicate: (a: NoInfer, key: K) => boolean): ( + self: ReadonlyRecord + ) => [excluded: Record, A>, satisfying: Record, A>] + /** + * Partitions a record into two separate records based on the result of a predicate function. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { partition } from "effect/Record" + * + * assert.deepStrictEqual( + * partition({ a: 1, b: 3 }, (n) => n > 2), + * [{ a: 1 }, { b: 3 }] + * ) + * ``` + * + * @category filtering + * @since 2.0.0 + */ + (self: ReadonlyRecord, refinement: (a: A, key: K) => a is B): [ + excluded: Record, Exclude>, + satisfying: Record, B> + ] + /** + * Partitions a record into two separate records based on the result of a predicate function. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { partition } from "effect/Record" + * + * assert.deepStrictEqual( + * partition({ a: 1, b: 3 }, (n) => n > 2), + * [{ a: 1 }, { b: 3 }] + * ) + * ``` + * + * @category filtering + * @since 2.0.0 + */ + (self: ReadonlyRecord, predicate: (a: A, key: K) => boolean): [excluded: Record, A>, satisfying: Record, A>] +} = dual( + 2, + ( + self: ReadonlyRecord, + predicate: (a: A, key: K) => boolean + ): [excluded: Record, A>, satisfying: Record, A>] => { + const left: Record = empty() + const right: Record = empty() + for (const key of keys(self)) { + if (predicate(self[key], key)) { + right[key] = self[key] + } else { + left[key] = self[key] + } + } + return [left, right] + } +) + +/** + * Retrieve the keys of a given record as an array. + * + * @since 2.0.0 + */ +export const keys = (self: ReadonlyRecord): Array => + Object.keys(self) as Array + +/** + * Retrieve the values of a given record as an array. + * + * @since 2.0.0 + */ +export const values = (self: ReadonlyRecord): Array => collect(self, (_, a) => a) + +/** + * Add a new key-value pair or update an existing key's value in a record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { set } from "effect/Record" + * + * assert.deepStrictEqual(set("a", 5)({ a: 1, b: 2 }), { a: 5, b: 2 }); + * assert.deepStrictEqual(set("c", 5)({ a: 1, b: 2 }), { a: 1, b: 2, c: 5 }); + * ``` + * + * @since 2.0.0 + */ +export const set: { + /** + * Add a new key-value pair or update an existing key's value in a record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { set } from "effect/Record" + * + * assert.deepStrictEqual(set("a", 5)({ a: 1, b: 2 }), { a: 5, b: 2 }); + * assert.deepStrictEqual(set("c", 5)({ a: 1, b: 2 }), { a: 1, b: 2, c: 5 }); + * ``` + * + * @since 2.0.0 + */ + (key: K1, value: B): (self: ReadonlyRecord) => Record + /** + * Add a new key-value pair or update an existing key's value in a record. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { set } from "effect/Record" + * + * assert.deepStrictEqual(set("a", 5)({ a: 1, b: 2 }), { a: 5, b: 2 }); + * assert.deepStrictEqual(set("c", 5)({ a: 1, b: 2 }), { a: 1, b: 2, c: 5 }); + * ``` + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, key: K1, value: B): Record +} = dual( + 3, + ( + self: ReadonlyRecord, + key: K1, + value: B + ): Record => { + return { ...self, [key]: value } as any + } +) + +/** + * Replace a key's value in a record and return the updated record. + * If the key does not exist in the record, a copy of the original record is returned. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record } from "effect" + * + * assert.deepStrictEqual(Record.replace("a", 3)({ a: 1, b: 2 }), { a: 3, b: 2 }); + * assert.deepStrictEqual(Record.replace("c", 3)({ a: 1, b: 2 }), { a: 1, b: 2 }); + * ``` + * + * @since 2.0.0 + */ +export const replace: { + /** + * Replace a key's value in a record and return the updated record. + * If the key does not exist in the record, a copy of the original record is returned. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record } from "effect" + * + * assert.deepStrictEqual(Record.replace("a", 3)({ a: 1, b: 2 }), { a: 3, b: 2 }); + * assert.deepStrictEqual(Record.replace("c", 3)({ a: 1, b: 2 }), { a: 1, b: 2 }); + * ``` + * + * @since 2.0.0 + */ + (key: NoInfer, value: B): (self: ReadonlyRecord) => Record + /** + * Replace a key's value in a record and return the updated record. + * If the key does not exist in the record, a copy of the original record is returned. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Record } from "effect" + * + * assert.deepStrictEqual(Record.replace("a", 3)({ a: 1, b: 2 }), { a: 3, b: 2 }); + * assert.deepStrictEqual(Record.replace("c", 3)({ a: 1, b: 2 }), { a: 1, b: 2 }); + * ``` + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, key: NoInfer, value: B): Record +} = dual( + 3, + (self: ReadonlyRecord, key: NoInfer, value: B): Record => { + if (has(self, key)) { + return { ...self, [key]: value } + } + return { ...self } + } +) + +/** + * Check if all the keys and values in one record are also found in another record. + * + * @since 2.0.0 + */ +export const isSubrecordBy = (equivalence: Equivalence): { + (that: ReadonlyRecord): (self: ReadonlyRecord) => boolean + (self: ReadonlyRecord, that: ReadonlyRecord): boolean +} => + dual(2, (self: ReadonlyRecord, that: ReadonlyRecord): boolean => { + for (const key of keys(self)) { + if (!has(that, key) || !equivalence(self[key], that[key])) { + return false + } + } + return true + }) + +/** + * Check if one record is a subrecord of another, meaning it contains all the keys and values found in the second record. + * This comparison uses default equality checks (`Equal.equivalence()`). + * + * @since 2.0.0 + */ +export const isSubrecord: { + /** + * Check if one record is a subrecord of another, meaning it contains all the keys and values found in the second record. + * This comparison uses default equality checks (`Equal.equivalence()`). + * + * @since 2.0.0 + */ + (that: ReadonlyRecord): (self: ReadonlyRecord) => boolean + /** + * Check if one record is a subrecord of another, meaning it contains all the keys and values found in the second record. + * This comparison uses default equality checks (`Equal.equivalence()`). + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, that: ReadonlyRecord): boolean +} = isSubrecordBy(Equal.equivalence()) + +/** + * Reduce a record to a single value by combining its entries with a specified function. + * + * @category folding + * @since 2.0.0 + */ +export const reduce: { + /** + * Reduce a record to a single value by combining its entries with a specified function. + * + * @category folding + * @since 2.0.0 + */ + (zero: Z, f: (accumulator: Z, value: V, key: K) => Z): (self: ReadonlyRecord) => Z + /** + * Reduce a record to a single value by combining its entries with a specified function. + * + * @category folding + * @since 2.0.0 + */ + ( + self: ReadonlyRecord, + zero: Z, + f: (accumulator: Z, value: V, key: K) => Z + ): Z +} = dual( + 3, + ( + self: ReadonlyRecord, + zero: Z, + f: (accumulator: Z, value: V, key: K) => Z + ): Z => { + let out: Z = zero + for (const key of keys(self)) { + out = f(out, self[key], key) + } + return out + } +) + +/** + * Check if all entries in a record meet a specific condition. + * + * @since 2.0.0 + */ +export const every: { + /** + * Check if all entries in a record meet a specific condition. + * + * @since 2.0.0 + */ + (refinement: (value: A, key: K) => value is B): (self: ReadonlyRecord) => self is ReadonlyRecord + /** + * Check if all entries in a record meet a specific condition. + * + * @since 2.0.0 + */ + (predicate: (value: A, key: K) => boolean): (self: ReadonlyRecord) => boolean + /** + * Check if all entries in a record meet a specific condition. + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, refinement: (value: A, key: K) => value is B): self is ReadonlyRecord + /** + * Check if all entries in a record meet a specific condition. + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, predicate: (value: A, key: K) => boolean): boolean +} = dual( + 2, + ( + self: ReadonlyRecord, + refinement: (value: A, key: K) => value is B + ): self is ReadonlyRecord => { + for (const key of keys(self)) { + if (!refinement(self[key], key)) { + return false + } + } + return true + } +) + +/** + * Check if any entry in a record meets a specific condition. + * + * @since 2.0.0 + */ +export const some: { + /** + * Check if any entry in a record meets a specific condition. + * + * @since 2.0.0 + */ + (predicate: (value: A, key: K) => boolean): (self: ReadonlyRecord) => boolean + /** + * Check if any entry in a record meets a specific condition. + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, predicate: (value: A, key: K) => boolean): boolean +} = dual( + 2, + (self: ReadonlyRecord, predicate: (value: A, key: K) => boolean): boolean => { + for (const key of keys(self)) { + if (predicate(self[key], key)) { + return true + } + } + return false + } +) + +/** + * Merge two records, preserving entries that exist in either of the records. + * + * @since 2.0.0 + */ +export const union: { + /** + * Merge two records, preserving entries that exist in either of the records. + * + * @since 2.0.0 + */ + (that: ReadonlyRecord, combine: (selfValue: A, thatValue: B) => C): (self: ReadonlyRecord) => Record + /** + * Merge two records, preserving entries that exist in either of the records. + * + * @since 2.0.0 + */ + ( + self: ReadonlyRecord, + that: ReadonlyRecord, + combine: (selfValue: A, thatValue: B) => C + ): Record +} = dual( + 3, + ( + self: ReadonlyRecord, + that: ReadonlyRecord, + combine: (selfValue: A, thatValue: B) => C + ): Record => { + if (isEmptyRecord(self)) { + return { ...that } as any + } + if (isEmptyRecord(that)) { + return { ...self } as any + } + const out: Record = empty() + for (const key of keys(self)) { + if (has(that, key as any)) { + out[key] = combine(self[key], that[key as unknown as K1]) + } else { + out[key] = self[key] + } + } + for (const key of keys(that)) { + if (!has(out, key)) { + out[key] = that[key] + } + } + return out + } +) + +/** + * Merge two records, retaining only the entries that exist in both records. + * + * @since 2.0.0 + */ +export const intersection: { + /** + * Merge two records, retaining only the entries that exist in both records. + * + * @since 2.0.0 + */ + (that: ReadonlyRecord, combine: (selfValue: A, thatValue: B) => C): (self: ReadonlyRecord) => Record, C> + /** + * Merge two records, retaining only the entries that exist in both records. + * + * @since 2.0.0 + */ + ( + self: ReadonlyRecord, + that: ReadonlyRecord, + combine: (selfValue: A, thatValue: B) => C + ): Record, C> +} = dual( + 3, + ( + self: ReadonlyRecord, + that: ReadonlyRecord, + combine: (selfValue: A, thatValue: B) => C + ): Record, C> => { + const out: Record = empty() + if (isEmptyRecord(self) || isEmptyRecord(that)) { + return out + } + for (const key of keys(self)) { + if (has(that, key as any)) { + out[key] = combine(self[key], that[key as unknown as K1]) + } + } + return out + } +) + +/** + * Merge two records, preserving only the entries that are unique to each record. + * + * @since 2.0.0 + */ +export const difference: { + /** + * Merge two records, preserving only the entries that are unique to each record. + * + * @since 2.0.0 + */ + (that: ReadonlyRecord): (self: ReadonlyRecord) => Record + /** + * Merge two records, preserving only the entries that are unique to each record. + * + * @since 2.0.0 + */ + (self: ReadonlyRecord, that: ReadonlyRecord): Record +} = dual(2, ( + self: ReadonlyRecord, + that: ReadonlyRecord +): Record => { + if (isEmptyRecord(self)) { + return { ...that } as any + } + if (isEmptyRecord(that)) { + return { ...self } as any + } + const out = > {} + for (const key of keys(self)) { + if (!has(that, key as any)) { + out[key] = self[key] + } + } + for (const key of keys(that)) { + if (!has(self, key as any)) { + out[key] = that[key] + } + } + return out +}) + +/** + * Create an `Equivalence` for records using the provided `Equivalence` for values. + * + * @category instances + * @since 2.0.0 + */ +export const getEquivalence = ( + equivalence: Equivalence +): Equivalence> => { + const is = isSubrecordBy(equivalence) + return (self, that) => is(self, that) && is(that, self) +} + +/** + * Create a non-empty record from a single element. + * + * @category constructors + * @since 2.0.0 + */ +export const singleton = (key: K, value: A): Record => ({ + [key]: value +} as any) + +/** + * Returns the first entry that satisfies the specified + * predicate, or `None` if no such entry exists. + * + * @example + * ```ts + * import { Record, Option } from "effect" + * + * const record = { a: 1, b: 2, c: 3 } + * const result = Record.findFirst(record, (value, key) => value > 1 && key !== "b") + * console.log(result) // Option.Some(["c", 3]) + * ``` + * + * @category elements + * @since 3.14.0 + */ +export const findFirst: { + /** + * Returns the first entry that satisfies the specified + * predicate, or `None` if no such entry exists. + * + * @example + * ```ts + * import { Record, Option } from "effect" + * + * const record = { a: 1, b: 2, c: 3 } + * const result = Record.findFirst(record, (value, key) => value > 1 && key !== "b") + * console.log(result) // Option.Some(["c", 3]) + * ``` + * + * @category elements + * @since 3.14.0 + */ + (refinement: (value: NoInfer, key: NoInfer) => value is V2): (self: ReadonlyRecord) => Option.Option<[K, V2]> + /** + * Returns the first entry that satisfies the specified + * predicate, or `None` if no such entry exists. + * + * @example + * ```ts + * import { Record, Option } from "effect" + * + * const record = { a: 1, b: 2, c: 3 } + * const result = Record.findFirst(record, (value, key) => value > 1 && key !== "b") + * console.log(result) // Option.Some(["c", 3]) + * ``` + * + * @category elements + * @since 3.14.0 + */ + (predicate: (value: NoInfer, key: NoInfer) => boolean): (self: ReadonlyRecord) => Option.Option<[K, V]> + /** + * Returns the first entry that satisfies the specified + * predicate, or `None` if no such entry exists. + * + * @example + * ```ts + * import { Record, Option } from "effect" + * + * const record = { a: 1, b: 2, c: 3 } + * const result = Record.findFirst(record, (value, key) => value > 1 && key !== "b") + * console.log(result) // Option.Some(["c", 3]) + * ``` + * + * @category elements + * @since 3.14.0 + */ + ( + self: ReadonlyRecord, + refinement: (value: NoInfer, key: NoInfer) => value is V2 + ): Option.Option<[K, V2]> + /** + * Returns the first entry that satisfies the specified + * predicate, or `None` if no such entry exists. + * + * @example + * ```ts + * import { Record, Option } from "effect" + * + * const record = { a: 1, b: 2, c: 3 } + * const result = Record.findFirst(record, (value, key) => value > 1 && key !== "b") + * console.log(result) // Option.Some(["c", 3]) + * ``` + * + * @category elements + * @since 3.14.0 + */ + ( + self: ReadonlyRecord, + predicate: (value: NoInfer, key: NoInfer) => boolean + ): Option.Option<[K, V]> +} = dual( + 2, + (self: ReadonlyRecord, f: (value: V, key: K) => boolean) => { + const k = keys(self) + for (let i = 0; i < k.length; i++) { + const key = k[i] + if (f(self[key], key)) { + return Option.some([key, self[key]]) + } + } + return Option.none() + } +) diff --git a/backend/node_modules/effect/src/Ref.ts b/backend/node_modules/effect/src/Ref.ts new file mode 100644 index 0000000000000000000000000000000000000000..460c7399ddb1b56a7375f341454d2c3106b44682 --- /dev/null +++ b/backend/node_modules/effect/src/Ref.ts @@ -0,0 +1,268 @@ +/** + * @since 2.0.0 + */ +import type * as Effect from "./Effect.js" +import * as internal from "./internal/ref.js" +import type * as Option from "./Option.js" +import type * as Readable from "./Readable.js" +import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const RefTypeId: unique symbol = internal.RefTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type RefTypeId = typeof RefTypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface Ref extends Ref.Variance, Effect.Effect, Readable.Readable { + modify(f: (a: A) => readonly [B, A]): Effect.Effect + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: RefUnify + readonly [Unify.ignoreSymbol]?: RefUnifyIgnore +} + +/** + * @category models + * @since 3.8.0 + */ +export interface RefUnify extends Effect.EffectUnify { + Ref?: () => Extract> +} + +/** + * @category models + * @since 3.8.0 + */ +export interface RefUnifyIgnore extends Effect.EffectUnifyIgnore { + Effect?: true +} + +/** + * @since 2.0.0 + * @category models + */ +export declare namespace Ref { + /** + * @since 2.0.0 + */ + export interface Variance { + readonly [RefTypeId]: { + readonly _A: Types.Invariant + } + } +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const make: (value: A) => Effect.Effect> = internal.make + +/** + * @since 2.0.0 + * @category getters + */ +export const get: (self: Ref) => Effect.Effect = internal.get + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndSet: { + /** + * @since 2.0.0 + * @category utils + */ + (value: A): (self: Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref, value: A): Effect.Effect +} = internal.getAndSet + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndUpdate: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => A): (self: Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref, f: (a: A) => A): Effect.Effect +} = internal.getAndUpdate + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndUpdateSome: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option): (self: Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref, pf: (a: A) => Option.Option): Effect.Effect +} = internal.getAndUpdateSome + +/** + * @since 2.0.0 + * @category utils + */ +export const modify: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => readonly [B, A]): (self: Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref, f: (a: A) => readonly [B, A]): Effect.Effect +} = internal.modify + +/** + * @since 2.0.0 + * @category utils + */ +export const modifySome: { + /** + * @since 2.0.0 + * @category utils + */ + (fallback: B, pf: (a: A) => Option.Option): (self: Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref, fallback: B, pf: (a: A) => Option.Option): Effect.Effect +} = internal.modifySome + +/** + * @since 2.0.0 + * @category utils + */ +export const set: { + /** + * @since 2.0.0 + * @category utils + */ + (value: A): (self: Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref, value: A): Effect.Effect +} = internal.set + +/** + * @since 2.0.0 + * @category utils + */ +export const setAndGet: { + /** + * @since 2.0.0 + * @category utils + */ + (value: A): (self: Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref, value: A): Effect.Effect +} = internal.setAndGet + +/** + * @since 2.0.0 + * @category utils + */ +export const update: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => A): (self: Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref, f: (a: A) => A): Effect.Effect +} = internal.update + +/** + * @since 2.0.0 + * @category utils + */ +export const updateAndGet: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => A): (self: Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref, f: (a: A) => A): Effect.Effect +} = internal.updateAndGet + +/** + * @since 2.0.0 + * @category utils + */ +export const updateSome: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Option.Option): (self: Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref, f: (a: A) => Option.Option): Effect.Effect +} = internal.updateSome + +/** + * @since 2.0.0 + * @category utils + */ +export const updateSomeAndGet: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option): (self: Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref, pf: (a: A) => Option.Option): Effect.Effect +} = internal.updateSomeAndGet + +/** + * @since 2.0.0 + * @category unsafe + */ +export const unsafeMake: (value: A) => Ref = internal.unsafeMake diff --git a/backend/node_modules/effect/src/RegExp.ts b/backend/node_modules/effect/src/RegExp.ts new file mode 100644 index 0000000000000000000000000000000000000000..9ee5a584cea68ea91835e51d398b4515259cd1da --- /dev/null +++ b/backend/node_modules/effect/src/RegExp.ts @@ -0,0 +1,38 @@ +/** + * This module provides utility functions for working with RegExp in TypeScript. + * + * @since 2.0.0 + */ +import * as predicate from "./Predicate.js" + +/** + * Tests if a value is a `RegExp`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { RegExp } from "effect" + * + * assert.deepStrictEqual(RegExp.isRegExp(/a/), true) + * assert.deepStrictEqual(RegExp.isRegExp("a"), false) + * ``` + * + * @category guards + * @since 3.9.0 + */ +export const isRegExp: (input: unknown) => input is RegExp = predicate.isRegExp + +/** + * Escapes special characters in a regular expression pattern. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { RegExp } from "effect" + * + * assert.deepStrictEqual(RegExp.escape("a*b"), "a\\*b") + * ``` + * + * @since 2.0.0 + */ +export const escape = (string: string): string => string.replace(/[/\\^$*+?.()|[\]{}]/g, "\\$&") diff --git a/backend/node_modules/effect/src/Reloadable.ts b/backend/node_modules/effect/src/Reloadable.ts new file mode 100644 index 0000000000000000000000000000000000000000..f8ce5b41e130f15b565c92e04fa2e00eba377f5e --- /dev/null +++ b/backend/node_modules/effect/src/Reloadable.ts @@ -0,0 +1,127 @@ +/** + * @since 2.0.0 + */ +import type * as Context from "./Context.js" +import type * as Effect from "./Effect.js" +import * as internal from "./internal/reloadable.js" +import type * as Layer from "./Layer.js" +import type * as Schedule from "./Schedule.js" +import type * as ScopedRef from "./ScopedRef.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const ReloadableTypeId: unique symbol = internal.ReloadableTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type ReloadableTypeId = typeof ReloadableTypeId + +/** + * A `Reloadable` is an implementation of some service that can be dynamically + * reloaded, or swapped out for another implementation on-the-fly. + * + * @since 2.0.0 + * @category models + */ +export interface Reloadable extends Reloadable.Variance { + /** + * @internal + */ + readonly scopedRef: ScopedRef.ScopedRef + /** + * @internal + */ + readonly reload: Effect.Effect +} + +/** + * @since 2.0.0 + */ +export declare namespace Reloadable { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [ReloadableTypeId]: { + readonly _A: Types.Invariant + } + } +} + +/** + * Makes a new reloadable service from a layer that describes the construction + * of a static service. The service is automatically reloaded according to the + * provided schedule. + * + * @since 2.0.0 + * @category constructors + */ +export const auto: ( + tag: Context.Tag, + options: { readonly layer: Layer.Layer; readonly schedule: Schedule.Schedule } +) => Layer.Layer, E, R | In> = internal.auto + +/** + * Makes a new reloadable service from a layer that describes the construction + * of a static service. The service is automatically reloaded according to a + * schedule, which is extracted from the input to the layer. + * + * @since 2.0.0 + * @category constructors + */ +export const autoFromConfig: ( + tag: Context.Tag, + options: { + readonly layer: Layer.Layer + readonly scheduleFromConfig: (context: Context.Context) => Schedule.Schedule + } +) => Layer.Layer, E, R | In> = internal.autoFromConfig + +/** + * Retrieves the current version of the reloadable service. + * + * @since 2.0.0 + * @category getters + */ +export const get: (tag: Context.Tag) => Effect.Effect> = internal.get + +/** + * Makes a new reloadable service from a layer that describes the construction + * of a static service. + * + * @since 2.0.0 + * @category constructors + */ +export const manual: ( + tag: Context.Tag, + options: { readonly layer: Layer.Layer } +) => Layer.Layer, E, In> = internal.manual + +/** + * Reloads the specified service. + * + * @since 2.0.0 + * @category constructors + */ +export const reload: (tag: Context.Tag) => Effect.Effect> = internal.reload + +/** + * @since 2.0.0 + * @category context + */ +export const tag: (tag: Context.Tag) => Context.Tag, Reloadable> = internal.reloadableTag + +/** + * Forks the reload of the service in the background, ignoring any errors. + * + * @since 2.0.0 + * @category constructors + */ +export const reloadFork: (tag: Context.Tag) => Effect.Effect> = + internal.reloadFork diff --git a/backend/node_modules/effect/src/Request.ts b/backend/node_modules/effect/src/Request.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd0b3e8159129ecfd0646b8952dd38d2ebfb0787 --- /dev/null +++ b/backend/node_modules/effect/src/Request.ts @@ -0,0 +1,418 @@ +/** + * @since 2.0.0 + */ +import type * as _Cache from "./Cache.js" +import type { Cause } from "./Cause.js" +import type { Deferred } from "./Deferred.js" +import type { DurationInput } from "./Duration.js" +import type * as Effect from "./Effect.js" +import type * as Exit from "./Exit.js" +import type { FiberId } from "./FiberId.js" +import * as RequestBlock_ from "./internal/blockedRequests.js" +import * as cache from "./internal/cache.js" +import * as core from "./internal/core.js" +import * as fiberRuntime from "./internal/fiberRuntime.js" +import * as internal from "./internal/request.js" +import type * as Option from "./Option.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const RequestTypeId: unique symbol = internal.RequestTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type RequestTypeId = typeof RequestTypeId + +/** + * A `Request` is a request from a data source for a value of type `A` + * that may fail with an `E`. + * + * @since 2.0.0 + * @category models + */ +export interface Request extends Request.Variance {} + +/** + * @since 2.0.0 + */ +export declare namespace Request { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [RequestTypeId]: { + readonly _A: Types.Covariant + readonly _E: Types.Covariant + } + } + + /** + * @since 2.0.0 + * @category models + */ + export interface Constructor, T extends keyof R = never> { + (args: Omit, Request.Error>)>): R + } + + /** + * A utility type to extract the error type from a `Request`. + * + * @since 2.0.0 + * @category type-level + */ + export type Error> = [T] extends [Request] ? _E : never + + /** + * A utility type to extract the value type from a `Request`. + * + * @since 2.0.0 + * @category type-level + */ + export type Success> = [T] extends [Request] ? _A : never + + /** + * A utility type to extract the result type from a `Request`. + * + * @since 2.0.0 + * @category type-level + */ + export type Result> = T extends Request ? Exit.Exit : never + + /** + * A utility type to extract the optional result type from a `Request`. + * + * @since 2.0.0 + * @category type-level + */ + export type OptionalResult> = T extends Request + ? Exit.Exit, E> + : never +} + +/** + * Returns `true` if the specified value is a `Request`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isRequest: (u: unknown) => u is Request = internal.isRequest + +/** + * Constructs a new `Request`. + * + * @since 2.0.0 + * @category constructors + */ +export const of: >() => Request.Constructor = internal.of + +/** + * Constructs a new `Request`. + * + * @since 2.0.0 + * @category constructors + */ +export const tagged: & { _tag: string }>( + tag: R["_tag"] +) => Request.Constructor = internal.tagged + +/** + * Provides a constructor for a Request Class. + * + * @example + * ```ts + * import { Request } from "effect" + * + * type Success = string + * type Error = never + * + * class MyRequest extends Request.Class {} + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const Class: new>( + args: Types.Equals>, {}> extends true ? void + : { readonly [P in keyof A as P extends keyof Request ? never : P]: A[P] } +) => Request & Readonly = internal.Class as any + +/** + * Provides a Tagged constructor for a Request Class. + * + * @example + * ```ts + * import { Request } from "effect" + * + * type Success = string + * type Error = never + * + * class MyRequest extends Request.TaggedClass("MyRequest") {} + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const TaggedClass: ( + tag: Tag +) => new>( + args: Types.Equals>, {}> extends true ? void + : { readonly [P in keyof A as P extends "_tag" | keyof Request ? never : P]: A[P] } +) => Request & Readonly & { readonly _tag: Tag } = internal.TaggedClass as any + +/** + * Complete a `Request` with the specified result. + * + * @since 2.0.0 + * @category request completion + */ +export const complete: { + /** + * Complete a `Request` with the specified result. + * + * @since 2.0.0 + * @category request completion + */ + >(result: Request.Result): (self: A) => Effect.Effect + /** + * Complete a `Request` with the specified result. + * + * @since 2.0.0 + * @category request completion + */ + >(self: A, result: Request.Result): Effect.Effect +} = internal.complete + +/** + * Interrupts the child effect when requests are no longer needed + * + * @since 2.0.0 + * @category request completion + */ +export const interruptWhenPossible: { + /** + * Interrupts the child effect when requests are no longer needed + * + * @since 2.0.0 + * @category request completion + */ + (all: Iterable>): (self: Effect.Effect) => Effect.Effect + /** + * Interrupts the child effect when requests are no longer needed + * + * @since 2.0.0 + * @category request completion + */ + (self: Effect.Effect, all: Iterable>): Effect.Effect +} = fiberRuntime.interruptWhenPossible + +/** + * Complete a `Request` with the specified effectful computation, failing the + * request with the error from the effect workflow if it fails, and completing + * the request with the value of the effect workflow if it succeeds. + * + * @since 2.0.0 + * @category request completion + */ +export const completeEffect: { + /** + * Complete a `Request` with the specified effectful computation, failing the + * request with the error from the effect workflow if it fails, and completing + * the request with the value of the effect workflow if it succeeds. + * + * @since 2.0.0 + * @category request completion + */ + , R>(effect: Effect.Effect, Request.Error, R>): (self: A) => Effect.Effect + /** + * Complete a `Request` with the specified effectful computation, failing the + * request with the error from the effect workflow if it fails, and completing + * the request with the value of the effect workflow if it succeeds. + * + * @since 2.0.0 + * @category request completion + */ + , R>(self: A, effect: Effect.Effect, Request.Error, R>): Effect.Effect +} = internal.completeEffect + +/** + * Complete a `Request` with the specified error. + * + * @since 2.0.0 + * @category request completion + */ +export const fail: { + /** + * Complete a `Request` with the specified error. + * + * @since 2.0.0 + * @category request completion + */ + >(error: Request.Error): (self: A) => Effect.Effect + /** + * Complete a `Request` with the specified error. + * + * @since 2.0.0 + * @category request completion + */ + >(self: A, error: Request.Error): Effect.Effect +} = internal.fail + +/** + * Complete a `Request` with the specified cause. + * + * @since 2.0.0 + * @category request completion + */ +export const failCause: { + /** + * Complete a `Request` with the specified cause. + * + * @since 2.0.0 + * @category request completion + */ + >(cause: Cause>): (self: A) => Effect.Effect + /** + * Complete a `Request` with the specified cause. + * + * @since 2.0.0 + * @category request completion + */ + >(self: A, cause: Cause>): Effect.Effect +} = internal.failCause + +/** + * Complete a `Request` with the specified value. + * + * @since 2.0.0 + * @category request completion + */ +export const succeed: { + /** + * Complete a `Request` with the specified value. + * + * @since 2.0.0 + * @category request completion + */ + >(value: Request.Success): (self: A) => Effect.Effect + /** + * Complete a `Request` with the specified value. + * + * @since 2.0.0 + * @category request completion + */ + >(self: A, value: Request.Success): Effect.Effect +} = internal.succeed + +/** + * @category models + * @since 2.0.0 + */ +export interface Listeners { + readonly count: number + readonly observers: Set<(count: number) => void> + interrupted: boolean + addObserver(f: (count: number) => void): void + removeObserver(f: (count: number) => void): void + increment(): void + decrement(): void +} + +/** + * @category models + * @since 2.0.0 + */ +export interface Cache extends + _Cache.ConsumerCache, { + listeners: Listeners + handle: Deferred + }> +{} + +/** + * @since 2.0.0 + * @category models + */ +export const makeCache = ( + options: { + readonly capacity: number + readonly timeToLive: DurationInput + } +): Effect.Effect => + cache.make({ + ...options, + lookup: () => + core.map(core.deferredMake(), (handle) => ({ listeners: new internal.Listeners(), handle })) + }) + +/** + * @since 2.0.0 + * @category symbols + */ +export const EntryTypeId: unique symbol = Symbol.for("effect/RequestBlock.Entry") + +/** + * @since 2.0.0 + * @category symbols + */ +export type EntryTypeId = typeof EntryTypeId + +/** + * A `Entry` keeps track of a request of type `A` along with a + * `Ref` containing the result of the request, existentially hiding the result + * type. This is used internally by the library to support data sources that + * return different result types for different requests while guaranteeing that + * results will be of the type requested. + * + * @since 2.0.0 + * @category models + */ +export interface Entry extends Entry.Variance { + readonly request: R + readonly result: Deferred< + [R] extends [Request] ? _A : never, + [R] extends [Request] ? _E : never + > + readonly listeners: Listeners + readonly ownerId: FiberId + readonly state: { + completed: boolean + } +} + +/** + * @since 2.0.0 + * @category models + */ +export declare namespace Entry { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [EntryTypeId]: { + readonly _R: Types.Covariant + } + } +} + +/** + * @since 2.0.0 + * @category guards + */ +export const isEntry = RequestBlock_.isEntry + +/** + * @since 2.0.0 + * @category constructors + */ +export const makeEntry = RequestBlock_.makeEntry diff --git a/backend/node_modules/effect/src/RequestBlock.ts b/backend/node_modules/effect/src/RequestBlock.ts new file mode 100644 index 0000000000000000000000000000000000000000..907710046fd4a0acb8de8609aefcbd76dfa89649 --- /dev/null +++ b/backend/node_modules/effect/src/RequestBlock.ts @@ -0,0 +1,118 @@ +/** + * @since 2.0.0 + */ +import * as RequestBlock_ from "./internal/blockedRequests.js" +import type * as Request from "./Request.js" +import type * as RequestResolver from "./RequestResolver.js" + +/** + * `RequestBlock` captures a collection of blocked requests as a data + * structure. By doing this the library is able to preserve information about + * which requests must be performed sequentially and which can be performed in + * parallel, allowing for maximum possible batching and pipelining while + * preserving ordering guarantees. + * + * @since 2.0.0 + * @category models + */ +export type RequestBlock = Empty | Par | Seq | Single + +/** + * @since 2.0.0 + * @category models + */ +export declare namespace RequestBlock { + /** + * @since 2.0.0 + * @category models + */ + export interface Reducer { + emptyCase(): Z + parCase(left: Z, right: Z): Z + singleCase( + dataSource: RequestResolver.RequestResolver, + blockedRequest: Request.Entry + ): Z + seqCase(left: Z, right: Z): Z + } +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Empty { + readonly _tag: "Empty" +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Par { + readonly _tag: "Par" + readonly left: RequestBlock + readonly right: RequestBlock +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Seq { + readonly _tag: "Seq" + readonly left: RequestBlock + readonly right: RequestBlock +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Single { + readonly _tag: "Single" + readonly dataSource: RequestResolver.RequestResolver + readonly blockedRequest: Request.Entry +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const single: ( + dataSource: RequestResolver.RequestResolver, + blockedRequest: Request.Entry +) => RequestBlock = RequestBlock_.single + +/** + * @since 2.0.0 + * @category constructors + */ +export const empty: RequestBlock = RequestBlock_.empty + +/** + * @since 2.0.0 + * @category constructors + */ +export const mapRequestResolvers: ( + self: RequestBlock, + f: (dataSource: RequestResolver.RequestResolver) => RequestResolver.RequestResolver +) => RequestBlock = RequestBlock_.mapRequestResolvers + +/** + * @since 2.0.0 + * @category constructors + */ +export const parallel: (self: RequestBlock, that: RequestBlock) => RequestBlock = RequestBlock_.par + +/** + * @since 2.0.0 + * @category constructors + */ +export const reduce: (self: RequestBlock, reducer: RequestBlock.Reducer) => Z = RequestBlock_.reduce + +/** + * @since 2.0.0 + * @category constructors + */ +export const sequential: (self: RequestBlock, that: RequestBlock) => RequestBlock = RequestBlock_.seq diff --git a/backend/node_modules/effect/src/RequestResolver.ts b/backend/node_modules/effect/src/RequestResolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b82415dedd42f19d2a27385b6c6763229567149 --- /dev/null +++ b/backend/node_modules/effect/src/RequestResolver.ts @@ -0,0 +1,495 @@ +/** + * @since 2.0.0 + */ + +import type { NonEmptyArray } from "./Array.js" +import * as Context from "./Context.js" +import * as Effect from "./Effect.js" +import type * as Either from "./Either.js" +import type * as Equal from "./Equal.js" +import type { FiberRef } from "./FiberRef.js" +import * as core from "./internal/core.js" +import * as internal from "./internal/dataSource.js" +import type { Pipeable } from "./Pipeable.js" +import type * as Request from "./Request.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const RequestResolverTypeId: unique symbol = core.RequestResolverTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type RequestResolverTypeId = typeof RequestResolverTypeId + +/** + * The `RequestResolver` interface requires an environment `R` and handles + * the execution of requests of type `A`. + * + * Implementations must provide a `runAll` method, which processes a collection + * of requests and produces an effect that fulfills these requests. Requests are + * organized into a `Array>`, where the outer `Array` groups requests + * into batches that are executed sequentially, and each inner `Array` contains + * requests that can be executed in parallel. This structure allows + * implementations to analyze all incoming requests collectively and optimize + * query execution accordingly. + * + * Implementations are typically specialized for a subtype of `Request`. + * However, they are not strictly limited to these subtypes as long as they can + * map any given request type to `Request`. Implementations should inspect + * the collection of requests to identify the needed information and execute the + * corresponding queries. It is imperative that implementations resolve all the + * requests they receive. Failing to do so will lead to a `QueryFailure` error + * during query execution. + * + * @since 2.0.0 + * @category models + */ +export interface RequestResolver extends RequestResolver.Variance, Equal.Equal, Pipeable { + /** + * Execute a collection of requests. The outer `Array` represents batches + * of requests that must be performed sequentially. The inner `Array` + * represents a batch of requests that can be performed in parallel. + */ + runAll(requests: Array>>): Effect.Effect + + /** + * Identify the data source using the specific identifier + */ + identified(...identifiers: Array): RequestResolver +} + +/** + * @since 2.0.0 + */ +export declare namespace RequestResolver { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [RequestResolverTypeId]: { + readonly _A: Types.Contravariant + readonly _R: Types.Covariant + } + } +} + +/** + * @since 2.0.0 + * @category utils + */ +export const contextFromEffect = >(self: RequestResolver) => + Effect.contextWith((_: Context.Context) => provideContext(self, _)) + +/** + * @since 2.0.0 + * @category utils + */ +export const contextFromServices = + >>(...services: Services) => + >( + self: RequestResolver + ): Effect.Effect< + RequestResolver }[number]>>, + never, + { [k in keyof Services]: Effect.Effect.Context }[number] + > => Effect.contextWith((_) => provideContext(self as any, Context.pick(...services)(_ as any))) + +/** + * Returns `true` if the specified value is a `RequestResolver`, `false` otherwise. + * + * @since 2.0.0 + * @category refinements + */ +export const isRequestResolver: (u: unknown) => u is RequestResolver = core.isRequestResolver + +/** + * Constructs a data source with the specified identifier and method to run + * requests. + * + * @since 2.0.0 + * @category constructors + */ +export const make: ( + runAll: (requests: Array>) => Effect.Effect +) => RequestResolver = internal.make + +/** + * Constructs a data source with the specified identifier and method to run + * requests. + * + * @since 2.0.0 + * @category constructors + */ +export const makeWithEntry: ( + runAll: (requests: Array>>) => Effect.Effect +) => RequestResolver = internal.makeWithEntry + +/** + * Constructs a data source from a function taking a collection of requests. + * + * @since 2.0.0 + * @category constructors + */ +export const makeBatched: , R>( + run: (requests: NonEmptyArray) => Effect.Effect +) => RequestResolver = internal.makeBatched + +/** + * A data source aspect that executes requests between two effects, `before` + * and `after`, where the result of `before` can be used by `after`. + * + * @since 2.0.0 + * @category combinators + */ +export const around: { + /** + * A data source aspect that executes requests between two effects, `before` + * and `after`, where the result of `before` can be used by `after`. + * + * @since 2.0.0 + * @category combinators + */ + ( + before: Effect.Effect, + after: (a: A2) => Effect.Effect + ): (self: RequestResolver) => RequestResolver + /** + * A data source aspect that executes requests between two effects, `before` + * and `after`, where the result of `before` can be used by `after`. + * + * @since 2.0.0 + * @category combinators + */ + ( + self: RequestResolver, + before: Effect.Effect, + after: (a: A2) => Effect.Effect + ): RequestResolver +} = internal.around + +/** + * A data source aspect that executes requests between two effects, `before` + * and `after`, where the result of `before` can be used by `after`. + * + * The `before` and `after` effects are provided with the requests being executed. + * + * @since 2.0.0 + * @category combinators + * @example + * ```ts + * import { Effect, Request, RequestResolver } from "effect" + * + * interface GetUserById extends Request.Request { + * readonly id: number + * } + * + * const resolver = RequestResolver.fromFunction( + * (request: GetUserById) => ({ id: request.id, name: "John" }) + * ) + * + * RequestResolver.aroundRequests( + * resolver, + * (requests) => Effect.log(`got ${requests.length} requests`), + * (requests, _) => Effect.log(`finised running ${requests.length} requests`) + * ) + * ``` + */ +export const aroundRequests: { + /** + * A data source aspect that executes requests between two effects, `before` + * and `after`, where the result of `before` can be used by `after`. + * + * The `before` and `after` effects are provided with the requests being executed. + * + * @since 2.0.0 + * @category combinators + * @example + * ```ts + * import { Effect, Request, RequestResolver } from "effect" + * + * interface GetUserById extends Request.Request { + * readonly id: number + * } + * + * const resolver = RequestResolver.fromFunction( + * (request: GetUserById) => ({ id: request.id, name: "John" }) + * ) + * + * RequestResolver.aroundRequests( + * resolver, + * (requests) => Effect.log(`got ${requests.length} requests`), + * (requests, _) => Effect.log(`finised running ${requests.length} requests`) + * ) + * ``` + */ + ( + before: (requests: ReadonlyArray>) => Effect.Effect, + after: (requests: ReadonlyArray>, _: A2) => Effect.Effect + ): (self: RequestResolver) => RequestResolver + /** + * A data source aspect that executes requests between two effects, `before` + * and `after`, where the result of `before` can be used by `after`. + * + * The `before` and `after` effects are provided with the requests being executed. + * + * @since 2.0.0 + * @category combinators + * @example + * ```ts + * import { Effect, Request, RequestResolver } from "effect" + * + * interface GetUserById extends Request.Request { + * readonly id: number + * } + * + * const resolver = RequestResolver.fromFunction( + * (request: GetUserById) => ({ id: request.id, name: "John" }) + * ) + * + * RequestResolver.aroundRequests( + * resolver, + * (requests) => Effect.log(`got ${requests.length} requests`), + * (requests, _) => Effect.log(`finised running ${requests.length} requests`) + * ) + * ``` + */ + ( + self: RequestResolver, + before: (requests: ReadonlyArray>) => Effect.Effect, + after: (requests: ReadonlyArray>, _: A2) => Effect.Effect + ): RequestResolver +} = internal.aroundRequests + +/** + * Returns a data source that executes at most `n` requests in parallel. + * + * @since 2.0.0 + * @category combinators + */ +export const batchN: { + /** + * Returns a data source that executes at most `n` requests in parallel. + * + * @since 2.0.0 + * @category combinators + */ + (n: number): (self: RequestResolver) => RequestResolver + /** + * Returns a data source that executes at most `n` requests in parallel. + * + * @since 2.0.0 + * @category combinators + */ + (self: RequestResolver, n: number): RequestResolver +} = internal.batchN + +/** + * Provides this data source with part of its required context. + * + * @since 2.0.0 + * @category context + */ +export const mapInputContext: { + /** + * Provides this data source with part of its required context. + * + * @since 2.0.0 + * @category context + */ + (f: (context: Context.Context) => Context.Context): >(self: RequestResolver) => RequestResolver + /** + * Provides this data source with part of its required context. + * + * @since 2.0.0 + * @category context + */ + , R0>( + self: RequestResolver, + f: (context: Context.Context) => Context.Context + ): RequestResolver +} = internal.mapInputContext + +/** + * Returns a new data source that executes requests of type `C` using the + * specified function to transform `C` requests into requests that either this + * data source or that data source can execute. + * + * @since 2.0.0 + * @category combinators + */ +export const eitherWith: { + /** + * Returns a new data source that executes requests of type `C` using the + * specified function to transform `C` requests into requests that either this + * data source or that data source can execute. + * + * @since 2.0.0 + * @category combinators + */ + , R2, B extends Request.Request, C extends Request.Request>( + that: RequestResolver, + f: (_: Request.Entry) => Either.Either, Request.Entry> + ): (self: RequestResolver) => RequestResolver + /** + * Returns a new data source that executes requests of type `C` using the + * specified function to transform `C` requests into requests that either this + * data source or that data source can execute. + * + * @since 2.0.0 + * @category combinators + */ + < + R, + A extends Request.Request, + R2, + B extends Request.Request, + C extends Request.Request + >( + self: RequestResolver, + that: RequestResolver, + f: (_: Request.Entry) => Either.Either, Request.Entry> + ): RequestResolver +} = internal.eitherWith + +/** + * Constructs a data source from a pure function. + * + * @since 2.0.0 + * @category constructors + */ +export const fromFunction: >( + f: (request: A) => Request.Request.Success +) => RequestResolver = internal.fromFunction + +/** + * Constructs a data source from a pure function that takes a list of requests + * and returns a list of results of the same size. Each item in the result + * list must correspond to the item at the same index in the request list. + * + * @since 2.0.0 + * @category constructors + */ +export const fromFunctionBatched: >( + f: (chunk: NonEmptyArray) => Iterable> +) => RequestResolver = internal.fromFunctionBatched + +/** + * Constructs a data source from an effectual function. + * + * @since 2.0.0 + * @category constructors + */ +export const fromEffect: >( + f: (a: A) => Effect.Effect, Request.Request.Error, R> +) => RequestResolver = internal.fromEffect + +/** + * Constructs a data source from a list of tags paired to functions, that takes + * a list of requests and returns a list of results of the same size. Each item + * in the result list must correspond to the item at the same index in the + * request list. + * + * @since 2.0.0 + * @category constructors + */ +export const fromEffectTagged: & { readonly _tag: string }>() => < + Fns extends { + readonly [Tag in A["_tag"]]: [Extract] extends [infer Req] + ? Req extends Request.Request + ? (requests: Array) => Effect.Effect, ReqE, any> + : never + : never + } +>( + fns: Fns +) => RequestResolver extends Effect.Effect ? R : never> = + internal.fromEffectTagged + +/** + * A data source that never executes requests. + * + * @since 2.0.0 + * @category constructors + */ +export const never: RequestResolver = internal.never + +/** + * Provides this data source with its required context. + * + * @since 2.0.0 + * @category context + */ +export const provideContext: { + /** + * Provides this data source with its required context. + * + * @since 2.0.0 + * @category context + */ + (context: Context.Context): >(self: RequestResolver) => RequestResolver + /** + * Provides this data source with its required context. + * + * @since 2.0.0 + * @category context + */ + >(self: RequestResolver, context: Context.Context): RequestResolver +} = internal.provideContext + +/** + * Returns a new data source that executes requests by sending them to this + * data source and that data source, returning the results from the first data + * source to complete and safely interrupting the loser. + * + * @since 2.0.0 + * @category combinators + */ +export const race: { + /** + * Returns a new data source that executes requests by sending them to this + * data source and that data source, returning the results from the first data + * source to complete and safely interrupting the loser. + * + * @since 2.0.0 + * @category combinators + */ + , R2>(that: RequestResolver): , R>(self: RequestResolver) => RequestResolver + /** + * Returns a new data source that executes requests by sending them to this + * data source and that data source, returning the results from the first data + * source to complete and safely interrupting the loser. + * + * @since 2.0.0 + * @category combinators + */ + , R, A2 extends Request.Request, R2>(self: RequestResolver, that: RequestResolver): RequestResolver +} = internal.race + +/** + * Returns a new data source with a localized FiberRef + * + * @since 2.0.0 + * @category combinators + */ +export const locally: { + /** + * Returns a new data source with a localized FiberRef + * + * @since 2.0.0 + * @category combinators + */ + (self: FiberRef, value: A): >(use: RequestResolver) => RequestResolver + /** + * Returns a new data source with a localized FiberRef + * + * @since 2.0.0 + * @category combinators + */ + , A>(use: RequestResolver, self: FiberRef, value: A): RequestResolver +} = core.resolverLocally diff --git a/backend/node_modules/effect/src/Resource.ts b/backend/node_modules/effect/src/Resource.ts new file mode 100644 index 0000000000000000000000000000000000000000..1f9713e770a679bbb39afa71caa88d3ab1784865 --- /dev/null +++ b/backend/node_modules/effect/src/Resource.ts @@ -0,0 +1,119 @@ +/** + * @since 2.0.0 + */ +import type * as Effect from "./Effect.js" +import type * as Exit from "./Exit.js" +import * as internal from "./internal/resource.js" +import type * as Schedule from "./Schedule.js" +import type * as Scope from "./Scope.js" +import type * as ScopedRef from "./ScopedRef.js" +import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const ResourceTypeId: unique symbol = internal.ResourceTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type ResourceTypeId = typeof ResourceTypeId + +/** + * A `Resource` is a possibly resourceful value that is loaded into memory, and + * which can be refreshed either manually or automatically. + * + * @since 2.0.0 + * @category models + */ +export interface Resource extends Effect.Effect, Resource.Variance { + /** @internal */ + readonly scopedRef: ScopedRef.ScopedRef> + /** @internal */ + readonly acquire: Effect.Effect + + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: ResourceUnify + readonly [Unify.ignoreSymbol]?: ResourceUnifyIgnore +} + +/** + * @category models + * @since 3.9.0 + */ +export interface ResourceUnify extends Effect.EffectUnify { + Resource?: () => Extract> +} + +/** + * @category models + * @since 3.9.0 + */ +export interface ResourceUnifyIgnore extends Effect.EffectUnifyIgnore { + Effect?: true +} + +/** + * @since 2.0.0 + */ +export declare namespace Resource { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [ResourceTypeId]: { + _A: Types.Invariant + _E: Types.Invariant + } + } +} + +/** + * Creates a new `Resource` value that is automatically refreshed according to + * the specified policy. Note that error retrying is not performed + * automatically, so if you want to retry on errors, you should first apply + * retry policies to the acquisition effect before passing it to this + * constructor. + * + * @since 2.0.0 + * @category constructors + */ +export const auto: ( + acquire: Effect.Effect, + policy: Schedule.Schedule +) => Effect.Effect, never, R | R2 | Scope.Scope> = internal.auto + +/** + * Retrieves the current value stored in the cache. + * + * @since 2.0.0 + * @category getters + */ +export const get: (self: Resource) => Effect.Effect = internal.get + +/** + * Creates a new `Resource` value that must be manually refreshed by calling + * the refresh method. Note that error retrying is not performed + * automatically, so if you want to retry on errors, you should first apply + * retry policies to the acquisition effect before passing it to this + * constructor. + * + * @since 2.0.0 + * @category constructors + */ +export const manual: ( + acquire: Effect.Effect +) => Effect.Effect, never, Scope.Scope | R> = internal.manual + +/** + * Refreshes the cache. This method will not return until either the refresh + * is successful, or the refresh operation fails. + * + * @since 2.0.0 + * @category utils + */ +export const refresh: (self: Resource) => Effect.Effect = internal.refresh diff --git a/backend/node_modules/effect/src/Runtime.ts b/backend/node_modules/effect/src/Runtime.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a728896d260d7d34baa2552ce8b23008f313b44 --- /dev/null +++ b/backend/node_modules/effect/src/Runtime.ts @@ -0,0 +1,631 @@ +/** + * @since 2.0.0 + */ +import type { Cause } from "./Cause.js" +import type * as Context from "./Context.js" +import type * as Effect from "./Effect.js" +import type * as Exit from "./Exit.js" +import type * as Fiber from "./Fiber.js" +import type * as FiberId from "./FiberId.js" +import type * as FiberRef from "./FiberRef.js" +import type * as FiberRefs from "./FiberRefs.js" +import type { Inspectable } from "./Inspectable.js" +import * as internal from "./internal/runtime.js" +import type { Pipeable } from "./Pipeable.js" +import type * as RuntimeFlags from "./RuntimeFlags.js" +import type { Scheduler } from "./Scheduler.js" +import type { Scope } from "./Scope.js" + +/** + * @since 2.0.0 + * @category models + */ +export interface AsyncFiberException { + readonly _tag: "AsyncFiberException" + readonly fiber: Fiber.RuntimeFiber +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Cancel { + (fiberId?: FiberId.FiberId, options?: RunCallbackOptions | undefined): void +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Runtime extends Pipeable { + /** + * The context used as initial for forks + */ + readonly context: Context.Context + /** + * The runtime flags used as initial for forks + */ + readonly runtimeFlags: RuntimeFlags.RuntimeFlags + /** + * The fiber references used as initial for forks + */ + readonly fiberRefs: FiberRefs.FiberRefs +} + +/** + * @since 3.12.0 + */ +export declare namespace Runtime { + /** + * @since 3.12.0 + * @category Type Extractors + */ + export type Context> = [T] extends [Runtime] ? R : never +} + +/** + * @since 2.0.0 + * @category models + */ +export interface RunForkOptions { + readonly scheduler?: Scheduler | undefined + readonly updateRefs?: ((refs: FiberRefs.FiberRefs, fiberId: FiberId.Runtime) => FiberRefs.FiberRefs) | undefined + readonly immediate?: boolean + readonly scope?: Scope +} + +/** + * Executes the effect using the provided Scheduler or using the global + * Scheduler if not provided + * + * @since 2.0.0 + * @category execution + */ +export const runFork: { + /** + * Executes the effect using the provided Scheduler or using the global + * Scheduler if not provided + * + * @since 2.0.0 + * @category execution + */ + (runtime: Runtime): (effect: Effect.Effect, options?: RunForkOptions | undefined) => Fiber.RuntimeFiber + /** + * Executes the effect using the provided Scheduler or using the global + * Scheduler if not provided + * + * @since 2.0.0 + * @category execution + */ + ( + runtime: Runtime, + effect: Effect.Effect, + options?: RunForkOptions | undefined + ): Fiber.RuntimeFiber +} = internal.unsafeFork + +/** + * Executes the effect synchronously returning the exit. + * + * This method is effectful and should only be invoked at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ +export const runSyncExit: { + /** + * Executes the effect synchronously returning the exit. + * + * This method is effectful and should only be invoked at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ + (runtime: Runtime, effect: Effect.Effect): Exit.Exit + /** + * Executes the effect synchronously returning the exit. + * + * This method is effectful and should only be invoked at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ + (runtime: Runtime): (effect: Effect.Effect) => Exit.Exit +} = internal.unsafeRunSyncExit + +/** + * Executes the effect synchronously throwing in case of errors or async boundaries. + * + * This method is effectful and should only be invoked at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ +export const runSync: { + /** + * Executes the effect synchronously throwing in case of errors or async boundaries. + * + * This method is effectful and should only be invoked at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ + (runtime: Runtime, effect: Effect.Effect): A + /** + * Executes the effect synchronously throwing in case of errors or async boundaries. + * + * This method is effectful and should only be invoked at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ + (runtime: Runtime): (effect: Effect.Effect) => A +} = internal.unsafeRunSync + +/** + * @since 2.0.0 + * @category models + */ +export interface RunCallbackOptions extends RunForkOptions { + readonly onExit?: ((exit: Exit.Exit) => void) | undefined +} + +/** + * Executes the effect asynchronously, eventually passing the exit value to + * the specified callback. + * + * This method is effectful and should only be invoked at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ +export const runCallback: { + /** + * Executes the effect asynchronously, eventually passing the exit value to + * the specified callback. + * + * This method is effectful and should only be invoked at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ + (runtime: Runtime): ( + effect: Effect.Effect, + options?: RunCallbackOptions | undefined + ) => (fiberId?: FiberId.FiberId, options?: RunCallbackOptions | undefined) => void + /** + * Executes the effect asynchronously, eventually passing the exit value to + * the specified callback. + * + * This method is effectful and should only be invoked at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ + ( + runtime: Runtime, + effect: Effect.Effect, + options?: RunCallbackOptions | undefined + ): (fiberId?: FiberId.FiberId, options?: RunCallbackOptions | undefined) => void +} = internal.unsafeRunCallback + +/** + * Runs the `Effect`, returning a JavaScript `Promise` that will be resolved + * with the value of the effect once the effect has been executed, or will be + * rejected with the first error or exception throw by the effect. + * + * This method is effectful and should only be used at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ +export const runPromise: { + /** + * Runs the `Effect`, returning a JavaScript `Promise` that will be resolved + * with the value of the effect once the effect has been executed, or will be + * rejected with the first error or exception throw by the effect. + * + * This method is effectful and should only be used at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ + (runtime: Runtime): (effect: Effect.Effect, options?: { readonly signal?: AbortSignal } | undefined) => Promise + /** + * Runs the `Effect`, returning a JavaScript `Promise` that will be resolved + * with the value of the effect once the effect has been executed, or will be + * rejected with the first error or exception throw by the effect. + * + * This method is effectful and should only be used at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ + ( + runtime: Runtime, + effect: Effect.Effect, + options?: { readonly signal?: AbortSignal } | undefined + ): Promise +} = internal.unsafeRunPromise + +/** + * Runs the `Effect`, returning a JavaScript `Promise` that will be resolved + * with the `Exit` state of the effect once the effect has been executed. + * + * This method is effectful and should only be used at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ +export const runPromiseExit: { + /** + * Runs the `Effect`, returning a JavaScript `Promise` that will be resolved + * with the `Exit` state of the effect once the effect has been executed. + * + * This method is effectful and should only be used at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ + (runtime: Runtime): ( + effect: Effect.Effect, + options?: { readonly signal?: AbortSignal } | undefined + ) => Promise> + /** + * Runs the `Effect`, returning a JavaScript `Promise` that will be resolved + * with the `Exit` state of the effect once the effect has been executed. + * + * This method is effectful and should only be used at the edges of your + * program. + * + * @since 2.0.0 + * @category execution + */ + ( + runtime: Runtime, + effect: Effect.Effect, + options?: { readonly signal?: AbortSignal } | undefined + ): Promise> +} = internal.unsafeRunPromiseExit + +/** + * @since 2.0.0 + * @category constructors + */ +export const defaultRuntime: Runtime = internal.defaultRuntime + +/** + * @since 2.0.0 + * @category constructors + */ +export const defaultRuntimeFlags: RuntimeFlags.RuntimeFlags = internal.defaultRuntimeFlags + +/** + * @since 2.0.0 + * @category constructors + */ +export const make: ( + options: { + readonly context: Context.Context + readonly runtimeFlags: RuntimeFlags.RuntimeFlags + readonly fiberRefs: FiberRefs.FiberRefs + } +) => Runtime = internal.make + +/** + * @since 2.0.0 + * @category symbols + */ +export const FiberFailureId = Symbol.for("effect/Runtime/FiberFailure") +/** + * @since 2.0.0 + * @category symbols + */ +export type FiberFailureId = typeof FiberFailureId + +/** + * @since 2.0.0 + * @category symbols + */ +export const FiberFailureCauseId: unique symbol = internal.FiberFailureCauseId + +/** + * @since 2.0.0 + * @category exports + */ +export type FiberFailureCauseId = typeof FiberFailureCauseId + +/** + * @since 2.0.0 + * @category models + */ +export interface FiberFailure extends Error, Inspectable { + readonly [FiberFailureId]: FiberFailureId + readonly [FiberFailureCauseId]: Cause +} + +/** + * @since 2.0.0 + * @category guards + */ +export const isAsyncFiberException: (u: unknown) => u is AsyncFiberException = + internal.isAsyncFiberException + +/** + * @since 2.0.0 + * @category guards + */ +export const isFiberFailure: (u: unknown) => u is FiberFailure = internal.isFiberFailure + +/** + * @since 2.0.0 + * @category constructors + */ +export const makeFiberFailure: (cause: Cause) => FiberFailure = internal.fiberFailure + +/** + * @since 2.0.0 + * @category runtime flags + */ +export const updateRuntimeFlags: { + /** + * @since 2.0.0 + * @category runtime flags + */ + (f: (flags: RuntimeFlags.RuntimeFlags) => RuntimeFlags.RuntimeFlags): (self: Runtime) => Runtime + /** + * @since 2.0.0 + * @category runtime flags + */ + ( + self: Runtime, + f: (flags: RuntimeFlags.RuntimeFlags) => RuntimeFlags.RuntimeFlags + ): Runtime +} = internal.updateRuntimeFlags + +/** + * @since 2.0.0 + * @category runtime flags + */ +export const enableRuntimeFlag: { + /** + * @since 2.0.0 + * @category runtime flags + */ + (flag: RuntimeFlags.RuntimeFlag): (self: Runtime) => Runtime + /** + * @since 2.0.0 + * @category runtime flags + */ + (self: Runtime, flag: RuntimeFlags.RuntimeFlag): Runtime +} = internal.enableRuntimeFlag + +/** + * @since 2.0.0 + * @category runtime flags + */ +export const disableRuntimeFlag: { + /** + * @since 2.0.0 + * @category runtime flags + */ + (flag: RuntimeFlags.RuntimeFlag): (self: Runtime) => Runtime + /** + * @since 2.0.0 + * @category runtime flags + */ + (self: Runtime, flag: RuntimeFlags.RuntimeFlag): Runtime +} = internal.disableRuntimeFlag + +/** + * @since 2.0.0 + * @category context + */ +export const updateContext: { + /** + * @since 2.0.0 + * @category context + */ + (f: (context: Context.Context) => Context.Context): (self: Runtime) => Runtime + /** + * @since 2.0.0 + * @category context + */ + (self: Runtime, f: (context: Context.Context) => Context.Context): Runtime +} = internal.updateContext + +/** + * @since 2.0.0 + * @category context + * @example + * ```ts + * import { Context, Runtime } from "effect" + * + * class Name extends Context.Tag("Name")() {} + * + * const runtime: Runtime.Runtime = Runtime.defaultRuntime.pipe( + * Runtime.provideService(Name, "John") + * ) + * ``` + */ +export const provideService: { + /** + * @since 2.0.0 + * @category context + * @example + * ```ts + * import { Context, Runtime } from "effect" + * + * class Name extends Context.Tag("Name")() {} + * + * const runtime: Runtime.Runtime = Runtime.defaultRuntime.pipe( + * Runtime.provideService(Name, "John") + * ) + * ``` + */ + (tag: Context.Tag, service: S): (self: Runtime) => Runtime + /** + * @since 2.0.0 + * @category context + * @example + * ```ts + * import { Context, Runtime } from "effect" + * + * class Name extends Context.Tag("Name")() {} + * + * const runtime: Runtime.Runtime = Runtime.defaultRuntime.pipe( + * Runtime.provideService(Name, "John") + * ) + * ``` + */ + (self: Runtime, tag: Context.Tag, service: S): Runtime +} = internal.provideService + +/** + * @since 2.0.0 + * @category fiber refs + */ +export const updateFiberRefs: { + /** + * @since 2.0.0 + * @category fiber refs + */ + (f: (fiberRefs: FiberRefs.FiberRefs) => FiberRefs.FiberRefs): (self: Runtime) => Runtime + /** + * @since 2.0.0 + * @category fiber refs + */ + ( + self: Runtime, + f: (fiberRefs: FiberRefs.FiberRefs) => FiberRefs.FiberRefs + ): Runtime +} = internal.updateFiberRefs + +/** + * @since 2.0.0 + * @category fiber refs + * @example + * ```ts + * import { Effect, FiberRef, Runtime } from "effect" + * + * const ref = FiberRef.unsafeMake(0) + * + * const updatedRuntime = Runtime.defaultRuntime.pipe( + * Runtime.setFiberRef(ref, 1) + * ) + * + * // returns 1 + * const result = Runtime.runSync(updatedRuntime)(FiberRef.get(ref)) + * ``` + */ +export const setFiberRef: { + /** + * @since 2.0.0 + * @category fiber refs + * @example + * ```ts + * import { Effect, FiberRef, Runtime } from "effect" + * + * const ref = FiberRef.unsafeMake(0) + * + * const updatedRuntime = Runtime.defaultRuntime.pipe( + * Runtime.setFiberRef(ref, 1) + * ) + * + * // returns 1 + * const result = Runtime.runSync(updatedRuntime)(FiberRef.get(ref)) + * ``` + */ + (fiberRef: FiberRef.FiberRef, value: A): (self: Runtime) => Runtime + /** + * @since 2.0.0 + * @category fiber refs + * @example + * ```ts + * import { Effect, FiberRef, Runtime } from "effect" + * + * const ref = FiberRef.unsafeMake(0) + * + * const updatedRuntime = Runtime.defaultRuntime.pipe( + * Runtime.setFiberRef(ref, 1) + * ) + * + * // returns 1 + * const result = Runtime.runSync(updatedRuntime)(FiberRef.get(ref)) + * ``` + */ + (self: Runtime, fiberRef: FiberRef.FiberRef, value: A): Runtime +} = internal.setFiberRef + +/** + * @since 2.0.0 + * @category fiber refs + * @example + * ```ts + * import { Effect, FiberRef, Runtime } from "effect" + * + * const ref = FiberRef.unsafeMake(0) + * + * const updatedRuntime = Runtime.defaultRuntime.pipe( + * Runtime.setFiberRef(ref, 1), + * Runtime.deleteFiberRef(ref) + * ) + * + * // returns 0 + * const result = Runtime.runSync(updatedRuntime)(FiberRef.get(ref)) + * ``` + */ +export const deleteFiberRef: { + /** + * @since 2.0.0 + * @category fiber refs + * @example + * ```ts + * import { Effect, FiberRef, Runtime } from "effect" + * + * const ref = FiberRef.unsafeMake(0) + * + * const updatedRuntime = Runtime.defaultRuntime.pipe( + * Runtime.setFiberRef(ref, 1), + * Runtime.deleteFiberRef(ref) + * ) + * + * // returns 0 + * const result = Runtime.runSync(updatedRuntime)(FiberRef.get(ref)) + * ``` + */ + (fiberRef: FiberRef.FiberRef): (self: Runtime) => Runtime + /** + * @since 2.0.0 + * @category fiber refs + * @example + * ```ts + * import { Effect, FiberRef, Runtime } from "effect" + * + * const ref = FiberRef.unsafeMake(0) + * + * const updatedRuntime = Runtime.defaultRuntime.pipe( + * Runtime.setFiberRef(ref, 1), + * Runtime.deleteFiberRef(ref) + * ) + * + * // returns 0 + * const result = Runtime.runSync(updatedRuntime)(FiberRef.get(ref)) + * ``` + */ + (self: Runtime, fiberRef: FiberRef.FiberRef): Runtime +} = internal.deleteFiberRef diff --git a/backend/node_modules/effect/src/RuntimeFlags.ts b/backend/node_modules/effect/src/RuntimeFlags.ts new file mode 100644 index 0000000000000000000000000000000000000000..071efc29181166357a72401e12e0163c95282d10 --- /dev/null +++ b/backend/node_modules/effect/src/RuntimeFlags.ts @@ -0,0 +1,436 @@ +/** + * @since 2.0.0 + */ + +import type * as Differ from "./Differ.js" +import * as circular from "./internal/layer/circular.js" +import * as internal from "./internal/runtimeFlags.js" +import type * as Layer from "./Layer.js" +import type * as RuntimeFlagsPatch from "./RuntimeFlagsPatch.js" + +/** + * Represents a set of `RuntimeFlag`s. `RuntimeFlag`s affect the operation of + * the Effect runtime system. They are exposed to application-level code because + * they affect the behavior and performance of application code. + * + * @since 2.0.0 + * @category models + */ +export type RuntimeFlags = number & { + readonly RuntimeFlags: unique symbol +} + +/** + * Represents a flag that can be set to enable or disable a particular feature + * of the Effect runtime. + * + * @since 2.0.0 + * @category models + */ +export type RuntimeFlag = number & { + readonly RuntimeFlag: unique symbol +} + +/** + * No runtime flags. + * + * @since 2.0.0 + * @category constructors + */ +export const None: RuntimeFlag = internal.None + +/** + * The interruption flag determines whether or not the Effect runtime system will + * interrupt a fiber. + * + * @since 2.0.0 + * @category constructors + */ +export const Interruption: RuntimeFlag = internal.Interruption + +/** + * The op supervision flag determines whether or not the Effect runtime system + * will supervise all operations of the Effect runtime. Use of this flag will + * negatively impact performance, but is required for some operations, such as + * profiling. + * + * @since 2.0.0 + * @category constructors + */ +export const OpSupervision: RuntimeFlag = internal.OpSupervision + +/** + * The runtime metrics flag determines whether or not the Effect runtime system + * will collect metrics about the Effect runtime. Use of this flag will have a + * very small negative impact on performance, but generates very helpful + * operational insight into running Effect applications that can be exported to + * Prometheus or other tools via Effect Metrics. + * + * @since 2.0.0 + * @category constructors + */ +export const RuntimeMetrics: RuntimeFlag = internal.RuntimeMetrics + +/** + * The wind down flag determines whether the Effect runtime system will execute + * effects in wind-down mode. In wind-down mode, even if interruption is + * enabled and a fiber has been interrupted, the fiber will continue its + * execution uninterrupted. + * + * @since 2.0.0 + * @category constructors + */ +export const WindDown: RuntimeFlag = internal.WindDown + +/** + * The cooperative yielding flag determines whether the Effect runtime will + * yield to another fiber. + * + * @since 2.0.0 + * @category constructors + */ +export const CooperativeYielding: RuntimeFlag = internal.CooperativeYielding + +/** + * Returns `true` if the `CooperativeYielding` `RuntimeFlag` is enabled, `false` + * otherwise. + * + * @since 2.0.0 + * @category getters + */ +export const cooperativeYielding: (self: RuntimeFlags) => boolean = internal.cooperativeYielding + +/** + * Creates a `RuntimeFlagsPatch` which describes the difference between `self` + * and `that`. + * + * @since 2.0.0 + * @category diffing + */ +export const diff: { + /** + * Creates a `RuntimeFlagsPatch` which describes the difference between `self` + * and `that`. + * + * @since 2.0.0 + * @category diffing + */ + (that: RuntimeFlags): (self: RuntimeFlags) => RuntimeFlagsPatch.RuntimeFlagsPatch + /** + * Creates a `RuntimeFlagsPatch` which describes the difference between `self` + * and `that`. + * + * @since 2.0.0 + * @category diffing + */ + (self: RuntimeFlags, that: RuntimeFlags): RuntimeFlagsPatch.RuntimeFlagsPatch +} = internal.diff + +/** + * Constructs a differ that knows how to diff `RuntimeFlags` values. + * + * @since 2.0.0 + * @category utils + */ +export const differ: Differ.Differ = internal.differ + +/** + * Disables the specified `RuntimeFlag`. + * + * @since 2.0.0 + * @category utils + */ +export const disable: { + /** + * Disables the specified `RuntimeFlag`. + * + * @since 2.0.0 + * @category utils + */ + (flag: RuntimeFlag): (self: RuntimeFlags) => RuntimeFlags + /** + * Disables the specified `RuntimeFlag`. + * + * @since 2.0.0 + * @category utils + */ + (self: RuntimeFlags, flag: RuntimeFlag): RuntimeFlags +} = internal.disable + +/** + * Disables all of the `RuntimeFlag`s in the specified set of `RuntimeFlags`. + * + * @since 2.0.0 + * @category utils + */ +export const disableAll: { + /** + * Disables all of the `RuntimeFlag`s in the specified set of `RuntimeFlags`. + * + * @since 2.0.0 + * @category utils + */ + (flags: RuntimeFlags): (self: RuntimeFlags) => RuntimeFlags + /** + * Disables all of the `RuntimeFlag`s in the specified set of `RuntimeFlags`. + * + * @since 2.0.0 + * @category utils + */ + (self: RuntimeFlags, flags: RuntimeFlags): RuntimeFlags +} = internal.disableAll + +/** + * @since 2.0.0 + * @category context + */ +export const disableCooperativeYielding: Layer.Layer = circular.disableCooperativeYielding + +/** + * @since 2.0.0 + * @category context + */ +export const disableInterruption: Layer.Layer = circular.disableInterruption + +/** + * @since 2.0.0 + * @category context + */ +export const disableOpSupervision: Layer.Layer = circular.disableOpSupervision + +/** + * @since 2.0.0 + * @category context + */ +export const disableRuntimeMetrics: Layer.Layer = circular.disableRuntimeMetrics + +/** + * @since 2.0.0 + * @category context + */ +export const disableWindDown: Layer.Layer = circular.disableWindDown + +/** + * Enables the specified `RuntimeFlag`. + * + * @since 2.0.0 + * @category utils + */ +export const enable: { + /** + * Enables the specified `RuntimeFlag`. + * + * @since 2.0.0 + * @category utils + */ + (flag: RuntimeFlag): (self: RuntimeFlags) => RuntimeFlags + /** + * Enables the specified `RuntimeFlag`. + * + * @since 2.0.0 + * @category utils + */ + (self: RuntimeFlags, flag: RuntimeFlag): RuntimeFlags +} = internal.enable + +/** + * Enables all of the `RuntimeFlag`s in the specified set of `RuntimeFlags`. + * + * @since 2.0.0 + * @category utils + */ +export const enableAll: { + /** + * Enables all of the `RuntimeFlag`s in the specified set of `RuntimeFlags`. + * + * @since 2.0.0 + * @category utils + */ + (flags: RuntimeFlags): (self: RuntimeFlags) => RuntimeFlags + /** + * Enables all of the `RuntimeFlag`s in the specified set of `RuntimeFlags`. + * + * @since 2.0.0 + * @category utils + */ + (self: RuntimeFlags, flags: RuntimeFlags): RuntimeFlags +} = internal.enableAll + +/** + * @since 2.0.0 + * @category context + */ +export const enableCooperativeYielding: Layer.Layer = circular.enableCooperativeYielding + +/** + * @since 2.0.0 + * @category context + */ +export const enableInterruption: Layer.Layer = circular.enableInterruption + +/** + * @since 2.0.0 + * @category context + */ +export const enableOpSupervision: Layer.Layer = circular.enableOpSupervision + +/** + * @since 2.0.0 + * @category context + */ +export const enableRuntimeMetrics: Layer.Layer = circular.enableRuntimeMetrics + +/** + * @since 2.0.0 + * @category context + */ +export const enableWindDown: Layer.Layer = circular.enableWindDown + +/** + * Returns true only if the `Interruption` flag is **enabled** and the + * `WindDown` flag is **disabled**. + * + * A fiber is said to be interruptible if interruption is enabled and the fiber + * is not in its wind-down phase, in which it takes care of cleanup activities + * related to fiber shutdown. + * + * @since 2.0.0 + * @category getters + */ +export const interruptible: (self: RuntimeFlags) => boolean = internal.interruptible + +/** + * Returns `true` if the `Interruption` `RuntimeFlag` is enabled, `false` + * otherwise. + * + * @since 2.0.0 + * @category getters + */ +export const interruption: (self: RuntimeFlags) => boolean = internal.interruption + +/** + * Returns `true` if the specified `RuntimeFlag` is enabled, `false` otherwise. + * + * @since 2.0.0 + * @category elements + */ +export const isEnabled: { + /** + * Returns `true` if the specified `RuntimeFlag` is enabled, `false` otherwise. + * + * @since 2.0.0 + * @category elements + */ + (flag: RuntimeFlag): (self: RuntimeFlags) => boolean + /** + * Returns `true` if the specified `RuntimeFlag` is enabled, `false` otherwise. + * + * @since 2.0.0 + * @category elements + */ + (self: RuntimeFlags, flag: RuntimeFlag): boolean +} = internal.isEnabled + +/** + * Returns `true` if the specified `RuntimeFlag` is disabled, `false` otherwise. + * + * @since 2.0.0 + * @category elements + */ +export const isDisabled: { + /** + * Returns `true` if the specified `RuntimeFlag` is disabled, `false` otherwise. + * + * @since 2.0.0 + * @category elements + */ + (flag: RuntimeFlag): (self: RuntimeFlags) => boolean + /** + * Returns `true` if the specified `RuntimeFlag` is disabled, `false` otherwise. + * + * @since 2.0.0 + * @category elements + */ + (self: RuntimeFlags, flag: RuntimeFlag): boolean +} = internal.isDisabled + +/** + * @since 2.0.0 + * @category constructors + */ +export const make: (...flags: ReadonlyArray) => RuntimeFlags = internal.make + +/** + * @since 2.0.0 + * @category constructors + */ +export const none: RuntimeFlags = internal.none + +/** + * Returns `true` if the `OpSupervision` `RuntimeFlag` is enabled, `false` + * otherwise. + * + * @since 2.0.0 + * @category getters + */ +export const opSupervision: (self: RuntimeFlags) => boolean = internal.opSupervision + +/** + * Patches a set of `RuntimeFlag`s with a `RuntimeFlagsPatch`, returning the + * patched set of `RuntimeFlag`s. + * + * @since 2.0.0 + * @category utils + */ +export const patch: { + /** + * Patches a set of `RuntimeFlag`s with a `RuntimeFlagsPatch`, returning the + * patched set of `RuntimeFlag`s. + * + * @since 2.0.0 + * @category utils + */ + (patch: RuntimeFlagsPatch.RuntimeFlagsPatch): (self: RuntimeFlags) => RuntimeFlags + /** + * Patches a set of `RuntimeFlag`s with a `RuntimeFlagsPatch`, returning the + * patched set of `RuntimeFlag`s. + * + * @since 2.0.0 + * @category utils + */ + (self: RuntimeFlags, patch: RuntimeFlagsPatch.RuntimeFlagsPatch): RuntimeFlags +} = internal.patch + +/** + * Converts the provided `RuntimeFlags` into a `string`. + * + * @category conversions + * @since 2.0.0 + */ +export const render: (self: RuntimeFlags) => string = internal.render + +/** + * Returns `true` if the `RuntimeMetrics` `RuntimeFlag` is enabled, `false` + * otherwise. + * + * @since 2.0.0 + * @category getters + */ +export const runtimeMetrics: (self: RuntimeFlags) => boolean = internal.runtimeMetrics + +/** + * Converts the provided `RuntimeFlags` into a `ReadonlySet`. + * + * @category conversions + * @since 2.0.0 + */ +export const toSet: (self: RuntimeFlags) => ReadonlySet = internal.toSet + +/** + * Returns `true` if the `WindDown` `RuntimeFlag` is enabled, `false` + * otherwise. + * + * @since 2.0.0 + * @category getters + */ +export const windDown: (self: RuntimeFlags) => boolean = internal.windDown diff --git a/backend/node_modules/effect/src/ScheduleDecision.ts b/backend/node_modules/effect/src/ScheduleDecision.ts new file mode 100644 index 0000000000000000000000000000000000000000..edfae432e185018ed4f6ae4ffd1eda477480103d --- /dev/null +++ b/backend/node_modules/effect/src/ScheduleDecision.ts @@ -0,0 +1,62 @@ +/** + * @since 2.0.0 + */ +import * as internal from "./internal/schedule/decision.js" +import type * as Interval from "./ScheduleInterval.js" +import type * as Intervals from "./ScheduleIntervals.js" + +/** + * @since 2.0.0 + * @category models + */ +export type ScheduleDecision = Continue | Done + +/** + * @since 2.0.0 + * @category models + */ +export interface Continue { + readonly _tag: "Continue" + readonly intervals: Intervals.Intervals +} + +/** + * @since 2.0.0 + * @category models + */ +export interface Done { + readonly _tag: "Done" +} + +const _continue = internal._continue +export { + /** + * @since 2.0.0 + * @category constructors + */ + _continue as continue +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const continueWith: (interval: Interval.Interval) => ScheduleDecision = internal.continueWith + +/** + * @since 2.0.0 + * @category constructors + */ +export const done: ScheduleDecision = internal.done + +/** + * @since 2.0.0 + * @category refinements + */ +export const isContinue: (self: ScheduleDecision) => self is Continue = internal.isContinue + +/** + * @since 2.0.0 + * @category refinements + */ +export const isDone: (self: ScheduleDecision) => self is Done = internal.isDone diff --git a/backend/node_modules/effect/src/ScheduleInterval.ts b/backend/node_modules/effect/src/ScheduleInterval.ts new file mode 100644 index 0000000000000000000000000000000000000000..08ef290633a186dfdcc4329a4168d423f9335140 --- /dev/null +++ b/backend/node_modules/effect/src/ScheduleInterval.ts @@ -0,0 +1,219 @@ +/** + * @since 2.0.0 + */ +import type * as Duration from "./Duration.js" +import * as internal from "./internal/schedule/interval.js" +import type * as Option from "./Option.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const IntervalTypeId: unique symbol = internal.IntervalTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type IntervalTypeId = typeof IntervalTypeId + +/** + * An `Interval` represents an interval of time. Intervals can encompass all + * time, or no time at all. + * + * @since 2.0.0 + * @category models + */ +export interface Interval { + readonly [IntervalTypeId]: IntervalTypeId + readonly startMillis: number + readonly endMillis: number +} + +/** + * Constructs a new interval from the two specified endpoints. If the start + * endpoint greater than the end endpoint, then a zero size interval will be + * returned. + * + * @since 2.0.0 + * @category constructors + */ +export const make: (startMillis: number, endMillis: number) => Interval = internal.make + +/** + * An `Interval` of zero-width. + * + * @since 2.0.0 + * @category constructors + */ +export const empty: Interval = internal.empty + +/** + * Returns `true` if this `Interval` is less than `that` interval, `false` + * otherwise. + * + * @since 2.0.0 + * @category ordering + */ +export const lessThan: { + /** + * Returns `true` if this `Interval` is less than `that` interval, `false` + * otherwise. + * + * @since 2.0.0 + * @category ordering + */ + (that: Interval): (self: Interval) => boolean + /** + * Returns `true` if this `Interval` is less than `that` interval, `false` + * otherwise. + * + * @since 2.0.0 + * @category ordering + */ + (self: Interval, that: Interval): boolean +} = internal.lessThan + +/** + * Returns the minimum of two `Interval`s. + * + * @since 2.0.0 + * @category ordering + */ +export const min: { + /** + * Returns the minimum of two `Interval`s. + * + * @since 2.0.0 + * @category ordering + */ + (that: Interval): (self: Interval) => Interval + /** + * Returns the minimum of two `Interval`s. + * + * @since 2.0.0 + * @category ordering + */ + (self: Interval, that: Interval): Interval +} = internal.min + +/** + * Returns the maximum of two `Interval`s. + * + * @since 2.0.0 + * @category ordering + */ +export const max: { + /** + * Returns the maximum of two `Interval`s. + * + * @since 2.0.0 + * @category ordering + */ + (that: Interval): (self: Interval) => Interval + /** + * Returns the maximum of two `Interval`s. + * + * @since 2.0.0 + * @category ordering + */ + (self: Interval, that: Interval): Interval +} = internal.max + +/** + * Returns `true` if the specified `Interval` is empty, `false` otherwise. + * + * @since 2.0.0 + * @category ordering + */ +export const isEmpty: (self: Interval) => boolean = internal.isEmpty + +/** + * Returns `true` if the specified `Interval` is non-empty, `false` otherwise. + * + * @since 2.0.0 + * @category ordering + */ +export const isNonEmpty: (self: Interval) => boolean = internal.isNonEmpty + +/** + * Computes a new `Interval` which is the intersection of this `Interval` and + * that `Interval`. + * + * @since 2.0.0 + * @category ordering + */ +export const intersect: { + /** + * Computes a new `Interval` which is the intersection of this `Interval` and + * that `Interval`. + * + * @since 2.0.0 + * @category ordering + */ + (that: Interval): (self: Interval) => Interval + /** + * Computes a new `Interval` which is the intersection of this `Interval` and + * that `Interval`. + * + * @since 2.0.0 + * @category ordering + */ + (self: Interval, that: Interval): Interval +} = internal.intersect + +/** + * Calculates the size of the `Interval` as the `Duration` from the start of the + * interval to the end of the interval. + * + * @since 2.0.0 + * @category getters + */ +export const size: (self: Interval) => Duration.Duration = internal.size + +/** + * Computes a new `Interval` which is the union of this `Interval` and that + * `Interval` as a `Some`, otherwise returns `None` if the two intervals cannot + * form a union. + * + * @since 2.0.0 + * @category utils + */ +export const union: { + /** + * Computes a new `Interval` which is the union of this `Interval` and that + * `Interval` as a `Some`, otherwise returns `None` if the two intervals cannot + * form a union. + * + * @since 2.0.0 + * @category utils + */ + (that: Interval): (self: Interval) => Option.Option + /** + * Computes a new `Interval` which is the union of this `Interval` and that + * `Interval` as a `Some`, otherwise returns `None` if the two intervals cannot + * form a union. + * + * @since 2.0.0 + * @category utils + */ + (self: Interval, that: Interval): Option.Option +} = internal.union + +/** + * Construct an `Interval` that includes all time equal to and after the + * specified start time. + * + * @since 2.0.0 + * @category constructors + */ +export const after: (startMilliseconds: number) => Interval = internal.after + +/** + * Construct an `Interval` that includes all time equal to and before the + * specified end time. + * + * @category constructors + * @since 2.0.0 + */ +export const before: (endMilliseconds: number) => Interval = internal.before diff --git a/backend/node_modules/effect/src/ScheduleIntervals.ts b/backend/node_modules/effect/src/ScheduleIntervals.ts new file mode 100644 index 0000000000000000000000000000000000000000..a0b2fa4bfb6a255ecc807046a4fc91159c7ead9b --- /dev/null +++ b/backend/node_modules/effect/src/ScheduleIntervals.ts @@ -0,0 +1,172 @@ +/** + * @since 2.0.0 + */ +import type * as Check from "./Chunk.js" +import * as internal from "./internal/schedule/intervals.js" +import type * as Interval from "./ScheduleInterval.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const IntervalsTypeId: unique symbol = internal.IntervalsTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type IntervalsTypeId = typeof IntervalsTypeId + +/** + * An `Intervals` represents a list of several `Interval`s. + * + * @since 2.0.0 + * @category models + */ +export interface Intervals { + readonly [IntervalsTypeId]: IntervalsTypeId + readonly intervals: Check.Chunk +} + +/** + * Creates a new `Intervals` from a `List` of `Interval`s. + * + * @since 2.0.0 + * @category constructors + */ +export const make: (intervals: Check.Chunk) => Intervals = internal.make + +/** + * Constructs an empty list of `Interval`s. + * + * @since 2.0.0 + * @category constructors + */ +export const empty: Intervals = internal.empty + +/** + * Creates `Intervals` from the specified `Iterable`. + * + * @since 2.0.0 + * @category constructors + */ +export const fromIterable: (intervals: Iterable) => Intervals = internal.fromIterable + +/** + * Computes the union of this `Intervals` and that `Intervals` + * + * @since 2.0.0 + * @category utils + */ +export const union: { + /** + * Computes the union of this `Intervals` and that `Intervals` + * + * @since 2.0.0 + * @category utils + */ + (that: Intervals): (self: Intervals) => Intervals + /** + * Computes the union of this `Intervals` and that `Intervals` + * + * @since 2.0.0 + * @category utils + */ + (self: Intervals, that: Intervals): Intervals +} = internal.union + +/** + * Produces the intersection of this `Intervals` and that `Intervals`. + * + * @since 2.0.0 + * @category utils + */ +export const intersect: { + /** + * Produces the intersection of this `Intervals` and that `Intervals`. + * + * @since 2.0.0 + * @category utils + */ + (that: Intervals): (self: Intervals) => Intervals + /** + * Produces the intersection of this `Intervals` and that `Intervals`. + * + * @since 2.0.0 + * @category utils + */ + (self: Intervals, that: Intervals): Intervals +} = internal.intersect + +/** + * The start of the earliest interval in the specified `Intervals`. + * + * @since 2.0.0 + * @category getters + */ +export const start: (self: Intervals) => number = internal.start + +/** + * The end of the latest interval in the specified `Intervals`. + * + * @since 2.0.0 + * @category getters + */ +export const end: (self: Intervals) => number = internal.end + +/** + * Returns `true` if the start of this `Intervals` is before the start of that + * `Intervals`, `false` otherwise. + * + * @since 2.0.0 + * @category ordering + */ +export const lessThan: { + /** + * Returns `true` if the start of this `Intervals` is before the start of that + * `Intervals`, `false` otherwise. + * + * @since 2.0.0 + * @category ordering + */ + (that: Intervals): (self: Intervals) => boolean + /** + * Returns `true` if the start of this `Intervals` is before the start of that + * `Intervals`, `false` otherwise. + * + * @since 2.0.0 + * @category ordering + */ + (self: Intervals, that: Intervals): boolean +} = internal.lessThan + +/** + * Returns `true` if this `Intervals` is non-empty, `false` otherwise. + * + * @since 2.0.0 + * @category getters + */ +export const isNonEmpty: (self: Intervals) => boolean = internal.isNonEmpty + +/** + * Returns the maximum of the two `Intervals` (i.e. which has the latest start). + * + * @since 2.0.0 + * @category ordering + */ +export const max: { + /** + * Returns the maximum of the two `Intervals` (i.e. which has the latest start). + * + * @since 2.0.0 + * @category ordering + */ + (that: Intervals): (self: Intervals) => Intervals + /** + * Returns the maximum of the two `Intervals` (i.e. which has the latest start). + * + * @since 2.0.0 + * @category ordering + */ + (self: Intervals, that: Intervals): Intervals +} = internal.max diff --git a/backend/node_modules/effect/src/SchemaAST.ts b/backend/node_modules/effect/src/SchemaAST.ts new file mode 100644 index 0000000000000000000000000000000000000000..e376f9b8f75f10b92206dd00a21e0515ec081c3c --- /dev/null +++ b/backend/node_modules/effect/src/SchemaAST.ts @@ -0,0 +1,3055 @@ +/** + * @since 3.10.0 + */ + +import * as Arr from "./Array.js" +import type { Effect } from "./Effect.js" +import type { Equivalence } from "./Equivalence.js" +import { dual, identity } from "./Function.js" +import { globalValue } from "./GlobalValue.js" +import * as Inspectable from "./Inspectable.js" +import * as errors_ from "./internal/schema/errors.js" +import * as util_ from "./internal/schema/util.js" +import * as Number from "./Number.js" +import * as Option from "./Option.js" +import * as Order from "./Order.js" +import type { ParseIssue } from "./ParseResult.js" +import * as Predicate from "./Predicate.js" +import * as regexp from "./RegExp.js" +import type { Concurrency } from "./Types.js" + +/** + * @category model + * @since 3.10.0 + */ +export type AST = + | Declaration + | Literal + | UniqueSymbol + | UndefinedKeyword + | VoidKeyword + | NeverKeyword + | UnknownKeyword + | AnyKeyword + | StringKeyword + | NumberKeyword + | BooleanKeyword + | BigIntKeyword + | SymbolKeyword + | ObjectKeyword + | Enums + | TemplateLiteral + // possible transformations + | Refinement + | TupleType + | TypeLiteral + | Union + | Suspend + // transformations + | Transformation + +// ------------------------------------------------------------------------------------- +// annotations +// ------------------------------------------------------------------------------------- + +/** + * @category annotations + * @since 3.19.0 + * @experimental + */ +export type TypeConstructorAnnotation = { + readonly _tag: string + [key: PropertyKey]: unknown +} + +/** + * @category annotations + * @since 3.19.0 + * @experimental + */ +export const TypeConstructorAnnotationId: unique symbol = Symbol.for("effect/annotation/TypeConstructor") + +/** + * @category annotations + * @since 3.10.0 + */ +export type BrandAnnotation = Arr.NonEmptyReadonlyArray + +/** + * @category annotations + * @since 3.10.0 + */ +export const BrandAnnotationId: unique symbol = Symbol.for("effect/annotation/Brand") + +/** + * @category annotations + * @since 3.10.0 + */ +export type SchemaIdAnnotation = string | symbol + +/** + * @category annotations + * @since 3.10.0 + */ +export const SchemaIdAnnotationId: unique symbol = Symbol.for("effect/annotation/SchemaId") + +/** + * @category annotations + * @since 3.10.0 + */ +export type MessageAnnotation = (issue: ParseIssue) => string | Effect | { + readonly message: string | Effect + readonly override: boolean +} + +/** + * @category annotations + * @since 3.10.0 + */ +export const MessageAnnotationId: unique symbol = Symbol.for("effect/annotation/Message") + +/** + * @category annotations + * @since 3.10.0 + */ +export type MissingMessageAnnotation = () => string | Effect + +/** + * @category annotations + * @since 3.10.0 + */ +export const MissingMessageAnnotationId: unique symbol = Symbol.for("effect/annotation/MissingMessage") + +/** + * @category annotations + * @since 3.10.0 + */ +export type IdentifierAnnotation = string + +/** + * @category annotations + * @since 3.10.0 + */ +export const IdentifierAnnotationId: unique symbol = Symbol.for("effect/annotation/Identifier") + +/** + * @category annotations + * @since 3.10.0 + */ +export type TitleAnnotation = string + +/** + * @category annotations + * @since 3.10.0 + */ +export const TitleAnnotationId: unique symbol = Symbol.for("effect/annotation/Title") + +/** @internal */ +export const AutoTitleAnnotationId: unique symbol = Symbol.for("effect/annotation/AutoTitle") + +/** + * @category annotations + * @since 3.10.0 + */ +export type DescriptionAnnotation = string + +/** + * @category annotations + * @since 3.10.0 + */ +export const DescriptionAnnotationId: unique symbol = Symbol.for("effect/annotation/Description") + +/** + * @category annotations + * @since 3.10.0 + */ +export type ExamplesAnnotation = Arr.NonEmptyReadonlyArray + +/** + * @category annotations + * @since 3.10.0 + */ +export const ExamplesAnnotationId: unique symbol = Symbol.for("effect/annotation/Examples") + +/** + * @category annotations + * @since 3.10.0 + */ +export type DefaultAnnotation = A + +/** + * @category annotations + * @since 3.10.0 + */ +export const DefaultAnnotationId: unique symbol = Symbol.for("effect/annotation/Default") + +/** + * @category annotations + * @since 3.10.0 + */ +export type JSONSchemaAnnotation = object + +/** + * @category annotations + * @since 3.10.0 + */ +export const JSONSchemaAnnotationId: unique symbol = Symbol.for("effect/annotation/JSONSchema") + +/** + * @category annotations + * @since 3.10.0 + */ +export const ArbitraryAnnotationId: unique symbol = Symbol.for("effect/annotation/Arbitrary") + +/** + * @category annotations + * @since 3.10.0 + */ +export const PrettyAnnotationId: unique symbol = Symbol.for("effect/annotation/Pretty") + +/** + * @category annotations + * @since 3.10.0 + */ +export type EquivalenceAnnotation = readonly []> = ( + ...equivalences: { readonly [K in keyof TypeParameters]: Equivalence } +) => Equivalence + +/** + * @category annotations + * @since 3.10.0 + */ +export const EquivalenceAnnotationId: unique symbol = Symbol.for("effect/annotation/Equivalence") + +/** + * @category annotations + * @since 3.10.0 + */ +export type DocumentationAnnotation = string + +/** + * @category annotations + * @since 3.10.0 + */ +export const DocumentationAnnotationId: unique symbol = Symbol.for("effect/annotation/Documentation") + +/** + * @category annotations + * @since 3.10.0 + */ +export type ConcurrencyAnnotation = Concurrency | undefined + +/** + * @category annotations + * @since 3.10.0 + */ +export const ConcurrencyAnnotationId: unique symbol = Symbol.for("effect/annotation/Concurrency") + +/** + * @category annotations + * @since 3.10.0 + */ +export type BatchingAnnotation = boolean | "inherit" | undefined + +/** + * @category annotations + * @since 3.10.0 + */ +export const BatchingAnnotationId: unique symbol = Symbol.for("effect/annotation/Batching") + +/** + * @category annotations + * @since 3.10.0 + */ +export type ParseIssueTitleAnnotation = (issue: ParseIssue) => string | undefined + +/** + * @category annotations + * @since 3.10.0 + */ +export const ParseIssueTitleAnnotationId: unique symbol = Symbol.for("effect/annotation/ParseIssueTitle") + +/** + * @category annotations + * @since 3.10.0 + */ +export const ParseOptionsAnnotationId: unique symbol = Symbol.for("effect/annotation/ParseOptions") + +/** + * @category annotations + * @since 3.10.0 + */ +export type DecodingFallbackAnnotation = (issue: ParseIssue) => Effect + +/** + * @category annotations + * @since 3.10.0 + */ +export const DecodingFallbackAnnotationId: unique symbol = Symbol.for("effect/annotation/DecodingFallback") + +/** + * @category annotations + * @since 3.10.0 + */ +export const SurrogateAnnotationId: unique symbol = Symbol.for("effect/annotation/Surrogate") + +/** + * @category annotations + * @since 3.10.0 + */ +export type SurrogateAnnotation = AST + +/** @internal */ +export const StableFilterAnnotationId: unique symbol = Symbol.for("effect/annotation/StableFilter") + +/** + * A stable filter consistently applies fixed validation rules, such as + * 'minItems', 'maxItems', and 'itemsCount', to ensure array length complies + * with set criteria regardless of the input data's content. + * + * @internal + */ +export type StableFilterAnnotation = boolean + +/** + * @category annotations + * @since 3.10.0 + */ +export interface Annotations { + readonly [_: string]: unknown + readonly [_: symbol]: unknown +} + +/** + * @category annotations + * @since 3.10.0 + */ +export interface Annotated { + readonly annotations: Annotations +} + +/** + * @category annotations + * @since 3.10.0 + */ +export const getAnnotation: { + /** + * @category annotations + * @since 3.10.0 + */ + (key: symbol): (annotated: Annotated) => Option.Option + /** + * @category annotations + * @since 3.10.0 + */ + (annotated: Annotated, key: symbol): Option.Option +} = dual( + 2, + (annotated: Annotated, key: symbol): Option.Option => + Object.prototype.hasOwnProperty.call(annotated.annotations, key) ? + Option.some(annotated.annotations[key] as any) : + Option.none() +) + +/** + * @category annotations + * @since 3.19.0 + * @experimental + */ +export const getTypeConstructorAnnotation = getAnnotation(TypeConstructorAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getBrandAnnotation = getAnnotation(BrandAnnotationId) + +/** + * @category annotations + * @since 3.14.2 + */ +export const getSchemaIdAnnotation = getAnnotation(SchemaIdAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getMessageAnnotation = getAnnotation(MessageAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getMissingMessageAnnotation = getAnnotation(MissingMessageAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getTitleAnnotation = getAnnotation(TitleAnnotationId) + +/** @internal */ +export const getAutoTitleAnnotation = getAnnotation(AutoTitleAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getIdentifierAnnotation = getAnnotation(IdentifierAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getDescriptionAnnotation = getAnnotation(DescriptionAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getExamplesAnnotation = getAnnotation>(ExamplesAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getDefaultAnnotation = getAnnotation>(DefaultAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getJSONSchemaAnnotation = getAnnotation(JSONSchemaAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getDocumentationAnnotation = getAnnotation(DocumentationAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getConcurrencyAnnotation = getAnnotation(ConcurrencyAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getBatchingAnnotation = getAnnotation(BatchingAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getParseIssueTitleAnnotation = getAnnotation(ParseIssueTitleAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getParseOptionsAnnotation = getAnnotation(ParseOptionsAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getDecodingFallbackAnnotation = getAnnotation>( + DecodingFallbackAnnotationId +) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getSurrogateAnnotation = getAnnotation(SurrogateAnnotationId) + +const getStableFilterAnnotation = getAnnotation(StableFilterAnnotationId) + +/** @internal */ +export const hasStableFilter = (annotated: Annotated) => + Option.exists(getStableFilterAnnotation(annotated), (b) => b === true) + +/** + * @category annotations + * @since 3.10.0 + */ +export const JSONIdentifierAnnotationId: unique symbol = Symbol.for("effect/annotation/JSONIdentifier") + +/** + * @category annotations + * @since 3.10.0 + */ +export const getJSONIdentifierAnnotation = getAnnotation(JSONIdentifierAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getJSONIdentifier = (annotated: Annotated) => + Option.orElse(getJSONIdentifierAnnotation(annotated), () => getIdentifierAnnotation(annotated)) + +// ------------------------------------------------------------------------------------- +// schema ids +// ------------------------------------------------------------------------------------- + +/** + * @category schema id + * @since 3.10.0 + */ +export const ParseJsonSchemaId: unique symbol = Symbol.for("effect/schema/ParseJson") + +/** + * @category model + * @since 3.10.0 + */ +export class Declaration implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "Declaration" + constructor( + readonly typeParameters: ReadonlyArray, + readonly decodeUnknown: ( + ...typeParameters: ReadonlyArray + ) => (input: unknown, options: ParseOptions, self: Declaration) => Effect, + readonly encodeUnknown: ( + ...typeParameters: ReadonlyArray + ) => (input: unknown, options: ParseOptions, self: Declaration) => Effect, + readonly annotations: Annotations = {} + ) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => "") + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + typeParameters: this.typeParameters.map((ast) => ast.toJSON()), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +const createASTGuard = (tag: T) => (ast: AST): ast is Extract => + ast._tag === tag + +/** + * @category guards + * @since 3.10.0 + */ +export const isDeclaration: (ast: AST) => ast is Declaration = createASTGuard("Declaration") + +/** + * @category model + * @since 3.10.0 + */ +export type LiteralValue = string | number | boolean | null | bigint + +/** + * @category model + * @since 3.10.0 + */ +export class Literal implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "Literal" + constructor(readonly literal: LiteralValue, readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => Inspectable.formatUnknown(this.literal)) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + literal: Predicate.isBigInt(this.literal) ? String(this.literal) : this.literal, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isLiteral: (ast: AST) => ast is Literal = createASTGuard("Literal") + +const $null = new Literal(null) + +export { + /** + * @category constructors + * @since 3.10.0 + */ + $null as null +} + +/** + * @category model + * @since 3.10.0 + */ +export class UniqueSymbol implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "UniqueSymbol" + constructor(readonly symbol: symbol, readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => Inspectable.formatUnknown(this.symbol)) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + symbol: String(this.symbol), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isUniqueSymbol: (ast: AST) => ast is UniqueSymbol = createASTGuard("UniqueSymbol") + +/** + * @category model + * @since 3.10.0 + */ +export class UndefinedKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "UndefinedKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const undefinedKeyword: UndefinedKeyword = new UndefinedKeyword({ + [TitleAnnotationId]: "undefined" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isUndefinedKeyword: (ast: AST) => ast is UndefinedKeyword = createASTGuard("UndefinedKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class VoidKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "VoidKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const voidKeyword: VoidKeyword = new VoidKeyword({ + [TitleAnnotationId]: "void" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isVoidKeyword: (ast: AST) => ast is VoidKeyword = createASTGuard("VoidKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class NeverKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "NeverKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const neverKeyword: NeverKeyword = new NeverKeyword({ + [TitleAnnotationId]: "never" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isNeverKeyword: (ast: AST) => ast is NeverKeyword = createASTGuard("NeverKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class UnknownKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "UnknownKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const unknownKeyword: UnknownKeyword = new UnknownKeyword({ + [TitleAnnotationId]: "unknown" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isUnknownKeyword: (ast: AST) => ast is UnknownKeyword = createASTGuard("UnknownKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class AnyKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "AnyKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const anyKeyword: AnyKeyword = new AnyKeyword({ + [TitleAnnotationId]: "any" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isAnyKeyword: (ast: AST) => ast is AnyKeyword = createASTGuard("AnyKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class StringKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "StringKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const stringKeyword: StringKeyword = new StringKeyword({ + [TitleAnnotationId]: "string", + [DescriptionAnnotationId]: "a string" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isStringKeyword: (ast: AST) => ast is StringKeyword = createASTGuard("StringKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class NumberKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "NumberKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const numberKeyword: NumberKeyword = new NumberKeyword({ + [TitleAnnotationId]: "number", + [DescriptionAnnotationId]: "a number" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isNumberKeyword: (ast: AST) => ast is NumberKeyword = createASTGuard("NumberKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class BooleanKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "BooleanKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const booleanKeyword: BooleanKeyword = new BooleanKeyword({ + [TitleAnnotationId]: "boolean", + [DescriptionAnnotationId]: "a boolean" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isBooleanKeyword: (ast: AST) => ast is BooleanKeyword = createASTGuard("BooleanKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class BigIntKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "BigIntKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const bigIntKeyword: BigIntKeyword = new BigIntKeyword({ + [TitleAnnotationId]: "bigint", + [DescriptionAnnotationId]: "a bigint" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isBigIntKeyword: (ast: AST) => ast is BigIntKeyword = createASTGuard("BigIntKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class SymbolKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "SymbolKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const symbolKeyword: SymbolKeyword = new SymbolKeyword({ + [TitleAnnotationId]: "symbol", + [DescriptionAnnotationId]: "a symbol" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isSymbolKeyword: (ast: AST) => ast is SymbolKeyword = createASTGuard("SymbolKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class ObjectKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "ObjectKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const objectKeyword: ObjectKeyword = new ObjectKeyword({ + [TitleAnnotationId]: "object", + [DescriptionAnnotationId]: "an object in the TypeScript meaning, i.e. the `object` type" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isObjectKeyword: (ast: AST) => ast is ObjectKeyword = createASTGuard("ObjectKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class Enums implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "Enums" + constructor( + readonly enums: ReadonlyArray, + readonly annotations: Annotations = {} + ) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse( + getExpected(this), + () => ` JSON.stringify(value)).join(" | ")}>` + ) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + enums: this.enums, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isEnums: (ast: AST) => ast is Enums = createASTGuard("Enums") + +type TemplateLiteralSpanBaseType = StringKeyword | NumberKeyword | Literal | TemplateLiteral + +type TemplateLiteralSpanType = TemplateLiteralSpanBaseType | Union + +const isTemplateLiteralSpanType = (ast: AST): ast is TemplateLiteralSpanType => { + switch (ast._tag) { + case "Literal": + case "NumberKeyword": + case "StringKeyword": + case "TemplateLiteral": + return true + case "Union": + return ast.types.every(isTemplateLiteralSpanType) + } + return false +} + +const templateLiteralSpanUnionTypeToString = (type: TemplateLiteralSpanType): string => { + switch (type._tag) { + case "Literal": + return JSON.stringify(String(type.literal)) + case "StringKeyword": + return "string" + case "NumberKeyword": + return "number" + case "TemplateLiteral": + return String(type) + case "Union": + return type.types.map(templateLiteralSpanUnionTypeToString).join(" | ") + } +} + +const templateLiteralSpanTypeToString = (type: TemplateLiteralSpanType): string => { + switch (type._tag) { + case "Literal": + return String(type.literal) + case "StringKeyword": + return "${string}" + case "NumberKeyword": + return "${number}" + case "TemplateLiteral": + return "${" + String(type) + "}" + case "Union": + return "${" + type.types.map(templateLiteralSpanUnionTypeToString).join(" | ") + "}" + } +} + +/** + * @category model + * @since 3.10.0 + */ +export class TemplateLiteralSpan { + /** + * @since 3.10.0 + */ + readonly type: TemplateLiteralSpanType + constructor(type: AST, readonly literal: string) { + if (isTemplateLiteralSpanType(type)) { + this.type = type + } else { + throw new Error(errors_.getSchemaUnsupportedLiteralSpanErrorMessage(type)) + } + } + /** + * @since 3.10.0 + */ + toString() { + return templateLiteralSpanTypeToString(this.type) + this.literal + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + type: this.type.toJSON(), + literal: this.literal + } + } +} + +/** + * @category model + * @since 3.10.0 + */ +export class TemplateLiteral implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "TemplateLiteral" + constructor( + readonly head: string, + readonly spans: Arr.NonEmptyReadonlyArray, + readonly annotations: Annotations = {} + ) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => formatTemplateLiteral(this)) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + head: this.head, + spans: this.spans.map((span) => span.toJSON()), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +const formatTemplateLiteral = (ast: TemplateLiteral): string => + "`" + ast.head + ast.spans.map(String).join("") + + "`" + +/** + * @category guards + * @since 3.10.0 + */ +export const isTemplateLiteral: (ast: AST) => ast is TemplateLiteral = createASTGuard("TemplateLiteral") + +/** + * @category model + * @since 3.10.0 + */ +export class Type implements Annotated { + constructor( + readonly type: AST, + readonly annotations: Annotations = {} + ) {} + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + type: this.type.toJSON(), + annotations: toJSONAnnotations(this.annotations) + } + } + /** + * @since 3.10.0 + */ + toString() { + return String(this.type) + } +} + +/** + * @category model + * @since 3.10.0 + */ +export class OptionalType extends Type { + constructor( + type: AST, + readonly isOptional: boolean, + annotations: Annotations = {} + ) { + super(type, annotations) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + type: this.type.toJSON(), + isOptional: this.isOptional, + annotations: toJSONAnnotations(this.annotations) + } + } + /** + * @since 3.10.0 + */ + toString() { + return String(this.type) + (this.isOptional ? "?" : "") + } +} + +const getRestASTs = (rest: ReadonlyArray): ReadonlyArray => rest.map((annotatedAST) => annotatedAST.type) + +/** + * @category model + * @since 3.10.0 + */ +export class TupleType implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "TupleType" + constructor( + readonly elements: ReadonlyArray, + readonly rest: ReadonlyArray, + readonly isReadonly: boolean, + readonly annotations: Annotations = {} + ) { + let hasOptionalElement = false + let hasIllegalRequiredElement = false + for (const e of elements) { + if (e.isOptional) { + hasOptionalElement = true + } else if (hasOptionalElement) { + hasIllegalRequiredElement = true + break + } + } + if (hasIllegalRequiredElement || (hasOptionalElement && rest.length > 1)) { + throw new Error(errors_.getASTRequiredElementFollowinAnOptionalElementErrorMessage) + } + } + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => formatTuple(this)) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + elements: this.elements.map((e) => e.toJSON()), + rest: this.rest.map((ast) => ast.toJSON()), + isReadonly: this.isReadonly, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +const formatTuple = (ast: TupleType): string => { + const formattedElements = ast.elements.map(String) + .join(", ") + return Arr.matchLeft(ast.rest, { + onEmpty: () => `readonly [${formattedElements}]`, + onNonEmpty: (head, tail) => { + const formattedHead = String(head) + const wrappedHead = formattedHead.includes(" | ") ? `(${formattedHead})` : formattedHead + + if (tail.length > 0) { + const formattedTail = tail.map(String).join(", ") + if (ast.elements.length > 0) { + return `readonly [${formattedElements}, ...${wrappedHead}[], ${formattedTail}]` + } else { + return `readonly [...${wrappedHead}[], ${formattedTail}]` + } + } else { + if (ast.elements.length > 0) { + return `readonly [${formattedElements}, ...${wrappedHead}[]]` + } else { + return `ReadonlyArray<${formattedHead}>` + } + } + } + }) +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isTupleType: (ast: AST) => ast is TupleType = createASTGuard("TupleType") + +/** + * @category model + * @since 3.10.0 + */ +export class PropertySignature extends OptionalType { + constructor( + readonly name: PropertyKey, + type: AST, + isOptional: boolean, + readonly isReadonly: boolean, + annotations?: Annotations + ) { + super(type, isOptional, annotations) + } + /** + * @since 3.10.0 + */ + toString(): string { + return (this.isReadonly ? "readonly " : "") + String(this.name) + (this.isOptional ? "?" : "") + ": " + + this.type + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + name: String(this.name), + type: this.type.toJSON(), + isOptional: this.isOptional, + isReadonly: this.isReadonly, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @since 3.10.0 + */ +export type Parameter = StringKeyword | SymbolKeyword | TemplateLiteral | Refinement + +/** + * @since 3.10.0 + */ +export const isParameter = (ast: AST): ast is Parameter => { + switch (ast._tag) { + case "StringKeyword": + case "SymbolKeyword": + case "TemplateLiteral": + return true + case "Refinement": + return isParameter(ast.from) + } + return false +} + +/** + * @category model + * @since 3.10.0 + */ +export class IndexSignature { + /** + * @since 3.10.0 + */ + readonly parameter: Parameter + constructor( + parameter: AST, + readonly type: AST, + readonly isReadonly: boolean + ) { + if (isParameter(parameter)) { + this.parameter = parameter + } else { + throw new Error(errors_.getASTIndexSignatureParameterErrorMessage) + } + } + /** + * @since 3.10.0 + */ + toString(): string { + return (this.isReadonly ? "readonly " : "") + `[x: ${this.parameter}]: ${this.type}` + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + parameter: this.parameter.toJSON(), + type: this.type.toJSON(), + isReadonly: this.isReadonly + } + } +} + +/** + * @category model + * @since 3.10.0 + */ +export class TypeLiteral implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "TypeLiteral" + /** + * @since 3.10.0 + */ + readonly propertySignatures: ReadonlyArray + /** + * @since 3.10.0 + */ + readonly indexSignatures: ReadonlyArray + constructor( + propertySignatures: ReadonlyArray, + indexSignatures: ReadonlyArray, + readonly annotations: Annotations = {} + ) { + // check for duplicate property signatures + const keys: Record = {} + for (let i = 0; i < propertySignatures.length; i++) { + const name = propertySignatures[i].name + if (Object.prototype.hasOwnProperty.call(keys, name)) { + throw new Error(errors_.getASTDuplicatePropertySignatureErrorMessage(name)) + } + keys[name] = null + } + // check for duplicate index signatures + const parameters = { + string: false, + symbol: false + } + for (let i = 0; i < indexSignatures.length; i++) { + const encodedParameter = getEncodedParameter(indexSignatures[i].parameter) + if (isStringKeyword(encodedParameter)) { + if (parameters.string) { + throw new Error(errors_.getASTDuplicateIndexSignatureErrorMessage("string")) + } + parameters.string = true + } else if (isSymbolKeyword(encodedParameter)) { + if (parameters.symbol) { + throw new Error(errors_.getASTDuplicateIndexSignatureErrorMessage("symbol")) + } + parameters.symbol = true + } + } + + this.propertySignatures = propertySignatures + this.indexSignatures = indexSignatures + } + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => formatTypeLiteral(this)) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + propertySignatures: this.propertySignatures.map((ps) => ps.toJSON()), + indexSignatures: this.indexSignatures.map((ps) => ps.toJSON()), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +const formatIndexSignatures = (iss: ReadonlyArray): string => iss.map(String).join("; ") + +const formatTypeLiteral = (ast: TypeLiteral): string => { + if (ast.propertySignatures.length > 0) { + const pss = ast.propertySignatures.map(String).join("; ") + if (ast.indexSignatures.length > 0) { + return `{ ${pss}; ${formatIndexSignatures(ast.indexSignatures)} }` + } else { + return `{ ${pss} }` + } + } else { + if (ast.indexSignatures.length > 0) { + return `{ ${formatIndexSignatures(ast.indexSignatures)} }` + } else { + return "{}" + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isTypeLiteral: (ast: AST) => ast is TypeLiteral = createASTGuard("TypeLiteral") + +/** + * @since 3.10.0 + */ +export type Members = readonly [A, A, ...Array] + +const sortCandidates = Arr.sort( + Order.mapInput(Number.Order, (ast: AST) => { + switch (ast._tag) { + case "AnyKeyword": + return 0 + case "UnknownKeyword": + return 1 + case "ObjectKeyword": + return 2 + case "StringKeyword": + case "NumberKeyword": + case "BooleanKeyword": + case "BigIntKeyword": + case "SymbolKeyword": + return 3 + } + return 4 + }) +) + +const literalMap = { + string: "StringKeyword", + number: "NumberKeyword", + boolean: "BooleanKeyword", + bigint: "BigIntKeyword" +} as const + +/** @internal */ +export const flatten = (candidates: ReadonlyArray): Array => + Arr.flatMap(candidates, (ast) => isUnion(ast) ? flatten(ast.types) : [ast]) + +/** @internal */ +export const unify = (candidates: ReadonlyArray): Array => { + const cs = sortCandidates(candidates) + const out: Array = [] + const uniques: { [K in AST["_tag"] | "{}"]?: AST } = {} + const literals: Array = [] + for (const ast of cs) { + switch (ast._tag) { + case "NeverKeyword": + break + case "AnyKeyword": + return [anyKeyword] + case "UnknownKeyword": + return [unknownKeyword] + // uniques + case "ObjectKeyword": + case "UndefinedKeyword": + case "VoidKeyword": + case "StringKeyword": + case "NumberKeyword": + case "BooleanKeyword": + case "BigIntKeyword": + case "SymbolKeyword": { + if (!uniques[ast._tag]) { + uniques[ast._tag] = ast + out.push(ast) + } + break + } + case "Literal": { + const type = typeof ast.literal + switch (type) { + case "string": + case "number": + case "bigint": + case "boolean": { + const _tag = literalMap[type] + if (!uniques[_tag] && !literals.includes(ast.literal)) { + literals.push(ast.literal) + out.push(ast) + } + break + } + // null + case "object": { + if (!literals.includes(ast.literal)) { + literals.push(ast.literal) + out.push(ast) + } + break + } + } + break + } + case "UniqueSymbol": { + if (!uniques["SymbolKeyword"] && !literals.includes(ast.symbol)) { + literals.push(ast.symbol) + out.push(ast) + } + break + } + case "TupleType": { + if (!uniques["ObjectKeyword"]) { + out.push(ast) + } + break + } + case "TypeLiteral": { + if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { + if (!uniques["{}"]) { + uniques["{}"] = ast + out.push(ast) + } + } else if (!uniques["ObjectKeyword"]) { + out.push(ast) + } + break + } + default: + out.push(ast) + } + } + return out +} + +/** + * @category model + * @since 3.10.0 + */ +export class Union implements Annotated { + static make = (types: ReadonlyArray, annotations?: Annotations): AST => { + return isMembers(types) ? new Union(types, annotations) : types.length === 1 ? types[0] : neverKeyword + } + /** @internal */ + static unify = (candidates: ReadonlyArray, annotations?: Annotations): AST => { + return Union.make(unify(flatten(candidates)), annotations) + } + /** + * @since 3.10.0 + */ + readonly _tag = "Union" + private constructor(readonly types: Members, readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => this.types.map(String).join(" | ")) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + types: this.types.map((ast) => ast.toJSON()), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** @internal */ +export const mapMembers = (members: Members, f: (a: A) => B): Members => members.map(f) as any + +/** @internal */ +export const isMembers = (as: ReadonlyArray): as is Members => as.length > 1 + +/** + * @category guards + * @since 3.10.0 + */ +export const isUnion: (ast: AST) => ast is Union = createASTGuard("Union") + +const toJSONMemoMap = globalValue( + Symbol.for("effect/Schema/AST/toJSONMemoMap"), + () => new WeakMap() +) + +/** + * @category model + * @since 3.10.0 + */ +export class Suspend implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "Suspend" + constructor(readonly f: () => AST, readonly annotations: Annotations = {}) { + this.f = util_.memoizeThunk(f) + } + /** + * @since 3.10.0 + */ + toString() { + return getExpected(this).pipe( + Option.orElse(() => + Option.flatMap( + Option.liftThrowable(this.f)(), + (ast) => getExpected(ast) + ) + ), + Option.getOrElse(() => "") + ) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + const ast = this.f() + let out = toJSONMemoMap.get(ast) + if (out) { + return out + } + toJSONMemoMap.set(ast, { _tag: this._tag }) + out = { + _tag: this._tag, + ast: ast.toJSON(), + annotations: toJSONAnnotations(this.annotations) + } + toJSONMemoMap.set(ast, out) + return out + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isSuspend: (ast: AST) => ast is Suspend = createASTGuard("Suspend") + +/** + * @category model + * @since 3.10.0 + */ +export class Refinement implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "Refinement" + constructor( + readonly from: From, + readonly filter: ( + input: any, + options: ParseOptions, + self: Refinement + ) => Option.Option, + readonly annotations: Annotations = {} + ) {} + /** + * @since 3.10.0 + */ + toString() { + return getIdentifierAnnotation(this).pipe(Option.getOrElse(() => + Option.match(getOrElseExpected(this), { + onNone: () => `{ ${this.from} | filter }`, + onSome: (expected) => isRefinement(this.from) ? String(this.from) + " & " + expected : expected + }) + )) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + from: this.from.toJSON(), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isRefinement: (ast: AST) => ast is Refinement = createASTGuard("Refinement") + +/** + * @category model + * @since 3.10.0 + */ +export interface ParseOptions { + /** + * The `errors` option allows you to receive all parsing errors when + * attempting to parse a value using a schema. By default only the first error + * is returned, but by setting the `errors` option to `"all"`, you can receive + * all errors that occurred during the parsing process. This can be useful for + * debugging or for providing more comprehensive error messages to the user. + * + * default: "first" + * + * @since 3.10.0 + */ + readonly errors?: "first" | "all" | undefined + /** + * When using a `Schema` to parse a value, by default any properties that are + * not specified in the `Schema` will be stripped out from the output. This is + * because the `Schema` is expecting a specific shape for the parsed value, + * and any excess properties do not conform to that shape. + * + * However, you can use the `onExcessProperty` option (default value: + * `"ignore"`) to trigger a parsing error. This can be particularly useful in + * cases where you need to detect and handle potential errors or unexpected + * values. + * + * If you want to allow excess properties to remain, you can use + * `onExcessProperty` set to `"preserve"`. + * + * default: "ignore" + * + * @since 3.10.0 + */ + readonly onExcessProperty?: "ignore" | "error" | "preserve" | undefined + /** + * The `propertyOrder` option provides control over the order of object fields + * in the output. This feature is particularly useful when the sequence of + * keys is important for the consuming processes or when maintaining the input + * order enhances readability and usability. + * + * By default, the `propertyOrder` option is set to `"none"`. This means that + * the internal system decides the order of keys to optimize parsing speed. + * The order of keys in this mode should not be considered stable, and it's + * recommended not to rely on key ordering as it may change in future updates + * without notice. + * + * Setting `propertyOrder` to `"original"` ensures that the keys are ordered + * as they appear in the input during the decoding/encoding process. + * + * default: "none" + * + * @since 3.10.0 + */ + readonly propertyOrder?: "none" | "original" | undefined + /** + * Handles missing properties in data structures. By default, missing + * properties are treated as if present with an `undefined` value. To treat + * missing properties as errors, set the `exact` option to `true`. This + * setting is already enabled by default for `is` and `asserts` functions, + * treating absent properties strictly unless overridden. + * + * default: false + * + * @since 3.10.0 + */ + readonly exact?: boolean | undefined +} + +/** + * @since 3.10.0 + */ +export const defaultParseOption: ParseOptions = {} + +/** + * @category model + * @since 3.10.0 + */ +export class Transformation implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "Transformation" + constructor( + readonly from: AST, + readonly to: AST, + readonly transformation: TransformationKind, + readonly annotations: Annotations = {} + ) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse( + getExpected(this), + () => `(${String(this.from)} <-> ${String(this.to)})` + ) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + from: this.from.toJSON(), + to: this.to.toJSON(), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isTransformation: (ast: AST) => ast is Transformation = createASTGuard("Transformation") + +/** + * @category model + * @since 3.10.0 + */ +export type TransformationKind = + | FinalTransformation + | ComposeTransformation + | TypeLiteralTransformation + +/** + * @category model + * @since 3.10.0 + */ +export class FinalTransformation { + /** + * @since 3.10.0 + */ + readonly _tag = "FinalTransformation" + constructor( + readonly decode: ( + fromA: any, + options: ParseOptions, + self: Transformation, + fromI: any + ) => Effect, + readonly encode: (toI: any, options: ParseOptions, self: Transformation, toA: any) => Effect + ) {} +} + +const createTransformationGuard = + (tag: T) => + (ast: TransformationKind): ast is Extract => ast._tag === tag + +/** + * @category guards + * @since 3.10.0 + */ +export const isFinalTransformation: (ast: TransformationKind) => ast is FinalTransformation = createTransformationGuard( + "FinalTransformation" +) + +/** + * @category model + * @since 3.10.0 + */ +export class ComposeTransformation { + /** + * @since 3.10.0 + */ + readonly _tag = "ComposeTransformation" +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const composeTransformation: ComposeTransformation = new ComposeTransformation() + +/** + * @category guards + * @since 3.10.0 + */ +export const isComposeTransformation: (ast: TransformationKind) => ast is ComposeTransformation = + createTransformationGuard( + "ComposeTransformation" + ) + +/** + * Represents a `PropertySignature -> PropertySignature` transformation + * + * The semantic of `decode` is: + * - `none()` represents the absence of the key/value pair + * - `some(value)` represents the presence of the key/value pair + * + * The semantic of `encode` is: + * - `none()` you don't want to output the key/value pair + * - `some(value)` you want to output the key/value pair + * + * @category model + * @since 3.10.0 + */ +export class PropertySignatureTransformation { + constructor( + readonly from: PropertyKey, + readonly to: PropertyKey, + readonly decode: (o: Option.Option) => Option.Option, + readonly encode: (o: Option.Option) => Option.Option + ) {} +} + +const isRenamingPropertySignatureTransformation = (t: PropertySignatureTransformation) => + t.decode === identity && t.encode === identity + +/** + * @category model + * @since 3.10.0 + */ +export class TypeLiteralTransformation { + /** + * @since 3.10.0 + */ + readonly _tag = "TypeLiteralTransformation" + constructor( + readonly propertySignatureTransformations: ReadonlyArray< + PropertySignatureTransformation + > + ) { + // check for duplicate property signature transformations + const fromKeys: Record = {} + const toKeys: Record = {} + for (const pst of propertySignatureTransformations) { + const from = pst.from + if (fromKeys[from]) { + throw new Error(errors_.getASTDuplicatePropertySignatureTransformationErrorMessage(from)) + } + fromKeys[from] = true + const to = pst.to + if (toKeys[to]) { + throw new Error(errors_.getASTDuplicatePropertySignatureTransformationErrorMessage(to)) + } + toKeys[to] = true + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isTypeLiteralTransformation: (ast: TransformationKind) => ast is TypeLiteralTransformation = + createTransformationGuard("TypeLiteralTransformation") + +// ------------------------------------------------------------------------------------- +// API +// ------------------------------------------------------------------------------------- + +/** + * Merges a set of new annotations with existing ones, potentially overwriting + * any duplicates. + * + * Any previously existing identifier annotations are deleted. + * + * @since 3.10.0 + */ +export const annotations = (ast: AST, overrides: Annotations): AST => { + const d = Object.getOwnPropertyDescriptors(ast) + const base: any = { ...ast.annotations } + delete base[IdentifierAnnotationId] + const value = { ...base, ...overrides } + const surrogate = getSurrogateAnnotation(ast) + if (Option.isSome(surrogate)) { + value[SurrogateAnnotationId] = annotations(surrogate.value, overrides) + } + d.annotations.value = value + return Object.create(Object.getPrototypeOf(ast), d) +} + +/** + * Equivalent at runtime to the TypeScript type-level `keyof` operator. + * + * @since 3.10.0 + */ +export const keyof = (ast: AST): AST => Union.unify(_keyof(ast)) + +const STRING_KEYWORD_PATTERN = "[\\s\\S]*?" // any string, including newlines +const NUMBER_KEYWORD_PATTERN = "[+-]?\\d*\\.?\\d+(?:[Ee][+-]?\\d+)?" + +const getTemplateLiteralSpanTypePattern = (type: TemplateLiteralSpanType, capture: boolean): string => { + switch (type._tag) { + case "Literal": + return regexp.escape(String(type.literal)) + case "StringKeyword": + return STRING_KEYWORD_PATTERN + case "NumberKeyword": + return NUMBER_KEYWORD_PATTERN + case "TemplateLiteral": + return getTemplateLiteralPattern(type, capture, false) + case "Union": + return type.types.map((type) => getTemplateLiteralSpanTypePattern(type, capture)).join("|") + } +} + +const handleTemplateLiteralSpanTypeParens = ( + type: TemplateLiteralSpanType, + s: string, + capture: boolean, + top: boolean +) => { + if (isUnion(type)) { + if (capture && !top) { + return `(?:${s})` + } + } else if (!capture || !top) { + return s + } + return `(${s})` +} + +const getTemplateLiteralPattern = (ast: TemplateLiteral, capture: boolean, top: boolean): string => { + let pattern = `` + if (ast.head !== "") { + const head = regexp.escape(ast.head) + pattern += capture && top ? `(${head})` : head + } + + for (const span of ast.spans) { + const spanPattern = getTemplateLiteralSpanTypePattern(span.type, capture) + pattern += handleTemplateLiteralSpanTypeParens(span.type, spanPattern, capture, top) + if (span.literal !== "") { + const literal = regexp.escape(span.literal) + pattern += capture && top ? `(${literal})` : literal + } + } + + return pattern +} + +/** + * Generates a regular expression from a `TemplateLiteral` AST node. + * + * @see {@link getTemplateLiteralCapturingRegExp} for a variant that captures the pattern. + * + * @since 3.10.0 + */ +export const getTemplateLiteralRegExp = (ast: TemplateLiteral): RegExp => + new RegExp(`^${getTemplateLiteralPattern(ast, false, true)}$`) + +/** + * Generates a regular expression that captures the pattern defined by the given `TemplateLiteral` AST. + * + * @see {@link getTemplateLiteralRegExp} for a variant that does not capture the pattern. + * + * @since 3.10.0 + */ +export const getTemplateLiteralCapturingRegExp = (ast: TemplateLiteral): RegExp => + new RegExp(`^${getTemplateLiteralPattern(ast, true, true)}$`) + +/** + * @since 3.10.0 + */ +export const getPropertySignatures = (ast: AST): Array => { + const annotation = getSurrogateAnnotation(ast) + if (Option.isSome(annotation)) { + return getPropertySignatures(annotation.value) + } + switch (ast._tag) { + case "TypeLiteral": + return ast.propertySignatures.slice() + case "Suspend": + return getPropertySignatures(ast.f()) + case "Refinement": + return getPropertySignatures(ast.from) + } + return getPropertyKeys(ast).map((name) => getPropertyKeyIndexedAccess(ast, name)) +} + +const getIndexSignatures = (ast: AST): Array => { + const annotation = getSurrogateAnnotation(ast) + if (Option.isSome(annotation)) { + return getIndexSignatures(annotation.value) + } + switch (ast._tag) { + case "TypeLiteral": + return ast.indexSignatures.slice() + case "Suspend": + return getIndexSignatures(ast.f()) + case "Refinement": + return getIndexSignatures(ast.from) + case "Transformation": + return getIndexSignatures(ast.to) + } + return [] +} + +/** @internal */ +export const getNumberIndexedAccess = (ast: AST): AST => { + switch (ast._tag) { + case "TupleType": { + let hasOptional = false + let out: Array = [] + for (const e of ast.elements) { + if (e.isOptional) { + hasOptional = true + } + out.push(e.type) + } + if (hasOptional) { + out.push(undefinedKeyword) + } + out = out.concat(getRestASTs(ast.rest)) + return Union.make(out) + } + case "Refinement": + return getNumberIndexedAccess(ast.from) + case "Union": + return Union.make(ast.types.map(getNumberIndexedAccess)) + case "Suspend": + return getNumberIndexedAccess(ast.f()) + } + throw new Error(errors_.getASTUnsupportedSchemaErrorMessage(ast)) +} + +const getTypeLiteralPropertySignature = (ast: TypeLiteral, name: PropertyKey): PropertySignature | undefined => { + // from property signatures... + const ops = Arr.findFirst(ast.propertySignatures, (ps) => ps.name === name) + if (Option.isSome(ops)) { + return ops.value + } + + // from index signatures... + if (Predicate.isString(name)) { + let out: PropertySignature | undefined = undefined + for (const is of ast.indexSignatures) { + const encodedParameter = getEncodedParameter(is.parameter) + switch (encodedParameter._tag) { + case "TemplateLiteral": { + const regex = getTemplateLiteralRegExp(encodedParameter) + if (regex.test(name)) { + return new PropertySignature(name, is.type, false, true) + } + break + } + case "StringKeyword": { + if (out === undefined) { + out = new PropertySignature(name, is.type, false, true) + } + } + } + } + if (out) { + return out + } + } else if (Predicate.isSymbol(name)) { + for (const is of ast.indexSignatures) { + const encodedParameter = getEncodedParameter(is.parameter) + if (isSymbolKeyword(encodedParameter)) { + return new PropertySignature(name, is.type, false, true) + } + } + } +} + +/** @internal */ +export const getPropertyKeyIndexedAccess = (ast: AST, name: PropertyKey): PropertySignature => { + const annotation = getSurrogateAnnotation(ast) + if (Option.isSome(annotation)) { + return getPropertyKeyIndexedAccess(annotation.value, name) + } + switch (ast._tag) { + case "TypeLiteral": { + const ps = getTypeLiteralPropertySignature(ast, name) + if (ps) { + return ps + } + break + } + case "Union": + return new PropertySignature( + name, + Union.make(ast.types.map((ast) => getPropertyKeyIndexedAccess(ast, name).type)), + false, + true + ) + case "Suspend": + return getPropertyKeyIndexedAccess(ast.f(), name) + case "Refinement": + return getPropertyKeyIndexedAccess(ast.from, name) + case "Transformation": + return getPropertyKeyIndexedAccess(ast.to, name) + } + throw new Error(errors_.getASTUnsupportedSchemaErrorMessage(ast)) +} + +const getPropertyKeys = (ast: AST): Array => { + const annotation = getSurrogateAnnotation(ast) + if (Option.isSome(annotation)) { + return getPropertyKeys(annotation.value) + } + switch (ast._tag) { + case "TypeLiteral": + return ast.propertySignatures.map((ps) => ps.name) + case "Union": + return ast.types.slice(1).reduce( + (out: Array, ast) => Arr.intersection(out, getPropertyKeys(ast)), + getPropertyKeys(ast.types[0]) + ) + case "Suspend": + return getPropertyKeys(ast.f()) + case "Refinement": + return getPropertyKeys(ast.from) + case "Transformation": + return getPropertyKeys(ast.to) + } + return [] +} + +/** @internal */ +export const record = (key: AST, value: AST): { + propertySignatures: Array + indexSignatures: Array +} => { + const propertySignatures: Array = [] + const indexSignatures: Array = [] + const go = (key: AST): void => { + switch (key._tag) { + case "NeverKeyword": + break + case "StringKeyword": + case "SymbolKeyword": + case "TemplateLiteral": + case "Refinement": + indexSignatures.push(new IndexSignature(key, value, true)) + break + case "Literal": + if (Predicate.isString(key.literal) || Predicate.isNumber(key.literal)) { + propertySignatures.push(new PropertySignature(key.literal, value, false, true)) + } else { + throw new Error(errors_.getASTUnsupportedLiteralErrorMessage(key.literal)) + } + break + case "Enums": { + for (const [_, name] of key.enums) { + propertySignatures.push(new PropertySignature(name, value, false, true)) + } + break + } + case "UniqueSymbol": + propertySignatures.push(new PropertySignature(key.symbol, value, false, true)) + break + case "Union": + key.types.forEach(go) + break + default: + throw new Error(errors_.getASTUnsupportedKeySchemaErrorMessage(key)) + } + } + go(key) + return { propertySignatures, indexSignatures } +} + +/** + * Equivalent at runtime to the built-in TypeScript utility type `Pick`. + * + * @since 3.10.0 + */ +export const pick = (ast: AST, keys: ReadonlyArray): TypeLiteral | Transformation => { + const annotation = getSurrogateAnnotation(ast) + if (Option.isSome(annotation)) { + return pick(annotation.value, keys) + } + switch (ast._tag) { + case "TypeLiteral": { + const pss: Array = [] + const names: Record = {} + for (const ps of ast.propertySignatures) { + names[ps.name] = null + if (keys.includes(ps.name)) { + pss.push(ps) + } + } + for (const key of keys) { + if (!(key in names)) { + const ps = getTypeLiteralPropertySignature(ast, key) + if (ps) { + pss.push(ps) + } + } + } + return new TypeLiteral(pss, []) + } + case "Union": + return new TypeLiteral(keys.map((name) => getPropertyKeyIndexedAccess(ast, name)), []) + case "Suspend": + return pick(ast.f(), keys) + case "Refinement": + return pick(ast.from, keys) + case "Transformation": { + switch (ast.transformation._tag) { + case "ComposeTransformation": + return new Transformation( + pick(ast.from, keys), + pick(ast.to, keys), + composeTransformation + ) + case "TypeLiteralTransformation": { + const ts: Array = [] + const fromKeys: Array = [] + for (const k of keys) { + const t = ast.transformation.propertySignatureTransformations.find((t) => t.to === k) + if (t) { + ts.push(t) + fromKeys.push(t.from) + } else { + fromKeys.push(k) + } + } + return Arr.isNonEmptyReadonlyArray(ts) ? + new Transformation( + pick(ast.from, fromKeys), + pick(ast.to, keys), + new TypeLiteralTransformation(ts) + ) : + pick(ast.from, fromKeys) + } + } + } + } + throw new Error(errors_.getASTUnsupportedSchemaErrorMessage(ast)) +} + +/** + * Equivalent at runtime to the built-in TypeScript utility type `Omit`. + * + * @since 3.10.0 + */ +export const omit = (ast: AST, keys: ReadonlyArray): TypeLiteral | Transformation => { + let indexSignatures = getIndexSignatures(ast) + if (indexSignatures.length > 0) { + if (indexSignatures.some((is) => isStringKeyword(getEncodedParameter(is.parameter)))) { + indexSignatures = indexSignatures.filter((is) => !isTemplateLiteral(getEncodedParameter(is.parameter))) + } + return new TypeLiteral([], indexSignatures) + } + return pick(ast, getPropertyKeys(ast).filter((name) => !keys.includes(name))) +} + +/** @internal */ +export const orUndefined = (ast: AST): AST => Union.make([ast, undefinedKeyword]) + +/** + * Equivalent at runtime to the built-in TypeScript utility type `Partial`. + * + * @since 3.10.0 + */ +export const partial = (ast: AST, options?: { readonly exact: true }): AST => { + const exact = options?.exact === true + switch (ast._tag) { + case "TupleType": + return new TupleType( + ast.elements.map((e) => new OptionalType(exact ? e.type : orUndefined(e.type), true)), + Arr.match(ast.rest, { + onEmpty: () => ast.rest, + onNonEmpty: (rest) => [new Type(Union.make([...getRestASTs(rest), undefinedKeyword]))] + }), + ast.isReadonly + ) + case "TypeLiteral": + return new TypeLiteral( + ast.propertySignatures.map((ps) => + new PropertySignature(ps.name, exact ? ps.type : orUndefined(ps.type), true, ps.isReadonly, ps.annotations) + ), + ast.indexSignatures.map((is) => new IndexSignature(is.parameter, orUndefined(is.type), is.isReadonly)) + ) + case "Union": + return Union.make(ast.types.map((member) => partial(member, options))) + case "Suspend": + return new Suspend(() => partial(ast.f(), options)) + case "Declaration": + case "Refinement": + throw new Error(errors_.getASTUnsupportedSchemaErrorMessage(ast)) + case "Transformation": { + if ( + isTypeLiteralTransformation(ast.transformation) && + ast.transformation.propertySignatureTransformations.every(isRenamingPropertySignatureTransformation) + ) { + return new Transformation(partial(ast.from, options), partial(ast.to, options), ast.transformation) + } + throw new Error(errors_.getASTUnsupportedSchemaErrorMessage(ast)) + } + } + return ast +} + +/** + * Equivalent at runtime to the built-in TypeScript utility type `Required`. + * + * @since 3.10.0 + */ +export const required = (ast: AST): AST => { + switch (ast._tag) { + case "TupleType": + return new TupleType( + ast.elements.map((e) => new OptionalType(e.type, false)), + ast.rest, + ast.isReadonly + ) + case "TypeLiteral": + return new TypeLiteral( + ast.propertySignatures.map((f) => new PropertySignature(f.name, f.type, false, f.isReadonly, f.annotations)), + ast.indexSignatures + ) + case "Union": + return Union.make(ast.types.map((member) => required(member))) + case "Suspend": + return new Suspend(() => required(ast.f())) + case "Declaration": + case "Refinement": + throw new Error(errors_.getASTUnsupportedSchemaErrorMessage(ast)) + case "Transformation": { + if ( + isTypeLiteralTransformation(ast.transformation) && + ast.transformation.propertySignatureTransformations.every(isRenamingPropertySignatureTransformation) + ) { + return new Transformation(required(ast.from), required(ast.to), ast.transformation) + } + throw new Error(errors_.getASTUnsupportedSchemaErrorMessage(ast)) + } + } + return ast +} + +/** + * Creates a new AST with shallow mutability applied to its properties. + * + * @since 3.10.0 + */ +export const mutable = (ast: AST): AST => { + switch (ast._tag) { + case "TupleType": + return ast.isReadonly === false ? ast : new TupleType(ast.elements, ast.rest, false, ast.annotations) + case "TypeLiteral": { + const propertySignatures = changeMap( + ast.propertySignatures, + (ps) => + ps.isReadonly === false ? ps : new PropertySignature(ps.name, ps.type, ps.isOptional, false, ps.annotations) + ) + const indexSignatures = changeMap( + ast.indexSignatures, + (is) => is.isReadonly === false ? is : new IndexSignature(is.parameter, is.type, false) + ) + return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? + ast : + new TypeLiteral(propertySignatures, indexSignatures, ast.annotations) + } + case "Union": { + const types = changeMap(ast.types, mutable) + return types === ast.types ? ast : Union.make(types, ast.annotations) + } + case "Suspend": + return new Suspend(() => mutable(ast.f()), ast.annotations) + case "Refinement": { + const from = mutable(ast.from) + return from === ast.from ? ast : new Refinement(from, ast.filter, ast.annotations) + } + case "Transformation": { + const from = mutable(ast.from) + const to = mutable(ast.to) + return from === ast.from && to === ast.to ? + ast : + new Transformation(from, to, ast.transformation, ast.annotations) + } + } + return ast +} + +// ------------------------------------------------------------------------------------- +// compiler harness +// ------------------------------------------------------------------------------------- + +/** + * @since 3.10.0 + */ +export type Compiler = (ast: AST, path: ReadonlyArray) => A + +/** + * @since 3.10.0 + */ +export type Match = { + [K in AST["_tag"]]: (ast: Extract, compile: Compiler, path: ReadonlyArray) => A +} + +/** + * @since 3.10.0 + */ +export const getCompiler = (match: Match): Compiler => { + const compile = (ast: AST, path: ReadonlyArray): A => match[ast._tag](ast as any, compile, path) + return compile +} + +/** @internal */ +export const pickAnnotations = + (annotationIds: ReadonlyArray) => (annotated: Annotated): Annotations | undefined => { + let out: { [_: symbol]: unknown } | undefined = undefined + for (const id of annotationIds) { + if (Object.prototype.hasOwnProperty.call(annotated.annotations, id)) { + if (out === undefined) { + out = {} + } + out[id] = annotated.annotations[id] + } + } + return out + } + +/** @internal */ +export const omitAnnotations = + (annotationIds: ReadonlyArray) => (annotated: Annotated): Annotations | undefined => { + const out = { ...annotated.annotations } + for (const id of annotationIds) { + delete out[id] + } + return out + } + +const preserveTransformationAnnotations = pickAnnotations([ + ExamplesAnnotationId, + DefaultAnnotationId, + JSONSchemaAnnotationId, + ArbitraryAnnotationId, + PrettyAnnotationId, + EquivalenceAnnotationId +]) + +/** + * @since 3.10.0 + */ +export const typeAST = (ast: AST): AST => { + switch (ast._tag) { + case "Declaration": { + const typeParameters = changeMap(ast.typeParameters, typeAST) + return typeParameters === ast.typeParameters ? + ast : + new Declaration(typeParameters, ast.decodeUnknown, ast.encodeUnknown, ast.annotations) + } + case "TupleType": { + const elements = changeMap(ast.elements, (e) => { + const type = typeAST(e.type) + return type === e.type ? e : new OptionalType(type, e.isOptional) + }) + const restASTs = getRestASTs(ast.rest) + const rest = changeMap(restASTs, typeAST) + return elements === ast.elements && rest === restASTs ? + ast : + new TupleType(elements, rest.map((type) => new Type(type)), ast.isReadonly, ast.annotations) + } + case "TypeLiteral": { + const propertySignatures = changeMap(ast.propertySignatures, (p) => { + const type = typeAST(p.type) + return type === p.type ? p : new PropertySignature(p.name, type, p.isOptional, p.isReadonly) + }) + const indexSignatures = changeMap(ast.indexSignatures, (is) => { + const type = typeAST(is.type) + return type === is.type ? is : new IndexSignature(is.parameter, type, is.isReadonly) + }) + return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? + ast : + new TypeLiteral(propertySignatures, indexSignatures, ast.annotations) + } + case "Union": { + const types = changeMap(ast.types, typeAST) + return types === ast.types ? ast : Union.make(types, ast.annotations) + } + case "Suspend": + return new Suspend(() => typeAST(ast.f()), ast.annotations) + case "Refinement": { + const from = typeAST(ast.from) + return from === ast.from ? + ast : + new Refinement(from, ast.filter, ast.annotations) + } + case "Transformation": { + const preserve = preserveTransformationAnnotations(ast) + return typeAST( + preserve !== undefined ? + annotations(ast.to, preserve) : + ast.to + ) + } + } + return ast +} + +function changeMap( + as: Arr.NonEmptyReadonlyArray, + f: (a: A) => A +): Arr.NonEmptyReadonlyArray +function changeMap(as: ReadonlyArray, f: (a: A) => A): ReadonlyArray +function changeMap(as: ReadonlyArray, f: (a: A) => A): ReadonlyArray { + let changed = false + const out = Arr.allocate(as.length) as Array + for (let i = 0; i < as.length; i++) { + const a = as[i] + const fa = f(a) + if (fa !== a) { + changed = true + } + out[i] = fa + } + return changed ? out : as +} + +/** + * Returns the from part of a transformation if it exists + * + * @internal + */ +export const getTransformationFrom = (ast: AST): AST | undefined => { + switch (ast._tag) { + case "Transformation": + return ast.from + case "Refinement": + return getTransformationFrom(ast.from) + case "Suspend": + return getTransformationFrom(ast.f()) + } +} + +const encodedAST_ = (ast: AST, isBound: boolean): AST => { + switch (ast._tag) { + case "Declaration": { + const typeParameters = changeMap(ast.typeParameters, (ast) => encodedAST_(ast, isBound)) + return typeParameters === ast.typeParameters ? + ast : + new Declaration(typeParameters, ast.decodeUnknown, ast.encodeUnknown) + } + case "TupleType": { + const elements = changeMap(ast.elements, (e) => { + const type = encodedAST_(e.type, isBound) + return type === e.type ? e : new OptionalType(type, e.isOptional) + }) + const restASTs = getRestASTs(ast.rest) + const rest = changeMap(restASTs, (ast) => encodedAST_(ast, isBound)) + return elements === ast.elements && rest === restASTs ? + ast : + new TupleType(elements, rest.map((ast) => new Type(ast)), ast.isReadonly) + } + case "TypeLiteral": { + const propertySignatures = changeMap(ast.propertySignatures, (ps) => { + const type = encodedAST_(ps.type, isBound) + return type === ps.type + ? ps + : new PropertySignature(ps.name, type, ps.isOptional, ps.isReadonly) + }) + const indexSignatures = changeMap(ast.indexSignatures, (is) => { + const type = encodedAST_(is.type, isBound) + return type === is.type ? is : new IndexSignature(is.parameter, type, is.isReadonly) + }) + return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? + ast : + new TypeLiteral(propertySignatures, indexSignatures) + } + case "Union": { + const types = changeMap(ast.types, (ast) => encodedAST_(ast, isBound)) + return types === ast.types ? ast : Union.make(types) + } + case "Suspend": { + let borrowedAnnotations = undefined + const identifier = getJSONIdentifier(ast) + if (Option.isSome(identifier)) { + const suffix = isBound ? "Bound" : "" + borrowedAnnotations = { [JSONIdentifierAnnotationId]: `${identifier.value}Encoded${suffix}` } + } + return new Suspend(() => encodedAST_(ast.f(), isBound), borrowedAnnotations) + } + case "Refinement": { + const from = encodedAST_(ast.from, isBound) + if (isBound) { + if (from === ast.from) return ast + if (getTransformationFrom(ast.from) === undefined && hasStableFilter(ast)) { + return new Refinement(from, ast.filter, ast.annotations) + } + return from + } else { + return from + } + } + case "Transformation": + return encodedAST_(ast.from, isBound) + } + return ast +} + +/** + * @since 3.10.0 + */ +export const encodedAST = (ast: AST): AST => encodedAST_(ast, false) + +/** + * @since 3.10.0 + */ +export const encodedBoundAST = (ast: AST): AST => encodedAST_(ast, true) + +const toJSONAnnotations = (annotations: Annotations): object => { + const out: Record = {} + for (const k of Object.getOwnPropertySymbols(annotations)) { + out[String(k)] = annotations[k] + } + return out +} + +/** @internal */ +export const getEncodedParameter = ( + ast: Parameter +): StringKeyword | SymbolKeyword | TemplateLiteral => { + switch (ast._tag) { + case "StringKeyword": + case "SymbolKeyword": + case "TemplateLiteral": + return ast + case "Refinement": + return getEncodedParameter(ast.from) + } +} + +/** @internal */ +export const equals = (self: AST, that: AST): boolean => { + switch (self._tag) { + case "Literal": + return isLiteral(that) && that.literal === self.literal + case "UniqueSymbol": + return isUniqueSymbol(that) && that.symbol === self.symbol + case "UndefinedKeyword": + case "VoidKeyword": + case "NeverKeyword": + case "UnknownKeyword": + case "AnyKeyword": + case "StringKeyword": + case "NumberKeyword": + case "BooleanKeyword": + case "BigIntKeyword": + case "SymbolKeyword": + case "ObjectKeyword": + return that._tag === self._tag + case "TemplateLiteral": + return isTemplateLiteral(that) && that.head === self.head && equalsTemplateLiteralSpan(that.spans, self.spans) + case "Enums": + return isEnums(that) && equalsEnums(that.enums, self.enums) + case "Union": + return isUnion(that) && equalsUnion(self.types, that.types) + case "Refinement": + case "TupleType": + case "TypeLiteral": + case "Suspend": + case "Transformation": + case "Declaration": + return self === that + } +} + +const equalsTemplateLiteralSpan = Arr.getEquivalence((self, that): boolean => { + return self.literal === that.literal && equals(self.type, that.type) +}) + +const equalsEnums = Arr.getEquivalence((self, that) => + that[0] === self[0] && that[1] === self[1] +) + +const equalsUnion = Arr.getEquivalence(equals) + +const intersection = Arr.intersectionWith(equals) + +const _keyof = (ast: AST): Array => { + switch (ast._tag) { + case "Declaration": { + const annotation = getSurrogateAnnotation(ast) + if (Option.isSome(annotation)) { + return _keyof(annotation.value) + } + break + } + case "TypeLiteral": + return ast.propertySignatures.map((p): AST => + Predicate.isSymbol(p.name) ? new UniqueSymbol(p.name) : new Literal(p.name) + ).concat(ast.indexSignatures.map((is) => getEncodedParameter(is.parameter))) + case "Suspend": + return _keyof(ast.f()) + case "Union": + return ast.types.slice(1).reduce( + (out: Array, ast) => intersection(out, _keyof(ast)), + _keyof(ast.types[0]) + ) + case "Transformation": + return _keyof(ast.to) + } + throw new Error(errors_.getASTUnsupportedSchemaErrorMessage(ast)) +} + +/** @internal */ +export const compose = (ab: AST, cd: AST): AST => new Transformation(ab, cd, composeTransformation) + +/** @internal */ +export const rename = (ast: AST, mapping: { readonly [K in PropertyKey]?: PropertyKey }): AST => { + switch (ast._tag) { + case "TypeLiteral": { + const propertySignatureTransformations: Array = [] + for (const key of Reflect.ownKeys(mapping)) { + const name = mapping[key] + if (name !== undefined) { + propertySignatureTransformations.push( + new PropertySignatureTransformation( + key, + name, + identity, + identity + ) + ) + } + } + if (propertySignatureTransformations.length === 0) { + return ast + } + return new Transformation( + ast, + new TypeLiteral( + ast.propertySignatures.map((ps) => { + const name = mapping[ps.name] + return new PropertySignature( + name === undefined ? ps.name : name, + typeAST(ps.type), + ps.isOptional, + ps.isReadonly, + ps.annotations + ) + }), + ast.indexSignatures + ), + new TypeLiteralTransformation(propertySignatureTransformations) + ) + } + case "Union": + return Union.make(ast.types.map((ast) => rename(ast, mapping))) + case "Suspend": + return new Suspend(() => rename(ast.f(), mapping)) + case "Transformation": + return compose(ast, rename(typeAST(ast), mapping)) + } + throw new Error(errors_.getASTUnsupportedRenameSchemaErrorMessage(ast)) +} + +const formatKeyword = (ast: AST): string => Option.getOrElse(getExpected(ast), () => ast._tag) + +function getBrands(ast: Annotated): string { + return Option.match(getBrandAnnotation(ast), { + onNone: () => "", + onSome: (brands) => brands.map((brand) => ` & Brand<${Inspectable.formatUnknown(brand)}>`).join("") + }) +} + +const getOrElseExpected = (ast: Annotated): Option.Option => + getTitleAnnotation(ast).pipe( + Option.orElse(() => getDescriptionAnnotation(ast)), + Option.orElse(() => getAutoTitleAnnotation(ast)), + Option.map((s) => s + getBrands(ast)) + ) + +const getExpected = (ast: Annotated): Option.Option => + Option.orElse(getIdentifierAnnotation(ast), () => getOrElseExpected(ast)) + +/** @internal */ +export const pruneUndefined = ( + ast: AST, + self: (ast: AST) => AST | undefined, + onTransformation: (ast: Transformation) => AST | undefined +): AST | undefined => { + switch (ast._tag) { + case "UndefinedKeyword": + return neverKeyword + case "Union": { + const types: Array = [] + let hasUndefined = false + for (const type of ast.types) { + const pruned = self(type) + if (pruned) { + hasUndefined = true + if (!isNeverKeyword(pruned)) { + types.push(pruned) + } + } else { + types.push(type) + } + } + if (hasUndefined) { + return Union.make(types) + } + break + } + case "Suspend": + return self(ast.f()) + case "Transformation": + return onTransformation(ast) + } +} diff --git a/backend/node_modules/effect/src/Secret.ts b/backend/node_modules/effect/src/Secret.ts new file mode 100644 index 0000000000000000000000000000000000000000..c19270fbe3d50256a8bfa23de164cda997daeb07 --- /dev/null +++ b/backend/node_modules/effect/src/Secret.ts @@ -0,0 +1,88 @@ +/** + * @since 2.0.0 + * @deprecated + */ +import type * as Equal from "./Equal.js" +import * as InternalSecret from "./internal/secret.js" +import type * as Redacted from "./Redacted.js" + +/** + * @since 2.0.0 + * @category symbols + * @deprecated + */ +export const SecretTypeId: unique symbol = InternalSecret.SecretTypeId + +/** + * @since 2.0.0 + * @category symbols + * @deprecated + */ +export type SecretTypeId = typeof SecretTypeId + +/** + * @since 2.0.0 + * @category models + * @deprecated + */ +export interface Secret extends Redacted.Redacted, Secret.Proto, Equal.Equal { + /** @internal */ + readonly raw: Array +} + +/** + * @since 2.0.0 + * @deprecated + */ +export declare namespace Secret { + /** + * @since 2.0.0 + * @category models + * @deprecated + */ + export interface Proto { + readonly [SecretTypeId]: SecretTypeId + } +} + +/** + * @since 2.0.0 + * @category refinements + * @deprecated + */ +export const isSecret: (u: unknown) => u is Secret = InternalSecret.isSecret + +/** + * @since 2.0.0 + * @category constructors + * @deprecated + */ +export const make: (bytes: Array) => Secret = InternalSecret.make + +/** + * @since 2.0.0 + * @category constructors + * @deprecated + */ +export const fromIterable: (iterable: Iterable) => Secret = InternalSecret.fromIterable + +/** + * @since 2.0.0 + * @category constructors + * @deprecated + */ +export const fromString: (text: string) => Secret = InternalSecret.fromString + +/** + * @since 2.0.0 + * @category getters + * @deprecated + */ +export const value: (self: Secret) => string = InternalSecret.value + +/** + * @since 2.0.0 + * @category unsafe + * @deprecated + */ +export const unsafeWipe: (self: Secret) => void = InternalSecret.unsafeWipe diff --git a/backend/node_modules/effect/src/Sink.ts b/backend/node_modules/effect/src/Sink.ts new file mode 100644 index 0000000000000000000000000000000000000000..a391409f03470ae36a584236ba3d705e04d376ca --- /dev/null +++ b/backend/node_modules/effect/src/Sink.ts @@ -0,0 +1,1877 @@ +/** + * @since 2.0.0 + */ +import type * as Cause from "./Cause.js" +import type * as Channel from "./Channel.js" +import type * as Chunk from "./Chunk.js" +import type * as Context from "./Context.js" +import type * as Duration from "./Duration.js" +import type * as Effect from "./Effect.js" +import type * as Either from "./Either.js" +import type * as Exit from "./Exit.js" +import type { LazyArg } from "./Function.js" +import type * as HashMap from "./HashMap.js" +import type * as HashSet from "./HashSet.js" +import * as internal from "./internal/sink.js" +import type * as MergeDecision from "./MergeDecision.js" +import type * as Option from "./Option.js" +import type { Pipeable } from "./Pipeable.js" +import type { Predicate, Refinement } from "./Predicate.js" +import type * as PubSub from "./PubSub.js" +import type * as Queue from "./Queue.js" +import type * as Scope from "./Scope.js" +import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const SinkTypeId: unique symbol = internal.SinkTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type SinkTypeId = typeof SinkTypeId + +/** + * A `Sink` is used to consume elements produced by a `Stream`. + * You can think of a sink as a function that will consume a variable amount of + * `In` elements (could be 0, 1, or many), might fail with an error of type `E`, + * and will eventually yield a value of type `A` together with a remainder of + * type `L` (i.e. any leftovers). + * + * @since 2.0.0 + * @category models + */ +export interface Sink + extends Sink.Variance, Pipeable +{} + +/** + * @since 2.0.0 + * @category models + */ +export interface SinkUnify extends Effect.EffectUnify { + Sink?: () => A[Unify.typeSymbol] extends + | Sink< + infer A, + infer In, + infer L, + infer E, + infer R + > + | infer _ ? Sink + : never +} + +/** + * @category models + * @since 2.0.0 + */ +export interface SinkUnifyIgnore extends Effect.EffectUnifyIgnore { + Sink?: true +} + +/** + * @since 2.0.0 + * @category models + */ +declare module "./Effect.js" { + interface Effect extends Sink {} + interface EffectUnifyIgnore { + Sink?: true + } +} + +/** + * @since 2.0.0 + */ +export declare namespace Sink { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [SinkTypeId]: VarianceStruct + } + /** + * @since 2.0.0 + * @category models + */ + export interface VarianceStruct { + _A: Types.Covariant + _In: Types.Contravariant + _L: Types.Covariant + _E: Types.Covariant + _R: Types.Covariant + } +} + +/** + * Replaces this sink's result with the provided value. + * + * @since 2.0.0 + * @category mapping + */ +export const as: { + /** + * Replaces this sink's result with the provided value. + * + * @since 2.0.0 + * @category mapping + */ + (a: A2): (self: Sink) => Sink + /** + * Replaces this sink's result with the provided value. + * + * @since 2.0.0 + * @category mapping + */ + (self: Sink, a: A2): Sink +} = internal.as + +/** + * A sink that collects all elements into a `Chunk`. + * + * @since 2.0.0 + * @category constructors + */ +export const collectAll: () => Sink, In> = internal.collectAll + +/** + * A sink that collects first `n` elements into a chunk. + * + * @since 2.0.0 + * @category constructors + */ +export const collectAllN: (n: number) => Sink, In, In> = internal.collectAllN + +/** + * Repeatedly runs the sink and accumulates its results into a `Chunk`. + * + * @since 2.0.0 + * @category utils + */ +export const collectAllFrom: ( + self: Sink +) => Sink, In, L, E, R> = internal.collectAllFrom + +/** + * A sink that collects all of its inputs into a map. The keys are extracted + * from inputs using the keying function `key`; if multiple inputs use the + * same key, they are merged using the `merge` function. + * + * @since 2.0.0 + * @category constructors + */ +export const collectAllToMap: ( + key: (input: In) => K, + merge: (x: In, y: In) => In +) => Sink, In> = internal.collectAllToMap + +/** + * A sink that collects first `n` keys into a map. The keys are calculated + * from inputs using the keying function `key`; if multiple inputs use the the + * same key, they are merged using the `merge` function. + * + * @since 2.0.0 + * @category constructors + */ +export const collectAllToMapN: ( + n: number, + key: (input: In) => K, + merge: (x: In, y: In) => In +) => Sink, In, In> = internal.collectAllToMapN + +/** + * A sink that collects all of its inputs into a set. + * + * @since 2.0.0 + * @category constructors + */ +export const collectAllToSet: () => Sink, In> = internal.collectAllToSet + +/** + * A sink that collects first `n` distinct inputs into a set. + * + * @since 2.0.0 + * @category constructors + */ +export const collectAllToSetN: (n: number) => Sink, In, In> = internal.collectAllToSetN + +/** + * Accumulates incoming elements into a chunk until predicate `p` is + * satisfied. + * + * @since 2.0.0 + * @category constructors + */ +export const collectAllUntil: (p: Predicate) => Sink, In, In> = internal.collectAllUntil + +/** + * Accumulates incoming elements into a chunk until effectful predicate `p` is + * satisfied. + * + * @since 2.0.0 + * @category constructors + */ +export const collectAllUntilEffect: ( + p: (input: In) => Effect.Effect +) => Sink, In, In, E, R> = internal.collectAllUntilEffect + +/** + * Accumulates incoming elements into a chunk as long as they verify predicate + * `p`. + * + * @since 2.0.0 + * @category constructors + */ +export const collectAllWhile: { + /** + * Accumulates incoming elements into a chunk as long as they verify predicate + * `p`. + * + * @since 2.0.0 + * @category constructors + */ + (refinement: Refinement): Sink, In, In> + /** + * Accumulates incoming elements into a chunk as long as they verify predicate + * `p`. + * + * @since 2.0.0 + * @category constructors + */ + (predicate: Predicate): Sink, In, In> +} = internal.collectAllWhile + +/** + * Accumulates incoming elements into a chunk as long as they verify effectful + * predicate `p`. + * + * @since 2.0.0 + * @category constructors + */ +export const collectAllWhileEffect: ( + predicate: (input: In) => Effect.Effect +) => Sink, In, In, E, R> = internal.collectAllWhileEffect + +/** + * Repeatedly runs the sink for as long as its results satisfy the predicate + * `p`. The sink's results will be accumulated using the stepping function `f`. + * + * @since 2.0.0 + * @category utils + */ +export const collectAllWhileWith: { + /** + * Repeatedly runs the sink for as long as its results satisfy the predicate + * `p`. The sink's results will be accumulated using the stepping function `f`. + * + * @since 2.0.0 + * @category utils + */ + ( + options: { readonly initial: S; readonly while: Predicate; readonly body: (s: S, a: A) => S } + ): (self: Sink) => Sink + /** + * Repeatedly runs the sink for as long as its results satisfy the predicate + * `p`. The sink's results will be accumulated using the stepping function `f`. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Sink, + options: { readonly initial: S; readonly while: Predicate; readonly body: (s: S, a: A) => S } + ): Sink +} = internal.collectAllWhileWith as any + +/** + * Collects the leftovers from the stream when the sink succeeds and returns + * them as part of the sink's result. + * + * @since 2.0.0 + * @category utils + */ +export const collectLeftover: ( + self: Sink +) => Sink<[A, Chunk.Chunk], In, never, E, R> = internal.collectLeftover + +/** + * Transforms this sink's input elements. + * + * @since 2.0.0 + * @category mapping + */ +export const mapInput: { + /** + * Transforms this sink's input elements. + * + * @since 2.0.0 + * @category mapping + */ + (f: (input: In0) => In): (self: Sink) => Sink + /** + * Transforms this sink's input elements. + * + * @since 2.0.0 + * @category mapping + */ + (self: Sink, f: (input: In0) => In): Sink +} = internal.mapInput + +/** + * Effectfully transforms this sink's input elements. + * + * @since 2.0.0 + * @category mapping + */ +export const mapInputEffect: { + /** + * Effectfully transforms this sink's input elements. + * + * @since 2.0.0 + * @category mapping + */ + (f: (input: In0) => Effect.Effect): (self: Sink) => Sink + /** + * Effectfully transforms this sink's input elements. + * + * @since 2.0.0 + * @category mapping + */ + (self: Sink, f: (input: In0) => Effect.Effect): Sink +} = internal.mapInputEffect + +/** + * Transforms this sink's input chunks. `f` must preserve chunking-invariance. + * + * @since 2.0.0 + * @category mapping + */ +export const mapInputChunks: { + /** + * Transforms this sink's input chunks. `f` must preserve chunking-invariance. + * + * @since 2.0.0 + * @category mapping + */ + (f: (chunk: Chunk.Chunk) => Chunk.Chunk): (self: Sink) => Sink + /** + * Transforms this sink's input chunks. `f` must preserve chunking-invariance. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Sink, + f: (chunk: Chunk.Chunk) => Chunk.Chunk + ): Sink +} = internal.mapInputChunks + +/** + * Effectfully transforms this sink's input chunks. `f` must preserve + * chunking-invariance. + * + * @since 2.0.0 + * @category mapping + */ +export const mapInputChunksEffect: { + /** + * Effectfully transforms this sink's input chunks. `f` must preserve + * chunking-invariance. + * + * @since 2.0.0 + * @category mapping + */ + (f: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2>): (self: Sink) => Sink + /** + * Effectfully transforms this sink's input chunks. `f` must preserve + * chunking-invariance. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Sink, + f: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2> + ): Sink +} = internal.mapInputChunksEffect + +/** + * A sink that counts the number of elements fed to it. + * + * @since 2.0.0 + * @category constructors + */ +export const count: Sink = internal.count + +/** + * Creates a sink halting with the specified defect. + * + * @since 2.0.0 + * @category constructors + */ +export const die: (defect: unknown) => Sink = internal.die + +/** + * Creates a sink halting with the specified message, wrapped in a + * `RuntimeException`. + * + * @since 2.0.0 + * @category constructors + */ +export const dieMessage: (message: string) => Sink = internal.dieMessage + +/** + * Creates a sink halting with the specified defect. + * + * @since 2.0.0 + * @category constructors + */ +export const dieSync: (evaluate: LazyArg) => Sink = internal.dieSync + +/** + * Transforms both inputs and result of this sink using the provided + * functions. + * + * @since 2.0.0 + * @category mapping + */ +export const dimap: { + /** + * Transforms both inputs and result of this sink using the provided + * functions. + * + * @since 2.0.0 + * @category mapping + */ + ( + options: { readonly onInput: (input: In0) => In; readonly onDone: (a: A) => A2 } + ): (self: Sink) => Sink + /** + * Transforms both inputs and result of this sink using the provided + * functions. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Sink, + options: { readonly onInput: (input: In0) => In; readonly onDone: (a: A) => A2 } + ): Sink +} = internal.dimap + +/** + * Effectfully transforms both inputs and result of this sink using the + * provided functions. + * + * @since 2.0.0 + * @category mapping + */ +export const dimapEffect: { + /** + * Effectfully transforms both inputs and result of this sink using the + * provided functions. + * + * @since 2.0.0 + * @category mapping + */ + ( + options: { + readonly onInput: (input: In0) => Effect.Effect + readonly onDone: (a: A) => Effect.Effect + } + ): (self: Sink) => Sink + /** + * Effectfully transforms both inputs and result of this sink using the + * provided functions. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Sink, + options: { + readonly onInput: (input: In0) => Effect.Effect + readonly onDone: (a: A) => Effect.Effect + } + ): Sink +} = internal.dimapEffect + +/** + * Transforms both input chunks and result of this sink using the provided + * functions. + * + * @since 2.0.0 + * @category mapping + */ +export const dimapChunks: { + /** + * Transforms both input chunks and result of this sink using the provided + * functions. + * + * @since 2.0.0 + * @category mapping + */ + ( + options: { readonly onInput: (chunk: Chunk.Chunk) => Chunk.Chunk; readonly onDone: (a: A) => A2 } + ): (self: Sink) => Sink + /** + * Transforms both input chunks and result of this sink using the provided + * functions. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Sink, + options: { readonly onInput: (chunk: Chunk.Chunk) => Chunk.Chunk; readonly onDone: (a: A) => A2 } + ): Sink +} = internal.dimapChunks + +/** + * Effectfully transforms both input chunks and result of this sink using the + * provided functions. `f` and `g` must preserve chunking-invariance. + * + * @since 2.0.0 + * @category mapping + */ +export const dimapChunksEffect: { + /** + * Effectfully transforms both input chunks and result of this sink using the + * provided functions. `f` and `g` must preserve chunking-invariance. + * + * @since 2.0.0 + * @category mapping + */ + ( + options: { + readonly onInput: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2> + readonly onDone: (a: A) => Effect.Effect + } + ): (self: Sink) => Sink + /** + * Effectfully transforms both input chunks and result of this sink using the + * provided functions. `f` and `g` must preserve chunking-invariance. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Sink, + options: { + readonly onInput: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2> + readonly onDone: (a: A) => Effect.Effect + } + ): Sink +} = internal.dimapChunksEffect + +/** + * A sink that ignores its inputs. + * + * @since 2.0.0 + * @category constructors + */ +export const drain: Sink = internal.drain + +/** + * Creates a sink that drops `n` elements. + * + * @since 2.0.0 + * @category constructors + */ +export const drop: (n: number) => Sink = internal.drop + +/** + * Drops incoming elements until the predicate is satisfied. + * + * @since 2.0.0 + * @category constructors + */ +export const dropUntil: (predicate: Predicate) => Sink = internal.dropUntil + +/** + * Drops incoming elements until the effectful predicate is satisfied. + * + * @since 2.0.0 + * @category constructors + */ +export const dropUntilEffect: ( + predicate: (input: In) => Effect.Effect +) => Sink = internal.dropUntilEffect + +/** + * Drops incoming elements as long as the predicate is satisfied. + * + * @since 2.0.0 + * @category constructors + */ +export const dropWhile: (predicate: Predicate) => Sink = internal.dropWhile + +/** + * Drops incoming elements as long as the effectful predicate is satisfied. + * + * @since 2.0.0 + * @category constructors + */ +export const dropWhileEffect: ( + predicate: (input: In) => Effect.Effect +) => Sink = internal.dropWhileEffect + +/** + * Returns a new sink with an attached finalizer. The finalizer is guaranteed + * to be executed so long as the sink begins execution (and regardless of + * whether or not it completes). + * + * @since 2.0.0 + * @category finalization + */ +export const ensuring: { + /** + * Returns a new sink with an attached finalizer. The finalizer is guaranteed + * to be executed so long as the sink begins execution (and regardless of + * whether or not it completes). + * + * @since 2.0.0 + * @category finalization + */ + (finalizer: Effect.Effect): (self: Sink) => Sink + /** + * Returns a new sink with an attached finalizer. The finalizer is guaranteed + * to be executed so long as the sink begins execution (and regardless of + * whether or not it completes). + * + * @since 2.0.0 + * @category finalization + */ + (self: Sink, finalizer: Effect.Effect): Sink +} = internal.ensuring + +/** + * Returns a new sink with an attached finalizer. The finalizer is guaranteed + * to be executed so long as the sink begins execution (and regardless of + * whether or not it completes). + * + * @since 2.0.0 + * @category finalization + */ +export const ensuringWith: { + /** + * Returns a new sink with an attached finalizer. The finalizer is guaranteed + * to be executed so long as the sink begins execution (and regardless of + * whether or not it completes). + * + * @since 2.0.0 + * @category finalization + */ + (finalizer: (exit: Exit.Exit) => Effect.Effect): (self: Sink) => Sink + /** + * Returns a new sink with an attached finalizer. The finalizer is guaranteed + * to be executed so long as the sink begins execution (and regardless of + * whether or not it completes). + * + * @since 2.0.0 + * @category finalization + */ + ( + self: Sink, + finalizer: (exit: Exit.Exit) => Effect.Effect + ): Sink +} = internal.ensuringWith + +/** + * Accesses the whole context of the sink. + * + * @since 2.0.0 + * @category constructors + */ +export const context: () => Sink, unknown, never, never, R> = internal.context + +/** + * Accesses the context of the sink. + * + * @since 2.0.0 + * @category constructors + */ +export const contextWith: (f: (context: Context.Context) => Z) => Sink = + internal.contextWith + +/** + * Accesses the context of the sink in the context of an effect. + * + * @since 2.0.0 + * @category constructors + */ +export const contextWithEffect: ( + f: (context: Context.Context) => Effect.Effect +) => Sink = internal.contextWithEffect + +/** + * Accesses the context of the sink in the context of a sink. + * + * @since 2.0.0 + * @category constructors + */ +export const contextWithSink: ( + f: (context: Context.Context) => Sink +) => Sink = internal.contextWithSink + +/** + * A sink that returns whether all elements satisfy the specified predicate. + * + * @since 2.0.0 + * @category constructors + */ +export const every: (predicate: Predicate) => Sink = internal.every + +/** + * A sink that always fails with the specified error. + * + * @since 2.0.0 + * @category constructors + */ +export const fail: (e: E) => Sink = internal.fail + +/** + * A sink that always fails with the specified lazily evaluated error. + * + * @since 2.0.0 + * @category constructors + */ +export const failSync: (evaluate: LazyArg) => Sink = internal.failSync + +/** + * Creates a sink halting with a specified `Cause`. + * + * @since 2.0.0 + * @category constructors + */ +export const failCause: (cause: Cause.Cause) => Sink = internal.failCause + +/** + * Creates a sink halting with a specified lazily evaluated `Cause`. + * + * @since 2.0.0 + * @category constructors + */ +export const failCauseSync: (evaluate: LazyArg>) => Sink = + internal.failCauseSync + +/** + * Filters the sink's input with the given predicate. + * + * @since 2.0.0 + * @category filtering + */ +export const filterInput: { + /** + * Filters the sink's input with the given predicate. + * + * @since 2.0.0 + * @category filtering + */ + (f: Refinement): (self: Sink) => Sink + /** + * Filters the sink's input with the given predicate. + * + * @since 2.0.0 + * @category filtering + */ + (f: Predicate): (self: Sink) => Sink +} = internal.filterInput + +/** + * Effectfully filter the input of this sink using the specified predicate. + * + * @since 2.0.0 + * @category filtering + */ +export const filterInputEffect: { + /** + * Effectfully filter the input of this sink using the specified predicate. + * + * @since 2.0.0 + * @category filtering + */ + (f: (input: In1) => Effect.Effect): (self: Sink) => Sink + /** + * Effectfully filter the input of this sink using the specified predicate. + * + * @since 2.0.0 + * @category filtering + */ + ( + self: Sink, + f: (input: In1) => Effect.Effect + ): Sink +} = internal.filterInputEffect + +/** + * Creates a sink that produces values until one verifies the predicate `f`. + * + * @since 2.0.0 + * @category elements + */ +export const findEffect: { + /** + * Creates a sink that produces values until one verifies the predicate `f`. + * + * @since 2.0.0 + * @category elements + */ + (f: (a: A) => Effect.Effect): (self: Sink) => Sink, In, L, E2 | E, R2 | R> + /** + * Creates a sink that produces values until one verifies the predicate `f`. + * + * @since 2.0.0 + * @category elements + */ + (self: Sink, f: (a: A) => Effect.Effect): Sink, In, L, E | E2, R | R2> +} = internal.findEffect as any + +/** + * A sink that folds its inputs with the provided function, termination + * predicate and initial state. + * + * @since 2.0.0 + * @category folding + */ +export const fold: (s: S, contFn: Predicate, f: (s: S, input: In) => S) => Sink = internal.fold + +/** + * Folds over the result of the sink + * + * @since 2.0.0 + * @category folding + */ +export const foldSink: { + /** + * Folds over the result of the sink + * + * @since 2.0.0 + * @category folding + */ + ( + options: { + readonly onFailure: (err: E) => Sink + readonly onSuccess: (a: A) => Sink + } + ): (self: Sink) => Sink + /** + * Folds over the result of the sink + * + * @since 2.0.0 + * @category folding + */ + ( + self: Sink, + options: { + readonly onFailure: (err: E) => Sink + readonly onSuccess: (a: A) => Sink + } + ): Sink +} = internal.foldSink + +/** + * A sink that folds its input chunks with the provided function, termination + * predicate and initial state. `contFn` condition is checked only for the + * initial value and at the end of processing of each chunk. `f` and `contFn` + * must preserve chunking-invariance. + * + * @since 2.0.0 + * @category constructors + */ +export const foldChunks: ( + s: S, + contFn: Predicate, + f: (s: S, chunk: Chunk.Chunk) => S +) => Sink = internal.foldChunks + +/** + * A sink that effectfully folds its input chunks with the provided function, + * termination predicate and initial state. `contFn` condition is checked only + * for the initial value and at the end of processing of each chunk. `f` and + * `contFn` must preserve chunking-invariance. + * + * @since 2.0.0 + * @category constructors + */ +export const foldChunksEffect: ( + s: S, + contFn: Predicate, + f: (s: S, chunk: Chunk.Chunk) => Effect.Effect +) => Sink = internal.foldChunksEffect + +/** + * A sink that effectfully folds its inputs with the provided function, + * termination predicate and initial state. + * + * @since 2.0.0 + * @category constructors + */ +export const foldEffect: ( + s: S, + contFn: Predicate, + f: (s: S, input: In) => Effect.Effect +) => Sink = internal.foldEffect + +/** + * A sink that folds its inputs with the provided function and initial state. + * + * @since 2.0.0 + * @category constructors + */ +export const foldLeft: (s: S, f: (s: S, input: In) => S) => Sink = internal.foldLeft + +/** + * A sink that folds its input chunks with the provided function and initial + * state. `f` must preserve chunking-invariance. + * + * @since 2.0.0 + * @category constructors + */ +export const foldLeftChunks: (s: S, f: (s: S, chunk: Chunk.Chunk) => S) => Sink = + internal.foldLeftChunks + +/** + * A sink that effectfully folds its input chunks with the provided function + * and initial state. `f` must preserve chunking-invariance. + * + * @since 2.0.0 + * @category constructors + */ +export const foldLeftChunksEffect: ( + s: S, + f: (s: S, chunk: Chunk.Chunk) => Effect.Effect +) => Sink = internal.foldLeftChunksEffect + +/** + * A sink that effectfully folds its inputs with the provided function and + * initial state. + * + * @since 2.0.0 + * @category constructors + */ +export const foldLeftEffect: ( + s: S, + f: (s: S, input: In) => Effect.Effect +) => Sink = internal.foldLeftEffect + +/** + * Creates a sink that folds elements of type `In` into a structure of type + * `S` until `max` elements have been folded. + * + * Like `Sink.foldWeighted`, but with a constant cost function of `1`. + * + * @since 2.0.0 + * @category constructors + */ +export const foldUntil: (s: S, max: number, f: (s: S, input: In) => S) => Sink = internal.foldUntil + +/** + * Creates a sink that effectfully folds elements of type `In` into a + * structure of type `S` until `max` elements have been folded. + * + * Like `Sink.foldWeightedEffect` but with a constant cost function of `1`. + * + * @since 2.0.0 + * @category constructors + */ +export const foldUntilEffect: ( + s: S, + max: number, + f: (s: S, input: In) => Effect.Effect +) => Sink = internal.foldUntilEffect + +/** + * Creates a sink that folds elements of type `In` into a structure of type `S`, + * until `max` worth of elements (determined by the `costFn`) have been folded. + * + * **Note** + * + * Elements that have an individual cost larger than `max` will force the sink + * to cross the `max` cost. See `Sink.foldWeightedDecompose` for a variant + * that can handle these cases. + * + * @since 2.0.0 + * @category constructors + */ +export const foldWeighted: ( + options: { + readonly initial: S + readonly maxCost: number + readonly cost: (s: S, input: In) => number + readonly body: (s: S, input: In) => S + } +) => Sink = internal.foldWeighted + +/** + * Creates a sink that folds elements of type `In` into a structure of type + * `S`, until `max` worth of elements (determined by the `costFn`) have been + * folded. + * + * The `decompose` function will be used for decomposing elements that cause + * an `S` aggregate to cross `max` into smaller elements. For example: + * + * ```ts skip-type-checking + * pipe( + * Stream.make(1, 5, 1), + * Stream.transduce( + * Sink.foldWeightedDecompose( + * Chunk.empty(), + * 4, + * (n: number) => n, + * (n: number) => Chunk.make(n - 1, 1), + * (acc, el) => pipe(acc, Chunk.append(el)) + * ) + * ), + * Stream.runCollect + * ) + * ``` + * + * The stream would emit the elements `Chunk(1), Chunk(4), Chunk(1, 1)`. + * + * Be vigilant with this function, it has to generate "simpler" values or the + * fold may never end. A value is considered indivisible if `decompose` yields + * the empty chunk or a single-valued chunk. In these cases, there is no other + * choice than to yield a value that will cross the threshold. + * + * `Sink.foldWeightedDecomposeEffect` allows the decompose function to return an + * effect value, and consequently it allows the sink to fail. + * + * @since 2.0.0 + * @category constructors + */ +export const foldWeightedDecompose: ( + options: { + readonly initial: S + readonly maxCost: number + readonly cost: (s: S, input: In) => number + readonly decompose: (input: In) => Chunk.Chunk + readonly body: (s: S, input: In) => S + } +) => Sink = internal.foldWeightedDecompose + +/** + * Creates a sink that effectfully folds elements of type `In` into a + * structure of type `S`, until `max` worth of elements (determined by the + * `costFn`) have been folded. + * + * The `decompose` function will be used for decomposing elements that cause + * an `S` aggregate to cross `max` into smaller elements. Be vigilant with + * this function, it has to generate "simpler" values or the fold may never + * end. A value is considered indivisible if `decompose` yields the empty + * chunk or a single-valued chunk. In these cases, there is no other choice + * than to yield a value that will cross the threshold. + * + * See `Sink.foldWeightedDecompose` for an example. + * + * @since 2.0.0 + * @category constructors + */ +export const foldWeightedDecomposeEffect: ( + options: { + readonly initial: S + readonly maxCost: number + readonly cost: (s: S, input: In) => Effect.Effect + readonly decompose: (input: In) => Effect.Effect, E2, R2> + readonly body: (s: S, input: In) => Effect.Effect + } +) => Sink = internal.foldWeightedDecomposeEffect + +/** + * Creates a sink that effectfully folds elements of type `In` into a + * structure of type `S`, until `max` worth of elements (determined by the + * `costFn`) have been folded. + * + * @note + * Elements that have an individual cost larger than `max` will force the + * sink to cross the `max` cost. See `Sink.foldWeightedDecomposeEffect` for + * a variant that can handle these cases. + * + * @since 2.0.0 + * @category constructors + */ +export const foldWeightedEffect: ( + options: { + readonly initial: S + readonly maxCost: number + readonly cost: (s: S, input: In) => Effect.Effect + readonly body: (s: S, input: In) => Effect.Effect + } +) => Sink = internal.foldWeightedEffect + +/** + * A sink that executes the provided effectful function for every element fed + * to it. + * + * @since 2.0.0 + * @category constructors + */ +export const forEach: (f: (input: In) => Effect.Effect) => Sink = + internal.forEach + +/** + * A sink that executes the provided effectful function for every chunk fed to + * it. + * + * @since 2.0.0 + * @category constructors + */ +export const forEachChunk: ( + f: (input: Chunk.Chunk) => Effect.Effect +) => Sink = internal.forEachChunk + +/** + * A sink that executes the provided effectful function for every chunk fed to + * it until `f` evaluates to `false`. + * + * @since 2.0.0 + * @category constructors + */ +export const forEachChunkWhile: ( + f: (input: Chunk.Chunk) => Effect.Effect +) => Sink = internal.forEachChunkWhile + +/** + * A sink that executes the provided effectful function for every element fed + * to it until `f` evaluates to `false`. + * + * @since 2.0.0 + * @category constructors + */ +export const forEachWhile: (f: (input: In) => Effect.Effect) => Sink = + internal.forEachWhile + +/** + * Runs this sink until it yields a result, then uses that result to create + * another sink from the provided function which will continue to run until it + * yields a result. + * + * This function essentially runs sinks in sequence. + * + * @since 2.0.0 + * @category sequencing + */ +export const flatMap: { + /** + * Runs this sink until it yields a result, then uses that result to create + * another sink from the provided function which will continue to run until it + * yields a result. + * + * This function essentially runs sinks in sequence. + * + * @since 2.0.0 + * @category sequencing + */ + (f: (a: A) => Sink): (self: Sink) => Sink + /** + * Runs this sink until it yields a result, then uses that result to create + * another sink from the provided function which will continue to run until it + * yields a result. + * + * This function essentially runs sinks in sequence. + * + * @since 2.0.0 + * @category sequencing + */ + (self: Sink, f: (a: A) => Sink): Sink +} = internal.flatMap + +/** + * Creates a sink from a `Channel`. + * + * @since 2.0.0 + * @category constructors + */ +export const fromChannel: ( + channel: Channel.Channel, Chunk.Chunk, E, never, A, unknown, R> +) => Sink = internal.fromChannel + +/** + * Creates a `Channel` from a Sink. + * + * @since 2.0.0 + * @category constructors + */ +export const toChannel: ( + self: Sink +) => Channel.Channel, Chunk.Chunk, E, never, A, unknown, R> = internal.toChannel + +/** + * Creates a single-value sink produced from an effect. + * + * @since 2.0.0 + * @category constructors + */ +export const fromEffect: (effect: Effect.Effect) => Sink = + internal.fromEffect + +/** + * Create a sink which publishes each element to the specified `PubSub`. + * + * If the `shutdown` parameter is `true`, the `PubSub` will be shutdown after + * the sink is evaluated (defaults to `false`). + * + * @since 2.0.0 + * @category constructors + */ +export const fromPubSub: ( + pubsub: PubSub.PubSub, + options?: { + readonly shutdown?: boolean | undefined + } +) => Sink = internal.fromPubSub + +/** + * Creates a sink from a chunk processing function. + * + * @since 2.0.0 + * @category constructors + */ +export const fromPush: ( + push: Effect.Effect< + (_: Option.Option>) => Effect.Effect, Chunk.Chunk], R>, + never, + R + > +) => Sink> = internal.fromPush + +/** + * Create a sink which enqueues each element into the specified queue. + * + * If the `shutdown` parameter is `true`, the queue will be shutdown after the + * sink is evaluated (defaults to `false`). + * + * @since 2.0.0 + * @category constructors + */ +export const fromQueue: ( + queue: Queue.Enqueue, + options?: { + readonly shutdown?: boolean | undefined + } +) => Sink = internal.fromQueue + +/** + * Creates a sink containing the first value. + * + * @since 2.0.0 + * @category constructors + */ +export const head: () => Sink, In, In> = internal.head + +/** + * Drains the remaining elements from the stream after the sink finishes + * + * @since 2.0.0 + * @category utils + */ +export const ignoreLeftover: (self: Sink) => Sink = + internal.ignoreLeftover + +/** + * Creates a sink containing the last value. + * + * @since 2.0.0 + * @category constructors + */ +export const last: () => Sink, In, In> = internal.last + +/** + * Creates a sink that does not consume any input but provides the given chunk + * as its leftovers + * + * @since 2.0.0 + * @category constructors + */ +export const leftover: (chunk: Chunk.Chunk) => Sink = internal.leftover + +/** + * Transforms this sink's result. + * + * @since 2.0.0 + * @category mapping + */ +export const map: { + /** + * Transforms this sink's result. + * + * @since 2.0.0 + * @category mapping + */ + (f: (a: A) => A2): (self: Sink) => Sink + /** + * Transforms this sink's result. + * + * @since 2.0.0 + * @category mapping + */ + (self: Sink, f: (a: A) => A2): Sink +} = internal.map + +/** + * Effectfully transforms this sink's result. + * + * @since 2.0.0 + * @category mapping + */ +export const mapEffect: { + /** + * Effectfully transforms this sink's result. + * + * @since 2.0.0 + * @category mapping + */ + (f: (a: A) => Effect.Effect): (self: Sink) => Sink + /** + * Effectfully transforms this sink's result. + * + * @since 2.0.0 + * @category mapping + */ + (self: Sink, f: (a: A) => Effect.Effect): Sink +} = internal.mapEffect + +/** + * Transforms the errors emitted by this sink using `f`. + * + * @since 2.0.0 + * @category mapping + */ +export const mapError: { + /** + * Transforms the errors emitted by this sink using `f`. + * + * @since 2.0.0 + * @category mapping + */ + (f: (error: E) => E2): (self: Sink) => Sink + /** + * Transforms the errors emitted by this sink using `f`. + * + * @since 2.0.0 + * @category mapping + */ + (self: Sink, f: (error: E) => E2): Sink +} = internal.mapError + +/** + * Transforms the leftovers emitted by this sink using `f`. + * + * @since 2.0.0 + * @category mapping + */ +export const mapLeftover: { + /** + * Transforms the leftovers emitted by this sink using `f`. + * + * @since 2.0.0 + * @category mapping + */ + (f: (leftover: L) => L2): (self: Sink) => Sink + /** + * Transforms the leftovers emitted by this sink using `f`. + * + * @since 2.0.0 + * @category mapping + */ + (self: Sink, f: (leftover: L) => L2): Sink +} = internal.mapLeftover + +/** + * Creates a sink which transforms it's inputs into a string. + * + * @since 2.0.0 + * @category constructors + */ +export const mkString: Sink = internal.mkString + +/** + * Creates a sink which never terminates. + * + * @since 2.0.0 + * @category constructors + */ +export const never: Sink = internal.never + +/** + * Switch to another sink in case of failure + * + * @since 2.0.0 + * @category error handling + */ +export const orElse: { + /** + * Switch to another sink in case of failure + * + * @since 2.0.0 + * @category error handling + */ + (that: LazyArg>): (self: Sink) => Sink + /** + * Switch to another sink in case of failure + * + * @since 2.0.0 + * @category error handling + */ + (self: Sink, that: LazyArg>): Sink +} = internal.orElse + +/** + * Provides the sink with its required context, which eliminates its + * dependency on `R`. + * + * @since 2.0.0 + * @category context + */ +export const provideContext: { + /** + * Provides the sink with its required context, which eliminates its + * dependency on `R`. + * + * @since 2.0.0 + * @category context + */ + (context: Context.Context): (self: Sink) => Sink + /** + * Provides the sink with its required context, which eliminates its + * dependency on `R`. + * + * @since 2.0.0 + * @category context + */ + (self: Sink, context: Context.Context): Sink +} = internal.provideContext + +/** + * Runs both sinks in parallel on the input, , returning the result or the + * error from the one that finishes first. + * + * @since 2.0.0 + * @category utils + */ +export const race: { + /** + * Runs both sinks in parallel on the input, , returning the result or the + * error from the one that finishes first. + * + * @since 2.0.0 + * @category utils + */ + (that: Sink): (self: Sink) => Sink + /** + * Runs both sinks in parallel on the input, , returning the result or the + * error from the one that finishes first. + * + * @since 2.0.0 + * @category utils + */ + (self: Sink, that: Sink): Sink +} = internal.race + +/** + * Runs both sinks in parallel on the input, returning the result or the error + * from the one that finishes first. + * + * @since 2.0.0 + * @category utils + */ +export const raceBoth: { + /** + * Runs both sinks in parallel on the input, returning the result or the error + * from the one that finishes first. + * + * @since 2.0.0 + * @category utils + */ + ( + that: Sink, + options?: { readonly capacity?: number | undefined } | undefined + ): (self: Sink) => Sink, In & In1, L1 | L, E1 | E, R1 | R> + /** + * Runs both sinks in parallel on the input, returning the result or the error + * from the one that finishes first. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Sink, + that: Sink, + options?: { readonly capacity?: number | undefined } | undefined + ): Sink, In & In1, L | L1, E | E1, R | R1> +} = internal.raceBoth + +/** + * Runs both sinks in parallel on the input, using the specified merge + * function as soon as one result or the other has been computed. + * + * @since 2.0.0 + * @category utils + */ +export const raceWith: { + /** + * Runs both sinks in parallel on the input, using the specified merge + * function as soon as one result or the other has been computed. + * + * @since 2.0.0 + * @category utils + */ + ( + options: { + readonly other: Sink + readonly onSelfDone: (exit: Exit.Exit) => MergeDecision.MergeDecision + readonly onOtherDone: (exit: Exit.Exit) => MergeDecision.MergeDecision + readonly capacity?: number | undefined + } + ): (self: Sink) => Sink + /** + * Runs both sinks in parallel on the input, using the specified merge + * function as soon as one result or the other has been computed. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Sink, + options: { + readonly other: Sink + readonly onSelfDone: (exit: Exit.Exit) => MergeDecision.MergeDecision + readonly onOtherDone: (exit: Exit.Exit) => MergeDecision.MergeDecision + readonly capacity?: number | undefined + } + ): Sink +} = internal.raceWith + +/** + * @since 2.0.0 + * @category error handling + */ +export const refineOrDie: { + /** + * @since 2.0.0 + * @category error handling + */ + (pf: (error: E) => Option.Option): (self: Sink) => Sink + /** + * @since 2.0.0 + * @category error handling + */ + (self: Sink, pf: (error: E) => Option.Option): Sink +} = internal.refineOrDie + +/** + * @since 2.0.0 + * @category error handling + */ +export const refineOrDieWith: { + /** + * @since 2.0.0 + * @category error handling + */ + (pf: (error: E) => Option.Option, f: (error: E) => unknown): (self: Sink) => Sink + /** + * @since 2.0.0 + * @category error handling + */ + ( + self: Sink, + pf: (error: E) => Option.Option, + f: (error: E) => unknown + ): Sink +} = internal.refineOrDieWith + +/** + * A sink that returns whether an element satisfies the specified predicate. + * + * @since 2.0.0 + * @category constructors + */ +export const some: (predicate: Predicate) => Sink = internal.some + +/** + * Splits the sink on the specified predicate, returning a new sink that + * consumes elements until an element after the first satisfies the specified + * predicate. + * + * @since 2.0.0 + * @category utils + */ +export const splitWhere: { + /** + * Splits the sink on the specified predicate, returning a new sink that + * consumes elements until an element after the first satisfies the specified + * predicate. + * + * @since 2.0.0 + * @category utils + */ + (f: Predicate): (self: Sink) => Sink + /** + * Splits the sink on the specified predicate, returning a new sink that + * consumes elements until an element after the first satisfies the specified + * predicate. + * + * @since 2.0.0 + * @category utils + */ + (self: Sink, f: Predicate): Sink +} = internal.splitWhere + +/** + * A sink that immediately ends with the specified value. + * + * @since 2.0.0 + * @category constructors + */ +export const succeed: (a: A) => Sink = internal.succeed + +/** + * A sink that sums incoming numeric values. + * + * @since 2.0.0 + * @category constructors + */ +export const sum: Sink = internal.sum + +/** + * Summarize a sink by running an effect when the sink starts and again when + * it completes. + * + * @since 2.0.0 + * @category utils + */ +export const summarized: { + /** + * Summarize a sink by running an effect when the sink starts and again when + * it completes. + * + * @since 2.0.0 + * @category utils + */ + (summary: Effect.Effect, f: (start: A2, end: A2) => A3): (self: Sink) => Sink<[A, A3], In, L, E2 | E, R2 | R> + /** + * Summarize a sink by running an effect when the sink starts and again when + * it completes. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Sink, + summary: Effect.Effect, + f: (start: A2, end: A2) => A3 + ): Sink<[A, A3], In, L, E | E2, R | R2> +} = internal.summarized + +/** + * Returns a lazily constructed sink that may require effects for its + * creation. + * + * @since 2.0.0 + * @category constructors + */ +export const suspend: (evaluate: LazyArg>) => Sink = + internal.suspend + +/** + * A sink that immediately ends with the specified lazy value. + * + * @since 2.0.0 + * @category constructors + */ +export const sync: (evaluate: LazyArg) => Sink = internal.sync + +/** + * A sink that takes the specified number of values. + * + * @since 2.0.0 + * @category constructors + */ +export const take: (n: number) => Sink, In, In> = internal.take + +/** + * @since 2.0.0 + * @category constructors + */ +export const timed: Sink = internal.timed + +/** + * Creates a sink produced from an effect. + * + * @since 2.0.0 + * @category constructors + */ +export const unwrap: ( + effect: Effect.Effect, E, R> +) => Sink = internal.unwrap + +/** + * Creates a sink produced from a scoped effect. + * + * @since 2.0.0 + * @category constructors + */ +export const unwrapScoped: ( + effect: Effect.Effect, E, R> +) => Sink> = internal.unwrapScoped + +/** + * Constructs a `Sink` from a function which receives a `Scope` and returns + * an effect that will result in a `Sink` if successful. + * + * @since 3.11.0 + * @category constructors + */ +export const unwrapScopedWith: ( + f: (scope: Scope.Scope) => Effect.Effect, E, R> +) => Sink = internal.unwrapScopedWith + +/** + * Returns the sink that executes this one and times its execution. + * + * @since 2.0.0 + * @category utils + */ +export const withDuration: ( + self: Sink +) => Sink<[A, Duration.Duration], In, L, E, R> = internal.withDuration + +/** + * Feeds inputs to this sink until it yields a result, then switches over to + * the provided sink until it yields a result, finally combining the two + * results into a tuple. + * + * @since 2.0.0 + * @category zipping + */ +export const zip: { + /** + * Feeds inputs to this sink until it yields a result, then switches over to + * the provided sink until it yields a result, finally combining the two + * results into a tuple. + * + * @since 2.0.0 + * @category zipping + */ + ( + that: Sink, + options?: { readonly concurrent?: boolean | undefined } | undefined + ): (self: Sink) => Sink<[A, A2], In & In2, L2 | L, E2 | E, R2 | R> + /** + * Feeds inputs to this sink until it yields a result, then switches over to + * the provided sink until it yields a result, finally combining the two + * results into a tuple. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Sink, + that: Sink, + options?: { readonly concurrent?: boolean | undefined } | undefined + ): Sink<[A, A2], In & In2, L | L2, E | E2, R | R2> +} = internal.zip + +/** + * Like `Sink.zip` but keeps only the result from this sink. + * + * @since 2.0.0 + * @category zipping + */ +export const zipLeft: { + /** + * Like `Sink.zip` but keeps only the result from this sink. + * + * @since 2.0.0 + * @category zipping + */ + ( + that: Sink, + options?: { readonly concurrent?: boolean | undefined } | undefined + ): (self: Sink) => Sink + /** + * Like `Sink.zip` but keeps only the result from this sink. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Sink, + that: Sink, + options?: { readonly concurrent?: boolean | undefined } | undefined + ): Sink +} = internal.zipLeft + +/** + * Like `Sink.zip` but keeps only the result from `that` sink. + * + * @since 2.0.0 + * @category zipping + */ +export const zipRight: { + /** + * Like `Sink.zip` but keeps only the result from `that` sink. + * + * @since 2.0.0 + * @category zipping + */ + ( + that: Sink, + options?: { readonly concurrent?: boolean | undefined } | undefined + ): (self: Sink) => Sink + /** + * Like `Sink.zip` but keeps only the result from `that` sink. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Sink, + that: Sink, + options?: { readonly concurrent?: boolean | undefined } | undefined + ): Sink +} = internal.zipRight + +/** + * Feeds inputs to this sink until it yields a result, then switches over to + * the provided sink until it yields a result, finally combining the two + * results with `f`. + * + * @since 2.0.0 + * @category zipping + */ +export const zipWith: { + /** + * Feeds inputs to this sink until it yields a result, then switches over to + * the provided sink until it yields a result, finally combining the two + * results with `f`. + * + * @since 2.0.0 + * @category zipping + */ + ( + that: Sink, + f: (a: A, a2: A2) => A3, + options?: { readonly concurrent?: boolean | undefined } | undefined + ): (self: Sink) => Sink + /** + * Feeds inputs to this sink until it yields a result, then switches over to + * the provided sink until it yields a result, finally combining the two + * results with `f`. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Sink, + that: Sink, + f: (a: A, a2: A2) => A3, + options?: { readonly concurrent?: boolean | undefined } | undefined + ): Sink +} = internal.zipWith diff --git a/backend/node_modules/effect/src/SortedSet.ts b/backend/node_modules/effect/src/SortedSet.ts new file mode 100644 index 0000000000000000000000000000000000000000..d895dfe9adc1759672f0a45b460ef20f42b72086 --- /dev/null +++ b/backend/node_modules/effect/src/SortedSet.ts @@ -0,0 +1,530 @@ +/** + * @since 2.0.0 + */ +import * as Equal from "./Equal.js" +import type * as Equivalence from "./Equivalence.js" +import * as Dual from "./Function.js" +import { pipe } from "./Function.js" +import * as Hash from "./Hash.js" +import type { Inspectable } from "./Inspectable.js" +import { format, NodeInspectSymbol, toJSON } from "./Inspectable.js" +import type { Order } from "./Order.js" +import type { Pipeable } from "./Pipeable.js" +import { pipeArguments } from "./Pipeable.js" +import type { Predicate } from "./Predicate.js" +import { hasProperty } from "./Predicate.js" +import * as RBT from "./RedBlackTree.js" +import type { Invariant, NoInfer } from "./Types.js" + +const TypeId: unique symbol = Symbol.for("effect/SortedSet") + +/** + * @since 2.0.0 + * @category symbol + */ +export type TypeId = typeof TypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface SortedSet extends Iterable, Equal.Equal, Pipeable, Inspectable { + readonly [TypeId]: { + readonly _A: Invariant + } + /** @internal */ + readonly keyTree: RBT.RedBlackTree +} + +const SortedSetProto: Omit, "keyTree"> = { + [TypeId]: { + _A: (_: any) => _ + }, + [Hash.symbol](this: SortedSet): number { + return pipe( + Hash.hash(this.keyTree), + Hash.combine(Hash.hash(TypeId)), + Hash.cached(this) + ) + }, + [Equal.symbol](this: SortedSet, that: unknown): boolean { + return isSortedSet(that) && Equal.equals(this.keyTree, that.keyTree) + }, + [Symbol.iterator](this: SortedSet): Iterator { + return RBT.keys(this.keyTree) + }, + toString(this: SortedSet) { + return format(this.toJSON()) + }, + toJSON() { + return { + _id: "SortedSet", + values: Array.from(this).map(toJSON) + } + }, + [NodeInspectSymbol]() { + return this.toJSON() + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +const fromTree = (keyTree: RBT.RedBlackTree): SortedSet => { + const a = Object.create(SortedSetProto) + a.keyTree = keyTree + return a +} + +/** + * @since 2.0.0 + * @category refinements + */ +export const isSortedSet: { + /** + * @since 2.0.0 + * @category refinements + */ + (u: Iterable): u is SortedSet + /** + * @since 2.0.0 + * @category refinements + */ + (u: unknown): u is SortedSet +} = (u: unknown): u is SortedSet => hasProperty(u, TypeId) + +/** + * @since 2.0.0 + * @category constructors + */ +export const empty = (O: Order): SortedSet => fromTree(RBT.empty(O)) + +/** + * Creates a new `SortedSet` from an iterable collection of values. + * + * @since 2.0.0 + * @category constructors + */ +export const fromIterable: { + /** + * Creates a new `SortedSet` from an iterable collection of values. + * + * @since 2.0.0 + * @category constructors + */ + (ord: Order): (iterable: Iterable) => SortedSet + /** + * Creates a new `SortedSet` from an iterable collection of values. + * + * @since 2.0.0 + * @category constructors + */ + (iterable: Iterable, ord: Order): SortedSet +} = Dual.dual( + 2, + (iterable: Iterable, ord: Order): SortedSet => + fromTree(RBT.fromIterable(Array.from(iterable).map((k) => [k, true]), ord)) +) + +/** + * @since 2.0.0 + * @category constructors + */ +export const make = + (ord: Order) => >(...entries: Entries): SortedSet => + fromIterable(entries, ord) + +/** + * @since 2.0.0 + * @category elements + */ +export const add: { + /** + * @since 2.0.0 + * @category elements + */ + (value: A): (self: SortedSet) => SortedSet + /** + * @since 2.0.0 + * @category elements + */ + (self: SortedSet, value: A): SortedSet +} = Dual.dual< + (value: A) => (self: SortedSet) => SortedSet, + (self: SortedSet, value: A) => SortedSet +>(2, (self, value) => + RBT.has(self.keyTree, value) + ? self + : fromTree(RBT.insert(self.keyTree, value, true))) + +/** + * @since 2.0.0 + */ +export const difference: { + /** + * @since 2.0.0 + */ + (that: Iterable): (self: SortedSet) => SortedSet + /** + * @since 2.0.0 + */ + (self: SortedSet, that: Iterable): SortedSet +} = Dual.dual< + (that: Iterable) => (self: SortedSet) => SortedSet, + (self: SortedSet, that: Iterable) => SortedSet +>(2, (self: SortedSet, that: Iterable) => { + let out = self + for (const value of that) { + out = remove(out, value) + } + return out +}) + +/** + * Check if a predicate holds true for every `SortedSet` element. + * + * @since 2.0.0 + * @category elements + */ +export const every: { + /** + * Check if a predicate holds true for every `SortedSet` element. + * + * @since 2.0.0 + * @category elements + */ + (predicate: Predicate): (self: SortedSet) => boolean + /** + * Check if a predicate holds true for every `SortedSet` element. + * + * @since 2.0.0 + * @category elements + */ + (self: SortedSet, predicate: Predicate): boolean +} = Dual.dual(2, (self: SortedSet, predicate: Predicate): boolean => { + for (const value of self) { + if (!predicate(value)) { + return false + } + } + return true +}) + +/** + * @since 2.0.0 + * @category filtering + */ +export const filter: { + /** + * @since 2.0.0 + * @category filtering + */ + (predicate: Predicate): (self: SortedSet) => SortedSet + /** + * @since 2.0.0 + * @category filtering + */ + (self: SortedSet, predicate: Predicate): SortedSet +} = Dual.dual(2, (self: SortedSet, predicate: Predicate): SortedSet => { + const ord = RBT.getOrder(self.keyTree) + let out = empty(ord) + for (const value of self) { + if (predicate(value)) { + out = add(out, value) + } + } + return out +}) + +/** + * @since 2.0.0 + * @category sequencing + */ +export const flatMap: { + /** + * @since 2.0.0 + * @category sequencing + */ + (O: Order, f: (a: A) => Iterable): (self: SortedSet) => SortedSet + /** + * @since 2.0.0 + * @category sequencing + */ + (self: SortedSet, O: Order, f: (a: A) => Iterable): SortedSet +} = Dual.dual< + (O: Order, f: (a: A) => Iterable) => (self: SortedSet) => SortedSet, + (self: SortedSet, O: Order, f: (a: A) => Iterable) => SortedSet +>(3, (self, O, f) => { + let out = empty(O) + forEach(self, (a) => { + for (const b of f(a)) { + out = add(out, b) + } + }) + return out +}) + +/** + * @since 2.0.0 + * @category traversing + */ +export const forEach: { + /** + * @since 2.0.0 + * @category traversing + */ + (f: (a: A) => void): (self: SortedSet) => void + /** + * @since 2.0.0 + * @category traversing + */ + (self: SortedSet, f: (a: A) => void): void +} = Dual.dual< + (f: (a: A) => void) => (self: SortedSet) => void, + (self: SortedSet, f: (a: A) => void) => void +>(2, (self, f) => RBT.forEach(self.keyTree, f)) + +/** + * @since 2.0.0 + * @category elements + */ +export const has: { + /** + * @since 2.0.0 + * @category elements + */ + (value: A): (self: SortedSet) => boolean + /** + * @since 2.0.0 + * @category elements + */ + (self: SortedSet, value: A): boolean +} = Dual.dual< + (value: A) => (self: SortedSet) => boolean, + (self: SortedSet, value: A) => boolean +>(2, (self, value) => RBT.has(self.keyTree, value)) + +/** + * @since 2.0.0 + */ +export const intersection: { + /** + * @since 2.0.0 + */ + (that: Iterable): (self: SortedSet) => SortedSet + /** + * @since 2.0.0 + */ + (self: SortedSet, that: Iterable): SortedSet +} = Dual.dual< + (that: Iterable) => (self: SortedSet) => SortedSet, + (self: SortedSet, that: Iterable) => SortedSet +>(2, (self, that) => { + const ord = RBT.getOrder(self.keyTree) + let out = empty(ord) + for (const value of that) { + if (has(self, value)) { + out = add(out, value) + } + } + return out +}) + +/** + * @since 2.0.0 + * @category elements + */ +export const isSubset: { + /** + * @since 2.0.0 + * @category elements + */ + (that: SortedSet): (self: SortedSet) => boolean + /** + * @since 2.0.0 + * @category elements + */ + (self: SortedSet, that: SortedSet): boolean +} = Dual.dual< + (that: SortedSet) => (self: SortedSet) => boolean, + (self: SortedSet, that: SortedSet) => boolean +>(2, (self, that) => every(self, (a) => has(that, a))) + +/** + * @since 2.0.0 + * @category mapping + */ +export const map: { + /** + * @since 2.0.0 + * @category mapping + */ + (O: Order, f: (a: A) => B): (self: SortedSet) => SortedSet + /** + * @since 2.0.0 + * @category mapping + */ + (self: SortedSet, O: Order, f: (a: A) => B): SortedSet +} = Dual.dual< + (O: Order, f: (a: A) => B) => (self: SortedSet) => SortedSet, + (self: SortedSet, O: Order, f: (a: A) => B) => SortedSet +>(3, (self, O, f) => { + let out = empty(O) + forEach(self, (a) => { + const b = f(a) + if (!has(out, b)) { + out = add(out, b) + } + }) + return out +}) + +/** + * @since 2.0.0 + * @category filtering + */ +export const partition: { + /** + * @since 2.0.0 + * @category filtering + */ + (predicate: (a: NoInfer) => boolean): (self: SortedSet) => [excluded: SortedSet, satisfying: SortedSet] + /** + * @since 2.0.0 + * @category filtering + */ + (self: SortedSet, predicate: (a: A) => boolean): [excluded: SortedSet, satisfying: SortedSet] +} = Dual.dual( + 2, + (self: SortedSet, predicate: (a: A) => boolean): [excluded: SortedSet, satisfying: SortedSet] => { + const ord = RBT.getOrder(self.keyTree) + let right = empty(ord) + let left = empty(ord) + for (const value of self) { + if (predicate(value)) { + right = add(right, value) + } else { + left = add(left, value) + } + } + return [left, right] + } +) + +/** + * @since 2.0.0 + * @category elements + */ +export const remove: { + /** + * @since 2.0.0 + * @category elements + */ + (value: A): (self: SortedSet) => SortedSet + /** + * @since 2.0.0 + * @category elements + */ + (self: SortedSet, value: A): SortedSet +} = Dual.dual< + (value: A) => (self: SortedSet) => SortedSet, + (self: SortedSet, value: A) => SortedSet +>(2, (self, value) => fromTree(RBT.removeFirst(self.keyTree, value))) + +/** + * @since 2.0.0 + * @category getters + */ +export const size = (self: SortedSet): number => RBT.size(self.keyTree) + +/** + * Check if a predicate holds true for some `SortedSet` element. + * + * @since 2.0.0 + * @category elements + */ +export const some: { + /** + * Check if a predicate holds true for some `SortedSet` element. + * + * @since 2.0.0 + * @category elements + */ + (predicate: Predicate): (self: SortedSet) => boolean + /** + * Check if a predicate holds true for some `SortedSet` element. + * + * @since 2.0.0 + * @category elements + */ + (self: SortedSet, predicate: Predicate): boolean +} = Dual.dual< + (predicate: Predicate) => (self: SortedSet) => boolean, + (self: SortedSet, predicate: Predicate) => boolean +>(2, (self, predicate) => { + for (const value of self) { + if (predicate(value)) { + return true + } + } + return false +}) + +/** + * @since 2.0.0 + * @category elements + */ +export const toggle: { + /** + * @since 2.0.0 + * @category elements + */ + (value: A): (self: SortedSet) => SortedSet + /** + * @since 2.0.0 + * @category elements + */ + (self: SortedSet, value: A): SortedSet +} = Dual.dual< + (value: A) => (self: SortedSet) => SortedSet, + (self: SortedSet, value: A) => SortedSet +>(2, (self, value) => has(self, value) ? remove(self, value) : add(self, value)) + +/** + * @since 2.0.0 + */ +export const union: { + /** + * @since 2.0.0 + */ + (that: Iterable): (self: SortedSet) => SortedSet + /** + * @since 2.0.0 + */ + (self: SortedSet, that: Iterable): SortedSet +} = Dual.dual< + (that: Iterable) => (self: SortedSet) => SortedSet, + (self: SortedSet, that: Iterable) => SortedSet +>(2, (self: SortedSet, that: Iterable) => { + const ord = RBT.getOrder(self.keyTree) + let out = empty(ord) + for (const value of self) { + out = add(value)(out) + } + for (const value of that) { + out = add(value)(out) + } + return out +}) + +/** + * @since 2.0.0 + * @category getters + */ +export const values = (self: SortedSet): IterableIterator => RBT.keys(self.keyTree) + +/** + * @since 2.0.0 + * @category equivalence + */ +export const getEquivalence = (): Equivalence.Equivalence> => (a, b) => isSubset(a, b) && isSubset(b, a) diff --git a/backend/node_modules/effect/src/Stream.ts b/backend/node_modules/effect/src/Stream.ts new file mode 100644 index 0000000000000000000000000000000000000000..5eb00446570bb04c0afdc601e6e4d725801b9a77 --- /dev/null +++ b/backend/node_modules/effect/src/Stream.ts @@ -0,0 +1,11561 @@ +/** + * @since 2.0.0 + */ +import type * as Cause from "./Cause.js" +import type * as Channel from "./Channel.js" +import type * as Chunk from "./Chunk.js" +import type * as Context from "./Context.js" +import type * as Deferred from "./Deferred.js" +import type * as Duration from "./Duration.js" +import type * as Effect from "./Effect.js" +import type * as Either from "./Either.js" +import type { ExecutionPlan } from "./ExecutionPlan.js" +import type * as Exit from "./Exit.js" +import type { LazyArg } from "./Function.js" +import type * as GroupBy from "./GroupBy.js" +import type { TypeLambda } from "./HKT.js" +import * as groupBy_ from "./internal/groupBy.js" +import * as internal from "./internal/stream.js" +import type * as Layer from "./Layer.js" +import type * as Option from "./Option.js" +import type * as Order from "./Order.js" +import type { Pipeable } from "./Pipeable.js" +import type { Predicate, Refinement } from "./Predicate.js" +import type * as PubSub from "./PubSub.js" +import type * as Queue from "./Queue.js" +import type { Runtime } from "./Runtime.js" +import type * as Schedule from "./Schedule.js" +import type * as Scope from "./Scope.js" +import type * as Sink from "./Sink.js" +import type * as Emit from "./StreamEmit.js" +import type * as HaltStrategy from "./StreamHaltStrategy.js" +import type * as Take from "./Take.js" +import type { TPubSub } from "./TPubSub.js" +import type { TDequeue } from "./TQueue.js" +import type * as Tracer from "./Tracer.js" +import type { Covariant, NoInfer, TupleOf } from "./Types.js" +import type * as Unify from "./Unify.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const StreamTypeId: unique symbol = internal.StreamTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type StreamTypeId = typeof StreamTypeId + +/** + * A `Stream` is a description of a program that, when evaluated, may + * emit zero or more values of type `A`, may fail with errors of type `E`, and + * uses an context of type `R`. One way to think of `Stream` is as a + * `Effect` program that could emit multiple values. + * + * `Stream` is a purely functional *pull* based stream. Pull based streams offer + * inherent laziness and backpressure, relieving users of the need to manage + * buffers between operators. As an optimization, `Stream` does not emit + * single values, but rather an array of values. This allows the cost of effect + * evaluation to be amortized. + * + * `Stream` forms a monad on its `A` type parameter, and has error management + * facilities for its `E` type parameter, modeled similarly to `Effect` (with + * some adjustments for the multiple-valued nature of `Stream`). These aspects + * allow for rich and expressive composition of streams. + * + * @since 2.0.0 + * @category models + */ +export interface Stream extends Stream.Variance, Pipeable { + [Unify.typeSymbol]?: unknown + [Unify.unifySymbol]?: StreamUnify + [Unify.ignoreSymbol]?: StreamUnifyIgnore +} + +/** + * @since 2.0.0 + * @category models + */ +export interface StreamUnify extends Effect.EffectUnify { + Stream?: () => A[Unify.typeSymbol] extends Stream | infer _ ? Stream : never +} + +/** + * @category models + * @since 2.0.0 + */ +export interface StreamUnifyIgnore extends Effect.EffectUnifyIgnore { + Effect?: true +} + +/** + * @since 2.0.0 + * @category models + */ +declare module "./Effect.js" { + interface Effect extends Stream {} +} + +/** + * @category type lambdas + * @since 2.0.0 + */ +export interface StreamTypeLambda extends TypeLambda { + readonly type: Stream +} + +/** + * @since 2.0.0 + */ +export declare namespace Stream { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [StreamTypeId]: VarianceStruct + } + + /** + * @since 3.4.0 + * @category models + */ + export interface VarianceStruct { + readonly _A: Covariant + readonly _E: Covariant + readonly _R: Covariant + } + + /** + * @since 3.4.0 + * @category type-level + */ + export type Success> = [T] extends [Stream] ? _A : never + + /** + * @since 3.4.0 + * @category type-level + */ + export type Error> = [T] extends [Stream] ? _E : never + + /** + * @since 3.4.0 + * @category type-level + */ + export type Context> = [T] extends [Stream] ? _R : never + + /** + * @since 2.0.0 + * @category models + * @deprecated use Types.TupleOf instead + */ + export type DynamicTuple = N extends N ? number extends N ? Array : DynamicTupleOf + : never + + /** + * @since 2.0.0 + * @category models + * @deprecated use Types.TupleOf instead + */ + export type DynamicTupleOf> = R["length"] extends N ? R + : DynamicTupleOf +} + +/** + * The default chunk size used by the various combinators and constructors of + * `Stream`. + * + * @since 2.0.0 + * @category constants + */ +export const DefaultChunkSize: number = internal.DefaultChunkSize + +/** + * Collects each underlying Chunk of the stream into a new chunk, and emits it + * on each pull. + * + * @since 2.0.0 + * @category utils + */ +export const accumulate: (self: Stream) => Stream, E, R> = internal.accumulate + +/** + * Re-chunks the elements of the stream by accumulating each underlying chunk. + * + * @since 2.0.0 + * @category utils + */ +export const accumulateChunks: (self: Stream) => Stream = internal.accumulateChunks + +/** + * Creates a stream from a single value that will get cleaned up after the + * stream is consumed. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * // Simulating File operations + * const open = (filename: string) => + * Effect.gen(function*() { + * yield* Console.log(`Opening ${filename}`) + * return { + * getLines: Effect.succeed(["Line 1", "Line 2", "Line 3"]), + * close: Console.log(`Closing ${filename}`) + * } + * }) + * + * const stream = Stream.acquireRelease( + * open("file.txt"), + * (file) => file.close + * ).pipe(Stream.flatMap((file) => file.getLines)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Opening file.txt + * // Closing file.txt + * // { _id: 'Chunk', values: [ [ 'Line 1', 'Line 2', 'Line 3' ] ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const acquireRelease: ( + acquire: Effect.Effect, + release: (resource: A, exit: Exit.Exit) => Effect.Effect +) => Stream = internal.acquireRelease + +/** + * Aggregates elements of this stream using the provided sink for as long as + * the downstream operators on the stream are busy. + * + * This operator divides the stream into two asynchronous "islands". Operators + * upstream of this operator run on one fiber, while downstream operators run + * on another. Whenever the downstream fiber is busy processing elements, the + * upstream fiber will feed elements into the sink until it signals + * completion. + * + * Any sink can be used here, but see `Sink.foldWeightedEffect` and + * `Sink.foldUntilEffect` for sinks that cover the common usecases. + * + * @since 2.0.0 + * @category utils + */ +export const aggregate: { + /** + * Aggregates elements of this stream using the provided sink for as long as + * the downstream operators on the stream are busy. + * + * This operator divides the stream into two asynchronous "islands". Operators + * upstream of this operator run on one fiber, while downstream operators run + * on another. Whenever the downstream fiber is busy processing elements, the + * upstream fiber will feed elements into the sink until it signals + * completion. + * + * Any sink can be used here, but see `Sink.foldWeightedEffect` and + * `Sink.foldUntilEffect` for sinks that cover the common usecases. + * + * @since 2.0.0 + * @category utils + */ + (sink: Sink.Sink): (self: Stream) => Stream + /** + * Aggregates elements of this stream using the provided sink for as long as + * the downstream operators on the stream are busy. + * + * This operator divides the stream into two asynchronous "islands". Operators + * upstream of this operator run on one fiber, while downstream operators run + * on another. Whenever the downstream fiber is busy processing elements, the + * upstream fiber will feed elements into the sink until it signals + * completion. + * + * Any sink can be used here, but see `Sink.foldWeightedEffect` and + * `Sink.foldUntilEffect` for sinks that cover the common usecases. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, sink: Sink.Sink): Stream +} = internal.aggregate + +/** + * Like {@link aggregateWithinEither}, but only returns the `Right` results. + * + * @since 2.0.0 + * @category utils + */ +export const aggregateWithin: { + /** + * Like {@link aggregateWithinEither}, but only returns the `Right` results. + * + * @since 2.0.0 + * @category utils + */ + ( + sink: Sink.Sink, + schedule: Schedule.Schedule, R3> + ): (self: Stream) => Stream + /** + * Like {@link aggregateWithinEither}, but only returns the `Right` results. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + sink: Sink.Sink, + schedule: Schedule.Schedule, R3> + ): Stream +} = internal.aggregateWithin + +/** + * Aggregates elements using the provided sink until it completes, or until + * the delay signalled by the schedule has passed. + * + * This operator divides the stream into two asynchronous islands. Operators + * upstream of this operator run on one fiber, while downstream operators run + * on another. Elements will be aggregated by the sink until the downstream + * fiber pulls the aggregated value, or until the schedule's delay has passed. + * + * Aggregated elements will be fed into the schedule to determine the delays + * between pulls. + * + * @since 2.0.0 + * @category utils + */ +export const aggregateWithinEither: { + /** + * Aggregates elements using the provided sink until it completes, or until + * the delay signalled by the schedule has passed. + * + * This operator divides the stream into two asynchronous islands. Operators + * upstream of this operator run on one fiber, while downstream operators run + * on another. Elements will be aggregated by the sink until the downstream + * fiber pulls the aggregated value, or until the schedule's delay has passed. + * + * Aggregated elements will be fed into the schedule to determine the delays + * between pulls. + * + * @since 2.0.0 + * @category utils + */ + ( + sink: Sink.Sink, + schedule: Schedule.Schedule, R3> + ): (self: Stream) => Stream, E2 | E, R2 | R3 | R> + /** + * Aggregates elements using the provided sink until it completes, or until + * the delay signalled by the schedule has passed. + * + * This operator divides the stream into two asynchronous islands. Operators + * upstream of this operator run on one fiber, while downstream operators run + * on another. Elements will be aggregated by the sink until the downstream + * fiber pulls the aggregated value, or until the schedule's delay has passed. + * + * Aggregated elements will be fed into the schedule to determine the delays + * between pulls. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + sink: Sink.Sink, + schedule: Schedule.Schedule, R3> + ): Stream, E | E2, R | R2 | R3> +} = internal.aggregateWithinEither + +/** + * Maps the success values of this stream to the specified constant value. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(1, 5).pipe(Stream.as(null)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ null, null, null, null, null ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ +export const as: { + /** + * Maps the success values of this stream to the specified constant value. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(1, 5).pipe(Stream.as(null)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ null, null, null, null, null ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + (value: B): (self: Stream) => Stream + /** + * Maps the success values of this stream to the specified constant value. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(1, 5).pipe(Stream.as(null)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ null, null, null, null, null ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + (self: Stream, value: B): Stream +} = internal.as + +const _async: ( + register: (emit: Emit.Emit) => Effect.Effect | void, + bufferSize?: number | "unbounded" | { + readonly bufferSize?: number | undefined + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + } | undefined +) => Stream = internal._async + +export { + /** + * Creates a stream from an asynchronous callback that can be called multiple + * times. The optionality of the error type `E` in `Emit` can be used to + * signal the end of the stream by setting it to `None`. + * + * The registration function can optionally return an `Effect`, which will be + * executed if the `Fiber` executing this Effect is interrupted. + * + * @example + * ```ts + * import type { StreamEmit } from "effect" + * import { Chunk, Effect, Option, Stream } from "effect" + * + * const events = [1, 2, 3, 4] + * + * const stream = Stream.async( + * (emit: StreamEmit.Emit) => { + * events.forEach((n) => { + * setTimeout(() => { + * if (n === 3) { + * emit(Effect.fail(Option.none())) // Terminate the stream + * } else { + * emit(Effect.succeed(Chunk.of(n))) // Add the current item to the stream + * } + * }, 100 * n) + * }) + * } + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2 ] } + * + * ``` + * @since 2.0.0 + * @category constructors + */ + _async as async +} + +/** + * Creates a stream from an asynchronous callback that can be called multiple + * times The registration of the callback itself returns an effect. The + * optionality of the error type `E` can be used to signal the end of the + * stream, by setting it to `None`. + * + * @since 2.0.0 + * @category constructors + */ +export const asyncEffect: ( + register: (emit: Emit.Emit) => Effect.Effect, + bufferSize?: number | "unbounded" | { + readonly bufferSize?: number | undefined + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + } | undefined +) => Stream = internal.asyncEffect + +/** + * Creates a stream from an external push-based resource. + * + * You can use the `emit` helper to emit values to the stream. The `emit` helper + * returns a boolean indicating whether the value was emitted or not. + * + * You can also use the `emit` helper to signal the end of the stream by + * using apis such as `emit.end` or `emit.fail`. + * + * By default it uses an "unbounded" buffer size. + * You can customize the buffer size and strategy by passing an object as the + * second argument with the `bufferSize` and `strategy` fields. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * Stream.asyncPush((emit) => + * Effect.acquireRelease( + * Effect.gen(function*() { + * yield* Effect.log("subscribing") + * return setInterval(() => emit.single("tick"), 1000) + * }), + * (handle) => + * Effect.gen(function*() { + * yield* Effect.log("unsubscribing") + * clearInterval(handle) + * }) + * ), { bufferSize: 16, strategy: "dropping" }) + * ``` + * + * @since 3.6.0 + * @category constructors + */ +export const asyncPush: ( + register: (emit: Emit.EmitOpsPush) => Effect.Effect, + options?: { readonly bufferSize: "unbounded" } | { + readonly bufferSize?: number | undefined + readonly strategy?: "dropping" | "sliding" | undefined + } | undefined +) => Stream> = internal.asyncPush + +/** + * Creates a stream from an asynchronous callback that can be called multiple + * times. The registration of the callback itself returns an a scoped + * resource. The optionality of the error type `E` can be used to signal the + * end of the stream, by setting it to `None`. + * + * @since 2.0.0 + * @category constructors + */ +export const asyncScoped: ( + register: (emit: Emit.Emit) => Effect.Effect, + bufferSize?: number | "unbounded" | { + readonly bufferSize?: number | undefined + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + } | undefined +) => Stream> = internal.asyncScoped + +/** + * Returns a `Stream` that first collects `n` elements from the input `Stream`, + * and then creates a new `Stream` using the specified function, and sends all + * the following elements through that. + * + * @since 2.0.0 + * @category sequencing + */ +export const branchAfter: { + /** + * Returns a `Stream` that first collects `n` elements from the input `Stream`, + * and then creates a new `Stream` using the specified function, and sends all + * the following elements through that. + * + * @since 2.0.0 + * @category sequencing + */ + (n: number, f: (input: Chunk.Chunk) => Stream): (self: Stream) => Stream + /** + * Returns a `Stream` that first collects `n` elements from the input `Stream`, + * and then creates a new `Stream` using the specified function, and sends all + * the following elements through that. + * + * @since 2.0.0 + * @category sequencing + */ + ( + self: Stream, + n: number, + f: (input: Chunk.Chunk) => Stream + ): Stream +} = internal.branchAfter + +/** + * Fan out the stream, producing a list of streams that have the same elements + * as this stream. The driver stream will only ever advance the `maximumLag` + * chunks before the slowest downstream stream. + * + * @example + * ```ts + * import { Console, Effect, Fiber, Schedule, Stream } from "effect" + * + * const numbers = Effect.scoped( + * Stream.range(1, 20).pipe( + * Stream.tap((n) => Console.log(`Emit ${n} element before broadcasting`)), + * Stream.broadcast(2, 5), + * Stream.flatMap(([first, second]) => + * Effect.gen(function*() { + * const fiber1 = yield* Stream.runFold(first, 0, (acc, e) => Math.max(acc, e)).pipe( + * Effect.andThen((max) => Console.log(`Maximum: ${max}`)), + * Effect.fork + * ) + * const fiber2 = yield* second.pipe( + * Stream.schedule(Schedule.spaced("1 second")), + * Stream.runForEach((n) => Console.log(`Logging to the Console: ${n}`)), + * Effect.fork + * ) + * yield* Fiber.join(fiber1).pipe( + * Effect.zip(Fiber.join(fiber2), { concurrent: true }) + * ) + * }) + * ), + * Stream.runCollect + * ) + * ) + * + * Effect.runPromise(numbers).then(console.log) + * // Emit 1 element before broadcasting + * // Emit 2 element before broadcasting + * // Emit 3 element before broadcasting + * // Emit 4 element before broadcasting + * // Emit 5 element before broadcasting + * // Emit 6 element before broadcasting + * // Emit 7 element before broadcasting + * // Emit 8 element before broadcasting + * // Emit 9 element before broadcasting + * // Emit 10 element before broadcasting + * // Emit 11 element before broadcasting + * // Logging to the Console: 1 + * // Logging to the Console: 2 + * // Logging to the Console: 3 + * // Logging to the Console: 4 + * // Logging to the Console: 5 + * // Emit 12 element before broadcasting + * // Emit 13 element before broadcasting + * // Emit 14 element before broadcasting + * // Emit 15 element before broadcasting + * // Emit 16 element before broadcasting + * // Logging to the Console: 6 + * // Logging to the Console: 7 + * // Logging to the Console: 8 + * // Logging to the Console: 9 + * // Logging to the Console: 10 + * // Emit 17 element before broadcasting + * // Emit 18 element before broadcasting + * // Emit 19 element before broadcasting + * // Emit 20 element before broadcasting + * // Logging to the Console: 11 + * // Logging to the Console: 12 + * // Logging to the Console: 13 + * // Logging to the Console: 14 + * // Logging to the Console: 15 + * // Maximum: 20 + * // Logging to the Console: 16 + * // Logging to the Console: 17 + * // Logging to the Console: 18 + * // Logging to the Console: 19 + * // Logging to the Console: 20 + * // { _id: 'Chunk', values: [ undefined ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const broadcast: { + /** + * Fan out the stream, producing a list of streams that have the same elements + * as this stream. The driver stream will only ever advance the `maximumLag` + * chunks before the slowest downstream stream. + * + * @example + * ```ts + * import { Console, Effect, Fiber, Schedule, Stream } from "effect" + * + * const numbers = Effect.scoped( + * Stream.range(1, 20).pipe( + * Stream.tap((n) => Console.log(`Emit ${n} element before broadcasting`)), + * Stream.broadcast(2, 5), + * Stream.flatMap(([first, second]) => + * Effect.gen(function*() { + * const fiber1 = yield* Stream.runFold(first, 0, (acc, e) => Math.max(acc, e)).pipe( + * Effect.andThen((max) => Console.log(`Maximum: ${max}`)), + * Effect.fork + * ) + * const fiber2 = yield* second.pipe( + * Stream.schedule(Schedule.spaced("1 second")), + * Stream.runForEach((n) => Console.log(`Logging to the Console: ${n}`)), + * Effect.fork + * ) + * yield* Fiber.join(fiber1).pipe( + * Effect.zip(Fiber.join(fiber2), { concurrent: true }) + * ) + * }) + * ), + * Stream.runCollect + * ) + * ) + * + * Effect.runPromise(numbers).then(console.log) + * // Emit 1 element before broadcasting + * // Emit 2 element before broadcasting + * // Emit 3 element before broadcasting + * // Emit 4 element before broadcasting + * // Emit 5 element before broadcasting + * // Emit 6 element before broadcasting + * // Emit 7 element before broadcasting + * // Emit 8 element before broadcasting + * // Emit 9 element before broadcasting + * // Emit 10 element before broadcasting + * // Emit 11 element before broadcasting + * // Logging to the Console: 1 + * // Logging to the Console: 2 + * // Logging to the Console: 3 + * // Logging to the Console: 4 + * // Logging to the Console: 5 + * // Emit 12 element before broadcasting + * // Emit 13 element before broadcasting + * // Emit 14 element before broadcasting + * // Emit 15 element before broadcasting + * // Emit 16 element before broadcasting + * // Logging to the Console: 6 + * // Logging to the Console: 7 + * // Logging to the Console: 8 + * // Logging to the Console: 9 + * // Logging to the Console: 10 + * // Emit 17 element before broadcasting + * // Emit 18 element before broadcasting + * // Emit 19 element before broadcasting + * // Emit 20 element before broadcasting + * // Logging to the Console: 11 + * // Logging to the Console: 12 + * // Logging to the Console: 13 + * // Logging to the Console: 14 + * // Logging to the Console: 15 + * // Maximum: 20 + * // Logging to the Console: 16 + * // Logging to the Console: 17 + * // Logging to the Console: 18 + * // Logging to the Console: 19 + * // Logging to the Console: 20 + * // { _id: 'Chunk', values: [ undefined ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + ( + n: N, + maximumLag: number | { readonly capacity: "unbounded"; readonly replay?: number | undefined } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ): (self: Stream) => Effect.Effect>, never, Scope.Scope | R> + /** + * Fan out the stream, producing a list of streams that have the same elements + * as this stream. The driver stream will only ever advance the `maximumLag` + * chunks before the slowest downstream stream. + * + * @example + * ```ts + * import { Console, Effect, Fiber, Schedule, Stream } from "effect" + * + * const numbers = Effect.scoped( + * Stream.range(1, 20).pipe( + * Stream.tap((n) => Console.log(`Emit ${n} element before broadcasting`)), + * Stream.broadcast(2, 5), + * Stream.flatMap(([first, second]) => + * Effect.gen(function*() { + * const fiber1 = yield* Stream.runFold(first, 0, (acc, e) => Math.max(acc, e)).pipe( + * Effect.andThen((max) => Console.log(`Maximum: ${max}`)), + * Effect.fork + * ) + * const fiber2 = yield* second.pipe( + * Stream.schedule(Schedule.spaced("1 second")), + * Stream.runForEach((n) => Console.log(`Logging to the Console: ${n}`)), + * Effect.fork + * ) + * yield* Fiber.join(fiber1).pipe( + * Effect.zip(Fiber.join(fiber2), { concurrent: true }) + * ) + * }) + * ), + * Stream.runCollect + * ) + * ) + * + * Effect.runPromise(numbers).then(console.log) + * // Emit 1 element before broadcasting + * // Emit 2 element before broadcasting + * // Emit 3 element before broadcasting + * // Emit 4 element before broadcasting + * // Emit 5 element before broadcasting + * // Emit 6 element before broadcasting + * // Emit 7 element before broadcasting + * // Emit 8 element before broadcasting + * // Emit 9 element before broadcasting + * // Emit 10 element before broadcasting + * // Emit 11 element before broadcasting + * // Logging to the Console: 1 + * // Logging to the Console: 2 + * // Logging to the Console: 3 + * // Logging to the Console: 4 + * // Logging to the Console: 5 + * // Emit 12 element before broadcasting + * // Emit 13 element before broadcasting + * // Emit 14 element before broadcasting + * // Emit 15 element before broadcasting + * // Emit 16 element before broadcasting + * // Logging to the Console: 6 + * // Logging to the Console: 7 + * // Logging to the Console: 8 + * // Logging to the Console: 9 + * // Logging to the Console: 10 + * // Emit 17 element before broadcasting + * // Emit 18 element before broadcasting + * // Emit 19 element before broadcasting + * // Emit 20 element before broadcasting + * // Logging to the Console: 11 + * // Logging to the Console: 12 + * // Logging to the Console: 13 + * // Logging to the Console: 14 + * // Logging to the Console: 15 + * // Maximum: 20 + * // Logging to the Console: 16 + * // Logging to the Console: 17 + * // Logging to the Console: 18 + * // Logging to the Console: 19 + * // Logging to the Console: 20 + * // { _id: 'Chunk', values: [ undefined ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + n: N, + maximumLag: number | { readonly capacity: "unbounded"; readonly replay?: number | undefined } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ): Effect.Effect>, never, Scope.Scope | R> +} = internal.broadcast + +/** + * Returns a new Stream that multicasts the original Stream, subscribing to it as soon as the first consumer subscribes. + * As long as there is at least one consumer, the upstream will continue running and emitting data. + * When all consumers have exited, the upstream will be finalized. + * + * @since 3.8.0 + * @category utils + */ +export const share: { + /** + * Returns a new Stream that multicasts the original Stream, subscribing to it as soon as the first consumer subscribes. + * As long as there is at least one consumer, the upstream will continue running and emitting data. + * When all consumers have exited, the upstream will be finalized. + * + * @since 3.8.0 + * @category utils + */ + ( + config: { + readonly capacity: "unbounded" + readonly replay?: number | undefined + readonly idleTimeToLive?: Duration.DurationInput | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + readonly idleTimeToLive?: Duration.DurationInput | undefined + } + ): (self: Stream) => Effect.Effect, never, R | Scope.Scope> + /** + * Returns a new Stream that multicasts the original Stream, subscribing to it as soon as the first consumer subscribes. + * As long as there is at least one consumer, the upstream will continue running and emitting data. + * When all consumers have exited, the upstream will be finalized. + * + * @since 3.8.0 + * @category utils + */ + ( + self: Stream, + config: { + readonly capacity: "unbounded" + readonly replay?: number | undefined + readonly idleTimeToLive?: Duration.DurationInput | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + readonly idleTimeToLive?: Duration.DurationInput | undefined + } + ): Effect.Effect, never, R | Scope.Scope> +} = internal.share + +/** + * Fan out the stream, producing a dynamic number of streams that have the + * same elements as this stream. The driver stream will only ever advance the + * `maximumLag` chunks before the slowest downstream stream. + * + * @since 2.0.0 + * @category utils + */ +export const broadcastDynamic: { + /** + * Fan out the stream, producing a dynamic number of streams that have the + * same elements as this stream. The driver stream will only ever advance the + * `maximumLag` chunks before the slowest downstream stream. + * + * @since 2.0.0 + * @category utils + */ + ( + maximumLag: number | { readonly capacity: "unbounded"; readonly replay?: number | undefined } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ): (self: Stream) => Effect.Effect, never, Scope.Scope | R> + /** + * Fan out the stream, producing a dynamic number of streams that have the + * same elements as this stream. The driver stream will only ever advance the + * `maximumLag` chunks before the slowest downstream stream. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + maximumLag: number | { readonly capacity: "unbounded"; readonly replay?: number | undefined } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ): Effect.Effect, never, Scope.Scope | R> +} = internal.broadcastDynamic + +/** + * Converts the stream to a scoped list of queues. Every value will be + * replicated to every queue with the slowest queue being allowed to buffer + * `maximumLag` chunks before the driver is back pressured. + * + * Queues can unsubscribe from upstream by shutting down. + * + * @since 2.0.0 + * @category utils + */ +export const broadcastedQueues: { + /** + * Converts the stream to a scoped list of queues. Every value will be + * replicated to every queue with the slowest queue being allowed to buffer + * `maximumLag` chunks before the driver is back pressured. + * + * Queues can unsubscribe from upstream by shutting down. + * + * @since 2.0.0 + * @category utils + */ + ( + n: N, + maximumLag: number | { readonly capacity: "unbounded"; readonly replay?: number | undefined } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ): ( + self: Stream + ) => Effect.Effect>>, never, Scope.Scope | R> + /** + * Converts the stream to a scoped list of queues. Every value will be + * replicated to every queue with the slowest queue being allowed to buffer + * `maximumLag` chunks before the driver is back pressured. + * + * Queues can unsubscribe from upstream by shutting down. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + n: N, + maximumLag: number | { readonly capacity: "unbounded"; readonly replay?: number | undefined } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ): Effect.Effect>>, never, Scope.Scope | R> +} = internal.broadcastedQueues + +/** + * Converts the stream to a scoped dynamic amount of queues. Every chunk will + * be replicated to every queue with the slowest queue being allowed to buffer + * `maximumLag` chunks before the driver is back pressured. + * + * Queues can unsubscribe from upstream by shutting down. + * + * @since 2.0.0 + * @category utils + */ +export const broadcastedQueuesDynamic: { + /** + * Converts the stream to a scoped dynamic amount of queues. Every chunk will + * be replicated to every queue with the slowest queue being allowed to buffer + * `maximumLag` chunks before the driver is back pressured. + * + * Queues can unsubscribe from upstream by shutting down. + * + * @since 2.0.0 + * @category utils + */ + ( + maximumLag: number | { readonly capacity: "unbounded"; readonly replay?: number | undefined } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ): ( + self: Stream + ) => Effect.Effect>, never, Scope.Scope>, never, Scope.Scope | R> + /** + * Converts the stream to a scoped dynamic amount of queues. Every chunk will + * be replicated to every queue with the slowest queue being allowed to buffer + * `maximumLag` chunks before the driver is back pressured. + * + * Queues can unsubscribe from upstream by shutting down. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + maximumLag: number | { readonly capacity: "unbounded"; readonly replay?: number | undefined } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ): Effect.Effect>, never, Scope.Scope>, never, Scope.Scope | R> +} = internal.broadcastedQueuesDynamic + +/** + * Allows a faster producer to progress independently of a slower consumer by + * buffering up to `capacity` elements in a queue. + * + * Note: This combinator destroys the chunking structure. It's recommended to + * use rechunk afterwards. Additionally, prefer capacities that are powers + * of 2 for better performance. + * + * @example + * ```ts + * import { Console, Effect, Schedule, Stream } from "effect" + * + * const stream = Stream.range(1, 10).pipe( + * Stream.tap((n) => Console.log(`before buffering: ${n}`)), + * Stream.buffer({ capacity: 4 }), + * Stream.tap((n) => Console.log(`after buffering: ${n}`)), + * Stream.schedule(Schedule.spaced("5 seconds")) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // before buffering: 1 + * // before buffering: 2 + * // before buffering: 3 + * // before buffering: 4 + * // before buffering: 5 + * // before buffering: 6 + * // after buffering: 1 + * // after buffering: 2 + * // before buffering: 7 + * // after buffering: 3 + * // before buffering: 8 + * // after buffering: 4 + * // before buffering: 9 + * // after buffering: 5 + * // before buffering: 10 + * // ... + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const buffer: { + /** + * Allows a faster producer to progress independently of a slower consumer by + * buffering up to `capacity` elements in a queue. + * + * Note: This combinator destroys the chunking structure. It's recommended to + * use rechunk afterwards. Additionally, prefer capacities that are powers + * of 2 for better performance. + * + * @example + * ```ts + * import { Console, Effect, Schedule, Stream } from "effect" + * + * const stream = Stream.range(1, 10).pipe( + * Stream.tap((n) => Console.log(`before buffering: ${n}`)), + * Stream.buffer({ capacity: 4 }), + * Stream.tap((n) => Console.log(`after buffering: ${n}`)), + * Stream.schedule(Schedule.spaced("5 seconds")) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // before buffering: 1 + * // before buffering: 2 + * // before buffering: 3 + * // before buffering: 4 + * // before buffering: 5 + * // before buffering: 6 + * // after buffering: 1 + * // after buffering: 2 + * // before buffering: 7 + * // after buffering: 3 + * // before buffering: 8 + * // after buffering: 4 + * // before buffering: 9 + * // after buffering: 5 + * // before buffering: 10 + * // ... + * ``` + * + * @since 2.0.0 + * @category utils + */ + ( + options: { readonly capacity: "unbounded" } | { + readonly capacity: number + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + } + ): (self: Stream) => Stream + /** + * Allows a faster producer to progress independently of a slower consumer by + * buffering up to `capacity` elements in a queue. + * + * Note: This combinator destroys the chunking structure. It's recommended to + * use rechunk afterwards. Additionally, prefer capacities that are powers + * of 2 for better performance. + * + * @example + * ```ts + * import { Console, Effect, Schedule, Stream } from "effect" + * + * const stream = Stream.range(1, 10).pipe( + * Stream.tap((n) => Console.log(`before buffering: ${n}`)), + * Stream.buffer({ capacity: 4 }), + * Stream.tap((n) => Console.log(`after buffering: ${n}`)), + * Stream.schedule(Schedule.spaced("5 seconds")) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // before buffering: 1 + * // before buffering: 2 + * // before buffering: 3 + * // before buffering: 4 + * // before buffering: 5 + * // before buffering: 6 + * // after buffering: 1 + * // after buffering: 2 + * // before buffering: 7 + * // after buffering: 3 + * // before buffering: 8 + * // after buffering: 4 + * // before buffering: 9 + * // after buffering: 5 + * // before buffering: 10 + * // ... + * ``` + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + options: { readonly capacity: "unbounded" } | { + readonly capacity: number + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + } + ): Stream +} = internal.buffer + +/** + * Allows a faster producer to progress independently of a slower consumer by + * buffering up to `capacity` chunks in a queue. + * + * @note Prefer capacities that are powers of 2 for better performance. + * @since 2.0.0 + * @category utils + */ +export const bufferChunks: { + /** + * Allows a faster producer to progress independently of a slower consumer by + * buffering up to `capacity` chunks in a queue. + * + * @note Prefer capacities that are powers of 2 for better performance. + * @since 2.0.0 + * @category utils + */ + ( + options: { readonly capacity: number; readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } + ): (self: Stream) => Stream + /** + * Allows a faster producer to progress independently of a slower consumer by + * buffering up to `capacity` chunks in a queue. + * + * @note Prefer capacities that are powers of 2 for better performance. + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + options: { readonly capacity: number; readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } + ): Stream +} = internal.bufferChunks + +/** + * Switches over to the stream produced by the provided function in case this + * one fails with a typed error. + * + * @since 2.0.0 + * @category error handling + */ +export const catchAll: { + /** + * Switches over to the stream produced by the provided function in case this + * one fails with a typed error. + * + * @since 2.0.0 + * @category error handling + */ + (f: (error: E) => Stream): (self: Stream) => Stream + /** + * Switches over to the stream produced by the provided function in case this + * one fails with a typed error. + * + * @since 2.0.0 + * @category error handling + */ + (self: Stream, f: (error: E) => Stream): Stream +} = internal.catchAll + +/** + * Switches over to the stream produced by the provided function in case this + * one fails. Allows recovery from all causes of failure, including + * interruption if the stream is uninterruptible. + * + * @since 2.0.0 + * @category error handling + */ +export const catchAllCause: { + /** + * Switches over to the stream produced by the provided function in case this + * one fails. Allows recovery from all causes of failure, including + * interruption if the stream is uninterruptible. + * + * @since 2.0.0 + * @category error handling + */ + (f: (cause: Cause.Cause) => Stream): (self: Stream) => Stream + /** + * Switches over to the stream produced by the provided function in case this + * one fails. Allows recovery from all causes of failure, including + * interruption if the stream is uninterruptible. + * + * @since 2.0.0 + * @category error handling + */ + (self: Stream, f: (cause: Cause.Cause) => Stream): Stream +} = internal.catchAllCause + +/** + * Switches over to the stream produced by the provided function in case this + * one fails with some typed error. + * + * @since 2.0.0 + * @category error handling + */ +export const catchSome: { + /** + * Switches over to the stream produced by the provided function in case this + * one fails with some typed error. + * + * @since 2.0.0 + * @category error handling + */ + (pf: (error: E) => Option.Option>): (self: Stream) => Stream + /** + * Switches over to the stream produced by the provided function in case this + * one fails with some typed error. + * + * @since 2.0.0 + * @category error handling + */ + (self: Stream, pf: (error: E) => Option.Option>): Stream +} = internal.catchSome + +/** + * Switches over to the stream produced by the provided function in case this + * one fails with an error matching the given `_tag`. + * + * @since 2.0.0 + * @category error handling + */ +export const catchTag: { + /** + * Switches over to the stream produced by the provided function in case this + * one fails with an error matching the given `_tag`. + * + * @since 2.0.0 + * @category error handling + */ + (k: K, f: (e: Extract) => Stream): (self: Stream) => Stream, R1 | R> + /** + * Switches over to the stream produced by the provided function in case this + * one fails with an error matching the given `_tag`. + * + * @since 2.0.0 + * @category error handling + */ + ( + self: Stream, + k: K, + f: (e: Extract) => Stream + ): Stream, R | R1> +} = internal.catchTag + +/** + * Switches over to the stream produced by one of the provided functions, in + * case this one fails with an error matching one of the given `_tag`'s. + * + * @since 2.0.0 + * @category error handling + */ +export const catchTags: { + /** + * Switches over to the stream produced by one of the provided functions, in + * case this one fails with an error matching one of the given `_tag`'s. + * + * @since 2.0.0 + * @category error handling + */ + < + E extends { _tag: string }, + Cases extends { [K in E["_tag"]]+?: (error: Extract) => Stream } + >(cases: Cases): ( + self: Stream + ) => Stream< + | A + | { + [K in keyof Cases]: Cases[K] extends (...args: Array) => Stream.Variance ? A + : never + }[keyof Cases], + | Exclude + | { + [K in keyof Cases]: Cases[K] extends (...args: Array) => Stream.Variance ? E + : never + }[keyof Cases], + | R + | { + [K in keyof Cases]: Cases[K] extends (...args: Array) => Stream.Variance ? R + : never + }[keyof Cases] + > + /** + * Switches over to the stream produced by one of the provided functions, in + * case this one fails with an error matching one of the given `_tag`'s. + * + * @since 2.0.0 + * @category error handling + */ + < + A, + E extends { _tag: string }, + R, + Cases extends { [K in E["_tag"]]+?: (error: Extract) => Stream } + >(self: Stream, cases: Cases): Stream< + | A + | { + [K in keyof Cases]: Cases[K] extends (...args: Array) => Stream.Variance ? A + : never + }[keyof Cases], + | Exclude + | { + [K in keyof Cases]: Cases[K] extends (...args: Array) => Stream.Variance ? E + : never + }[keyof Cases], + | R + | { + [K in keyof Cases]: Cases[K] extends (...args: Array) => Stream.Variance ? R + : never + }[keyof Cases] + > +} = internal.catchTags + +/** + * Switches over to the stream produced by the provided function in case this + * one fails with some errors. Allows recovery from all causes of failure, + * including interruption if the stream is uninterruptible. + * + * @since 2.0.0 + * @category error handling + */ +export const catchSomeCause: { + /** + * Switches over to the stream produced by the provided function in case this + * one fails with some errors. Allows recovery from all causes of failure, + * including interruption if the stream is uninterruptible. + * + * @since 2.0.0 + * @category error handling + */ + (pf: (cause: Cause.Cause) => Option.Option>): (self: Stream) => Stream + /** + * Switches over to the stream produced by the provided function in case this + * one fails with some errors. Allows recovery from all causes of failure, + * including interruption if the stream is uninterruptible. + * + * @since 2.0.0 + * @category error handling + */ + ( + self: Stream, + pf: (cause: Cause.Cause) => Option.Option> + ): Stream +} = internal.catchSomeCause + +/** + * Returns a new stream that only emits elements that are not equal to the + * previous element emitted, using natural equality to determine whether two + * elements are equal. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 1, 1, 2, 2, 3, 4).pipe(Stream.changes) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const changes: (self: Stream) => Stream = internal.changes + +/** + * Returns a new stream that only emits elements that are not equal to the + * previous element emitted, using the specified function to determine whether + * two elements are equal. + * + * @since 2.0.0 + * @category utils + */ +export const changesWith: { + /** + * Returns a new stream that only emits elements that are not equal to the + * previous element emitted, using the specified function to determine whether + * two elements are equal. + * + * @since 2.0.0 + * @category utils + */ + (f: (x: A, y: A) => boolean): (self: Stream) => Stream + /** + * Returns a new stream that only emits elements that are not equal to the + * previous element emitted, using the specified function to determine whether + * two elements are equal. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, f: (x: A, y: A) => boolean): Stream +} = internal.changesWith + +/** + * Returns a new stream that only emits elements that are not equal to the + * previous element emitted, using the specified effectual function to + * determine whether two elements are equal. + * + * @since 2.0.0 + * @category utils + */ +export const changesWithEffect: { + /** + * Returns a new stream that only emits elements that are not equal to the + * previous element emitted, using the specified effectual function to + * determine whether two elements are equal. + * + * @since 2.0.0 + * @category utils + */ + (f: (x: A, y: A) => Effect.Effect): (self: Stream) => Stream + /** + * Returns a new stream that only emits elements that are not equal to the + * previous element emitted, using the specified effectual function to + * determine whether two elements are equal. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, f: (x: A, y: A) => Effect.Effect): Stream +} = internal.changesWithEffect + +/** + * Exposes the underlying chunks of the stream as a stream of chunks of + * elements. + * + * @since 2.0.0 + * @category utils + */ +export const chunks: (self: Stream) => Stream, E, R> = internal.chunks + +/** + * Performs the specified stream transformation with the chunk structure of + * the stream exposed. + * + * @since 2.0.0 + * @category utils + */ +export const chunksWith: { + /** + * Performs the specified stream transformation with the chunk structure of + * the stream exposed. + * + * @since 2.0.0 + * @category utils + */ + ( + f: (stream: Stream, E, R>) => Stream, E2, R2> + ): (self: Stream) => Stream + /** + * Performs the specified stream transformation with the chunk structure of + * the stream exposed. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + f: (stream: Stream, E, R>) => Stream, E2, R2> + ): Stream +} = internal.chunksWith + +/** + * Combines the elements from this stream and the specified stream by + * repeatedly applying the function `f` to extract an element using both sides + * and conceptually "offer" it to the destination stream. `f` can maintain + * some internal state to control the combining process, with the initial + * state being specified by `s`. + * + * Where possible, prefer `Stream.combineChunks` for a more efficient + * implementation. + * + * @since 2.0.0 + * @category utils + */ +export const combine: { + /** + * Combines the elements from this stream and the specified stream by + * repeatedly applying the function `f` to extract an element using both sides + * and conceptually "offer" it to the destination stream. `f` can maintain + * some internal state to control the combining process, with the initial + * state being specified by `s`. + * + * Where possible, prefer `Stream.combineChunks` for a more efficient + * implementation. + * + * @since 2.0.0 + * @category utils + */ + ( + that: Stream, + s: S, + f: ( + s: S, + pullLeft: Effect.Effect, R3>, + pullRight: Effect.Effect, R4> + ) => Effect.Effect>, never, R5> + ): (self: Stream) => Stream + /** + * Combines the elements from this stream and the specified stream by + * repeatedly applying the function `f` to extract an element using both sides + * and conceptually "offer" it to the destination stream. `f` can maintain + * some internal state to control the combining process, with the initial + * state being specified by `s`. + * + * Where possible, prefer `Stream.combineChunks` for a more efficient + * implementation. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + that: Stream, + s: S, + f: ( + s: S, + pullLeft: Effect.Effect, R3>, + pullRight: Effect.Effect, R4> + ) => Effect.Effect>, never, R5> + ): Stream +} = internal.combine + +/** + * Combines the chunks from this stream and the specified stream by repeatedly + * applying the function `f` to extract a chunk using both sides and + * conceptually "offer" it to the destination stream. `f` can maintain some + * internal state to control the combining process, with the initial state + * being specified by `s`. + * + * @since 2.0.0 + * @category utils + */ +export const combineChunks: { + /** + * Combines the chunks from this stream and the specified stream by repeatedly + * applying the function `f` to extract a chunk using both sides and + * conceptually "offer" it to the destination stream. `f` can maintain some + * internal state to control the combining process, with the initial state + * being specified by `s`. + * + * @since 2.0.0 + * @category utils + */ + ( + that: Stream, + s: S, + f: ( + s: S, + pullLeft: Effect.Effect, Option.Option, R3>, + pullRight: Effect.Effect, Option.Option, R4> + ) => Effect.Effect, S], Option.Option>, never, R5> + ): (self: Stream) => Stream + /** + * Combines the chunks from this stream and the specified stream by repeatedly + * applying the function `f` to extract a chunk using both sides and + * conceptually "offer" it to the destination stream. `f` can maintain some + * internal state to control the combining process, with the initial state + * being specified by `s`. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + that: Stream, + s: S, + f: ( + s: S, + pullLeft: Effect.Effect, Option.Option, R3>, + pullRight: Effect.Effect, Option.Option, R4> + ) => Effect.Effect, S], Option.Option>, never, R5> + ): Stream +} = internal.combineChunks + +/** + * Concatenates the specified stream with this stream, resulting in a stream + * that emits the elements from this stream and then the elements from the + * specified stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3) + * const s2 = Stream.make(4, 5) + * + * const stream = Stream.concat(s1, s2) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3, 4, 5 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const concat: { + /** + * Concatenates the specified stream with this stream, resulting in a stream + * that emits the elements from this stream and then the elements from the + * specified stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3) + * const s2 = Stream.make(4, 5) + * + * const stream = Stream.concat(s1, s2) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3, 4, 5 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (that: Stream): (self: Stream) => Stream + /** + * Concatenates the specified stream with this stream, resulting in a stream + * that emits the elements from this stream and then the elements from the + * specified stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3) + * const s2 = Stream.make(4, 5) + * + * const stream = Stream.concat(s1, s2) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3, 4, 5 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, that: Stream): Stream +} = internal.concat + +/** + * Concatenates all of the streams in the chunk to one stream. + * + * @example + * ```ts + * import { Chunk, Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3) + * const s2 = Stream.make(4, 5) + * const s3 = Stream.make(6, 7, 8) + * + * const stream = Stream.concatAll(Chunk.make(s1, s2, s3)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ + * // 1, 2, 3, 4, + * // 5, 6, 7, 8 + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const concatAll: (streams: Chunk.Chunk>) => Stream = internal.concatAll + +/** + * Composes this stream with the specified stream to create a cartesian + * product of elements. The `right` stream would be run multiple times, for + * every element in the `left` stream. + * + * See also `Stream.zip` for the more common point-wise variant. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3) + * const s2 = Stream.make("a", "b") + * + * const product = Stream.cross(s1, s2) + * + * Effect.runPromise(Stream.runCollect(product)).then(console.log) + * // { + * // _id: "Chunk", + * // values: [ + * // [ 1, "a" ], [ 1, "b" ], [ 2, "a" ], [ 2, "b" ], [ 3, "a" ], [ 3, "b" ] + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const cross: { + /** + * Composes this stream with the specified stream to create a cartesian + * product of elements. The `right` stream would be run multiple times, for + * every element in the `left` stream. + * + * See also `Stream.zip` for the more common point-wise variant. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3) + * const s2 = Stream.make("a", "b") + * + * const product = Stream.cross(s1, s2) + * + * Effect.runPromise(Stream.runCollect(product)).then(console.log) + * // { + * // _id: "Chunk", + * // values: [ + * // [ 1, "a" ], [ 1, "b" ], [ 2, "a" ], [ 2, "b" ], [ 3, "a" ], [ 3, "b" ] + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (right: Stream): (left: Stream) => Stream<[AL, AR], EL | ER, RL | RR> + /** + * Composes this stream with the specified stream to create a cartesian + * product of elements. The `right` stream would be run multiple times, for + * every element in the `left` stream. + * + * See also `Stream.zip` for the more common point-wise variant. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3) + * const s2 = Stream.make("a", "b") + * + * const product = Stream.cross(s1, s2) + * + * Effect.runPromise(Stream.runCollect(product)).then(console.log) + * // { + * // _id: "Chunk", + * // values: [ + * // [ 1, "a" ], [ 1, "b" ], [ 2, "a" ], [ 2, "b" ], [ 3, "a" ], [ 3, "b" ] + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (left: Stream, right: Stream): Stream<[AL, AR], EL | ER, RL | RR> +} = internal.cross + +/** + * Composes this stream with the specified stream to create a cartesian + * product of elements, but keeps only elements from `left` stream. The `right` + * stream would be run multiple times, for every element in the `left` stream. + * + * See also `Stream.zipLeft` for the more common point-wise variant. + * + * @since 2.0.0 + * @category utils + */ +export const crossLeft: { + /** + * Composes this stream with the specified stream to create a cartesian + * product of elements, but keeps only elements from `left` stream. The `right` + * stream would be run multiple times, for every element in the `left` stream. + * + * See also `Stream.zipLeft` for the more common point-wise variant. + * + * @since 2.0.0 + * @category utils + */ + (right: Stream): (left: Stream) => Stream + /** + * Composes this stream with the specified stream to create a cartesian + * product of elements, but keeps only elements from `left` stream. The `right` + * stream would be run multiple times, for every element in the `left` stream. + * + * See also `Stream.zipLeft` for the more common point-wise variant. + * + * @since 2.0.0 + * @category utils + */ + (left: Stream, right: Stream): Stream +} = internal.crossLeft + +/** + * Composes this stream with the specified stream to create a cartesian + * product of elements, but keeps only elements from the `right` stream. The + * `left` stream would be run multiple times, for every element in the `right` + * stream. + * + * See also `Stream.zipRight` for the more common point-wise variant. + * + * @since 2.0.0 + * @category utils + */ +export const crossRight: { + /** + * Composes this stream with the specified stream to create a cartesian + * product of elements, but keeps only elements from the `right` stream. The + * `left` stream would be run multiple times, for every element in the `right` + * stream. + * + * See also `Stream.zipRight` for the more common point-wise variant. + * + * @since 2.0.0 + * @category utils + */ + (right: Stream): (left: Stream) => Stream + /** + * Composes this stream with the specified stream to create a cartesian + * product of elements, but keeps only elements from the `right` stream. The + * `left` stream would be run multiple times, for every element in the `right` + * stream. + * + * See also `Stream.zipRight` for the more common point-wise variant. + * + * @since 2.0.0 + * @category utils + */ + (left: Stream, right: Stream): Stream +} = internal.crossRight + +/** + * Composes this stream with the specified stream to create a cartesian + * product of elements with a specified function. The `right` stream would be + * run multiple times, for every element in the `left` stream. + * + * See also `Stream.zipWith` for the more common point-wise variant. + * + * @since 2.0.0 + * @category utils + */ +export const crossWith: { + /** + * Composes this stream with the specified stream to create a cartesian + * product of elements with a specified function. The `right` stream would be + * run multiple times, for every element in the `left` stream. + * + * See also `Stream.zipWith` for the more common point-wise variant. + * + * @since 2.0.0 + * @category utils + */ + (right: Stream, f: (left: AL, right: AR) => A): (left: Stream) => Stream + /** + * Composes this stream with the specified stream to create a cartesian + * product of elements with a specified function. The `right` stream would be + * run multiple times, for every element in the `left` stream. + * + * See also `Stream.zipWith` for the more common point-wise variant. + * + * @since 2.0.0 + * @category utils + */ + ( + left: Stream, + right: Stream, + f: (left: AL, right: AR) => A + ): Stream +} = internal.crossWith + +/** + * Delays the emission of values by holding new values for a set duration. If + * no new values arrive during that time the value is emitted, however if a + * new value is received during the holding period the previous value is + * discarded and the process is repeated with the new value. + * + * This operator is useful if you have a stream of "bursty" events which + * eventually settle down and you only need the final event of the burst. For + * example, a search engine may only want to initiate a search after a user + * has paused typing so as to not prematurely recommend results. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * let last = Date.now() + * const log = (message: string) => + * Effect.sync(() => { + * const end = Date.now() + * console.log(`${message} after ${end - last}ms`) + * last = end + * }) + * + * const stream = Stream.make(1, 2, 3).pipe( + * Stream.concat( + * Stream.fromEffect(Effect.sleep("200 millis").pipe(Effect.as(4))) // Emit 4 after 200 ms + * ), + * Stream.concat(Stream.make(5, 6)), // Continue with more rapid values + * Stream.concat( + * Stream.fromEffect(Effect.sleep("150 millis").pipe(Effect.as(7))) // Emit 7 after 150 ms + * ), + * Stream.concat(Stream.make(8)), + * Stream.tap((n) => log(`Received ${n}`)), + * Stream.debounce("100 millis"), // Only emit values after a pause of at least 100 milliseconds, + * Stream.tap((n) => log(`> Emitted ${n}`)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Received 1 after 5ms + * // Received 2 after 2ms + * // Received 3 after 0ms + * // > Emitted 3 after 104ms + * // Received 4 after 99ms + * // Received 5 after 1ms + * // Received 6 after 0ms + * // > Emitted 6 after 101ms + * // Received 7 after 50ms + * // Received 8 after 1ms + * // > Emitted 8 after 101ms + * // { _id: 'Chunk', values: [ 3, 6, 8 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const debounce: { + /** + * Delays the emission of values by holding new values for a set duration. If + * no new values arrive during that time the value is emitted, however if a + * new value is received during the holding period the previous value is + * discarded and the process is repeated with the new value. + * + * This operator is useful if you have a stream of "bursty" events which + * eventually settle down and you only need the final event of the burst. For + * example, a search engine may only want to initiate a search after a user + * has paused typing so as to not prematurely recommend results. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * let last = Date.now() + * const log = (message: string) => + * Effect.sync(() => { + * const end = Date.now() + * console.log(`${message} after ${end - last}ms`) + * last = end + * }) + * + * const stream = Stream.make(1, 2, 3).pipe( + * Stream.concat( + * Stream.fromEffect(Effect.sleep("200 millis").pipe(Effect.as(4))) // Emit 4 after 200 ms + * ), + * Stream.concat(Stream.make(5, 6)), // Continue with more rapid values + * Stream.concat( + * Stream.fromEffect(Effect.sleep("150 millis").pipe(Effect.as(7))) // Emit 7 after 150 ms + * ), + * Stream.concat(Stream.make(8)), + * Stream.tap((n) => log(`Received ${n}`)), + * Stream.debounce("100 millis"), // Only emit values after a pause of at least 100 milliseconds, + * Stream.tap((n) => log(`> Emitted ${n}`)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Received 1 after 5ms + * // Received 2 after 2ms + * // Received 3 after 0ms + * // > Emitted 3 after 104ms + * // Received 4 after 99ms + * // Received 5 after 1ms + * // Received 6 after 0ms + * // > Emitted 6 after 101ms + * // Received 7 after 50ms + * // Received 8 after 1ms + * // > Emitted 8 after 101ms + * // { _id: 'Chunk', values: [ 3, 6, 8 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (duration: Duration.DurationInput): (self: Stream) => Stream + /** + * Delays the emission of values by holding new values for a set duration. If + * no new values arrive during that time the value is emitted, however if a + * new value is received during the holding period the previous value is + * discarded and the process is repeated with the new value. + * + * This operator is useful if you have a stream of "bursty" events which + * eventually settle down and you only need the final event of the burst. For + * example, a search engine may only want to initiate a search after a user + * has paused typing so as to not prematurely recommend results. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * let last = Date.now() + * const log = (message: string) => + * Effect.sync(() => { + * const end = Date.now() + * console.log(`${message} after ${end - last}ms`) + * last = end + * }) + * + * const stream = Stream.make(1, 2, 3).pipe( + * Stream.concat( + * Stream.fromEffect(Effect.sleep("200 millis").pipe(Effect.as(4))) // Emit 4 after 200 ms + * ), + * Stream.concat(Stream.make(5, 6)), // Continue with more rapid values + * Stream.concat( + * Stream.fromEffect(Effect.sleep("150 millis").pipe(Effect.as(7))) // Emit 7 after 150 ms + * ), + * Stream.concat(Stream.make(8)), + * Stream.tap((n) => log(`Received ${n}`)), + * Stream.debounce("100 millis"), // Only emit values after a pause of at least 100 milliseconds, + * Stream.tap((n) => log(`> Emitted ${n}`)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Received 1 after 5ms + * // Received 2 after 2ms + * // Received 3 after 0ms + * // > Emitted 3 after 104ms + * // Received 4 after 99ms + * // Received 5 after 1ms + * // Received 6 after 0ms + * // > Emitted 6 after 101ms + * // Received 7 after 50ms + * // Received 8 after 1ms + * // > Emitted 8 after 101ms + * // { _id: 'Chunk', values: [ 3, 6, 8 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, duration: Duration.DurationInput): Stream +} = internal.debounce + +/** + * The stream that dies with the specified defect. + * + * @since 2.0.0 + * @category constructors + */ +export const die: (defect: unknown) => Stream = internal.die + +/** + * The stream that dies with the specified lazily evaluated defect. + * + * @since 2.0.0 + * @category constructors + */ +export const dieSync: (evaluate: LazyArg) => Stream = internal.dieSync + +/** + * The stream that dies with an exception described by `message`. + * + * @since 2.0.0 + * @category constructors + */ +export const dieMessage: (message: string) => Stream = internal.dieMessage + +/** + * More powerful version of `Stream.broadcast`. Allows to provide a function + * that determines what queues should receive which elements. The decide + * function will receive the indices of the queues in the resulting list. + * + * @since 2.0.0 + * @category utils + */ +export const distributedWith: { + /** + * More powerful version of `Stream.broadcast`. Allows to provide a function + * that determines what queues should receive which elements. The decide + * function will receive the indices of the queues in the resulting list. + * + * @since 2.0.0 + * @category utils + */ + ( + options: { + readonly size: N + readonly maximumLag: number + readonly decide: (a: A) => Effect.Effect> + } + ): ( + self: Stream + ) => Effect.Effect>>>, never, Scope.Scope | R> + /** + * More powerful version of `Stream.broadcast`. Allows to provide a function + * that determines what queues should receive which elements. The decide + * function will receive the indices of the queues in the resulting list. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + options: { + readonly size: N + readonly maximumLag: number + readonly decide: (a: A) => Effect.Effect> + } + ): Effect.Effect>>>, never, Scope.Scope | R> +} = internal.distributedWith + +/** + * More powerful version of `Stream.distributedWith`. This returns a function + * that will produce new queues and corresponding indices. You can also + * provide a function that will be executed after the final events are + * enqueued in all queues. Shutdown of the queues is handled by the driver. + * Downstream users can also shutdown queues manually. In this case the driver + * will continue but no longer backpressure on them. + * + * @since 2.0.0 + * @category utils + */ +export const distributedWithDynamic: { + /** + * More powerful version of `Stream.distributedWith`. This returns a function + * that will produce new queues and corresponding indices. You can also + * provide a function that will be executed after the final events are + * enqueued in all queues. Shutdown of the queues is handled by the driver. + * Downstream users can also shutdown queues manually. In this case the driver + * will continue but no longer backpressure on them. + * + * @since 2.0.0 + * @category utils + */ + ( + options: { readonly maximumLag: number; readonly decide: (a: A) => Effect.Effect, never, never> } + ): ( + self: Stream + ) => Effect.Effect< + Effect.Effect<[number, Queue.Dequeue>>], never, never>, + never, + Scope.Scope | R + > + /** + * More powerful version of `Stream.distributedWith`. This returns a function + * that will produce new queues and corresponding indices. You can also + * provide a function that will be executed after the final events are + * enqueued in all queues. Shutdown of the queues is handled by the driver. + * Downstream users can also shutdown queues manually. In this case the driver + * will continue but no longer backpressure on them. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + options: { readonly maximumLag: number; readonly decide: (a: A) => Effect.Effect, never, never> } + ): Effect.Effect< + Effect.Effect<[number, Queue.Dequeue>>], never, never>, + never, + Scope.Scope | R + > +} = internal.distributedWithDynamic + +/** + * Converts this stream to a stream that executes its effects but emits no + * elements. Useful for sequencing effects using streams: + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * // We create a stream and immediately drain it. + * const stream = Stream.range(1, 6).pipe(Stream.drain) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const drain: (self: Stream) => Stream = internal.drain + +/** + * Drains the provided stream in the background for as long as this stream is + * running. If this stream ends before `other`, `other` will be interrupted. + * If `other` fails, this stream will fail with that error. + * + * @since 2.0.0 + * @category utils + */ +export const drainFork: { + /** + * Drains the provided stream in the background for as long as this stream is + * running. If this stream ends before `other`, `other` will be interrupted. + * If `other` fails, this stream will fail with that error. + * + * @since 2.0.0 + * @category utils + */ + (that: Stream): (self: Stream) => Stream + /** + * Drains the provided stream in the background for as long as this stream is + * running. If this stream ends before `other`, `other` will be interrupted. + * If `other` fails, this stream will fail with that error. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, that: Stream): Stream +} = internal.drainFork + +/** + * Drops the specified number of elements from this stream. + * + * @since 2.0.0 + * @category utils + */ +export const drop: { + /** + * Drops the specified number of elements from this stream. + * + * @since 2.0.0 + * @category utils + */ + (n: number): (self: Stream) => Stream + /** + * Drops the specified number of elements from this stream. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, n: number): Stream +} = internal.drop + +/** + * Drops the last specified number of elements from this stream. + * + * @note This combinator keeps `n` elements in memory. Be careful with big + * numbers. + * @since 2.0.0 + * @category utils + */ +export const dropRight: { + /** + * Drops the last specified number of elements from this stream. + * + * @note This combinator keeps `n` elements in memory. Be careful with big + * numbers. + * @since 2.0.0 + * @category utils + */ + (n: number): (self: Stream) => Stream + /** + * Drops the last specified number of elements from this stream. + * + * @note This combinator keeps `n` elements in memory. Be careful with big + * numbers. + * @since 2.0.0 + * @category utils + */ + (self: Stream, n: number): Stream +} = internal.dropRight + +/** + * Drops all elements of the stream until the specified predicate evaluates to + * `true`. + * + * @since 2.0.0 + * @category utils + */ +export const dropUntil: { + /** + * Drops all elements of the stream until the specified predicate evaluates to + * `true`. + * + * @since 2.0.0 + * @category utils + */ + (predicate: Predicate>): (self: Stream) => Stream + /** + * Drops all elements of the stream until the specified predicate evaluates to + * `true`. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, predicate: Predicate): Stream +} = internal.dropUntil + +/** + * Drops all elements of the stream until the specified effectful predicate + * evaluates to `true`. + * + * @since 2.0.0 + * @category utils + */ +export const dropUntilEffect: { + /** + * Drops all elements of the stream until the specified effectful predicate + * evaluates to `true`. + * + * @since 2.0.0 + * @category utils + */ + (predicate: (a: NoInfer) => Effect.Effect): (self: Stream) => Stream + /** + * Drops all elements of the stream until the specified effectful predicate + * evaluates to `true`. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + predicate: (a: NoInfer) => Effect.Effect + ): Stream +} = internal.dropUntilEffect + +/** + * Drops all elements of the stream for as long as the specified predicate + * evaluates to `true`. + * + * @since 2.0.0 + * @category utils + */ +export const dropWhile: { + /** + * Drops all elements of the stream for as long as the specified predicate + * evaluates to `true`. + * + * @since 2.0.0 + * @category utils + */ + (predicate: Predicate>): (self: Stream) => Stream + /** + * Drops all elements of the stream for as long as the specified predicate + * evaluates to `true`. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, predicate: Predicate): Stream +} = internal.dropWhile + +/** + * Drops all elements of the stream for as long as the specified predicate + * produces an effect that evalutates to `true` + * + * @since 2.0.0 + * @category utils + */ +export const dropWhileEffect: { + /** + * Drops all elements of the stream for as long as the specified predicate + * produces an effect that evalutates to `true` + * + * @since 2.0.0 + * @category utils + */ + (predicate: (a: NoInfer) => Effect.Effect): (self: Stream) => Stream + /** + * Drops all elements of the stream for as long as the specified predicate + * produces an effect that evalutates to `true` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, predicate: (a: A) => Effect.Effect): Stream +} = internal.dropWhileEffect + +/** + * Returns a stream whose failures and successes have been lifted into an + * `Either`. The resulting stream cannot fail, because the failures have been + * exposed as part of the `Either` success case. + * + * @note The stream will end as soon as the first error occurs. + * + * @since 2.0.0 + * @category utils + */ +export const either: (self: Stream) => Stream, never, R> = internal.either + +/** + * The empty stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.empty + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const empty: Stream = internal.empty + +/** + * Executes the provided finalizer after this stream's finalizers run. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const program = Stream.fromEffect(Console.log("Application Logic.")).pipe( + * Stream.concat(Stream.finalizer(Console.log("Finalizing the stream"))), + * Stream.ensuring( + * Console.log("Doing some other works after stream's finalization") + * ) + * ) + * + * Effect.runPromise(Stream.runCollect(program)).then(console.log) + * // Application Logic. + * // Finalizing the stream + * // Doing some other works after stream's finalization + * // { _id: 'Chunk', values: [ undefined, undefined ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const ensuring: { + /** + * Executes the provided finalizer after this stream's finalizers run. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const program = Stream.fromEffect(Console.log("Application Logic.")).pipe( + * Stream.concat(Stream.finalizer(Console.log("Finalizing the stream"))), + * Stream.ensuring( + * Console.log("Doing some other works after stream's finalization") + * ) + * ) + * + * Effect.runPromise(Stream.runCollect(program)).then(console.log) + * // Application Logic. + * // Finalizing the stream + * // Doing some other works after stream's finalization + * // { _id: 'Chunk', values: [ undefined, undefined ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (finalizer: Effect.Effect): (self: Stream) => Stream + /** + * Executes the provided finalizer after this stream's finalizers run. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const program = Stream.fromEffect(Console.log("Application Logic.")).pipe( + * Stream.concat(Stream.finalizer(Console.log("Finalizing the stream"))), + * Stream.ensuring( + * Console.log("Doing some other works after stream's finalization") + * ) + * ) + * + * Effect.runPromise(Stream.runCollect(program)).then(console.log) + * // Application Logic. + * // Finalizing the stream + * // Doing some other works after stream's finalization + * // { _id: 'Chunk', values: [ undefined, undefined ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, finalizer: Effect.Effect): Stream +} = internal.ensuring + +/** + * Executes the provided finalizer after this stream's finalizers run. + * + * @since 2.0.0 + * @category utils + */ +export const ensuringWith: { + /** + * Executes the provided finalizer after this stream's finalizers run. + * + * @since 2.0.0 + * @category utils + */ + ( + finalizer: (exit: Exit.Exit) => Effect.Effect + ): (self: Stream) => Stream + /** + * Executes the provided finalizer after this stream's finalizers run. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + finalizer: (exit: Exit.Exit) => Effect.Effect + ): Stream +} = internal.ensuringWith + +/** + * Accesses the whole context of the stream. + * + * @since 2.0.0 + * @category context + */ +export const context: () => Stream, never, R> = internal.context + +/** + * Accesses the context of the stream. + * + * @since 2.0.0 + * @category context + */ +export const contextWith: (f: (env: Context.Context) => A) => Stream = internal.contextWith + +/** + * Accesses the context of the stream in the context of an effect. + * + * @since 2.0.0 + * @category context + */ +export const contextWithEffect: ( + f: (env: Context.Context) => Effect.Effect +) => Stream = internal.contextWithEffect + +/** + * Accesses the context of the stream in the context of a stream. + * + * @since 2.0.0 + * @category context + */ +export const contextWithStream: ( + f: (env: Context.Context) => Stream +) => Stream = internal.contextWithStream + +/** + * Creates a stream that executes the specified effect but emits no elements. + * + * @since 2.0.0 + * @category constructors + */ +export const execute: (effect: Effect.Effect) => Stream = internal.execute + +/** + * Terminates with the specified error. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.fail("Uh oh!") + * + * Effect.runPromiseExit(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Exit', + * // _tag: 'Failure', + * // cause: { _id: 'Cause', _tag: 'Fail', failure: 'Uh oh!' } + * // } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const fail: (error: E) => Stream = internal.fail + +/** + * Terminates with the specified lazily evaluated error. + * + * @since 2.0.0 + * @category constructors + */ +export const failSync: (evaluate: LazyArg) => Stream = internal.failSync + +/** + * The stream that always fails with the specified `Cause`. + * + * @since 2.0.0 + * @category constructors + */ +export const failCause: (cause: Cause.Cause) => Stream = internal.failCause + +/** + * The stream that always fails with the specified lazily evaluated `Cause`. + * + * @since 2.0.0 + * @category constructors + */ +export const failCauseSync: (evaluate: LazyArg>) => Stream = internal.failCauseSync + +/** + * Filters the elements emitted by this stream using the provided function. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(1, 11).pipe(Stream.filter((n) => n % 2 === 0)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 2, 4, 6, 8, 10 ] } + * ``` + * + * @since 2.0.0 + * @category filtering + */ +export const filter: { + /** + * Filters the elements emitted by this stream using the provided function. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(1, 11).pipe(Stream.filter((n) => n % 2 === 0)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 2, 4, 6, 8, 10 ] } + * ``` + * + * @since 2.0.0 + * @category filtering + */ + (refinement: Refinement, B>): (self: Stream) => Stream + /** + * Filters the elements emitted by this stream using the provided function. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(1, 11).pipe(Stream.filter((n) => n % 2 === 0)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 2, 4, 6, 8, 10 ] } + * ``` + * + * @since 2.0.0 + * @category filtering + */ + (predicate: Predicate): (self: Stream) => Stream + /** + * Filters the elements emitted by this stream using the provided function. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(1, 11).pipe(Stream.filter((n) => n % 2 === 0)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 2, 4, 6, 8, 10 ] } + * ``` + * + * @since 2.0.0 + * @category filtering + */ + (self: Stream, refinement: Refinement): Stream + /** + * Filters the elements emitted by this stream using the provided function. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(1, 11).pipe(Stream.filter((n) => n % 2 === 0)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 2, 4, 6, 8, 10 ] } + * ``` + * + * @since 2.0.0 + * @category filtering + */ + (self: Stream, predicate: Predicate): Stream +} = internal.filter + +/** + * Effectfully filters the elements emitted by this stream. + * + * @since 2.0.0 + * @category filtering + */ +export const filterEffect: { + /** + * Effectfully filters the elements emitted by this stream. + * + * @since 2.0.0 + * @category filtering + */ + (f: (a: NoInfer) => Effect.Effect): (self: Stream) => Stream + /** + * Effectfully filters the elements emitted by this stream. + * + * @since 2.0.0 + * @category filtering + */ + (self: Stream, f: (a: A) => Effect.Effect): Stream +} = internal.filterEffect + +/** + * Performs a filter and map in a single step. + * + * @since 2.0.0 + * @category utils + */ +export const filterMap: { + /** + * Performs a filter and map in a single step. + * + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option): (self: Stream) => Stream + /** + * Performs a filter and map in a single step. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, pf: (a: A) => Option.Option): Stream +} = internal.filterMap + +/** + * Performs an effectful filter and map in a single step. + * + * @since 2.0.0 + * @category utils + */ +export const filterMapEffect: { + /** + * Performs an effectful filter and map in a single step. + * + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option>): (self: Stream) => Stream + /** + * Performs an effectful filter and map in a single step. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + pf: (a: A) => Option.Option> + ): Stream +} = internal.filterMapEffect + +/** + * Transforms all elements of the stream for as long as the specified partial + * function is defined. + * + * @since 2.0.0 + * @category utils + */ +export const filterMapWhile: { + /** + * Transforms all elements of the stream for as long as the specified partial + * function is defined. + * + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option): (self: Stream) => Stream + /** + * Transforms all elements of the stream for as long as the specified partial + * function is defined. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, pf: (a: A) => Option.Option): Stream +} = internal.filterMapWhile + +/** + * Effectfully transforms all elements of the stream for as long as the + * specified partial function is defined. + * + * @since 2.0.0 + * @category utils + */ +export const filterMapWhileEffect: { + /** + * Effectfully transforms all elements of the stream for as long as the + * specified partial function is defined. + * + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option>): (self: Stream) => Stream + /** + * Effectfully transforms all elements of the stream for as long as the + * specified partial function is defined. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + pf: (a: A) => Option.Option> + ): Stream +} = internal.filterMapWhileEffect + +/** + * Creates a one-element stream that never fails and executes the finalizer + * when it ends. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const application = Stream.fromEffect(Console.log("Application Logic.")) + * + * const deleteDir = (dir: string) => Console.log(`Deleting dir: ${dir}`) + * + * const program = application.pipe( + * Stream.concat( + * Stream.finalizer( + * deleteDir("tmp").pipe( + * Effect.andThen(Console.log("Temporary directory was deleted.")) + * ) + * ) + * ) + * ) + * + * Effect.runPromise(Stream.runCollect(program)).then(console.log) + * // Application Logic. + * // Deleting dir: tmp + * // Temporary directory was deleted. + * // { _id: 'Chunk', values: [ undefined, undefined ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const finalizer: (finalizer: Effect.Effect) => Stream = internal.finalizer + +/** + * Finds the first element emitted by this stream that satisfies the provided + * predicate. + * + * @since 2.0.0 + * @category elements + */ +export const find: { + /** + * Finds the first element emitted by this stream that satisfies the provided + * predicate. + * + * @since 2.0.0 + * @category elements + */ + (refinement: Refinement, B>): (self: Stream) => Stream + /** + * Finds the first element emitted by this stream that satisfies the provided + * predicate. + * + * @since 2.0.0 + * @category elements + */ + (predicate: Predicate>): (self: Stream) => Stream + /** + * Finds the first element emitted by this stream that satisfies the provided + * predicate. + * + * @since 2.0.0 + * @category elements + */ + (self: Stream, refinement: Refinement): Stream + /** + * Finds the first element emitted by this stream that satisfies the provided + * predicate. + * + * @since 2.0.0 + * @category elements + */ + (self: Stream, predicate: Predicate): Stream +} = internal.find + +/** + * Finds the first element emitted by this stream that satisfies the provided + * effectful predicate. + * + * @since 2.0.0 + * @category elements + */ +export const findEffect: { + /** + * Finds the first element emitted by this stream that satisfies the provided + * effectful predicate. + * + * @since 2.0.0 + * @category elements + */ + (predicate: (a: NoInfer) => Effect.Effect): (self: Stream) => Stream + /** + * Finds the first element emitted by this stream that satisfies the provided + * effectful predicate. + * + * @since 2.0.0 + * @category elements + */ + ( + self: Stream, + predicate: (a: NoInfer) => Effect.Effect + ): Stream +} = internal.findEffect + +/** + * Returns a stream made of the concatenation in strict order of all the + * streams produced by passing each element of this stream to `f0` + * + * @since 2.0.0 + * @category sequencing + */ +export const flatMap: { + /** + * Returns a stream made of the concatenation in strict order of all the + * streams produced by passing each element of this stream to `f0` + * + * @since 2.0.0 + * @category sequencing + */ + ( + f: (a: A) => Stream, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly bufferSize?: number | undefined + readonly switch?: boolean | undefined + } | undefined + ): (self: Stream) => Stream + /** + * Returns a stream made of the concatenation in strict order of all the + * streams produced by passing each element of this stream to `f0` + * + * @since 2.0.0 + * @category sequencing + */ + ( + self: Stream, + f: (a: A) => Stream, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly bufferSize?: number | undefined + readonly switch?: boolean | undefined + } | undefined + ): Stream +} = internal.flatMap + +/** + * Flattens this stream-of-streams into a stream made of the concatenation in + * strict order of all the streams. + * + * @since 2.0.0 + * @category sequencing + */ +export const flatten: { + /** + * Flattens this stream-of-streams into a stream made of the concatenation in + * strict order of all the streams. + * + * @since 2.0.0 + * @category sequencing + */ + ( + options?: + | { readonly concurrency?: number | "unbounded" | undefined; readonly bufferSize?: number | undefined } + | undefined + ): (self: Stream, E, R>) => Stream + /** + * Flattens this stream-of-streams into a stream made of the concatenation in + * strict order of all the streams. + * + * @since 2.0.0 + * @category sequencing + */ + ( + self: Stream, E, R>, + options?: + | { readonly concurrency?: number | "unbounded" | undefined; readonly bufferSize?: number | undefined } + | undefined + ): Stream +} = internal.flatten + +/** + * Submerges the chunks carried by this stream into the stream's structure, + * while still preserving them. + * + * @since 2.0.0 + * @category sequencing + */ +export const flattenChunks: (self: Stream, E, R>) => Stream = internal.flattenChunks + +/** + * Flattens `Effect` values into the stream's structure, preserving all + * information about the effect. + * + * @since 2.0.0 + * @category sequencing + */ +export const flattenEffect: { + /** + * Flattens `Effect` values into the stream's structure, preserving all + * information about the effect. + * + * @since 2.0.0 + * @category sequencing + */ + ( + options?: + | { readonly concurrency?: number | "unbounded" | undefined; readonly unordered?: boolean | undefined } + | undefined + ): (self: Stream, E, R>) => Stream + /** + * Flattens `Effect` values into the stream's structure, preserving all + * information about the effect. + * + * @since 2.0.0 + * @category sequencing + */ + ( + self: Stream, E, R>, + options?: + | { readonly concurrency?: number | "unbounded" | undefined; readonly unordered?: boolean | undefined } + | undefined + ): Stream +} = internal.flattenEffect + +/** + * Unwraps `Exit` values that also signify end-of-stream by failing with `None`. + * + * @since 2.0.0 + * @category sequencing + */ +export const flattenExitOption: ( + self: Stream>, E, R> +) => Stream = internal.flattenExitOption + +/** + * Submerges the iterables carried by this stream into the stream's structure, + * while still preserving them. + * + * @since 2.0.0 + * @category sequencing + */ +export const flattenIterables: (self: Stream, E, R>) => Stream = internal.flattenIterables + +/** + * Unwraps `Exit` values and flatten chunks that also signify end-of-stream + * by failing with `None`. + * + * @since 2.0.0 + * @category sequencing + */ +export const flattenTake: (self: Stream, E, R>) => Stream = + internal.flattenTake + +/** + * Repeats this stream forever. + * + * @since 2.0.0 + * @category utils + */ +export const forever: (self: Stream) => Stream = internal.forever + +/** + * Creates a stream from an `AsyncIterable`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const myAsyncIterable = async function*() { + * yield 1 + * yield 2 + * } + * + * const stream = Stream.fromAsyncIterable( + * myAsyncIterable(), + * (e) => new Error(String(e)) // Error Handling + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const fromAsyncIterable: (iterable: AsyncIterable, onError: (e: unknown) => E) => Stream = + internal.fromAsyncIterable + +/** + * Creates a stream from a `Channel`. + * + * @since 2.0.0 + * @category constructors + */ +export const fromChannel: ( + channel: Channel.Channel, unknown, E, unknown, unknown, unknown, R> +) => Stream = internal.fromChannel + +/** + * Creates a channel from a `Stream`. + * + * @since 2.0.0 + * @category constructors + */ +export const toChannel: ( + stream: Stream +) => Channel.Channel, unknown, E, unknown, unknown, unknown, R> = internal.toChannel + +/** + * Creates a stream from a `Chunk` of values. + * + * @example + * ```ts + * import { Chunk, Effect, Stream } from "effect" + * + * // Creating a stream with values from a single Chunk + * const stream = Stream.fromChunk(Chunk.make(1, 2, 3)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const fromChunk: (chunk: Chunk.Chunk) => Stream = internal.fromChunk + +/** + * Creates a stream from a subscription to a `PubSub`. + * + * **Options** + * + * - `shutdown`: If `true`, the `PubSub` will be shutdown after the stream is evaluated (defaults to `false`) + * + * @since 2.0.0 + * @category constructors + */ +export const fromChunkPubSub: { + /** + * Creates a stream from a subscription to a `PubSub`. + * + * **Options** + * + * - `shutdown`: If `true`, the `PubSub` will be shutdown after the stream is evaluated (defaults to `false`) + * + * @since 2.0.0 + * @category constructors + */ + ( + pubsub: PubSub.PubSub>, + options: { readonly scoped: true; readonly shutdown?: boolean | undefined } + ): Effect.Effect, never, Scope.Scope> + /** + * Creates a stream from a subscription to a `PubSub`. + * + * **Options** + * + * - `shutdown`: If `true`, the `PubSub` will be shutdown after the stream is evaluated (defaults to `false`) + * + * @since 2.0.0 + * @category constructors + */ + ( + pubsub: PubSub.PubSub>, + options?: { readonly scoped?: false | undefined; readonly shutdown?: boolean | undefined } | undefined + ): Stream +} = internal.fromChunkPubSub + +/** + * Creates a stream from a `Queue` of values. + * + * **Options** + * + * - `shutdown`: If `true`, the queue will be shutdown after the stream is evaluated (defaults to `false`) + * + * @since 2.0.0 + * @category constructors + */ +export const fromChunkQueue: ( + queue: Queue.Dequeue>, + options?: { + readonly shutdown?: boolean | undefined + } +) => Stream = internal.fromChunkQueue + +/** + * Creates a stream from an arbitrary number of chunks. + * + * @example + * ```ts + * import { Chunk, Effect, Stream } from "effect" + * + * // Creating a stream with values from multiple Chunks + * const stream = Stream.fromChunks(Chunk.make(1, 2, 3), Chunk.make(4, 5, 6)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3, 4, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const fromChunks: (...chunks: Array>) => Stream = internal.fromChunks + +/** + * Either emits the success value of this effect or terminates the stream + * with the failure value of this effect. + * + * @example + * ```ts + * import { Effect, Random, Stream } from "effect" + * + * const stream = Stream.fromEffect(Random.nextInt) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Example Output: { _id: 'Chunk', values: [ 922694024 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const fromEffect: (effect: Effect.Effect) => Stream = internal.fromEffect + +/** + * Creates a stream from an effect producing a value of type `A` or an empty + * `Stream`. + * + * @since 2.0.0 + * @category constructors + */ +export const fromEffectOption: (effect: Effect.Effect, R>) => Stream = + internal.fromEffectOption + +/** + * Creates a stream from a subscription to a `PubSub`. + * + * **Options** + * + * - `shutdown`: If `true`, the `PubSub` will be shutdown after the stream is evaluated (defaults to `false`) + * + * @since 2.0.0 + * @category constructors + */ +export const fromPubSub: { + /** + * Creates a stream from a subscription to a `PubSub`. + * + * **Options** + * + * - `shutdown`: If `true`, the `PubSub` will be shutdown after the stream is evaluated (defaults to `false`) + * + * @since 2.0.0 + * @category constructors + */ + ( + pubsub: PubSub.PubSub, + options: { + readonly scoped: true + readonly maxChunkSize?: number | undefined + readonly shutdown?: boolean | undefined + } + ): Effect.Effect, never, Scope.Scope> + /** + * Creates a stream from a subscription to a `PubSub`. + * + * **Options** + * + * - `shutdown`: If `true`, the `PubSub` will be shutdown after the stream is evaluated (defaults to `false`) + * + * @since 2.0.0 + * @category constructors + */ + ( + pubsub: PubSub.PubSub, + options?: { + readonly scoped?: false | undefined + readonly maxChunkSize?: number | undefined + readonly shutdown?: boolean | undefined + } | undefined + ): Stream +} = internal.fromPubSub + +/** + * Creates a stream from a subscription to a `TPubSub`. + * + * @since 3.10.0 + * @category constructors + */ +export const fromTPubSub: (pubsub: TPubSub) => Stream = internal.fromTPubSub + +/** + * Creates a new `Stream` from an iterable collection of values. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const numbers = [1, 2, 3] + * + * const stream = Stream.fromIterable(numbers) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const fromIterable: (iterable: Iterable) => Stream = internal.fromIterable + +/** + * Creates a stream from an effect producing a value of type `Iterable`. + * + * @example + * ```ts + * import { Context, Effect, Stream } from "effect" + * + * class Database extends Context.Tag("Database")< + * Database, + * { readonly getUsers: Effect.Effect> } + * >() {} + * + * const getUsers = Database.pipe(Effect.andThen((_) => _.getUsers)) + * + * const stream = Stream.fromIterableEffect(getUsers) + * + * Effect.runPromise( + * Stream.runCollect(stream.pipe(Stream.provideService(Database, { getUsers: Effect.succeed(["user1", "user2"]) }))) + * ).then(console.log) + * // { _id: 'Chunk', values: [ 'user1', 'user2' ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const fromIterableEffect: (effect: Effect.Effect, E, R>) => Stream = + internal.fromIterableEffect + +/** + * Creates a stream from an iterator + * + * @since 2.0.0 + * @category constructors + */ +export const fromIteratorSucceed: (iterator: IterableIterator, maxChunkSize?: number) => Stream = + internal.fromIteratorSucceed + +/** + * Creates a stream from an effect that pulls elements from another stream. + * + * See `Stream.toPull` for reference. + * + * @since 2.0.0 + * @category constructors + */ +export const fromPull: ( + effect: Effect.Effect, Option.Option, R2>, never, Scope.Scope | R> +) => Stream> = internal.fromPull + +/** + * Creates a stream from a queue of values + * + * **Options** + * + * - `maxChunkSize`: The maximum number of queued elements to put in one chunk in the stream + * - `shutdown`: If `true`, the queue will be shutdown after the stream is evaluated (defaults to `false`) + * + * @since 2.0.0 + * @category constructors + */ +export const fromQueue: ( + queue: Queue.Dequeue, + options?: { + readonly maxChunkSize?: number | undefined + readonly shutdown?: boolean | undefined + } +) => Stream = internal.fromQueue + +/** + * Creates a stream from a TQueue of values + * + * @since 3.10.0 + * @category constructors + */ +export const fromTQueue: (queue: TDequeue) => Stream = internal.fromTQueue + +/** + * Creates a stream from a `ReadableStream`. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. + * + * @since 2.0.0 + * @category constructors + */ +export const fromReadableStream: { + /** + * Creates a stream from a `ReadableStream`. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. + * + * @since 2.0.0 + * @category constructors + */ + ( + options: { + readonly evaluate: LazyArg> + readonly onError: (error: unknown) => E + readonly releaseLockOnEnd?: boolean | undefined + } + ): Stream + /** + * Creates a stream from a `ReadableStream`. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. + * + * @since 2.0.0 + * @category constructors + */ + (evaluate: LazyArg>, onError: (error: unknown) => E): Stream +} = internal.fromReadableStream + +/** + * Creates a stream from a `ReadableStreamBYOBReader`. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamBYOBReader. + * + * @since 2.0.0 + * @category constructors + */ +export const fromReadableStreamByob: { + /** + * Creates a stream from a `ReadableStreamBYOBReader`. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamBYOBReader. + * + * @since 2.0.0 + * @category constructors + */ + ( + options: { + readonly evaluate: LazyArg> + readonly onError: (error: unknown) => E + readonly bufferSize?: number | undefined + readonly releaseLockOnEnd?: boolean | undefined + } + ): Stream + /** + * Creates a stream from a `ReadableStreamBYOBReader`. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamBYOBReader. + * + * @since 2.0.0 + * @category constructors + */ + ( + evaluate: LazyArg>, + onError: (error: unknown) => E, + /** Controls the size of the underlying `ArrayBuffer` (defaults to `4096`) */ + allocSize?: number + ): Stream +} = internal.fromReadableStreamByob + +/** + * Creates a stream from a `Schedule` that does not require any further + * input. The stream will emit an element for each value output from the + * schedule, continuing for as long as the schedule continues. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * // Emits values every 1 second for a total of 5 emissions + * const schedule = Schedule.spaced("1 second").pipe( + * Schedule.compose(Schedule.recurs(5)) + * ) + * + * const stream = Stream.fromSchedule(schedule) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const fromSchedule: (schedule: Schedule.Schedule) => Stream = + internal.fromSchedule + +/** + * Creates a pipeline that groups on adjacent keys, calculated by the + * specified function. + * + * @since 2.0.0 + * @category grouping + */ +export const groupAdjacentBy: { + /** + * Creates a pipeline that groups on adjacent keys, calculated by the + * specified function. + * + * @since 2.0.0 + * @category grouping + */ + (f: (a: A) => K): (self: Stream) => Stream<[K, Chunk.NonEmptyChunk], E, R> + /** + * Creates a pipeline that groups on adjacent keys, calculated by the + * specified function. + * + * @since 2.0.0 + * @category grouping + */ + (self: Stream, f: (a: A) => K): Stream<[K, Chunk.NonEmptyChunk], E, R> +} = internal.groupAdjacentBy + +/** + * More powerful version of `Stream.groupByKey`. + * + * @example + * ```ts + * import { Chunk, Effect, GroupBy, Stream } from "effect" + * + * const groupByKeyResult = Stream.fromIterable([ + * "Mary", + * "James", + * "Robert", + * "Patricia", + * "John", + * "Jennifer", + * "Rebecca", + * "Peter" + * ]).pipe( + * Stream.groupBy((name) => Effect.succeed([name.substring(0, 1), name])) + * ) + * + * const stream = GroupBy.evaluate(groupByKeyResult, (key, stream) => + * Stream.fromEffect( + * Stream.runCollect(stream).pipe( + * Effect.andThen((chunk) => [key, Chunk.size(chunk)] as const) + * ) + * )) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ [ 'M', 1 ], [ 'J', 3 ], [ 'R', 2 ], [ 'P', 2 ] ] + * // } + * ``` + * + * @since 2.0.0 + * @category grouping + */ +export const groupBy: { + /** + * More powerful version of `Stream.groupByKey`. + * + * @example + * ```ts + * import { Chunk, Effect, GroupBy, Stream } from "effect" + * + * const groupByKeyResult = Stream.fromIterable([ + * "Mary", + * "James", + * "Robert", + * "Patricia", + * "John", + * "Jennifer", + * "Rebecca", + * "Peter" + * ]).pipe( + * Stream.groupBy((name) => Effect.succeed([name.substring(0, 1), name])) + * ) + * + * const stream = GroupBy.evaluate(groupByKeyResult, (key, stream) => + * Stream.fromEffect( + * Stream.runCollect(stream).pipe( + * Effect.andThen((chunk) => [key, Chunk.size(chunk)] as const) + * ) + * )) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ [ 'M', 1 ], [ 'J', 3 ], [ 'R', 2 ], [ 'P', 2 ] ] + * // } + * ``` + * + * @since 2.0.0 + * @category grouping + */ + ( + f: (a: A) => Effect.Effect, + options?: { readonly bufferSize?: number | undefined } | undefined + ): (self: Stream) => GroupBy.GroupBy + /** + * More powerful version of `Stream.groupByKey`. + * + * @example + * ```ts + * import { Chunk, Effect, GroupBy, Stream } from "effect" + * + * const groupByKeyResult = Stream.fromIterable([ + * "Mary", + * "James", + * "Robert", + * "Patricia", + * "John", + * "Jennifer", + * "Rebecca", + * "Peter" + * ]).pipe( + * Stream.groupBy((name) => Effect.succeed([name.substring(0, 1), name])) + * ) + * + * const stream = GroupBy.evaluate(groupByKeyResult, (key, stream) => + * Stream.fromEffect( + * Stream.runCollect(stream).pipe( + * Effect.andThen((chunk) => [key, Chunk.size(chunk)] as const) + * ) + * )) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ [ 'M', 1 ], [ 'J', 3 ], [ 'R', 2 ], [ 'P', 2 ] ] + * // } + * ``` + * + * @since 2.0.0 + * @category grouping + */ + ( + self: Stream, + f: (a: A) => Effect.Effect, + options?: { readonly bufferSize?: number | undefined } | undefined + ): GroupBy.GroupBy +} = groupBy_.groupBy + +/** + * Partition a stream using a function and process each stream individually. + * This returns a data structure that can be used to further filter down which + * groups shall be processed. + * + * After calling apply on the GroupBy object, the remaining groups will be + * processed in parallel and the resulting streams merged in a + * nondeterministic fashion. + * + * Up to `buffer` elements may be buffered in any group stream before the + * producer is backpressured. Take care to consume from all streams in order + * to prevent deadlocks. + * + * For example, to collect the first 2 words for every starting letter from a + * stream of words: + * + * ```ts + * import { pipe, GroupBy, Stream } from "effect" + * + * pipe( + * Stream.fromIterable(["hello", "world", "hi", "holla"]), + * Stream.groupByKey((word) => word[0]), + * GroupBy.evaluate((key, stream) => + * pipe( + * stream, + * Stream.take(2), + * Stream.map((words) => [key, words] as const) + * ) + * ) + * ) + * ``` + * + * @since 2.0.0 + * @category grouping + */ +export const groupByKey: { + /** + * Partition a stream using a function and process each stream individually. + * This returns a data structure that can be used to further filter down which + * groups shall be processed. + * + * After calling apply on the GroupBy object, the remaining groups will be + * processed in parallel and the resulting streams merged in a + * nondeterministic fashion. + * + * Up to `buffer` elements may be buffered in any group stream before the + * producer is backpressured. Take care to consume from all streams in order + * to prevent deadlocks. + * + * For example, to collect the first 2 words for every starting letter from a + * stream of words: + * + * ```ts + * import { pipe, GroupBy, Stream } from "effect" + * + * pipe( + * Stream.fromIterable(["hello", "world", "hi", "holla"]), + * Stream.groupByKey((word) => word[0]), + * GroupBy.evaluate((key, stream) => + * pipe( + * stream, + * Stream.take(2), + * Stream.map((words) => [key, words] as const) + * ) + * ) + * ) + * ``` + * + * @since 2.0.0 + * @category grouping + */ + ( + f: (a: A) => K, + options?: { + readonly bufferSize?: number | undefined + } + ): (self: Stream) => GroupBy.GroupBy + /** + * Partition a stream using a function and process each stream individually. + * This returns a data structure that can be used to further filter down which + * groups shall be processed. + * + * After calling apply on the GroupBy object, the remaining groups will be + * processed in parallel and the resulting streams merged in a + * nondeterministic fashion. + * + * Up to `buffer` elements may be buffered in any group stream before the + * producer is backpressured. Take care to consume from all streams in order + * to prevent deadlocks. + * + * For example, to collect the first 2 words for every starting letter from a + * stream of words: + * + * ```ts + * import { pipe, GroupBy, Stream } from "effect" + * + * pipe( + * Stream.fromIterable(["hello", "world", "hi", "holla"]), + * Stream.groupByKey((word) => word[0]), + * GroupBy.evaluate((key, stream) => + * pipe( + * stream, + * Stream.take(2), + * Stream.map((words) => [key, words] as const) + * ) + * ) + * ) + * ``` + * + * @since 2.0.0 + * @category grouping + */ + ( + self: Stream, + f: (a: A) => K, + options?: { + readonly bufferSize?: number | undefined + } + ): GroupBy.GroupBy +} = groupBy_.groupByKey + +/** + * Partitions the stream with specified `chunkSize`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(0, 8).pipe(Stream.grouped(3)) + * + * Effect.runPromise(Stream.runCollect(stream)).then((chunks) => console.log("%o", chunks)) + * // { + * // _id: 'Chunk', + * // values: [ + * // { _id: 'Chunk', values: [ 0, 1, 2, [length]: 3 ] }, + * // { _id: 'Chunk', values: [ 3, 4, 5, [length]: 3 ] }, + * // { _id: 'Chunk', values: [ 6, 7, 8, [length]: 3 ] }, + * // [length]: 3 + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category grouping + */ +export const grouped: { + /** + * Partitions the stream with specified `chunkSize`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(0, 8).pipe(Stream.grouped(3)) + * + * Effect.runPromise(Stream.runCollect(stream)).then((chunks) => console.log("%o", chunks)) + * // { + * // _id: 'Chunk', + * // values: [ + * // { _id: 'Chunk', values: [ 0, 1, 2, [length]: 3 ] }, + * // { _id: 'Chunk', values: [ 3, 4, 5, [length]: 3 ] }, + * // { _id: 'Chunk', values: [ 6, 7, 8, [length]: 3 ] }, + * // [length]: 3 + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category grouping + */ + (chunkSize: number): (self: Stream) => Stream, E, R> + /** + * Partitions the stream with specified `chunkSize`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(0, 8).pipe(Stream.grouped(3)) + * + * Effect.runPromise(Stream.runCollect(stream)).then((chunks) => console.log("%o", chunks)) + * // { + * // _id: 'Chunk', + * // values: [ + * // { _id: 'Chunk', values: [ 0, 1, 2, [length]: 3 ] }, + * // { _id: 'Chunk', values: [ 3, 4, 5, [length]: 3 ] }, + * // { _id: 'Chunk', values: [ 6, 7, 8, [length]: 3 ] }, + * // [length]: 3 + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category grouping + */ + (self: Stream, chunkSize: number): Stream, E, R> +} = internal.grouped + +/** + * Partitions the stream with the specified `chunkSize` or until the specified + * `duration` has passed, whichever is satisfied first. + * + * @example + * ```ts + * import { Chunk, Effect, Schedule, Stream } from "effect" + * + * const stream = Stream.range(0, 9).pipe( + * Stream.repeat(Schedule.spaced("1 second")), + * Stream.groupedWithin(18, "1.5 seconds"), + * Stream.take(3) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then((chunks) => console.log(Chunk.toArray(chunks))) + * // [ + * // { + * // _id: 'Chunk', + * // values: [ + * // 0, 1, 2, 3, 4, 5, 6, + * // 7, 8, 9, 0, 1, 2, 3, + * // 4, 5, 6, 7 + * // ] + * // }, + * // { + * // _id: 'Chunk', + * // values: [ + * // 8, 9, 0, 1, 2, + * // 3, 4, 5, 6, 7, + * // 8, 9 + * // ] + * // }, + * // { + * // _id: 'Chunk', + * // values: [ + * // 0, 1, 2, 3, 4, 5, 6, + * // 7, 8, 9, 0, 1, 2, 3, + * // 4, 5, 6, 7 + * // ] + * // } + * // ] + * ``` + * + * @since 2.0.0 + * @category grouping + */ +export const groupedWithin: { + /** + * Partitions the stream with the specified `chunkSize` or until the specified + * `duration` has passed, whichever is satisfied first. + * + * @example + * ```ts + * import { Chunk, Effect, Schedule, Stream } from "effect" + * + * const stream = Stream.range(0, 9).pipe( + * Stream.repeat(Schedule.spaced("1 second")), + * Stream.groupedWithin(18, "1.5 seconds"), + * Stream.take(3) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then((chunks) => console.log(Chunk.toArray(chunks))) + * // [ + * // { + * // _id: 'Chunk', + * // values: [ + * // 0, 1, 2, 3, 4, 5, 6, + * // 7, 8, 9, 0, 1, 2, 3, + * // 4, 5, 6, 7 + * // ] + * // }, + * // { + * // _id: 'Chunk', + * // values: [ + * // 8, 9, 0, 1, 2, + * // 3, 4, 5, 6, 7, + * // 8, 9 + * // ] + * // }, + * // { + * // _id: 'Chunk', + * // values: [ + * // 0, 1, 2, 3, 4, 5, 6, + * // 7, 8, 9, 0, 1, 2, 3, + * // 4, 5, 6, 7 + * // ] + * // } + * // ] + * ``` + * + * @since 2.0.0 + * @category grouping + */ + (chunkSize: number, duration: Duration.DurationInput): (self: Stream) => Stream, E, R> + /** + * Partitions the stream with the specified `chunkSize` or until the specified + * `duration` has passed, whichever is satisfied first. + * + * @example + * ```ts + * import { Chunk, Effect, Schedule, Stream } from "effect" + * + * const stream = Stream.range(0, 9).pipe( + * Stream.repeat(Schedule.spaced("1 second")), + * Stream.groupedWithin(18, "1.5 seconds"), + * Stream.take(3) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then((chunks) => console.log(Chunk.toArray(chunks))) + * // [ + * // { + * // _id: 'Chunk', + * // values: [ + * // 0, 1, 2, 3, 4, 5, 6, + * // 7, 8, 9, 0, 1, 2, 3, + * // 4, 5, 6, 7 + * // ] + * // }, + * // { + * // _id: 'Chunk', + * // values: [ + * // 8, 9, 0, 1, 2, + * // 3, 4, 5, 6, 7, + * // 8, 9 + * // ] + * // }, + * // { + * // _id: 'Chunk', + * // values: [ + * // 0, 1, 2, 3, 4, 5, 6, + * // 7, 8, 9, 0, 1, 2, 3, + * // 4, 5, 6, 7 + * // ] + * // } + * // ] + * ``` + * + * @since 2.0.0 + * @category grouping + */ + (self: Stream, chunkSize: number, duration: Duration.DurationInput): Stream, E, R> +} = internal.groupedWithin + +/** + * Specialized version of haltWhen which halts the evaluation of this stream + * after the given duration. + * + * An element in the process of being pulled will not be interrupted when the + * given duration completes. See `interruptAfter` for this behavior. + * + * @since 2.0.0 + * @category utils + */ +export const haltAfter: { + /** + * Specialized version of haltWhen which halts the evaluation of this stream + * after the given duration. + * + * An element in the process of being pulled will not be interrupted when the + * given duration completes. See `interruptAfter` for this behavior. + * + * @since 2.0.0 + * @category utils + */ + (duration: Duration.DurationInput): (self: Stream) => Stream + /** + * Specialized version of haltWhen which halts the evaluation of this stream + * after the given duration. + * + * An element in the process of being pulled will not be interrupted when the + * given duration completes. See `interruptAfter` for this behavior. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, duration: Duration.DurationInput): Stream +} = internal.haltAfter + +/** + * Halts the evaluation of this stream when the provided effect completes. The + * given effect will be forked as part of the returned stream, and its success + * will be discarded. + * + * An element in the process of being pulled will not be interrupted when the + * effect completes. See `interruptWhen` for this behavior. + * + * If the effect completes with a failure, the stream will emit that failure. + * + * @since 2.0.0 + * @category utils + */ +export const haltWhen: { + /** + * Halts the evaluation of this stream when the provided effect completes. The + * given effect will be forked as part of the returned stream, and its success + * will be discarded. + * + * An element in the process of being pulled will not be interrupted when the + * effect completes. See `interruptWhen` for this behavior. + * + * If the effect completes with a failure, the stream will emit that failure. + * + * @since 2.0.0 + * @category utils + */ + (effect: Effect.Effect): (self: Stream) => Stream + /** + * Halts the evaluation of this stream when the provided effect completes. The + * given effect will be forked as part of the returned stream, and its success + * will be discarded. + * + * An element in the process of being pulled will not be interrupted when the + * effect completes. See `interruptWhen` for this behavior. + * + * If the effect completes with a failure, the stream will emit that failure. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, effect: Effect.Effect): Stream +} = internal.haltWhen + +/** + * Halts the evaluation of this stream when the provided promise resolves. + * + * If the promise completes with a failure, the stream will emit that failure. + * + * @since 2.0.0 + * @category utils + */ +export const haltWhenDeferred: { + /** + * Halts the evaluation of this stream when the provided promise resolves. + * + * If the promise completes with a failure, the stream will emit that failure. + * + * @since 2.0.0 + * @category utils + */ + (deferred: Deferred.Deferred): (self: Stream) => Stream + /** + * Halts the evaluation of this stream when the provided promise resolves. + * + * If the promise completes with a failure, the stream will emit that failure. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, deferred: Deferred.Deferred): Stream +} = internal.haltWhenDeferred + +/** + * The identity pipeline, which does not modify streams in any way. + * + * @since 2.0.0 + * @category utils + */ +export const identity: () => Stream = internal.identityStream + +/** + * Interleaves this stream and the specified stream deterministically by + * alternating pulling values from this stream and the specified stream. When + * one stream is exhausted all remaining values in the other stream will be + * pulled. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3) + * const s2 = Stream.make(4, 5, 6) + * + * const stream = Stream.interleave(s1, s2) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 4, 2, 5, 3, 6 ] } + * ``` + * @since 2.0.0 + * @category utils + */ +export const interleave: { + /** + * Interleaves this stream and the specified stream deterministically by + * alternating pulling values from this stream and the specified stream. When + * one stream is exhausted all remaining values in the other stream will be + * pulled. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3) + * const s2 = Stream.make(4, 5, 6) + * + * const stream = Stream.interleave(s1, s2) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 4, 2, 5, 3, 6 ] } + * ``` + * @since 2.0.0 + * @category utils + */ + (that: Stream): (self: Stream) => Stream + /** + * Interleaves this stream and the specified stream deterministically by + * alternating pulling values from this stream and the specified stream. When + * one stream is exhausted all remaining values in the other stream will be + * pulled. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3) + * const s2 = Stream.make(4, 5, 6) + * + * const stream = Stream.interleave(s1, s2) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 4, 2, 5, 3, 6 ] } + * ``` + * @since 2.0.0 + * @category utils + */ + (self: Stream, that: Stream): Stream +} = internal.interleave + +/** + * Combines this stream and the specified stream deterministically using the + * stream of boolean values `pull` to control which stream to pull from next. + * A value of `true` indicates to pull from this stream and a value of `false` + * indicates to pull from the specified stream. Only consumes as many elements + * as requested by the `pull` stream. If either this stream or the specified + * stream are exhausted further requests for values from that stream will be + * ignored. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 3, 5, 7, 9) + * const s2 = Stream.make(2, 4, 6, 8, 10) + * + * const booleanStream = Stream.make(true, false, false).pipe(Stream.forever) + * + * const stream = Stream.interleaveWith(s1, s2, booleanStream) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ + * // 1, 2, 4, 3, 6, + * // 8, 5, 10, 7, 9 + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const interleaveWith: { + /** + * Combines this stream and the specified stream deterministically using the + * stream of boolean values `pull` to control which stream to pull from next. + * A value of `true` indicates to pull from this stream and a value of `false` + * indicates to pull from the specified stream. Only consumes as many elements + * as requested by the `pull` stream. If either this stream or the specified + * stream are exhausted further requests for values from that stream will be + * ignored. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 3, 5, 7, 9) + * const s2 = Stream.make(2, 4, 6, 8, 10) + * + * const booleanStream = Stream.make(true, false, false).pipe(Stream.forever) + * + * const stream = Stream.interleaveWith(s1, s2, booleanStream) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ + * // 1, 2, 4, 3, 6, + * // 8, 5, 10, 7, 9 + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (that: Stream, decider: Stream): (self: Stream) => Stream + /** + * Combines this stream and the specified stream deterministically using the + * stream of boolean values `pull` to control which stream to pull from next. + * A value of `true` indicates to pull from this stream and a value of `false` + * indicates to pull from the specified stream. Only consumes as many elements + * as requested by the `pull` stream. If either this stream or the specified + * stream are exhausted further requests for values from that stream will be + * ignored. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const s1 = Stream.make(1, 3, 5, 7, 9) + * const s2 = Stream.make(2, 4, 6, 8, 10) + * + * const booleanStream = Stream.make(true, false, false).pipe(Stream.forever) + * + * const stream = Stream.interleaveWith(s1, s2, booleanStream) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ + * // 1, 2, 4, 3, 6, + * // 8, 5, 10, 7, 9 + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + that: Stream, + decider: Stream + ): Stream +} = internal.interleaveWith + +/** + * Intersperse stream with provided `element`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3, 4, 5).pipe(Stream.intersperse(0)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ + * // 1, 0, 2, 0, 3, + * // 0, 4, 0, 5 + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const intersperse: { + /** + * Intersperse stream with provided `element`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3, 4, 5).pipe(Stream.intersperse(0)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ + * // 1, 0, 2, 0, 3, + * // 0, 4, 0, 5 + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (element: A2): (self: Stream) => Stream + /** + * Intersperse stream with provided `element`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3, 4, 5).pipe(Stream.intersperse(0)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ + * // 1, 0, 2, 0, 3, + * // 0, 4, 0, 5 + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, element: A2): Stream +} = internal.intersperse + +/** + * Intersperse the specified element, also adding a prefix and a suffix. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3, 4, 5).pipe( + * Stream.intersperseAffixes({ + * start: "[", + * middle: "-", + * end: "]" + * }) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ + * // '[', 1, '-', 2, '-', + * // 3, '-', 4, '-', 5, + * // ']' + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const intersperseAffixes: { + /** + * Intersperse the specified element, also adding a prefix and a suffix. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3, 4, 5).pipe( + * Stream.intersperseAffixes({ + * start: "[", + * middle: "-", + * end: "]" + * }) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ + * // '[', 1, '-', 2, '-', + * // 3, '-', 4, '-', 5, + * // ']' + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (options: { readonly start: A2; readonly middle: A3; readonly end: A4 }): (self: Stream) => Stream + /** + * Intersperse the specified element, also adding a prefix and a suffix. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3, 4, 5).pipe( + * Stream.intersperseAffixes({ + * start: "[", + * middle: "-", + * end: "]" + * }) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ + * // '[', 1, '-', 2, '-', + * // 3, '-', 4, '-', 5, + * // ']' + * // ] + * // } + * ``` + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + options: { readonly start: A2; readonly middle: A3; readonly end: A4 } + ): Stream +} = internal.intersperseAffixes + +/** + * Specialized version of `Stream.interruptWhen` which interrupts the + * evaluation of this stream after the given `Duration`. + * + * @since 2.0.0 + * @category utils + */ +export const interruptAfter: { + /** + * Specialized version of `Stream.interruptWhen` which interrupts the + * evaluation of this stream after the given `Duration`. + * + * @since 2.0.0 + * @category utils + */ + (duration: Duration.DurationInput): (self: Stream) => Stream + /** + * Specialized version of `Stream.interruptWhen` which interrupts the + * evaluation of this stream after the given `Duration`. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, duration: Duration.DurationInput): Stream +} = internal.interruptAfter + +/** + * Interrupts the evaluation of this stream when the provided effect + * completes. The given effect will be forked as part of this stream, and its + * success will be discarded. This combinator will also interrupt any + * in-progress element being pulled from upstream. + * + * If the effect completes with a failure before the stream completes, the + * returned stream will emit that failure. + * + * @since 2.0.0 + * @category utils + */ +export const interruptWhen: { + /** + * Interrupts the evaluation of this stream when the provided effect + * completes. The given effect will be forked as part of this stream, and its + * success will be discarded. This combinator will also interrupt any + * in-progress element being pulled from upstream. + * + * If the effect completes with a failure before the stream completes, the + * returned stream will emit that failure. + * + * @since 2.0.0 + * @category utils + */ + (effect: Effect.Effect): (self: Stream) => Stream + /** + * Interrupts the evaluation of this stream when the provided effect + * completes. The given effect will be forked as part of this stream, and its + * success will be discarded. This combinator will also interrupt any + * in-progress element being pulled from upstream. + * + * If the effect completes with a failure before the stream completes, the + * returned stream will emit that failure. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, effect: Effect.Effect): Stream +} = internal.interruptWhen + +/** + * Interrupts the evaluation of this stream when the provided promise + * resolves. This combinator will also interrupt any in-progress element being + * pulled from upstream. + * + * If the promise completes with a failure, the stream will emit that failure. + * + * @since 2.0.0 + * @category utils + */ +export const interruptWhenDeferred: { + /** + * Interrupts the evaluation of this stream when the provided promise + * resolves. This combinator will also interrupt any in-progress element being + * pulled from upstream. + * + * If the promise completes with a failure, the stream will emit that failure. + * + * @since 2.0.0 + * @category utils + */ + (deferred: Deferred.Deferred): (self: Stream) => Stream + /** + * Interrupts the evaluation of this stream when the provided promise + * resolves. This combinator will also interrupt any in-progress element being + * pulled from upstream. + * + * If the promise completes with a failure, the stream will emit that failure. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, deferred: Deferred.Deferred): Stream +} = internal.interruptWhenDeferred + +/** + * The infinite stream of iterative function application: a, f(a), f(f(a)), + * f(f(f(a))), ... + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * // An infinite Stream of numbers starting from 1 and incrementing + * const stream = Stream.iterate(1, (n) => n + 1) + * + * Effect.runPromise(Stream.runCollect(stream.pipe(Stream.take(10)))).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const iterate: (value: A, next: (value: A) => A) => Stream = internal.iterate + +/** + * Creates a stream from an sequence of values. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const make: >(...as: As) => Stream = internal.make + +/** + * Transforms the elements of this stream using the supplied function. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3).pipe(Stream.map((n) => n + 1)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ +export const map: { + /** + * Transforms the elements of this stream using the supplied function. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3).pipe(Stream.map((n) => n + 1)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + (f: (a: A) => B): (self: Stream) => Stream + /** + * Transforms the elements of this stream using the supplied function. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3).pipe(Stream.map((n) => n + 1)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + (self: Stream, f: (a: A) => B): Stream +} = internal.map + +/** + * Statefully maps over the elements of this stream to produce new elements. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const runningTotal = (stream: Stream.Stream): Stream.Stream => + * stream.pipe(Stream.mapAccum(0, (s, a) => [s + a, s + a])) + * + * // input: 0, 1, 2, 3, 4, 5, 6 + * Effect.runPromise(Stream.runCollect(runningTotal(Stream.range(0, 6)))).then( + * console.log + * ) + * // { _id: "Chunk", values: [ 0, 1, 3, 6, 10, 15, 21 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ +export const mapAccum: { + /** + * Statefully maps over the elements of this stream to produce new elements. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const runningTotal = (stream: Stream.Stream): Stream.Stream => + * stream.pipe(Stream.mapAccum(0, (s, a) => [s + a, s + a])) + * + * // input: 0, 1, 2, 3, 4, 5, 6 + * Effect.runPromise(Stream.runCollect(runningTotal(Stream.range(0, 6)))).then( + * console.log + * ) + * // { _id: "Chunk", values: [ 0, 1, 3, 6, 10, 15, 21 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + (s: S, f: (s: S, a: A) => readonly [S, A2]): (self: Stream) => Stream + /** + * Statefully maps over the elements of this stream to produce new elements. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const runningTotal = (stream: Stream.Stream): Stream.Stream => + * stream.pipe(Stream.mapAccum(0, (s, a) => [s + a, s + a])) + * + * // input: 0, 1, 2, 3, 4, 5, 6 + * Effect.runPromise(Stream.runCollect(runningTotal(Stream.range(0, 6)))).then( + * console.log + * ) + * // { _id: "Chunk", values: [ 0, 1, 3, 6, 10, 15, 21 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + (self: Stream, s: S, f: (s: S, a: A) => readonly [S, A2]): Stream +} = internal.mapAccum + +/** + * Statefully and effectfully maps over the elements of this stream to produce + * new elements. + * + * @since 2.0.0 + * @category mapping + */ +export const mapAccumEffect: { + /** + * Statefully and effectfully maps over the elements of this stream to produce + * new elements. + * + * @since 2.0.0 + * @category mapping + */ + (s: S, f: (s: S, a: A) => Effect.Effect): (self: Stream) => Stream + /** + * Statefully and effectfully maps over the elements of this stream to produce + * new elements. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Stream, + s: S, + f: (s: S, a: A) => Effect.Effect + ): Stream +} = internal.mapAccumEffect + +/** + * Returns a stream whose failure and success channels have been mapped by the + * specified `onFailure` and `onSuccess` functions. + * + * @since 2.0.0 + * @category utils + */ +export const mapBoth: { + /** + * Returns a stream whose failure and success channels have been mapped by the + * specified `onFailure` and `onSuccess` functions. + * + * @since 2.0.0 + * @category utils + */ + ( + options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } + ): (self: Stream) => Stream + /** + * Returns a stream whose failure and success channels have been mapped by the + * specified `onFailure` and `onSuccess` functions. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } + ): Stream +} = internal.mapBoth + +/** + * Transforms the chunks emitted by this stream. + * + * @since 2.0.0 + * @category mapping + */ +export const mapChunks: { + /** + * Transforms the chunks emitted by this stream. + * + * @since 2.0.0 + * @category mapping + */ + (f: (chunk: Chunk.Chunk) => Chunk.Chunk): (self: Stream) => Stream + /** + * Transforms the chunks emitted by this stream. + * + * @since 2.0.0 + * @category mapping + */ + (self: Stream, f: (chunk: Chunk.Chunk) => Chunk.Chunk): Stream +} = internal.mapChunks + +/** + * Effectfully transforms the chunks emitted by this stream. + * + * @since 2.0.0 + * @category mapping + */ +export const mapChunksEffect: { + /** + * Effectfully transforms the chunks emitted by this stream. + * + * @since 2.0.0 + * @category mapping + */ + (f: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2>): (self: Stream) => Stream + /** + * Effectfully transforms the chunks emitted by this stream. + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Stream, + f: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2> + ): Stream +} = internal.mapChunksEffect + +/** + * Maps each element to an iterable, and flattens the iterables into the + * output of this stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const numbers = Stream.make("1-2-3", "4-5", "6").pipe( + * Stream.mapConcat((s) => s.split("-")), + * Stream.map((s) => parseInt(s)) + * ) + * + * Effect.runPromise(Stream.runCollect(numbers)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3, 4, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ +export const mapConcat: { + /** + * Maps each element to an iterable, and flattens the iterables into the + * output of this stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const numbers = Stream.make("1-2-3", "4-5", "6").pipe( + * Stream.mapConcat((s) => s.split("-")), + * Stream.map((s) => parseInt(s)) + * ) + * + * Effect.runPromise(Stream.runCollect(numbers)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3, 4, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + (f: (a: A) => Iterable): (self: Stream) => Stream + /** + * Maps each element to an iterable, and flattens the iterables into the + * output of this stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const numbers = Stream.make("1-2-3", "4-5", "6").pipe( + * Stream.mapConcat((s) => s.split("-")), + * Stream.map((s) => parseInt(s)) + * ) + * + * Effect.runPromise(Stream.runCollect(numbers)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3, 4, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + (self: Stream, f: (a: A) => Iterable): Stream +} = internal.mapConcat + +/** + * Maps each element to a chunk, and flattens the chunks into the output of + * this stream. + * + * @since 2.0.0 + * @category mapping + */ +export const mapConcatChunk: { + /** + * Maps each element to a chunk, and flattens the chunks into the output of + * this stream. + * + * @since 2.0.0 + * @category mapping + */ + (f: (a: A) => Chunk.Chunk): (self: Stream) => Stream + /** + * Maps each element to a chunk, and flattens the chunks into the output of + * this stream. + * + * @since 2.0.0 + * @category mapping + */ + (self: Stream, f: (a: A) => Chunk.Chunk): Stream +} = internal.mapConcatChunk + +/** + * Effectfully maps each element to a chunk, and flattens the chunks into the + * output of this stream. + * + * @since 2.0.0 + * @category mapping + */ +export const mapConcatChunkEffect: { + /** + * Effectfully maps each element to a chunk, and flattens the chunks into the + * output of this stream. + * + * @since 2.0.0 + * @category mapping + */ + (f: (a: A) => Effect.Effect, E2, R2>): (self: Stream) => Stream + /** + * Effectfully maps each element to a chunk, and flattens the chunks into the + * output of this stream. + * + * @since 2.0.0 + * @category mapping + */ + (self: Stream, f: (a: A) => Effect.Effect, E2, R2>): Stream +} = internal.mapConcatChunkEffect + +/** + * Effectfully maps each element to an iterable, and flattens the iterables + * into the output of this stream. + * + * @since 2.0.0 + * @category mapping + */ +export const mapConcatEffect: { + /** + * Effectfully maps each element to an iterable, and flattens the iterables + * into the output of this stream. + * + * @since 2.0.0 + * @category mapping + */ + (f: (a: A) => Effect.Effect, E2, R2>): (self: Stream) => Stream + /** + * Effectfully maps each element to an iterable, and flattens the iterables + * into the output of this stream. + * + * @since 2.0.0 + * @category mapping + */ + (self: Stream, f: (a: A) => Effect.Effect, E2, R2>): Stream +} = internal.mapConcatEffect + +/** + * Maps over elements of the stream with the specified effectful function. + * + * @example + * ```ts + * import { Effect, Random, Stream } from "effect" + * + * const stream = Stream.make(10, 20, 30).pipe( + * Stream.mapEffect((n) => Random.nextIntBetween(0, n)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Example Output: { _id: 'Chunk', values: [ 7, 19, 8 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ +export const mapEffect: { + /** + * Maps over elements of the stream with the specified effectful function. + * + * @example + * ```ts + * import { Effect, Random, Stream } from "effect" + * + * const stream = Stream.make(10, 20, 30).pipe( + * Stream.mapEffect((n) => Random.nextIntBetween(0, n)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Example Output: { _id: 'Chunk', values: [ 7, 19, 8 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + ( + f: (a: A) => Effect.Effect, + options?: + | { readonly concurrency?: number | "unbounded" | undefined; readonly unordered?: boolean | undefined } + | undefined + ): (self: Stream) => Stream + /** + * Maps over elements of the stream with the specified effectful function. + * + * @example + * ```ts + * import { Effect, Random, Stream } from "effect" + * + * const stream = Stream.make(10, 20, 30).pipe( + * Stream.mapEffect((n) => Random.nextIntBetween(0, n)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Example Output: { _id: 'Chunk', values: [ 7, 19, 8 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + ( + f: (a: A) => Effect.Effect, + options: { readonly key: (a: A) => K; readonly bufferSize?: number | undefined } + ): (self: Stream) => Stream + /** + * Maps over elements of the stream with the specified effectful function. + * + * @example + * ```ts + * import { Effect, Random, Stream } from "effect" + * + * const stream = Stream.make(10, 20, 30).pipe( + * Stream.mapEffect((n) => Random.nextIntBetween(0, n)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Example Output: { _id: 'Chunk', values: [ 7, 19, 8 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Stream, + f: (a: A) => Effect.Effect, + options?: + | { readonly concurrency?: number | "unbounded" | undefined; readonly unordered?: boolean | undefined } + | undefined + ): Stream + /** + * Maps over elements of the stream with the specified effectful function. + * + * @example + * ```ts + * import { Effect, Random, Stream } from "effect" + * + * const stream = Stream.make(10, 20, 30).pipe( + * Stream.mapEffect((n) => Random.nextIntBetween(0, n)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Example Output: { _id: 'Chunk', values: [ 7, 19, 8 ] } + * ``` + * + * @since 2.0.0 + * @category mapping + */ + ( + self: Stream, + f: (a: A) => Effect.Effect, + options: { readonly key: (a: A) => K; readonly bufferSize?: number | undefined } + ): Stream +} = groupBy_.mapEffectOptions + +/** + * Transforms the errors emitted by this stream using `f`. + * + * @since 2.0.0 + * @category mapping + */ +export const mapError: { + /** + * Transforms the errors emitted by this stream using `f`. + * + * @since 2.0.0 + * @category mapping + */ + (f: (error: E) => E2): (self: Stream) => Stream + /** + * Transforms the errors emitted by this stream using `f`. + * + * @since 2.0.0 + * @category mapping + */ + (self: Stream, f: (error: E) => E2): Stream +} = internal.mapError + +/** + * Transforms the full causes of failures emitted by this stream. + * + * @since 2.0.0 + * @category mapping + */ +export const mapErrorCause: { + /** + * Transforms the full causes of failures emitted by this stream. + * + * @since 2.0.0 + * @category mapping + */ + (f: (cause: Cause.Cause) => Cause.Cause): (self: Stream) => Stream + /** + * Transforms the full causes of failures emitted by this stream. + * + * @since 2.0.0 + * @category mapping + */ + (self: Stream, f: (cause: Cause.Cause) => Cause.Cause): Stream +} = internal.mapErrorCause + +/** + * Merges this stream and the specified stream together. + * + * New produced stream will terminate when both specified stream terminate if + * no termination strategy is specified. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3).pipe( + * Stream.schedule(Schedule.spaced("100 millis")) + * ) + * const s2 = Stream.make(4, 5, 6).pipe( + * Stream.schedule(Schedule.spaced("200 millis")) + * ) + * + * const stream = Stream.merge(s1, s2) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 4, 2, 3, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const merge: { + /** + * Merges this stream and the specified stream together. + * + * New produced stream will terminate when both specified stream terminate if + * no termination strategy is specified. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3).pipe( + * Stream.schedule(Schedule.spaced("100 millis")) + * ) + * const s2 = Stream.make(4, 5, 6).pipe( + * Stream.schedule(Schedule.spaced("200 millis")) + * ) + * + * const stream = Stream.merge(s1, s2) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 4, 2, 3, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + ( + that: Stream, + options?: { readonly haltStrategy?: HaltStrategy.HaltStrategyInput | undefined } | undefined + ): (self: Stream) => Stream + /** + * Merges this stream and the specified stream together. + * + * New produced stream will terminate when both specified stream terminate if + * no termination strategy is specified. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3).pipe( + * Stream.schedule(Schedule.spaced("100 millis")) + * ) + * const s2 = Stream.make(4, 5, 6).pipe( + * Stream.schedule(Schedule.spaced("200 millis")) + * ) + * + * const stream = Stream.merge(s1, s2) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 4, 2, 3, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + that: Stream, + options?: { readonly haltStrategy?: HaltStrategy.HaltStrategyInput | undefined } | undefined + ): Stream +} = internal.merge + +/** + * Merges a variable list of streams in a non-deterministic fashion. Up to `n` + * streams may be consumed in parallel and up to `outputBuffer` chunks may be + * buffered by this operator. + * + * @since 2.0.0 + * @category utils + */ +export const mergeAll: { + /** + * Merges a variable list of streams in a non-deterministic fashion. Up to `n` + * streams may be consumed in parallel and up to `outputBuffer` chunks may be + * buffered by this operator. + * + * @since 2.0.0 + * @category utils + */ + ( + options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + } + ): (streams: Iterable>) => Stream + /** + * Merges a variable list of streams in a non-deterministic fashion. Up to `n` + * streams may be consumed in parallel and up to `outputBuffer` chunks may be + * buffered by this operator. + * + * @since 2.0.0 + * @category utils + */ + ( + streams: Iterable>, + options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + } + ): Stream +} = internal.mergeAll + +/** + * Merges a struct of streams into a single stream of tagged values. + * @category combinators + * @since 3.8.5 + * + * @example + * ```ts + * import { Stream } from "effect" + * // Stream.Stream<{ _tag: "a"; value: number; } | { _tag: "b"; value: string; }> + * const res = Stream.mergeWithTag({ + * a: Stream.make(0), + * b: Stream.make("") + * }, { concurrency: "unbounded" }) + * ``` + */ +export const mergeWithTag: { + /** + * Merges a struct of streams into a single stream of tagged values. + * @category combinators + * @since 3.8.5 + * + * @example + * ```ts + * import { Stream } from "effect" + * // Stream.Stream<{ _tag: "a"; value: number; } | { _tag: "b"; value: string; }> + * const res = Stream.mergeWithTag({ + * a: Stream.make(0), + * b: Stream.make("") + * }, { concurrency: "unbounded" }) + * ``` + */ + }>( + streams: S, + options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + } + ): Stream< + { [K in keyof S]: { _tag: K; value: Stream.Success } }[keyof S], + Stream.Error, + Stream.Context + > + /** + * Merges a struct of streams into a single stream of tagged values. + * @category combinators + * @since 3.8.5 + * + * @example + * ```ts + * import { Stream } from "effect" + * // Stream.Stream<{ _tag: "a"; value: number; } | { _tag: "b"; value: string; }> + * const res = Stream.mergeWithTag({ + * a: Stream.make(0), + * b: Stream.make("") + * }, { concurrency: "unbounded" }) + * ``` + */ + ( + options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + } + ): }>(streams: S) => Stream< + { [K in keyof S]: { _tag: K; value: Stream.Success } }[keyof S], + Stream.Error, + Stream.Context + > +} = internal.mergeWithTag + +/** + * Merges this stream and the specified stream together to a common element + * type with the specified mapping functions. + * + * New produced stream will terminate when both specified stream terminate if + * no termination strategy is specified. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * const s1 = Stream.make("1", "2", "3").pipe( + * Stream.schedule(Schedule.spaced("100 millis")) + * ) + * const s2 = Stream.make(4.1, 5.3, 6.2).pipe( + * Stream.schedule(Schedule.spaced("200 millis")) + * ) + * + * const stream = Stream.mergeWith(s1, s2, { + * onSelf: (s) => parseInt(s), + * onOther: (n) => Math.floor(n) + * }) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 4, 2, 3, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const mergeWith: { + /** + * Merges this stream and the specified stream together to a common element + * type with the specified mapping functions. + * + * New produced stream will terminate when both specified stream terminate if + * no termination strategy is specified. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * const s1 = Stream.make("1", "2", "3").pipe( + * Stream.schedule(Schedule.spaced("100 millis")) + * ) + * const s2 = Stream.make(4.1, 5.3, 6.2).pipe( + * Stream.schedule(Schedule.spaced("200 millis")) + * ) + * + * const stream = Stream.mergeWith(s1, s2, { + * onSelf: (s) => parseInt(s), + * onOther: (n) => Math.floor(n) + * }) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 4, 2, 3, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + ( + other: Stream, + options: { + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A4 + readonly haltStrategy?: HaltStrategy.HaltStrategyInput | undefined + } + ): (self: Stream) => Stream + /** + * Merges this stream and the specified stream together to a common element + * type with the specified mapping functions. + * + * New produced stream will terminate when both specified stream terminate if + * no termination strategy is specified. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * const s1 = Stream.make("1", "2", "3").pipe( + * Stream.schedule(Schedule.spaced("100 millis")) + * ) + * const s2 = Stream.make(4.1, 5.3, 6.2).pipe( + * Stream.schedule(Schedule.spaced("200 millis")) + * ) + * + * const stream = Stream.mergeWith(s1, s2, { + * onSelf: (s) => parseInt(s), + * onOther: (n) => Math.floor(n) + * }) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 4, 2, 3, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + other: Stream, + options: { + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A4 + readonly haltStrategy?: HaltStrategy.HaltStrategyInput | undefined + } + ): Stream +} = internal.mergeWith + +/** + * Merges this stream and the specified stream together to produce a stream of + * eithers. + * + * @since 2.0.0 + * @category utils + */ +export const mergeEither: { + /** + * Merges this stream and the specified stream together to produce a stream of + * eithers. + * + * @since 2.0.0 + * @category utils + */ + (that: Stream): (self: Stream) => Stream, E2 | E, R2 | R> + /** + * Merges this stream and the specified stream together to produce a stream of + * eithers. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, that: Stream): Stream, E | E2, R | R2> +} = internal.mergeEither + +/** + * Merges this stream and the specified stream together, discarding the values + * from the right stream. + * + * @since 2.0.0 + * @category utils + */ +export const mergeLeft: { + /** + * Merges this stream and the specified stream together, discarding the values + * from the right stream. + * + * @since 2.0.0 + * @category utils + */ + (right: Stream): (left: Stream) => Stream + /** + * Merges this stream and the specified stream together, discarding the values + * from the right stream. + * + * @since 2.0.0 + * @category utils + */ + (left: Stream, right: Stream): Stream +} = internal.mergeLeft + +/** + * Merges this stream and the specified stream together, discarding the values + * from the left stream. + * + * @since 2.0.0 + * @category utils + */ +export const mergeRight: { + /** + * Merges this stream and the specified stream together, discarding the values + * from the left stream. + * + * @since 2.0.0 + * @category utils + */ + (right: Stream): (left: Stream) => Stream + /** + * Merges this stream and the specified stream together, discarding the values + * from the left stream. + * + * @since 2.0.0 + * @category utils + */ + (left: Stream, right: Stream): Stream +} = internal.mergeRight + +/** + * Returns a combined string resulting from concatenating each of the values + * from the stream. + * + * @since 2.0.0 + * @category utils + */ +export const mkString: (self: Stream) => Effect.Effect = internal.mkString + +/** + * The stream that never produces any value or fails with any error. + * + * @since 2.0.0 + * @category constructors + */ +export const never: Stream = internal.never + +/** + * Adds an effect to be executed at the end of the stream. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3).pipe( + * Stream.map((n) => n * 2), + * Stream.tap((n) => Console.log(`after mapping: ${n}`)), + * Stream.onEnd(Console.log("Stream ended")) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // after mapping: 2 + * // after mapping: 4 + * // after mapping: 6 + * // Stream ended + * // { _id: 'Chunk', values: [ 2, 4, 6 ] } + * ``` + * + * @since 3.6.0 + * @category sequencing + */ +export const onEnd: { + /** + * Adds an effect to be executed at the end of the stream. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3).pipe( + * Stream.map((n) => n * 2), + * Stream.tap((n) => Console.log(`after mapping: ${n}`)), + * Stream.onEnd(Console.log("Stream ended")) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // after mapping: 2 + * // after mapping: 4 + * // after mapping: 6 + * // Stream ended + * // { _id: 'Chunk', values: [ 2, 4, 6 ] } + * ``` + * + * @since 3.6.0 + * @category sequencing + */ + <_, E2, R2>(effect: Effect.Effect<_, E2, R2>): (self: Stream) => Stream + /** + * Adds an effect to be executed at the end of the stream. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3).pipe( + * Stream.map((n) => n * 2), + * Stream.tap((n) => Console.log(`after mapping: ${n}`)), + * Stream.onEnd(Console.log("Stream ended")) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // after mapping: 2 + * // after mapping: 4 + * // after mapping: 6 + * // Stream ended + * // { _id: 'Chunk', values: [ 2, 4, 6 ] } + * ``` + * + * @since 3.6.0 + * @category sequencing + */ + (self: Stream, effect: Effect.Effect<_, E2, R2>): Stream +} = internal.onEnd + +/** + * Runs the specified effect if this stream fails, providing the error to the + * effect if it exists. + * + * Note: Unlike `Effect.onError` there is no guarantee that the provided + * effect will not be interrupted. + * + * @since 2.0.0 + * @category utils + */ +export const onError: { + /** + * Runs the specified effect if this stream fails, providing the error to the + * effect if it exists. + * + * Note: Unlike `Effect.onError` there is no guarantee that the provided + * effect will not be interrupted. + * + * @since 2.0.0 + * @category utils + */ + (cleanup: (cause: Cause.Cause) => Effect.Effect): (self: Stream) => Stream + /** + * Runs the specified effect if this stream fails, providing the error to the + * effect if it exists. + * + * Note: Unlike `Effect.onError` there is no guarantee that the provided + * effect will not be interrupted. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + cleanup: (cause: Cause.Cause) => Effect.Effect + ): Stream +} = internal.onError + +/** + * Runs the specified effect if this stream ends. + * + * @since 2.0.0 + * @category utils + */ +export const onDone: { + /** + * Runs the specified effect if this stream ends. + * + * @since 2.0.0 + * @category utils + */ + (cleanup: () => Effect.Effect): (self: Stream) => Stream + /** + * Runs the specified effect if this stream ends. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, cleanup: () => Effect.Effect): Stream +} = internal.onDone + +/** + * Adds an effect to be executed at the start of the stream. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3).pipe( + * Stream.onStart(Console.log("Stream started")), + * Stream.map((n) => n * 2), + * Stream.tap((n) => Console.log(`after mapping: ${n}`)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Stream started + * // after mapping: 2 + * // after mapping: 4 + * // after mapping: 6 + * // { _id: 'Chunk', values: [ 2, 4, 6 ] } + * ``` + * + * @since 3.6.0 + * @category sequencing + */ +export const onStart: { + /** + * Adds an effect to be executed at the start of the stream. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3).pipe( + * Stream.onStart(Console.log("Stream started")), + * Stream.map((n) => n * 2), + * Stream.tap((n) => Console.log(`after mapping: ${n}`)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Stream started + * // after mapping: 2 + * // after mapping: 4 + * // after mapping: 6 + * // { _id: 'Chunk', values: [ 2, 4, 6 ] } + * ``` + * + * @since 3.6.0 + * @category sequencing + */ + <_, E2, R2>(effect: Effect.Effect<_, E2, R2>): (self: Stream) => Stream + /** + * Adds an effect to be executed at the start of the stream. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3).pipe( + * Stream.onStart(Console.log("Stream started")), + * Stream.map((n) => n * 2), + * Stream.tap((n) => Console.log(`after mapping: ${n}`)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Stream started + * // after mapping: 2 + * // after mapping: 4 + * // after mapping: 6 + * // { _id: 'Chunk', values: [ 2, 4, 6 ] } + * ``` + * + * @since 3.6.0 + * @category sequencing + */ + (self: Stream, effect: Effect.Effect<_, E2, R2>): Stream +} = internal.onStart + +/** + * Translates any failure into a stream termination, making the stream + * infallible and all failures unchecked. + * + * @since 2.0.0 + * @category error handling + */ +export const orDie: (self: Stream) => Stream = internal.orDie + +/** + * Keeps none of the errors, and terminates the stream with them, using the + * specified function to convert the `E` into a defect. + * + * @since 2.0.0 + * @category error handling + */ +export const orDieWith: { + /** + * Keeps none of the errors, and terminates the stream with them, using the + * specified function to convert the `E` into a defect. + * + * @since 2.0.0 + * @category error handling + */ + (f: (e: E) => unknown): (self: Stream) => Stream + /** + * Keeps none of the errors, and terminates the stream with them, using the + * specified function to convert the `E` into a defect. + * + * @since 2.0.0 + * @category error handling + */ + (self: Stream, f: (e: E) => unknown): Stream +} = internal.orDieWith + +/** + * Switches to the provided stream in case this one fails with a typed error. + * + * See also `Stream.catchAll`. + * + * @since 2.0.0 + * @category error handling + */ +export const orElse: { + /** + * Switches to the provided stream in case this one fails with a typed error. + * + * See also `Stream.catchAll`. + * + * @since 2.0.0 + * @category error handling + */ + (that: LazyArg>): (self: Stream) => Stream + /** + * Switches to the provided stream in case this one fails with a typed error. + * + * See also `Stream.catchAll`. + * + * @since 2.0.0 + * @category error handling + */ + (self: Stream, that: LazyArg>): Stream +} = internal.orElse + +/** + * Switches to the provided stream in case this one fails with a typed error. + * + * See also `Stream.catchAll`. + * + * @since 2.0.0 + * @category error handling + */ +export const orElseEither: { + /** + * Switches to the provided stream in case this one fails with a typed error. + * + * See also `Stream.catchAll`. + * + * @since 2.0.0 + * @category error handling + */ + (that: LazyArg>): (self: Stream) => Stream, E2, R2 | R> + /** + * Switches to the provided stream in case this one fails with a typed error. + * + * See also `Stream.catchAll`. + * + * @since 2.0.0 + * @category error handling + */ + (self: Stream, that: LazyArg>): Stream, E2, R | R2> +} = internal.orElseEither + +/** + * Fails with given error in case this one fails with a typed error. + * + * See also `Stream.catchAll`. + * + * @since 2.0.0 + * @category error handling + */ +export const orElseFail: { + /** + * Fails with given error in case this one fails with a typed error. + * + * See also `Stream.catchAll`. + * + * @since 2.0.0 + * @category error handling + */ + (error: LazyArg): (self: Stream) => Stream + /** + * Fails with given error in case this one fails with a typed error. + * + * See also `Stream.catchAll`. + * + * @since 2.0.0 + * @category error handling + */ + (self: Stream, error: LazyArg): Stream +} = internal.orElseFail + +/** + * Produces the specified element if this stream is empty. + * + * @since 2.0.0 + * @category error handling + */ +export const orElseIfEmpty: { + /** + * Produces the specified element if this stream is empty. + * + * @since 2.0.0 + * @category error handling + */ + (element: LazyArg): (self: Stream) => Stream + /** + * Produces the specified element if this stream is empty. + * + * @since 2.0.0 + * @category error handling + */ + (self: Stream, element: LazyArg): Stream +} = internal.orElseIfEmpty + +/** + * Produces the specified chunk if this stream is empty. + * + * @since 2.0.0 + * @category error handling + */ +export const orElseIfEmptyChunk: { + /** + * Produces the specified chunk if this stream is empty. + * + * @since 2.0.0 + * @category error handling + */ + (chunk: LazyArg>): (self: Stream) => Stream + /** + * Produces the specified chunk if this stream is empty. + * + * @since 2.0.0 + * @category error handling + */ + (self: Stream, chunk: LazyArg>): Stream +} = internal.orElseIfEmptyChunk + +/** + * Switches to the provided stream in case this one is empty. + * + * @since 2.0.0 + * @category error handling + */ +export const orElseIfEmptyStream: { + /** + * Switches to the provided stream in case this one is empty. + * + * @since 2.0.0 + * @category error handling + */ + (stream: LazyArg>): (self: Stream) => Stream + /** + * Switches to the provided stream in case this one is empty. + * + * @since 2.0.0 + * @category error handling + */ + (self: Stream, stream: LazyArg>): Stream +} = internal.orElseIfEmptyStream + +/** + * Succeeds with the specified value if this one fails with a typed error. + * + * @since 2.0.0 + * @category error handling + */ +export const orElseSucceed: { + /** + * Succeeds with the specified value if this one fails with a typed error. + * + * @since 2.0.0 + * @category error handling + */ + (value: LazyArg): (self: Stream) => Stream + /** + * Succeeds with the specified value if this one fails with a typed error. + * + * @since 2.0.0 + * @category error handling + */ + (self: Stream, value: LazyArg): Stream +} = internal.orElseSucceed + +/** + * Like `Stream.unfold`, but allows the emission of values to end one step further + * than the unfolding of the state. This is useful for embedding paginated + * APIs, hence the name. + * + * @example + * ```ts + * import { Effect, Option, Stream } from "effect" + * + * const stream = Stream.paginate(0, (n) => [ + * n, + * n < 3 ? Option.some(n + 1) : Option.none() + * ]) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const paginate: (s: S, f: (s: S) => readonly [A, Option.Option]) => Stream = internal.paginate + +/** + * Like `Stream.unfoldChunk`, but allows the emission of values to end one step + * further than the unfolding of the state. This is useful for embedding + * paginated APIs, hence the name. + * + * @since 2.0.0 + * @category constructors + */ +export const paginateChunk: ( + s: S, + f: (s: S) => readonly [Chunk.Chunk, Option.Option] +) => Stream = internal.paginateChunk + +/** + * Like `Stream.unfoldChunkEffect`, but allows the emission of values to end one step + * further than the unfolding of the state. This is useful for embedding + * paginated APIs, hence the name. + * + * @since 2.0.0 + * @category constructors + */ +export const paginateChunkEffect: ( + s: S, + f: (s: S) => Effect.Effect, Option.Option], E, R> +) => Stream = internal.paginateChunkEffect + +/** + * Like `Stream.unfoldEffect` but allows the emission of values to end one step + * further than the unfolding of the state. This is useful for embedding + * paginated APIs, hence the name. + * + * @since 2.0.0 + * @category constructors + */ +export const paginateEffect: ( + s: S, + f: (s: S) => Effect.Effect], E, R> +) => Stream = internal.paginateEffect + +/** + * Splits a stream into two substreams based on a predicate. + * + * **Details** + * + * The `Stream.partition` function splits a stream into two parts: one for + * elements that satisfy the predicate (evaluated to `true`) and another for + * those that do not (evaluated to `false`). + * + * The faster stream may advance up to `bufferSize` elements ahead of the slower + * one. + * + * **Example** (Partitioning a Stream into Even and Odd Numbers) + * + * ```ts + * import { Effect, Stream } from "effect" + * + * const partition = Stream.range(1, 9).pipe( + * Stream.partition((n) => n % 2 === 0, { bufferSize: 5 }) + * ) + * + * const program = Effect.scoped( + * Effect.gen(function*() { + * const [odds, evens] = yield* partition + * console.log(yield* Stream.runCollect(odds)) + * console.log(yield* Stream.runCollect(evens)) + * }) + * ) + * + * Effect.runPromise(program) + * // { _id: 'Chunk', values: [ 1, 3, 5, 7, 9 ] } + * // { _id: 'Chunk', values: [ 2, 4, 6, 8 ] } + * ``` + * + * @see {@link partitionEither} for partitioning a stream based on effectful + * conditions. + * + * @since 2.0.0 + * @category utils + */ +export const partition: { + /** + * Splits a stream into two substreams based on a predicate. + * + * **Details** + * + * The `Stream.partition` function splits a stream into two parts: one for + * elements that satisfy the predicate (evaluated to `true`) and another for + * those that do not (evaluated to `false`). + * + * The faster stream may advance up to `bufferSize` elements ahead of the slower + * one. + * + * **Example** (Partitioning a Stream into Even and Odd Numbers) + * + * ```ts + * import { Effect, Stream } from "effect" + * + * const partition = Stream.range(1, 9).pipe( + * Stream.partition((n) => n % 2 === 0, { bufferSize: 5 }) + * ) + * + * const program = Effect.scoped( + * Effect.gen(function*() { + * const [odds, evens] = yield* partition + * console.log(yield* Stream.runCollect(odds)) + * console.log(yield* Stream.runCollect(evens)) + * }) + * ) + * + * Effect.runPromise(program) + * // { _id: 'Chunk', values: [ 1, 3, 5, 7, 9 ] } + * // { _id: 'Chunk', values: [ 2, 4, 6, 8 ] } + * ``` + * + * @see {@link partitionEither} for partitioning a stream based on effectful + * conditions. + * + * @since 2.0.0 + * @category utils + */ + ( + refinement: Refinement, B>, + options?: { bufferSize?: number | undefined } | undefined + ): ( + self: Stream + ) => Effect.Effect<[excluded: Stream, E, never>, satisfying: Stream], E, R | Scope.Scope> + /** + * Splits a stream into two substreams based on a predicate. + * + * **Details** + * + * The `Stream.partition` function splits a stream into two parts: one for + * elements that satisfy the predicate (evaluated to `true`) and another for + * those that do not (evaluated to `false`). + * + * The faster stream may advance up to `bufferSize` elements ahead of the slower + * one. + * + * **Example** (Partitioning a Stream into Even and Odd Numbers) + * + * ```ts + * import { Effect, Stream } from "effect" + * + * const partition = Stream.range(1, 9).pipe( + * Stream.partition((n) => n % 2 === 0, { bufferSize: 5 }) + * ) + * + * const program = Effect.scoped( + * Effect.gen(function*() { + * const [odds, evens] = yield* partition + * console.log(yield* Stream.runCollect(odds)) + * console.log(yield* Stream.runCollect(evens)) + * }) + * ) + * + * Effect.runPromise(program) + * // { _id: 'Chunk', values: [ 1, 3, 5, 7, 9 ] } + * // { _id: 'Chunk', values: [ 2, 4, 6, 8 ] } + * ``` + * + * @see {@link partitionEither} for partitioning a stream based on effectful + * conditions. + * + * @since 2.0.0 + * @category utils + */ + ( + predicate: Predicate, + options?: { bufferSize?: number | undefined } | undefined + ): ( + self: Stream + ) => Effect.Effect<[excluded: Stream, satisfying: Stream], E, Scope.Scope | R> + /** + * Splits a stream into two substreams based on a predicate. + * + * **Details** + * + * The `Stream.partition` function splits a stream into two parts: one for + * elements that satisfy the predicate (evaluated to `true`) and another for + * those that do not (evaluated to `false`). + * + * The faster stream may advance up to `bufferSize` elements ahead of the slower + * one. + * + * **Example** (Partitioning a Stream into Even and Odd Numbers) + * + * ```ts + * import { Effect, Stream } from "effect" + * + * const partition = Stream.range(1, 9).pipe( + * Stream.partition((n) => n % 2 === 0, { bufferSize: 5 }) + * ) + * + * const program = Effect.scoped( + * Effect.gen(function*() { + * const [odds, evens] = yield* partition + * console.log(yield* Stream.runCollect(odds)) + * console.log(yield* Stream.runCollect(evens)) + * }) + * ) + * + * Effect.runPromise(program) + * // { _id: 'Chunk', values: [ 1, 3, 5, 7, 9 ] } + * // { _id: 'Chunk', values: [ 2, 4, 6, 8 ] } + * ``` + * + * @see {@link partitionEither} for partitioning a stream based on effectful + * conditions. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + refinement: Refinement, + options?: { bufferSize?: number | undefined } | undefined + ): Effect.Effect<[excluded: Stream, E, never>, satisfying: Stream], E, R | Scope.Scope> + /** + * Splits a stream into two substreams based on a predicate. + * + * **Details** + * + * The `Stream.partition` function splits a stream into two parts: one for + * elements that satisfy the predicate (evaluated to `true`) and another for + * those that do not (evaluated to `false`). + * + * The faster stream may advance up to `bufferSize` elements ahead of the slower + * one. + * + * **Example** (Partitioning a Stream into Even and Odd Numbers) + * + * ```ts + * import { Effect, Stream } from "effect" + * + * const partition = Stream.range(1, 9).pipe( + * Stream.partition((n) => n % 2 === 0, { bufferSize: 5 }) + * ) + * + * const program = Effect.scoped( + * Effect.gen(function*() { + * const [odds, evens] = yield* partition + * console.log(yield* Stream.runCollect(odds)) + * console.log(yield* Stream.runCollect(evens)) + * }) + * ) + * + * Effect.runPromise(program) + * // { _id: 'Chunk', values: [ 1, 3, 5, 7, 9 ] } + * // { _id: 'Chunk', values: [ 2, 4, 6, 8 ] } + * ``` + * + * @see {@link partitionEither} for partitioning a stream based on effectful + * conditions. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + predicate: Predicate, + options?: { bufferSize?: number | undefined } | undefined + ): Effect.Effect<[excluded: Stream, satisfying: Stream], E, R | Scope.Scope> +} = internal.partition + +/** + * Splits a stream into two substreams based on an effectful condition. + * + * **Details** + * + * The `Stream.partitionEither` function is used to divide a stream into two + * parts: one for elements that satisfy a condition producing `Either.left` + * values, and another for those that produce `Either.right` values. This + * function applies an effectful predicate to each element in the stream to + * determine which substream it belongs to. + * + * The faster stream may advance up to `bufferSize` elements ahead of the slower + * one. + * + * **Example** (Partitioning a Stream with an Effectful Predicate) + * + * ```ts + * import { Effect, Either, Stream } from "effect" + * + * const partition = Stream.range(1, 9).pipe( + * Stream.partitionEither( + * (n) => Effect.succeed(n % 2 === 0 ? Either.right(n) : Either.left(n)), + * { bufferSize: 5 } + * ) + * ) + * + * const program = Effect.scoped( + * Effect.gen(function*() { + * const [evens, odds] = yield* partition + * console.log(yield* Stream.runCollect(evens)) + * console.log(yield* Stream.runCollect(odds)) + * }) + * ) + * + * Effect.runPromise(program) + * // { _id: 'Chunk', values: [ 1, 3, 5, 7, 9 ] } + * // { _id: 'Chunk', values: [ 2, 4, 6, 8 ] } + * ``` + * + * @see {@link partition} for partitioning a stream based on simple conditions. + * + * @since 2.0.0 + * @category utils + */ +export const partitionEither: { + /** + * Splits a stream into two substreams based on an effectful condition. + * + * **Details** + * + * The `Stream.partitionEither` function is used to divide a stream into two + * parts: one for elements that satisfy a condition producing `Either.left` + * values, and another for those that produce `Either.right` values. This + * function applies an effectful predicate to each element in the stream to + * determine which substream it belongs to. + * + * The faster stream may advance up to `bufferSize` elements ahead of the slower + * one. + * + * **Example** (Partitioning a Stream with an Effectful Predicate) + * + * ```ts + * import { Effect, Either, Stream } from "effect" + * + * const partition = Stream.range(1, 9).pipe( + * Stream.partitionEither( + * (n) => Effect.succeed(n % 2 === 0 ? Either.right(n) : Either.left(n)), + * { bufferSize: 5 } + * ) + * ) + * + * const program = Effect.scoped( + * Effect.gen(function*() { + * const [evens, odds] = yield* partition + * console.log(yield* Stream.runCollect(evens)) + * console.log(yield* Stream.runCollect(odds)) + * }) + * ) + * + * Effect.runPromise(program) + * // { _id: 'Chunk', values: [ 1, 3, 5, 7, 9 ] } + * // { _id: 'Chunk', values: [ 2, 4, 6, 8 ] } + * ``` + * + * @see {@link partition} for partitioning a stream based on simple conditions. + * + * @since 2.0.0 + * @category utils + */ + ( + predicate: (a: NoInfer) => Effect.Effect, E2, R2>, + options?: { readonly bufferSize?: number | undefined } | undefined + ): ( + self: Stream + ) => Effect.Effect<[left: Stream, right: Stream], E2 | E, Scope.Scope | R2 | R> + /** + * Splits a stream into two substreams based on an effectful condition. + * + * **Details** + * + * The `Stream.partitionEither` function is used to divide a stream into two + * parts: one for elements that satisfy a condition producing `Either.left` + * values, and another for those that produce `Either.right` values. This + * function applies an effectful predicate to each element in the stream to + * determine which substream it belongs to. + * + * The faster stream may advance up to `bufferSize` elements ahead of the slower + * one. + * + * **Example** (Partitioning a Stream with an Effectful Predicate) + * + * ```ts + * import { Effect, Either, Stream } from "effect" + * + * const partition = Stream.range(1, 9).pipe( + * Stream.partitionEither( + * (n) => Effect.succeed(n % 2 === 0 ? Either.right(n) : Either.left(n)), + * { bufferSize: 5 } + * ) + * ) + * + * const program = Effect.scoped( + * Effect.gen(function*() { + * const [evens, odds] = yield* partition + * console.log(yield* Stream.runCollect(evens)) + * console.log(yield* Stream.runCollect(odds)) + * }) + * ) + * + * Effect.runPromise(program) + * // { _id: 'Chunk', values: [ 1, 3, 5, 7, 9 ] } + * // { _id: 'Chunk', values: [ 2, 4, 6, 8 ] } + * ``` + * + * @see {@link partition} for partitioning a stream based on simple conditions. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + predicate: (a: A) => Effect.Effect, E2, R2>, + options?: { readonly bufferSize?: number | undefined } | undefined + ): Effect.Effect<[left: Stream, right: Stream], E | E2, Scope.Scope | R | R2> +} = internal.partitionEither + +/** + * Peels off enough material from the stream to construct a `Z` using the + * provided `Sink` and then returns both the `Z` and the rest of the + * `Stream` in a scope. Like all scoped values, the provided stream is + * valid only within the scope. + * + * @since 2.0.0 + * @category utils + */ +export const peel: { + /** + * Peels off enough material from the stream to construct a `Z` using the + * provided `Sink` and then returns both the `Z` and the rest of the + * `Stream` in a scope. Like all scoped values, the provided stream is + * valid only within the scope. + * + * @since 2.0.0 + * @category utils + */ + (sink: Sink.Sink): (self: Stream) => Effect.Effect<[A2, Stream], E2 | E, Scope.Scope | R2 | R> + /** + * Peels off enough material from the stream to construct a `Z` using the + * provided `Sink` and then returns both the `Z` and the rest of the + * `Stream` in a scope. Like all scoped values, the provided stream is + * valid only within the scope. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, sink: Sink.Sink): Effect.Effect<[A2, Stream], E | E2, Scope.Scope | R | R2> +} = internal.peel + +/** + * Pipes all of the values from this stream through the provided sink. + * + * See also `Stream.transduce`. + * + * @since 2.0.0 + * @category utils + */ +export const pipeThrough: { + /** + * Pipes all of the values from this stream through the provided sink. + * + * See also `Stream.transduce`. + * + * @since 2.0.0 + * @category utils + */ + (sink: Sink.Sink): (self: Stream) => Stream + /** + * Pipes all of the values from this stream through the provided sink. + * + * See also `Stream.transduce`. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, sink: Sink.Sink): Stream +} = internal.pipeThrough + +/** + * Pipes all the values from this stream through the provided channel. + * + * @since 2.0.0 + * @category utils + */ +export const pipeThroughChannel: { + /** + * Pipes all the values from this stream through the provided channel. + * + * @since 2.0.0 + * @category utils + */ + ( + channel: Channel.Channel, Chunk.Chunk, E2, E, unknown, unknown, R2> + ): (self: Stream) => Stream + /** + * Pipes all the values from this stream through the provided channel. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + channel: Channel.Channel, Chunk.Chunk, E2, E, unknown, unknown, R2> + ): Stream +} = internal.pipeThroughChannel + +/** + * Pipes all values from this stream through the provided channel, passing + * through any error emitted by this stream unchanged. + * + * @since 2.0.0 + * @category utils + */ +export const pipeThroughChannelOrFail: { + /** + * Pipes all values from this stream through the provided channel, passing + * through any error emitted by this stream unchanged. + * + * @since 2.0.0 + * @category utils + */ + ( + chan: Channel.Channel, Chunk.Chunk, E2, E, unknown, unknown, R2> + ): (self: Stream) => Stream + /** + * Pipes all values from this stream through the provided channel, passing + * through any error emitted by this stream unchanged. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + chan: Channel.Channel, Chunk.Chunk, E2, E, unknown, unknown, R2> + ): Stream +} = internal.pipeThroughChannelOrFail + +/** + * Emits the provided chunk before emitting any other value. + * + * @since 2.0.0 + * @category utils + */ +export const prepend: { + /** + * Emits the provided chunk before emitting any other value. + * + * @since 2.0.0 + * @category utils + */ + (values: Chunk.Chunk): (self: Stream) => Stream + /** + * Emits the provided chunk before emitting any other value. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, values: Chunk.Chunk): Stream +} = internal.prepend + +/** + * Provides the stream with its required context, which eliminates its + * dependency on `R`. + * + * @since 2.0.0 + * @category context + */ +export const provideContext: { + /** + * Provides the stream with its required context, which eliminates its + * dependency on `R`. + * + * @since 2.0.0 + * @category context + */ + (context: Context.Context): (self: Stream) => Stream + /** + * Provides the stream with its required context, which eliminates its + * dependency on `R`. + * + * @since 2.0.0 + * @category context + */ + (self: Stream, context: Context.Context): Stream +} = internal.provideContext + +/** + * Provides the stream with some of its required context, which eliminates its + * dependency on `R`. + * + * @since 3.16.9 + * @category context + */ +export const provideSomeContext: { + /** + * Provides the stream with some of its required context, which eliminates its + * dependency on `R`. + * + * @since 3.16.9 + * @category context + */ + (context: Context.Context): (self: Stream) => Stream> + /** + * Provides the stream with some of its required context, which eliminates its + * dependency on `R`. + * + * @since 3.16.9 + * @category context + */ + (self: Stream, context: Context.Context): Stream> +} = internal.provideSomeContext + +/** + * Provides a `Layer` to the stream, which translates it to another level. + * + * @since 2.0.0 + * @category context + */ +export const provideLayer: { + /** + * Provides a `Layer` to the stream, which translates it to another level. + * + * @since 2.0.0 + * @category context + */ + (layer: Layer.Layer): (self: Stream) => Stream + /** + * Provides a `Layer` to the stream, which translates it to another level. + * + * @since 2.0.0 + * @category context + */ + (self: Stream, layer: Layer.Layer): Stream +} = internal.provideLayer + +/** + * Provides the stream with the single service it requires. If the stream + * requires more than one service use `Stream.provideContext` instead. + * + * @since 2.0.0 + * @category context + */ +export const provideService: { + /** + * Provides the stream with the single service it requires. If the stream + * requires more than one service use `Stream.provideContext` instead. + * + * @since 2.0.0 + * @category context + */ + (tag: Context.Tag, resource: NoInfer): (self: Stream) => Stream> + /** + * Provides the stream with the single service it requires. If the stream + * requires more than one service use `Stream.provideContext` instead. + * + * @since 2.0.0 + * @category context + */ + (self: Stream, tag: Context.Tag, resource: NoInfer): Stream> +} = internal.provideService + +/** + * Provides the stream with the single service it requires. If the stream + * requires more than one service use `Stream.provideContext` instead. + * + * @since 2.0.0 + * @category context + */ +export const provideServiceEffect: { + /** + * Provides the stream with the single service it requires. If the stream + * requires more than one service use `Stream.provideContext` instead. + * + * @since 2.0.0 + * @category context + */ + (tag: Context.Tag, effect: Effect.Effect, E2, R2>): (self: Stream) => Stream> + /** + * Provides the stream with the single service it requires. If the stream + * requires more than one service use `Stream.provideContext` instead. + * + * @since 2.0.0 + * @category context + */ + ( + self: Stream, + tag: Context.Tag, + effect: Effect.Effect, E2, R2> + ): Stream> +} = internal.provideServiceEffect + +/** + * Provides the stream with the single service it requires. If the stream + * requires more than one service use `Stream.provideContext` instead. + * + * @since 2.0.0 + * @category context + */ +export const provideServiceStream: { + /** + * Provides the stream with the single service it requires. If the stream + * requires more than one service use `Stream.provideContext` instead. + * + * @since 2.0.0 + * @category context + */ + (tag: Context.Tag, stream: Stream, E2, R2>): (self: Stream) => Stream> + /** + * Provides the stream with the single service it requires. If the stream + * requires more than one service use `Stream.provideContext` instead. + * + * @since 2.0.0 + * @category context + */ + ( + self: Stream, + tag: Context.Tag, + stream: Stream, E2, R2> + ): Stream> +} = internal.provideServiceStream + +/** + * Transforms the context being provided to the stream with the specified + * function. + * + * @since 2.0.0 + * @category context + */ +export const mapInputContext: { + /** + * Transforms the context being provided to the stream with the specified + * function. + * + * @since 2.0.0 + * @category context + */ + (f: (env: Context.Context) => Context.Context): (self: Stream) => Stream + /** + * Transforms the context being provided to the stream with the specified + * function. + * + * @since 2.0.0 + * @category context + */ + (self: Stream, f: (env: Context.Context) => Context.Context): Stream +} = internal.mapInputContext + +/** + * Splits the context into two parts, providing one part using the + * specified layer and leaving the remainder `R0`. + * + * @since 2.0.0 + * @category context + */ +export const provideSomeLayer: { + /** + * Splits the context into two parts, providing one part using the + * specified layer and leaving the remainder `R0`. + * + * @since 2.0.0 + * @category context + */ + (layer: Layer.Layer): (self: Stream) => Stream> + /** + * Splits the context into two parts, providing one part using the + * specified layer and leaving the remainder `R0`. + * + * @since 2.0.0 + * @category context + */ + (self: Stream, layer: Layer.Layer): Stream> +} = internal.provideSomeLayer + +/** + * Returns a stream that mirrors the first upstream to emit an item. + * As soon as one of the upstream emits a first value, the other is interrupted. + * The resulting stream will forward all items from the "winning" source stream. + * Any upstream failures will cause the returned stream to fail. + * + * @example + * ```ts + * import { Stream, Schedule, Console, Effect } from "effect" + * + * const stream = Stream.fromSchedule(Schedule.spaced('2 millis')).pipe( + * Stream.race(Stream.fromSchedule(Schedule.spaced('1 millis'))), + * Stream.take(6), + * Stream.tap(Console.log) + * ) + * + * Effect.runPromise(Stream.runDrain(stream)) + * // Output each millisecond from the first stream, the rest streams are interrupted + * // 0 + * // 1 + * // 2 + * // 3 + * // 4 + * // 5 + * ``` + * @since 3.7.0 + * @category racing + */ +export const race: { + /** + * Returns a stream that mirrors the first upstream to emit an item. + * As soon as one of the upstream emits a first value, the other is interrupted. + * The resulting stream will forward all items from the "winning" source stream. + * Any upstream failures will cause the returned stream to fail. + * + * @example + * ```ts + * import { Stream, Schedule, Console, Effect } from "effect" + * + * const stream = Stream.fromSchedule(Schedule.spaced('2 millis')).pipe( + * Stream.race(Stream.fromSchedule(Schedule.spaced('1 millis'))), + * Stream.take(6), + * Stream.tap(Console.log) + * ) + * + * Effect.runPromise(Stream.runDrain(stream)) + * // Output each millisecond from the first stream, the rest streams are interrupted + * // 0 + * // 1 + * // 2 + * // 3 + * // 4 + * // 5 + * ``` + * @since 3.7.0 + * @category racing + */ + (right: Stream): (left: Stream) => Stream + /** + * Returns a stream that mirrors the first upstream to emit an item. + * As soon as one of the upstream emits a first value, the other is interrupted. + * The resulting stream will forward all items from the "winning" source stream. + * Any upstream failures will cause the returned stream to fail. + * + * @example + * ```ts + * import { Stream, Schedule, Console, Effect } from "effect" + * + * const stream = Stream.fromSchedule(Schedule.spaced('2 millis')).pipe( + * Stream.race(Stream.fromSchedule(Schedule.spaced('1 millis'))), + * Stream.take(6), + * Stream.tap(Console.log) + * ) + * + * Effect.runPromise(Stream.runDrain(stream)) + * // Output each millisecond from the first stream, the rest streams are interrupted + * // 0 + * // 1 + * // 2 + * // 3 + * // 4 + * // 5 + * ``` + * @since 3.7.0 + * @category racing + */ + (left: Stream, right: Stream): Stream +} = internal.race + +/** + * Returns a stream that mirrors the first upstream to emit an item. + * As soon as one of the upstream emits a first value, all the others are interrupted. + * The resulting stream will forward all items from the "winning" source stream. + * Any upstream failures will cause the returned stream to fail. + * + * @example + * ```ts + * import { Stream, Schedule, Console, Effect } from "effect" + * + * const stream = Stream.raceAll( + * Stream.fromSchedule(Schedule.spaced('1 millis')), + * Stream.fromSchedule(Schedule.spaced('2 millis')), + * Stream.fromSchedule(Schedule.spaced('4 millis')), + * ).pipe(Stream.take(6), Stream.tap(Console.log)) + * + * Effect.runPromise(Stream.runDrain(stream)) + * // Output each millisecond from the first stream, the rest streams are interrupted + * // 0 + * // 1 + * // 2 + * // 3 + * // 4 + * // 5 + * ``` + * @since 3.5.0 + * @category racing + */ +export const raceAll: >>( + ...streams: S +) => Stream< + Stream.Success, + Stream.Error, + Stream.Context +> = internal.raceAll + +/** + * Constructs a stream from a range of integers, including both endpoints. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * // A Stream with a range of numbers from 1 to 5 + * const stream = Stream.range(1, 5) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3, 4, 5 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const range: (min: number, max: number, chunkSize?: number) => Stream = internal.range + +/** + * Re-chunks the elements of the stream into chunks of `n` elements each. The + * last chunk might contain less than `n` elements. + * + * @since 2.0.0 + * @category utils + */ +export const rechunk: { + /** + * Re-chunks the elements of the stream into chunks of `n` elements each. The + * last chunk might contain less than `n` elements. + * + * @since 2.0.0 + * @category utils + */ + (n: number): (self: Stream) => Stream + /** + * Re-chunks the elements of the stream into chunks of `n` elements each. The + * last chunk might contain less than `n` elements. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, n: number): Stream +} = internal.rechunk + +/** + * Keeps some of the errors, and terminates the fiber with the rest + * + * @since 2.0.0 + * @category error handling + */ +export const refineOrDie: { + /** + * Keeps some of the errors, and terminates the fiber with the rest + * + * @since 2.0.0 + * @category error handling + */ + (pf: (error: E) => Option.Option): (self: Stream) => Stream + /** + * Keeps some of the errors, and terminates the fiber with the rest + * + * @since 2.0.0 + * @category error handling + */ + (self: Stream, pf: (error: E) => Option.Option): Stream +} = internal.refineOrDie + +/** + * Keeps some of the errors, and terminates the fiber with the rest, using the + * specified function to convert the `E` into a defect. + * + * @since 2.0.0 + * @category error handling + */ +export const refineOrDieWith: { + /** + * Keeps some of the errors, and terminates the fiber with the rest, using the + * specified function to convert the `E` into a defect. + * + * @since 2.0.0 + * @category error handling + */ + (pf: (error: E) => Option.Option, f: (error: E) => unknown): (self: Stream) => Stream + /** + * Keeps some of the errors, and terminates the fiber with the rest, using the + * specified function to convert the `E` into a defect. + * + * @since 2.0.0 + * @category error handling + */ + ( + self: Stream, + pf: (error: E) => Option.Option, + f: (error: E) => unknown + ): Stream +} = internal.refineOrDieWith + +/** + * Repeats the entire stream using the specified schedule. The stream will + * execute normally, and then repeat again according to the provided schedule. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * const stream = Stream.repeat(Stream.succeed(1), Schedule.forever) + * + * Effect.runPromise(Stream.runCollect(stream.pipe(Stream.take(5)))).then(console.log) + * // { _id: 'Chunk', values: [ 1, 1, 1, 1, 1 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const repeat: { + /** + * Repeats the entire stream using the specified schedule. The stream will + * execute normally, and then repeat again according to the provided schedule. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * const stream = Stream.repeat(Stream.succeed(1), Schedule.forever) + * + * Effect.runPromise(Stream.runCollect(stream.pipe(Stream.take(5)))).then(console.log) + * // { _id: 'Chunk', values: [ 1, 1, 1, 1, 1 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (schedule: Schedule.Schedule): (self: Stream) => Stream + /** + * Repeats the entire stream using the specified schedule. The stream will + * execute normally, and then repeat again according to the provided schedule. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * const stream = Stream.repeat(Stream.succeed(1), Schedule.forever) + * + * Effect.runPromise(Stream.runCollect(stream.pipe(Stream.take(5)))).then(console.log) + * // { _id: 'Chunk', values: [ 1, 1, 1, 1, 1 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, schedule: Schedule.Schedule): Stream +} = internal.repeat + +/** + * Creates a stream from an effect producing a value of type `A` which repeats + * forever. + * + * @example + * ```ts + * import { Effect, Random, Stream } from "effect" + * + * const stream = Stream.repeatEffect(Random.nextInt) + * + * Effect.runPromise(Stream.runCollect(stream.pipe(Stream.take(5)))).then(console.log) + * // Example Output: { _id: 'Chunk', values: [ 3891571149, 4239494205, 2352981603, 2339111046, 1488052210 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const repeatEffect: (effect: Effect.Effect) => Stream = internal.repeatEffect + +/** + * Creates a stream from an effect producing chunks of `A` values which + * repeats forever. + * + * @since 2.0.0 + * @category constructors + */ +export const repeatEffectChunk: (effect: Effect.Effect, E, R>) => Stream = + internal.repeatEffectChunk + +/** + * Creates a stream from an effect producing chunks of `A` values until it + * fails with `None`. + * + * @since 2.0.0 + * @category constructors + */ +export const repeatEffectChunkOption: ( + effect: Effect.Effect, Option.Option, R> +) => Stream = internal.repeatEffectChunkOption + +/** + * Creates a stream from an effect producing values of type `A` until it fails + * with `None`. + * + * @example + * ```ts + * // In this example, we're draining an Iterator to create a stream from it + * import { Stream, Effect, Option } from "effect" + * + * const drainIterator = (it: Iterator): Stream.Stream => + * Stream.repeatEffectOption( + * Effect.sync(() => it.next()).pipe( + * Effect.andThen((res) => { + * if (res.done) { + * return Effect.fail(Option.none()) + * } + * return Effect.succeed(res.value) + * }) + * ) + * ) + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const repeatEffectOption: (effect: Effect.Effect, R>) => Stream = + internal.repeatEffectOption + +/** + * Creates a stream from an effect producing a value of type `A`, which is + * repeated using the specified schedule. + * + * @since 2.0.0 + * @category constructors + */ +export const repeatEffectWithSchedule: ( + effect: Effect.Effect, + schedule: Schedule.Schedule +) => Stream = internal.repeatEffectWithSchedule + +/** + * Repeats the entire stream using the specified schedule. The stream will + * execute normally, and then repeat again according to the provided schedule. + * The schedule output will be emitted at the end of each repetition. + * + * @since 2.0.0 + * @category utils + */ +export const repeatEither: { + /** + * Repeats the entire stream using the specified schedule. The stream will + * execute normally, and then repeat again according to the provided schedule. + * The schedule output will be emitted at the end of each repetition. + * + * @since 2.0.0 + * @category utils + */ + (schedule: Schedule.Schedule): (self: Stream) => Stream, E, R2 | R> + /** + * Repeats the entire stream using the specified schedule. The stream will + * execute normally, and then repeat again according to the provided schedule. + * The schedule output will be emitted at the end of each repetition. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, schedule: Schedule.Schedule): Stream, E, R | R2> +} = internal.repeatEither + +/** + * Repeats each element of the stream using the provided schedule. Repetitions + * are done in addition to the first execution, which means using + * `Schedule.recurs(1)` actually results in the original effect, plus an + * additional recurrence, for a total of two repetitions of each value in the + * stream. + * + * @since 2.0.0 + * @category utils + */ +export const repeatElements: { + /** + * Repeats each element of the stream using the provided schedule. Repetitions + * are done in addition to the first execution, which means using + * `Schedule.recurs(1)` actually results in the original effect, plus an + * additional recurrence, for a total of two repetitions of each value in the + * stream. + * + * @since 2.0.0 + * @category utils + */ + (schedule: Schedule.Schedule): (self: Stream) => Stream + /** + * Repeats each element of the stream using the provided schedule. Repetitions + * are done in addition to the first execution, which means using + * `Schedule.recurs(1)` actually results in the original effect, plus an + * additional recurrence, for a total of two repetitions of each value in the + * stream. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, schedule: Schedule.Schedule): Stream +} = internal.repeatElements + +/** + * Repeats each element of the stream using the provided schedule. When the + * schedule is finished, then the output of the schedule will be emitted into + * the stream. Repetitions are done in addition to the first execution, which + * means using `Schedule.recurs(1)` actually results in the original effect, + * plus an additional recurrence, for a total of two repetitions of each value + * in the stream. + * + * This function accepts two conversion functions, which allow the output of + * this stream and the output of the provided schedule to be unified into a + * single type. For example, `Either` or similar data type. + * + * @since 2.0.0 + * @category utils + */ +export const repeatElementsWith: { + /** + * Repeats each element of the stream using the provided schedule. When the + * schedule is finished, then the output of the schedule will be emitted into + * the stream. Repetitions are done in addition to the first execution, which + * means using `Schedule.recurs(1)` actually results in the original effect, + * plus an additional recurrence, for a total of two repetitions of each value + * in the stream. + * + * This function accepts two conversion functions, which allow the output of + * this stream and the output of the provided schedule to be unified into a + * single type. For example, `Either` or similar data type. + * + * @since 2.0.0 + * @category utils + */ + ( + schedule: Schedule.Schedule, + options: { readonly onElement: (a: A) => C; readonly onSchedule: (b: B) => C } + ): (self: Stream) => Stream + /** + * Repeats each element of the stream using the provided schedule. When the + * schedule is finished, then the output of the schedule will be emitted into + * the stream. Repetitions are done in addition to the first execution, which + * means using `Schedule.recurs(1)` actually results in the original effect, + * plus an additional recurrence, for a total of two repetitions of each value + * in the stream. + * + * This function accepts two conversion functions, which allow the output of + * this stream and the output of the provided schedule to be unified into a + * single type. For example, `Either` or similar data type. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + schedule: Schedule.Schedule, + options: { readonly onElement: (a: A) => C; readonly onSchedule: (b: B) => C } + ): Stream +} = internal.repeatElementsWith + +/** + * Repeats the provided value infinitely. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.repeatValue(0) + * + * Effect.runPromise(Stream.runCollect(stream.pipe(Stream.take(5)))).then(console.log) + * // { _id: 'Chunk', values: [ 0, 0, 0, 0, 0 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const repeatValue: (value: A) => Stream = internal.repeatValue + +/** + * Repeats the entire stream using the specified schedule. The stream will + * execute normally, and then repeat again according to the provided schedule. + * The schedule output will be emitted at the end of each repetition and can + * be unified with the stream elements using the provided functions. + * + * @since 2.0.0 + * @category utils + */ +export const repeatWith: { + /** + * Repeats the entire stream using the specified schedule. The stream will + * execute normally, and then repeat again according to the provided schedule. + * The schedule output will be emitted at the end of each repetition and can + * be unified with the stream elements using the provided functions. + * + * @since 2.0.0 + * @category utils + */ + ( + schedule: Schedule.Schedule, + options: { readonly onElement: (a: A) => C; readonly onSchedule: (b: B) => C } + ): (self: Stream) => Stream + /** + * Repeats the entire stream using the specified schedule. The stream will + * execute normally, and then repeat again according to the provided schedule. + * The schedule output will be emitted at the end of each repetition and can + * be unified with the stream elements using the provided functions. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + schedule: Schedule.Schedule, + options: { readonly onElement: (a: A) => C; readonly onSchedule: (b: B) => C } + ): Stream +} = internal.repeatWith + +/** + * When the stream fails, retry it according to the given schedule + * + * This retries the entire stream, so will re-execute all of the stream's + * acquire operations. + * + * The schedule is reset as soon as the first element passes through the + * stream again. + * + * @since 2.0.0 + * @category utils + */ +export const retry: { + /** + * When the stream fails, retry it according to the given schedule + * + * This retries the entire stream, so will re-execute all of the stream's + * acquire operations. + * + * The schedule is reset as soon as the first element passes through the + * stream again. + * + * @since 2.0.0 + * @category utils + */ + (policy: Schedule.Schedule, R2>): (self: Stream) => Stream + /** + * When the stream fails, retry it according to the given schedule + * + * This retries the entire stream, so will re-execute all of the stream's + * acquire operations. + * + * The schedule is reset as soon as the first element passes through the + * stream again. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, policy: Schedule.Schedule, R2>): Stream +} = internal.retry + +/** + * Apply an `ExecutionPlan` to the stream, which allows you to fallback to + * different resources in case of failure. + * + * If you have a stream that could fail with partial results, you can use + * the `preventFallbackOnPartialStream` option to prevent contamination of + * the final stream with partial results. + * + * @since 3.16.0 + * @category Error handling + * @experimental + */ +export const withExecutionPlan: { + /** + * Apply an `ExecutionPlan` to the stream, which allows you to fallback to + * different resources in case of failure. + * + * If you have a stream that could fail with partial results, you can use + * the `preventFallbackOnPartialStream` option to prevent contamination of + * the final stream with partial results. + * + * @since 3.16.0 + * @category Error handling + * @experimental + */ + ( + policy: ExecutionPlan<{ provides: Provides; input: Input; error: PolicyE; requirements: R2 }>, + options?: { readonly preventFallbackOnPartialStream?: boolean | undefined } + ): (self: Stream) => Stream> + /** + * Apply an `ExecutionPlan` to the stream, which allows you to fallback to + * different resources in case of failure. + * + * If you have a stream that could fail with partial results, you can use + * the `preventFallbackOnPartialStream` option to prevent contamination of + * the final stream with partial results. + * + * @since 3.16.0 + * @category Error handling + * @experimental + */ + ( + self: Stream, + policy: ExecutionPlan<{ provides: Provides; input: Input; error: PolicyE; requirements: R2 }>, + options?: { readonly preventFallbackOnPartialStream?: boolean | undefined } + ): Stream> +} = internal.withExecutionPlan + +/** + * Runs the sink on the stream to produce either the sink's result or an error. + * + * @since 2.0.0 + * @category destructors + */ +export const run: { + /** + * Runs the sink on the stream to produce either the sink's result or an error. + * + * @since 2.0.0 + * @category destructors + */ + (sink: Sink.Sink): (self: Stream) => Effect.Effect> + /** + * Runs the sink on the stream to produce either the sink's result or an error. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, sink: Sink.Sink): Effect.Effect> +} = internal.run + +/** + * Runs the stream and collects all of its elements to a chunk. + * + * @since 2.0.0 + * @category destructors + */ +export const runCollect: (self: Stream) => Effect.Effect, E, R> = internal.runCollect + +/** + * Runs the stream and emits the number of elements processed + * + * @since 2.0.0 + * @category destructors + */ +export const runCount: (self: Stream) => Effect.Effect = internal.runCount + +/** + * Runs the stream only for its effects. The emitted elements are discarded. + * + * @since 2.0.0 + * @category destructors + */ +export const runDrain: (self: Stream) => Effect.Effect = internal.runDrain + +/** + * Executes a pure fold over the stream of values - reduces all elements in + * the stream to a value of type `S`. + * + * @since 2.0.0 + * @category destructors + */ +export const runFold: { + /** + * Executes a pure fold over the stream of values - reduces all elements in + * the stream to a value of type `S`. + * + * @since 2.0.0 + * @category destructors + */ + (s: S, f: (s: S, a: A) => S): (self: Stream) => Effect.Effect + /** + * Executes a pure fold over the stream of values - reduces all elements in + * the stream to a value of type `S`. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, s: S, f: (s: S, a: A) => S): Effect.Effect +} = internal.runFold + +/** + * Executes an effectful fold over the stream of values. + * + * @since 2.0.0 + * @category destructors + */ +export const runFoldEffect: { + /** + * Executes an effectful fold over the stream of values. + * + * @since 2.0.0 + * @category destructors + */ + (s: S, f: (s: S, a: A) => Effect.Effect): (self: Stream) => Effect.Effect> + /** + * Executes an effectful fold over the stream of values. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, s: S, f: (s: S, a: A) => Effect.Effect): Effect.Effect> +} = internal.runFoldEffect + +/** + * Executes a pure fold over the stream of values. Returns a scoped value that + * represents the scope of the stream. + * + * @since 2.0.0 + * @category destructors + */ +export const runFoldScoped: { + /** + * Executes a pure fold over the stream of values. Returns a scoped value that + * represents the scope of the stream. + * + * @since 2.0.0 + * @category destructors + */ + (s: S, f: (s: S, a: A) => S): (self: Stream) => Effect.Effect + /** + * Executes a pure fold over the stream of values. Returns a scoped value that + * represents the scope of the stream. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, s: S, f: (s: S, a: A) => S): Effect.Effect +} = internal.runFoldScoped + +/** + * Executes an effectful fold over the stream of values. Returns a scoped + * value that represents the scope of the stream. + * + * @since 2.0.0 + * @category destructors + */ +export const runFoldScopedEffect: { + /** + * Executes an effectful fold over the stream of values. Returns a scoped + * value that represents the scope of the stream. + * + * @since 2.0.0 + * @category destructors + */ + (s: S, f: (s: S, a: A) => Effect.Effect): (self: Stream) => Effect.Effect + /** + * Executes an effectful fold over the stream of values. Returns a scoped + * value that represents the scope of the stream. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, s: S, f: (s: S, a: A) => Effect.Effect): Effect.Effect +} = internal.runFoldScopedEffect + +/** + * Reduces the elements in the stream to a value of type `S`. Stops the fold + * early when the condition is not fulfilled. Example: + * + * @since 2.0.0 + * @category destructors + */ +export const runFoldWhile: { + /** + * Reduces the elements in the stream to a value of type `S`. Stops the fold + * early when the condition is not fulfilled. Example: + * + * @since 2.0.0 + * @category destructors + */ + (s: S, cont: Predicate, f: (s: S, a: A) => S): (self: Stream) => Effect.Effect + /** + * Reduces the elements in the stream to a value of type `S`. Stops the fold + * early when the condition is not fulfilled. Example: + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, s: S, cont: Predicate, f: (s: S, a: A) => S): Effect.Effect +} = internal.runFoldWhile + +/** + * Executes an effectful fold over the stream of values. Stops the fold early + * when the condition is not fulfilled. + * + * @since 2.0.0 + * @category destructors + */ +export const runFoldWhileEffect: { + /** + * Executes an effectful fold over the stream of values. Stops the fold early + * when the condition is not fulfilled. + * + * @since 2.0.0 + * @category destructors + */ + (s: S, cont: Predicate, f: (s: S, a: A) => Effect.Effect): (self: Stream) => Effect.Effect> + /** + * Executes an effectful fold over the stream of values. Stops the fold early + * when the condition is not fulfilled. + * + * @since 2.0.0 + * @category destructors + */ + ( + self: Stream, + s: S, + cont: Predicate, + f: (s: S, a: A) => Effect.Effect + ): Effect.Effect> +} = internal.runFoldWhileEffect + +/** + * Executes a pure fold over the stream of values. Returns a scoped value that + * represents the scope of the stream. Stops the fold early when the condition + * is not fulfilled. + * + * @since 2.0.0 + * @category destructors + */ +export const runFoldWhileScoped: { + /** + * Executes a pure fold over the stream of values. Returns a scoped value that + * represents the scope of the stream. Stops the fold early when the condition + * is not fulfilled. + * + * @since 2.0.0 + * @category destructors + */ + (s: S, cont: Predicate, f: (s: S, a: A) => S): (self: Stream) => Effect.Effect + /** + * Executes a pure fold over the stream of values. Returns a scoped value that + * represents the scope of the stream. Stops the fold early when the condition + * is not fulfilled. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, s: S, cont: Predicate, f: (s: S, a: A) => S): Effect.Effect +} = internal.runFoldWhileScoped + +/** + * Executes an effectful fold over the stream of values. Returns a scoped + * value that represents the scope of the stream. Stops the fold early when + * the condition is not fulfilled. + * + * @since 2.0.0 + * @category destructors + */ +export const runFoldWhileScopedEffect: { + /** + * Executes an effectful fold over the stream of values. Returns a scoped + * value that represents the scope of the stream. Stops the fold early when + * the condition is not fulfilled. + * + * @since 2.0.0 + * @category destructors + */ + (s: S, cont: Predicate, f: (s: S, a: A) => Effect.Effect): (self: Stream) => Effect.Effect + /** + * Executes an effectful fold over the stream of values. Returns a scoped + * value that represents the scope of the stream. Stops the fold early when + * the condition is not fulfilled. + * + * @since 2.0.0 + * @category destructors + */ + ( + self: Stream, + s: S, + cont: Predicate, + f: (s: S, a: A) => Effect.Effect + ): Effect.Effect +} = internal.runFoldWhileScopedEffect + +/** + * Consumes all elements of the stream, passing them to the specified + * callback. + * + * @since 2.0.0 + * @category destructors + */ +export const runForEach: { + /** + * Consumes all elements of the stream, passing them to the specified + * callback. + * + * @since 2.0.0 + * @category destructors + */ + (f: (a: A) => Effect.Effect): (self: Stream) => Effect.Effect + /** + * Consumes all elements of the stream, passing them to the specified + * callback. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, f: (a: A) => Effect.Effect): Effect.Effect +} = internal.runForEach + +/** + * Consumes all elements of the stream, passing them to the specified + * callback. + * + * @since 2.0.0 + * @category destructors + */ +export const runForEachChunk: { + /** + * Consumes all elements of the stream, passing them to the specified + * callback. + * + * @since 2.0.0 + * @category destructors + */ + (f: (a: Chunk.Chunk) => Effect.Effect): (self: Stream) => Effect.Effect + /** + * Consumes all elements of the stream, passing them to the specified + * callback. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, f: (a: Chunk.Chunk) => Effect.Effect): Effect.Effect +} = internal.runForEachChunk + +/** + * Like `Stream.runForEachChunk`, but returns a scoped effect so the + * finalization order can be controlled. + * + * @since 2.0.0 + * @category destructors + */ +export const runForEachChunkScoped: { + /** + * Like `Stream.runForEachChunk`, but returns a scoped effect so the + * finalization order can be controlled. + * + * @since 2.0.0 + * @category destructors + */ + (f: (a: Chunk.Chunk) => Effect.Effect): (self: Stream) => Effect.Effect + /** + * Like `Stream.runForEachChunk`, but returns a scoped effect so the + * finalization order can be controlled. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, f: (a: Chunk.Chunk) => Effect.Effect): Effect.Effect +} = internal.runForEachChunkScoped + +/** + * Like `Stream.forEach`, but returns a scoped effect so the finalization + * order can be controlled. + * + * @since 2.0.0 + * @category destructors + */ +export const runForEachScoped: { + /** + * Like `Stream.forEach`, but returns a scoped effect so the finalization + * order can be controlled. + * + * @since 2.0.0 + * @category destructors + */ + (f: (a: A) => Effect.Effect): (self: Stream) => Effect.Effect + /** + * Like `Stream.forEach`, but returns a scoped effect so the finalization + * order can be controlled. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, f: (a: A) => Effect.Effect): Effect.Effect +} = internal.runForEachScoped + +/** + * Consumes elements of the stream, passing them to the specified callback, + * and terminating consumption when the callback returns `false`. + * + * @since 2.0.0 + * @category destructors + */ +export const runForEachWhile: { + /** + * Consumes elements of the stream, passing them to the specified callback, + * and terminating consumption when the callback returns `false`. + * + * @since 2.0.0 + * @category destructors + */ + (f: (a: A) => Effect.Effect): (self: Stream) => Effect.Effect + /** + * Consumes elements of the stream, passing them to the specified callback, + * and terminating consumption when the callback returns `false`. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, f: (a: A) => Effect.Effect): Effect.Effect +} = internal.runForEachWhile + +/** + * Like `Stream.runForEachWhile`, but returns a scoped effect so the + * finalization order can be controlled. + * + * @since 2.0.0 + * @category destructors + */ +export const runForEachWhileScoped: { + /** + * Like `Stream.runForEachWhile`, but returns a scoped effect so the + * finalization order can be controlled. + * + * @since 2.0.0 + * @category destructors + */ + (f: (a: A) => Effect.Effect): (self: Stream) => Effect.Effect + /** + * Like `Stream.runForEachWhile`, but returns a scoped effect so the + * finalization order can be controlled. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, f: (a: A) => Effect.Effect): Effect.Effect +} = internal.runForEachWhileScoped + +/** + * Runs the stream to completion and yields the first value emitted by it, + * discarding the rest of the elements. + * + * @since 2.0.0 + * @category destructors + */ +export const runHead: (self: Stream) => Effect.Effect, E, R> = internal.runHead + +/** + * Publishes elements of this stream to a `PubSub`. Stream failure and ending will + * also be signalled. + * + * @since 2.0.0 + * @category destructors + */ +export const runIntoPubSub: { + /** + * Publishes elements of this stream to a `PubSub`. Stream failure and ending will + * also be signalled. + * + * @since 2.0.0 + * @category destructors + */ + (pubsub: PubSub.PubSub>): (self: Stream) => Effect.Effect + /** + * Publishes elements of this stream to a `PubSub`. Stream failure and ending will + * also be signalled. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, pubsub: PubSub.PubSub>): Effect.Effect +} = internal.runIntoPubSub + +/** + * Like `Stream.runIntoPubSub`, but provides the result as a scoped effect to + * allow for scope composition. + * + * @since 2.0.0 + * @category destructors + */ +export const runIntoPubSubScoped: { + /** + * Like `Stream.runIntoPubSub`, but provides the result as a scoped effect to + * allow for scope composition. + * + * @since 2.0.0 + * @category destructors + */ + (pubsub: PubSub.PubSub>): (self: Stream) => Effect.Effect + /** + * Like `Stream.runIntoPubSub`, but provides the result as a scoped effect to + * allow for scope composition. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, pubsub: PubSub.PubSub>): Effect.Effect +} = internal.runIntoPubSubScoped + +/** + * Enqueues elements of this stream into a queue. Stream failure and ending + * will also be signalled. + * + * @since 2.0.0 + * @category destructors + */ +export const runIntoQueue: { + /** + * Enqueues elements of this stream into a queue. Stream failure and ending + * will also be signalled. + * + * @since 2.0.0 + * @category destructors + */ + (queue: Queue.Enqueue>): (self: Stream) => Effect.Effect + /** + * Enqueues elements of this stream into a queue. Stream failure and ending + * will also be signalled. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, queue: Queue.Enqueue>): Effect.Effect +} = internal.runIntoQueue + +/** + * Like `Stream.runIntoQueue`, but provides the result as a scoped Effect + * to allow for scope composition. + * + * @since 2.0.0 + * @category destructors + */ +export const runIntoQueueElementsScoped: { + /** + * Like `Stream.runIntoQueue`, but provides the result as a scoped Effect + * to allow for scope composition. + * + * @since 2.0.0 + * @category destructors + */ + (queue: Queue.Enqueue>>): (self: Stream) => Effect.Effect + /** + * Like `Stream.runIntoQueue`, but provides the result as a scoped Effect + * to allow for scope composition. + * + * @since 2.0.0 + * @category destructors + */ + ( + self: Stream, + queue: Queue.Enqueue>> + ): Effect.Effect +} = internal.runIntoQueueElementsScoped + +/** + * Like `Stream.runIntoQueue`, but provides the result as a scoped effect + * to allow for scope composition. + * + * @since 2.0.0 + * @category destructors + */ +export const runIntoQueueScoped: { + /** + * Like `Stream.runIntoQueue`, but provides the result as a scoped effect + * to allow for scope composition. + * + * @since 2.0.0 + * @category destructors + */ + (queue: Queue.Enqueue>): (self: Stream) => Effect.Effect + /** + * Like `Stream.runIntoQueue`, but provides the result as a scoped effect + * to allow for scope composition. + * + * @since 2.0.0 + * @category destructors + */ + (self: Stream, queue: Queue.Enqueue>): Effect.Effect +} = internal.runIntoQueueScoped + +/** + * Runs the stream to completion and yields the last value emitted by it, + * discarding the rest of the elements. + * + * @since 2.0.0 + * @category destructors + */ +export const runLast: (self: Stream) => Effect.Effect, E, R> = internal.runLast + +/** + * @since 2.0.0 + * @category destructors + */ +export const runScoped: { + /** + * @since 2.0.0 + * @category destructors + */ + (sink: Sink.Sink): (self: Stream) => Effect.Effect + /** + * @since 2.0.0 + * @category destructors + */ + (self: Stream, sink: Sink.Sink): Effect.Effect +} = internal.runScoped + +/** + * Runs the stream to a sink which sums elements, provided they are Numeric. + * + * @since 2.0.0 + * @category destructors + */ +export const runSum: (self: Stream) => Effect.Effect = internal.runSum + +/** + * Statefully maps over the elements of this stream to produce all + * intermediate results of type `S` given an initial S. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(1, 6).pipe(Stream.scan(0, (a, b) => a + b)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 3, 6, 10, 15, 21 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const scan: { + /** + * Statefully maps over the elements of this stream to produce all + * intermediate results of type `S` given an initial S. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(1, 6).pipe(Stream.scan(0, (a, b) => a + b)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 3, 6, 10, 15, 21 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (s: S, f: (s: S, a: A) => S): (self: Stream) => Stream + /** + * Statefully maps over the elements of this stream to produce all + * intermediate results of type `S` given an initial S. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.range(1, 6).pipe(Stream.scan(0, (a, b) => a + b)) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 3, 6, 10, 15, 21 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, s: S, f: (s: S, a: A) => S): Stream +} = internal.scan + +/** + * Statefully and effectfully maps over the elements of this stream to produce + * all intermediate results of type `S` given an initial S. + * + * @since 2.0.0 + * @category utils + */ +export const scanEffect: { + /** + * Statefully and effectfully maps over the elements of this stream to produce + * all intermediate results of type `S` given an initial S. + * + * @since 2.0.0 + * @category utils + */ + (s: S, f: (s: S, a: A) => Effect.Effect): (self: Stream) => Stream + /** + * Statefully and effectfully maps over the elements of this stream to produce + * all intermediate results of type `S` given an initial S. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, s: S, f: (s: S, a: A) => Effect.Effect): Stream +} = internal.scanEffect + +/** + * Statefully maps over the elements of this stream to produce all + * intermediate results. + * + * See also `Stream.scan`. + * + * @since 2.0.0 + * @category utils + */ +export const scanReduce: { + /** + * Statefully maps over the elements of this stream to produce all + * intermediate results. + * + * See also `Stream.scan`. + * + * @since 2.0.0 + * @category utils + */ + (f: (a2: A2 | A, a: A) => A2): (self: Stream) => Stream + /** + * Statefully maps over the elements of this stream to produce all + * intermediate results. + * + * See also `Stream.scan`. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, f: (a2: A | A2, a: A) => A2): Stream +} = internal.scanReduce + +/** + * Statefully and effectfully maps over the elements of this stream to produce + * all intermediate results. + * + * See also `Stream.scanEffect`. + * + * @since 2.0.0 + * @category utils + */ +export const scanReduceEffect: { + /** + * Statefully and effectfully maps over the elements of this stream to produce + * all intermediate results. + * + * See also `Stream.scanEffect`. + * + * @since 2.0.0 + * @category utils + */ + (f: (a2: A2 | A, a: A) => Effect.Effect): (self: Stream) => Stream + /** + * Statefully and effectfully maps over the elements of this stream to produce + * all intermediate results. + * + * See also `Stream.scanEffect`. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + f: (a2: A | A2, a: A) => Effect.Effect + ): Stream +} = internal.scanReduceEffect + +/** + * Schedules the output of the stream using the provided `schedule`. + * + * @since 2.0.0 + * @category utils + */ +export const schedule: { + /** + * Schedules the output of the stream using the provided `schedule`. + * + * @since 2.0.0 + * @category utils + */ + (schedule: Schedule.Schedule): (self: Stream) => Stream + /** + * Schedules the output of the stream using the provided `schedule`. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, schedule: Schedule.Schedule): Stream +} = internal.schedule + +/** + * Schedules the output of the stream using the provided `schedule` and emits + * its output at the end (if `schedule` is finite). Uses the provided function + * to align the stream and schedule outputs on the same type. + * + * @since 2.0.0 + * @category utils + */ +export const scheduleWith: { + /** + * Schedules the output of the stream using the provided `schedule` and emits + * its output at the end (if `schedule` is finite). Uses the provided function + * to align the stream and schedule outputs on the same type. + * + * @since 2.0.0 + * @category utils + */ + ( + schedule: Schedule.Schedule, + options: { readonly onElement: (a: A) => C; readonly onSchedule: (b: B) => C } + ): (self: Stream) => Stream + /** + * Schedules the output of the stream using the provided `schedule` and emits + * its output at the end (if `schedule` is finite). Uses the provided function + * to align the stream and schedule outputs on the same type. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + schedule: Schedule.Schedule, + options: { readonly onElement: (a: A) => C; readonly onSchedule: (b: B) => C } + ): Stream +} = internal.scheduleWith + +/** + * Creates a single-valued stream from a scoped resource. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * // Creating a single-valued stream from a scoped resource + * const stream = Stream.scoped( + * Effect.acquireRelease( + * Console.log("acquire"), + * () => Console.log("release") + * ) + * ).pipe( + * Stream.flatMap(() => Console.log("use")) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // acquire + * // use + * // release + * // { _id: 'Chunk', values: [ undefined ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const scoped: (effect: Effect.Effect) => Stream> = + internal.scoped + +/** + * Use a function that receives a scope and returns an effect to emit an output + * element. The output element will be the result of the returned effect, if + * successful. + * + * @since 3.11.0 + * @category constructors + */ +export const scopedWith: (f: (scope: Scope.Scope) => Effect.Effect) => Stream = + internal.scopedWith + +/** + * Emits a sliding window of `n` elements. + * + * ```ts + * import { pipe, Stream } from "effect" + * + * pipe( + * Stream.make(1, 2, 3, 4), + * Stream.sliding(2), + * Stream.runCollect + * ) + * // => Chunk(Chunk(1, 2), Chunk(2, 3), Chunk(3, 4)) + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const sliding: { + /** + * Emits a sliding window of `n` elements. + * + * ```ts + * import { pipe, Stream } from "effect" + * + * pipe( + * Stream.make(1, 2, 3, 4), + * Stream.sliding(2), + * Stream.runCollect + * ) + * // => Chunk(Chunk(1, 2), Chunk(2, 3), Chunk(3, 4)) + * ``` + * + * @since 2.0.0 + * @category utils + */ + (chunkSize: number): (self: Stream) => Stream, E, R> + /** + * Emits a sliding window of `n` elements. + * + * ```ts + * import { pipe, Stream } from "effect" + * + * pipe( + * Stream.make(1, 2, 3, 4), + * Stream.sliding(2), + * Stream.runCollect + * ) + * // => Chunk(Chunk(1, 2), Chunk(2, 3), Chunk(3, 4)) + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, chunkSize: number): Stream, E, R> +} = internal.sliding + +/** + * Like `sliding`, but with a configurable `stepSize` parameter. + * + * @since 2.0.0 + * @category utils + */ +export const slidingSize: { + /** + * Like `sliding`, but with a configurable `stepSize` parameter. + * + * @since 2.0.0 + * @category utils + */ + (chunkSize: number, stepSize: number): (self: Stream) => Stream, E, R> + /** + * Like `sliding`, but with a configurable `stepSize` parameter. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, chunkSize: number, stepSize: number): Stream, E, R> +} = internal.slidingSize + +/** + * Converts an option on values into an option on errors. + * + * @since 2.0.0 + * @category utils + */ +export const some: (self: Stream, E, R>) => Stream, R> = internal.some + +/** + * Extracts the optional value, or returns the given 'default'. + * + * @since 2.0.0 + * @category utils + */ +export const someOrElse: { + /** + * Extracts the optional value, or returns the given 'default'. + * + * @since 2.0.0 + * @category utils + */ + (fallback: LazyArg): (self: Stream, E, R>) => Stream + /** + * Extracts the optional value, or returns the given 'default'. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, E, R>, fallback: LazyArg): Stream +} = internal.someOrElse + +/** + * Extracts the optional value, or fails with the given error 'e'. + * + * @since 2.0.0 + * @category utils + */ +export const someOrFail: { + /** + * Extracts the optional value, or fails with the given error 'e'. + * + * @since 2.0.0 + * @category utils + */ + (error: LazyArg): (self: Stream, E, R>) => Stream + /** + * Extracts the optional value, or fails with the given error 'e'. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, E, R>, error: LazyArg): Stream +} = internal.someOrFail + +/** + * Splits elements based on a predicate or refinement. + * + * ```ts + * import { pipe, Stream } from "effect" + * + * pipe( + * Stream.range(1, 10), + * Stream.split((n) => n % 4 === 0), + * Stream.runCollect + * ) + * // => Chunk(Chunk(1, 2, 3), Chunk(5, 6, 7), Chunk(9)) + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const split: { + /** + * Splits elements based on a predicate or refinement. + * + * ```ts + * import { pipe, Stream } from "effect" + * + * pipe( + * Stream.range(1, 10), + * Stream.split((n) => n % 4 === 0), + * Stream.runCollect + * ) + * // => Chunk(Chunk(1, 2, 3), Chunk(5, 6, 7), Chunk(9)) + * ``` + * + * @since 2.0.0 + * @category utils + */ + (refinement: Refinement, B>): (self: Stream) => Stream>, E, R> + /** + * Splits elements based on a predicate or refinement. + * + * ```ts + * import { pipe, Stream } from "effect" + * + * pipe( + * Stream.range(1, 10), + * Stream.split((n) => n % 4 === 0), + * Stream.runCollect + * ) + * // => Chunk(Chunk(1, 2, 3), Chunk(5, 6, 7), Chunk(9)) + * ``` + * + * @since 2.0.0 + * @category utils + */ + (predicate: Predicate>): (self: Stream) => Stream, E, R> + /** + * Splits elements based on a predicate or refinement. + * + * ```ts + * import { pipe, Stream } from "effect" + * + * pipe( + * Stream.range(1, 10), + * Stream.split((n) => n % 4 === 0), + * Stream.runCollect + * ) + * // => Chunk(Chunk(1, 2, 3), Chunk(5, 6, 7), Chunk(9)) + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, refinement: Refinement): Stream>, E, R> + /** + * Splits elements based on a predicate or refinement. + * + * ```ts + * import { pipe, Stream } from "effect" + * + * pipe( + * Stream.range(1, 10), + * Stream.split((n) => n % 4 === 0), + * Stream.runCollect + * ) + * // => Chunk(Chunk(1, 2, 3), Chunk(5, 6, 7), Chunk(9)) + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, predicate: Predicate): Stream, E, R> +} = internal.split + +/** + * Splits elements on a delimiter and transforms the splits into desired output. + * + * @since 2.0.0 + * @category utils + */ +export const splitOnChunk: { + /** + * Splits elements on a delimiter and transforms the splits into desired output. + * + * @since 2.0.0 + * @category utils + */ + (delimiter: Chunk.Chunk): (self: Stream) => Stream, E, R> + /** + * Splits elements on a delimiter and transforms the splits into desired output. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, delimiter: Chunk.Chunk): Stream, E, R> +} = internal.splitOnChunk + +/** + * Splits strings on newlines. Handles both Windows newlines (`\r\n`) and UNIX + * newlines (`\n`). + * + * @since 2.0.0 + * @category combinators + */ +export const splitLines: (self: Stream) => Stream = internal.splitLines + +/** + * Creates a single-valued pure stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * // A Stream with a single number + * const stream = Stream.succeed(3) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 3 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const succeed: (value: A) => Stream = internal.succeed + +/** + * Creates a single-valued pure stream. + * + * @since 2.0.0 + * @category constructors + */ +export const sync: (evaluate: LazyArg) => Stream = internal.sync + +/** + * Returns a lazily constructed stream. + * + * @since 2.0.0 + * @category constructors + */ +export const suspend: (stream: LazyArg>) => Stream = internal.suspend + +/** + * Takes the specified number of elements from this stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.take(Stream.iterate(0, (n) => n + 1), 5) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const take: { + /** + * Takes the specified number of elements from this stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.take(Stream.iterate(0, (n) => n + 1), 5) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (n: number): (self: Stream) => Stream + /** + * Takes the specified number of elements from this stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.take(Stream.iterate(0, (n) => n + 1), 5) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, n: number): Stream +} = internal.take + +/** + * Takes the last specified number of elements from this stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.takeRight(Stream.make(1, 2, 3, 4, 5, 6), 3) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 4, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const takeRight: { + /** + * Takes the last specified number of elements from this stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.takeRight(Stream.make(1, 2, 3, 4, 5, 6), 3) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 4, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (n: number): (self: Stream) => Stream + /** + * Takes the last specified number of elements from this stream. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.takeRight(Stream.make(1, 2, 3, 4, 5, 6), 3) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 4, 5, 6 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, n: number): Stream +} = internal.takeRight + +/** + * Takes all elements of the stream until the specified predicate evaluates to + * `true`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.takeUntil(Stream.iterate(0, (n) => n + 1), (n) => n === 4) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const takeUntil: { + /** + * Takes all elements of the stream until the specified predicate evaluates to + * `true`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.takeUntil(Stream.iterate(0, (n) => n + 1), (n) => n === 4) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (predicate: Predicate>): (self: Stream) => Stream + /** + * Takes all elements of the stream until the specified predicate evaluates to + * `true`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.takeUntil(Stream.iterate(0, (n) => n + 1), (n) => n === 4) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, predicate: Predicate): Stream +} = internal.takeUntil + +/** + * Takes all elements of the stream until the specified effectual predicate + * evaluates to `true`. + * + * @since 2.0.0 + * @category utils + */ +export const takeUntilEffect: { + /** + * Takes all elements of the stream until the specified effectual predicate + * evaluates to `true`. + * + * @since 2.0.0 + * @category utils + */ + (predicate: (a: NoInfer) => Effect.Effect): (self: Stream) => Stream + /** + * Takes all elements of the stream until the specified effectual predicate + * evaluates to `true`. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, predicate: (a: A) => Effect.Effect): Stream +} = internal.takeUntilEffect + +/** + * Takes all elements of the stream for as long as the specified predicate + * evaluates to `true`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.takeWhile(Stream.iterate(0, (n) => n + 1), (n) => n < 5) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const takeWhile: { + /** + * Takes all elements of the stream for as long as the specified predicate + * evaluates to `true`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.takeWhile(Stream.iterate(0, (n) => n + 1), (n) => n < 5) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (refinement: Refinement, B>): (self: Stream) => Stream + /** + * Takes all elements of the stream for as long as the specified predicate + * evaluates to `true`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.takeWhile(Stream.iterate(0, (n) => n + 1), (n) => n < 5) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (predicate: Predicate>): (self: Stream) => Stream + /** + * Takes all elements of the stream for as long as the specified predicate + * evaluates to `true`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.takeWhile(Stream.iterate(0, (n) => n + 1), (n) => n < 5) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, refinement: Refinement): Stream + /** + * Takes all elements of the stream for as long as the specified predicate + * evaluates to `true`. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.takeWhile(Stream.iterate(0, (n) => n + 1), (n) => n < 5) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, predicate: Predicate): Stream +} = internal.takeWhile + +/** + * Adds an effect to consumption of every element of the stream. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3).pipe( + * Stream.tap((n) => Console.log(`before mapping: ${n}`)), + * Stream.map((n) => n * 2), + * Stream.tap((n) => Console.log(`after mapping: ${n}`)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // before mapping: 1 + * // after mapping: 2 + * // before mapping: 2 + * // after mapping: 4 + * // before mapping: 3 + * // after mapping: 6 + * // { _id: 'Chunk', values: [ 2, 4, 6 ] } + * ``` + * + * @since 2.0.0 + * @category sequencing + */ +export const tap: { + /** + * Adds an effect to consumption of every element of the stream. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3).pipe( + * Stream.tap((n) => Console.log(`before mapping: ${n}`)), + * Stream.map((n) => n * 2), + * Stream.tap((n) => Console.log(`after mapping: ${n}`)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // before mapping: 1 + * // after mapping: 2 + * // before mapping: 2 + * // after mapping: 4 + * // before mapping: 3 + * // after mapping: 6 + * // { _id: 'Chunk', values: [ 2, 4, 6 ] } + * ``` + * + * @since 2.0.0 + * @category sequencing + */ + (f: (a: NoInfer) => Effect.Effect): (self: Stream) => Stream + /** + * Adds an effect to consumption of every element of the stream. + * + * @example + * ```ts + * import { Console, Effect, Stream } from "effect" + * + * const stream = Stream.make(1, 2, 3).pipe( + * Stream.tap((n) => Console.log(`before mapping: ${n}`)), + * Stream.map((n) => n * 2), + * Stream.tap((n) => Console.log(`after mapping: ${n}`)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // before mapping: 1 + * // after mapping: 2 + * // before mapping: 2 + * // after mapping: 4 + * // before mapping: 3 + * // after mapping: 6 + * // { _id: 'Chunk', values: [ 2, 4, 6 ] } + * ``` + * + * @since 2.0.0 + * @category sequencing + */ + (self: Stream, f: (a: NoInfer) => Effect.Effect): Stream +} = internal.tap + +/** + * Returns a stream that effectfully "peeks" at the failure or success of + * the stream. + * + * @since 2.0.0 + * @category sequencing + */ +export const tapBoth: { + /** + * Returns a stream that effectfully "peeks" at the failure or success of + * the stream. + * + * @since 2.0.0 + * @category sequencing + */ + ( + options: { + readonly onFailure: (e: NoInfer) => Effect.Effect + readonly onSuccess: (a: NoInfer) => Effect.Effect + } + ): (self: Stream) => Stream + /** + * Returns a stream that effectfully "peeks" at the failure or success of + * the stream. + * + * @since 2.0.0 + * @category sequencing + */ + ( + self: Stream, + options: { + readonly onFailure: (e: NoInfer) => Effect.Effect + readonly onSuccess: (a: NoInfer) => Effect.Effect + } + ): Stream +} = internal.tapBoth + +/** + * Returns a stream that effectfully "peeks" at the failure of the stream. + * + * @since 2.0.0 + * @category sequencing + */ +export const tapError: { + /** + * Returns a stream that effectfully "peeks" at the failure of the stream. + * + * @since 2.0.0 + * @category sequencing + */ + (f: (error: NoInfer) => Effect.Effect): (self: Stream) => Stream + /** + * Returns a stream that effectfully "peeks" at the failure of the stream. + * + * @since 2.0.0 + * @category sequencing + */ + (self: Stream, f: (error: E) => Effect.Effect): Stream +} = internal.tapError + +/** + * Returns a stream that effectfully "peeks" at the cause of failure of the + * stream. + * + * @since 2.0.0 + * @category utils + */ +export const tapErrorCause: { + /** + * Returns a stream that effectfully "peeks" at the cause of failure of the + * stream. + * + * @since 2.0.0 + * @category utils + */ + (f: (cause: Cause.Cause>) => Effect.Effect): (self: Stream) => Stream + /** + * Returns a stream that effectfully "peeks" at the cause of failure of the + * stream. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + f: (cause: Cause.Cause) => Effect.Effect + ): Stream +} = internal.tapErrorCause + +/** + * Sends all elements emitted by this stream to the specified sink in addition + * to emitting them. + * + * @since 2.0.0 + * @category sequencing + */ +export const tapSink: { + /** + * Sends all elements emitted by this stream to the specified sink in addition + * to emitting them. + * + * @since 2.0.0 + * @category sequencing + */ + (sink: Sink.Sink): (self: Stream) => Stream + /** + * Sends all elements emitted by this stream to the specified sink in addition + * to emitting them. + * + * @since 2.0.0 + * @category sequencing + */ + (self: Stream, sink: Sink.Sink): Stream +} = internal.tapSink + +/** + * Delays the chunks of this stream according to the given bandwidth + * parameters using the token bucket algorithm. Allows for burst in the + * processing of elements by allowing the token bucket to accumulate tokens up + * to a `units + burst` threshold. The weight of each chunk is determined by + * the `cost` function. + * + * If using the "enforce" strategy, chunks that do not meet the bandwidth + * constraints are dropped. If using the "shape" strategy, chunks are delayed + * until they can be emitted without exceeding the bandwidth constraints. + * + * Defaults to the "shape" strategy. + * + * @example + * ```ts + * import { Chunk, Effect, Schedule, Stream } from "effect" + * + * let last = Date.now() + * const log = (message: string) => + * Effect.sync(() => { + * const end = Date.now() + * console.log(`${message} after ${end - last}ms`) + * last = end + * }) + * + * const stream = Stream.fromSchedule(Schedule.spaced("50 millis")).pipe( + * Stream.take(6), + * Stream.tap((n) => log(`Received ${n}`)), + * Stream.throttle({ + * cost: Chunk.size, + * duration: "100 millis", + * units: 1 + * }), + * Stream.tap((n) => log(`> Emitted ${n}`)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Received 0 after 56ms + * // > Emitted 0 after 0ms + * // Received 1 after 52ms + * // > Emitted 1 after 48ms + * // Received 2 after 52ms + * // > Emitted 2 after 49ms + * // Received 3 after 52ms + * // > Emitted 3 after 48ms + * // Received 4 after 52ms + * // > Emitted 4 after 47ms + * // Received 5 after 52ms + * // > Emitted 5 after 49ms + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4, 5 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ +export const throttle: { + /** + * Delays the chunks of this stream according to the given bandwidth + * parameters using the token bucket algorithm. Allows for burst in the + * processing of elements by allowing the token bucket to accumulate tokens up + * to a `units + burst` threshold. The weight of each chunk is determined by + * the `cost` function. + * + * If using the "enforce" strategy, chunks that do not meet the bandwidth + * constraints are dropped. If using the "shape" strategy, chunks are delayed + * until they can be emitted without exceeding the bandwidth constraints. + * + * Defaults to the "shape" strategy. + * + * @example + * ```ts + * import { Chunk, Effect, Schedule, Stream } from "effect" + * + * let last = Date.now() + * const log = (message: string) => + * Effect.sync(() => { + * const end = Date.now() + * console.log(`${message} after ${end - last}ms`) + * last = end + * }) + * + * const stream = Stream.fromSchedule(Schedule.spaced("50 millis")).pipe( + * Stream.take(6), + * Stream.tap((n) => log(`Received ${n}`)), + * Stream.throttle({ + * cost: Chunk.size, + * duration: "100 millis", + * units: 1 + * }), + * Stream.tap((n) => log(`> Emitted ${n}`)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Received 0 after 56ms + * // > Emitted 0 after 0ms + * // Received 1 after 52ms + * // > Emitted 1 after 48ms + * // Received 2 after 52ms + * // > Emitted 2 after 49ms + * // Received 3 after 52ms + * // > Emitted 3 after 48ms + * // Received 4 after 52ms + * // > Emitted 4 after 47ms + * // Received 5 after 52ms + * // > Emitted 5 after 49ms + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4, 5 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + ( + options: { + readonly cost: (chunk: Chunk.Chunk) => number + readonly units: number + readonly duration: Duration.DurationInput + readonly burst?: number | undefined + readonly strategy?: "enforce" | "shape" | undefined + } + ): (self: Stream) => Stream + /** + * Delays the chunks of this stream according to the given bandwidth + * parameters using the token bucket algorithm. Allows for burst in the + * processing of elements by allowing the token bucket to accumulate tokens up + * to a `units + burst` threshold. The weight of each chunk is determined by + * the `cost` function. + * + * If using the "enforce" strategy, chunks that do not meet the bandwidth + * constraints are dropped. If using the "shape" strategy, chunks are delayed + * until they can be emitted without exceeding the bandwidth constraints. + * + * Defaults to the "shape" strategy. + * + * @example + * ```ts + * import { Chunk, Effect, Schedule, Stream } from "effect" + * + * let last = Date.now() + * const log = (message: string) => + * Effect.sync(() => { + * const end = Date.now() + * console.log(`${message} after ${end - last}ms`) + * last = end + * }) + * + * const stream = Stream.fromSchedule(Schedule.spaced("50 millis")).pipe( + * Stream.take(6), + * Stream.tap((n) => log(`Received ${n}`)), + * Stream.throttle({ + * cost: Chunk.size, + * duration: "100 millis", + * units: 1 + * }), + * Stream.tap((n) => log(`> Emitted ${n}`)) + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // Received 0 after 56ms + * // > Emitted 0 after 0ms + * // Received 1 after 52ms + * // > Emitted 1 after 48ms + * // Received 2 after 52ms + * // > Emitted 2 after 49ms + * // Received 3 after 52ms + * // > Emitted 3 after 48ms + * // Received 4 after 52ms + * // > Emitted 4 after 47ms + * // Received 5 after 52ms + * // > Emitted 5 after 49ms + * // { _id: 'Chunk', values: [ 0, 1, 2, 3, 4, 5 ] } + * ``` + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + options: { + readonly cost: (chunk: Chunk.Chunk) => number + readonly units: number + readonly duration: Duration.DurationInput + readonly burst?: number | undefined + readonly strategy?: "enforce" | "shape" | undefined + } + ): Stream +} = internal.throttle + +/** + * Delays the chunks of this stream according to the given bandwidth + * parameters using the token bucket algorithm. Allows for burst in the + * processing of elements by allowing the token bucket to accumulate tokens up + * to a `units + burst` threshold. The weight of each chunk is determined by + * the effectful `costFn` function. + * + * If using the "enforce" strategy, chunks that do not meet the bandwidth + * constraints are dropped. If using the "shape" strategy, chunks are delayed + * until they can be emitted without exceeding the bandwidth constraints. + * + * Defaults to the "shape" strategy. + * + * @since 2.0.0 + * @category utils + */ +export const throttleEffect: { + /** + * Delays the chunks of this stream according to the given bandwidth + * parameters using the token bucket algorithm. Allows for burst in the + * processing of elements by allowing the token bucket to accumulate tokens up + * to a `units + burst` threshold. The weight of each chunk is determined by + * the effectful `costFn` function. + * + * If using the "enforce" strategy, chunks that do not meet the bandwidth + * constraints are dropped. If using the "shape" strategy, chunks are delayed + * until they can be emitted without exceeding the bandwidth constraints. + * + * Defaults to the "shape" strategy. + * + * @since 2.0.0 + * @category utils + */ + ( + options: { + readonly cost: (chunk: Chunk.Chunk) => Effect.Effect + readonly units: number + readonly duration: Duration.DurationInput + readonly burst?: number | undefined + readonly strategy?: "enforce" | "shape" | undefined + } + ): (self: Stream) => Stream + /** + * Delays the chunks of this stream according to the given bandwidth + * parameters using the token bucket algorithm. Allows for burst in the + * processing of elements by allowing the token bucket to accumulate tokens up + * to a `units + burst` threshold. The weight of each chunk is determined by + * the effectful `costFn` function. + * + * If using the "enforce" strategy, chunks that do not meet the bandwidth + * constraints are dropped. If using the "shape" strategy, chunks are delayed + * until they can be emitted without exceeding the bandwidth constraints. + * + * Defaults to the "shape" strategy. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + options: { + readonly cost: (chunk: Chunk.Chunk) => Effect.Effect + readonly units: number + readonly duration: Duration.DurationInput + readonly burst?: number | undefined + readonly strategy?: "enforce" | "shape" | undefined + } + ): Stream +} = internal.throttleEffect + +/** + * A stream that emits void values spaced by the specified duration. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * let last = Date.now() + * const log = (message: string) => + * Effect.sync(() => { + * const end = Date.now() + * console.log(`${message} after ${end - last}ms`) + * last = end + * }) + * + * const stream = Stream.tick("1 seconds").pipe(Stream.tap(() => log("tick"))) + * + * Effect.runPromise(Stream.runCollect(stream.pipe(Stream.take(5)))).then(console.log) + * // tick after 4ms + * // tick after 1003ms + * // tick after 1001ms + * // tick after 1002ms + * // tick after 1002ms + * // { _id: 'Chunk', values: [ undefined, undefined, undefined, undefined, undefined ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const tick: (interval: Duration.DurationInput) => Stream = internal.tick + +/** + * Ends the stream if it does not produce a value after the specified duration. + * + * @since 2.0.0 + * @category utils + */ +export const timeout: { + /** + * Ends the stream if it does not produce a value after the specified duration. + * + * @since 2.0.0 + * @category utils + */ + (duration: Duration.DurationInput): (self: Stream) => Stream + /** + * Ends the stream if it does not produce a value after the specified duration. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, duration: Duration.DurationInput): Stream +} = internal.timeout + +/** + * Fails the stream with given error if it does not produce a value after d + * duration. + * + * @since 2.0.0 + * @category utils + */ +export const timeoutFail: { + /** + * Fails the stream with given error if it does not produce a value after d + * duration. + * + * @since 2.0.0 + * @category utils + */ + (error: LazyArg, duration: Duration.DurationInput): (self: Stream) => Stream + /** + * Fails the stream with given error if it does not produce a value after d + * duration. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + error: LazyArg, + duration: Duration.DurationInput + ): Stream +} = internal.timeoutFail + +/** + * Fails the stream with given cause if it does not produce a value after d + * duration. + * + * @since 2.0.0 + * @category utils + */ +export const timeoutFailCause: { + /** + * Fails the stream with given cause if it does not produce a value after d + * duration. + * + * @since 2.0.0 + * @category utils + */ + (cause: LazyArg>, duration: Duration.DurationInput): (self: Stream) => Stream + /** + * Fails the stream with given cause if it does not produce a value after d + * duration. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + cause: LazyArg>, + duration: Duration.DurationInput + ): Stream +} = internal.timeoutFailCause + +/** + * Switches the stream if it does not produce a value after the specified + * duration. + * + * @since 2.0.0 + * @category utils + */ +export const timeoutTo: { + /** + * Switches the stream if it does not produce a value after the specified + * duration. + * + * @since 2.0.0 + * @category utils + */ + (duration: Duration.DurationInput, that: Stream): (self: Stream) => Stream + /** + * Switches the stream if it does not produce a value after the specified + * duration. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Stream, + duration: Duration.DurationInput, + that: Stream + ): Stream +} = internal.timeoutTo + +/** + * Converts the stream to a scoped `PubSub` of chunks. After the scope is closed, + * the `PubSub` will never again produce values and should be discarded. + * + * @since 2.0.0 + * @category destructors + */ +export const toPubSub: { + /** + * Converts the stream to a scoped `PubSub` of chunks. After the scope is closed, + * the `PubSub` will never again produce values and should be discarded. + * + * @since 2.0.0 + * @category destructors + */ + ( + capacity: number | { readonly capacity: "unbounded"; readonly replay?: number | undefined } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ): (self: Stream) => Effect.Effect>, never, Scope.Scope | R> + /** + * Converts the stream to a scoped `PubSub` of chunks. After the scope is closed, + * the `PubSub` will never again produce values and should be discarded. + * + * @since 2.0.0 + * @category destructors + */ + ( + self: Stream, + capacity: number | { readonly capacity: "unbounded"; readonly replay?: number | undefined } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ): Effect.Effect>, never, Scope.Scope | R> +} = internal.toPubSub + +/** + * Returns in a scope an Effect that can be used to repeatedly pull chunks + * from the stream. The pull effect fails with None when the stream is + * finished, or with Some error if it fails, otherwise it returns a chunk of + * the stream's output. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * // Simulate a chunked stream + * const stream = Stream.fromIterable([1, 2, 3, 4, 5]).pipe(Stream.rechunk(2)) + * + * const program = Effect.gen(function*() { + * // Create an effect to get data chunks from the stream + * const getChunk = yield* Stream.toPull(stream) + * + * // Continuously fetch and process chunks + * while (true) { + * const chunk = yield* getChunk + * console.log(chunk) + * } + * }) + * + * Effect.runPromise(Effect.scoped(program)).then(console.log, console.error) + * // { _id: 'Chunk', values: [ 1, 2 ] } + * // { _id: 'Chunk', values: [ 3, 4 ] } + * // { _id: 'Chunk', values: [ 5 ] } + * // (FiberFailure) Error: { + * // "_id": "Option", + * // "_tag": "None" + * // } + * ``` + * + * @since 2.0.0 + * @category destructors + */ +export const toPull: ( + self: Stream +) => Effect.Effect, Option.Option, R>, never, Scope.Scope | R> = internal.toPull + +/** + * Converts the stream to a scoped queue of chunks. After the scope is closed, + * the queue will never again produce values and should be discarded. + * + * Defaults to the "suspend" back pressure strategy with a capacity of 2. + * + * @since 2.0.0 + * @category destructors + */ +export const toQueue: { + /** + * Converts the stream to a scoped queue of chunks. After the scope is closed, + * the queue will never again produce values and should be discarded. + * + * Defaults to the "suspend" back pressure strategy with a capacity of 2. + * + * @since 2.0.0 + * @category destructors + */ + ( + options?: + | { readonly strategy?: "dropping" | "sliding" | "suspend" | undefined; readonly capacity?: number | undefined } + | { readonly strategy: "unbounded" } + | undefined + ): (self: Stream) => Effect.Effect>, never, Scope.Scope | R> + /** + * Converts the stream to a scoped queue of chunks. After the scope is closed, + * the queue will never again produce values and should be discarded. + * + * Defaults to the "suspend" back pressure strategy with a capacity of 2. + * + * @since 2.0.0 + * @category destructors + */ + ( + self: Stream, + options?: + | { readonly strategy?: "dropping" | "sliding" | "suspend" | undefined; readonly capacity?: number | undefined } + | { readonly strategy: "unbounded" } + | undefined + ): Effect.Effect>, never, Scope.Scope | R> +} = internal.toQueue + +/** + * Converts the stream to a scoped queue of elements. After the scope is + * closed, the queue will never again produce values and should be discarded. + * + * Defaults to a capacity of 2. + * + * @since 2.0.0 + * @category destructors + */ +export const toQueueOfElements: { + /** + * Converts the stream to a scoped queue of elements. After the scope is + * closed, the queue will never again produce values and should be discarded. + * + * Defaults to a capacity of 2. + * + * @since 2.0.0 + * @category destructors + */ + (options?: { readonly capacity?: number | undefined } | undefined): ( + self: Stream + ) => Effect.Effect>>, never, Scope.Scope | R> + /** + * Converts the stream to a scoped queue of elements. After the scope is + * closed, the queue will never again produce values and should be discarded. + * + * Defaults to a capacity of 2. + * + * @since 2.0.0 + * @category destructors + */ + ( + self: Stream, + options?: { readonly capacity?: number | undefined } | undefined + ): Effect.Effect>>, never, Scope.Scope | R> +} = internal.toQueueOfElements + +/** + * Converts the stream to a `ReadableStream`. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. + * + * @since 2.0.0 + * @category destructors + */ +export const toReadableStream: { + /** + * Converts the stream to a `ReadableStream`. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. + * + * @since 2.0.0 + * @category destructors + */ + (options?: { readonly strategy?: QueuingStrategy | undefined }): ( + self: Stream + ) => ReadableStream + /** + * Converts the stream to a `ReadableStream`. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. + * + * @since 2.0.0 + * @category destructors + */ + ( + self: Stream, + options?: { readonly strategy?: QueuingStrategy | undefined } + ): ReadableStream +} = internal.toReadableStream + +/** + * Converts the stream to a `Effect`. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. + * + * @since 2.0.0 + * @category destructors + */ +export const toReadableStreamEffect: { + /** + * Converts the stream to a `Effect`. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. + * + * @since 2.0.0 + * @category destructors + */ + (options?: { readonly strategy?: QueuingStrategy | undefined }): ( + self: Stream + ) => Effect.Effect, never, R> + /** + * Converts the stream to a `Effect`. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. + * + * @since 2.0.0 + * @category destructors + */ + ( + self: Stream, + options?: { readonly strategy?: QueuingStrategy | undefined } + ): Effect.Effect, never, R> +} = internal.toReadableStreamEffect + +/** + * Converts the stream to a `ReadableStream` using the provided runtime. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. + * + * @since 2.0.0 + * @category destructors + */ +export const toReadableStreamRuntime: { + /** + * Converts the stream to a `ReadableStream` using the provided runtime. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. + * + * @since 2.0.0 + * @category destructors + */ + ( + runtime: Runtime, + options?: { readonly strategy?: QueuingStrategy | undefined } + ): (self: Stream) => ReadableStream + /** + * Converts the stream to a `ReadableStream` using the provided runtime. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. + * + * @since 2.0.0 + * @category destructors + */ + ( + self: Stream, + runtime: Runtime, + options?: { readonly strategy?: QueuingStrategy | undefined } + ): ReadableStream +} = internal.toReadableStreamRuntime + +/** + * Converts the stream to a `AsyncIterable` using the provided runtime. + * + * @since 3.15.0 + * @category destructors + */ +export const toAsyncIterableRuntime: { + /** + * Converts the stream to a `AsyncIterable` using the provided runtime. + * + * @since 3.15.0 + * @category destructors + */ + (runtime: Runtime): (self: Stream) => AsyncIterable + /** + * Converts the stream to a `AsyncIterable` using the provided runtime. + * + * @since 3.15.0 + * @category destructors + */ + (self: Stream, runtime: Runtime): AsyncIterable +} = internal.toAsyncIterableRuntime + +/** + * Converts the stream to a `AsyncIterable` capturing the required dependencies. + * + * @since 3.15.0 + * @category destructors + */ +export const toAsyncIterableEffect: (self: Stream) => Effect.Effect, never, R> = + internal.toAsyncIterableEffect + +/** + * Converts the stream to a `AsyncIterable`. + * + * @since 3.15.0 + * @category destructors + */ +export const toAsyncIterable: (self: Stream) => AsyncIterable = internal.toAsyncIterable + +/** + * Applies the transducer to the stream and emits its outputs. + * + * @since 2.0.0 + * @category utils + */ +export const transduce: { + /** + * Applies the transducer to the stream and emits its outputs. + * + * @since 2.0.0 + * @category utils + */ + (sink: Sink.Sink): (self: Stream) => Stream + /** + * Applies the transducer to the stream and emits its outputs. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, sink: Sink.Sink): Stream +} = internal.transduce + +/** + * Creates a stream by peeling off the "layers" of a value of type `S`. + * + * @example + * ```ts + * import { Effect, Option, Stream } from "effect" + * + * const stream = Stream.unfold(1, (n) => Option.some([n, n + 1])) + * + * Effect.runPromise(Stream.runCollect(stream.pipe(Stream.take(5)))).then(console.log) + * // { _id: 'Chunk', values: [ 1, 2, 3, 4, 5 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const unfold: (s: S, f: (s: S) => Option.Option) => Stream = internal.unfold + +/** + * Creates a stream by peeling off the "layers" of a value of type `S`. + * + * @since 2.0.0 + * @category constructors + */ +export const unfoldChunk: ( + s: S, + f: (s: S) => Option.Option, S]> +) => Stream = internal.unfoldChunk + +/** + * Creates a stream by effectfully peeling off the "layers" of a value of type + * `S`. + * + * @since 2.0.0 + * @category constructors + */ +export const unfoldChunkEffect: ( + s: S, + f: (s: S) => Effect.Effect, S]>, E, R> +) => Stream = internal.unfoldChunkEffect + +/** + * Creates a stream by effectfully peeling off the "layers" of a value of type + * `S`. + * + * @example + * ```ts + * import { Effect, Option, Random, Stream } from "effect" + * + * const stream = Stream.unfoldEffect(1, (n) => + * Random.nextBoolean.pipe( + * Effect.map((b) => (b ? Option.some([n, -n]) : Option.some([n, n]))) + * )) + * + * Effect.runPromise(Stream.runCollect(stream.pipe(Stream.take(5)))).then(console.log) + * // { _id: 'Chunk', values: [ 1, -1, -1, -1, -1 ] } + * ``` + * + * @since 2.0.0 + * @category constructors + */ +export const unfoldEffect: ( + s: S, + f: (s: S) => Effect.Effect, E, R> +) => Stream = internal.unfoldEffect + +const void_: Stream = internal.void +export { + /** + * A stream that contains a single `void` value. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.void + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ undefined ] } + * + * ``` + * @since 2.0.0 + * @category constructors + */ + void_ as void +} + +/** + * Creates a stream produced from an `Effect`. + * + * @since 2.0.0 + * @category constructors + */ +export const unwrap: (effect: Effect.Effect, E, R>) => Stream = + internal.unwrap + +/** + * Creates a stream produced from a scoped `Effect`. + * + * @since 2.0.0 + * @category constructors + */ +export const unwrapScoped: ( + effect: Effect.Effect, E, R> +) => Stream> = internal.unwrapScoped + +/** + * Creates a stream produced from a function which receives a `Scope` and + * returns an `Effect`. The resulting stream will emit a single element, which + * will be the result of the returned effect, if successful. + * + * @since 3.11.0 + * @category constructors + */ +export const unwrapScopedWith: ( + f: (scope: Scope.Scope) => Effect.Effect, E, R> +) => Stream = internal.unwrapScopedWith + +/** + * Updates the specified service within the context of the `Stream`. + * + * @since 2.0.0 + * @category context + */ +export const updateService: { + /** + * Updates the specified service within the context of the `Stream`. + * + * @since 2.0.0 + * @category context + */ + (tag: Context.Tag, f: (service: NoInfer) => NoInfer): (self: Stream) => Stream + /** + * Updates the specified service within the context of the `Stream`. + * + * @since 2.0.0 + * @category context + */ + ( + self: Stream, + tag: Context.Tag, + f: (service: NoInfer) => NoInfer + ): Stream +} = internal.updateService + +/** + * Returns the specified stream if the given condition is satisfied, otherwise + * returns an empty stream. + * + * @since 2.0.0 + * @category utils + */ +export const when: { + /** + * Returns the specified stream if the given condition is satisfied, otherwise + * returns an empty stream. + * + * @since 2.0.0 + * @category utils + */ + (test: LazyArg): (self: Stream) => Stream + /** + * Returns the specified stream if the given condition is satisfied, otherwise + * returns an empty stream. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, test: LazyArg): Stream +} = internal.when + +/** + * Returns the resulting stream when the given `PartialFunction` is defined + * for the given value, otherwise returns an empty stream. + * + * @since 2.0.0 + * @category constructors + */ +export const whenCase: ( + evaluate: LazyArg, + pf: (a: A) => Option.Option> +) => Stream = internal.whenCase + +/** + * Returns the stream when the given partial function is defined for the given + * effectful value, otherwise returns an empty stream. + * + * @since 2.0.0 + * @category utils + */ +export const whenCaseEffect: { + /** + * Returns the stream when the given partial function is defined for the given + * effectful value, otherwise returns an empty stream. + * + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option>): (self: Effect.Effect) => Stream + /** + * Returns the stream when the given partial function is defined for the given + * effectful value, otherwise returns an empty stream. + * + * @since 2.0.0 + * @category utils + */ + ( + self: Effect.Effect, + pf: (a: A) => Option.Option> + ): Stream +} = internal.whenCaseEffect + +/** + * Returns the stream if the given effectful condition is satisfied, otherwise + * returns an empty stream. + * + * @since 2.0.0 + * @category utils + */ +export const whenEffect: { + /** + * Returns the stream if the given effectful condition is satisfied, otherwise + * returns an empty stream. + * + * @since 2.0.0 + * @category utils + */ + (effect: Effect.Effect): (self: Stream) => Stream + /** + * Returns the stream if the given effectful condition is satisfied, otherwise + * returns an empty stream. + * + * @since 2.0.0 + * @category utils + */ + (self: Stream, effect: Effect.Effect): Stream +} = internal.whenEffect + +/** + * Wraps the stream with a new span for tracing. + * + * @since 2.0.0 + * @category tracing + */ +export const withSpan: { + /** + * Wraps the stream with a new span for tracing. + * + * @since 2.0.0 + * @category tracing + */ + (name: string, options?: Tracer.SpanOptions | undefined): (self: Stream) => Stream> + /** + * Wraps the stream with a new span for tracing. + * + * @since 2.0.0 + * @category tracing + */ + ( + self: Stream, + name: string, + options?: Tracer.SpanOptions | undefined + ): Stream> +} = internal.withSpan + +/** + * Zips this stream with another point-wise and emits tuples of elements from + * both streams. + * + * The new stream will end when one of the sides ends. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * // We create two streams and zip them together. + * const stream = Stream.zip( + * Stream.make(1, 2, 3, 4, 5, 6), + * Stream.make("a", "b", "c") + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ [ 1, 'a' ], [ 2, 'b' ], [ 3, 'c' ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ +export const zip: { + /** + * Zips this stream with another point-wise and emits tuples of elements from + * both streams. + * + * The new stream will end when one of the sides ends. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * // We create two streams and zip them together. + * const stream = Stream.zip( + * Stream.make(1, 2, 3, 4, 5, 6), + * Stream.make("a", "b", "c") + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ [ 1, 'a' ], [ 2, 'b' ], [ 3, 'c' ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ + (that: Stream): (self: Stream) => Stream<[A, A2], E2 | E, R2 | R> + /** + * Zips this stream with another point-wise and emits tuples of elements from + * both streams. + * + * The new stream will end when one of the sides ends. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * // We create two streams and zip them together. + * const stream = Stream.zip( + * Stream.make(1, 2, 3, 4, 5, 6), + * Stream.make("a", "b", "c") + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ [ 1, 'a' ], [ 2, 'b' ], [ 3, 'c' ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ + (self: Stream, that: Stream): Stream<[A, A2], E | E2, R | R2> +} = internal.zip + +/** + * Zips this stream with another point-wise and emits tuples of elements from + * both streams. + * + * The new stream will end when one of the sides ends. + * + * @since 2.0.0 + * @category zipping + */ +export const zipFlatten: { + /** + * Zips this stream with another point-wise and emits tuples of elements from + * both streams. + * + * The new stream will end when one of the sides ends. + * + * @since 2.0.0 + * @category zipping + */ + (that: Stream): , E, R>(self: Stream) => Stream<[...A, A2], E2 | E, R2 | R> + /** + * Zips this stream with another point-wise and emits tuples of elements from + * both streams. + * + * The new stream will end when one of the sides ends. + * + * @since 2.0.0 + * @category zipping + */ + , E, R, A2, E2, R2>(self: Stream, that: Stream): Stream<[...A, A2], E | E2, R | R2> +} = internal.zipFlatten + +/** + * Zips this stream with another point-wise, creating a new stream of pairs of + * elements from both sides. + * + * The defaults `defaultLeft` and `defaultRight` will be used if the streams + * have different lengths and one of the streams has ended before the other. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.zipAll(Stream.make(1, 2, 3, 4, 5, 6), { + * other: Stream.make("a", "b", "c"), + * defaultSelf: 0, + * defaultOther: "x" + * }) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: "Chunk", values: [ [ 1, "a" ], [ 2, "b" ], [ 3, "c" ], [ 4, "x" ], [ 5, "x" ], [ 6, "x" ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ +export const zipAll: { + /** + * Zips this stream with another point-wise, creating a new stream of pairs of + * elements from both sides. + * + * The defaults `defaultLeft` and `defaultRight` will be used if the streams + * have different lengths and one of the streams has ended before the other. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.zipAll(Stream.make(1, 2, 3, 4, 5, 6), { + * other: Stream.make("a", "b", "c"), + * defaultSelf: 0, + * defaultOther: "x" + * }) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: "Chunk", values: [ [ 1, "a" ], [ 2, "b" ], [ 3, "c" ], [ 4, "x" ], [ 5, "x" ], [ 6, "x" ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ + ( + options: { readonly other: Stream; readonly defaultSelf: A; readonly defaultOther: A2 } + ): (self: Stream) => Stream<[A, A2], E2 | E, R2 | R> + /** + * Zips this stream with another point-wise, creating a new stream of pairs of + * elements from both sides. + * + * The defaults `defaultLeft` and `defaultRight` will be used if the streams + * have different lengths and one of the streams has ended before the other. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.zipAll(Stream.make(1, 2, 3, 4, 5, 6), { + * other: Stream.make("a", "b", "c"), + * defaultSelf: 0, + * defaultOther: "x" + * }) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: "Chunk", values: [ [ 1, "a" ], [ 2, "b" ], [ 3, "c" ], [ 4, "x" ], [ 5, "x" ], [ 6, "x" ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Stream, + options: { readonly other: Stream; readonly defaultSelf: A; readonly defaultOther: A2 } + ): Stream<[A, A2], E | E2, R | R2> +} = internal.zipAll + +/** + * Zips this stream with another point-wise, and keeps only elements from this + * stream. + * + * The provided default value will be used if the other stream ends before + * this one. + * + * @since 2.0.0 + * @category zipping + */ +export const zipAllLeft: { + /** + * Zips this stream with another point-wise, and keeps only elements from this + * stream. + * + * The provided default value will be used if the other stream ends before + * this one. + * + * @since 2.0.0 + * @category zipping + */ + (that: Stream, defaultLeft: A): (self: Stream) => Stream + /** + * Zips this stream with another point-wise, and keeps only elements from this + * stream. + * + * The provided default value will be used if the other stream ends before + * this one. + * + * @since 2.0.0 + * @category zipping + */ + (self: Stream, that: Stream, defaultLeft: A): Stream +} = internal.zipAllLeft + +/** + * Zips this stream with another point-wise, and keeps only elements from the + * other stream. + * + * The provided default value will be used if this stream ends before the + * other one. + * + * @since 2.0.0 + * @category zipping + */ +export const zipAllRight: { + /** + * Zips this stream with another point-wise, and keeps only elements from the + * other stream. + * + * The provided default value will be used if this stream ends before the + * other one. + * + * @since 2.0.0 + * @category zipping + */ + (that: Stream, defaultRight: A2): (self: Stream) => Stream + /** + * Zips this stream with another point-wise, and keeps only elements from the + * other stream. + * + * The provided default value will be used if this stream ends before the + * other one. + * + * @since 2.0.0 + * @category zipping + */ + (self: Stream, that: Stream, defaultRight: A2): Stream +} = internal.zipAllRight + +/** + * Zips this stream that is sorted by distinct keys and the specified stream + * that is sorted by distinct keys to produce a new stream that is sorted by + * distinct keys. Combines values associated with each key into a tuple, + * using the specified values `defaultLeft` and `defaultRight` to fill in + * missing values. + * + * This allows zipping potentially unbounded streams of data by key in + * constant space but the caller is responsible for ensuring that the + * streams are sorted by distinct keys. + * + * @since 2.0.0 + * @category zipping + */ +export const zipAllSortedByKey: { + /** + * Zips this stream that is sorted by distinct keys and the specified stream + * that is sorted by distinct keys to produce a new stream that is sorted by + * distinct keys. Combines values associated with each key into a tuple, + * using the specified values `defaultLeft` and `defaultRight` to fill in + * missing values. + * + * This allows zipping potentially unbounded streams of data by key in + * constant space but the caller is responsible for ensuring that the + * streams are sorted by distinct keys. + * + * @since 2.0.0 + * @category zipping + */ + ( + options: { + readonly other: Stream + readonly defaultSelf: A + readonly defaultOther: A2 + readonly order: Order.Order + } + ): (self: Stream) => Stream<[K, [A, A2]], E2 | E, R2 | R> + /** + * Zips this stream that is sorted by distinct keys and the specified stream + * that is sorted by distinct keys to produce a new stream that is sorted by + * distinct keys. Combines values associated with each key into a tuple, + * using the specified values `defaultLeft` and `defaultRight` to fill in + * missing values. + * + * This allows zipping potentially unbounded streams of data by key in + * constant space but the caller is responsible for ensuring that the + * streams are sorted by distinct keys. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Stream, + options: { + readonly other: Stream + readonly defaultSelf: A + readonly defaultOther: A2 + readonly order: Order.Order + } + ): Stream<[K, [A, A2]], E | E2, R | R2> +} = internal.zipAllSortedByKey + +/** + * Zips this stream that is sorted by distinct keys and the specified stream + * that is sorted by distinct keys to produce a new stream that is sorted by + * distinct keys. Keeps only values from this stream, using the specified + * value `default` to fill in missing values. + * + * This allows zipping potentially unbounded streams of data by key in + * constant space but the caller is responsible for ensuring that the + * streams are sorted by distinct keys. + * + * @since 2.0.0 + * @category zipping + */ +export const zipAllSortedByKeyLeft: { + /** + * Zips this stream that is sorted by distinct keys and the specified stream + * that is sorted by distinct keys to produce a new stream that is sorted by + * distinct keys. Keeps only values from this stream, using the specified + * value `default` to fill in missing values. + * + * This allows zipping potentially unbounded streams of data by key in + * constant space but the caller is responsible for ensuring that the + * streams are sorted by distinct keys. + * + * @since 2.0.0 + * @category zipping + */ + ( + options: { + readonly other: Stream + readonly defaultSelf: A + readonly order: Order.Order + } + ): (self: Stream) => Stream<[K, A], E2 | E, R2 | R> + /** + * Zips this stream that is sorted by distinct keys and the specified stream + * that is sorted by distinct keys to produce a new stream that is sorted by + * distinct keys. Keeps only values from this stream, using the specified + * value `default` to fill in missing values. + * + * This allows zipping potentially unbounded streams of data by key in + * constant space but the caller is responsible for ensuring that the + * streams are sorted by distinct keys. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Stream, + options: { + readonly other: Stream + readonly defaultSelf: A + readonly order: Order.Order + } + ): Stream<[K, A], E | E2, R | R2> +} = internal.zipAllSortedByKeyLeft + +/** + * Zips this stream that is sorted by distinct keys and the specified stream + * that is sorted by distinct keys to produce a new stream that is sorted by + * distinct keys. Keeps only values from that stream, using the specified + * value `default` to fill in missing values. + * + * This allows zipping potentially unbounded streams of data by key in + * constant space but the caller is responsible for ensuring that the + * streams are sorted by distinct keys. + * + * @since 2.0.0 + * @category zipping + */ +export const zipAllSortedByKeyRight: { + /** + * Zips this stream that is sorted by distinct keys and the specified stream + * that is sorted by distinct keys to produce a new stream that is sorted by + * distinct keys. Keeps only values from that stream, using the specified + * value `default` to fill in missing values. + * + * This allows zipping potentially unbounded streams of data by key in + * constant space but the caller is responsible for ensuring that the + * streams are sorted by distinct keys. + * + * @since 2.0.0 + * @category zipping + */ + ( + options: { + readonly other: Stream + readonly defaultOther: A2 + readonly order: Order.Order + } + ): (self: Stream) => Stream<[K, A2], E2 | E, R2 | R> + /** + * Zips this stream that is sorted by distinct keys and the specified stream + * that is sorted by distinct keys to produce a new stream that is sorted by + * distinct keys. Keeps only values from that stream, using the specified + * value `default` to fill in missing values. + * + * This allows zipping potentially unbounded streams of data by key in + * constant space but the caller is responsible for ensuring that the + * streams are sorted by distinct keys. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Stream, + options: { + readonly other: Stream + readonly defaultOther: A2 + readonly order: Order.Order + } + ): Stream<[K, A2], E | E2, R | R2> +} = internal.zipAllSortedByKeyRight + +/** + * Zips this stream that is sorted by distinct keys and the specified stream + * that is sorted by distinct keys to produce a new stream that is sorted by + * distinct keys. Uses the functions `left`, `right`, and `both` to handle + * the cases where a key and value exist in this stream, that stream, or + * both streams. + * + * This allows zipping potentially unbounded streams of data by key in + * constant space but the caller is responsible for ensuring that the + * streams are sorted by distinct keys. + * + * @since 2.0.0 + * @category zipping + */ +export const zipAllSortedByKeyWith: { + /** + * Zips this stream that is sorted by distinct keys and the specified stream + * that is sorted by distinct keys to produce a new stream that is sorted by + * distinct keys. Uses the functions `left`, `right`, and `both` to handle + * the cases where a key and value exist in this stream, that stream, or + * both streams. + * + * This allows zipping potentially unbounded streams of data by key in + * constant space but the caller is responsible for ensuring that the + * streams are sorted by distinct keys. + * + * @since 2.0.0 + * @category zipping + */ + ( + options: { + readonly other: Stream + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A3 + readonly onBoth: (a: A, a2: A2) => A3 + readonly order: Order.Order + } + ): (self: Stream) => Stream<[K, A3], E2 | E, R2 | R> + /** + * Zips this stream that is sorted by distinct keys and the specified stream + * that is sorted by distinct keys to produce a new stream that is sorted by + * distinct keys. Uses the functions `left`, `right`, and `both` to handle + * the cases where a key and value exist in this stream, that stream, or + * both streams. + * + * This allows zipping potentially unbounded streams of data by key in + * constant space but the caller is responsible for ensuring that the + * streams are sorted by distinct keys. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Stream, + options: { + readonly other: Stream + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A3 + readonly onBoth: (a: A, a2: A2) => A3 + readonly order: Order.Order + } + ): Stream<[K, A3], E | E2, R | R2> +} = internal.zipAllSortedByKeyWith + +/** + * Zips this stream with another point-wise. The provided functions will be + * used to create elements for the composed stream. + * + * The functions `left` and `right` will be used if the streams have different + * lengths and one of the streams has ended before the other. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.zipAllWith(Stream.make(1, 2, 3, 4, 5, 6), { + * other: Stream.make("a", "b", "c"), + * onSelf: (n) => [n, "x"], + * onOther: (s) => [0, s], + * onBoth: (n, s) => [n - s.length, s] + * }) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: "Chunk", values: [ [ 0, "a" ], [ 1, "b" ], [ 2, "c" ], [ 4, "x" ], [ 5, "x" ], [ 6, "x" ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ +export const zipAllWith: { + /** + * Zips this stream with another point-wise. The provided functions will be + * used to create elements for the composed stream. + * + * The functions `left` and `right` will be used if the streams have different + * lengths and one of the streams has ended before the other. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.zipAllWith(Stream.make(1, 2, 3, 4, 5, 6), { + * other: Stream.make("a", "b", "c"), + * onSelf: (n) => [n, "x"], + * onOther: (s) => [0, s], + * onBoth: (n, s) => [n - s.length, s] + * }) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: "Chunk", values: [ [ 0, "a" ], [ 1, "b" ], [ 2, "c" ], [ 4, "x" ], [ 5, "x" ], [ 6, "x" ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ + ( + options: { + readonly other: Stream + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A3 + readonly onBoth: (a: A, a2: A2) => A3 + } + ): (self: Stream) => Stream + /** + * Zips this stream with another point-wise. The provided functions will be + * used to create elements for the composed stream. + * + * The functions `left` and `right` will be used if the streams have different + * lengths and one of the streams has ended before the other. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.zipAllWith(Stream.make(1, 2, 3, 4, 5, 6), { + * other: Stream.make("a", "b", "c"), + * onSelf: (n) => [n, "x"], + * onOther: (s) => [0, s], + * onBoth: (n, s) => [n - s.length, s] + * }) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: "Chunk", values: [ [ 0, "a" ], [ 1, "b" ], [ 2, "c" ], [ 4, "x" ], [ 5, "x" ], [ 6, "x" ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Stream, + options: { + readonly other: Stream + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A3 + readonly onBoth: (a: A, a2: A2) => A3 + } + ): Stream +} = internal.zipAllWith + +/** + * Zips the two streams so that when a value is emitted by either of the two + * streams, it is combined with the latest value from the other stream to + * produce a result. + * + * Note: tracking the latest value is done on a per-chunk basis. That means + * that emitted elements that are not the last value in chunks will never be + * used for zipping. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3).pipe( + * Stream.schedule(Schedule.spaced("1 second")) + * ) + * + * const s2 = Stream.make("a", "b", "c", "d").pipe( + * Stream.schedule(Schedule.spaced("500 millis")) + * ) + * + * const stream = Stream.zipLatest(s1, s2) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: "Chunk", values: [ [ 1, "a" ], [ 1, "b" ], [ 2, "b" ], [ 2, "c" ], [ 2, "d" ], [ 3, "d" ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ +export const zipLatest: { + /** + * Zips the two streams so that when a value is emitted by either of the two + * streams, it is combined with the latest value from the other stream to + * produce a result. + * + * Note: tracking the latest value is done on a per-chunk basis. That means + * that emitted elements that are not the last value in chunks will never be + * used for zipping. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3).pipe( + * Stream.schedule(Schedule.spaced("1 second")) + * ) + * + * const s2 = Stream.make("a", "b", "c", "d").pipe( + * Stream.schedule(Schedule.spaced("500 millis")) + * ) + * + * const stream = Stream.zipLatest(s1, s2) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: "Chunk", values: [ [ 1, "a" ], [ 1, "b" ], [ 2, "b" ], [ 2, "c" ], [ 2, "d" ], [ 3, "d" ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ + (right: Stream): (left: Stream) => Stream<[AL, AR], EL | ER, RL | RR> + /** + * Zips the two streams so that when a value is emitted by either of the two + * streams, it is combined with the latest value from the other stream to + * produce a result. + * + * Note: tracking the latest value is done on a per-chunk basis. That means + * that emitted elements that are not the last value in chunks will never be + * used for zipping. + * + * @example + * ```ts + * import { Effect, Schedule, Stream } from "effect" + * + * const s1 = Stream.make(1, 2, 3).pipe( + * Stream.schedule(Schedule.spaced("1 second")) + * ) + * + * const s2 = Stream.make("a", "b", "c", "d").pipe( + * Stream.schedule(Schedule.spaced("500 millis")) + * ) + * + * const stream = Stream.zipLatest(s1, s2) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: "Chunk", values: [ [ 1, "a" ], [ 1, "b" ], [ 2, "b" ], [ 2, "c" ], [ 2, "d" ], [ 3, "d" ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ + (left: Stream, right: Stream): Stream<[AL, AR], EL | ER, RL | RR> +} = internal.zipLatest + +/** + * Zips multiple streams so that when a value is emitted by any of the streams, + * it is combined with the latest values from the other streams to produce a result. + * + * Note: tracking the latest value is done on a per-chunk basis. That means + * that emitted elements that are not the last value in chunks will never be + * used for zipping. + * + * @example + * ```ts + * import { Stream, Schedule, Console, Effect } from "effect" + * + * const stream = Stream.zipLatestAll( + * Stream.fromSchedule(Schedule.spaced('1 millis')), + * Stream.fromSchedule(Schedule.spaced('2 millis')), + * Stream.fromSchedule(Schedule.spaced('4 millis')), + * ).pipe(Stream.take(6), Stream.tap(Console.log)) + * + * Effect.runPromise(Stream.runDrain(stream)) + * // Output: + * // [ 0, 0, 0 ] + * // [ 1, 0, 0 ] + * // [ 1, 1, 0 ] + * // [ 2, 1, 0 ] + * // [ 3, 1, 0 ] + * // [ 3, 1, 1 ] + * // ..... + * ``` + * + * @since 3.3.0 + * @category zipping + */ +export const zipLatestAll: >>( + ...streams: T +) => Stream< + [T[number]] extends [never] ? never + : { [K in keyof T]: T[K] extends Stream ? A : never }, + [T[number]] extends [never] ? never : T[number] extends Stream ? _E : never, + [T[number]] extends [never] ? never : T[number] extends Stream ? _R : never +> = internal.zipLatestAll + +/** + * Zips the two streams so that when a value is emitted by either of the two + * streams, it is combined with the latest value from the other stream to + * produce a result. + * + * Note: tracking the latest value is done on a per-chunk basis. That means + * that emitted elements that are not the last value in chunks will never be + * used for zipping. + * + * @since 2.0.0 + * @category zipping + */ +export const zipLatestWith: { + /** + * Zips the two streams so that when a value is emitted by either of the two + * streams, it is combined with the latest value from the other stream to + * produce a result. + * + * Note: tracking the latest value is done on a per-chunk basis. That means + * that emitted elements that are not the last value in chunks will never be + * used for zipping. + * + * @since 2.0.0 + * @category zipping + */ + (right: Stream, f: (left: AL, right: AR) => A): (left: Stream) => Stream + /** + * Zips the two streams so that when a value is emitted by either of the two + * streams, it is combined with the latest value from the other stream to + * produce a result. + * + * Note: tracking the latest value is done on a per-chunk basis. That means + * that emitted elements that are not the last value in chunks will never be + * used for zipping. + * + * @since 2.0.0 + * @category zipping + */ + ( + left: Stream, + right: Stream, + f: (left: AL, right: AR) => A + ): Stream +} = internal.zipLatestWith + +/** + * Zips this stream with another point-wise, but keeps only the outputs of + * `left` stream. + * + * The new stream will end when one of the sides ends. + * + * @since 2.0.0 + * @category zipping + */ +export const zipLeft: { + /** + * Zips this stream with another point-wise, but keeps only the outputs of + * `left` stream. + * + * The new stream will end when one of the sides ends. + * + * @since 2.0.0 + * @category zipping + */ + (right: Stream): (left: Stream) => Stream + /** + * Zips this stream with another point-wise, but keeps only the outputs of + * `left` stream. + * + * The new stream will end when one of the sides ends. + * + * @since 2.0.0 + * @category zipping + */ + (left: Stream, right: Stream): Stream +} = internal.zipLeft + +/** + * Zips this stream with another point-wise, but keeps only the outputs of the + * `right` stream. + * + * The new stream will end when one of the sides ends. + * + * @since 2.0.0 + * @category zipping + */ +export const zipRight: { + /** + * Zips this stream with another point-wise, but keeps only the outputs of the + * `right` stream. + * + * The new stream will end when one of the sides ends. + * + * @since 2.0.0 + * @category zipping + */ + (right: Stream): (left: Stream) => Stream + /** + * Zips this stream with another point-wise, but keeps only the outputs of the + * `right` stream. + * + * The new stream will end when one of the sides ends. + * + * @since 2.0.0 + * @category zipping + */ + (left: Stream, right: Stream): Stream +} = internal.zipRight + +/** + * Zips this stream with another point-wise and applies the function to the + * paired elements. + * + * The new stream will end when one of the sides ends. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * // We create two streams and zip them with custom logic. + * const stream = Stream.zipWith( + * Stream.make(1, 2, 3, 4, 5, 6), + * Stream.make("a", "b", "c"), + * (n, s) => [n - s.length, s] + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ [ 0, 'a' ], [ 1, 'b' ], [ 2, 'c' ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ +export const zipWith: { + /** + * Zips this stream with another point-wise and applies the function to the + * paired elements. + * + * The new stream will end when one of the sides ends. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * // We create two streams and zip them with custom logic. + * const stream = Stream.zipWith( + * Stream.make(1, 2, 3, 4, 5, 6), + * Stream.make("a", "b", "c"), + * (n, s) => [n - s.length, s] + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ [ 0, 'a' ], [ 1, 'b' ], [ 2, 'c' ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ + (right: Stream, f: (left: AL, right: AR) => A): (left: Stream) => Stream + /** + * Zips this stream with another point-wise and applies the function to the + * paired elements. + * + * The new stream will end when one of the sides ends. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * // We create two streams and zip them with custom logic. + * const stream = Stream.zipWith( + * Stream.make(1, 2, 3, 4, 5, 6), + * Stream.make("a", "b", "c"), + * (n, s) => [n - s.length, s] + * ) + * + * Effect.runPromise(Stream.runCollect(stream)).then(console.log) + * // { _id: 'Chunk', values: [ [ 0, 'a' ], [ 1, 'b' ], [ 2, 'c' ] ] } + * ``` + * + * @since 2.0.0 + * @category zipping + */ + ( + left: Stream, + right: Stream, + f: (left: AL, right: AR) => A + ): Stream +} = internal.zipWith + +/** + * Zips this stream with another point-wise and applies the function to the + * paired elements. + * + * The new stream will end when one of the sides ends. + * + * @since 2.0.0 + * @category zipping + */ +export const zipWithChunks: { + /** + * Zips this stream with another point-wise and applies the function to the + * paired elements. + * + * The new stream will end when one of the sides ends. + * + * @since 2.0.0 + * @category zipping + */ + ( + that: Stream, + f: ( + left: Chunk.Chunk, + right: Chunk.Chunk + ) => readonly [Chunk.Chunk, Either.Either, Chunk.Chunk>] + ): (self: Stream) => Stream + /** + * Zips this stream with another point-wise and applies the function to the + * paired elements. + * + * The new stream will end when one of the sides ends. + * + * @since 2.0.0 + * @category zipping + */ + ( + self: Stream, + that: Stream, + f: ( + left: Chunk.Chunk, + right: Chunk.Chunk + ) => readonly [Chunk.Chunk, Either.Either, Chunk.Chunk>] + ): Stream +} = internal.zipWithChunks + +/** + * Zips each element with the next element if present. + * + * @example + * ```ts + * import { Chunk, Effect, Stream } from "effect" + * + * const stream = Stream.zipWithNext(Stream.make(1, 2, 3, 4)) + * + * Effect.runPromise(Stream.runCollect(stream)).then((chunk) => console.log(Chunk.toArray(chunk))) + * // [ + * // [ 1, { _id: 'Option', _tag: 'Some', value: 2 } ], + * // [ 2, { _id: 'Option', _tag: 'Some', value: 3 } ], + * // [ 3, { _id: 'Option', _tag: 'Some', value: 4 } ], + * // [ 4, { _id: 'Option', _tag: 'None' } ] + * // ] + * ``` + * + * @since 2.0.0 + * @category zipping + */ +export const zipWithNext: (self: Stream) => Stream<[A, Option.Option], E, R> = internal.zipWithNext + +/** + * Zips each element with the previous element. Initially accompanied by + * `None`. + * + * @example + * ```ts + * import { Chunk, Effect, Stream } from "effect" + * + * const stream = Stream.zipWithPrevious(Stream.make(1, 2, 3, 4)) + * + * Effect.runPromise(Stream.runCollect(stream)).then((chunk) => console.log(Chunk.toArray(chunk))) + * // [ + * // [ { _id: 'Option', _tag: 'None' }, 1 ], + * // [ { _id: 'Option', _tag: 'Some', value: 1 }, 2 ], + * // [ { _id: 'Option', _tag: 'Some', value: 2 }, 3 ], + * // [ { _id: 'Option', _tag: 'Some', value: 3 }, 4 ] + * // ] + * ``` + * + * @since 2.0.0 + * @category zipping + */ +export const zipWithPrevious: (self: Stream) => Stream<[Option.Option, A], E, R> = + internal.zipWithPrevious + +/** + * Zips each element with both the previous and next element. + * + * @example + * ```ts + * import { Chunk, Effect, Stream } from "effect" + * + * const stream = Stream.zipWithPreviousAndNext(Stream.make(1, 2, 3, 4)) + * + * Effect.runPromise(Stream.runCollect(stream)).then((chunk) => console.log(Chunk.toArray(chunk))) + * // [ + * // [ + * // { _id: 'Option', _tag: 'None' }, + * // 1, + * // { _id: 'Option', _tag: 'Some', value: 2 } + * // ], + * // [ + * // { _id: 'Option', _tag: 'Some', value: 1 }, + * // 2, + * // { _id: 'Option', _tag: 'Some', value: 3 } + * // ], + * // [ + * // { _id: 'Option', _tag: 'Some', value: 2 }, + * // 3, + * // { _id: 'Option', _tag: 'Some', value: 4 } + * // ], + * // [ + * // { _id: 'Option', _tag: 'Some', value: 3 }, + * // 4, + * // { _id: 'Option', _tag: 'None' } + * // ] + * // ] + * ``` + * + * @since 2.0.0 + * @category zipping + */ +export const zipWithPreviousAndNext: ( + self: Stream +) => Stream<[Option.Option, A, Option.Option], E, R> = internal.zipWithPreviousAndNext + +/** + * Zips this stream together with the index of elements. + * + * @example + * ```ts + * import { Effect, Stream } from "effect" + * + * const stream = Stream.make("Mary", "James", "Robert", "Patricia") + * + * const indexedStream = Stream.zipWithIndex(stream) + * + * Effect.runPromise(Stream.runCollect(indexedStream)).then(console.log) + * // { + * // _id: 'Chunk', + * // values: [ [ 'Mary', 0 ], [ 'James', 1 ], [ 'Robert', 2 ], [ 'Patricia', 3 ] ] + * // } + * ``` + * + * @since 2.0.0 + * @category zipping + */ +export const zipWithIndex: (self: Stream) => Stream<[A, number], E, R> = internal.zipWithIndex + +// ------------------------------------------------------------------------------------- +// do notation +// ------------------------------------------------------------------------------------- + +/** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Stream` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Chunk, Effect, pipe, Stream } from "effect" + * + * const result = pipe( + * Stream.Do, + * Stream.bind("x", () => Stream.succeed(2)), + * Stream.bind("y", () => Stream.succeed(3)), + * Stream.let("sum", ({ x, y }) => x + y) + * ) + * assert.deepStrictEqual(Effect.runSync(Stream.runCollect(result)), Chunk.of({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link bindTo} + * @see {@link bind} + * @see {@link bindEffect} + * @see {@link let_ let} + * + * @category do notation + * @since 2.0.0 + */ +export const Do: Stream<{}> = internal.Do + +/** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Stream` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Chunk, Effect, pipe, Stream } from "effect" + * + * const result = pipe( + * Stream.Do, + * Stream.bind("x", () => Stream.succeed(2)), + * Stream.bind("y", () => Stream.succeed(3)), + * Stream.let("sum", ({ x, y }) => x + y) + * ) + * assert.deepStrictEqual(Effect.runSync(Stream.runCollect(result)), Chunk.of({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bindTo} + * @see {@link bindEffect} + * @see {@link let_ let} + * + * @category do notation + * @since 2.0.0 + */ +export const bind: { + /** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Stream` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Chunk, Effect, pipe, Stream } from "effect" + * + * const result = pipe( + * Stream.Do, + * Stream.bind("x", () => Stream.succeed(2)), + * Stream.bind("y", () => Stream.succeed(3)), + * Stream.let("sum", ({ x, y }) => x + y) + * ) + * assert.deepStrictEqual(Effect.runSync(Stream.runCollect(result)), Chunk.of({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bindTo} + * @see {@link bindEffect} + * @see {@link let_ let} + * + * @category do notation + * @since 2.0.0 + */ + ( + tag: Exclude, + f: (_: NoInfer) => Stream, + options?: + | { readonly concurrency?: number | "unbounded" | undefined; readonly bufferSize?: number | undefined } + | undefined + ): (self: Stream) => Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E2 | E, R2 | R> + /** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Stream` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Chunk, Effect, pipe, Stream } from "effect" + * + * const result = pipe( + * Stream.Do, + * Stream.bind("x", () => Stream.succeed(2)), + * Stream.bind("y", () => Stream.succeed(3)), + * Stream.let("sum", ({ x, y }) => x + y) + * ) + * assert.deepStrictEqual(Effect.runSync(Stream.runCollect(result)), Chunk.of({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bindTo} + * @see {@link bindEffect} + * @see {@link let_ let} + * + * @category do notation + * @since 2.0.0 + */ + ( + self: Stream, + tag: Exclude, + f: (_: NoInfer) => Stream, + options?: + | { readonly concurrency?: number | "unbounded" | undefined; readonly bufferSize?: number | undefined } + | undefined + ): Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E | E2, R | R2> +} = internal.bind + +/** + * Binds an effectful value in a `do` scope + * + * @see {@link Do} + * @see {@link bindTo} + * @see {@link bind} + * @see {@link let_ let} + * + * @since 2.0.0 + * @category do notation + */ +export const bindEffect: { + /** + * Binds an effectful value in a `do` scope + * + * @see {@link Do} + * @see {@link bindTo} + * @see {@link bind} + * @see {@link let_ let} + * + * @since 2.0.0 + * @category do notation + */ + ( + tag: Exclude, + f: (_: NoInfer) => Effect.Effect, + options?: { readonly concurrency?: number | "unbounded" | undefined; readonly bufferSize?: number | undefined } + ): (self: Stream) => Stream<{ [K in keyof A | N]: K extends keyof A ? A[K] : B }, E | E2, R | R2> + /** + * Binds an effectful value in a `do` scope + * + * @see {@link Do} + * @see {@link bindTo} + * @see {@link bind} + * @see {@link let_ let} + * + * @since 2.0.0 + * @category do notation + */ + ( + self: Stream, + tag: Exclude, + f: (_: NoInfer) => Effect.Effect, + options?: { readonly concurrency?: number | "unbounded" | undefined; readonly unordered?: boolean | undefined } + ): Stream<{ [K in keyof A | N]: K extends keyof A ? A[K] : B }, E | E2, R | R2> +} = groupBy_.bindEffect + +/** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Stream` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Chunk, Effect, pipe, Stream } from "effect" + * + * const result = pipe( + * Stream.Do, + * Stream.bind("x", () => Stream.succeed(2)), + * Stream.bind("y", () => Stream.succeed(3)), + * Stream.let("sum", ({ x, y }) => x + y) + * ) + * assert.deepStrictEqual(Effect.runSync(Stream.runCollect(result)), Chunk.of({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bind} + * @see {@link bindEffect} + * @see {@link let_ let} + * + * @category do notation + * @since 2.0.0 + */ +export const bindTo: { + /** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Stream` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Chunk, Effect, pipe, Stream } from "effect" + * + * const result = pipe( + * Stream.Do, + * Stream.bind("x", () => Stream.succeed(2)), + * Stream.bind("y", () => Stream.succeed(3)), + * Stream.let("sum", ({ x, y }) => x + y) + * ) + * assert.deepStrictEqual(Effect.runSync(Stream.runCollect(result)), Chunk.of({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bind} + * @see {@link bindEffect} + * @see {@link let_ let} + * + * @category do notation + * @since 2.0.0 + */ + (name: N): (self: Stream) => Stream<{ [K in N]: A }, E, R> + /** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Stream` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Chunk, Effect, pipe, Stream } from "effect" + * + * const result = pipe( + * Stream.Do, + * Stream.bind("x", () => Stream.succeed(2)), + * Stream.bind("y", () => Stream.succeed(3)), + * Stream.let("sum", ({ x, y }) => x + y) + * ) + * assert.deepStrictEqual(Effect.runSync(Stream.runCollect(result)), Chunk.of({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bind} + * @see {@link bindEffect} + * @see {@link let_ let} + * + * @category do notation + * @since 2.0.0 + */ + (self: Stream, name: N): Stream<{ [K in N]: A }, E, R> +} = internal.bindTo + +const let_: { + ( + name: Exclude, + f: (a: NoInfer) => B + ): (self: Stream) => Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E, R> + ( + self: Stream, + name: Exclude, + f: (a: NoInfer) => B + ): Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E, R> +} = internal.let_ + +export { + /** + * The "do simulation" in Effect allows you to write code in a more declarative style, similar to the "do notation" in other programming languages. It provides a way to define variables and perform operations on them using functions like `bind` and `let`. + * + * Here's how the do simulation works: + * + * 1. Start the do simulation using the `Do` value + * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Stream` values + * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope + * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Chunk, Effect, pipe, Stream } from "effect" + * + * const result = pipe( + * Stream.Do, + * Stream.bind("x", () => Stream.succeed(2)), + * Stream.bind("y", () => Stream.succeed(3)), + * Stream.let("sum", ({ x, y }) => x + y) + * ) + * assert.deepStrictEqual(Effect.runSync(Stream.runCollect(result)), Chunk.of({ x: 2, y: 3, sum: 5 })) + * ``` + * + * @see {@link Do} + * @see {@link bindTo} + * @see {@link bind} + * @see {@link bindEffect} + * + * @category do notation + * @since 2.0.0 + */ + let_ as let +} + +// ------------------------------------------------------------------------------------- +// encoding +// ------------------------------------------------------------------------------------- + +/** + * Decode Uint8Array chunks into a stream of strings using the specified encoding. + * + * @since 2.0.0 + * @category encoding + */ +export const decodeText: { + // ------------------------------------------------------------------------------------- + // encoding + // ------------------------------------------------------------------------------------- + + /** + * Decode Uint8Array chunks into a stream of strings using the specified encoding. + * + * @since 2.0.0 + * @category encoding + */ + (encoding?: string | undefined): (self: Stream) => Stream + // ------------------------------------------------------------------------------------- + // encoding + // ------------------------------------------------------------------------------------- + + /** + * Decode Uint8Array chunks into a stream of strings using the specified encoding. + * + * @since 2.0.0 + * @category encoding + */ + (self: Stream, encoding?: string | undefined): Stream +} = internal.decodeText + +/** + * Encode a stream of strings into a stream of Uint8Array chunks using the specified encoding. + * + * @since 2.0.0 + * @category encoding + */ +export const encodeText: (self: Stream) => Stream = internal.encodeText + +/** + * @since 3.4.0 + * @category models + */ +export interface EventListener { + addEventListener( + event: string, + f: (event: A) => void, + options?: { + readonly capture?: boolean + readonly passive?: boolean + readonly once?: boolean + readonly signal?: AbortSignal + } | boolean + ): void + removeEventListener( + event: string, + f: (event: A) => void, + options?: { + readonly capture?: boolean + } | boolean + ): void +} + +/** + * Creates a `Stream` using addEventListener. + * @since 3.1.0 + */ +export const fromEventListener: ( + target: EventListener, + type: string, + options?: boolean | { + readonly capture?: boolean + readonly passive?: boolean + readonly once?: boolean + readonly bufferSize?: number | "unbounded" | undefined + } | undefined +) => Stream = internal.fromEventListener diff --git a/backend/node_modules/effect/src/Streamable.ts b/backend/node_modules/effect/src/Streamable.ts new file mode 100644 index 0000000000000000000000000000000000000000..059952c23ff039baa5f51a086eef717cb27864e9 --- /dev/null +++ b/backend/node_modules/effect/src/Streamable.ts @@ -0,0 +1,45 @@ +/** + * @since 2.0.0 + */ + +import { pipeArguments } from "./Pipeable.js" +import * as Stream from "./Stream.js" + +const streamVariance = { + /* c8 ignore next */ + _R: (_: never) => _, + /* c8 ignore next */ + _E: (_: never) => _, + /* c8 ignore next */ + _A: (_: never) => _ +} + +/** + * @since 2.0.0 + * @category constructors + */ +export abstract class Class implements Stream.Stream { + /** + * @since 2.0.0 + */ + readonly [Stream.StreamTypeId] = streamVariance + + /** + * @since 2.0.0 + */ + pipe() { + return pipeArguments(this, arguments) + } + + /** + * @since 2.0.0 + */ + abstract toStream(): Stream.Stream + + /** + * @internal + */ + get channel() { + return Stream.toChannel(this.toStream()) + } +} diff --git a/backend/node_modules/effect/src/Subscribable.ts b/backend/node_modules/effect/src/Subscribable.ts new file mode 100644 index 0000000000000000000000000000000000000000..050264ad40abcf7a8cfb3421ed082c709d387f3c --- /dev/null +++ b/backend/node_modules/effect/src/Subscribable.ts @@ -0,0 +1,114 @@ +/** + * @since 2.0.0 + */ +import * as Effect from "./Effect.js" +import { dual } from "./Function.js" +import { pipeArguments } from "./Pipeable.js" +import { hasProperty } from "./Predicate.js" +import * as Readable from "./Readable.js" +import * as Stream from "./Stream.js" +import type { NoInfer } from "./Types.js" + +/** + * @since 2.0.0 + * @category type ids + */ +export const TypeId: unique symbol = Symbol.for("effect/Subscribable") + +/** + * @since 2.0.0 + * @category type ids + */ +export type TypeId = typeof TypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface Subscribable extends Readable.Readable { + readonly [TypeId]: TypeId + readonly changes: Stream.Stream +} + +/** + * @since 2.0.0 + * @category refinements + */ +export const isSubscribable = (u: unknown): u is Subscribable => hasProperty(u, TypeId) + +const Proto: Omit, "get" | "changes"> = { + [Readable.TypeId]: Readable.TypeId, + [TypeId]: TypeId, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const make = (options: { + readonly get: Effect.Effect + readonly changes: Stream.Stream +}): Subscribable => Object.assign(Object.create(Proto), options) + +/** + * @since 2.0.0 + * @category combinators + */ +export const map: { + /** + * @since 2.0.0 + * @category combinators + */ + (f: (a: NoInfer) => B): (fa: Subscribable) => Subscribable + /** + * @since 2.0.0 + * @category combinators + */ + (self: Subscribable, f: (a: NoInfer) => B): Subscribable +} = dual(2, (self: Subscribable, f: (a: NoInfer) => B): Subscribable => + make({ + get: Effect.map(self.get, f), + changes: Stream.map(self.changes, f) + })) + +/** + * @since 2.0.0 + * @category combinators + */ +export const mapEffect: { + /** + * @since 2.0.0 + * @category combinators + */ + (f: (a: NoInfer) => Effect.Effect): (fa: Subscribable) => Subscribable + /** + * @since 2.0.0 + * @category combinators + */ + ( + self: Subscribable, + f: (a: NoInfer) => Effect.Effect + ): Subscribable +} = dual(2, ( + self: Subscribable, + f: (a: NoInfer) => Effect.Effect +): Subscribable => + make({ + get: Effect.flatMap(self.get, f), + changes: Stream.mapEffect(self.changes, f) + })) + +/** + * @since 2.0.0 + * @category constructors + */ +export const unwrap = ( + effect: Effect.Effect, E1, R1> +): Subscribable => + make({ + get: Effect.flatMap(effect, (s) => s.get), + changes: Stream.unwrap(Effect.map(effect, (s) => s.changes)) + }) diff --git a/backend/node_modules/effect/src/SubscriptionRef.ts b/backend/node_modules/effect/src/SubscriptionRef.ts new file mode 100644 index 0000000000000000000000000000000000000000..7cceab93cf67bdca1dab27e64e6ae7903f7bce79 --- /dev/null +++ b/backend/node_modules/effect/src/SubscriptionRef.ts @@ -0,0 +1,444 @@ +/** + * @since 2.0.0 + */ +import type * as Effect from "./Effect.js" +import * as internal from "./internal/subscriptionRef.js" +import type * as Option from "./Option.js" +import type * as PubSub from "./PubSub.js" +import * as Ref from "./Ref.js" +import type * as Stream from "./Stream.js" +import type { Subscribable } from "./Subscribable.js" +import * as Synchronized from "./SynchronizedRef.js" +import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const SubscriptionRefTypeId: unique symbol = internal.SubscriptionRefTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type SubscriptionRefTypeId = typeof SubscriptionRefTypeId + +/** + * A `SubscriptionRef` is a `Ref` that can be subscribed to in order to + * receive the current value as well as all changes to the value. + * + * @since 2.0.0 + * @category models + */ +export interface SubscriptionRef + extends SubscriptionRef.Variance, Synchronized.SynchronizedRef, Subscribable +{ + /** @internal */ + readonly ref: Ref.Ref + /** @internal */ + readonly pubsub: PubSub.PubSub + /** @internal */ + readonly semaphore: Effect.Semaphore + /** + * A stream containing the current value of the `Ref` as well as all changes + * to that value. + */ + readonly changes: Stream.Stream + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: SubscriptionRefUnify + readonly [Unify.ignoreSymbol]?: SubscriptionRefUnifyIgnore +} + +/** + * @category models + * @since 3.8.0 + */ +export interface SubscriptionRefUnify + extends Synchronized.SynchronizedRefUnify +{ + SubscriptionRef?: () => Extract> +} + +/** + * @category models + * @since 3.8.0 + */ +export interface SubscriptionRefUnifyIgnore extends Synchronized.SynchronizedRefUnifyIgnore { + SynchronizedRef?: true +} + +/** + * @since 2.0.0 + */ +export declare namespace SubscriptionRef { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [SubscriptionRefTypeId]: { + readonly _A: Types.Invariant + } + } +} + +/** + * @since 2.0.0 + * @category getters + */ +export const get: (self: SubscriptionRef) => Effect.Effect = internal.get + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndSet: { + /** + * @since 2.0.0 + * @category utils + */ + (value: A): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SubscriptionRef, value: A): Effect.Effect +} = Ref.getAndSet + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndUpdate: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => A): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SubscriptionRef, f: (a: A) => A): Effect.Effect +} = Ref.getAndUpdate + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndUpdateEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Effect.Effect): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SubscriptionRef, f: (a: A) => Effect.Effect): Effect.Effect +} = Synchronized.getAndUpdateEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndUpdateSome: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SubscriptionRef, pf: (a: A) => Option.Option): Effect.Effect +} = Ref.getAndUpdateSome + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndUpdateSomeEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option>): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + ( + self: SubscriptionRef, + pf: (a: A) => Option.Option> + ): Effect.Effect +} = Synchronized.getAndUpdateSomeEffect + +/** + * Creates a new `SubscriptionRef` with the specified value. + * + * @since 2.0.0 + * @category constructors + */ +export const make: (value: A) => Effect.Effect> = internal.make + +/** + * @since 2.0.0 + * @category utils + */ +export const modify: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => readonly [B, A]): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SubscriptionRef, f: (a: A) => readonly [B, A]): Effect.Effect +} = internal.modify + +/** + * @since 2.0.0 + * @category utils + */ +export const modifyEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Effect.Effect): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + ( + self: SubscriptionRef, + f: (a: A) => Effect.Effect + ): Effect.Effect +} = internal.modifyEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const modifySome: { + /** + * @since 2.0.0 + * @category utils + */ + (fallback: B, pf: (a: A) => Option.Option): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + ( + self: SubscriptionRef, + fallback: B, + pf: (a: A) => Option.Option + ): Effect.Effect +} = Ref.modifySome + +/** + * @since 2.0.0 + * @category utils + */ +export const modifySomeEffect: { + /** + * @since 2.0.0 + * @category utils + */ + ( + fallback: B, + pf: (a: A) => Option.Option> + ): (self: Synchronized.SynchronizedRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + ( + self: Synchronized.SynchronizedRef, + fallback: B, + pf: (a: A) => Option.Option> + ): Effect.Effect +} = Synchronized.modifySomeEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const set: { + /** + * @since 2.0.0 + * @category utils + */ + (value: A): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SubscriptionRef, value: A): Effect.Effect +} = internal.set + +/** + * @since 2.0.0 + * @category utils + */ +export const setAndGet: { + /** + * @since 2.0.0 + * @category utils + */ + (value: A): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SubscriptionRef, value: A): Effect.Effect +} = Ref.setAndGet + +/** + * @since 2.0.0 + * @category utils + */ +export const update: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => A): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SubscriptionRef, f: (a: A) => A): Effect.Effect +} = Ref.update + +/** + * @since 2.0.0 + * @category utils + */ +export const updateEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Effect.Effect): (self: Synchronized.SynchronizedRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Synchronized.SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect +} = Synchronized.updateEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const updateAndGet: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => A): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SubscriptionRef, f: (a: A) => A): Effect.Effect +} = Ref.updateAndGet + +/** + * @since 2.0.0 + * @category utils + */ +export const updateAndGetEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Effect.Effect): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SubscriptionRef, f: (a: A) => Effect.Effect): Effect.Effect +} = Synchronized.updateAndGetEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const updateSome: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Option.Option): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SubscriptionRef, f: (a: A) => Option.Option): Effect.Effect +} = Ref.updateSome + +/** + * @since 2.0.0 + * @category utils + */ +export const updateSomeEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option>): (self: Synchronized.SynchronizedRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + ( + self: Synchronized.SynchronizedRef, + pf: (a: A) => Option.Option> + ): Effect.Effect +} = Synchronized.updateSomeEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const updateSomeAndGet: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SubscriptionRef, pf: (a: A) => Option.Option): Effect.Effect +} = Ref.updateSomeAndGet + +/** + * @since 2.0.0 + * @category utils + */ +export const updateSomeAndGetEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option>): (self: SubscriptionRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + ( + self: SubscriptionRef, + pf: (a: A) => Option.Option> + ): Effect.Effect +} = Synchronized.updateSomeAndGetEffect diff --git a/backend/node_modules/effect/src/Supervisor.ts b/backend/node_modules/effect/src/Supervisor.ts new file mode 100644 index 0000000000000000000000000000000000000000..b323dcab70985134970bfb26cc6871654fe4203a --- /dev/null +++ b/backend/node_modules/effect/src/Supervisor.ts @@ -0,0 +1,240 @@ +/** + * A `Supervisor` is allowed to supervise the launching and termination of + * fibers, producing some visible value of type `T` from the supervision. + * + * @since 2.0.0 + */ +import type * as Context from "./Context.js" +import type * as Effect from "./Effect.js" +import type * as Exit from "./Exit.js" +import type * as Fiber from "./Fiber.js" +import * as core from "./internal/core.js" +import * as circular from "./internal/layer/circular.js" +import * as internal from "./internal/supervisor.js" +import type * as Layer from "./Layer.js" +import type * as MutableRef from "./MutableRef.js" +import type * as Option from "./Option.js" +import type * as SortedSet from "./SortedSet.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const SupervisorTypeId: unique symbol = internal.SupervisorTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type SupervisorTypeId = typeof SupervisorTypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface Supervisor extends Supervisor.Variance { + /** + * Returns an `Effect` that succeeds with the value produced by this + * supervisor. This value may change over time, reflecting what the supervisor + * produces as it supervises fibers. + */ + readonly value: Effect.Effect + + /** + * Supervises the start of a `Fiber`. + */ + onStart( + context: Context.Context, + effect: Effect.Effect, + parent: Option.Option>, + fiber: Fiber.RuntimeFiber + ): void + + /** + * Supervises the end of a `Fiber`. + */ + onEnd(value: Exit.Exit, fiber: Fiber.RuntimeFiber): void + + /** + * Supervises the execution of an `Effect` by a `Fiber`. + */ + onEffect(fiber: Fiber.RuntimeFiber, effect: Effect.Effect): void + + /** + * Supervises the suspension of a computation running within a `Fiber`. + */ + onSuspend(fiber: Fiber.RuntimeFiber): void + + /** + * Supervises the resumption of a computation running within a `Fiber`. + */ + onResume(fiber: Fiber.RuntimeFiber): void + + /** + * Maps this supervisor to another one, which has the same effect, but whose + * value has been transformed by the specified function. + */ + map(f: (a: T) => B): Supervisor + + /** + * Returns a new supervisor that performs the function of this supervisor, and + * the function of the specified supervisor, producing a tuple of the outputs + * produced by both supervisors. + */ + zip(right: Supervisor): Supervisor<[T, A]> +} + +/** + * @since 2.0.0 + */ +export declare namespace Supervisor { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [SupervisorTypeId]: { + readonly _T: Types.Covariant + } + } +} + +/** + * @since 2.0.0 + * @category context + */ +export const addSupervisor: (supervisor: Supervisor) => Layer.Layer = circular.addSupervisor + +/** + * Creates a new supervisor that tracks children in a set. + * + * @since 2.0.0 + * @category constructors + */ +export const fibersIn: ( + ref: MutableRef.MutableRef>> +) => Effect.Effect>>> = internal.fibersIn + +/** + * Creates a new supervisor that constantly yields effect when polled + * + * @since 2.0.0 + * @category constructors + */ +export const fromEffect: (effect: Effect.Effect) => Supervisor = internal.fromEffect + +/** + * A supervisor that doesn't do anything in response to supervision events. + * + * @since 2.0.0 + * @category constructors + */ +export const none: Supervisor = internal.none + +/** + * Creates a new supervisor that tracks children in a set. + * + * @since 2.0.0 + * @category constructors + */ +export const track: Effect.Effect>>> = internal.track + +/** + * Unsafely creates a new supervisor that tracks children in a set. + * + * @since 2.0.0 + * @category unsafe + */ +export const unsafeTrack: () => Supervisor>> = internal.unsafeTrack + +/** + * @since 2.0.0 + * @category constructors + */ +export abstract class AbstractSupervisor implements Supervisor { + /** + * @since 2.0.0 + */ + abstract value: Effect.Effect + + /** + * @since 2.0.0 + */ + onStart( + _context: Context.Context, + _effect: Effect.Effect, + _parent: Option.Option>, + _fiber: Fiber.RuntimeFiber + ): void { + // + } + + /** + * @since 2.0.0 + */ + onEnd( + _value: Exit.Exit, + _fiber: Fiber.RuntimeFiber + ): void { + // + } + + /** + * @since 2.0.0 + */ + onEffect( + _fiber: Fiber.RuntimeFiber, + _effect: Effect.Effect + ): void { + // + } + + /** + * @since 2.0.0 + */ + onSuspend( + _fiber: Fiber.RuntimeFiber + ): void { + // + } + + /** + * @since 2.0.0 + */ + onResume( + _fiber: Fiber.RuntimeFiber + ): void { + // + } + + /** + * @since 2.0.0 + */ + map(f: (a: T) => B): Supervisor { + return new internal.ProxySupervisor(this, core.map(this.value, f)) + } + + /** + * @since 2.0.0 + */ + zip( + right: Supervisor + ): Supervisor<[T, A]> { + return new internal.Zip(this, right) + } + + /** + * @since 2.0.0 + */ + onRun(execution: () => X, _fiber: Fiber.RuntimeFiber): X { + return execution() + } + + /** + * @since 2.0.0 + */ + readonly [SupervisorTypeId]: { + _T: (_: never) => never + } = internal.supervisorVariance +} diff --git a/backend/node_modules/effect/src/Symbol.ts b/backend/node_modules/effect/src/Symbol.ts new file mode 100644 index 0000000000000000000000000000000000000000..eaf452743b62522d3a45dd605ee5aff8f867f3da --- /dev/null +++ b/backend/node_modules/effect/src/Symbol.ts @@ -0,0 +1,29 @@ +/** + * @since 2.0.0 + */ + +import * as equivalence from "./Equivalence.js" +import * as predicate from "./Predicate.js" + +/** + * Tests if a value is a `symbol`. + * + * @example + * ```ts + * import * as assert from "node:assert" + * import { Predicate } from "effect" + * + * assert.deepStrictEqual(Predicate.isSymbol(Symbol.for("a")), true) + * assert.deepStrictEqual(Predicate.isSymbol("a"), false) + * ``` + * + * @category guards + * @since 2.0.0 + */ +export const isSymbol: (u: unknown) => u is symbol = predicate.isSymbol + +/** + * @category instances + * @since 2.0.0 + */ +export const Equivalence: equivalence.Equivalence = equivalence.symbol diff --git a/backend/node_modules/effect/src/SynchronizedRef.ts b/backend/node_modules/effect/src/SynchronizedRef.ts new file mode 100644 index 0000000000000000000000000000000000000000..1654ee08e669e3ac6f4c560a0658732af1f3ad02 --- /dev/null +++ b/backend/node_modules/effect/src/SynchronizedRef.ts @@ -0,0 +1,429 @@ +/** + * @since 2.0.0 + */ +import type * as Effect from "./Effect.js" +import * as circular from "./internal/effect/circular.js" +import * as ref from "./internal/ref.js" +import * as internal from "./internal/synchronizedRef.js" +import type * as Option from "./Option.js" +import type * as Ref from "./Ref.js" +import type * as Types from "./Types.js" +import type * as Unify from "./Unify.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const SynchronizedRefTypeId: unique symbol = circular.SynchronizedTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type SynchronizedRefTypeId = typeof SynchronizedRefTypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface SynchronizedRef extends SynchronizedRef.Variance, Ref.Ref { + modifyEffect(f: (a: A) => Effect.Effect): Effect.Effect + readonly [Unify.typeSymbol]?: unknown + readonly [Unify.unifySymbol]?: SynchronizedRefUnify + readonly [Unify.ignoreSymbol]?: SynchronizedRefUnifyIgnore +} + +/** + * @category models + * @since 3.8.0 + */ +export interface SynchronizedRefUnify extends Ref.RefUnify { + SynchronizedRef?: () => Extract> +} + +/** + * @category models + * @since 3.8.0 + */ +export interface SynchronizedRefUnifyIgnore extends Ref.RefUnifyIgnore { + Ref?: true +} + +/** + * @since 2.0.0 + */ +export declare namespace SynchronizedRef { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [SynchronizedRefTypeId]: { + readonly _A: Types.Invariant + } + } +} + +/** + * @since 2.0.0 + * @category constructors + */ +export const make: (value: A) => Effect.Effect> = circular.makeSynchronized + +/** + * @since 2.0.0 + * @category getters + */ +export const get: (self: SynchronizedRef) => Effect.Effect = ref.get + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndSet: { + /** + * @since 2.0.0 + * @category utils + */ + (value: A): (self: Ref.Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref.Ref, value: A): Effect.Effect +} = ref.getAndSet + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndUpdate: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => A): (self: Ref.Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref.Ref, f: (a: A) => A): Effect.Effect +} = ref.getAndUpdate + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndUpdateEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Effect.Effect): (self: SynchronizedRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect +} = internal.getAndUpdateEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndUpdateSome: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option): (self: Ref.Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref.Ref, pf: (a: A) => Option.Option): Effect.Effect +} = ref.getAndUpdateSome + +/** + * @since 2.0.0 + * @category utils + */ +export const getAndUpdateSomeEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option>): (self: SynchronizedRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + ( + self: SynchronizedRef, + pf: (a: A) => Option.Option> + ): Effect.Effect +} = internal.getAndUpdateSomeEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const modify: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => readonly [B, A]): (self: SynchronizedRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SynchronizedRef, f: (a: A) => readonly [B, A]): Effect.Effect +} = internal.modify + +/** + * @since 2.0.0 + * @category utils + */ +export const modifyEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Effect.Effect): (self: SynchronizedRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + ( + self: SynchronizedRef, + f: (a: A) => Effect.Effect + ): Effect.Effect +} = internal.modifyEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const modifySome: { + /** + * @since 2.0.0 + * @category utils + */ + (fallback: B, pf: (a: A) => Option.Option): (self: Ref.Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + ( + self: Ref.Ref, + fallback: B, + pf: (a: A) => Option.Option + ): Effect.Effect +} = ref.modifySome + +/** + * @since 2.0.0 + * @category utils + */ +export const modifySomeEffect: { + /** + * @since 2.0.0 + * @category utils + */ + ( + fallback: B, + pf: (a: A) => Option.Option> + ): (self: SynchronizedRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + ( + self: SynchronizedRef, + fallback: B, + pf: (a: A) => Option.Option> + ): Effect.Effect +} = internal.modifySomeEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const set: { + /** + * @since 2.0.0 + * @category utils + */ + (value: A): (self: Ref.Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref.Ref, value: A): Effect.Effect +} = ref.set + +/** + * @since 2.0.0 + * @category utils + */ +export const setAndGet: { + /** + * @since 2.0.0 + * @category utils + */ + (value: A): (self: Ref.Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref.Ref, value: A): Effect.Effect +} = ref.setAndGet + +/** + * @since 2.0.0 + * @category utils + */ +export const update: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => A): (self: Ref.Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref.Ref, f: (a: A) => A): Effect.Effect +} = ref.update + +/** + * @since 2.0.0 + * @category utils + */ +export const updateEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Effect.Effect): (self: SynchronizedRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect +} = internal.updateEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const updateAndGet: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => A): (self: Ref.Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref.Ref, f: (a: A) => A): Effect.Effect +} = ref.updateAndGet + +/** + * @since 2.0.0 + * @category utils + */ +export const updateAndGetEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Effect.Effect): (self: SynchronizedRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect +} = internal.updateAndGetEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const updateSome: { + /** + * @since 2.0.0 + * @category utils + */ + (f: (a: A) => Option.Option): (self: Ref.Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref.Ref, f: (a: A) => Option.Option): Effect.Effect +} = ref.updateSome + +/** + * @since 2.0.0 + * @category utils + */ +export const updateSomeEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option>): (self: SynchronizedRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + ( + self: SynchronizedRef, + pf: (a: A) => Option.Option> + ): Effect.Effect +} = internal.updateSomeEffect + +/** + * @since 2.0.0 + * @category utils + */ +export const updateSomeAndGet: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option): (self: Ref.Ref) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + (self: Ref.Ref, pf: (a: A) => Option.Option): Effect.Effect +} = ref.updateSomeAndGet + +/** + * @since 2.0.0 + * @category utils + */ +export const updateSomeAndGetEffect: { + /** + * @since 2.0.0 + * @category utils + */ + (pf: (a: A) => Option.Option>): (self: SynchronizedRef) => Effect.Effect + /** + * @since 2.0.0 + * @category utils + */ + ( + self: SynchronizedRef, + pf: (a: A) => Option.Option> + ): Effect.Effect +} = circular.updateSomeAndGetEffectSynchronized + +/** + * @since 2.0.0 + * @category unsafe + */ +export const unsafeMake: (value: A) => SynchronizedRef = circular.unsafeMakeSynchronized diff --git a/backend/node_modules/effect/src/TArray.ts b/backend/node_modules/effect/src/TArray.ts new file mode 100644 index 0000000000000000000000000000000000000000..9bb4f6845ecc7aeb4099c7aa646238a5deabc862 --- /dev/null +++ b/backend/node_modules/effect/src/TArray.ts @@ -0,0 +1,922 @@ +/** + * @since 2.0.0 + */ +import * as internal from "./internal/stm/tArray.js" +import type * as Option from "./Option.js" +import type * as Order from "./Order.js" +import type { Predicate } from "./Predicate.js" +import type * as STM from "./STM.js" +import type * as TRef from "./TRef.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const TArrayTypeId: unique symbol = internal.TArrayTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type TArrayTypeId = typeof TArrayTypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface TArray extends TArray.Variance {} +/** + * @internal + * @since 2.0.0 + */ +export interface TArray { + /** @internal */ + readonly chunk: Array> +} + +/** + * @since 2.0.0 + */ +export declare namespace TArray { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [TArrayTypeId]: { + readonly _A: Types.Invariant + } + } +} + +/** + * Finds the result of applying a partial function to the first value in its + * domain. + * + * @since 2.0.0 + * @category elements + */ +export const collectFirst: { + /** + * Finds the result of applying a partial function to the first value in its + * domain. + * + * @since 2.0.0 + * @category elements + */ + (pf: (a: A) => Option.Option): (self: TArray) => STM.STM> + /** + * Finds the result of applying a partial function to the first value in its + * domain. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, pf: (a: A) => Option.Option): STM.STM> +} = internal.collectFirst + +/** + * Finds the result of applying an transactional partial function to the first + * value in its domain. + * + * @since 2.0.0 + * @category elements + */ +export const collectFirstSTM: { + /** + * Finds the result of applying an transactional partial function to the first + * value in its domain. + * + * @since 2.0.0 + * @category elements + */ + (pf: (a: A) => Option.Option>): (self: TArray) => STM.STM, E, R> + /** + * Finds the result of applying an transactional partial function to the first + * value in its domain. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, pf: (a: A) => Option.Option>): STM.STM, E, R> +} = internal.collectFirstSTM + +/** + * Determine if the array contains a specified value. + * + * @macro trace + * @since 2.0.0 + * @category elements + */ +export const contains: { + /** + * Determine if the array contains a specified value. + * + * @macro trace + * @since 2.0.0 + * @category elements + */ + (value: A): (self: TArray) => STM.STM + /** + * Determine if the array contains a specified value. + * + * @macro trace + * @since 2.0.0 + * @category elements + */ + (self: TArray, value: A): STM.STM +} = internal.contains + +/** + * Count the values in the array matching a predicate. + * + * @macro trace + * @since 2.0.0 + * @category folding + */ +export const count: { + /** + * Count the values in the array matching a predicate. + * + * @macro trace + * @since 2.0.0 + * @category folding + */ + (predicate: Predicate): (self: TArray) => STM.STM + /** + * Count the values in the array matching a predicate. + * + * @macro trace + * @since 2.0.0 + * @category folding + */ + (self: TArray, predicate: Predicate): STM.STM +} = internal.count + +/** + * Count the values in the array matching a transactional predicate. + * + * @macro trace + * @since 2.0.0 + * @category folding + */ +export const countSTM: { + /** + * Count the values in the array matching a transactional predicate. + * + * @macro trace + * @since 2.0.0 + * @category folding + */ + (predicate: (value: A) => STM.STM): (self: TArray) => STM.STM + /** + * Count the values in the array matching a transactional predicate. + * + * @macro trace + * @since 2.0.0 + * @category folding + */ + (self: TArray, predicate: (value: A) => STM.STM): STM.STM +} = internal.countSTM + +/** + * Makes an empty `TArray`. + * + * @since 2.0.0 + * @category constructors + */ +export const empty: () => STM.STM> = internal.empty + +/** + * Atomically evaluate the conjunction of a predicate across the members of + * the array. + * + * @since 2.0.0 + * @category elements + */ +export const every: { + /** + * Atomically evaluate the conjunction of a predicate across the members of + * the array. + * + * @since 2.0.0 + * @category elements + */ + (predicate: Predicate): (self: TArray) => STM.STM + /** + * Atomically evaluate the conjunction of a predicate across the members of + * the array. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, predicate: Predicate): STM.STM +} = internal.every + +/** + * Atomically evaluate the conjunction of a transactional predicate across the + * members of the array. + * + * @since 2.0.0 + * @category elements + */ +export const everySTM: { + /** + * Atomically evaluate the conjunction of a transactional predicate across the + * members of the array. + * + * @since 2.0.0 + * @category elements + */ + (predicate: (value: A) => STM.STM): (self: TArray) => STM.STM + /** + * Atomically evaluate the conjunction of a transactional predicate across the + * members of the array. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, predicate: (value: A) => STM.STM): STM.STM +} = internal.everySTM + +/** + * Find the first element in the array matching the specified predicate. + * + * @since 2.0.0 + * @category elements + */ +export const findFirst: { + /** + * Find the first element in the array matching the specified predicate. + * + * @since 2.0.0 + * @category elements + */ + (predicate: Predicate): (self: TArray) => STM.STM> + /** + * Find the first element in the array matching the specified predicate. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, predicate: Predicate): STM.STM> +} = internal.findFirst + +/** + * Get the first index of a specific value in the array. + * + * @since 2.0.0 + * @category elements + */ +export const findFirstIndex: { + /** + * Get the first index of a specific value in the array. + * + * @since 2.0.0 + * @category elements + */ + (value: A): (self: TArray) => STM.STM> + /** + * Get the first index of a specific value in the array. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, value: A): STM.STM> +} = internal.findFirstIndex + +/** + * Get the first index of a specific value in the array starting from the + * specified index. + * + * @since 2.0.0 + * @category elements + */ +export const findFirstIndexFrom: { + /** + * Get the first index of a specific value in the array starting from the + * specified index. + * + * @since 2.0.0 + * @category elements + */ + (value: A, from: number): (self: TArray) => STM.STM> + /** + * Get the first index of a specific value in the array starting from the + * specified index. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, value: A, from: number): STM.STM> +} = internal.findFirstIndexFrom + +/** + * Get the index of the first entry in the array matching a predicate. + * + * @since 2.0.0 + * @category elements + */ +export const findFirstIndexWhere: { + /** + * Get the index of the first entry in the array matching a predicate. + * + * @since 2.0.0 + * @category elements + */ + (predicate: Predicate): (self: TArray) => STM.STM> + /** + * Get the index of the first entry in the array matching a predicate. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, predicate: Predicate): STM.STM> +} = internal.findFirstIndexWhere + +/** + * Get the index of the first entry in the array starting from the specified + * index, matching a predicate. + * + * @since 2.0.0 + * @category elements + */ +export const findFirstIndexWhereFrom: { + /** + * Get the index of the first entry in the array starting from the specified + * index, matching a predicate. + * + * @since 2.0.0 + * @category elements + */ + (predicate: Predicate, from: number): (self: TArray) => STM.STM> + /** + * Get the index of the first entry in the array starting from the specified + * index, matching a predicate. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, predicate: Predicate, from: number): STM.STM> +} = internal.findFirstIndexWhereFrom + +/** + * Get the index of the next entry that matches a transactional predicate. + * + * @since 2.0.0 + * @category elements + */ +export const findFirstIndexWhereSTM: { + /** + * Get the index of the next entry that matches a transactional predicate. + * + * @since 2.0.0 + * @category elements + */ + (predicate: (value: A) => STM.STM): (self: TArray) => STM.STM, E, R> + /** + * Get the index of the next entry that matches a transactional predicate. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, predicate: (value: A) => STM.STM): STM.STM, E, R> +} = internal.findFirstIndexWhereSTM + +/** + * Starting at specified index, get the index of the next entry that matches a + * transactional predicate. + * + * @since 2.0.0 + * @category elements + */ +export const findFirstIndexWhereFromSTM: { + /** + * Starting at specified index, get the index of the next entry that matches a + * transactional predicate. + * + * @since 2.0.0 + * @category elements + */ + (predicate: (value: A) => STM.STM, from: number): (self: TArray) => STM.STM, E, R> + /** + * Starting at specified index, get the index of the next entry that matches a + * transactional predicate. + * + * @since 2.0.0 + * @category elements + */ + ( + self: TArray, + predicate: (value: A) => STM.STM, + from: number + ): STM.STM, E, R> +} = internal.findFirstIndexWhereFromSTM + +/** + * Find the first element in the array matching a transactional predicate. + * + * @since 2.0.0 + * @category elements + */ +export const findFirstSTM: { + /** + * Find the first element in the array matching a transactional predicate. + * + * @since 2.0.0 + * @category elements + */ + (predicate: (value: A) => STM.STM): (self: TArray) => STM.STM, E, R> + /** + * Find the first element in the array matching a transactional predicate. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, predicate: (value: A) => STM.STM): STM.STM, E, R> +} = internal.findFirstSTM + +/** + * Find the last element in the array matching a predicate. + * + * @since 2.0.0 + * @category elements + */ +export const findLast: { + /** + * Find the last element in the array matching a predicate. + * + * @since 2.0.0 + * @category elements + */ + (predicate: Predicate): (self: TArray) => STM.STM> + /** + * Find the last element in the array matching a predicate. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, predicate: Predicate): STM.STM> +} = internal.findLast + +/** + * Get the last index of a specific value in the array bounded above by a + * specific index. + * + * @since 2.0.0 + * @category elements + */ +export const findLastIndex: { + /** + * Get the last index of a specific value in the array bounded above by a + * specific index. + * + * @since 2.0.0 + * @category elements + */ + (value: A): (self: TArray) => STM.STM> + /** + * Get the last index of a specific value in the array bounded above by a + * specific index. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, value: A): STM.STM> +} = internal.findLastIndex + +/** + * Get the last index of a specific value in the array bounded above by a + * specific index. + * + * @since 2.0.0 + * @category elements + */ +export const findLastIndexFrom: { + /** + * Get the last index of a specific value in the array bounded above by a + * specific index. + * + * @since 2.0.0 + * @category elements + */ + (value: A, end: number): (self: TArray) => STM.STM> + /** + * Get the last index of a specific value in the array bounded above by a + * specific index. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, value: A, end: number): STM.STM> +} = internal.findLastIndexFrom + +/** + * Find the last element in the array matching a transactional predicate. + * + * @since 2.0.0 + * @category elements + */ +export const findLastSTM: { + /** + * Find the last element in the array matching a transactional predicate. + * + * @since 2.0.0 + * @category elements + */ + (predicate: (value: A) => STM.STM): (self: TArray) => STM.STM, E, R> + /** + * Find the last element in the array matching a transactional predicate. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, predicate: (value: A) => STM.STM): STM.STM, E, R> +} = internal.findLastSTM + +/** + * Atomically performs transactional effect for each item in array. + * + * @since 2.0.0 + * @category elements + */ +export const forEach: { + /** + * Atomically performs transactional effect for each item in array. + * + * @since 2.0.0 + * @category elements + */ + (f: (value: A) => STM.STM): (self: TArray) => STM.STM + /** + * Atomically performs transactional effect for each item in array. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, f: (value: A) => STM.STM): STM.STM +} = internal.forEach + +/** + * Creates a new `TArray` from an iterable collection of values. + * + * @since 2.0.0 + * @category constructors + */ +export const fromIterable: (iterable: Iterable) => STM.STM> = internal.fromIterable + +/** + * Extracts value from ref in array. + * + * @since 2.0.0 + * @category elements + */ +export const get: { + /** + * Extracts value from ref in array. + * + * @since 2.0.0 + * @category elements + */ + (index: number): (self: TArray) => STM.STM + /** + * Extracts value from ref in array. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, index: number): STM.STM +} = internal.get + +/** + * The first entry of the array, if it exists. + * + * @since 2.0.0 + * @category elements + */ +export const headOption: (self: TArray) => STM.STM> = internal.headOption + +/** + * The last entry in the array, if it exists. + * + * @since 2.0.0 + * @category elements + */ +export const lastOption: (self: TArray) => STM.STM> = internal.lastOption + +/** + * Makes a new `TArray` that is initialized with specified values. + * + * @since 2.0.0 + * @category constructors + */ +export const make: ]>( + ...elements: Elements +) => STM.STM> = internal.make + +/** + * Atomically compute the greatest element in the array, if it exists. + * + * @since 2.0.0 + * @category elements + */ +export const maxOption: { + /** + * Atomically compute the greatest element in the array, if it exists. + * + * @since 2.0.0 + * @category elements + */ + (order: Order.Order): (self: TArray) => STM.STM> + /** + * Atomically compute the greatest element in the array, if it exists. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, order: Order.Order): STM.STM> +} = internal.maxOption + +/** + * Atomically compute the least element in the array, if it exists. + * + * @since 2.0.0 + * @category elements + */ +export const minOption: { + /** + * Atomically compute the least element in the array, if it exists. + * + * @since 2.0.0 + * @category elements + */ + (order: Order.Order): (self: TArray) => STM.STM> + /** + * Atomically compute the least element in the array, if it exists. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, order: Order.Order): STM.STM> +} = internal.minOption + +/** + * Atomically folds using a pure function. + * + * @since 2.0.0 + * @category folding + */ +export const reduce: { + /** + * Atomically folds using a pure function. + * + * @since 2.0.0 + * @category folding + */ + (zero: Z, f: (accumulator: Z, current: A) => Z): (self: TArray) => STM.STM + /** + * Atomically folds using a pure function. + * + * @since 2.0.0 + * @category folding + */ + (self: TArray, zero: Z, f: (accumulator: Z, current: A) => Z): STM.STM +} = internal.reduce + +/** + * Atomically reduce the array, if non-empty, by a binary operator. + * + * @since 2.0.0 + * @category elements + */ +export const reduceOption: { + /** + * Atomically reduce the array, if non-empty, by a binary operator. + * + * @since 2.0.0 + * @category elements + */ + (f: (x: A, y: A) => A): (self: TArray) => STM.STM> + /** + * Atomically reduce the array, if non-empty, by a binary operator. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, f: (x: A, y: A) => A): STM.STM> +} = internal.reduceOption + +/** + * Atomically reduce the non-empty array using a transactional binary + * operator. + * + * @since 2.0.0 + * @category elements + */ +export const reduceOptionSTM: { + /** + * Atomically reduce the non-empty array using a transactional binary + * operator. + * + * @since 2.0.0 + * @category elements + */ + (f: (x: A, y: A) => STM.STM): (self: TArray) => STM.STM, E, R> + /** + * Atomically reduce the non-empty array using a transactional binary + * operator. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, f: (x: A, y: A) => STM.STM): STM.STM, E, R> +} = internal.reduceOptionSTM + +/** + * Atomically folds using a transactional function. + * + * @macro trace + * @since 2.0.0 + * @category folding + */ +export const reduceSTM: { + /** + * Atomically folds using a transactional function. + * + * @macro trace + * @since 2.0.0 + * @category folding + */ + (zero: Z, f: (accumulator: Z, current: A) => STM.STM): (self: TArray) => STM.STM + /** + * Atomically folds using a transactional function. + * + * @macro trace + * @since 2.0.0 + * @category folding + */ + ( + self: TArray, + zero: Z, + f: (accumulator: Z, current: A) => STM.STM + ): STM.STM +} = internal.reduceSTM + +/** + * Returns the size of the `TArray`. + * + * @since 2.0.0 + * @category getters + */ +export const size: (self: TArray) => number = internal.size + +/** + * Determine if the array contains a value satisfying a predicate. + * + * @since 2.0.0 + * @category elements + */ +export const some: { + /** + * Determine if the array contains a value satisfying a predicate. + * + * @since 2.0.0 + * @category elements + */ + (predicate: Predicate): (self: TArray) => STM.STM + /** + * Determine if the array contains a value satisfying a predicate. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, predicate: Predicate): STM.STM +} = internal.some + +/** + * Determine if the array contains a value satisfying a transactional + * predicate. + * + * @since 2.0.0 + * @category elements + */ +export const someSTM: { + /** + * Determine if the array contains a value satisfying a transactional + * predicate. + * + * @since 2.0.0 + * @category elements + */ + (predicate: (value: A) => STM.STM): (self: TArray) => STM.STM + /** + * Determine if the array contains a value satisfying a transactional + * predicate. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, predicate: (value: A) => STM.STM): STM.STM +} = internal.someSTM + +/** + * Collects all elements into a chunk. + * + * @since 2.0.0 + * @since 2.0.0 + * @category destructors + */ +export const toArray: (self: TArray) => STM.STM> = internal.toArray + +/** + * Atomically updates all elements using a pure function. + * + * @since 2.0.0 + * @category elements + */ +export const transform: { + /** + * Atomically updates all elements using a pure function. + * + * @since 2.0.0 + * @category elements + */ + (f: (value: A) => A): (self: TArray) => STM.STM + /** + * Atomically updates all elements using a pure function. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, f: (value: A) => A): STM.STM +} = internal.transform + +/** + * Atomically updates all elements using a transactional effect. + * + * @since 2.0.0 + * @category elements + */ +export const transformSTM: { + /** + * Atomically updates all elements using a transactional effect. + * + * @since 2.0.0 + * @category elements + */ + (f: (value: A) => STM.STM): (self: TArray) => STM.STM + /** + * Atomically updates all elements using a transactional effect. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, f: (value: A) => STM.STM): STM.STM +} = internal.transformSTM + +/** + * Updates element in the array with given function. + * + * @since 2.0.0 + * @category elements + */ +export const update: { + /** + * Updates element in the array with given function. + * + * @since 2.0.0 + * @category elements + */ + (index: number, f: (value: A) => A): (self: TArray) => STM.STM + /** + * Updates element in the array with given function. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, index: number, f: (value: A) => A): STM.STM +} = internal.update + +/** + * Atomically updates element in the array with given transactional effect. + * + * @since 2.0.0 + * @category elements + */ +export const updateSTM: { + /** + * Atomically updates element in the array with given transactional effect. + * + * @since 2.0.0 + * @category elements + */ + (index: number, f: (value: A) => STM.STM): (self: TArray) => STM.STM + /** + * Atomically updates element in the array with given transactional effect. + * + * @since 2.0.0 + * @category elements + */ + (self: TArray, index: number, f: (value: A) => STM.STM): STM.STM +} = internal.updateSTM diff --git a/backend/node_modules/effect/src/TDeferred.ts b/backend/node_modules/effect/src/TDeferred.ts new file mode 100644 index 0000000000000000000000000000000000000000..28e2f018aa1f3fcd33baab4cf100c638a9d6c17b --- /dev/null +++ b/backend/node_modules/effect/src/TDeferred.ts @@ -0,0 +1,124 @@ +/** + * @since 2.0.0 + */ +import type * as Either from "./Either.js" +import * as internal from "./internal/stm/tDeferred.js" +import type * as Option from "./Option.js" +import type * as STM from "./STM.js" +import type * as TRef from "./TRef.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const TDeferredTypeId: unique symbol = internal.TDeferredTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type TDeferredTypeId = typeof TDeferredTypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface TDeferred extends TDeferred.Variance {} +/** + * @internal + * @since 2.0.0 + */ +export interface TDeferred { + /** @internal */ + readonly ref: TRef.TRef>> +} + +/** + * @since 2.0.0 + */ +export declare namespace TDeferred { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [TDeferredTypeId]: { + readonly _A: Types.Invariant + readonly _E: Types.Invariant + } + } +} + +const _await: (self: TDeferred) => STM.STM = internal._await + +export { + /** + * @since 2.0.0 + * @category getters + */ + _await as await +} + +/** + * @since 2.0.0 + * @category mutations + */ +export const done: { + /** + * @since 2.0.0 + * @category mutations + */ + (either: Either.Either): (self: TDeferred) => STM.STM + /** + * @since 2.0.0 + * @category mutations + */ + (self: TDeferred, either: Either.Either): STM.STM +} = internal.done + +/** + * @since 2.0.0 + * @category mutations + */ +export const fail: { + /** + * @since 2.0.0 + * @category mutations + */ + (error: E): (self: TDeferred) => STM.STM + /** + * @since 2.0.0 + * @category mutations + */ + (self: TDeferred, error: E): STM.STM +} = internal.fail + +/** + * @since 2.0.0 + * @category constructors + */ +export const make: () => STM.STM> = internal.make + +/** + * @since 2.0.0 + * @category getters + */ +export const poll: (self: TDeferred) => STM.STM>> = internal.poll + +/** + * @since 2.0.0 + * @category mutations + */ +export const succeed: { + /** + * @since 2.0.0 + * @category mutations + */ + (value: A): (self: TDeferred) => STM.STM + /** + * @since 2.0.0 + * @category mutations + */ + (self: TDeferred, value: A): STM.STM +} = internal.succeed diff --git a/backend/node_modules/effect/src/TPriorityQueue.ts b/backend/node_modules/effect/src/TPriorityQueue.ts new file mode 100644 index 0000000000000000000000000000000000000000..4bb2c4aa8aaf3a506cc0d7a4a4a30a619e55606c --- /dev/null +++ b/backend/node_modules/effect/src/TPriorityQueue.ts @@ -0,0 +1,283 @@ +/** + * @since 2.0.0 + */ +import type * as Chunk from "./Chunk.js" +import * as internal from "./internal/stm/tPriorityQueue.js" +import type * as Option from "./Option.js" +import type * as Order from "./Order.js" +import type { Predicate } from "./Predicate.js" +import type * as SortedMap from "./SortedMap.js" +import type * as STM from "./STM.js" +import type * as TRef from "./TRef.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const TPriorityQueueTypeId: unique symbol = internal.TPriorityQueueTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type TPriorityQueueTypeId = typeof TPriorityQueueTypeId + +/** + * A `TPriorityQueue` contains values of type `A` that an `Order` is defined + * on. Unlike a `TQueue`, `take` returns the highest priority value (the value + * that is first in the specified ordering) as opposed to the first value + * offered to the queue. The ordering that elements with the same priority will + * be taken from the queue is not guaranteed. + * + * @since 2.0.0 + * @category models + */ +export interface TPriorityQueue extends TPriorityQueue.Variance {} +/** + * @internal + * @since 2.0.0 + */ +export interface TPriorityQueue { + /** @internal */ + readonly ref: TRef.TRef]>> +} + +/** + * @since 2.0.0 + */ +export declare namespace TPriorityQueue { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [TPriorityQueueTypeId]: { + readonly _A: Types.Invariant + } + } +} + +/** + * Constructs a new empty `TPriorityQueue` with the specified `Order`. + * + * @since 2.0.0 + * @category constructors + */ +export const empty: (order: Order.Order) => STM.STM> = internal.empty + +/** + * Creates a new `TPriorityQueue` from an iterable collection of values. + * + * @since 2.0.0 + * @category constructors + */ +export const fromIterable: ( + order: Order.Order +) => (iterable: Iterable) => STM.STM> = internal.fromIterable + +/** + * Checks whether the queue is empty. + * + * @since 2.0.0 + * @category getters + */ +export const isEmpty: (self: TPriorityQueue) => STM.STM = internal.isEmpty + +/** + * Checks whether the queue is not empty. + * + * @since 2.0.0 + * @category getters + */ +export const isNonEmpty: (self: TPriorityQueue) => STM.STM = internal.isNonEmpty + +/** + * Makes a new `TPriorityQueue` that is initialized with specified values. + * + * @since 2.0.0 + * @category constructors + */ +export const make: (order: Order.Order) => (...elements: Array) => STM.STM> = internal.make + +/** + * Offers the specified value to the queue. + * + * @since 2.0.0 + * @category mutations + */ +export const offer: { + /** + * Offers the specified value to the queue. + * + * @since 2.0.0 + * @category mutations + */ + (value: A): (self: TPriorityQueue) => STM.STM + /** + * Offers the specified value to the queue. + * + * @since 2.0.0 + * @category mutations + */ + (self: TPriorityQueue, value: A): STM.STM +} = internal.offer + +/** + * Offers all of the elements in the specified collection to the queue. + * + * @since 2.0.0 + * @category mutations + */ +export const offerAll: { + /** + * Offers all of the elements in the specified collection to the queue. + * + * @since 2.0.0 + * @category mutations + */ + (values: Iterable): (self: TPriorityQueue) => STM.STM + /** + * Offers all of the elements in the specified collection to the queue. + * + * @since 2.0.0 + * @category mutations + */ + (self: TPriorityQueue, values: Iterable): STM.STM +} = internal.offerAll + +/** + * Peeks at the first value in the queue without removing it, retrying until a + * value is in the queue. + * + * @since 2.0.0 + * @category getters + */ +export const peek: (self: TPriorityQueue) => STM.STM = internal.peek + +/** + * Peeks at the first value in the queue without removing it, returning `None` + * if there is not a value in the queue. + * + * @since 2.0.0 + * @category getters + */ +export const peekOption: (self: TPriorityQueue) => STM.STM> = internal.peekOption + +/** + * Removes all elements from the queue matching the specified predicate. + * + * @since 2.0.0 + * @category getters + */ +export const removeIf: { + /** + * Removes all elements from the queue matching the specified predicate. + * + * @since 2.0.0 + * @category getters + */ + (predicate: Predicate): (self: TPriorityQueue) => STM.STM + /** + * Removes all elements from the queue matching the specified predicate. + * + * @since 2.0.0 + * @category getters + */ + (self: TPriorityQueue, predicate: Predicate): STM.STM +} = internal.removeIf + +/** + * Retains only elements from the queue matching the specified predicate. + * + * @since 2.0.0 + * @category getters + */ +export const retainIf: { + /** + * Retains only elements from the queue matching the specified predicate. + * + * @since 2.0.0 + * @category getters + */ + (predicate: Predicate): (self: TPriorityQueue) => STM.STM + /** + * Retains only elements from the queue matching the specified predicate. + * + * @since 2.0.0 + * @category getters + */ + (self: TPriorityQueue, predicate: Predicate): STM.STM +} = internal.retainIf + +/** + * Returns the size of the queue. + * + * @since 2.0.0 + * @category getters + */ +export const size: (self: TPriorityQueue) => STM.STM = internal.size + +/** + * Takes a value from the queue, retrying until a value is in the queue. + * + * @since 2.0.0 + * @category mutations + */ +export const take: (self: TPriorityQueue) => STM.STM = internal.take + +/** + * Takes all values from the queue. + * + * @since 2.0.0 + * @category mutations + */ +export const takeAll: (self: TPriorityQueue) => STM.STM> = internal.takeAll + +/** + * Takes a value from the queue, returning `None` if there is not a value in + * the queue. + * + * @since 2.0.0 + * @category mutations + */ +export const takeOption: (self: TPriorityQueue) => STM.STM> = internal.takeOption + +/** + * Takes up to the specified maximum number of elements from the queue. + * + * @since 2.0.0 + * @category mutations + */ +export const takeUpTo: { + /** + * Takes up to the specified maximum number of elements from the queue. + * + * @since 2.0.0 + * @category mutations + */ + (n: number): (self: TPriorityQueue) => STM.STM> + /** + * Takes up to the specified maximum number of elements from the queue. + * + * @since 2.0.0 + * @category mutations + */ + (self: TPriorityQueue, n: number): STM.STM> +} = internal.takeUpTo + +/** + * Collects all values into a `Chunk`. + * + * @since 2.0.0 + * @category destructors + */ +export const toChunk: (self: TPriorityQueue) => STM.STM> = internal.toChunk + +/** + * Collects all values into an array. + * + * @since 2.0.0 + * @category destructors + */ +export const toArray: (self: TPriorityQueue) => STM.STM> = internal.toArray diff --git a/backend/node_modules/effect/src/TReentrantLock.ts b/backend/node_modules/effect/src/TReentrantLock.ts new file mode 100644 index 0000000000000000000000000000000000000000..b4f8a518cfeeced5ff5871725577004c1653564d --- /dev/null +++ b/backend/node_modules/effect/src/TReentrantLock.ts @@ -0,0 +1,260 @@ +/** + * @since 2.0.0 + */ +import type * as Effect from "./Effect.js" +import * as internal from "./internal/stm/tReentrantLock.js" +import type * as Scope from "./Scope.js" +import type * as STM from "./STM.js" +import type * as TRef from "./TRef.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const TReentrantLockTypeId: unique symbol = internal.TReentrantLockTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type TReentrantLockTypeId = typeof TReentrantLockTypeId + +/** + * A `TReentrantLock` is a reentrant read/write lock. Multiple readers may all + * concurrently acquire read locks. Only one writer is allowed to acquire a + * write lock at any given time. Read locks may be upgraded into write locks. A + * fiber that has a write lock may acquire other write locks or read locks. + * + * The two primary methods of this structure are `readLock`, which acquires a + * read lock in a scoped context, and `writeLock`, which acquires a write lock + * in a scoped context. + * + * Although located in the STM package, there is no need for locks within STM + * transactions. However, this lock can be quite useful in effectful code, to + * provide consistent read/write access to mutable state; and being in STM + * allows this structure to be composed into more complicated concurrent + * structures that are consumed from effectful code. + * + * @since 2.0.0 + * @category models + */ +export interface TReentrantLock extends TReentrantLock.Proto {} +/** + * @internal + * @since 2.0.0 + */ +export interface TReentrantLock { + /** @internal */ + readonly state: TRef.TRef +} + +/** + * @since 2.0.0 + */ +export declare namespace TReentrantLock { + /** + * @since 2.0.0 + * @category models + */ + export interface Proto { + readonly [TReentrantLockTypeId]: TReentrantLockTypeId + } +} + +/** + * Acquires a read lock. The transaction will suspend until no other fiber is + * holding a write lock. Succeeds with the number of read locks held by this + * fiber. + * + * @since 2.0.0 + * @category mutations + */ +export const acquireRead: (self: TReentrantLock) => STM.STM = internal.acquireRead + +/** + * Acquires a write lock. The transaction will suspend until no other fibers + * are holding read or write locks. Succeeds with the number of write locks + * held by this fiber. + * + * @since 2.0.0 + * @category mutations + */ +export const acquireWrite: (self: TReentrantLock) => STM.STM = internal.acquireWrite + +/** + * Retrieves the number of acquired read locks for this fiber. + * + * @since 2.0.0 + * @category mutations + */ +export const fiberReadLocks: (self: TReentrantLock) => STM.STM = internal.fiberReadLocks + +/** + * Retrieves the number of acquired write locks for this fiber. + * + * @since 2.0.0 + * @category mutations + */ +export const fiberWriteLocks: (self: TReentrantLock) => STM.STM = internal.fiberWriteLocks + +/** + * Just a convenience method for applications that only need reentrant locks, + * without needing a distinction between readers / writers. + * + * See `TReentrantLock.writeLock`. + * + * @since 2.0.0 + * @category mutations + */ +export const lock: (self: TReentrantLock) => Effect.Effect = internal.lock + +/** + * Determines if any fiber has a read or write lock. + * + * @since 2.0.0 + * @category mutations + */ +export const locked: (self: TReentrantLock) => STM.STM = internal.locked + +/** + * Makes a new reentrant read/write lock. + * + * @since 2.0.0 + * @category constructors + */ +export const make: STM.STM = internal.make + +/** + * Obtains a read lock in a scoped context. + * + * @since 2.0.0 + * @category mutations + */ +export const readLock: (self: TReentrantLock) => Effect.Effect = internal.readLock + +/** + * Retrieves the total number of acquired read locks. + * + * @since 2.0.0 + * @category mutations + */ +export const readLocks: (self: TReentrantLock) => STM.STM = internal.readLocks + +/** + * Determines if any fiber has a read lock. + * + * @since 2.0.0 + * @category mutations + */ +export const readLocked: (self: TReentrantLock) => STM.STM = internal.readLocked + +/** + * Releases a read lock held by this fiber. Succeeds with the outstanding + * number of read locks held by this fiber. + * + * @since 2.0.0 + * @category mutations + */ +export const releaseRead: (self: TReentrantLock) => STM.STM = internal.releaseRead + +/** + * Releases a write lock held by this fiber. Succeeds with the outstanding + * number of write locks held by this fiber. + * + * @since 2.0.0 + * @category mutations + */ +export const releaseWrite: (self: TReentrantLock) => STM.STM = internal.releaseWrite + +/** + * Runs the specified workflow with a lock. + * + * @since 2.0.0 + * @category mutations + */ +export const withLock: { + /** + * Runs the specified workflow with a lock. + * + * @since 2.0.0 + * @category mutations + */ + (self: TReentrantLock): (effect: Effect.Effect) => Effect.Effect + /** + * Runs the specified workflow with a lock. + * + * @since 2.0.0 + * @category mutations + */ + (effect: Effect.Effect, self: TReentrantLock): Effect.Effect +} = internal.withLock + +/** + * Runs the specified workflow with a read lock. + * + * @since 2.0.0 + * @category mutations + */ +export const withReadLock: { + /** + * Runs the specified workflow with a read lock. + * + * @since 2.0.0 + * @category mutations + */ + (self: TReentrantLock): (effect: Effect.Effect) => Effect.Effect + /** + * Runs the specified workflow with a read lock. + * + * @since 2.0.0 + * @category mutations + */ + (effect: Effect.Effect, self: TReentrantLock): Effect.Effect +} = internal.withReadLock + +/** + * Runs the specified workflow with a write lock. + * + * @since 2.0.0 + * @category mutations + */ +export const withWriteLock: { + /** + * Runs the specified workflow with a write lock. + * + * @since 2.0.0 + * @category mutations + */ + (self: TReentrantLock): (effect: Effect.Effect) => Effect.Effect + /** + * Runs the specified workflow with a write lock. + * + * @since 2.0.0 + * @category mutations + */ + (effect: Effect.Effect, self: TReentrantLock): Effect.Effect +} = internal.withWriteLock + +/** + * Obtains a write lock in a scoped context. + * + * @since 2.0.0 + * @category mutations + */ +export const writeLock: (self: TReentrantLock) => Effect.Effect = internal.writeLock + +/** + * Determines if a write lock is held by some fiber. + * + * @since 2.0.0 + * @category mutations + */ +export const writeLocked: (self: TReentrantLock) => STM.STM = internal.writeLocked + +/** + * Computes the number of write locks held by fibers. + * + * @since 2.0.0 + * @category mutations + */ +export const writeLocks: (self: TReentrantLock) => STM.STM = internal.writeLocks diff --git a/backend/node_modules/effect/src/TSemaphore.ts b/backend/node_modules/effect/src/TSemaphore.ts new file mode 100644 index 0000000000000000000000000000000000000000..ee22a7f6bfdf7872a1746b1357b8e6b94cf6dc4f --- /dev/null +++ b/backend/node_modules/effect/src/TSemaphore.ts @@ -0,0 +1,169 @@ +/** + * @since 2.0.0 + */ + +import type * as Effect from "./Effect.js" +import * as internal from "./internal/stm/tSemaphore.js" +import type * as Scope from "./Scope.js" +import type * as STM from "./STM.js" +import type * as TRef from "./TRef.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const TSemaphoreTypeId: unique symbol = internal.TSemaphoreTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type TSemaphoreTypeId = typeof TSemaphoreTypeId + +/** + * @since 2.0.0 + * @category models + */ +export interface TSemaphore extends TSemaphore.Proto {} +/** + * @internal + * @since 2.0.0 + */ +export interface TSemaphore { + /** @internal */ + readonly permits: TRef.TRef +} + +/** + * @since 2.0.0 + */ +export declare namespace TSemaphore { + /** + * @since 2.0.0 + * @category models + */ + export interface Proto { + readonly [TSemaphoreTypeId]: TSemaphoreTypeId + } +} + +/** + * @since 2.0.0 + * @category mutations + */ +export const acquire: (self: TSemaphore) => STM.STM = internal.acquire + +/** + * @since 2.0.0 + * @category mutations + */ +export const acquireN: { + /** + * @since 2.0.0 + * @category mutations + */ + (n: number): (self: TSemaphore) => STM.STM + /** + * @since 2.0.0 + * @category mutations + */ + (self: TSemaphore, n: number): STM.STM +} = internal.acquireN + +/** + * @since 2.0.0 + * @category getters + */ +export const available: (self: TSemaphore) => STM.STM = internal.available + +/** + * @since 2.0.0 + * @category constructors + */ +export const make: (permits: number) => STM.STM = internal.make + +/** + * @since 2.0.0 + * @category mutations + */ +export const release: (self: TSemaphore) => STM.STM = internal.release + +/** + * @since 2.0.0 + * @category mutations + */ +export const releaseN: { + /** + * @since 2.0.0 + * @category mutations + */ + (n: number): (self: TSemaphore) => STM.STM + /** + * @since 2.0.0 + * @category mutations + */ + (self: TSemaphore, n: number): STM.STM +} = internal.releaseN + +/** + * @since 2.0.0 + * @category mutations + */ +export const withPermit: { + /** + * @since 2.0.0 + * @category mutations + */ + (semaphore: TSemaphore): (self: Effect.Effect) => Effect.Effect + /** + * @since 2.0.0 + * @category mutations + */ + (self: Effect.Effect, semaphore: TSemaphore): Effect.Effect +} = internal.withPermit + +/** + * @since 2.0.0 + * @category mutations + */ +export const withPermits: { + /** + * @since 2.0.0 + * @category mutations + */ + (semaphore: TSemaphore, permits: number): (self: Effect.Effect) => Effect.Effect + /** + * @since 2.0.0 + * @category mutations + */ + (self: Effect.Effect, semaphore: TSemaphore, permits: number): Effect.Effect +} = internal.withPermits + +/** + * @since 2.0.0 + * @category mutations + */ +export const withPermitScoped: (self: TSemaphore) => Effect.Effect = internal.withPermitScoped + +/** + * @since 2.0.0 + * @category mutations + */ +export const withPermitsScoped: { + /** + * @since 2.0.0 + * @category mutations + */ + (permits: number): (self: TSemaphore) => Effect.Effect + /** + * @since 2.0.0 + * @category mutations + */ + (self: TSemaphore, permits: number): Effect.Effect +} = internal.withPermitsScoped + +/** + * @since 2.0.0 + * @category unsafe + */ +export const unsafeMake: (permits: number) => TSemaphore = internal.unsafeMakeSemaphore diff --git a/backend/node_modules/effect/src/TSet.ts b/backend/node_modules/effect/src/TSet.ts new file mode 100644 index 0000000000000000000000000000000000000000..bb593256fb2e8b1a759cc9c5ded54645be61cb3d --- /dev/null +++ b/backend/node_modules/effect/src/TSet.ts @@ -0,0 +1,633 @@ +/** + * @since 2.0.0 + */ +import type * as Chunk from "./Chunk.js" +import type * as HashSet from "./HashSet.js" +import * as internal from "./internal/stm/tSet.js" +import type * as Option from "./Option.js" +import type { Predicate } from "./Predicate.js" +import type * as STM from "./STM.js" +import type * as TMap from "./TMap.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const TSetTypeId: unique symbol = internal.TSetTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type TSetTypeId = typeof TSetTypeId + +/** + * Transactional set implemented on top of `TMap`. + * + * @since 2.0.0 + * @category models + */ +export interface TSet extends TSet.Variance {} +/** + * @internal + * @since 2.0.0 + */ +export interface TSet { + /** @internal */ + readonly tMap: TMap.TMap +} + +/** + * @since 2.0.0 + */ +export declare namespace TSet { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [TSetTypeId]: { + readonly _A: Types.Invariant + } + } +} + +/** + * Stores new element in the set. + * + * @since 2.0.0 + * @category mutations + */ +export const add: { + /** + * Stores new element in the set. + * + * @since 2.0.0 + * @category mutations + */ + (value: A): (self: TSet) => STM.STM + /** + * Stores new element in the set. + * + * @since 2.0.0 + * @category mutations + */ + (self: TSet, value: A): STM.STM +} = internal.add + +/** + * Atomically transforms the set into the difference of itself and the + * provided set. + * + * @since 2.0.0 + * @category mutations + */ +export const difference: { + /** + * Atomically transforms the set into the difference of itself and the + * provided set. + * + * @since 2.0.0 + * @category mutations + */ + (other: TSet): (self: TSet) => STM.STM + /** + * Atomically transforms the set into the difference of itself and the + * provided set. + * + * @since 2.0.0 + * @category mutations + */ + (self: TSet, other: TSet): STM.STM +} = internal.difference + +/** + * Makes an empty `TSet`. + * + * @since 2.0.0 + * @category constructors + */ +export const empty: () => STM.STM> = internal.empty + +/** + * Atomically performs transactional-effect for each element in set. + * + * @since 2.0.0 + * @category elements + */ +export const forEach: { + /** + * Atomically performs transactional-effect for each element in set. + * + * @since 2.0.0 + * @category elements + */ + (f: (value: A) => STM.STM): (self: TSet) => STM.STM + /** + * Atomically performs transactional-effect for each element in set. + * + * @since 2.0.0 + * @category elements + */ + (self: TSet, f: (value: A) => STM.STM): STM.STM +} = internal.forEach + +/** + * Creates a new `TSet` from an iterable collection of values. + * + * @since 2.0.0 + * @category constructors + */ +export const fromIterable: (iterable: Iterable) => STM.STM> = internal.fromIterable + +/** + * Tests whether or not set contains an element. + * + * @since 2.0.0 + * @category elements + */ +export const has: { + /** + * Tests whether or not set contains an element. + * + * @since 2.0.0 + * @category elements + */ + (value: A): (self: TSet) => STM.STM + /** + * Tests whether or not set contains an element. + * + * @since 2.0.0 + * @category elements + */ + (self: TSet, value: A): STM.STM +} = internal.has + +/** + * Atomically transforms the set into the intersection of itself and the + * provided set. + * + * @since 2.0.0 + * @category mutations + */ +export const intersection: { + /** + * Atomically transforms the set into the intersection of itself and the + * provided set. + * + * @since 2.0.0 + * @category mutations + */ + (other: TSet): (self: TSet) => STM.STM + /** + * Atomically transforms the set into the intersection of itself and the + * provided set. + * + * @since 2.0.0 + * @category mutations + */ + (self: TSet, other: TSet): STM.STM +} = internal.intersection + +/** + * Tests if the set is empty or not + * + * @since 2.0.0 + * @category getters + */ +export const isEmpty: (self: TSet) => STM.STM = internal.isEmpty + +/** + * Makes a new `TSet` that is initialized with specified values. + * + * @since 2.0.0 + * @category constructors + */ +export const make: >( + ...elements: Elements +) => STM.STM> = internal.make + +/** + * Atomically folds using a pure function. + * + * @since 2.0.0 + * @category folding + */ +export const reduce: { + /** + * Atomically folds using a pure function. + * + * @since 2.0.0 + * @category folding + */ + (zero: Z, f: (accumulator: Z, value: A) => Z): (self: TSet) => STM.STM + /** + * Atomically folds using a pure function. + * + * @since 2.0.0 + * @category folding + */ + (self: TSet, zero: Z, f: (accumulator: Z, value: A) => Z): STM.STM +} = internal.reduce + +/** + * Atomically folds using a transactional function. + * + * @since 2.0.0 + * @category folding + */ +export const reduceSTM: { + /** + * Atomically folds using a transactional function. + * + * @since 2.0.0 + * @category folding + */ + (zero: Z, f: (accumulator: Z, value: A) => STM.STM): (self: TSet) => STM.STM + /** + * Atomically folds using a transactional function. + * + * @since 2.0.0 + * @category folding + */ + (self: TSet, zero: Z, f: (accumulator: Z, value: A) => STM.STM): STM.STM +} = internal.reduceSTM + +/** + * Removes a single element from the set. + * + * @since 2.0.0 + * @category mutations + */ +export const remove: { + /** + * Removes a single element from the set. + * + * @since 2.0.0 + * @category mutations + */ + (value: A): (self: TSet) => STM.STM + /** + * Removes a single element from the set. + * + * @since 2.0.0 + * @category mutations + */ + (self: TSet, value: A): STM.STM +} = internal.remove + +/** + * Removes elements from the set. + * + * @since 2.0.0 + * @category mutations + */ +export const removeAll: { + /** + * Removes elements from the set. + * + * @since 2.0.0 + * @category mutations + */ + (iterable: Iterable): (self: TSet) => STM.STM + /** + * Removes elements from the set. + * + * @since 2.0.0 + * @category mutations + */ + (self: TSet, iterable: Iterable): STM.STM +} = internal.removeAll + +/** + * Removes entries from a `TSet` that satisfy the specified predicate and returns the removed entries + * (or `void` if `discard = true`). + * + * @since 2.0.0 + * @category mutations + */ +export const removeIf: { + /** + * Removes entries from a `TSet` that satisfy the specified predicate and returns the removed entries + * (or `void` if `discard = true`). + * + * @since 2.0.0 + * @category mutations + */ + ( + predicate: Predicate, + options: { + readonly discard: true + } + ): (self: TSet) => STM.STM + /** + * Removes entries from a `TSet` that satisfy the specified predicate and returns the removed entries + * (or `void` if `discard = true`). + * + * @since 2.0.0 + * @category mutations + */ + ( + predicate: Predicate, + options?: { + readonly discard: false + } + ): (self: TSet) => STM.STM> + /** + * Removes entries from a `TSet` that satisfy the specified predicate and returns the removed entries + * (or `void` if `discard = true`). + * + * @since 2.0.0 + * @category mutations + */ + ( + self: TSet, + predicate: Predicate, + options: { + readonly discard: true + } + ): STM.STM + /** + * Removes entries from a `TSet` that satisfy the specified predicate and returns the removed entries + * (or `void` if `discard = true`). + * + * @since 2.0.0 + * @category mutations + */ + ( + self: TSet, + predicate: Predicate, + options?: { + readonly discard: false + } + ): STM.STM> +} = internal.removeIf + +/** + * Retains entries in a `TSet` that satisfy the specified predicate and returns the removed entries + * (or `void` if `discard = true`). + * + * @since 2.0.0 + * @category mutations + */ +export const retainIf: { + /** + * Retains entries in a `TSet` that satisfy the specified predicate and returns the removed entries + * (or `void` if `discard = true`). + * + * @since 2.0.0 + * @category mutations + */ + ( + predicate: Predicate, + options: { + readonly discard: true + } + ): (self: TSet) => STM.STM + /** + * Retains entries in a `TSet` that satisfy the specified predicate and returns the removed entries + * (or `void` if `discard = true`). + * + * @since 2.0.0 + * @category mutations + */ + ( + predicate: Predicate, + options?: { + readonly discard: false + } + ): (self: TSet) => STM.STM> + /** + * Retains entries in a `TSet` that satisfy the specified predicate and returns the removed entries + * (or `void` if `discard = true`). + * + * @since 2.0.0 + * @category mutations + */ + ( + self: TSet, + predicate: Predicate, + options: { + readonly discard: true + } + ): STM.STM + /** + * Retains entries in a `TSet` that satisfy the specified predicate and returns the removed entries + * (or `void` if `discard = true`). + * + * @since 2.0.0 + * @category mutations + */ + ( + self: TSet, + predicate: Predicate, + options?: { + readonly discard: false + } + ): STM.STM> +} = internal.retainIf + +/** + * Returns the set's cardinality. + * + * @since 2.0.0 + * @category getters + */ +export const size: (self: TSet) => STM.STM = internal.size + +/** + * Takes the first matching value, or retries until there is one. + * + * @since 2.0.0 + * @category mutations + */ +export const takeFirst: { + /** + * Takes the first matching value, or retries until there is one. + * + * @since 2.0.0 + * @category mutations + */ + (pf: (a: A) => Option.Option): (self: TSet) => STM.STM + /** + * Takes the first matching value, or retries until there is one. + * + * @since 2.0.0 + * @category mutations + */ + (self: TSet, pf: (a: A) => Option.Option): STM.STM +} = internal.takeFirst + +/** + * Takes the first matching value, or retries until there is one. + * + * @since 2.0.0 + * @category mutations + */ +export const takeFirstSTM: { + /** + * Takes the first matching value, or retries until there is one. + * + * @since 2.0.0 + * @category mutations + */ + (pf: (a: A) => STM.STM, R>): (self: TSet) => STM.STM + /** + * Takes the first matching value, or retries until there is one. + * + * @since 2.0.0 + * @category mutations + */ + (self: TSet, pf: (a: A) => STM.STM, R>): STM.STM +} = internal.takeFirstSTM + +/** + * Takes all matching values, or retries until there is at least one. + * + * @since 2.0.0 + * @category mutations + */ +export const takeSome: { + /** + * Takes all matching values, or retries until there is at least one. + * + * @since 2.0.0 + * @category mutations + */ + (pf: (a: A) => Option.Option): (self: TSet) => STM.STM<[B, ...Array]> + /** + * Takes all matching values, or retries until there is at least one. + * + * @since 2.0.0 + * @category mutations + */ + (self: TSet, pf: (a: A) => Option.Option): STM.STM<[B, ...Array]> +} = internal.takeSome + +/** + * Takes all matching values, or retries until there is at least one. + * + * @since 2.0.0 + * @category mutations + */ +export const takeSomeSTM: { + /** + * Takes all matching values, or retries until there is at least one. + * + * @since 2.0.0 + * @category mutations + */ + (pf: (a: A) => STM.STM, R>): (self: TSet) => STM.STM<[B, ...Array], E, R> + /** + * Takes all matching values, or retries until there is at least one. + * + * @since 2.0.0 + * @category mutations + */ + (self: TSet, pf: (a: A) => STM.STM, R>): STM.STM<[B, ...Array], E, R> +} = internal.takeSomeSTM + +/** + * Collects all elements into a `Chunk`. + * + * @since 2.0.0 + * @category destructors + */ +export const toChunk: (self: TSet) => STM.STM> = internal.toChunk + +/** + * Collects all elements into a `HashSet`. + * + * @since 2.0.0 + * @category destructors + */ +export const toHashSet: (self: TSet) => STM.STM> = internal.toHashSet + +/** + * Collects all elements into a `Array`. + * + * @since 2.0.0 + * @category destructors + */ +export const toArray: (self: TSet) => STM.STM> = internal.toArray + +/** + * Collects all elements into a `ReadonlySet`. + * + * @since 2.0.0 + * @category destructors + */ +export const toReadonlySet: (self: TSet) => STM.STM> = internal.toReadonlySet + +/** + * Atomically updates all elements using a pure function. + * + * @since 2.0.0 + * @category mutations + */ +export const transform: { + /** + * Atomically updates all elements using a pure function. + * + * @since 2.0.0 + * @category mutations + */ + (f: (a: A) => A): (self: TSet) => STM.STM + /** + * Atomically updates all elements using a pure function. + * + * @since 2.0.0 + * @category mutations + */ + (self: TSet, f: (a: A) => A): STM.STM +} = internal.transform + +/** + * Atomically updates all elements using a transactional function. + * + * @since 2.0.0 + * @category mutations + */ +export const transformSTM: { + /** + * Atomically updates all elements using a transactional function. + * + * @since 2.0.0 + * @category mutations + */ + (f: (a: A) => STM.STM): (self: TSet) => STM.STM + /** + * Atomically updates all elements using a transactional function. + * + * @since 2.0.0 + * @category mutations + */ + (self: TSet, f: (a: A) => STM.STM): STM.STM +} = internal.transformSTM + +/** + * Atomically transforms the set into the union of itself and the provided + * set. + * + * @since 2.0.0 + * @category mutations + */ +export const union: { + /** + * Atomically transforms the set into the union of itself and the provided + * set. + * + * @since 2.0.0 + * @category mutations + */ + (other: TSet): (self: TSet) => STM.STM + /** + * Atomically transforms the set into the union of itself and the provided + * set. + * + * @since 2.0.0 + * @category mutations + */ + (self: TSet, other: TSet): STM.STM +} = internal.union diff --git a/backend/node_modules/effect/src/Take.ts b/backend/node_modules/effect/src/Take.ts new file mode 100644 index 0000000000000000000000000000000000000000..83ae502b3475a56b3649d7fac288350a8f568dd0 --- /dev/null +++ b/backend/node_modules/effect/src/Take.ts @@ -0,0 +1,309 @@ +/** + * @since 2.0.0 + */ +import type * as Cause from "./Cause.js" +import type * as Chunk from "./Chunk.js" +import type * as Effect from "./Effect.js" +import type * as Exit from "./Exit.js" +import * as internal from "./internal/take.js" +import type * as Option from "./Option.js" +import type { Pipeable } from "./Pipeable.js" +import type * as Types from "./Types.js" + +/** + * @since 2.0.0 + * @category symbols + */ +export const TakeTypeId: unique symbol = internal.TakeTypeId + +/** + * @since 2.0.0 + * @category symbols + */ +export type TakeTypeId = typeof TakeTypeId + +/** + * A `Take` represents a single `take` from a queue modeling a stream of + * values. A `Take` may be a failure cause `Cause`, a chunk value `Chunk`, + * or an end-of-stream marker. + * + * @since 2.0.0 + * @category models + */ +export interface Take extends Take.Variance, Pipeable { + /** @internal */ + readonly exit: Exit.Exit, Option.Option> +} + +/** + * @since 2.0.0 + */ +export declare namespace Take { + /** + * @since 2.0.0 + * @category models + */ + export interface Variance { + readonly [TakeTypeId]: { + readonly _A: Types.Covariant + readonly _E: Types.Covariant + } + } +} + +/** + * Creates a `Take` with the specified chunk. + * + * @since 2.0.0 + * @category constructors + */ +export const chunk: (chunk: Chunk.Chunk) => Take = internal.chunk + +/** + * Creates a failing `Take` with the specified defect. + * + * @since 2.0.0 + * @category constructors + */ +export const die: (defect: unknown) => Take = internal.die + +/** + * Creates a failing `Take` with the specified error message. + * + * @since 2.0.0 + * @category constructors + */ +export const dieMessage: (message: string) => Take = internal.dieMessage + +/** + * Transforms a `Take` to an `Effect`. + * + * @since 2.0.0 + * @category destructors + */ +export const done: (self: Take) => Effect.Effect, Option.Option> = internal.done + +/** + * Represents the end-of-stream marker. + * + * @since 2.0.0 + * @category constructors + */ +export const end: Take = internal.end + +/** + * Creates a failing `Take` with the specified error. + * + * @since 2.0.0 + * @category constructors + */ +export const fail: (error: E) => Take = internal.fail + +/** + * Creates a failing `Take` with the specified cause. + * + * @since 2.0.0 + * @category constructors + */ +export const failCause: (cause: Cause.Cause) => Take = internal.failCause + +/** + * Creates an effect from `Effect` that does not fail, but succeeds with + * the `Take`. Error from stream when pulling is converted to + * `Take.failCause`. Creates a single value chunk. + * + * @since 2.0.0 + * @category constructors + */ +export const fromEffect: (effect: Effect.Effect) => Effect.Effect, never, R> = + internal.fromEffect + +/** + * Creates a `Take` from an `Exit`. + * + * @since 2.0.0 + * @category constructors + */ +export const fromExit: (exit: Exit.Exit) => Take = internal.fromExit + +/** + * Creates effect from `Effect, Option, R>` that does not fail, but + * succeeds with the `Take`. Errors from stream when pulling are converted + * to `Take.failCause`, and the end-of-stream is converted to `Take.end`. + * + * @since 2.0.0 + * @category constructors + */ +export const fromPull: ( + pull: Effect.Effect, Option.Option, R> +) => Effect.Effect, never, R> = internal.fromPull + +/** + * Checks if this `take` is done (`Take.end`). + * + * @since 2.0.0 + * @category getters + */ +export const isDone: (self: Take) => boolean = internal.isDone + +/** + * Checks if this `take` is a failure. + * + * @since 2.0.0 + * @category getters + */ +export const isFailure: (self: Take) => boolean = internal.isFailure + +/** + * Checks if this `take` is a success. + * + * @since 2.0.0 + * @category getters + */ +export const isSuccess: (self: Take) => boolean = internal.isSuccess + +/** + * Constructs a `Take`. + * + * @since 2.0.0 + * @category constructors + */ +export const make: (exit: Exit.Exit, Option.Option>) => Take = internal.make + +/** + * Transforms `Take` to `Take` by applying function `f`. + * + * @since 2.0.0 + * @category mapping + */ +export const map: { + /** + * Transforms `Take` to `Take` by applying function `f`. + * + * @since 2.0.0 + * @category mapping + */ + (f: (a: A) => B): (self: Take) => Take + /** + * Transforms `Take` to `Take` by applying function `f`. + * + * @since 2.0.0 + * @category mapping + */ + (self: Take, f: (a: A) => B): Take +} = internal.map + +/** + * Folds over the failure cause, success value and end-of-stream marker to + * yield a value. + * + * @since 2.0.0 + * @category destructors + */ +export const match: { + /** + * Folds over the failure cause, success value and end-of-stream marker to + * yield a value. + * + * @since 2.0.0 + * @category destructors + */ + ( + options: { + readonly onEnd: () => Z + readonly onFailure: (cause: Cause.Cause) => Z2 + readonly onSuccess: (chunk: Chunk.Chunk) => Z3 + } + ): (self: Take) => Z | Z2 | Z3 + /** + * Folds over the failure cause, success value and end-of-stream marker to + * yield a value. + * + * @since 2.0.0 + * @category destructors + */ + ( + self: Take, + options: { + readonly onEnd: () => Z + readonly onFailure: (cause: Cause.Cause) => Z2 + readonly onSuccess: (chunk: Chunk.Chunk) => Z3 + } + ): Z | Z2 | Z3 +} = internal.match + +/** + * Effectful version of `Take.fold`. + * + * Folds over the failure cause, success value and end-of-stream marker to + * yield an effect. + * + * @since 2.0.0 + * @category destructors + */ +export const matchEffect: { + /** + * Effectful version of `Take.fold`. + * + * Folds over the failure cause, success value and end-of-stream marker to + * yield an effect. + * + * @since 2.0.0 + * @category destructors + */ + ( + options: { + readonly onEnd: Effect.Effect + readonly onFailure: (cause: Cause.Cause) => Effect.Effect + readonly onSuccess: (chunk: Chunk.Chunk) => Effect.Effect + } + ): (self: Take) => Effect.Effect + /** + * Effectful version of `Take.fold`. + * + * Folds over the failure cause, success value and end-of-stream marker to + * yield an effect. + * + * @since 2.0.0 + * @category destructors + */ + ( + self: Take, + options: { + readonly onEnd: Effect.Effect + readonly onFailure: (cause: Cause.Cause) => Effect.Effect + readonly onSuccess: (chunk: Chunk.Chunk) => Effect.Effect + } + ): Effect.Effect +} = internal.matchEffect + +/** + * Creates a `Take` with a single value chunk. + * + * @since 2.0.0 + * @category constructors + */ +export const of: (value: A) => Take = internal.of + +/** + * Returns an effect that effectfully "peeks" at the success of this take. + * + * @since 2.0.0 + * @category sequencing + */ +export const tap: { + /** + * Returns an effect that effectfully "peeks" at the success of this take. + * + * @since 2.0.0 + * @category sequencing + */ + (f: (chunk: Chunk.Chunk) => Effect.Effect): (self: Take) => Effect.Effect + /** + * Returns an effect that effectfully "peeks" at the success of this take. + * + * @since 2.0.0 + * @category sequencing + */ + (self: Take, f: (chunk: Chunk.Chunk) => Effect.Effect): Effect.Effect +} = internal.tap diff --git a/backend/node_modules/effect/src/TestAnnotationMap.ts b/backend/node_modules/effect/src/TestAnnotationMap.ts new file mode 100644 index 0000000000000000000000000000000000000000..07fd40974fc89f2af956a799dd6c5b15f869ee97 --- /dev/null +++ b/backend/node_modules/effect/src/TestAnnotationMap.ts @@ -0,0 +1,163 @@ +/** + * @since 2.0.0 + */ +import { dual } from "./Function.js" +import * as HashMap from "./HashMap.js" +import { hasProperty } from "./Predicate.js" +import type * as TestAnnotation from "./TestAnnotation.js" + +/** + * @since 2.0.0 + */ +export const TestAnnotationMapTypeId: unique symbol = Symbol.for("effect/TestAnnotationMap") + +/** + * @since 2.0.0 + */ +export type TestAnnotationMapTypeId = typeof TestAnnotationMapTypeId + +/** + * An annotation map keeps track of annotations of different types. + * + * @since 2.0.0 + */ +export interface TestAnnotationMap { + readonly [TestAnnotationMapTypeId]: TestAnnotationMapTypeId + /** @internal */ + readonly map: HashMap.HashMap, any> +} + +/** @internal */ +class TestAnnotationMapImpl implements TestAnnotationMap { + readonly [TestAnnotationMapTypeId]: TestAnnotationMapTypeId = TestAnnotationMapTypeId + constructor(readonly map: HashMap.HashMap, any>) { + } +} + +/** + * @since 2.0.0 + */ +export const isTestAnnotationMap = (u: unknown): u is TestAnnotationMap => hasProperty(u, TestAnnotationMapTypeId) + +/** + * @since 2.0.0 + */ +export const empty: (_: void) => TestAnnotationMap = () => new TestAnnotationMapImpl(HashMap.empty()) + +/** + * @since 2.0.0 + */ +export const make = (map: HashMap.HashMap, any>): TestAnnotationMap => { + return new TestAnnotationMapImpl(map) +} + +/** + * @since 2.0.0 + */ +export const overwrite = dual< + /** + * @since 2.0.0 + */ + (key: TestAnnotation.TestAnnotation, value: A) => (self: TestAnnotationMap) => TestAnnotationMap, + /** + * @since 2.0.0 + */ + (self: TestAnnotationMap, key: TestAnnotation.TestAnnotation, value: A) => TestAnnotationMap +>(3, (self, key, value) => make(HashMap.set(self.map, key, value))) + +/** + * @since 2.0.0 + */ +export const update = dual< + /** + * @since 2.0.0 + */ + (key: TestAnnotation.TestAnnotation, f: (value: A) => A) => (self: TestAnnotationMap) => TestAnnotationMap, + /** + * @since 2.0.0 + */ + ( + self: TestAnnotationMap, + key: TestAnnotation.TestAnnotation, + f: (value: A) => A + ) => TestAnnotationMap +>(3, (self: TestAnnotationMap, key: TestAnnotation.TestAnnotation, f: (value: A) => A) => { + let value = key.initial + if (HashMap.has(self.map, key)) { + value = HashMap.unsafeGet(self.map, key) as A + } + return overwrite(self, key, f(value)) +}) + +/** + * Retrieves the annotation of the specified type, or its default value if + * there is none. + * + * @since 2.0.0 + */ +export const get = dual< + /** + * Retrieves the annotation of the specified type, or its default value if + * there is none. + * + * @since 2.0.0 + */ + (key: TestAnnotation.TestAnnotation) => (self: TestAnnotationMap) => A, + /** + * Retrieves the annotation of the specified type, or its default value if + * there is none. + * + * @since 2.0.0 + */ + (self: TestAnnotationMap, key: TestAnnotation.TestAnnotation) => A +>(2, (self: TestAnnotationMap, key: TestAnnotation.TestAnnotation) => { + if (HashMap.has(self.map, key)) { + return HashMap.unsafeGet(self.map, key) as A + } + return key.initial +}) + +/** + * Appends the specified annotation to the annotation map. + * + * @since 2.0.0 + */ +export const annotate = dual< + /** + * Appends the specified annotation to the annotation map. + * + * @since 2.0.0 + */ + (key: TestAnnotation.TestAnnotation, value: A) => (self: TestAnnotationMap) => TestAnnotationMap, + /** + * Appends the specified annotation to the annotation map. + * + * @since 2.0.0 + */ + (self: TestAnnotationMap, key: TestAnnotation.TestAnnotation, value: A) => TestAnnotationMap +>(3, (self, key, value) => update(self, key, (_) => key.combine(_, value))) + +/** + * @since 2.0.0 + */ +export const combine = dual< + /** + * @since 2.0.0 + */ + (that: TestAnnotationMap) => (self: TestAnnotationMap) => TestAnnotationMap, + /** + * @since 2.0.0 + */ + (self: TestAnnotationMap, that: TestAnnotationMap) => TestAnnotationMap +>(2, (self, that) => { + let result = self.map + for (const entry of that.map) { + if (HashMap.has(result, entry[0])) { + const value = HashMap.get(result, entry[0])! + result = HashMap.set(result, entry[0], entry[0].combine(value, entry[1])) + } else { + result = HashMap.set(result, entry[0], entry[1]) + } + } + return make(result) +}) diff --git a/backend/node_modules/effect/src/TestAnnotations.ts b/backend/node_modules/effect/src/TestAnnotations.ts new file mode 100644 index 0000000000000000000000000000000000000000..745ca7c107a8bf628f9ad034b327c23f6c856544 --- /dev/null +++ b/backend/node_modules/effect/src/TestAnnotations.ts @@ -0,0 +1,117 @@ +/** + * @since 2.0.0 + */ +import * as RA from "./Array.js" +import * as Context from "./Context.js" +import type * as Effect from "./Effect.js" +import * as Equal from "./Equal.js" +import type * as Fiber from "./Fiber.js" +import { pipe } from "./Function.js" +import * as effect from "./internal/core-effect.js" +import * as core from "./internal/core.js" +import * as fiber from "./internal/fiber.js" +import * as MutableRef from "./MutableRef.js" +import { hasProperty } from "./Predicate.js" +import * as Ref from "./Ref.js" +import * as SortedSet from "./SortedSet.js" +import * as TestAnnotation from "./TestAnnotation.js" +import * as TestAnnotationMap from "./TestAnnotationMap.js" + +/** + * @since 2.0.0 + */ +export const TestAnnotationsTypeId: unique symbol = Symbol.for("effect/TestAnnotations") + +/** + * @since 2.0.0 + */ +export type TestAnnotationsTypeId = typeof TestAnnotationsTypeId + +/** + * The `Annotations` trait provides access to an annotation map that tests can + * add arbitrary annotations to. Each annotation consists of a string + * identifier, an initial value, and a function for combining two values. + * Annotations form monoids and you can think of `Annotations` as a more + * structured logging service or as a super polymorphic version of the writer + * monad effect. + * + * @since 2.0.0 + */ +export interface TestAnnotations { + readonly [TestAnnotationsTypeId]: TestAnnotationsTypeId + + /** + * A ref containing the bacnking map for all annotations + */ + readonly ref: Ref.Ref + + /** + * Accesses an `Annotations` instance in the context and retrieves the + * annotation of the specified type, or its default value if there is none. + */ + get(key: TestAnnotation.TestAnnotation): Effect.Effect + + /** + * Accesses an `Annotations` instance in the context and appends the + * specified annotation to the annotation map. + */ + annotate(key: TestAnnotation.TestAnnotation, value: A): Effect.Effect + + /** + * Returns the set of all fibers in this test. + */ + readonly supervisedFibers: Effect.Effect< + SortedSet.SortedSet> + > +} + +/** @internal */ +class AnnotationsImpl implements TestAnnotations { + readonly [TestAnnotationsTypeId]: TestAnnotationsTypeId = TestAnnotationsTypeId + constructor(readonly ref: Ref.Ref) { + } + get(key: TestAnnotation.TestAnnotation): Effect.Effect { + return core.map(Ref.get(this.ref), TestAnnotationMap.get(key)) + } + annotate(key: TestAnnotation.TestAnnotation, value: A): Effect.Effect { + return Ref.update(this.ref, TestAnnotationMap.annotate(key, value)) + } + get supervisedFibers(): Effect.Effect>> { + return effect.descriptorWith((descriptor) => + core.flatMap(this.get(TestAnnotation.fibers), (either) => { + switch (either._tag) { + case "Left": { + return core.succeed(SortedSet.empty(fiber.Order)) + } + case "Right": { + return pipe( + either.right, + core.forEachSequential((ref) => core.sync(() => MutableRef.get(ref))), + core.map(RA.reduce(SortedSet.empty(fiber.Order), (a, b) => SortedSet.union(a, b))), + core.map(SortedSet.filter((fiber) => !Equal.equals(fiber.id(), descriptor.id))) + ) + } + } + }) + ) + } +} + +/** + * @since 2.0.0 + */ +export const TestAnnotations: Context.Tag = Context.GenericTag( + "effect/Annotations" +) + +/** + * @since 2.0.0 + */ +export const isTestAnnotations = (u: unknown): u is TestAnnotations => hasProperty(u, TestAnnotationsTypeId) + +/** + * @since 2.0.0 + */ +export const make = ( + ref: Ref.Ref +): TestAnnotations => new AnnotationsImpl(ref) diff --git a/backend/node_modules/effect/src/TestClock.ts b/backend/node_modules/effect/src/TestClock.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b71447dc5dd34c6d6acb08887f0051ea403ace4 --- /dev/null +++ b/backend/node_modules/effect/src/TestClock.ts @@ -0,0 +1,562 @@ +/** + * @since 2.0.0 + */ +import * as Chunk from "./Chunk.js" +import type * as Clock from "./Clock.js" +import * as Context from "./Context.js" +import * as DateTime from "./DateTime.js" +import type * as Deferred from "./Deferred.js" +import * as Duration from "./Duration.js" +import type * as Effect from "./Effect.js" +import * as Equal from "./Equal.js" +import type * as Fiber from "./Fiber.js" +import type * as FiberId from "./FiberId.js" +import * as FiberStatus from "./FiberStatus.js" +import { constVoid, dual, identity, pipe } from "./Function.js" +import * as HashMap from "./HashMap.js" +import * as clock from "./internal/clock.js" +import * as effect from "./internal/core-effect.js" +import * as core from "./internal/core.js" +import * as defaultServices from "./internal/defaultServices.js" +import * as circular from "./internal/effect/circular.js" +import * as fiberRuntime from "./internal/fiberRuntime.js" +import * as layer from "./internal/layer.js" +import * as ref from "./internal/ref.js" +import * as synchronized from "./internal/synchronizedRef.js" +import * as SuspendedWarningData from "./internal/testing/suspendedWarningData.js" +import * as WarningData from "./internal/testing/warningData.js" +import type * as Layer from "./Layer.js" +import * as number from "./Number.js" +import * as Option from "./Option.js" +import * as Order from "./Order.js" +import type * as Ref from "./Ref.js" +import type * as SortedSet from "./SortedSet.js" +import type * as Synchronized from "./SynchronizedRef.js" +import * as Annotations from "./TestAnnotations.js" +import * as Live from "./TestLive.js" + +/** + * A `TestClock` makes it easy to deterministically and efficiently test effects + * involving the passage of time. + * + * Instead of waiting for actual time to pass, `sleep` and methods implemented + * in terms of it schedule effects to take place at a given clock time. Users + * can adjust the clock time using the `adjust` and `setTime` methods, and all + * effects scheduled to take place on or before that time will automatically be + * run in order. + * + * For example, here is how we can test `Effect.timeout` using `TestClock`: + * + * ```ts + * import * as assert from "node:assert" + * import { Duration, Effect, Fiber, TestClock, Option, pipe } from "effect" + * + * Effect.gen(function*() { + * const fiber = yield* pipe( + * Effect.sleep(Duration.minutes(5)), + * Effect.timeout(Duration.minutes(1)), + * Effect.fork + * ) + * yield* TestClock.adjust(Duration.minutes(1)) + * const result = yield* Fiber.join(fiber) + * assert.deepStrictEqual(result, Option.none()) + * }) + * ``` + * + * Note how we forked the fiber that `sleep` was invoked on. Calls to `sleep` + * and methods derived from it will semantically block until the time is set to + * on or after the time they are scheduled to run. If we didn't fork the fiber + * on which we called sleep we would never get to set the time on the line + * below. Thus, a useful pattern when using `TestClock` is to fork the effect + * being tested, then adjust the clock time, and finally verify that the + * expected effects have been performed. + * + * @since 2.0.0 + */ +export interface TestClock extends Clock.Clock { + adjust(duration: Duration.DurationInput): Effect.Effect + adjustWith(duration: Duration.DurationInput): (effect: Effect.Effect) => Effect.Effect + readonly save: Effect.Effect> + setTime(time: number): Effect.Effect + readonly sleeps: Effect.Effect> +} + +/** + * `Data` represents the state of the `TestClock`, including the clock time. + * + * @since 2.0.1 + */ +export interface Data { + readonly instant: number + readonly sleeps: Chunk.Chunk]> +} + +/** + * @since 2.0.0 + */ +export const makeData = ( + instant: number, + sleeps: Chunk.Chunk]> +): Data => ({ + instant, + sleeps +}) + +/** + * @since 2.0.0 + */ +export const TestClock: Context.Tag = Context.GenericTag("effect/TestClock") + +/** + * The warning message that will be displayed if a test is using time but is + * not advancing the `TestClock`. + * + * @internal + */ +const warning = "Warning: A test is using time, but is not advancing " + + "the test clock, which may result in the test hanging. Use TestClock.adjust to " + + "manually advance the time." + +/** + * The warning message that will be displayed if a test is advancing the clock + * but a fiber is still running. + * + * @internal + */ +const suspendedWarning = "Warning: A test is advancing the test clock, " + + "but a fiber is not suspending, which may result in the test hanging. Use " + + "TestAspect.diagnose to identity the fiber that is not suspending." + +/** @internal */ +export class TestClockImpl implements TestClock { + [clock.ClockTypeId]: Clock.ClockTypeId = clock.ClockTypeId + constructor( + readonly clockState: Ref.Ref, + readonly live: Live.TestLive, + readonly annotations: Annotations.TestAnnotations, + readonly warningState: Synchronized.SynchronizedRef, + readonly suspendedWarningState: Synchronized.SynchronizedRef + ) { + this.currentTimeMillis = core.map( + ref.get(this.clockState), + (data) => data.instant + ) + this.currentTimeNanos = core.map( + ref.get(this.clockState), + (data) => BigInt(data.instant * 1000000) + ) + } + + /** + * Unsafely returns the current time in milliseconds. + */ + unsafeCurrentTimeMillis(): number { + return ref.unsafeGet(this.clockState).instant + } + + /** + * Unsafely returns the current time in nanoseconds. + */ + unsafeCurrentTimeNanos(): bigint { + return BigInt(ref.unsafeGet(this.clockState).instant * 1000000) + } + + /** + * Returns the current clock time in milliseconds. + */ + currentTimeMillis: Effect.Effect + + /** + * Returns the current clock time in nanoseconds. + */ + currentTimeNanos: Effect.Effect + + /** + * Saves the `TestClock`'s current state in an effect which, when run, will + * restore the `TestClock` state to the saved state. + */ + get save(): Effect.Effect> { + return core.map(ref.get(this.clockState), (data) => ref.set(this.clockState, data)) + } + /** + * Sets the current clock time to the specified instant. Any effects that + * were scheduled to occur on or before the new time will be run in order. + */ + setTime(instant: number): Effect.Effect { + return core.zipRight(this.warningDone(), this.run(() => instant)) + } + /** + * Semantically blocks the current fiber until the clock time is equal to or + * greater than the specified duration. Once the clock time is adjusted to + * on or after the duration, the fiber will automatically be resumed. + */ + sleep(durationInput: Duration.DurationInput): Effect.Effect { + const duration = Duration.decode(durationInput) + return core.flatMap(core.deferredMake(), (deferred) => + pipe( + ref.modify(this.clockState, (data) => { + const end = data.instant + Duration.toMillis(duration) + if (end > data.instant) { + return [ + true, + makeData(data.instant, pipe(data.sleeps, Chunk.prepend([end, deferred] as const))) + ] as const + } + return [false, data] as const + }), + core.flatMap((shouldAwait) => + shouldAwait ? + pipe(this.warningStart(), core.zipRight(core.deferredAwait(deferred))) : + pipe(core.deferredSucceed(deferred, void 0), core.asVoid) + ) + )) + } + /** + * Returns a list of the times at which all queued effects are scheduled to + * resume. + */ + get sleeps(): Effect.Effect> { + return core.map( + ref.get(this.clockState), + (data) => Chunk.map(data.sleeps, (_) => _[0]) + ) + } + /** + * Increments the current clock time by the specified duration. Any effects + * that were scheduled to occur on or before the new time will be run in + * order. + */ + adjust(durationInput: Duration.DurationInput): Effect.Effect { + const duration = Duration.decode(durationInput) + return core.zipRight(this.warningDone(), this.run((n) => n + Duration.toMillis(duration))) + } + /** + * Increments the current clock time by the specified duration. Any effects + * that were scheduled to occur on or before the new time will be run in + * order. + */ + adjustWith(durationInput: Duration.DurationInput) { + const duration = Duration.decode(durationInput) + return (effect: Effect.Effect): Effect.Effect => + fiberRuntime.zipLeftOptions(effect, this.adjust(duration), { concurrent: true }) + } + /** + * Returns a set of all fibers in this test. + */ + supervisedFibers(): Effect.Effect>> { + return this.annotations.supervisedFibers + } + /** + * Captures a "snapshot" of the identifier and status of all fibers in this + * test other than the current fiber. Fails with the `void` value if any of + * these fibers are not done or suspended. Note that because we cannot + * synchronize on the status of multiple fibers at the same time this + * snapshot may not be fully consistent. + */ + freeze(): Effect.Effect, void> { + return core.flatMap(this.supervisedFibers(), (fibers) => + pipe( + fibers, + effect.reduce(HashMap.empty(), (map, fiber) => + pipe( + fiber.status, + core.flatMap((status) => { + if (FiberStatus.isDone(status)) { + return core.succeed(HashMap.set(map, fiber.id() as FiberId.FiberId, status as FiberStatus.FiberStatus)) + } + if (FiberStatus.isSuspended(status)) { + return core.succeed(HashMap.set(map, fiber.id() as FiberId.FiberId, status as FiberStatus.FiberStatus)) + } + return core.fail(void 0) + }) + )) + )) + } + /** + * Forks a fiber that will display a warning message if a test is using time + * but is not advancing the `TestClock`. + */ + warningStart(): Effect.Effect { + return synchronized.updateSomeEffect(this.warningState, (data) => + WarningData.isStart(data) ? + Option.some( + pipe( + this.live.provide( + pipe(effect.logWarning(warning), effect.delay(Duration.seconds(5))) + ), + core.interruptible, + fiberRuntime.fork, + core.map((fiber) => WarningData.pending(fiber)) + ) + ) : + Option.none()) + } + /** + * Cancels the warning message that is displayed if a test is using time but + * is not advancing the `TestClock`. + */ + warningDone(): Effect.Effect { + return synchronized.updateSomeEffect(this.warningState, (warningData) => { + if (WarningData.isStart(warningData)) { + return Option.some(core.succeed(WarningData.done)) + } + if (WarningData.isPending(warningData)) { + return Option.some(pipe(core.interruptFiber(warningData.fiber), core.as(WarningData.done))) + } + return Option.none() + }) + } + + private yieldTimer = core.async((resume) => { + const timer = setTimeout(() => { + resume(core.void) + }, 0) + return core.sync(() => clearTimeout(timer)) + }) + + /** + * Returns whether all descendants of this fiber are done or suspended. + */ + suspended(): Effect.Effect, void> { + return pipe( + this.freeze(), + core.zip(pipe(this.yieldTimer, core.zipRight(this.freeze()))), + core.flatMap(([first, last]) => + Equal.equals(first, last) ? + core.succeed(first) : + core.fail(void 0) + ) + ) + } + /** + * Polls until all descendants of this fiber are done or suspended. + */ + awaitSuspended(): Effect.Effect { + return pipe( + this.suspendedWarningStart(), + core.zipRight( + pipe( + this.suspended(), + core.zipWith( + pipe(this.yieldTimer, core.zipRight(this.suspended())), + Equal.equals + ), + effect.filterOrFail(identity, constVoid), + effect.eventually + ) + ), + core.zipRight(this.suspendedWarningDone()) + ) + } + /** + * Forks a fiber that will display a warning message if a test is advancing + * the `TestClock` but a fiber is not suspending. + */ + suspendedWarningStart(): Effect.Effect { + return synchronized.updateSomeEffect(this.suspendedWarningState, (suspendedWarningData) => { + if (SuspendedWarningData.isStart(suspendedWarningData)) { + return Option.some( + pipe( + this.live.provide( + pipe( + effect.logWarning(suspendedWarning), + core.zipRight(ref.set(this.suspendedWarningState, SuspendedWarningData.done)), + effect.delay(Duration.seconds(5)) + ) + ), + core.interruptible, + fiberRuntime.fork, + core.map((fiber) => SuspendedWarningData.pending(fiber)) + ) + ) + } + return Option.none() + }) + } + /** + * Cancels the warning message that is displayed if a test is advancing the + * `TestClock` but a fiber is not suspending. + */ + suspendedWarningDone(): Effect.Effect { + return synchronized.updateSomeEffect(this.suspendedWarningState, (suspendedWarningData) => { + if (SuspendedWarningData.isPending(suspendedWarningData)) { + return Option.some(pipe(core.interruptFiber(suspendedWarningData.fiber), core.as(SuspendedWarningData.start))) + } + return Option.none() + }) + } + /** + * Runs all effects scheduled to occur on or before the specified instant, + * which may depend on the current time, in order. + */ + run(f: (instant: number) => number): Effect.Effect { + return pipe( + this.awaitSuspended(), + core.zipRight(pipe( + ref.modify(this.clockState, (data) => { + const end = f(data.instant) + const sorted = pipe( + data.sleeps, + Chunk.sort]>( + pipe(number.Order, Order.mapInput((_) => _[0])) + ) + ) + if (Chunk.isNonEmpty(sorted)) { + const [instant, deferred] = Chunk.headNonEmpty(sorted) + if (instant <= end) { + return [ + Option.some([end, deferred] as const), + makeData(instant, Chunk.tailNonEmpty(sorted)) + ] as const + } + } + return [Option.none(), makeData(end, data.sleeps)] as const + }), + core.flatMap((option) => { + switch (option._tag) { + case "None": { + return core.void + } + case "Some": { + const [end, deferred] = option.value + return pipe( + core.deferredSucceed(deferred, void 0), + core.zipRight(core.yieldNow()), + core.zipRight(this.run(() => end)) + ) + } + } + }) + )) + ) + } +} + +/** + * @since 2.0.0 + */ +export const live = (data: Data): Layer.Layer => + layer.scoped( + TestClock, + core.gen(function*() { + const live = yield* Live.TestLive + const annotations = yield* Annotations.TestAnnotations + const clockState = yield* core.sync(() => ref.unsafeMake(data)) + const warningState = yield* circular.makeSynchronized(WarningData.start) + const suspendedWarningState = yield* circular.makeSynchronized(SuspendedWarningData.start) + const testClock = new TestClockImpl(clockState, live, annotations, warningState, suspendedWarningState) + yield* fiberRuntime.withClockScoped(testClock) + yield* fiberRuntime.addFinalizer( + () => core.zipRight(testClock.warningDone(), testClock.suspendedWarningDone()) + ) + return testClock + }) + ) + +/** + * @since 2.0.0 + */ +export const defaultTestClock: Layer.Layer = live( + makeData(new Date(0).getTime(), Chunk.empty()) +) + +/** + * Accesses a `TestClock` instance in the context and increments the time + * by the specified duration, running any actions scheduled for on or before + * the new time in order. + * + * @since 2.0.0 + */ +export const adjust = (durationInput: Duration.DurationInput): Effect.Effect => { + const duration = Duration.decode(durationInput) + return testClockWith((testClock) => testClock.adjust(duration)) +} + +/** + * @since 2.0.0 + */ +export const adjustWith = dual< + /** + * @since 2.0.0 + */ + (duration: Duration.DurationInput) => (effect: Effect.Effect) => Effect.Effect, + /** + * @since 2.0.0 + */ + (effect: Effect.Effect, duration: Duration.DurationInput) => Effect.Effect +>(2, (effect, durationInput) => { + const duration = Duration.decode(durationInput) + return testClockWith((testClock) => testClock.adjustWith(duration)(effect)) +}) + +/** + * Accesses a `TestClock` instance in the context and saves the clock + * state in an effect which, when run, will restore the `TestClock` to the + * saved state. + * + * @since 2.0.0 + */ +export const save = (): Effect.Effect> => testClockWith((testClock) => testClock.save) + +/** + * Accesses a `TestClock` instance in the context and sets the clock time + * to the specified `Instant` or `Date`, running any actions scheduled for on or before + * the new time in order. + * + * @since 2.0.0 + */ +export const setTime = (input: DateTime.DateTime.Input): Effect.Effect => + testClockWith((testClock) => + testClock.setTime( + typeof input === "number" + ? input + : DateTime.unsafeMake(input).epochMillis + ) + ) + +/** + * Semantically blocks the current fiber until the clock time is equal to or + * greater than the specified duration. Once the clock time is adjusted to + * on or after the duration, the fiber will automatically be resumed. + * + * @since 2.0.0 + */ +export const sleep = (durationInput: Duration.DurationInput): Effect.Effect => { + const duration = Duration.decode(durationInput) + return testClockWith((testClock) => testClock.sleep(duration)) +} + +/** + * Accesses a `TestClock` instance in the context and returns a list of + * times that effects are scheduled to run. + * + * @since 2.0.0 + */ +export const sleeps = (): Effect.Effect> => testClockWith((testClock) => testClock.sleeps) + +/** + * Retrieves the `TestClock` service for this test. + * + * @since 2.0.0 + */ +export const testClock = (): Effect.Effect => testClockWith(core.succeed) + +/** + * Retrieves the `TestClock` service for this test and uses it to run the + * specified workflow. + * + * @since 2.0.0 + */ +export const testClockWith = (f: (testClock: TestClock) => Effect.Effect): Effect.Effect => + core.fiberRefGetWith( + defaultServices.currentServices, + (services) => f(pipe(services, Context.get(clock.clockTag)) as TestClock) + ) + +/** + * Accesses the current time of a `TestClock` instance in the context in + * milliseconds. + * + * @since 2.0.0 + */ +export const currentTimeMillis: Effect.Effect = testClockWith((testClock) => testClock.currentTimeMillis) diff --git a/backend/node_modules/effect/src/TestLive.ts b/backend/node_modules/effect/src/TestLive.ts new file mode 100644 index 0000000000000000000000000000000000000000..f9ae5e027184f50b60487bce76ab86cfeab8ed11 --- /dev/null +++ b/backend/node_modules/effect/src/TestLive.ts @@ -0,0 +1,53 @@ +/** + * @since 2.0.0 + */ +import * as Context from "./Context.js" +import type * as DefaultServices from "./DefaultServices.js" +import type * as Effect from "./Effect.js" +import * as core from "./internal/core.js" +import * as defaultServices from "./internal/defaultServices.js" + +/** + * @since 2.0.0 + */ +export const TestLiveTypeId: unique symbol = Symbol.for("effect/TestLive") + +/** + * @since 2.0.0 + */ +export type TestLiveTypeId = typeof TestLiveTypeId + +/** + * The `Live` trait provides access to the "live" default Effect services from + * within tests for workflows such as printing test results to the console or + * timing out tests where it is necessary to access the real implementations of + * these services. + * + * @since 2.0.0 + */ +export interface TestLive { + readonly [TestLiveTypeId]: TestLiveTypeId + provide(effect: Effect.Effect): Effect.Effect +} + +/** + * @since 2.0.0 + */ +export const TestLive: Context.Tag = Context.GenericTag("effect/TestLive") + +/** @internal */ +class LiveImpl implements TestLive { + readonly [TestLiveTypeId]: TestLiveTypeId = TestLiveTypeId + constructor(readonly services: Context.Context) {} + provide(effect: Effect.Effect): Effect.Effect { + return core.fiberRefLocallyWith( + defaultServices.currentServices, + Context.merge(this.services) + )(effect) + } +} + +/** + * @since 2.0.0 + */ +export const make = (services: Context.Context): TestLive => new LiveImpl(services) diff --git a/backend/node_modules/effect/src/TestServices.ts b/backend/node_modules/effect/src/TestServices.ts new file mode 100644 index 0000000000000000000000000000000000000000..7dd55932b3bb39abe4eb821a55a36e3a899c5d04 --- /dev/null +++ b/backend/node_modules/effect/src/TestServices.ts @@ -0,0 +1,454 @@ +/** + * @since 2.0.0 + */ +import * as Context from "./Context.js" +import type * as DefaultServices from "./DefaultServices.js" +import * as Effect from "./Effect.js" +import type * as Fiber from "./Fiber.js" +import type * as FiberRef from "./FiberRef.js" +import { dual, pipe } from "./Function.js" +import * as core from "./internal/core.js" +import * as defaultServices from "./internal/defaultServices.js" +import * as fiberRuntime from "./internal/fiberRuntime.js" +import * as layer from "./internal/layer.js" +import * as ref from "./internal/ref.js" +import type * as Layer from "./Layer.js" +import type * as Scope from "./Scope.js" +import type * as SortedSet from "./SortedSet.js" +import type * as TestAnnotation from "./TestAnnotation.js" +import * as TestAnnotationMap from "./TestAnnotationMap.js" +import * as Annotations from "./TestAnnotations.js" +import * as TestConfig from "./TestConfig.js" +import * as Live from "./TestLive.js" +import * as Sized from "./TestSized.js" + +/** + * @since 2.0.0 + */ +export type TestServices = + | Annotations.TestAnnotations + | Live.TestLive + | Sized.TestSized + | TestConfig.TestConfig + +/** + * The default Effect test services. + * + * @since 2.0.0 + */ +export const liveServices: Context.Context = pipe( + Context.make(Annotations.TestAnnotations, Annotations.make(ref.unsafeMake(TestAnnotationMap.empty()))), + Context.add(Live.TestLive, Live.make(defaultServices.liveServices)), + Context.add(Sized.TestSized, Sized.make(100)), + Context.add(TestConfig.TestConfig, TestConfig.make({ repeats: 100, retries: 100, samples: 200, shrinks: 1000 })) +) + +/** + * @since 2.0.0 + */ +export const currentServices: FiberRef.FiberRef> = core.fiberRefUnsafeMakeContext( + liveServices +) + +/** + * Retrieves the `Annotations` service for this test. + * + * @since 2.0.0 + */ +export const annotations = (): Effect.Effect => annotationsWith(core.succeed) + +/** + * Retrieves the `Annotations` service for this test and uses it to run the + * specified workflow. + * + * @since 2.0.0 + */ +export const annotationsWith = ( + f: (annotations: Annotations.TestAnnotations) => Effect.Effect +): Effect.Effect => + core.fiberRefGetWith( + currentServices, + (services) => f(Context.get(services, Annotations.TestAnnotations)) + ) + +/** + * Executes the specified workflow with the specified implementation of the + * annotations service. + * + * @since 2.0.0 + */ +export const withAnnotations = dual< + /** + * Executes the specified workflow with the specified implementation of the + * annotations service. + * + * @since 2.0.0 + */ + (annotations: Annotations.TestAnnotations) => (effect: Effect.Effect) => Effect.Effect, + /** + * Executes the specified workflow with the specified implementation of the + * annotations service. + * + * @since 2.0.0 + */ + (effect: Effect.Effect, annotations: Annotations.TestAnnotations) => Effect.Effect +>(2, (effect, annotations) => + core.fiberRefLocallyWith( + currentServices, + Context.add(Annotations.TestAnnotations, annotations) + )(effect)) + +/** + * Sets the implementation of the annotations service to the specified value + * and restores it to its original value when the scope is closed. + * + * @since 2.0.0 + */ +export const withAnnotationsScoped = ( + annotations: Annotations.TestAnnotations +): Effect.Effect => + fiberRuntime.fiberRefLocallyScopedWith( + currentServices, + Context.add(Annotations.TestAnnotations, annotations) + ) + +/** + * Constructs a new `Annotations` service wrapped in a layer. + * + * @since 2.0.0 + */ +export const annotationsLayer = (): Layer.Layer => + layer.scoped( + Annotations.TestAnnotations, + pipe( + core.sync(() => ref.unsafeMake(TestAnnotationMap.empty())), + core.map(Annotations.make), + core.tap(withAnnotationsScoped) + ) + ) + +/** + * Accesses an `Annotations` instance in the context and retrieves the + * annotation of the specified type, or its default value if there is none. + * + * @since 2.0.0 + */ +export const get = (key: TestAnnotation.TestAnnotation): Effect.Effect => + annotationsWith((annotations) => annotations.get(key)) + +/** + * Accesses an `Annotations` instance in the context and appends the + * specified annotation to the annotation map. + * + * @since 2.0.0 + */ +export const annotate = (key: TestAnnotation.TestAnnotation, value: A): Effect.Effect => + annotationsWith((annotations) => annotations.annotate(key, value)) + +/** + * Returns the set of all fibers in this test. + * + * @since 2.0.0 + */ +export const supervisedFibers = (): Effect.Effect< + SortedSet.SortedSet> +> => annotationsWith((annotations) => annotations.supervisedFibers) + +/** + * Retrieves the `Live` service for this test and uses it to run the specified + * workflow. + * + * @since 2.0.0 + */ +export const liveWith = (f: (live: Live.TestLive) => Effect.Effect): Effect.Effect => + core.fiberRefGetWith(currentServices, (services) => f(Context.get(services, Live.TestLive))) + +/** + * Retrieves the `Live` service for this test. + * + * @since 2.0.0 + */ +export const live: Effect.Effect = liveWith(core.succeed) + +/** + * Executes the specified workflow with the specified implementation of the + * live service. + * + * @since 2.0.0 + */ +export const withLive = dual< + /** + * Executes the specified workflow with the specified implementation of the + * live service. + * + * @since 2.0.0 + */ + (live: Live.TestLive) => (effect: Effect.Effect) => Effect.Effect, + /** + * Executes the specified workflow with the specified implementation of the + * live service. + * + * @since 2.0.0 + */ + (effect: Effect.Effect, live: Live.TestLive) => Effect.Effect +>(2, (effect, live) => + core.fiberRefLocallyWith( + currentServices, + Context.add(Live.TestLive, live) + )(effect)) + +/** + * Sets the implementation of the live service to the specified value and + * restores it to its original value when the scope is closed. + * + * @since 2.0.0 + */ +export const withLiveScoped = (live: Live.TestLive): Effect.Effect => + fiberRuntime.fiberRefLocallyScopedWith(currentServices, Context.add(Live.TestLive, live)) + +/** + * Constructs a new `Live` service wrapped in a layer. + * + * @since 2.0.0 + */ +export const liveLayer = (): Layer.Layer => + layer.scoped( + Live.TestLive, + pipe( + core.context(), + core.map(Live.make), + core.tap(withLiveScoped) + ) + ) + +/** + * Provides a workflow with the "live" default Effect services. + * + * @since 2.0.0 + */ +export const provideLive = (effect: Effect.Effect): Effect.Effect => + liveWith((live) => live.provide(effect)) + +/** + * Runs a transformation function with the live default Effect services while + * ensuring that the workflow itself is run with the test services. + * + * @since 2.0.0 + */ +export const provideWithLive = dual< + /** + * Runs a transformation function with the live default Effect services while + * ensuring that the workflow itself is run with the test services. + * + * @since 2.0.0 + */ + (f: (effect: Effect.Effect) => Effect.Effect) => (self: Effect.Effect) => Effect.Effect, + /** + * Runs a transformation function with the live default Effect services while + * ensuring that the workflow itself is run with the test services. + * + * @since 2.0.0 + */ + ( + self: Effect.Effect, + f: (effect: Effect.Effect) => Effect.Effect + ) => Effect.Effect +>(2, (self, f) => + core.fiberRefGetWith( + defaultServices.currentServices, + (services) => provideLive(f(core.fiberRefLocally(defaultServices.currentServices, services)(self))) + )) + +/** + * Retrieves the `Sized` service for this test and uses it to run the + * specified workflow. + * + * @since 2.0.0 + */ +export const sizedWith = (f: (sized: Sized.TestSized) => Effect.Effect): Effect.Effect => + core.fiberRefGetWith( + currentServices, + (services) => f(Context.get(services, Sized.TestSized)) + ) + +/** + * Retrieves the `Sized` service for this test. + * + * @since 2.0.0 + */ +export const sized: Effect.Effect = sizedWith(core.succeed) + +/** + * Executes the specified workflow with the specified implementation of the + * sized service. + * + * @since 2.0.0 + */ +export const withSized = dual< + /** + * Executes the specified workflow with the specified implementation of the + * sized service. + * + * @since 2.0.0 + */ + (sized: Sized.TestSized) => (effect: Effect.Effect) => Effect.Effect, + /** + * Executes the specified workflow with the specified implementation of the + * sized service. + * + * @since 2.0.0 + */ + (effect: Effect.Effect, sized: Sized.TestSized) => Effect.Effect +>(2, (effect, sized) => + core.fiberRefLocallyWith( + currentServices, + Context.add(Sized.TestSized, sized) + )(effect)) + +/** + * Sets the implementation of the sized service to the specified value and + * restores it to its original value when the scope is closed. + * + * @since 2.0.0 + */ +export const withSizedScoped = (sized: Sized.TestSized): Effect.Effect => + fiberRuntime.fiberRefLocallyScopedWith(currentServices, Context.add(Sized.TestSized, sized)) + +/** + * @since 2.0.0 + */ +export const sizedLayer = (size: number): Layer.Layer => + layer.scoped( + Sized.TestSized, + pipe( + fiberRuntime.fiberRefMake(size), + core.map(Sized.fromFiberRef), + core.tap(withSizedScoped) + ) + ) + +/** + * @since 2.0.0 + */ +export const size: Effect.Effect = sizedWith((sized) => sized.size) + +/** + * @since 2.0.0 + */ +export const withSize = dual< + /** + * @since 2.0.0 + */ + (size: number) => (effect: Effect.Effect) => Effect.Effect, + /** + * @since 2.0.0 + */ + (effect: Effect.Effect, size: number) => Effect.Effect +>(2, (effect, size) => sizedWith((sized) => sized.withSize(size)(effect))) + +/** + * Retrieves the `TestConfig` service for this test and uses it to run the + * specified workflow. + * + * @since 2.0.0 + */ +export const testConfigWith = ( + f: (config: TestConfig.TestConfig) => Effect.Effect +): Effect.Effect => + core.fiberRefGetWith( + currentServices, + (services) => f(Context.get(services, TestConfig.TestConfig)) + ) + +/** + * Retrieves the `TestConfig` service for this test. + * + * @since 2.0.0 + */ +export const testConfig: Effect.Effect = testConfigWith(core.succeed) + +/** + * Executes the specified workflow with the specified implementation of the + * config service. + * + * @since 2.0.0 + */ +export const withTestConfig = dual< + /** + * Executes the specified workflow with the specified implementation of the + * config service. + * + * @since 2.0.0 + */ + (config: TestConfig.TestConfig) => (effect: Effect.Effect) => Effect.Effect, + /** + * Executes the specified workflow with the specified implementation of the + * config service. + * + * @since 2.0.0 + */ + (effect: Effect.Effect, config: TestConfig.TestConfig) => Effect.Effect +>(2, (effect, config) => + core.fiberRefLocallyWith( + currentServices, + Context.add(TestConfig.TestConfig, config) + )(effect)) + +/** + * Sets the implementation of the config service to the specified value and + * restores it to its original value when the scope is closed. + * + * @since 2.0.0 + */ +export const withTestConfigScoped = (config: TestConfig.TestConfig): Effect.Effect => + fiberRuntime.fiberRefLocallyScopedWith(currentServices, Context.add(TestConfig.TestConfig, config)) + +/** + * Constructs a new `TestConfig` service with the specified settings. + * + * @since 2.0.0 + */ +export const testConfigLayer = (params: { + readonly repeats: number + readonly retries: number + readonly samples: number + readonly shrinks: number +}): Layer.Layer => + layer.scoped( + TestConfig.TestConfig, + Effect.suspend(() => { + const testConfig = TestConfig.make(params) + return pipe( + withTestConfigScoped(testConfig), + core.as(testConfig) + ) + }) + ) + +/** + * The number of times to repeat tests to ensure they are stable. + * + * @since 2.0.0 + */ +export const repeats: Effect.Effect = testConfigWith((config) => core.succeed(config.repeats)) + +/** + * The number of times to retry flaky tests. + * + * @since 2.0.0 + */ +export const retries: Effect.Effect = testConfigWith((config) => core.succeed(config.retries)) + +/** + * The number of sufficient samples to check for a random variable. + * + * @since 2.0.0 + */ +export const samples: Effect.Effect = testConfigWith((config) => core.succeed(config.samples)) + +/** + * The maximum number of shrinkings to minimize large failures. + * + * @since 2.0.0 + */ +export const shrinks: Effect.Effect = testConfigWith((config) => core.succeed(config.shrinks)) diff --git a/backend/node_modules/effect/src/Types.ts b/backend/node_modules/effect/src/Types.ts new file mode 100644 index 0000000000000000000000000000000000000000..90d6476553c9f9c04f0114e37cf9b58091a0c1f9 --- /dev/null +++ b/backend/node_modules/effect/src/Types.ts @@ -0,0 +1,361 @@ +/** + * A collection of types that are commonly used types. + * + * @since 2.0.0 + */ + +type _TupleOf> = `${N}` extends `-${number}` ? never + : R["length"] extends N ? R + : _TupleOf + +/** + * Represents a tuple with a fixed number of elements of type `T`. + * + * This type constructs a tuple that has exactly `N` elements of type `T`. + * + * @typeParam N - The number of elements in the tuple. + * @typeParam T - The type of elements in the tuple. + * + * @example + * ```ts + * import { TupleOf } from "effect/Types" + * + * // A tuple with exactly 3 numbers + * const example1: TupleOf<3, number> = [1, 2, 3]; // valid + * // @ts-expect-error + * const example2: TupleOf<3, number> = [1, 2]; // invalid + * // @ts-expect-error + * const example3: TupleOf<3, number> = [1, 2, 3, 4]; // invalid + * ``` + * + * @category tuples + * @since 3.3.0 + */ +export type TupleOf = N extends N ? number extends N ? Array : _TupleOf : never + +/** + * Represents a tuple with at least `N` elements of type `T`. + * + * This type constructs a tuple that has a fixed number of elements `N` of type `T` at the start, + * followed by any number (including zero) of additional elements of the same type `T`. + * + * @typeParam N - The minimum number of elements in the tuple. + * @typeParam T - The type of elements in the tuple. + * + * @example + * ```ts + * import { TupleOfAtLeast } from "effect/Types" + * + * // A tuple with at least 3 numbers + * const example1: TupleOfAtLeast<3, number> = [1, 2, 3]; // valid + * const example2: TupleOfAtLeast<3, number> = [1, 2, 3, 4, 5]; // valid + * // @ts-expect-error + * const example3: TupleOfAtLeast<3, number> = [1, 2]; // invalid + * ``` + * + * @category tuples + * @since 3.3.0 + */ +export type TupleOfAtLeast = [...TupleOf, ...Array] + +/** + * Returns the tags in a type. + * @example + * ```ts + * import type { Types } from "effect" + * + * type Res = Types.Tags // "a" | "b" + * ``` + * + * @category types + * @since 2.0.0 + */ +export type Tags = E extends { _tag: string } ? E["_tag"] : never + +/** + * Excludes the tagged object from the type. + * @example + * ```ts + * import type { Types } from "effect" + * + * type Res = Types.ExcludeTag // string | { _tag: "b" } + * ``` + * + * @category types + * @since 2.0.0 + */ +export type ExcludeTag> = Exclude + +/** + * Extracts the type of the given tag. + * + * @example + * ```ts + * import type { Types } from "effect" + * + * type Res = Types.ExtractTag<{ _tag: "a", a: number } | { _tag: "b", b: number }, "b"> // { _tag: "b", b: number } + * ``` + * + * @category types + * @since 2.0.0 + */ +export type ExtractTag> = Extract + +/** + * A utility type that transforms a union type `T` into an intersection type. + * + * @since 2.0.0 + * @category types + */ +export type UnionToIntersection = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R + : never + +/** + * Simplifies the type signature of a type. + * + * @example + * ```ts + * import type { Types } from "effect" + * + * type Res = Types.Simplify<{ a: number } & { b: number }> // { a: number; b: number; } + * ``` + * + * @since 2.0.0 + * @category types + */ +export type Simplify = { + [K in keyof A]: A[K] +} extends infer B ? B : never + +/** + * Determines if two types are equal. + * + * @example + * ```ts + * import type { Types } from "effect" + * + * type Res1 = Types.Equals<{ a: number }, { a: number }> // true + * type Res2 = Types.Equals<{ a: number }, { b: number }> // false + * ``` + * + * @since 2.0.0 + * @category models + */ +export type Equals = (() => T extends X ? 1 : 2) extends < + T +>() => T extends Y ? 1 : 2 ? true + : false + +/** + * Determines if two types are equal, allowing to specify the return types. + * + * @since 3.15.0 + * @category models + */ +export type EqualsWith = (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? Y : N + +/** + * Determines if a record contains any of the given keys. + * + * @example + * ```ts + * import type { Types } from "effect" + * + * type Res1 = Types.Has<{ a: number }, "a" | "b"> // true + * type Res2 = Types.Has<{ c: number }, "a" | "b"> // false + * ``` + * + * @since 2.0.0 + * @category models + */ +export type Has = (Key extends infer K ? K extends keyof A ? true : never : never) extends never + ? false + : true + +/** + * Merges two object where the keys of the left object take precedence in the case of a conflict. + * + * @example + * ```ts + * import type { Types } from "effect" + * type MergeLeft = Types.MergeLeft<{ a: number, b: number; }, { a: string }> // { a: number; b: number; } + * ``` + * + * @since 2.0.0 + * @category models + */ +export type MergeLeft = MergeRight + +/** + * Merges two object where the keys of the right object take precedence in the case of a conflict. + * + * @example + * ```ts + * import type { Types } from "effect" + * type MergeRight = Types.MergeRight<{ a: number, b: number; }, { a: string }> // { a: string; b: number; } + * ``` + * + * @since 2.0.0 + * @category models + */ +export type MergeRight = Simplify< + & Source + & { + [Key in keyof Target as Key extends keyof Source ? never : Key]: Target[Key] + } +> + +/** + * @since 2.0.0 + * @category models + */ +export type MergeRecord = MergeLeft + +/** + * Describes the concurrency to use when executing multiple Effect's. + * + * @since 2.0.0 + * @category models + */ +export type Concurrency = number | "unbounded" | "inherit" + +/** + * Make all properties in `T` mutable. Supports arrays, tuples, and records as well. + * + * @example + * ```ts + * import type { Types } from "effect" + * + * type MutableStruct = Types.Mutable<{ readonly a: string; readonly b: number }> // { a: string; b: number; } + * + * type MutableArray = Types.Mutable> // string[] + * + * type MutableTuple = Types.Mutable // [string, number] + * + * type MutableRecord = Types.Mutable<{ readonly [_: string]: number }> // { [x: string]: number; } + * ``` + * + * @since 2.0.0 + * @category types + */ +export type Mutable = { + -readonly [P in keyof T]: T[P] +} + +/** + * Like `Types.Mutable`, but works recursively. + * + * @example + * ```ts + * import type { Types } from "effect" + * + * type DeepMutableStruct = Types.DeepMutable<{ + * readonly a: string; + * readonly b: readonly string[] + * }> + * // { a: string; b: string[] } + * ``` + * + * @since 3.1.0 + * @category types + */ +export type DeepMutable = T extends ReadonlyMap ? Map, DeepMutable> + : T extends ReadonlySet ? Set> + : T extends string | number | boolean | bigint | symbol | Function ? T + : { -readonly [K in keyof T]: DeepMutable } + +/** + * Avoid inference on a specific parameter + * + * @since 2.0.0 + * @category models + */ +export type NoInfer = [A][A extends any ? 0 : never] + +/** + * Invariant helper. + * + * @since 2.0.0 + * @category models + */ +export type Invariant = (_: A) => A + +/** + * @since 3.9.0 + * @category models + */ +export declare namespace Invariant { + /** + * @since 3.9.0 + * @category models + */ + export type Type = A extends Invariant ? U : never +} + +/** + * Covariant helper. + * + * @since 2.0.0 + * @category models + */ +export type Covariant = (_: never) => A + +/** + * @since 3.9.0 + * @category models + */ +export declare namespace Covariant { + /** + * @since 3.9.0 + * @category models + */ + export type Type = A extends Covariant ? U : never +} + +/** + * Contravariant helper. + * + * @since 2.0.0 + * @category models + */ +export type Contravariant = (_: A) => void + +/** + * @since 3.9.0 + * @category models + */ +export declare namespace Contravariant { + /** + * @since 3.9.0 + * @category models + */ + export type Type = A extends Contravariant ? U : never +} + +/** + * @since 2.0.0 + */ +export type MatchRecord = {} extends S ? onTrue : onFalse + +/** + * @since 2.0.0 + */ +export type NotFunction = T extends Function ? never : T + +/** + * @since 3.9.0 + */ +export type NoExcessProperties = T & { readonly [K in Exclude]: never } + +/** + * @since 3.15.0 + */ +export type Ctor = new(...args: Array) => T + +/** + * Conditional type that returns `void` if `S` is an empty object type, + * otherwise returns `S`. + * + * @since 3.19.20 + */ +export type VoidIfEmpty = keyof S extends never ? void : S diff --git a/backend/node_modules/effect/src/Unify.ts b/backend/node_modules/effect/src/Unify.ts new file mode 100644 index 0000000000000000000000000000000000000000..dfbc8b153842dbd8da7a529be4927f8d4c2c13c4 --- /dev/null +++ b/backend/node_modules/effect/src/Unify.ts @@ -0,0 +1,127 @@ +/** + * @since 2.0.0 + */ + +import { identity } from "./Function.js" + +/** + * @since 2.0.0 + */ +export declare const unifySymbol: unique symbol + +/** + * @since 2.0.0 + */ +export type unifySymbol = typeof unifySymbol + +/** + * @since 2.0.0 + */ +export declare const typeSymbol: unique symbol + +/** + * @since 2.0.0 + */ +export type typeSymbol = typeof typeSymbol + +/** + * @since 2.0.0 + */ +export declare const ignoreSymbol: unique symbol + +/** + * @since 2.0.0 + */ +export type ignoreSymbol = typeof ignoreSymbol + +type MaybeReturn = F extends () => infer R ? R : NonNullable + +type Values = X extends [infer A, infer Ignore] + ? Exclude extends infer k ? k extends keyof A ? MaybeReturn : never : never + : never + +type Ignore = X extends { [ignoreSymbol]?: infer Obj } ? keyof NonNullable + : never + +type ExtractTypes< + X +> = X extends { + [typeSymbol]?: infer _Type + [unifySymbol]?: infer _Unify +} ? [NonNullable<_Unify>, Ignore] + : never + +type FilterIn = A extends any ? typeSymbol extends keyof A ? A : never : never + +type FilterOut = A extends any ? typeSymbol extends keyof A ? never : A : never + +/** + * @since 2.0.0 + */ +export type Unify = Values< + ExtractTypes< + ( + & FilterIn + & { [typeSymbol]: A } + ) + > +> extends infer Z ? Z | Exclude | FilterOut : never + +/** + * @since 2.0.0 + */ +export const unify: { + /** + * @since 2.0.0 + */ + < + Args extends Array, + Args2 extends Array, + Args3 extends Array, + Args4 extends Array, + Args5 extends Array, + T + >( + x: (...args: Args) => (...args: Args2) => (...args: Args3) => (...args: Args4) => (...args: Args5) => T + ): (...args: Args) => (...args: Args2) => (...args: Args3) => (...args: Args4) => (...args: Args5) => Unify + /** + * @since 2.0.0 + */ + < + Args extends Array, + Args2 extends Array, + Args3 extends Array, + Args4 extends Array, + T + >( + x: (...args: Args) => (...args: Args2) => (...args: Args3) => (...args: Args4) => T + ): (...args: Args) => (...args: Args2) => (...args: Args3) => (...args: Args4) => Unify + /** + * @since 2.0.0 + */ + < + Args extends Array, + Args2 extends Array, + Args3 extends Array, + T + >(x: (...args: Args) => (...args: Args2) => (...args: Args3) => T): (...args: Args) => (...args: Args2) => (...args: Args3) => Unify + /** + * @since 2.0.0 + */ + < + Args extends Array, + Args2 extends Array, + T + >(x: (...args: Args) => (...args: Args2) => T): (...args: Args) => (...args: Args2) => Unify + /** + * @since 2.0.0 + */ + < + Args extends Array, + T + >(x: (...args: Args) => T): (...args: Args) => Unify + /** + * @since 2.0.0 + */ + (x: T): Unify +} = identity as any diff --git a/backend/node_modules/effect/src/Utils.ts b/backend/node_modules/effect/src/Utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..ef4a07019393482d33068065bfd990b35cb23135 --- /dev/null +++ b/backend/node_modules/effect/src/Utils.ts @@ -0,0 +1,809 @@ +/** + * @since 2.0.0 + */ +import { identity } from "./Function.js" +import { globalValue } from "./GlobalValue.js" +import type { Kind, TypeLambda } from "./HKT.js" +import { getBugErrorMessage } from "./internal/errors.js" +import { isNullable, isObject } from "./Predicate.js" +import type * as Types from "./Types.js" + +/* + * Copyright 2014 Thom Chiovoloni, released under the MIT license. + * + * A random number generator based on the basic implementation of the PCG algorithm, + * as described here: http://www.pcg-random.org/ + * + * Adapted for TypeScript from Thom's original code at https://github.com/thomcc/pcg-random + * + * forked from https://github.com/frptools + * + * @since 2.0.0 + */ + +/** + * @category symbols + * @since 2.0.0 + */ +export const GenKindTypeId: unique symbol = Symbol.for("effect/Gen/GenKind") + +/** + * @category symbols + * @since 2.0.0 + */ +export type GenKindTypeId = typeof GenKindTypeId + +/** + * @category models + * @since 2.0.0 + */ +export interface GenKind extends Variance { + readonly value: Kind + + [Symbol.iterator](): IterableIterator, A> +} + +/** + * @category predicates + * @since 3.0.6 + */ +export const isGenKind = (u: unknown): u is GenKind => isObject(u) && GenKindTypeId in u + +/** + * @category constructors + * @since 2.0.0 + */ +export class GenKindImpl implements GenKind { + constructor( + /** + * @since 2.0.0 + */ + readonly value: Kind + ) {} + + /** + * @since 2.0.0 + */ + get _F() { + return identity + } + + /** + * @since 2.0.0 + */ + get _R() { + return (_: R) => _ + } + + /** + * @since 2.0.0 + */ + get _O() { + return (_: never): O => _ + } + + /** + * @since 2.0.0 + */ + get _E() { + return (_: never): E => _ + } + + /** + * @since 2.0.0 + */ + readonly [GenKindTypeId]: typeof GenKindTypeId = GenKindTypeId; + + /** + * @since 2.0.0 + */ + [Symbol.iterator](): IterableIterator, A> { + return new SingleShotGen, A>(this as any) + } +} + +/** + * @category constructors + * @since 2.0.0 + */ +export class SingleShotGen implements IterableIterator { + private called = false + + constructor(readonly self: T) {} + + /** + * @since 2.0.0 + */ + next(a: A): IteratorResult { + return this.called ? + ({ + value: a, + done: true + }) : + (this.called = true, + ({ + value: this.self, + done: false + })) + } + + /** + * @since 2.0.0 + */ + return(a: A): IteratorResult { + return ({ + value: a, + done: true + }) + } + + /** + * @since 2.0.0 + */ + throw(e: unknown): IteratorResult { + throw e + } + + /** + * @since 2.0.0 + */ + [Symbol.iterator](): IterableIterator { + return new SingleShotGen(this.self) + } +} + +/** + * @category constructors + * @since 2.0.0 + */ +export const makeGenKind = ( + kind: Kind +): GenKind => new GenKindImpl(kind) + +/** + * @category models + * @since 2.0.0 + */ +export interface Variance { + readonly [GenKindTypeId]: GenKindTypeId + readonly _F: Types.Invariant + readonly _R: Types.Contravariant + readonly _O: Types.Covariant + readonly _E: Types.Covariant +} + +/** + * @category models + * @since 2.0.0 + */ +export interface Gen { + | YieldWrap>, A>( + ...args: + | [ + self: Self, + body: (this: Self, resume: Z) => Generator + ] + | [ + body: (resume: Z) => Generator + ] + ): Kind< + F, + [K] extends [Variance] ? R + : [K] extends [YieldWrap>] ? R + : never, + [K] extends [Variance] ? O + : [K] extends [YieldWrap>] ? O + : never, + [K] extends [Variance] ? E + : [K] extends [YieldWrap>] ? E + : never, + A + > +} + +/** + * @category models + * @since 2.0.0 + */ +export interface Adapter { + <_R, _O, _E, _A>( + self: Kind + ): GenKind + (a: A, ab: (a: A) => Kind): GenKind + (a: A, ab: (a: A) => B, bc: (b: B) => Kind): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: F) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (g: H) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => R, + rs: (r: R) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => R, + rs: (r: R) => S, + st: (s: S) => Kind + ): GenKind + ( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => R, + rs: (r: R) => S, + st: (s: S) => T, + tu: (s: T) => Kind + ): GenKind +} + +/** + * @category adapters + * @since 2.0.0 + */ +export const adapter: () => Adapter = () => (function() { + let x = arguments[0] + for (let i = 1; i < arguments.length; i++) { + x = arguments[i](x) + } + return new GenKindImpl(x) as any +}) + +const defaultIncHi = 0x14057b7e +const defaultIncLo = 0xf767814f +const MUL_HI = 0x5851f42d >>> 0 +const MUL_LO = 0x4c957f2d >>> 0 +const BIT_53 = 9007199254740992.0 +const BIT_27 = 134217728.0 + +/** + * @category model + * @since 2.0.0 + */ +export type PCGRandomState = [number, number, number, number] + +/** + * @category model + * @since 2.0.0 + */ +export type OptionalNumber = number | null | undefined + +/** + * PCG is a family of simple fast space-efficient statistically good algorithms + * for random number generation. Unlike many general-purpose RNGs, they are also + * hard to predict. + * + * @category model + * @since 2.0.0 + */ +export class PCGRandom { + private _state!: Int32Array + + /** + * Creates an instance of PCGRandom. + * + * - `seed` - The low 32 bits of the seed (0 is used for high 32 bits). + * + * @memberOf PCGRandom + */ + constructor(seed?: OptionalNumber) + /** + * Creates an instance of PCGRandom. + * + * - `seedHi` - The high 32 bits of the seed. + * - `seedLo` - The low 32 bits of the seed. + * - `inc` - The low 32 bits of the incrementer (0 is used for high 32 bits). + * + * @memberOf PCGRandom + */ + constructor(seedHi: OptionalNumber, seedLo: OptionalNumber, inc?: OptionalNumber) + /** + * Creates an instance of PCGRandom. + * + * - `seedHi` - The high 32 bits of the seed. + * - `seedLo` - The low 32 bits of the seed. + * - `incHi` - The high 32 bits of the incrementer. + * - `incLo` - The low 32 bits of the incrementer. + * + * @memberOf PCGRandom + */ + constructor( + seedHi: OptionalNumber, + seedLo: OptionalNumber, + incHi: OptionalNumber, + incLo: OptionalNumber + ) + constructor( + seedHi?: OptionalNumber, + seedLo?: OptionalNumber, + incHi?: OptionalNumber, + incLo?: OptionalNumber + ) { + if (isNullable(seedLo) && isNullable(seedHi)) { + seedLo = (Math.random() * 0xffffffff) >>> 0 + seedHi = 0 + } else if (isNullable(seedLo)) { + seedLo = seedHi + seedHi = 0 + } + if (isNullable(incLo) && isNullable(incHi)) { + incLo = this._state ? this._state[3] : defaultIncLo + incHi = this._state ? this._state[2] : defaultIncHi + } else if (isNullable(incLo)) { + incLo = incHi + incHi = 0 + } + + this._state = new Int32Array([0, 0, ( incHi) >>> 0, ((incLo || 0) | 1) >>> 0]) + this._next() + add64( + this._state, + this._state[0]!, + this._state[1]!, + ( seedHi) >>> 0, + ( seedLo) >>> 0 + ) + this._next() + return this + } + + /** + * Returns a copy of the internal state of this random number generator as a + * JavaScript Array. + * + * @category getters + * @since 2.0.0 + */ + getState(): PCGRandomState { + return [this._state[0]!, this._state[1]!, this._state[2]!, this._state[3]!] + } + + /** + * Restore state previously retrieved using `getState()`. + * + * @since 2.0.0 + */ + setState(state: PCGRandomState) { + this._state[0] = state[0] + this._state[1] = state[1] + this._state[2] = state[2] + this._state[3] = state[3] | 1 + } + + /** + * Get a uniformly distributed 32 bit integer between [0, max). + * + * @category getter + * @since 2.0.0 + */ + integer(max: number) { + return Math.round(this.number() * Number.MAX_SAFE_INTEGER) % max + } + + /** + * Get a uniformly distributed IEEE-754 double between 0.0 and 1.0, with + * 53 bits of precision (every bit of the mantissa is randomized). + * + * @category getters + * @since 2.0.0 + */ + number() { + const hi = (this._next() & 0x03ffffff) * 1.0 + const lo = (this._next() & 0x07ffffff) * 1.0 + return (hi * BIT_27 + lo) / BIT_53 + } + + /** @internal */ + private _next() { + // save current state (what we'll use for this number) + const oldHi = this._state[0]! >>> 0 + const oldLo = this._state[1]! >>> 0 + + // churn LCG. + mul64(this._state, oldHi, oldLo, MUL_HI, MUL_LO) + add64(this._state, this._state[0]!, this._state[1]!, this._state[2]!, this._state[3]!) + + // get least sig. 32 bits of ((oldstate >> 18) ^ oldstate) >> 27 + let xsHi = oldHi >>> 18 + let xsLo = ((oldLo >>> 18) | (oldHi << 14)) >>> 0 + xsHi = (xsHi ^ oldHi) >>> 0 + xsLo = (xsLo ^ oldLo) >>> 0 + const xorshifted = ((xsLo >>> 27) | (xsHi << 5)) >>> 0 + // rotate xorshifted right a random amount, based on the most sig. 5 bits + // bits of the old state. + const rot = oldHi >>> 27 + const rot2 = ((-rot >>> 0) & 31) >>> 0 + return ((xorshifted >>> rot) | (xorshifted << rot2)) >>> 0 + } +} + +function mul64( + out: Int32Array, + aHi: number, + aLo: number, + bHi: number, + bLo: number +): void { + let c1 = ((aLo >>> 16) * (bLo & 0xffff)) >>> 0 + let c0 = ((aLo & 0xffff) * (bLo >>> 16)) >>> 0 + + let lo = ((aLo & 0xffff) * (bLo & 0xffff)) >>> 0 + let hi = ((aLo >>> 16) * (bLo >>> 16) + ((c0 >>> 16) + (c1 >>> 16))) >>> 0 + + c0 = (c0 << 16) >>> 0 + lo = (lo + c0) >>> 0 + if ((lo >>> 0) < (c0 >>> 0)) { + hi = (hi + 1) >>> 0 + } + + c1 = (c1 << 16) >>> 0 + lo = (lo + c1) >>> 0 + if ((lo >>> 0) < (c1 >>> 0)) { + hi = (hi + 1) >>> 0 + } + + hi = (hi + Math.imul(aLo, bHi)) >>> 0 + hi = (hi + Math.imul(aHi, bLo)) >>> 0 + + out[0] = hi + out[1] = lo +} + +// add two 64 bit numbers (given in parts), and store the result in `out`. +function add64( + out: Int32Array, + aHi: number, + aLo: number, + bHi: number, + bLo: number +): void { + let hi = (aHi + bHi) >>> 0 + const lo = (aLo + bLo) >>> 0 + if ((lo >>> 0) < (aLo >>> 0)) { + hi = (hi + 1) | 0 + } + out[0] = hi + out[1] = lo +} + +/** + * @since 3.0.6 + */ +export const YieldWrapTypeId: unique symbol = Symbol.for("effect/Utils/YieldWrap") + +/** + * @since 3.0.6 + */ +export class YieldWrap { + /** + * @since 3.0.6 + */ + readonly #value: T + constructor(value: T) { + this.#value = value + } + /** + * @since 3.0.6 + */ + [YieldWrapTypeId](): T { + return this.#value + } +} + +/** + * @since 3.0.6 + */ +export function yieldWrapGet(self: YieldWrap): T { + if (typeof self === "object" && self !== null && YieldWrapTypeId in self) { + return self[YieldWrapTypeId]() + } + throw new Error(getBugErrorMessage("yieldWrapGet")) +} + +/** + * Note: this is an experimental feature made available to allow custom matchers in tests, not to be directly used yet in user code + * + * @since 3.1.1 + * @status experimental + * @category modifiers + */ +export const structuralRegionState = globalValue( + "effect/Utils/isStructuralRegion", + (): { enabled: boolean; tester: ((a: unknown, b: unknown) => boolean) | undefined } => ({ + enabled: false, + tester: undefined + }) +) + +/** + * Note: this is an experimental feature made available to allow custom matchers in tests, not to be directly used yet in user code + * + * @since 3.1.1 + * @status experimental + * @category modifiers + */ +export const structuralRegion = (body: () => A, tester?: (a: unknown, b: unknown) => boolean): A => { + const current = structuralRegionState.enabled + const currentTester = structuralRegionState.tester + structuralRegionState.enabled = true + if (tester) { + structuralRegionState.tester = tester + } + try { + return body() + } finally { + structuralRegionState.enabled = current + structuralRegionState.tester = currentTester + } +} + +const standard = { + effect_internal_function: (body: () => A) => { + return body() + } +} + +const forced = { + effect_internal_function: (body: () => A) => { + try { + return body() + } finally { + // + } + } +} + +const isNotOptimizedAway = + standard.effect_internal_function(() => new Error().stack)?.includes("effect_internal_function") === true + +/** + * @since 3.2.2 + * @status experimental + * @category tracing + */ +export const internalCall = isNotOptimizedAway ? standard.effect_internal_function : forced.effect_internal_function + +const genConstructor = (function*() {}).constructor + +/** + * @since 3.11.0 + */ +export const isGeneratorFunction = (u: unknown): u is (...args: Array) => Generator => + isObject(u) && u.constructor === genConstructor diff --git a/backend/node_modules/effect/src/index.ts b/backend/node_modules/effect/src/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..d1a7c98f76f15ceecdf3e250757237da31fc32ea --- /dev/null +++ b/backend/node_modules/effect/src/index.ts @@ -0,0 +1,1561 @@ +/** + * @since 2.0.0 + */ + +export { + /** + * @since 2.0.0 + */ + absurd, + /** + * @since 2.0.0 + */ + flow, + /** + * @since 2.0.0 + */ + hole, + /** + * @since 2.0.0 + */ + identity, + /** + * @since 2.0.0 + */ + pipe, + /** + * @since 2.0.0 + */ + unsafeCoerce +} from "./Function.js" + +/** + * @since 3.10.0 + */ +export * as Arbitrary from "./Arbitrary.js" + +/** + * This module provides utility functions for working with arrays in TypeScript. + * + * @since 2.0.0 + */ +export * as Array from "./Array.js" + +/** + * This module provides utility functions and type class instances for working with the `BigDecimal` type in TypeScript. + * It includes functions for basic arithmetic operations, as well as type class instances for `Equivalence` and `Order`. + * + * A `BigDecimal` allows storing any real number to arbitrary precision; which avoids common floating point errors + * (such as 0.1 + 0.2 ≠ 0.3) at the cost of complexity. + * + * Internally, `BigDecimal` uses a `BigInt` object, paired with a 64-bit integer which determines the position of the + * decimal point. Therefore, the precision *is not* actually arbitrary, but limited to 263 decimal places. + * + * It is not recommended to convert a floating point number to a decimal directly, as the floating point representation + * may be unexpected. + * + * @module BigDecimal + * @since 2.0.0 + * @see {@link module:BigInt} for more similar operations on `bigint` types + * @see {@link module:Number} for more similar operations on `number` types + */ +export * as BigDecimal from "./BigDecimal.js" + +/** + * This module provides utility functions and type class instances for working with the `bigint` type in TypeScript. + * It includes functions for basic arithmetic operations, as well as type class instances for + * `Equivalence` and `Order`. + * + * @module BigInt + * @since 2.0.0 + * @see {@link module:BigDecimal} for more similar operations on `BigDecimal` types + * @see {@link module:Number} for more similar operations on `number` types + */ +export * as BigInt from "./BigInt.js" + +/** + * This module provides utility functions and type class instances for working with the `boolean` type in TypeScript. + * It includes functions for basic boolean operations, as well as type class instances for + * `Equivalence` and `Order`. + * + * @since 2.0.0 + */ +export * as Boolean from "./Boolean.js" + +/** + * This module provides types and utility functions to create and work with branded types, + * which are TypeScript types with an added type tag to prevent accidental usage of a value in the wrong context. + * + * The `refined` and `nominal` functions are both used to create branded types in TypeScript. + * The main difference between them is that `refined` allows for validation of the data, while `nominal` does not. + * + * The `nominal` function is used to create a new branded type that has the same underlying type as the input, but with a different name. + * This is useful when you want to distinguish between two values of the same type that have different meanings. + * The `nominal` function does not perform any validation of the input data. + * + * On the other hand, the `refined` function is used to create a new branded type that has the same underlying type as the input, + * but with a different name, and it also allows for validation of the input data. + * The `refined` function takes a predicate that is used to validate the input data. + * If the input data fails the validation, a `BrandErrors` is returned, which provides information about the specific validation failure. + * + * @since 2.0.0 + */ +export * as Brand from "./Brand.js" + +/** + * @since 2.0.0 + */ +export * as Cache from "./Cache.js" + +/** + * The `Effect` type is polymorphic in values of type `E` and we can + * work with any error type that we want. However, there is a lot of information + * that is not inside an arbitrary `E` value. So as a result, an `Effect` needs + * somewhere to store things like unexpected errors or defects, stack and + * execution traces, causes of fiber interruptions, and so forth. + * + * Effect-TS is very strict about preserving the full information related to a + * failure. It captures all type of errors into the `Cause` data type. `Effect` + * uses the `Cause` data type to store the full story of failure. So its + * error model is lossless. It doesn't throw information related to the failure + * result. So we can figure out exactly what happened during the operation of + * our effects. + * + * It is important to note that `Cause` is an underlying data type representing + * errors occuring within an `Effect` workflow. Thus, we don't usually deal with + * `Cause`s directly. Even though it is not a data type that we deal with very + * often, the `Cause` of a failing `Effect` workflow can be accessed at any + * time, which gives us total access to all parallel and sequential errors in + * occurring within our codebase. + * + * @since 2.0.0 + */ +export * as Cause from "./Cause.js" + +/** + * @since 2.0.0 + */ +export * as Channel from "./Channel.js" + +/** + * @since 2.0.0 + */ +export * as ChildExecutorDecision from "./ChildExecutorDecision.js" + +/** + * @since 2.0.0 + */ +export * as Chunk from "./Chunk.js" + +/** + * @since 2.0.0 + */ +export * as Clock from "./Clock.js" + +/** + * @since 2.0.0 + */ +export * as Config from "./Config.js" + +/** + * @since 2.0.0 + */ +export * as ConfigError from "./ConfigError.js" + +/** + * @since 2.0.0 + */ +export * as ConfigProvider from "./ConfigProvider.js" + +/** + * @since 2.0.0 + */ +export * as ConfigProviderPathPatch from "./ConfigProviderPathPatch.js" + +/** + * @since 2.0.0 + */ +export * as Console from "./Console.js" + +/** + * This module provides a data structure called `Context` that can be used for dependency injection in effectful + * programs. It is essentially a table mapping `Tag`s to their implementations (called `Service`s), and can be used to + * manage dependencies in a type-safe way. The `Context` data structure is essentially a way of providing access to a set + * of related services that can be passed around as a single unit. This module provides functions to create, modify, and + * query the contents of a `Context`, as well as a number of utility types for working with tags and services. + * + * @since 2.0.0 + */ +export * as Context from "./Context.js" + +/** + * @since 2.0.0 + */ +export * as Cron from "./Cron.js" + +/** + * @since 2.0.0 + */ +export * as Data from "./Data.js" + +/** + * @since 3.6.0 + */ +export * as DateTime from "./DateTime.js" + +/** + * @since 2.0.0 + */ +export * as DefaultServices from "./DefaultServices.js" + +/** + * @since 2.0.0 + */ +export * as Deferred from "./Deferred.js" + +/** + * @since 2.0.0 + */ +export * as Differ from "./Differ.js" + +/** + * @since 2.0.0 + */ +export * as Duration from "./Duration.js" + +/** + * @since 2.0.0 + */ +export * as Effect from "./Effect.js" + +/** + * @since 2.0.0 + */ +export * as Effectable from "./Effectable.js" + +/** + * @since 2.0.0 + */ +export * as Either from "./Either.js" + +/** + * This module provides encoding & decoding functionality for: + * + * - base64 (RFC4648) + * - base64 (URL) + * - hex + * + * @since 2.0.0 + */ +export * as Encoding from "./Encoding.js" + +/** + * @since 2.0.0 + */ +export * as Equal from "./Equal.js" + +/** + * This module provides an implementation of the `Equivalence` type class, which defines a binary relation + * that is reflexive, symmetric, and transitive. In other words, it defines a notion of equivalence between values of a certain type. + * These properties are also known in mathematics as an "equivalence relation". + * + * @since 2.0.0 + */ +export * as Equivalence from "./Equivalence.js" + +/** + * @since 3.16.0 + * @experimental + */ +export * as ExecutionPlan from "./ExecutionPlan.js" + +/** + * @since 2.0.0 + */ +export * as ExecutionStrategy from "./ExecutionStrategy.js" + +/** + * @since 2.0.0 + */ +export * as Exit from "./Exit.js" + +/** + * @since 3.10.0 + */ +export * as FastCheck from "./FastCheck.js" + +/** + * @since 2.0.0 + */ +export * as Fiber from "./Fiber.js" + +/** + * @since 2.0.0 + */ +export * as FiberHandle from "./FiberHandle.js" + +/** + * @since 2.0.0 + */ +export * as FiberId from "./FiberId.js" + +/** + * @since 2.0.0 + */ +export * as FiberMap from "./FiberMap.js" + +/** + * @since 2.0.0 + */ +export * as FiberRef from "./FiberRef.js" + +/** + * @since 2.0.0 + */ +export * as FiberRefs from "./FiberRefs.js" + +/** + * @since 2.0.0 + */ +export * as FiberRefsPatch from "./FiberRefsPatch.js" + +/** + * @since 2.0.0 + */ +export * as FiberSet from "./FiberSet.js" + +/** + * @since 2.0.0 + */ +export * as FiberStatus from "./FiberStatus.js" + +/** + * @since 2.0.0 + */ +export * as Function from "./Function.js" + +/** + * The `GlobalValue` module ensures that a single instance of a value is created globally, + * even when modules are imported multiple times (e.g., due to mixing CommonJS and ESM builds) + * or during hot-reloading in development environments like Next.js or Remix. + * + * It achieves this by using a versioned global store, identified by a unique `Symbol` tied to + * the current version of the `effect` library. The store holds values that are keyed by an identifier, + * allowing the reuse of previously computed instances across imports or reloads. + * + * This pattern is particularly useful in scenarios where frequent reloading can cause services or + * single-instance objects to be recreated unnecessarily, such as in development environments with hot-reloading. + * + * @since 2.0.0 + */ +export * as GlobalValue from "./GlobalValue.js" + +/** + * @experimental + * @since 3.18.0 + */ +export * as Graph from "./Graph.js" + +/** + * @since 2.0.0 + */ +export * as GroupBy from "./GroupBy.js" + +/** + * @since 2.0.0 + */ +export * as HKT from "./HKT.js" + +/** + * @since 2.0.0 + */ +export * as Hash from "./Hash.js" + +/** + * @since 2.0.0 + */ +export * as HashMap from "./HashMap.js" + +/** + * @since 3.19.0 + * @experimental + */ +export * as HashRing from "./HashRing.js" + +/** + * # HashSet + * + * An immutable `HashSet` provides a collection of unique values with efficient + * lookup, insertion and removal. Once created, a `HashSet` cannot be modified; + * any operation that would alter the set instead returns a new `HashSet` with + * the changes. This immutability offers benefits like predictable state + * management and easier reasoning about your code. + * + * ## What Problem Does It Solve? + * + * `HashSet` solves the problem of maintaining an unsorted collection where each + * value appears exactly once, with fast operations for checking membership and + * adding/removing values. + * + * ## When to Use + * + * Use `HashSet` when you need: + * + * - A collection with no duplicate values + * - Efficient membership testing (**`O(1)`** average complexity) + * - Set operations like union, intersection, and difference + * - An immutable data structure that preserves functional programming patterns + * + * ## Advanced Features + * + * HashSet provides operations for: + * + * - Transforming sets with map and flatMap + * - Filtering elements with filter + * - Combining sets with union, intersection and difference + * - Performance optimizations via mutable operations in controlled contexts + * + * ## Performance Characteristics + * + * - **Lookup** operations ({@link module:HashSet.has}): **`O(1)`** average time + * complexity + * - **Insertion** operations ({@link module:HashSet.add}): **`O(1)`** average time + * complexity + * - **Removal** operations ({@link module:HashSet.remove}): **`O(1)`** average + * time complexity + * - **Set** operations ({@link module:HashSet.union}, + * {@link module:HashSet.intersection}): **`O(n)`** where n is the size of the + * smaller set + * - **Iteration**: **`O(n)`** where n is the size of the set + * + * The HashSet data structure implements the following traits: + * + * - {@link Iterable}: allows iterating over the values in the set + * - {@link Equal}: allows comparing two sets for value-based equality + * - {@link Pipeable}: allows chaining operations with the pipe operator + * - {@link Inspectable}: allows inspecting the contents of the set + * + * ## Operations Reference + * + * | Category | Operation | Description | Complexity | + * | ------------ | ----------------------------------- | ------------------------------------------- | ---------- | + * | constructors | {@link module:HashSet.empty} | Creates an empty HashSet | O(1) | + * | constructors | {@link module:HashSet.fromIterable} | Creates a HashSet from an iterable | O(n) | + * | constructors | {@link module:HashSet.make} | Creates a HashSet from multiple values | O(n) | + * | | | | | + * | elements | {@link module:HashSet.has} | Checks if a value exists in the set | O(1) avg | + * | elements | {@link module:HashSet.some} | Checks if any element satisfies a predicate | O(n) | + * | elements | {@link module:HashSet.every} | Checks if all elements satisfy a predicate | O(n) | + * | elements | {@link module:HashSet.isSubset} | Checks if a set is a subset of another | O(n) | + * | | | | | + * | getters | {@link module:HashSet.values} | Gets an iterator of all values | O(1) | + * | getters | {@link module:HashSet.toValues} | Gets an array of all values | O(n) | + * | getters | {@link module:HashSet.size} | Gets the number of elements | O(1) | + * | | | | | + * | mutations | {@link module:HashSet.add} | Adds a value to the set | O(1) avg | + * | mutations | {@link module:HashSet.remove} | Removes a value from the set | O(1) avg | + * | mutations | {@link module:HashSet.toggle} | Toggles a value's presence | O(1) avg | + * | | | | | + * | operations | {@link module:HashSet.difference} | Computes set difference (A - B) | O(n) | + * | operations | {@link module:HashSet.intersection} | Computes set intersection (A ∩ B) | O(n) | + * | operations | {@link module:HashSet.union} | Computes set union (A ∪ B) | O(n) | + * | | | | | + * | mapping | {@link module:HashSet.map} | Transforms each element | O(n) | + * | | | | | + * | sequencing | {@link module:HashSet.flatMap} | Transforms and flattens elements | O(n) | + * | | | | | + * | traversing | {@link module:HashSet.forEach} | Applies a function to each element | O(n) | + * | | | | | + * | folding | {@link module:HashSet.reduce} | Reduces the set to a single value | O(n) | + * | | | | | + * | filtering | {@link module:HashSet.filter} | Keeps elements that satisfy a predicate | O(n) | + * | | | | | + * | partitioning | {@link module:HashSet.partition} | Splits into two sets by a predicate | O(n) | + * + * ## Notes + * + * ### Composability with the Effect Ecosystem: + * + * This `HashSet` is designed to work seamlessly within the Effect ecosystem. It + * implements the {@link Iterable}, {@link Equal}, {@link Pipeable}, and + * {@link Inspectable} traits from Effect. This ensures compatibility with other + * Effect data structures and functionalities. For example, you can easily use + * Effect's `pipe` method to chain operations on the `HashSet`. + * + * **Equality of Elements with Effect's {@link Equal `Equal`} Trait:** + * + * This `HashSet` relies on Effect's {@link Equal} trait to determine the + * uniqueness of elements within the set. The way equality is checked depends on + * the type of the elements: + * + * - **Primitive Values:** For primitive JavaScript values like strings, numbers, + * booleans, `null`, and `undefined`, equality is determined by their value + * (similar to the `===` operator). + * - **Objects and Custom Types:** For objects and other custom types, equality is + * determined by whether those types implement the {@link Equal} interface + * themselves. If an element type implements `Equal`, the `HashSet` will + * delegate to that implementation to perform the equality check. This allows + * you to define custom logic for determining when two instances of your + * objects should be considered equal based on their properties, rather than + * just their object identity. + * + * ```ts + * import { Equal, Hash, HashSet } from "effect" + * + * class Person implements Equal.Equal { + * constructor( + * readonly id: number, // Unique identifier + * readonly name: string, + * readonly age: number + * ) {} + * + * // Define equality based on id, name, and age + * [Equal.symbol](that: Equal.Equal): boolean { + * if (that instanceof Person) { + * return ( + * Equal.equals(this.id, that.id) && + * Equal.equals(this.name, that.name) && + * Equal.equals(this.age, that.age) + * ) + * } + * return false + * } + * + * // Generate a hash code based on the unique id + * [Hash.symbol](): number { + * return Hash.hash(this.id) + * } + * } + * + * // Creating a HashSet with objects that implement the Equal interface + * const set = HashSet.empty().pipe( + * HashSet.add(new Person(1, "Alice", 30)), + * HashSet.add(new Person(1, "Alice", 30)) + * ) + * + * // HashSet recognizes them as equal, so only one element is stored + * console.log(HashSet.size(set)) + * // Output: 1 + * ``` + * + * **Simplifying Equality and Hashing with `Data` and `Schema`:** + * + * Effect's {@link Data} and {@link Schema `Schema.Data`} modules offer powerful + * ways to automatically handle the implementation of both the {@link Equal} and + * {@link Hash} traits for your custom data structures. + * + * - **`Data` Module:** By using constructors like `Data.struct`, `Data.tuple`, + * `Data.array`, or `Data.case` to define your data types, Effect + * automatically generates the necessary implementations for value-based + * equality and consistent hashing. This significantly reduces boilerplate and + * ensures correctness. + * + * ```ts + * import { HashSet, Data, Equal } from "effect" + * import assert from "node:assert/strict" + * + * // Data.* implements the `Equal` traits for us + * const person1 = Data.struct({ id: 1, name: "Alice", age: 30 }) + * const person2 = Data.struct({ id: 1, name: "Alice", age: 30 }) + * + * assert(Equal.equals(person1, person2)) + * + * const set = HashSet.empty().pipe( + * HashSet.add(person1), + * HashSet.add(person2) + * ) + * + * // HashSet recognizes them as equal, so only one element is stored + * console.log(HashSet.size(set)) // Output: 1 + * ``` + * + * - **`Schema` Module:** When defining data schemas using the {@link Schema} + * module, you can use `Schema.Data` to automatically include the `Equal` and + * `Hash` traits in the decoded objects. This is particularly important when + * working with `HashSet`. **For decoded objects to be correctly recognized as + * equal within a `HashSet`, ensure that the schema for those objects is + * defined using `Schema.Data`.** + * + * ```ts + * import { Equal, HashSet, Schema } from "effect" + * import assert from "node:assert/strict" + * + * // Schema.Data implements the `Equal` traits for us + * const PersonSchema = Schema.Data( + * Schema.Struct({ + * id: Schema.Number, + * name: Schema.String, + * age: Schema.Number + * }) + * ) + * + * const Person = Schema.decode(PersonSchema) + * + * const person1 = Person({ id: 1, name: "Alice", age: 30 }) + * const person2 = Person({ id: 1, name: "Alice", age: 30 }) + * + * assert(Equal.equals(person1, person2)) // Output: true + * + * const set = HashSet.empty().pipe( + * HashSet.add(person1), + * HashSet.add(person2) + * ) + * + * // HashSet thanks to Schema.Data implementation of the `Equal` trait, recognizes the two Person as equal, so only one element is stored + * console.log(HashSet.size(set)) // Output: 1 + * ``` + * + * ### Interoperability with the JavaScript Runtime: + * + * To interoperate with the regular JavaScript runtime, Effect's `HashSet` + * provides methods to access its elements in formats readily usable by + * JavaScript APIs: {@link values `HashSet.values`}, + * {@link toValues `HashSet.toValues`} + * + * ```ts + * import { HashSet } from "effect" + * + * const hashSet: HashSet.HashSet = HashSet.make(1, 2, 3) + * + * // Using HashSet.values to convert HashSet.HashSet to IterableIterator + * const iterable: IterableIterator = HashSet.values(hashSet) + * + * console.log(...iterable) // Logs: 1 2 3 + * + * // Using HashSet.toValues to convert HashSet.HashSet to Array + * const array: Array = HashSet.toValues(hashSet) + * + * console.log(array) // Logs: [ 1, 2, 3 ] + * ``` + * + * Be mindful of performance implications (both time and space complexity) when + * frequently converting between Effect's immutable HashSet and mutable + * JavaScript data structures, especially for large collections. + * + * @module HashSet + * @since 2.0.0 + */ +export * as HashSet from "./HashSet.js" + +/** + * @since 2.0.0 + */ +export * as Inspectable from "./Inspectable.js" + +/** + * This module provides utility functions for working with Iterables in TypeScript. + * + * @since 2.0.0 + */ +export * as Iterable from "./Iterable.js" + +/** + * @since 3.10.0 + */ +export * as JSONSchema from "./JSONSchema.js" + +/** + * @since 2.0.0 + */ +export * as KeyedPool from "./KeyedPool.js" + +/** + * A `Layer` describes how to build one or more services in your + * application. Services can be injected into effects via + * `Effect.provideService`. Effects can require services via `Effect.service`. + * + * Layer can be thought of as recipes for producing bundles of services, given + * their dependencies (other services). + * + * Construction of services can be effectful and utilize resources that must be + * acquired and safely released when the services are done being utilized. + * + * By default layers are shared, meaning that if the same layer is used twice + * the layer will only be allocated a single time. + * + * Because of their excellent composition properties, layers are the idiomatic + * way in Effect-TS to create services that depend on other services. + * + * @since 2.0.0 + */ +export * as Layer from "./Layer.js" + +/** + * @since 3.14.0 + * @experimental + */ +export * as LayerMap from "./LayerMap.js" + +/** + * A data type for immutable linked lists representing ordered collections of elements of type `A`. + * + * This data type is optimal for last-in-first-out (LIFO), stack-like access patterns. If you need another access pattern, for example, random access or FIFO, consider using a collection more suited to this than `List`. + * + * **Performance** + * + * - Time: `List` has `O(1)` prepend and head/tail access. Most other operations are `O(n)` on the number of elements in the list. This includes the index-based lookup of elements, `length`, `append` and `reverse`. + * - Space: `List` implements structural sharing of the tail list. This means that many operations are either zero- or constant-memory cost. + * + * @since 2.0.0 + */ +export * as List from "./List.js" + +/** + * @since 2.0.0 + */ +export * as LogLevel from "./LogLevel.js" + +/** + * @since 2.0.0 + */ +export * as LogSpan from "./LogSpan.js" + +/** + * @since 2.0.0 + */ +export * as Logger from "./Logger.js" + +/** + * @since 3.8.0 + * @experimental + */ +export * as Mailbox from "./Mailbox.js" + +/** + * @since 2.0.0 + */ +export * as ManagedRuntime from "./ManagedRuntime.js" + +/** + * The `effect/match` module provides a type-safe pattern matching system for + * TypeScript. Inspired by functional programming, it simplifies conditional + * logic by replacing verbose if/else or switch statements with a structured and + * expressive API. + * + * This module supports matching against types, values, and discriminated unions + * while enforcing exhaustiveness checking to ensure all cases are handled. + * + * Although pattern matching is not yet a native JavaScript feature, + * `effect/match` offers a reliable implementation that is available today. + * + * **How Pattern Matching Works** + * + * Pattern matching follows a structured process: + * + * - **Creating a matcher**: Define a `Matcher` that operates on either a + * specific `Match.type` or `Match.value`. + * + * - **Defining patterns**: Use combinators such as `Match.when`, `Match.not`, + * and `Match.tag` to specify matching conditions. + * + * - **Completing the match**: Apply a finalizer such as `Match.exhaustive`, + * `Match.orElse`, or `Match.option` to determine how unmatched cases should + * be handled. + * + * @since 1.0.0 + */ +export * as Match from "./Match.js" + +/** + * @since 2.0.0 + */ +export * as MergeDecision from "./MergeDecision.js" + +/** + * @since 2.0.0 + */ +export * as MergeState from "./MergeState.js" + +/** + * @since 2.0.0 + */ +export * as MergeStrategy from "./MergeStrategy.js" + +/** + * @since 2.0.0 + */ +export * as Metric from "./Metric.js" + +/** + * @since 2.0.0 + */ +export * as MetricBoundaries from "./MetricBoundaries.js" + +/** + * @since 2.0.0 + */ +export * as MetricHook from "./MetricHook.js" + +/** + * @since 2.0.0 + */ +export * as MetricKey from "./MetricKey.js" + +/** + * @since 2.0.0 + */ +export * as MetricKeyType from "./MetricKeyType.js" + +/** + * @since 2.0.0 + */ +export * as MetricLabel from "./MetricLabel.js" + +/** + * @since 2.0.0 + */ +export * as MetricPair from "./MetricPair.js" + +/** + * @since 2.0.0 + */ +export * as MetricPolling from "./MetricPolling.js" + +/** + * @since 2.0.0 + */ +export * as MetricRegistry from "./MetricRegistry.js" + +/** + * @since 2.0.0 + */ +export * as MetricState from "./MetricState.js" + +/** + * A lightweight alternative to the `Effect` data type, with a subset of the functionality. + * + * @since 3.4.0 + * @experimental + */ +export * as Micro from "./Micro.js" + +/** + * @since 2.0.0 + * + * Enables low level framework authors to run on their own isolated effect version + */ +export * as ModuleVersion from "./ModuleVersion.js" + +/** + * @since 2.0.0 + */ +export * as MutableHashMap from "./MutableHashMap.js" + +/** + * # MutableHashSet + * + * A mutable `MutableHashSet` provides a collection of unique values with + * efficient lookup, insertion and removal. Unlike its immutable sibling + * {@link module:HashSet}, a `MutableHashSet` can be modified in-place; + * operations like add, remove, and clear directly modify the original set + * rather than creating a new one. This mutability offers benefits like improved + * performance in scenarios where you need to build or modify a set + * incrementally. + * + * ## What Problem Does It Solve? + * + * `MutableHashSet` solves the problem of maintaining an unsorted collection + * where each value appears exactly once, with fast operations for checking + * membership and adding/removing values, in contexts where mutability is + * preferred for performance or implementation simplicity. + * + * ## When to Use + * + * Use `MutableHashSet` when you need: + * + * - A collection with no duplicate values + * - Efficient membership testing (**`O(1)`** average complexity) + * - In-place modifications for better performance + * - A set that will be built or modified incrementally + * - Local mutability in otherwise immutable code + * + * ## Advanced Features + * + * MutableHashSet provides operations for: + * + * - Adding and removing elements with direct mutation + * - Checking for element existence + * - Clearing all elements at once + * - Converting to/from other collection types + * + * ## Performance Characteristics + * + * - **Lookup** operations ({@link module:MutableHashSet.has}): **`O(1)`** average + * time complexity + * - **Insertion** operations ({@link module:MutableHashSet.add}): **`O(1)`** + * average time complexity + * - **Removal** operations ({@link module:MutableHashSet.remove}): **`O(1)`** + * average time complexity + * - **Iteration**: **`O(n)`** where n is the size of the set + * + * The MutableHashSet data structure implements the following traits: + * + * - {@link Iterable}: allows iterating over the values in the set + * - {@link Pipeable}: allows chaining operations with the pipe operator + * - {@link Inspectable}: allows inspecting the contents of the set + * + * ## Operations Reference + * + * | Category | Operation | Description | Complexity | + * | ------------ | ------------------------------------------ | ----------------------------------- | ---------- | + * | constructors | {@link module:MutableHashSet.empty} | Creates an empty MutableHashSet | O(1) | + * | constructors | {@link module:MutableHashSet.fromIterable} | Creates a set from an iterable | O(n) | + * | constructors | {@link module:MutableHashSet.make} | Creates a set from multiple values | O(n) | + * | | | | | + * | elements | {@link module:MutableHashSet.has} | Checks if a value exists in the set | O(1) avg | + * | elements | {@link module:MutableHashSet.add} | Adds a value to the set | O(1) avg | + * | elements | {@link module:MutableHashSet.remove} | Removes a value from the set | O(1) avg | + * | elements | {@link module:MutableHashSet.size} | Gets the number of elements | O(1) | + * | elements | {@link module:MutableHashSet.clear} | Removes all values from the set | O(1) | + * + * ## Notes + * + * ### Mutability Considerations: + * + * Unlike most data structures in the Effect ecosystem, `MutableHashSet` is + * mutable. This means that operations like `add`, `remove`, and `clear` modify + * the original set rather than creating a new one. This can lead to more + * efficient code in some scenarios, but requires careful handling to avoid + * unexpected side effects. + * + * ### When to Choose `MutableHashSet` vs {@link module:HashSet}: + * + * - Use `MutableHashSet` when you need to build or modify a set incrementally and + * performance is a priority + * - Use `HashSet` when you want immutability guarantees and functional + * programming patterns + * - Consider using {@link module:HashSet}'s bounded mutation context (via + * {@link module:HashSet.beginMutation}, {@link module:HashSet.endMutation}, and + * {@link module:HashSet.mutate} methods) when you need temporary mutability + * within an otherwise immutable context - this approach might be sufficient + * for many use cases without requiring a separate `MutableHashSet` + * - `MutableHashSet` is often useful for local operations where the mutability is + * contained and doesn't leak into the broader application + * + * @module MutableHashSet + * @since 2.0.0 + */ +export * as MutableHashSet from "./MutableHashSet.js" + +/** + * @since 2.0.0 + */ +export * as MutableList from "./MutableList.js" + +/** + * @since 2.0.0 + */ +export * as MutableQueue from "./MutableQueue.js" + +/** + * @since 2.0.0 + */ +export * as MutableRef from "./MutableRef.js" + +/** + * @since 2.0.0 + */ +export * as NonEmptyIterable from "./NonEmptyIterable.js" + +/** + * # Number + * + * This module provides utility functions and type class instances for working + * with the `number` type in TypeScript. It includes functions for basic + * arithmetic operations, as well as type class instances for `Equivalence` and + * `Order`. + * + * ## Operations Reference + * + * | Category | Operation | Description | Domain | Co-domain | + * | ------------ | ------------------------------------------ | ------------------------------------------------------- | ------------------------------ | --------------------- | + * | constructors | {@link module:Number.parse} | Safely parses a string to a number | `string` | `Option` | + * | | | | | | + * | math | {@link module:Number.sum} | Adds two numbers | `number`, `number` | `number` | + * | math | {@link module:Number.sumAll} | Sums all numbers in a collection | `Iterable` | `number` | + * | math | {@link module:Number.subtract} | Subtracts one number from another | `number`, `number` | `number` | + * | math | {@link module:Number.multiply} | Multiplies two numbers | `number`, `number` | `number` | + * | math | {@link module:Number.multiplyAll} | Multiplies all numbers in a collection | `Iterable` | `number` | + * | math | {@link module:Number.divide} | Safely divides handling division by zero | `number`, `number` | `Option` | + * | math | {@link module:Number.unsafeDivide} | Divides but misbehaves for division by zero | `number`, `number` | `number` | + * | math | {@link module:Number.remainder} | Calculates remainder of division | `number`, `number` | `number` | + * | math | {@link module:Number.increment} | Adds 1 to a number | `number` | `number` | + * | math | {@link module:Number.decrement} | Subtracts 1 from a number | `number` | `number` | + * | math | {@link module:Number.sign} | Determines the sign of a number | `number` | `Ordering` | + * | math | {@link module:Number.nextPow2} | Finds the next power of 2 | `number` | `number` | + * | math | {@link module:Number.round} | Rounds a number with specified precision | `number`, `number` | `number` | + * | | | | | | + * | predicates | {@link module:Number.between} | Checks if a number is in a range | `number`, `{minimum, maximum}` | `boolean` | + * | predicates | {@link module:Number.lessThan} | Checks if one number is less than another | `number`, `number` | `boolean` | + * | predicates | {@link module:Number.lessThanOrEqualTo} | Checks if one number is less than or equal | `number`, `number` | `boolean` | + * | predicates | {@link module:Number.greaterThan} | Checks if one number is greater than another | `number`, `number` | `boolean` | + * | predicates | {@link module:Number.greaterThanOrEqualTo} | Checks if one number is greater or equal | `number`, `number` | `boolean` | + * | | | | | | + * | guards | {@link module:Number.isNumber} | Type guard for JavaScript numbers | `unknown` | `boolean` | + * | | | | | | + * | comparison | {@link module:Number.min} | Returns the minimum of two numbers | `number`, `number` | `number` | + * | comparison | {@link module:Number.max} | Returns the maximum of two numbers | `number`, `number` | `number` | + * | comparison | {@link module:Number.clamp} | Restricts a number to a range | `number`, `{minimum, maximum}` | `number` | + * | | | | | | + * | instances | {@link module:Number.Equivalence} | Equivalence instance for numbers | | `Equivalence` | + * | instances | {@link module:Number.Order} | Order instance for numbers | | `Order` | + * | | | | | | + * | errors | {@link module:Number.DivisionByZeroError} | Error thrown by unsafeDivide | | | + * + * ## Composition Patterns and Type Safety + * + * When building function pipelines, understanding how types flow through + * operations is critical: + * + * ### Composing with type-preserving operations + * + * Most operations in this module are type-preserving (`number → number`), + * making them easily composable in pipelines: + * + * ```ts + * import { pipe } from "effect" + * import * as Number from "effect/Number" + * + * const result = pipe( + * 10, + * Number.increment, // number → number + * Number.multiply(2), // number → number + * Number.round(1) // number → number + * ) // Result: number (21) + * ``` + * + * ### Working with Option results + * + * Operations that might fail (like division by zero) return Option types and + * require Option combinators: + * + * ```ts + * import { pipe, Option } from "effect" + * import * as Number from "effect/Number" + * + * const result = pipe( + * 10, + * Number.divide(0), // number → Option + * Option.getOrElse(() => 0) // Option → number + * ) // Result: number (0) + * ``` + * + * ### Composition best practices + * + * - Chain type-preserving operations for maximum composability + * - Use Option combinators when working with potentially failing operations + * - Consider using Effect for operations that might fail with specific errors + * - Remember that all operations maintain JavaScript's floating-point precision + * limitations + * + * @module Number + * @since 2.0.0 + * @see {@link module:BigInt} for more similar operations on `bigint` types + * @see {@link module:BigDecimal} for more similar operations on `BigDecimal` types + */ +export * as Number from "./Number.js" + +/** + * @since 2.0.0 + */ +export * as Option from "./Option.js" + +/** + * This module provides an implementation of the `Order` type class which is used to define a total ordering on some type `A`. + * An order is defined by a relation `<=`, which obeys the following laws: + * + * - either `x <= y` or `y <= x` (totality) + * - if `x <= y` and `y <= x`, then `x == y` (antisymmetry) + * - if `x <= y` and `y <= z`, then `x <= z` (transitivity) + * + * The truth table for compare is defined as follows: + * + * | `x <= y` | `x >= y` | Ordering | | + * | -------- | -------- | -------- | --------------------- | + * | `true` | `true` | `0` | corresponds to x == y | + * | `true` | `false` | `< 0` | corresponds to x < y | + * | `false` | `true` | `> 0` | corresponds to x > y | + * + * @since 2.0.0 + */ +export * as Order from "./Order.js" + +/** + * @since 2.0.0 + */ +export * as Ordering from "./Ordering.js" + +/** + * @since 3.10.0 + */ +export * as ParseResult from "./ParseResult.js" + +/** + * @since 3.19.4 + * @experimental + */ +export * as PartitionedSemaphore from "./PartitionedSemaphore.js" + +/** + * @since 2.0.0 + */ +export * as Pipeable from "./Pipeable.js" + +/** + * @since 2.0.0 + */ +export * as Pool from "./Pool.js" + +/** + * This module provides a collection of functions for working with predicates and refinements. + * + * A `Predicate` is a function that takes a value of type `A` and returns a boolean. + * It is used to check if a value satisfies a certain condition. + * + * A `Refinement` is a special type of predicate that not only checks a condition + * but also provides a type guard, allowing TypeScript to narrow the type of the input + * value from `A` to a more specific type `B` within a conditional block. + * + * The module includes: + * - Basic predicates and refinements for common types (e.g., `isString`, `isNumber`). + * - Combinators to create new predicates from existing ones (e.g., `and`, `or`, `not`). + * - Advanced combinators for working with data structures (e.g., `tuple`, `struct`). + * - Type-level utilities for inspecting predicate and refinement types. + * + * @since 2.0.0 + */ +export * as Predicate from "./Predicate.js" + +/** + * @since 3.10.0 + */ +export * as Pretty from "./Pretty.js" + +/** + * @since 2.0.0 + */ +export * as PrimaryKey from "./PrimaryKey.js" + +/** + * @since 2.0.0 + */ +export * as PubSub from "./PubSub.js" + +/** + * @since 2.0.0 + */ +export * as Queue from "./Queue.js" + +/** + * @since 2.0.0 + */ +export * as Random from "./Random.js" + +/** + * Limits the number of calls to a resource to a maximum amount in some interval. + * + * @since 2.0.0 + */ +export * as RateLimiter from "./RateLimiter.js" + +/** + * @since 3.5.0 + */ +export * as RcMap from "./RcMap.js" + +/** + * @since 3.5.0 + */ +export * as RcRef from "./RcRef.js" + +/** + * @since 2.0.0 + */ +export * as Readable from "./Readable.js" + +/** + * This module provides utility functions for working with records in TypeScript. + * + * @since 2.0.0 + */ +export * as Record from "./Record.js" + +/** + * @since 2.0.0 + */ +export * as RedBlackTree from "./RedBlackTree.js" + +/** + * The Redacted module provides functionality for handling sensitive information + * securely within your application. By using the `Redacted` data type, you can + * ensure that sensitive values are not accidentally exposed in logs or error + * messages. + * + * @since 3.3.0 + */ +export * as Redacted from "./Redacted.js" + +/** + * @since 2.0.0 + */ +export * as Ref from "./Ref.js" + +/** + * This module provides utility functions for working with RegExp in TypeScript. + * + * @since 2.0.0 + */ +export * as RegExp from "./RegExp.js" + +/** + * @since 2.0.0 + */ +export * as Reloadable from "./Reloadable.js" + +/** + * @since 2.0.0 + */ +export * as Request from "./Request.js" + +/** + * @since 2.0.0 + */ +export * as RequestBlock from "./RequestBlock.js" + +/** + * @since 2.0.0 + */ +export * as RequestResolver from "./RequestResolver.js" + +/** + * @since 2.0.0 + */ +export * as Resource from "./Resource.js" + +/** + * @since 2.0.0 + */ +export * as Runtime from "./Runtime.js" + +/** + * @since 2.0.0 + */ +export * as RuntimeFlags from "./RuntimeFlags.js" + +/** + * @since 2.0.0 + */ +export * as RuntimeFlagsPatch from "./RuntimeFlagsPatch.js" + +/** + * @since 2.0.0 + */ +export * as STM from "./STM.js" + +/** + * @since 2.0.0 + */ +export * as Schedule from "./Schedule.js" + +/** + * @since 2.0.0 + */ +export * as ScheduleDecision from "./ScheduleDecision.js" + +/** + * @since 2.0.0 + */ +export * as ScheduleInterval from "./ScheduleInterval.js" + +/** + * @since 2.0.0 + */ +export * as ScheduleIntervals from "./ScheduleIntervals.js" + +/** + * @since 2.0.0 + */ +export * as Scheduler from "./Scheduler.js" + +/** + * @since 3.10.0 + */ +export * as Schema from "./Schema.js" + +/** + * @since 3.10.0 + */ +export * as SchemaAST from "./SchemaAST.js" + +/** + * @since 2.0.0 + */ +export * as Scope from "./Scope.js" + +/** + * @since 2.0.0 + */ +export * as ScopedCache from "./ScopedCache.js" + +/** + * @since 2.0.0 + */ +export * as ScopedRef from "./ScopedRef.js" + +/** + * @since 2.0.0 + * @deprecated + */ +export * as Secret from "./Secret.js" + +/** + * @since 2.0.0 + */ +export * as SingleProducerAsyncInput from "./SingleProducerAsyncInput.js" + +/** + * @since 2.0.0 + */ +export * as Sink from "./Sink.js" + +/** + * @since 2.0.0 + */ +export * as SortedMap from "./SortedMap.js" + +/** + * @since 2.0.0 + */ +export * as SortedSet from "./SortedSet.js" + +/** + * @since 2.0.0 + */ +export * as Stream from "./Stream.js" + +/** + * @since 2.0.0 + */ +export * as StreamEmit from "./StreamEmit.js" + +/** + * @since 2.0.0 + */ +export * as StreamHaltStrategy from "./StreamHaltStrategy.js" + +/** + * @since 2.0.0 + */ +export * as Streamable from "./Streamable.js" + +/** + * This module provides utility functions and type class instances for working with the `string` type in TypeScript. + * It includes functions for basic string manipulation, as well as type class instances for + * `Equivalence` and `Order`. + * + * @since 2.0.0 + */ +export * as String from "./String.js" + +/** + * This module provides utility functions for working with structs in TypeScript. + * + * @since 2.0.0 + */ +export * as Struct from "./Struct.js" + +/** + * @since 2.0.0 + */ +export * as Subscribable from "./Subscribable.js" + +/** + * @since 2.0.0 + */ +export * as SubscriptionRef from "./SubscriptionRef.js" + +/** + * A `Supervisor` is allowed to supervise the launching and termination of + * fibers, producing some visible value of type `T` from the supervision. + * + * @since 2.0.0 + */ +export * as Supervisor from "./Supervisor.js" + +/** + * @since 2.0.0 + */ +export * as Symbol from "./Symbol.js" + +/** + * @since 2.0.0 + */ +export * as SynchronizedRef from "./SynchronizedRef.js" + +/** + * @since 2.0.0 + */ +export * as TArray from "./TArray.js" + +/** + * @since 2.0.0 + */ +export * as TDeferred from "./TDeferred.js" + +/** + * @since 2.0.0 + */ +export * as TMap from "./TMap.js" + +/** + * @since 2.0.0 + */ +export * as TPriorityQueue from "./TPriorityQueue.js" + +/** + * @since 2.0.0 + */ +export * as TPubSub from "./TPubSub.js" + +/** + * @since 2.0.0 + */ +export * as TQueue from "./TQueue.js" + +/** + * @since 2.0.0 + */ +export * as TRandom from "./TRandom.js" + +/** + * @since 2.0.0 + */ +export * as TReentrantLock from "./TReentrantLock.js" + +/** + * @since 2.0.0 + */ +export * as TRef from "./TRef.js" + +/** + * @since 2.0.0 + */ +export * as TSemaphore from "./TSemaphore.js" + +/** + * @since 2.0.0 + */ +export * as TSet from "./TSet.js" + +/** + * @since 3.10.0 + */ +export * as TSubscriptionRef from "./TSubscriptionRef.js" + +/** + * @since 2.0.0 + */ +export * as Take from "./Take.js" + +/** + * @since 2.0.0 + */ +export * as TestAnnotation from "./TestAnnotation.js" + +/** + * @since 2.0.0 + */ +export * as TestAnnotationMap from "./TestAnnotationMap.js" + +/** + * @since 2.0.0 + */ +export * as TestAnnotations from "./TestAnnotations.js" + +/** + * @since 2.0.0 + */ +export * as TestClock from "./TestClock.js" + +/** + * @since 2.0.0 + */ +export * as TestConfig from "./TestConfig.js" + +/** + * @since 2.0.0 + */ +export * as TestContext from "./TestContext.js" + +/** + * @since 2.0.0 + */ +export * as TestLive from "./TestLive.js" + +/** + * @since 2.0.0 + */ +export * as TestServices from "./TestServices.js" + +/** + * @since 2.0.0 + */ +export * as TestSized from "./TestSized.js" + +/** + * @since 2.0.0 + */ +export * as Tracer from "./Tracer.js" + +/** + * A `Trie` is used for locating specific `string` keys from within a set. + * + * It works similar to `HashMap`, but with keys required to be `string`. + * This constraint unlocks some performance optimizations and new methods to get string prefixes (e.g. `keysWithPrefix`, `longestPrefixOf`). + * + * Prefix search is also the main feature that makes a `Trie` more suited than `HashMap` for certain usecases. + * + * A `Trie` is often used to store a dictionary (list of words) that can be searched + * in a manner that allows for efficient generation of completion lists + * (e.g. predict the rest of a word a user is typing). + * + * A `Trie` has O(n) lookup time where `n` is the size of the key, + * or even less than `n` on search misses. + * + * @since 2.0.0 + */ +export * as Trie from "./Trie.js" + +/** + * This module provides utility functions for working with tuples in TypeScript. + * + * @since 2.0.0 + */ +export * as Tuple from "./Tuple.js" + +/** + * A collection of types that are commonly used types. + * + * @since 2.0.0 + */ +export * as Types from "./Types.js" + +/** + * @since 2.0.0 + */ +export * as Unify from "./Unify.js" + +/** + * @since 2.0.0 + */ +export * as UpstreamPullRequest from "./UpstreamPullRequest.js" + +/** + * @since 2.0.0 + */ +export * as UpstreamPullStrategy from "./UpstreamPullStrategy.js" + +/** + * @since 2.0.0 + */ +export * as Utils from "./Utils.js" diff --git a/backend/node_modules/effect/src/internal/array.ts b/backend/node_modules/effect/src/internal/array.ts new file mode 100644 index 0000000000000000000000000000000000000000..24d7ee216e1ebcf5646fe9ad79d08670e9f8ac1f --- /dev/null +++ b/backend/node_modules/effect/src/internal/array.ts @@ -0,0 +1,8 @@ +/** + * @since 2.0.0 + */ + +import type { NonEmptyArray } from "../Array.js" + +/** @internal */ +export const isNonEmptyArray = (self: ReadonlyArray): self is NonEmptyArray => self.length > 0 diff --git a/backend/node_modules/effect/src/internal/blockedRequests.ts b/backend/node_modules/effect/src/internal/blockedRequests.ts new file mode 100644 index 0000000000000000000000000000000000000000..baa627a86db3ff168f1d69ae284710e17f52a504 --- /dev/null +++ b/backend/node_modules/effect/src/internal/blockedRequests.ts @@ -0,0 +1,520 @@ +import * as Chunk from "../Chunk.js" +import type * as Deferred from "../Deferred.js" +import * as Either from "../Either.js" +import * as Equal from "../Equal.js" +import type { FiberId } from "../FiberId.js" +import * as HashMap from "../HashMap.js" +import * as List from "../List.js" +import * as Option from "../Option.js" +import { hasProperty } from "../Predicate.js" +import type * as Request from "../Request.js" +import type * as RequestBlock from "../RequestBlock.js" +import type * as RequestResolver from "../RequestResolver.js" + +/** @internal */ +export const empty: RequestBlock.RequestBlock = { + _tag: "Empty" +} + +/** + * Combines this collection of blocked requests with the specified collection + * of blocked requests, in parallel. + * + * @internal + */ +export const par = ( + self: RequestBlock.RequestBlock, + that: RequestBlock.RequestBlock +): RequestBlock.RequestBlock => ({ + _tag: "Par", + left: self, + right: that +}) + +/** + * Combines this collection of blocked requests with the specified collection + * of blocked requests, in sequence. + * + * @internal + */ +export const seq = ( + self: RequestBlock.RequestBlock, + that: RequestBlock.RequestBlock +): RequestBlock.RequestBlock => ({ + _tag: "Seq", + left: self, + right: that +}) + +/** + * Constructs a collection of blocked requests from the specified blocked + * request and data source. + * + * @internal + */ +export const single = ( + dataSource: RequestResolver.RequestResolver, + blockedRequest: Request.Entry +): RequestBlock.RequestBlock => ({ + _tag: "Single", + dataSource: dataSource as any, + blockedRequest +}) + +/** @internal */ +export const MapRequestResolversReducer = ( + f: (dataSource: RequestResolver.RequestResolver) => RequestResolver.RequestResolver +): RequestBlock.RequestBlock.Reducer => ({ + emptyCase: () => empty, + parCase: (left, right) => par(left, right), + seqCase: (left, right) => seq(left, right), + singleCase: (dataSource, blockedRequest) => single(f(dataSource), blockedRequest as any) +}) + +type BlockedRequestsCase = ParCase | SeqCase + +interface ParCase { + readonly _tag: "ParCase" +} + +interface SeqCase { + readonly _tag: "SeqCase" +} + +/** + * Transforms all data sources with the specified data source aspect, which + * can change the environment type of data sources but must preserve the + * request type of each data source. + * + * @internal + */ +export const mapRequestResolvers = ( + self: RequestBlock.RequestBlock, + f: (dataSource: RequestResolver.RequestResolver) => RequestResolver.RequestResolver +): RequestBlock.RequestBlock => reduce(self, MapRequestResolversReducer(f)) + +/** + * Folds over the cases of this collection of blocked requests with the + * specified functions. + * + * @internal + */ +export const reduce = ( + self: RequestBlock.RequestBlock, + reducer: RequestBlock.RequestBlock.Reducer +): Z => { + let input: List.List = List.of(self) + let output = List.empty>() + while (List.isCons(input)) { + const current: RequestBlock.RequestBlock = input.head + switch (current._tag) { + case "Empty": { + output = List.cons(Either.right(reducer.emptyCase()), output) + input = input.tail + break + } + case "Par": { + output = List.cons(Either.left({ _tag: "ParCase" }), output) + input = List.cons(current.left, List.cons(current.right, input.tail)) + break + } + case "Seq": { + output = List.cons(Either.left({ _tag: "SeqCase" }), output) + input = List.cons(current.left, List.cons(current.right, input.tail)) + break + } + case "Single": { + const result = reducer.singleCase(current.dataSource, current.blockedRequest) + output = List.cons(Either.right(result), output) + input = input.tail + break + } + } + } + const result = List.reduce(output, List.empty(), (acc, current) => { + switch (current._tag) { + case "Left": { + const left = List.unsafeHead(acc) + const right = List.unsafeHead(List.unsafeTail(acc)) + const tail = List.unsafeTail(List.unsafeTail(acc)) + switch (current.left._tag) { + case "ParCase": { + return List.cons(reducer.parCase(left, right), tail) + } + case "SeqCase": { + return List.cons(reducer.seqCase(left, right), tail) + } + } + } + case "Right": { + return List.cons(current.right, acc) + } + } + }) + if (List.isNil(result)) { + throw new Error( + "BUG: BlockedRequests.reduce - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + } + return result.head +} + +/** + * Flattens a collection of blocked requests into a collection of pipelined + * and batched requests that can be submitted for execution. + * + * @internal + */ +export const flatten = ( + self: RequestBlock.RequestBlock +): List.List => { + let current = List.of(self) + let updated = List.empty() + // eslint-disable-next-line no-constant-condition + while (1) { + const [parallel, sequential] = List.reduce( + current, + [parallelCollectionEmpty(), List.empty()] as const, + ([parallel, sequential], blockedRequest) => { + const [par, seq] = step(blockedRequest) + return [ + parallelCollectionCombine(parallel, par), + List.appendAll(sequential, seq) + ] + } + ) + updated = merge(updated, parallel) + if (List.isNil(sequential)) { + return List.reverse(updated) + } + current = sequential + } + throw new Error( + "BUG: BlockedRequests.flatten - please report an issue at https://github.com/Effect-TS/effect/issues" + ) +} + +/** + * Takes one step in evaluating a collection of blocked requests, returning a + * collection of blocked requests that can be performed in parallel and a list + * of blocked requests that must be performed sequentially after those + * requests. + */ +const step = ( + requests: RequestBlock.RequestBlock +): [ParallelCollection, List.List] => { + let current: RequestBlock.RequestBlock = requests + let parallel = parallelCollectionEmpty() + let stack = List.empty() + let sequential = List.empty() + // eslint-disable-next-line no-constant-condition + while (1) { + switch (current._tag) { + case "Empty": { + if (List.isNil(stack)) { + return [parallel, sequential] + } + current = stack.head + stack = stack.tail + break + } + case "Par": { + stack = List.cons(current.right, stack) + current = current.left + break + } + case "Seq": { + const left = current.left + const right = current.right + switch (left._tag) { + case "Empty": { + current = right + break + } + case "Par": { + const l = left.left + const r = left.right + current = par(seq(l, right), seq(r, right)) + break + } + case "Seq": { + const l = left.left + const r = left.right + current = seq(l, seq(r, right)) + break + } + case "Single": { + current = left + sequential = List.cons(right, sequential) + break + } + } + break + } + case "Single": { + parallel = parallelCollectionAdd( + parallel, + current + ) + if (List.isNil(stack)) { + return [parallel, sequential] + } + current = stack.head + stack = stack.tail + break + } + } + } + throw new Error( + "BUG: BlockedRequests.step - please report an issue at https://github.com/Effect-TS/effect/issues" + ) +} + +/** + * Merges a collection of requests that must be executed sequentially with a + * collection of requests that can be executed in parallel. If the collections + * are both from the same single data source then the requests can be + * pipelined while preserving ordering guarantees. + */ +const merge = ( + sequential: List.List, + parallel: ParallelCollection +): List.List => { + if (List.isNil(sequential)) { + return List.of(parallelCollectionToSequentialCollection(parallel)) + } + if (parallelCollectionIsEmpty(parallel)) { + return sequential + } + const seqHeadKeys = sequentialCollectionKeys(sequential.head) + const parKeys = parallelCollectionKeys(parallel) + if ( + seqHeadKeys.length === 1 && + parKeys.length === 1 && + Equal.equals(seqHeadKeys[0], parKeys[0]) + ) { + return List.cons( + sequentialCollectionCombine( + sequential.head, + parallelCollectionToSequentialCollection(parallel) + ), + sequential.tail + ) + } + return List.cons(parallelCollectionToSequentialCollection(parallel), sequential) +} + +// +// circular +// + +/** @internal */ +export const EntryTypeId: Request.EntryTypeId = Symbol.for( + "effect/RequestBlock/Entry" +) as Request.EntryTypeId + +/** @internal */ +class EntryImpl> implements Request.Entry { + readonly [EntryTypeId] = blockedRequestVariance + constructor( + readonly request: A, + readonly result: Deferred.Deferred, Request.Request.Error>, + readonly listeners: Request.Listeners, + readonly ownerId: FiberId, + readonly state: { + completed: boolean + } + ) {} +} + +const blockedRequestVariance = { + /* c8 ignore next */ + _R: (_: never) => _ +} + +/** @internal */ +export const isEntry = (u: unknown): u is Request.Entry => hasProperty(u, EntryTypeId) + +/** @internal */ +export const makeEntry = >( + options: { + readonly request: A + readonly result: Deferred.Deferred, Request.Request.Error> + readonly listeners: Request.Listeners + readonly ownerId: FiberId + readonly state: { completed: boolean } + } +): Request.Entry => new EntryImpl(options.request, options.result, options.listeners, options.ownerId, options.state) + +/** @internal */ +export const RequestBlockParallelTypeId = Symbol.for( + "effect/RequestBlock/RequestBlockParallel" +) + +const parallelVariance = { + /* c8 ignore next */ + _R: (_: never) => _ +} + +class ParallelImpl implements ParallelCollection { + readonly [RequestBlockParallelTypeId] = parallelVariance + constructor( + readonly map: HashMap.HashMap< + RequestResolver.RequestResolver, + Chunk.Chunk> + > + ) {} +} + +/** @internal */ +export const parallelCollectionEmpty = (): ParallelCollection => new ParallelImpl(HashMap.empty()) + +/** @internal */ +export const parallelCollectionMake = ( + dataSource: RequestResolver.RequestResolver, + blockedRequest: Request.Entry +): ParallelCollection => new ParallelImpl(HashMap.make([dataSource, Chunk.of(blockedRequest)]) as any) + +/** @internal */ +export const parallelCollectionAdd = ( + self: ParallelCollection, + blockedRequest: RequestBlock.Single +): ParallelCollection => + new ParallelImpl(HashMap.modifyAt( + self.map, + blockedRequest.dataSource, + (_) => + Option.orElseSome( + Option.map(_, Chunk.append(blockedRequest.blockedRequest)), + () => Chunk.of(blockedRequest.blockedRequest) + ) + )) + +/** @internal */ +export const parallelCollectionCombine = ( + self: ParallelCollection, + that: ParallelCollection +): ParallelCollection => + new ParallelImpl(HashMap.reduce(self.map, that.map, (map, value, key) => + HashMap.set( + map, + key, + Option.match(HashMap.get(map, key), { + onNone: () => value, + onSome: (other) => Chunk.appendAll(value, other) + }) + ))) + +/** @internal */ +export const parallelCollectionIsEmpty = (self: ParallelCollection): boolean => HashMap.isEmpty(self.map) + +/** @internal */ +export const parallelCollectionKeys = ( + self: ParallelCollection +): Array> => Array.from(HashMap.keys(self.map)) as any + +/** @internal */ +export const parallelCollectionToSequentialCollection = ( + self: ParallelCollection +): SequentialCollection => sequentialCollectionMake(HashMap.map(self.map, (x) => Chunk.of(x)) as any) + +// TODO +// /** @internal */ +// export const parallelCollectionToChunk = ( +// self: ParallelCollection +// ): Array<[RequestResolver.RequestResolver, Array>]> => Array.from(self.map) as any + +/** @internal */ +export const SequentialCollectionTypeId = Symbol.for( + "effect/RequestBlock/RequestBlockSequential" +) + +const sequentialVariance = { + /* c8 ignore next */ + _R: (_: never) => _ +} + +class SequentialImpl implements SequentialCollection { + readonly [SequentialCollectionTypeId] = sequentialVariance + constructor( + readonly map: HashMap.HashMap< + RequestResolver.RequestResolver, + Chunk.Chunk>> + > + ) {} +} + +/** @internal */ +export const sequentialCollectionMake = ( + map: HashMap.HashMap< + RequestResolver.RequestResolver, + Chunk.Chunk>> + > +): SequentialCollection => new SequentialImpl(map as any) + +/** @internal */ +export const sequentialCollectionCombine = ( + self: SequentialCollection, + that: SequentialCollection +): SequentialCollection => + new SequentialImpl(HashMap.reduce(that.map, self.map, (map, value, key) => + HashMap.set( + map, + key, + Option.match(HashMap.get(map, key), { + onNone: () => Chunk.empty(), + onSome: (a) => Chunk.appendAll(a, value) + }) + ))) + +/** @internal */ +export const sequentialCollectionIsEmpty = (self: SequentialCollection): boolean => HashMap.isEmpty(self.map) + +/** @internal */ +export const sequentialCollectionKeys = ( + self: SequentialCollection +): Array> => Array.from(HashMap.keys(self.map)) as any + +/** @internal */ +export const sequentialCollectionToChunk = ( + self: SequentialCollection +): Array<[RequestResolver.RequestResolver, Chunk.Chunk>>]> => + Array.from(self.map) as any + +/** @internal */ +export type RequestBlockParallelTypeId = typeof RequestBlockParallelTypeId + +/** @internal */ +export interface ParallelCollection extends ParallelCollection.Variance { + readonly map: HashMap.HashMap< + RequestResolver.RequestResolver, + Chunk.Chunk> + > +} + +/** @internal */ +export declare namespace ParallelCollection { + /** @internal */ + export interface Variance { + readonly [RequestBlockParallelTypeId]: {} + } +} + +/** @internal */ +export type SequentialCollectionTypeId = typeof SequentialCollectionTypeId + +/** @internal */ +export interface SequentialCollection extends SequentialCollection.Variance { + readonly map: HashMap.HashMap< + RequestResolver.RequestResolver, + Chunk.Chunk>> + > +} + +/** @internal */ +export declare namespace SequentialCollection { + /** @internal */ + export interface Variance { + readonly [SequentialCollectionTypeId]: {} + } +} diff --git a/backend/node_modules/effect/src/internal/cache.ts b/backend/node_modules/effect/src/internal/cache.ts new file mode 100644 index 0000000000000000000000000000000000000000..480b749073df5a112ff0b75112ab6e5bb8016b6c --- /dev/null +++ b/backend/node_modules/effect/src/internal/cache.ts @@ -0,0 +1,733 @@ +import type * as Cache from "../Cache.js" +import type * as Clock from "../Clock.js" +import * as Context from "../Context.js" +import * as Deferred from "../Deferred.js" +import * as Duration from "../Duration.js" +import type * as Effect from "../Effect.js" +import * as Either from "../Either.js" +import * as Equal from "../Equal.js" +import * as Exit from "../Exit.js" +import type * as FiberId from "../FiberId.js" +import { pipe } from "../Function.js" +import * as Hash from "../Hash.js" +import * as MutableHashMap from "../MutableHashMap.js" +import * as MutableQueue from "../MutableQueue.js" +import * as MutableRef from "../MutableRef.js" +import * as Option from "../Option.js" +import { hasProperty } from "../Predicate.js" +import * as effect from "./core-effect.js" +import * as core from "./core.js" +import * as Data from "./data.js" +import { none } from "./fiberId.js" +import * as fiberRuntime from "./fiberRuntime.js" + +/** + * A `MapValue` represents a value in the cache. A value may either be + * `Pending` with a `Promise` that will contain the result of computing the + * lookup function, when it is available, or `Complete` with an `Exit` value + * that contains the result of computing the lookup function. + * + * @internal + */ +export type MapValue = + | Complete + | Pending + | Refreshing + +/** @internal */ +export interface Complete { + readonly _tag: "Complete" + readonly key: MapKey + readonly exit: Exit.Exit + readonly entryStats: Cache.EntryStats + readonly timeToLiveMillis: number +} + +/** @internal */ +export interface Pending { + readonly _tag: "Pending" + readonly key: MapKey + readonly deferred: Deferred.Deferred +} + +/** @internal */ +export interface Refreshing { + readonly _tag: "Refreshing" + readonly deferred: Deferred.Deferred + readonly complete: Complete +} + +/** @internal */ +export const complete = ( + key: MapKey, + exit: Exit.Exit, + entryStats: Cache.EntryStats, + timeToLiveMillis: number +): MapValue => + Data.struct({ + _tag: "Complete" as const, + key, + exit, + entryStats, + timeToLiveMillis + }) + +/** @internal */ +export const pending = ( + key: MapKey, + deferred: Deferred.Deferred +): MapValue => + Data.struct({ + _tag: "Pending" as const, + key, + deferred + }) + +/** @internal */ +export const refreshing = ( + deferred: Deferred.Deferred, + complete: Complete +): MapValue => + Data.struct({ + _tag: "Refreshing" as const, + deferred, + complete + }) + +/** @internal */ +export const MapKeyTypeId = Symbol.for("effect/Cache/MapKey") + +/** @internal */ +export type MapKeyTypeId = typeof MapKeyTypeId + +/** + * A `MapKey` represents a key in the cache. It contains mutable references + * to the previous key and next key in the `KeySet` to support an efficient + * implementation of a sorted set of keys. + * + * @internal + */ +export interface MapKey extends Equal.Equal { + readonly [MapKeyTypeId]: MapKeyTypeId + readonly current: K + previous: MapKey | undefined // mutable by design + next: MapKey | undefined // mutable by design +} + +class MapKeyImpl implements MapKey { + readonly [MapKeyTypeId]: MapKeyTypeId = MapKeyTypeId + previous: MapKey | undefined = undefined + next: MapKey | undefined = undefined + constructor(readonly current: K) {} + [Hash.symbol](): number { + return pipe( + Hash.hash(this.current), + Hash.combine(Hash.hash(this.previous)), + Hash.combine(Hash.hash(this.next)), + Hash.cached(this) + ) + } + [Equal.symbol](that: unknown): boolean { + if (this === that) { + return true + } + return isMapKey(that) && + Equal.equals(this.current, that.current) && + Equal.equals(this.previous, that.previous) && + Equal.equals(this.next, that.next) + } +} + +/** @internal */ +export const makeMapKey = (current: K): MapKey => new MapKeyImpl(current) + +/** @internal */ +export const isMapKey = (u: unknown): u is MapKey => hasProperty(u, MapKeyTypeId) + +/** + * A `KeySet` is a sorted set of keys in the cache ordered by last access. + * For efficiency, the set is implemented in terms of a doubly linked list + * and is not safe for concurrent access. + * + * @internal + */ +export interface KeySet { + head: MapKey | undefined // mutable by design + tail: MapKey | undefined // mutable by design + /** + * Adds the specified key to the set. + */ + add(key: MapKey): void + /** + * Removes the lowest priority key from the set. + */ + remove(): MapKey | undefined +} + +class KeySetImpl implements KeySet { + head: MapKey | undefined = undefined + tail: MapKey | undefined = undefined + add(key: MapKey): void { + if (key !== this.tail) { + if (this.tail === undefined) { + this.head = key + this.tail = key + } else { + const previous = key.previous + const next = key.next + if (next !== undefined) { + key.next = undefined + if (previous !== undefined) { + previous.next = next + next.previous = previous + } else { + this.head = next + this.head.previous = undefined + } + } + this.tail.next = key + key.previous = this.tail + this.tail = key + } + } + } + remove(): MapKey | undefined { + const key = this.head + if (key !== undefined) { + const next = key.next + if (next !== undefined) { + key.next = undefined + this.head = next + this.head.previous = undefined + } else { + this.head = undefined + this.tail = undefined + } + } + return key + } +} + +/** @internal */ +export const makeKeySet = (): KeySet => new KeySetImpl() + +/** + * The `CacheState` represents the mutable state underlying the cache. + * + * @internal + */ +export interface CacheState { + map: MutableHashMap.MutableHashMap> // mutable by design + keys: KeySet // mutable by design + accesses: MutableQueue.MutableQueue> // mutable by design + updating: MutableRef.MutableRef // mutable by design + hits: number // mutable by design + misses: number // mutable by design +} + +/** + * Constructs a new `CacheState` from the specified values. + * + * @internal + */ +export const makeCacheState = ( + map: MutableHashMap.MutableHashMap>, + keys: KeySet, + accesses: MutableQueue.MutableQueue>, + updating: MutableRef.MutableRef, + hits: number, + misses: number +): CacheState => ({ + map, + keys, + accesses, + updating, + hits, + misses +}) + +/** + * Constructs an initial cache state. + * + * @internal + */ +export const initialCacheState = (): CacheState => + makeCacheState( + MutableHashMap.empty(), + makeKeySet(), + MutableQueue.unbounded(), + MutableRef.make(false), + 0, + 0 + ) + +/** @internal */ +const CacheSymbolKey = "effect/Cache" + +/** @internal */ +export const CacheTypeId: Cache.CacheTypeId = Symbol.for( + CacheSymbolKey +) as Cache.CacheTypeId + +const cacheVariance = { + /* c8 ignore next */ + _Key: (_: any) => _, + /* c8 ignore next */ + _Error: (_: never) => _, + /* c8 ignore next */ + _Value: (_: any) => _ +} + +/** @internal */ +const ConsumerCacheSymbolKey = "effect/ConsumerCache" + +/** @internal */ +export const ConsumerCacheTypeId: Cache.ConsumerCacheTypeId = Symbol.for( + ConsumerCacheSymbolKey +) as Cache.ConsumerCacheTypeId + +const consumerCacheVariance = { + /* c8 ignore next */ + _Key: (_: any) => _, + /* c8 ignore next */ + _Error: (_: never) => _, + /* c8 ignore next */ + _Value: (_: never) => _ +} + +/** @internal */ +export const makeCacheStats = ( + options: { + readonly hits: number + readonly misses: number + readonly size: number + } +): Cache.CacheStats => options + +/** @internal */ +export const makeEntryStats = (loadedMillis: number): Cache.EntryStats => ({ + loadedMillis +}) + +class CacheImpl implements Cache.Cache { + readonly [CacheTypeId] = cacheVariance + readonly [ConsumerCacheTypeId] = consumerCacheVariance + readonly cacheState: CacheState + constructor( + readonly capacity: number, + readonly context: Context.Context, + readonly fiberId: FiberId.FiberId, + readonly lookup: Cache.Lookup, + readonly timeToLive: (exit: Exit.Exit) => Duration.DurationInput + ) { + this.cacheState = initialCacheState() + } + + get(key: Key): Effect.Effect { + return core.map(this.getEither(key), Either.merge) + } + + get cacheStats(): Effect.Effect { + return core.sync(() => + makeCacheStats({ + hits: this.cacheState.hits, + misses: this.cacheState.misses, + size: MutableHashMap.size(this.cacheState.map) + }) + ) + } + + getOption(key: Key): Effect.Effect, Error> { + return core.suspend(() => + Option.match(MutableHashMap.get(this.cacheState.map, key), { + onNone: () => { + const mapKey = makeMapKey(key) + this.trackAccess(mapKey) + this.trackMiss() + return core.succeed(Option.none()) + }, + onSome: (value) => this.resolveMapValue(value) + }) + ) + } + + getOptionComplete(key: Key): Effect.Effect> { + return core.suspend(() => + Option.match(MutableHashMap.get(this.cacheState.map, key), { + onNone: () => { + const mapKey = makeMapKey(key) + this.trackAccess(mapKey) + this.trackMiss() + return core.succeed(Option.none()) + }, + onSome: (value) => this.resolveMapValue(value, true) as Effect.Effect> + }) + ) + } + + contains(key: Key): Effect.Effect { + return core.sync(() => MutableHashMap.has(this.cacheState.map, key)) + } + + entryStats(key: Key): Effect.Effect> { + return core.sync(() => { + const option = MutableHashMap.get(this.cacheState.map, key) + if (Option.isSome(option)) { + switch (option.value._tag) { + case "Complete": { + const loaded = option.value.entryStats.loadedMillis + return Option.some(makeEntryStats(loaded)) + } + case "Pending": { + return Option.none() + } + case "Refreshing": { + const loaded = option.value.complete.entryStats.loadedMillis + return Option.some(makeEntryStats(loaded)) + } + } + } + return Option.none() + }) + } + + getEither(key: Key): Effect.Effect, Error> { + return core.suspend((): Effect.Effect, Error> => { + const k = key + let mapKey: MapKey | undefined = undefined + let deferred: Deferred.Deferred | undefined = undefined + let value = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, k)) + if (value === undefined) { + deferred = Deferred.unsafeMake(this.fiberId) + mapKey = makeMapKey(k) + if (MutableHashMap.has(this.cacheState.map, k)) { + value = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, k)) + } else { + MutableHashMap.set(this.cacheState.map, k, pending(mapKey, deferred)) + } + } + if (value === undefined) { + this.trackAccess(mapKey!) + this.trackMiss() + return core.map(this.lookupValueOf(key, deferred!), Either.right) + } else { + return core.flatMap( + this.resolveMapValue(value), + Option.match({ + onNone: () => this.getEither(key), + onSome: (value) => core.succeed(Either.left(value)) + }) + ) + } + }) + } + + invalidate(key: Key): Effect.Effect { + return core.sync(() => { + MutableHashMap.remove(this.cacheState.map, key) + }) + } + + invalidateWhen(key: Key, when: (value: Value) => boolean): Effect.Effect { + return core.sync(() => { + const value = MutableHashMap.get(this.cacheState.map, key) + if (Option.isSome(value) && value.value._tag === "Complete") { + if (value.value.exit._tag === "Success") { + if (when(value.value.exit.value)) { + MutableHashMap.remove(this.cacheState.map, key) + } + } + } + }) + } + + get invalidateAll(): Effect.Effect { + return core.sync(() => { + this.cacheState.map = MutableHashMap.empty() + }) + } + + refresh(key: Key): Effect.Effect { + return effect.clockWith((clock) => + core.suspend(() => { + const k = key + const deferred: Deferred.Deferred = Deferred.unsafeMake(this.fiberId) + let value = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, k)) + if (value === undefined) { + if (MutableHashMap.has(this.cacheState.map, k)) { + value = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, k)) + } else { + MutableHashMap.set(this.cacheState.map, k, pending(makeMapKey(k), deferred)) + } + } + if (value === undefined) { + return core.asVoid(this.lookupValueOf(key, deferred)) + } else { + switch (value._tag) { + case "Complete": { + if (this.hasExpired(clock, value.timeToLiveMillis)) { + const found = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, k)) + if (Equal.equals(found, value)) { + MutableHashMap.remove(this.cacheState.map, k) + } + return core.asVoid(this.get(key)) + } + // Only trigger the lookup if we're still the current value, `completedResult` + return pipe( + this.lookupValueOf(key, deferred), + effect.when(() => { + const current = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, k)) + if (Equal.equals(current, value)) { + const mapValue = refreshing(deferred, value as Complete) + MutableHashMap.set(this.cacheState.map, k, mapValue) + return true + } + return false + }), + core.asVoid + ) + } + case "Pending": { + return Deferred.await(value.deferred) + } + case "Refreshing": { + return Deferred.await(value.deferred) + } + } + } + }) + ) + } + + set(key: Key, value: Value): Effect.Effect { + return effect.clockWith((clock) => + core.sync(() => { + const now = clock.unsafeCurrentTimeMillis() + const k = key + const lookupResult = Exit.succeed(value) + const mapValue = complete( + makeMapKey(k), + lookupResult, + makeEntryStats(now), + now + Duration.toMillis(Duration.decode(this.timeToLive(lookupResult))) + ) + MutableHashMap.set( + this.cacheState.map, + k, + mapValue as Complete + ) + }) + ) + } + + get size(): Effect.Effect { + return core.sync(() => { + return MutableHashMap.size(this.cacheState.map) + }) + } + + get values(): Effect.Effect> { + return core.sync(() => { + const values: Array = [] + for (const entry of this.cacheState.map) { + if (entry[1]._tag === "Complete" && entry[1].exit._tag === "Success") { + values.push(entry[1].exit.value) + } + } + return values + }) + } + + get entries(): Effect.Effect> { + return core.sync(() => { + const values: Array<[Key, Value]> = [] + for (const entry of this.cacheState.map) { + if (entry[1]._tag === "Complete" && entry[1].exit._tag === "Success") { + values.push([entry[0], entry[1].exit.value]) + } + } + return values + }) + } + + get keys(): Effect.Effect> { + return core.sync(() => { + const keys: Array = [] + for (const entry of this.cacheState.map) { + if (entry[1]._tag === "Complete" && entry[1].exit._tag === "Success") { + keys.push(entry[0]) + } + } + return keys + }) + } + + resolveMapValue( + value: MapValue, + ignorePending = false + ): Effect.Effect, Error> { + return effect.clockWith((clock) => { + switch (value._tag) { + case "Complete": { + this.trackAccess(value.key) + if (this.hasExpired(clock, value.timeToLiveMillis)) { + MutableHashMap.remove(this.cacheState.map, value.key.current) + return core.succeed(Option.none()) + } + this.trackHit() + return core.map(value.exit, Option.some) + } + case "Pending": { + this.trackAccess(value.key) + this.trackHit() + if (ignorePending) { + return core.succeed(Option.none()) + } + return core.map(Deferred.await(value.deferred), Option.some) + } + case "Refreshing": { + this.trackAccess(value.complete.key) + this.trackHit() + if (this.hasExpired(clock, value.complete.timeToLiveMillis)) { + if (ignorePending) { + return core.succeed(Option.none()) + } + return core.map(Deferred.await(value.deferred), Option.some) + } + return core.map(value.complete.exit, Option.some) + } + } + }) + } + + trackHit(): void { + this.cacheState.hits = this.cacheState.hits + 1 + } + + trackMiss(): void { + this.cacheState.misses = this.cacheState.misses + 1 + } + + trackAccess(key: MapKey): void { + MutableQueue.offer(this.cacheState.accesses, key) + if (MutableRef.compareAndSet(this.cacheState.updating, false, true)) { + let loop = true + while (loop) { + const key = MutableQueue.poll(this.cacheState.accesses, MutableQueue.EmptyMutableQueue) + if (key === MutableQueue.EmptyMutableQueue) { + loop = false + } else { + this.cacheState.keys.add(key) + } + } + let size = MutableHashMap.size(this.cacheState.map) + loop = size > this.capacity + while (loop) { + const key = this.cacheState.keys.remove() + if (key !== undefined) { + if (MutableHashMap.has(this.cacheState.map, key.current)) { + MutableHashMap.remove(this.cacheState.map, key.current) + size = size - 1 + loop = size > this.capacity + } + } else { + loop = false + } + } + MutableRef.set(this.cacheState.updating, false) + } + } + + hasExpired(clock: Clock.Clock, timeToLiveMillis: number): boolean { + return clock.unsafeCurrentTimeMillis() > timeToLiveMillis + } + + lookupValueOf( + input: Key, + deferred: Deferred.Deferred + ): Effect.Effect { + return effect.clockWith((clock) => + core.suspend(() => { + const key = input + return pipe( + this.lookup(input), + core.provideContext(this.context), + core.exit, + core.flatMap((exit) => { + const now = clock.unsafeCurrentTimeMillis() + const stats = makeEntryStats(now) + const value = complete( + makeMapKey(key), + exit, + stats, + now + Duration.toMillis(Duration.decode(this.timeToLive(exit))) + ) + MutableHashMap.set(this.cacheState.map, key, value) + return core.zipRight( + Deferred.done(deferred, exit), + exit + ) + }), + core.onInterrupt(() => + core.zipRight( + Deferred.interrupt(deferred), + core.sync(() => { + MutableHashMap.remove(this.cacheState.map, key) + }) + ) + ) + ) + }) + ) + } +} + +/** @internal */ +export const make = ( + options: { + readonly capacity: number + readonly timeToLive: Duration.DurationInput + readonly lookup: Cache.Lookup + } +): Effect.Effect, never, Environment> => { + const timeToLive = Duration.decode(options.timeToLive) + return makeWith({ + capacity: options.capacity, + lookup: options.lookup, + timeToLive: () => timeToLive + }) +} + +/** @internal */ +export const makeWith = ( + options: { + readonly capacity: number + readonly lookup: Cache.Lookup + readonly timeToLive: (exit: Exit.Exit) => Duration.DurationInput + } +): Effect.Effect, never, Environment> => + core.map( + fiberRuntime.all([core.context(), core.fiberId]), + ([context, fiberId]) => + new CacheImpl( + options.capacity, + context, + fiberId, + options.lookup, + (exit) => Duration.decode(options.timeToLive(exit)) + ) + ) + +/** @internal */ +export const unsafeMakeWith = ( + capacity: number, + lookup: Cache.Lookup, + timeToLive: (exit: Exit.Exit) => Duration.DurationInput +): Cache.Cache => + new CacheImpl( + capacity, + Context.empty() as Context.Context, + none, + lookup, + (exit) => Duration.decode(timeToLive(exit)) + ) diff --git a/backend/node_modules/effect/src/internal/cause.ts b/backend/node_modules/effect/src/internal/cause.ts new file mode 100644 index 0000000000000000000000000000000000000000..e557a9142352c64b09bf7b5290c953166c4c0527 --- /dev/null +++ b/backend/node_modules/effect/src/internal/cause.ts @@ -0,0 +1,1049 @@ +import * as Arr from "../Array.js" +import type * as Cause from "../Cause.js" +import * as Chunk from "../Chunk.js" +import * as Either from "../Either.js" +import * as Equal from "../Equal.js" +import type * as FiberId from "../FiberId.js" +import { constFalse, constTrue, dual, identity, pipe } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import * as Hash from "../Hash.js" +import * as HashSet from "../HashSet.js" +import { NodeInspectSymbol, stringifyCircular, toJSON } from "../Inspectable.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import type { Predicate, Refinement } from "../Predicate.js" +import { hasProperty, isFunction } from "../Predicate.js" +import type { AnySpan, Span } from "../Tracer.js" +import type * as Types from "../Types.js" +import { getBugErrorMessage } from "./errors.js" +import * as OpCodes from "./opCodes/cause.js" + +// ----------------------------------------------------------------------------- +// Models +// ----------------------------------------------------------------------------- + +/** @internal */ +const CauseSymbolKey = "effect/Cause" + +/** @internal */ +export const CauseTypeId: Cause.CauseTypeId = Symbol.for( + CauseSymbolKey +) as Cause.CauseTypeId + +const variance = { + /* c8 ignore next */ + _E: (_: never) => _ +} + +/** @internal */ +const proto = { + [CauseTypeId]: variance, + [Hash.symbol](this: Cause.Cause): number { + return pipe( + Hash.hash(CauseSymbolKey), + Hash.combine(Hash.hash(flattenCause(this))), + Hash.cached(this) + ) + }, + [Equal.symbol](this: Cause.Cause, that: unknown): boolean { + return isCause(that) && causeEquals(this, that) + }, + pipe() { + return pipeArguments(this, arguments) + }, + toJSON(this: Cause.Cause) { + switch (this._tag) { + case "Empty": + return { _id: "Cause", _tag: this._tag } + case "Die": + return { _id: "Cause", _tag: this._tag, defect: toJSON(this.defect) } + case "Interrupt": + return { _id: "Cause", _tag: this._tag, fiberId: this.fiberId.toJSON() } + case "Fail": + return { _id: "Cause", _tag: this._tag, failure: toJSON(this.error) } + case "Sequential": + case "Parallel": + return { _id: "Cause", _tag: this._tag, left: toJSON(this.left), right: toJSON(this.right) } + } + }, + toString(this: Cause.Cause) { + return pretty(this) + }, + [NodeInspectSymbol](this: Cause.Cause) { + return this.toJSON() + } +} + +// ----------------------------------------------------------------------------- +// Constructors +// ----------------------------------------------------------------------------- + +/** @internal */ +export const empty: Cause.Cause = (() => { + const o = Object.create(proto) + o._tag = OpCodes.OP_EMPTY + return o +})() + +/** @internal */ +export const fail = (error: E): Cause.Cause => { + const o = Object.create(proto) + o._tag = OpCodes.OP_FAIL + o.error = error + return o +} + +/** @internal */ +export const die = (defect: unknown): Cause.Cause => { + const o = Object.create(proto) + o._tag = OpCodes.OP_DIE + o.defect = defect + return o +} + +/** @internal */ +export const interrupt = (fiberId: FiberId.FiberId): Cause.Cause => { + const o = Object.create(proto) + o._tag = OpCodes.OP_INTERRUPT + o.fiberId = fiberId + return o +} + +/** @internal */ +export const parallel = (left: Cause.Cause, right: Cause.Cause): Cause.Cause => { + const o = Object.create(proto) + o._tag = OpCodes.OP_PARALLEL + o.left = left + o.right = right + return o +} + +/** @internal */ +export const sequential = (left: Cause.Cause, right: Cause.Cause): Cause.Cause => { + const o = Object.create(proto) + o._tag = OpCodes.OP_SEQUENTIAL + o.left = left + o.right = right + return o +} + +// ----------------------------------------------------------------------------- +// Refinements +// ----------------------------------------------------------------------------- + +/** @internal */ +export const isCause = (u: unknown): u is Cause.Cause => hasProperty(u, CauseTypeId) + +/** @internal */ +export const isEmptyType = (self: Cause.Cause): self is Cause.Empty => self._tag === OpCodes.OP_EMPTY + +/** @internal */ +export const isFailType = (self: Cause.Cause): self is Cause.Fail => self._tag === OpCodes.OP_FAIL + +/** @internal */ +export const isDieType = (self: Cause.Cause): self is Cause.Die => self._tag === OpCodes.OP_DIE + +/** @internal */ +export const isInterruptType = (self: Cause.Cause): self is Cause.Interrupt => self._tag === OpCodes.OP_INTERRUPT + +/** @internal */ +export const isSequentialType = (self: Cause.Cause): self is Cause.Sequential => + self._tag === OpCodes.OP_SEQUENTIAL + +/** @internal */ +export const isParallelType = (self: Cause.Cause): self is Cause.Parallel => self._tag === OpCodes.OP_PARALLEL + +// ----------------------------------------------------------------------------- +// Getters +// ----------------------------------------------------------------------------- + +/** @internal */ +export const size = (self: Cause.Cause): number => reduceWithContext(self, void 0, SizeCauseReducer) + +/** @internal */ +export const isEmpty = (self: Cause.Cause): boolean => { + if (self._tag === OpCodes.OP_EMPTY) { + return true + } + return reduce(self, true, (acc, cause) => { + switch (cause._tag) { + case OpCodes.OP_EMPTY: { + return Option.some(acc) + } + case OpCodes.OP_DIE: + case OpCodes.OP_FAIL: + case OpCodes.OP_INTERRUPT: { + return Option.some(false) + } + default: { + return Option.none() + } + } + }) +} + +/** @internal */ +export const isFailure = (self: Cause.Cause): boolean => Option.isSome(failureOption(self)) + +/** @internal */ +export const isDie = (self: Cause.Cause): boolean => Option.isSome(dieOption(self)) + +/** @internal */ +export const isInterrupted = (self: Cause.Cause): boolean => Option.isSome(interruptOption(self)) + +/** @internal */ +export const isInterruptedOnly = (self: Cause.Cause): boolean => + reduceWithContext(undefined, IsInterruptedOnlyCauseReducer)(self) + +/** @internal */ +export const failures = (self: Cause.Cause): Chunk.Chunk => + Chunk.reverse( + reduce, E>( + self, + Chunk.empty(), + (list, cause) => + cause._tag === OpCodes.OP_FAIL ? + Option.some(pipe(list, Chunk.prepend(cause.error))) : + Option.none() + ) + ) + +/** @internal */ +export const defects = (self: Cause.Cause): Chunk.Chunk => + Chunk.reverse( + reduce, E>( + self, + Chunk.empty(), + (list, cause) => + cause._tag === OpCodes.OP_DIE ? + Option.some(pipe(list, Chunk.prepend(cause.defect))) : + Option.none() + ) + ) + +/** @internal */ +export const interruptors = (self: Cause.Cause): HashSet.HashSet => + reduce(self, HashSet.empty(), (set, cause) => + cause._tag === OpCodes.OP_INTERRUPT ? + Option.some(pipe(set, HashSet.add(cause.fiberId))) : + Option.none()) + +/** @internal */ +export const failureOption = (self: Cause.Cause): Option.Option => + find(self, (cause) => + cause._tag === OpCodes.OP_FAIL ? + Option.some(cause.error) : + Option.none()) + +/** @internal */ +export const failureOrCause = (self: Cause.Cause): Either.Either, E> => { + const option = failureOption(self) + switch (option._tag) { + case "None": { + // no `E` inside this `Cause`, so it can be safely cast to `never` + return Either.right(self as Cause.Cause) + } + case "Some": { + return Either.left(option.value) + } + } +} + +/** @internal */ +export const dieOption = (self: Cause.Cause): Option.Option => + find(self, (cause) => + cause._tag === OpCodes.OP_DIE ? + Option.some(cause.defect) : + Option.none()) + +/** @internal */ +export const flipCauseOption = (self: Cause.Cause>): Option.Option> => + match(self, { + onEmpty: Option.some>(empty), + onFail: Option.map(fail), + onDie: (defect) => Option.some(die(defect)), + onInterrupt: (fiberId) => Option.some(interrupt(fiberId)), + onSequential: Option.mergeWith(sequential), + onParallel: Option.mergeWith(parallel) + }) + +/** @internal */ +export const interruptOption = (self: Cause.Cause): Option.Option => + find(self, (cause) => + cause._tag === OpCodes.OP_INTERRUPT ? + Option.some(cause.fiberId) : + Option.none()) + +/** @internal */ +export const keepDefects = (self: Cause.Cause): Option.Option> => + match(self, { + onEmpty: Option.none(), + onFail: () => Option.none(), + onDie: (defect) => Option.some(die(defect)), + onInterrupt: () => Option.none(), + onSequential: Option.mergeWith(sequential), + onParallel: Option.mergeWith(parallel) + }) + +/** @internal */ +export const keepDefectsAndElectFailures = (self: Cause.Cause): Option.Option> => + match(self, { + onEmpty: Option.none(), + onFail: (failure) => Option.some(die(failure)), + onDie: (defect) => Option.some(die(defect)), + onInterrupt: () => Option.none(), + onSequential: Option.mergeWith(sequential), + onParallel: Option.mergeWith(parallel) + }) + +/** @internal */ +export const linearize = (self: Cause.Cause): HashSet.HashSet> => + match(self, { + onEmpty: HashSet.empty(), + onFail: (error) => HashSet.make(fail(error)), + onDie: (defect) => HashSet.make(die(defect)), + onInterrupt: (fiberId) => HashSet.make(interrupt(fiberId)), + onSequential: (leftSet, rightSet) => + HashSet.flatMap(leftSet, (leftCause) => HashSet.map(rightSet, (rightCause) => sequential(leftCause, rightCause))), + onParallel: (leftSet, rightSet) => + HashSet.flatMap(leftSet, (leftCause) => HashSet.map(rightSet, (rightCause) => parallel(leftCause, rightCause))) + }) + +/** @internal */ +export const stripFailures = (self: Cause.Cause): Cause.Cause => + match(self, { + onEmpty: empty, + onFail: () => empty, + onDie: die, + onInterrupt: interrupt, + onSequential: sequential, + onParallel: parallel + }) + +/** @internal */ +export const electFailures = (self: Cause.Cause): Cause.Cause => + match(self, { + onEmpty: empty, + onFail: die, + onDie: die, + onInterrupt: interrupt, + onSequential: sequential, + onParallel: parallel + }) + +/** @internal */ +export const stripSomeDefects = dual< + (pf: (defect: unknown) => Option.Option) => (self: Cause.Cause) => Option.Option>, + (self: Cause.Cause, pf: (defect: unknown) => Option.Option) => Option.Option> +>( + 2, + (self: Cause.Cause, pf: (defect: unknown) => Option.Option): Option.Option> => + match(self, { + onEmpty: Option.some>(empty), + onFail: (error) => Option.some(fail(error)), + onDie: (defect) => { + const option = pf(defect) + return Option.isSome(option) ? Option.none() : Option.some(die(defect)) + }, + onInterrupt: (fiberId) => Option.some(interrupt(fiberId)), + onSequential: Option.mergeWith(sequential), + onParallel: Option.mergeWith(parallel) + }) +) + +// ----------------------------------------------------------------------------- +// Mapping +// ----------------------------------------------------------------------------- + +/** @internal */ +export const as = dual< + (error: E2) => (self: Cause.Cause) => Cause.Cause, + (self: Cause.Cause, error: E2) => Cause.Cause +>(2, (self, error) => map(self, () => error)) + +/** @internal */ +export const map = dual< + (f: (e: E) => E2) => (self: Cause.Cause) => Cause.Cause, + (self: Cause.Cause, f: (e: E) => E2) => Cause.Cause +>(2, (self, f) => flatMap(self, (e) => fail(f(e)))) + +// ----------------------------------------------------------------------------- +// Sequencing +// ----------------------------------------------------------------------------- + +/** @internal */ +export const flatMap = dual< + (f: (e: E) => Cause.Cause) => (self: Cause.Cause) => Cause.Cause, + (self: Cause.Cause, f: (e: E) => Cause.Cause) => Cause.Cause +>(2, (self, f) => + match(self, { + onEmpty: empty, + onFail: (error) => f(error), + onDie: (defect) => die(defect), + onInterrupt: (fiberId) => interrupt(fiberId), + onSequential: (left, right) => sequential(left, right), + onParallel: (left, right) => parallel(left, right) + })) + +/** @internal */ +export const flatten = (self: Cause.Cause>): Cause.Cause => flatMap(self, identity) + +/** @internal */ +export const andThen: { + (f: (e: E) => Cause.Cause): (self: Cause.Cause) => Cause.Cause + (f: Cause.Cause): (self: Cause.Cause) => Cause.Cause + (self: Cause.Cause, f: (e: E) => Cause.Cause): Cause.Cause + (self: Cause.Cause, f: Cause.Cause): Cause.Cause +} = dual( + 2, + (self: Cause.Cause, f: ((e: E) => Cause.Cause) | Cause.Cause): Cause.Cause => + isFunction(f) ? flatMap(self, f) : flatMap(self, () => f) +) + +// ----------------------------------------------------------------------------- +// Equality +// ----------------------------------------------------------------------------- + +/** @internal */ +export const contains = dual< + (that: Cause.Cause) => (self: Cause.Cause) => boolean, + (self: Cause.Cause, that: Cause.Cause) => boolean +>(2, (self, that) => { + if (that._tag === OpCodes.OP_EMPTY || self === that) { + return true + } + return reduce(self, false, (accumulator, cause) => { + return Option.some(accumulator || causeEquals(cause, that)) + }) +}) + +/** @internal */ +const causeEquals = (left: Cause.Cause, right: Cause.Cause): boolean => { + let leftStack: Chunk.Chunk> = Chunk.of(left) + let rightStack: Chunk.Chunk> = Chunk.of(right) + while (Chunk.isNonEmpty(leftStack) && Chunk.isNonEmpty(rightStack)) { + const [leftParallel, leftSequential] = pipe( + Chunk.headNonEmpty(leftStack), + reduce( + [HashSet.empty(), Chunk.empty>()] as const, + ([parallel, sequential], cause) => { + const [par, seq] = evaluateCause(cause) + return Option.some( + [ + pipe(parallel, HashSet.union(par)), + pipe(sequential, Chunk.appendAll(seq)) + ] as const + ) + } + ) + ) + const [rightParallel, rightSequential] = pipe( + Chunk.headNonEmpty(rightStack), + reduce( + [HashSet.empty(), Chunk.empty>()] as const, + ([parallel, sequential], cause) => { + const [par, seq] = evaluateCause(cause) + return Option.some( + [ + pipe(parallel, HashSet.union(par)), + pipe(sequential, Chunk.appendAll(seq)) + ] as const + ) + } + ) + ) + if (!Equal.equals(leftParallel, rightParallel)) { + return false + } + leftStack = leftSequential + rightStack = rightSequential + } + return true +} + +// ----------------------------------------------------------------------------- +// Flattening +// ----------------------------------------------------------------------------- + +/** + * Flattens a cause to a sequence of sets of causes, where each set represents + * causes that fail in parallel and sequential sets represent causes that fail + * after each other. + * + * @internal + */ +const flattenCause = (cause: Cause.Cause): Chunk.Chunk> => { + return flattenCauseLoop(Chunk.of(cause), Chunk.empty()) +} + +/** @internal */ +const flattenCauseLoop = ( + causes: Chunk.Chunk>, + flattened: Chunk.Chunk> +): Chunk.Chunk> => { + // eslint-disable-next-line no-constant-condition + while (1) { + const [parallel, sequential] = pipe( + causes, + Arr.reduce( + [HashSet.empty(), Chunk.empty>()] as const, + ([parallel, sequential], cause) => { + const [par, seq] = evaluateCause(cause) + return [ + pipe(parallel, HashSet.union(par)), + pipe(sequential, Chunk.appendAll(seq)) + ] + } + ) + ) + const updated = HashSet.size(parallel) > 0 ? + pipe(flattened, Chunk.prepend(parallel)) : + flattened + if (Chunk.isEmpty(sequential)) { + return Chunk.reverse(updated) + } + causes = sequential + flattened = updated + } + throw new Error(getBugErrorMessage("Cause.flattenCauseLoop")) +} + +// ----------------------------------------------------------------------------- +// Finding +// ----------------------------------------------------------------------------- + +/** @internal */ +export const find = dual< + (pf: (cause: Cause.Cause) => Option.Option) => (self: Cause.Cause) => Option.Option, + (self: Cause.Cause, pf: (cause: Cause.Cause) => Option.Option) => Option.Option +>(2, (self: Cause.Cause, pf: (cause: Cause.Cause) => Option.Option) => { + const stack: Array> = [self] + while (stack.length > 0) { + const item = stack.pop()! + const option = pf(item) + switch (option._tag) { + case "None": { + switch (item._tag) { + case OpCodes.OP_SEQUENTIAL: + case OpCodes.OP_PARALLEL: { + stack.push(item.right) + stack.push(item.left) + break + } + } + break + } + case "Some": { + return option + } + } + } + return Option.none() +}) + +// ----------------------------------------------------------------------------- +// Filtering +// ----------------------------------------------------------------------------- + +/** @internal */ +export const filter: { + ( + refinement: Refinement>, Cause.Cause> + ): (self: Cause.Cause) => Cause.Cause + (predicate: Predicate>>): (self: Cause.Cause) => Cause.Cause + (self: Cause.Cause, refinement: Refinement, Cause.Cause>): Cause.Cause + (self: Cause.Cause, predicate: Predicate>): Cause.Cause +} = dual( + 2, + (self: Cause.Cause, predicate: Predicate>): Cause.Cause => + reduceWithContext(self, void 0, FilterCauseReducer(predicate)) +) + +// ----------------------------------------------------------------------------- +// Evaluation +// ----------------------------------------------------------------------------- + +/** + * Takes one step in evaluating a cause, returning a set of causes that fail + * in parallel and a list of causes that fail sequentially after those causes. + * + * @internal + */ +const evaluateCause = ( + self: Cause.Cause +): [HashSet.HashSet, Chunk.Chunk>] => { + let cause: Cause.Cause | undefined = self + const stack: Array> = [] + let _parallel = HashSet.empty() + let _sequential = Chunk.empty>() + while (cause !== undefined) { + switch (cause._tag) { + case OpCodes.OP_EMPTY: { + if (stack.length === 0) { + return [_parallel, _sequential] + } + cause = stack.pop() + break + } + case OpCodes.OP_FAIL: { + _parallel = HashSet.add(_parallel, Chunk.make(cause._tag, cause.error)) + if (stack.length === 0) { + return [_parallel, _sequential] + } + cause = stack.pop() + break + } + case OpCodes.OP_DIE: { + _parallel = HashSet.add(_parallel, Chunk.make(cause._tag, cause.defect)) + if (stack.length === 0) { + return [_parallel, _sequential] + } + cause = stack.pop() + break + } + case OpCodes.OP_INTERRUPT: { + _parallel = HashSet.add(_parallel, Chunk.make(cause._tag, cause.fiberId as unknown)) + if (stack.length === 0) { + return [_parallel, _sequential] + } + cause = stack.pop() + break + } + case OpCodes.OP_SEQUENTIAL: { + switch (cause.left._tag) { + case OpCodes.OP_EMPTY: { + cause = cause.right + break + } + case OpCodes.OP_SEQUENTIAL: { + cause = sequential(cause.left.left, sequential(cause.left.right, cause.right)) + break + } + case OpCodes.OP_PARALLEL: { + cause = parallel( + sequential(cause.left.left, cause.right), + sequential(cause.left.right, cause.right) + ) + break + } + default: { + _sequential = Chunk.prepend(_sequential, cause.right) + cause = cause.left + break + } + } + break + } + case OpCodes.OP_PARALLEL: { + stack.push(cause.right) + cause = cause.left + break + } + } + } + throw new Error(getBugErrorMessage("Cause.evaluateCauseLoop")) +} + +// ----------------------------------------------------------------------------- +// Reducing +// ----------------------------------------------------------------------------- + +/** @internal */ +const SizeCauseReducer: Cause.CauseReducer = { + emptyCase: () => 0, + failCase: () => 1, + dieCase: () => 1, + interruptCase: () => 1, + sequentialCase: (_, left, right) => left + right, + parallelCase: (_, left, right) => left + right +} + +/** @internal */ +const IsInterruptedOnlyCauseReducer: Cause.CauseReducer = { + emptyCase: constTrue, + failCase: constFalse, + dieCase: constFalse, + interruptCase: constTrue, + sequentialCase: (_, left, right) => left && right, + parallelCase: (_, left, right) => left && right +} + +/** @internal */ +const FilterCauseReducer = ( + predicate: Predicate> +): Cause.CauseReducer> => ({ + emptyCase: () => empty, + failCase: (_, error) => fail(error), + dieCase: (_, defect) => die(defect), + interruptCase: (_, fiberId) => interrupt(fiberId), + sequentialCase: (_, left, right) => { + if (predicate(left)) { + if (predicate(right)) { + return sequential(left, right) + } + return left + } + if (predicate(right)) { + return right + } + return empty + }, + parallelCase: (_, left, right) => { + if (predicate(left)) { + if (predicate(right)) { + return parallel(left, right) + } + return left + } + if (predicate(right)) { + return right + } + return empty + } +}) + +/** @internal */ +type CauseCase = SequentialCase | ParallelCase + +const OP_SEQUENTIAL_CASE = "SequentialCase" + +const OP_PARALLEL_CASE = "ParallelCase" + +/** @internal */ +interface SequentialCase { + readonly _tag: typeof OP_SEQUENTIAL_CASE +} + +/** @internal */ +interface ParallelCase { + readonly _tag: typeof OP_PARALLEL_CASE +} + +/** @internal */ +export const match = dual< + ( + options: { + readonly onEmpty: Z + readonly onFail: (error: E) => Z + readonly onDie: (defect: unknown) => Z + readonly onInterrupt: (fiberId: FiberId.FiberId) => Z + readonly onSequential: (left: Z, right: Z) => Z + readonly onParallel: (left: Z, right: Z) => Z + } + ) => (self: Cause.Cause) => Z, + ( + self: Cause.Cause, + options: { + readonly onEmpty: Z + readonly onFail: (error: E) => Z + readonly onDie: (defect: unknown) => Z + readonly onInterrupt: (fiberId: FiberId.FiberId) => Z + readonly onSequential: (left: Z, right: Z) => Z + readonly onParallel: (left: Z, right: Z) => Z + } + ) => Z +>(2, (self, { onDie, onEmpty, onFail, onInterrupt, onParallel, onSequential }) => { + return reduceWithContext(self, void 0, { + emptyCase: () => onEmpty, + failCase: (_, error) => onFail(error), + dieCase: (_, defect) => onDie(defect), + interruptCase: (_, fiberId) => onInterrupt(fiberId), + sequentialCase: (_, left, right) => onSequential(left, right), + parallelCase: (_, left, right) => onParallel(left, right) + }) +}) + +/** @internal */ +export const reduce = dual< + (zero: Z, pf: (accumulator: Z, cause: Cause.Cause) => Option.Option) => (self: Cause.Cause) => Z, + (self: Cause.Cause, zero: Z, pf: (accumulator: Z, cause: Cause.Cause) => Option.Option) => Z +>(3, (self: Cause.Cause, zero: Z, pf: (accumulator: Z, cause: Cause.Cause) => Option.Option) => { + let accumulator: Z = zero + let cause: Cause.Cause | undefined = self + const causes: Array> = [] + while (cause !== undefined) { + const option = pf(accumulator, cause) + accumulator = Option.isSome(option) ? option.value : accumulator + switch (cause._tag) { + case OpCodes.OP_SEQUENTIAL: { + causes.push(cause.right) + cause = cause.left + break + } + case OpCodes.OP_PARALLEL: { + causes.push(cause.right) + cause = cause.left + break + } + default: { + cause = undefined + break + } + } + if (cause === undefined && causes.length > 0) { + cause = causes.pop()! + } + } + return accumulator +}) + +/** @internal */ +export const reduceWithContext = dual< + (context: C, reducer: Cause.CauseReducer) => (self: Cause.Cause) => Z, + (self: Cause.Cause, context: C, reducer: Cause.CauseReducer) => Z +>(3, (self: Cause.Cause, context: C, reducer: Cause.CauseReducer) => { + const input: Array> = [self] + const output: Array> = [] + while (input.length > 0) { + const cause = input.pop()! + switch (cause._tag) { + case OpCodes.OP_EMPTY: { + output.push(Either.right(reducer.emptyCase(context))) + break + } + case OpCodes.OP_FAIL: { + output.push(Either.right(reducer.failCase(context, cause.error))) + break + } + case OpCodes.OP_DIE: { + output.push(Either.right(reducer.dieCase(context, cause.defect))) + break + } + case OpCodes.OP_INTERRUPT: { + output.push(Either.right(reducer.interruptCase(context, cause.fiberId))) + break + } + case OpCodes.OP_SEQUENTIAL: { + input.push(cause.right) + input.push(cause.left) + output.push(Either.left({ _tag: OP_SEQUENTIAL_CASE })) + break + } + case OpCodes.OP_PARALLEL: { + input.push(cause.right) + input.push(cause.left) + output.push(Either.left({ _tag: OP_PARALLEL_CASE })) + break + } + } + } + const accumulator: Array = [] + while (output.length > 0) { + const either = output.pop()! + switch (either._tag) { + case "Left": { + switch (either.left._tag) { + case OP_SEQUENTIAL_CASE: { + const left = accumulator.pop()! + const right = accumulator.pop()! + const value = reducer.sequentialCase(context, left, right) + accumulator.push(value) + break + } + case OP_PARALLEL_CASE: { + const left = accumulator.pop()! + const right = accumulator.pop()! + const value = reducer.parallelCase(context, left, right) + accumulator.push(value) + break + } + } + break + } + case "Right": { + accumulator.push(either.right) + break + } + } + } + if (accumulator.length === 0) { + throw new Error( + "BUG: Cause.reduceWithContext - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + } + return accumulator.pop()! +}) + +// ----------------------------------------------------------------------------- +// Pretty Printing +// ----------------------------------------------------------------------------- + +/** @internal */ +export const pretty = (cause: Cause.Cause, options?: { + readonly renderErrorCause?: boolean | undefined +}): string => { + if (isInterruptedOnly(cause)) { + return "All fibers interrupted without errors." + } + return prettyErrors(cause).map(function(e) { + if (options?.renderErrorCause !== true || e.cause === undefined) { + return e.stack + } + return `${e.stack} {\n${renderErrorCause(e.cause as Cause.PrettyError, " ")}\n}` + }).join("\n") +} + +const renderErrorCause = (cause: Cause.PrettyError, prefix: string) => { + const lines = cause.stack!.split("\n") + let stack = `${prefix}[cause]: ${lines[0]}` + for (let i = 1, len = lines.length; i < len; i++) { + stack += `\n${prefix}${lines[i]}` + } + if (cause.cause) { + stack += ` {\n${renderErrorCause(cause.cause as Cause.PrettyError, `${prefix} `)}\n${prefix}}` + } + return stack +} + +/** @internal */ +export const makePrettyError = (originalError: unknown): Cause.PrettyError => { + const originalErrorIsObject = typeof originalError === "object" && originalError !== null + const prevLimit = Error.stackTraceLimit + Error.stackTraceLimit = 1 + const error = new Error( + prettyErrorMessage(originalError), + originalErrorIsObject && "cause" in originalError && typeof originalError.cause !== "undefined" + ? { cause: makePrettyError(originalError.cause) } + : undefined + ) as Types.Mutable + Error.stackTraceLimit = prevLimit + if (error.message === "") { + error.message = "An error has occurred" + } + Error.stackTraceLimit = prevLimit + error.name = originalError instanceof Error ? originalError.name : "Error" + if (originalErrorIsObject) { + if (spanSymbol in originalError) { + error.span = originalError[spanSymbol] as Span + } + Object.keys(originalError).forEach((key) => { + if (!(key in error)) { + // @ts-expect-error + error[key] = originalError[key] + } + }) + } + error.stack = prettyErrorStack( + `${error.name}: ${error.message}`, + originalError instanceof Error && originalError.stack + ? originalError.stack + : "", + error.span + ) + return error +} + +/** + * A utility function for generating human-readable error messages from a generic error of type `unknown`. + * + * Rules: + * + * 1) If the input `u` is already a string, it's considered a message. + * 2) If `u` is an Error instance with a message defined, it uses the message. + * 3) If `u` has a user-defined `toString()` method, it uses that method. + * 4) Otherwise, it uses `Inspectable.stringifyCircular` to produce a string representation and uses it as the error message, + * with "Error" added as a prefix. + * + * @internal + */ +export const prettyErrorMessage = (u: unknown): string => { + // 1) + if (typeof u === "string") { + return u + } + // 2) + if (typeof u === "object" && u !== null && u instanceof Error) { + return u.message + } + // 3) + try { + if ( + hasProperty(u, "toString") && + isFunction(u["toString"]) && + u["toString"] !== Object.prototype.toString && + u["toString"] !== globalThis.Array.prototype.toString + ) { + return u["toString"]() + } + } catch { + // something's off, rollback to json + } + // 4) + return stringifyCircular(u) +} + +const locationRegex = /\((.*)\)/g + +/** @internal */ +export const spanToTrace = globalValue("effect/Tracer/spanToTrace", () => new WeakMap()) + +const prettyErrorStack = (message: string, stack: string, span?: Span | undefined): string => { + const out: Array = [message] + const lines = stack.startsWith(message) ? stack.slice(message.length).split("\n") : stack.split("\n") + + for (let i = 1; i < lines.length; i++) { + if (lines[i].includes(" at new BaseEffectError") || lines[i].includes(" at new YieldableError")) { + i++ + continue + } + if (lines[i].includes("Generator.next")) { + break + } + if (lines[i].includes("effect_internal_function")) { + break + } + out.push( + lines[i] + .replace(/at .*effect_instruction_i.*\((.*)\)/, "at $1") + .replace(/EffectPrimitive\.\w+/, "") + ) + } + + if (span) { + let current: Span | AnySpan | undefined = span + let i = 0 + while (current && current._tag === "Span" && i < 10) { + const stackFn = spanToTrace.get(current) + if (typeof stackFn === "function") { + const stack = stackFn() + if (typeof stack === "string") { + const locationMatchAll = stack.matchAll(locationRegex) + let match = false + for (const [, location] of locationMatchAll) { + match = true + out.push(` at ${current.name} (${location})`) + } + if (!match) { + out.push(` at ${current.name} (${stack.replace(/^at /, "")})`) + } + } else { + out.push(` at ${current.name}`) + } + } else { + out.push(` at ${current.name}`) + } + current = Option.getOrUndefined(current.parent) + i++ + } + } + + return out.join("\n") +} + +/** @internal */ +export const spanSymbol = Symbol.for("effect/SpanAnnotation") + +/** @internal */ +export const prettyErrors = (cause: Cause.Cause): Array => + reduceWithContext(cause, void 0, { + emptyCase: (): Array => [], + dieCase: (_, unknownError) => { + return [makePrettyError(unknownError)] + }, + failCase: (_, error) => { + return [makePrettyError(error)] + }, + interruptCase: () => [], + parallelCase: (_, l, r) => [...l, ...r], + sequentialCase: (_, l, r) => [...l, ...r] + }) diff --git a/backend/node_modules/effect/src/internal/channel.ts b/backend/node_modules/effect/src/internal/channel.ts new file mode 100644 index 0000000000000000000000000000000000000000..f0710dbbda5920c52e5fd1beb1447e0638455631 --- /dev/null +++ b/backend/node_modules/effect/src/internal/channel.ts @@ -0,0 +1,2603 @@ +import * as Cause from "../Cause.js" +import type * as Channel from "../Channel.js" +import * as Chunk from "../Chunk.js" +import * as Context from "../Context.js" +import * as Deferred from "../Deferred.js" +import * as Effect from "../Effect.js" +import * as Either from "../Either.js" +import * as Equal from "../Equal.js" +import * as Exit from "../Exit.js" +import * as Fiber from "../Fiber.js" +import * as FiberRef from "../FiberRef.js" +import { constVoid, dual, identity, pipe } from "../Function.js" +import type { LazyArg } from "../Function.js" +import * as Layer from "../Layer.js" +import type * as MergeDecision from "../MergeDecision.js" +import type * as MergeState from "../MergeState.js" +import type * as MergeStrategy from "../MergeStrategy.js" +import * as Option from "../Option.js" +import { hasProperty, type Predicate } from "../Predicate.js" +import * as PubSub from "../PubSub.js" +import * as Queue from "../Queue.js" +import * as Ref from "../Ref.js" +import * as Scope from "../Scope.js" +import type * as SingleProducerAsyncInput from "../SingleProducerAsyncInput.js" +import type * as Tracer from "../Tracer.js" +import type * as Types from "../Types.js" +import * as executor from "./channel/channelExecutor.js" +import type * as ChannelState from "./channel/channelState.js" +import * as mergeDecision from "./channel/mergeDecision.js" +import * as mergeState from "./channel/mergeState.js" +import * as mergeStrategy_ from "./channel/mergeStrategy.js" +import * as singleProducerAsyncInput from "./channel/singleProducerAsyncInput.js" +import * as coreEffect from "./core-effect.js" +import * as core from "./core-stream.js" +import * as MergeDecisionOpCodes from "./opCodes/channelMergeDecision.js" +import * as MergeStateOpCodes from "./opCodes/channelMergeState.js" +import * as ChannelStateOpCodes from "./opCodes/channelState.js" +import * as tracer from "./tracer.js" + +/** @internal */ +export const acquireUseRelease = ( + acquire: Effect.Effect, + use: (a: Acquired) => Channel.Channel, + release: (a: Acquired, exit: Exit.Exit) => Effect.Effect +): Channel.Channel => + core.flatMap( + core.fromEffect( + Ref.make< + (exit: Exit.Exit) => Effect.Effect + >(() => Effect.void) + ), + (ref) => + pipe( + core.fromEffect( + Effect.uninterruptible( + Effect.tap( + acquire, + (a) => Ref.set(ref, (exit) => release(a, exit)) + ) + ) + ), + core.flatMap(use), + core.ensuringWith((exit) => Effect.flatMap(Ref.get(ref), (f) => f(exit))) + ) + ) + +/** @internal */ +export const as = dual< + ( + value: OutDone2 + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + value: OutDone2 + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + value: OutDone2 +): Channel.Channel => map(self, () => value)) + +/** @internal */ +export const asVoid = ( + self: Channel.Channel +): Channel.Channel => map(self, constVoid) + +/** @internal */ +export const buffer = ( + options: { + readonly empty: InElem + readonly isEmpty: Predicate + readonly ref: Ref.Ref + } +): Channel.Channel => + core.suspend(() => { + const doBuffer = ( + empty: InElem, + isEmpty: Predicate, + ref: Ref.Ref + ): Channel.Channel => + unwrap( + Ref.modify(ref, (inElem) => + isEmpty(inElem) ? + [ + core.readWith({ + onInput: (input: InElem) => + core.flatMap( + core.write(input), + () => doBuffer(empty, isEmpty, ref) + ), + onFailure: (error: InErr) => core.fail(error), + onDone: (done: InDone) => core.succeedNow(done) + }), + inElem + ] as const : + [ + core.flatMap( + core.write(inElem), + () => doBuffer(empty, isEmpty, ref) + ), + empty + ] as const) + ) + return doBuffer(options.empty, options.isEmpty, options.ref) + }) + +/** @internal */ +export const bufferChunk = ( + ref: Ref.Ref> +): Channel.Channel, Chunk.Chunk, InErr, InErr, InDone, InDone> => + buffer({ + empty: Chunk.empty(), + isEmpty: Chunk.isEmpty, + ref + }) + +/** @internal */ +export const catchAll = dual< + ( + f: (error: OutErr) => Channel.Channel + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone1 | OutDone, + InDone & InDone1, + Env1 | Env + >, + ( + self: Channel.Channel, + f: (error: OutErr) => Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone1 | OutDone, + InDone & InDone1, + Env1 | Env + > +>( + 2, + ( + self: Channel.Channel, + f: (error: OutErr) => Channel.Channel + ): Channel.Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone | OutDone1, + InDone & InDone1, + Env | Env1 + > => + core.catchAllCause(self, (cause) => + Either.match(Cause.failureOrCause(cause), { + onLeft: f, + onRight: core.failCause + })) +) + +/** @internal */ +export const concatMap = dual< + ( + f: (o: OutElem) => Channel.Channel + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem2, + InElem & InElem2, + OutErr2 | OutErr, + InErr & InErr2, + unknown, + InDone & InDone2, + Env2 | Env + >, + ( + self: Channel.Channel, + f: (o: OutElem) => Channel.Channel + ) => Channel.Channel< + OutElem2, + InElem & InElem2, + OutErr2 | OutErr, + InErr & InErr2, + unknown, + InDone & InDone2, + Env2 | Env + > +>(2, ( + self: Channel.Channel, + f: (o: OutElem) => Channel.Channel +): Channel.Channel< + OutElem2, + InElem & InElem2, + OutErr | OutErr2, + InErr & InErr2, + unknown, + InDone & InDone2, + Env | Env2 +> => core.concatMapWith(self, f, () => void 0, () => void 0)) + +/** @internal */ +export const collect = dual< + ( + pf: (o: OutElem) => Option.Option + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + pf: (o: OutElem) => Option.Option + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + pf: (o: OutElem) => Option.Option +): Channel.Channel => { + const collector: Channel.Channel = core + .readWith({ + onInput: (out) => + Option.match(pf(out), { + onNone: () => collector, + onSome: (out2) => core.flatMap(core.write(out2), () => collector) + }), + onFailure: core.fail, + onDone: core.succeedNow + }) + return core.pipeTo(self, collector) +}) + +/** @internal */ +export const concatOut = ( + self: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env + > +): Channel.Channel => core.concatAll(self) + +/** @internal */ +export const mapInput = dual< + ( + f: (a: InDone0) => InDone + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (a: InDone0) => InDone + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (a: InDone0) => InDone +): Channel.Channel => { + const reader: Channel.Channel = core.readWith({ + onInput: (inElem: InElem) => core.flatMap(core.write(inElem), () => reader), + onFailure: core.fail, + onDone: (done: InDone0) => core.succeedNow(f(done)) + }) + return core.pipeTo(reader, self) +}) + +/** @internal */ +export const mapInputEffect = dual< + ( + f: (i: InDone0) => Effect.Effect + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (i: InDone0) => Effect.Effect + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (i: InDone0) => Effect.Effect +): Channel.Channel => { + const reader: Channel.Channel = core.readWith({ + onInput: (inElem) => core.flatMap(core.write(inElem), () => reader), + onFailure: core.fail, + onDone: (done) => core.fromEffect(f(done)) + }) + return core.pipeTo(reader, self) +}) + +/** @internal */ +export const mapInputError = dual< + ( + f: (a: InErr0) => InErr + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (a: InErr0) => InErr + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (a: InErr0) => InErr +): Channel.Channel => { + const reader: Channel.Channel = core.readWith({ + onInput: (inElem: InElem) => core.flatMap(core.write(inElem), () => reader), + onFailure: (error) => core.fail(f(error)), + onDone: core.succeedNow + }) + return core.pipeTo(reader, self) +}) + +/** @internal */ +export const mapInputErrorEffect = dual< + ( + f: (error: InErr0) => Effect.Effect + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (error: InErr0) => Effect.Effect + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (error: InErr0) => Effect.Effect +): Channel.Channel => { + const reader: Channel.Channel = core.readWith({ + onInput: (inElem) => core.flatMap(core.write(inElem), () => reader), + onFailure: (error) => core.fromEffect(f(error)), + onDone: core.succeedNow + }) + return core.pipeTo(reader, self) +}) + +/** @internal */ +export const mapInputIn = dual< + ( + f: (a: InElem0) => InElem + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (a: InElem0) => InElem + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (a: InElem0) => InElem +): Channel.Channel => { + const reader: Channel.Channel = core.readWith({ + onInput: (inElem) => core.flatMap(core.write(f(inElem)), () => reader), + onFailure: core.fail, + onDone: core.succeedNow + }) + return core.pipeTo(reader, self) +}) + +/** @internal */ +export const mapInputInEffect = dual< + ( + f: (a: InElem0) => Effect.Effect + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (a: InElem0) => Effect.Effect + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (a: InElem0) => Effect.Effect +): Channel.Channel => { + const reader: Channel.Channel = core.readWith({ + onInput: (inElem) => core.flatMap(core.flatMap(core.fromEffect(f(inElem)), core.write), () => reader), + onFailure: core.fail, + onDone: core.succeedNow + }) + return core.pipeTo(reader, self) +}) + +/** @internal */ +export const doneCollect = ( + self: Channel.Channel +): Channel.Channel, OutDone], InDone, Env> => + core.suspend(() => { + const builder: Array = [] + return pipe( + core.pipeTo(self, doneCollectReader(builder)), + core.flatMap((outDone) => core.succeed([Chunk.unsafeFromArray(builder), outDone])) + ) + }) + +/** @internal */ +const doneCollectReader = ( + builder: Array +): Channel.Channel => { + return core.readWith({ + onInput: (outElem) => + core.flatMap( + core.sync(() => { + builder.push(outElem) + }), + () => doneCollectReader(builder) + ), + onFailure: core.fail, + onDone: core.succeed + }) +} + +/** @internal */ +export const drain = ( + self: Channel.Channel +): Channel.Channel => { + const drainer: Channel.Channel = core + .readWithCause({ + onInput: () => drainer, + onFailure: core.failCause, + onDone: core.succeed + }) + return core.pipeTo(self, drainer) +} + +/** @internal */ +export const emitCollect = ( + self: Channel.Channel +): Channel.Channel<[Chunk.Chunk, OutDone], InElem, OutErr, InErr, void, InDone, Env> => + core.flatMap(doneCollect(self), core.write) + +/** @internal */ +export const ensuring = dual< + ( + finalizer: Effect.Effect + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + finalizer: Effect.Effect + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + finalizer: Effect.Effect +): Channel.Channel => + core.ensuringWith(self, () => finalizer)) + +/** @internal */ +export const context = (): Channel.Channel, unknown, Env> => + core.fromEffect(Effect.context()) + +/** @internal */ +export const contextWith = ( + f: (env: Context.Context) => OutDone +): Channel.Channel => map(context(), f) + +/** @internal */ +export const contextWithChannel = < + Env, + OutElem, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env1 +>( + f: (env: Context.Context) => Channel.Channel +): Channel.Channel => core.flatMap(context(), f) + +/** @internal */ +export const contextWithEffect = ( + f: (env: Context.Context) => Effect.Effect +): Channel.Channel => mapEffect(context(), f) + +/** @internal */ +export const flatten = < + OutElem, + InElem, + OutErr, + InErr, + OutElem1, + InElem1, + OutErr1, + InErr1, + OutDone2, + InDone1, + Env1, + InDone, + Env +>( + self: Channel.Channel< + OutElem, + InElem, + OutErr, + InErr, + Channel.Channel, + InDone, + Env + > +): Channel.Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone2, + InDone & InDone1, + Env | Env1 +> => core.flatMap(self, identity) + +/** @internal */ +export const foldChannel = dual< + < + OutErr, + OutElem1, + InElem1, + OutErr1, + InErr1, + OutDone1, + InDone1, + Env1, + OutDone, + OutElem2, + InElem2, + OutErr2, + InErr2, + OutDone2, + InDone2, + Env2 + >( + options: { + readonly onFailure: ( + error: OutErr + ) => Channel.Channel + readonly onSuccess: ( + done: OutDone + ) => Channel.Channel + } + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem2 | OutElem, + InElem & InElem1 & InElem2, + OutErr1 | OutErr2, + InErr & InErr1 & InErr2, + OutDone1 | OutDone2, + InDone & InDone1 & InDone2, + Env1 | Env2 | Env + >, + < + OutElem, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem1, + InElem1, + OutErr1, + InErr1, + OutDone1, + InDone1, + Env1, + OutElem2, + InElem2, + OutErr2, + InErr2, + OutDone2, + InDone2, + Env2 + >( + self: Channel.Channel, + options: { + readonly onFailure: ( + error: OutErr + ) => Channel.Channel + readonly onSuccess: ( + done: OutDone + ) => Channel.Channel + } + ) => Channel.Channel< + OutElem1 | OutElem2 | OutElem, + InElem & InElem1 & InElem2, + OutErr1 | OutErr2, + InErr & InErr1 & InErr2, + OutDone1 | OutDone2, + InDone & InDone1 & InDone2, + Env1 | Env2 | Env + > +>(2, < + OutElem, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem1, + InElem1, + OutErr1, + InErr1, + OutDone1, + InDone1, + Env1, + OutElem2, + InElem2, + OutErr2, + InErr2, + OutDone2, + InDone2, + Env2 +>( + self: Channel.Channel, + options: { + readonly onFailure: (error: OutErr) => Channel.Channel + readonly onSuccess: (done: OutDone) => Channel.Channel + } +): Channel.Channel< + OutElem | OutElem2 | OutElem1, + InElem & InElem1 & InElem2, + OutErr2 | OutErr1, + InErr & InErr1 & InErr2, + OutDone2 | OutDone1, + InDone & InDone1 & InDone2, + Env | Env1 | Env2 +> => + core.foldCauseChannel(self, { + onFailure: (cause) => { + const either = Cause.failureOrCause(cause) + switch (either._tag) { + case "Left": { + return options.onFailure(either.left) + } + case "Right": { + return core.failCause(either.right) + } + } + }, + onSuccess: options.onSuccess + })) + +/** @internal */ +export const fromEither = ( + either: Either.Either +): Channel.Channel => + core.suspend(() => Either.match(either, { onLeft: core.fail, onRight: core.succeed })) + +/** @internal */ +export const fromInput = ( + input: SingleProducerAsyncInput.AsyncInputConsumer +): Channel.Channel => + unwrap( + input.takeWith( + core.failCause, + (elem) => core.flatMap(core.write(elem), () => fromInput(input)), + core.succeed + ) + ) + +/** @internal */ +export const fromPubSub = ( + pubsub: PubSub.PubSub>> +): Channel.Channel => + unwrapScoped(Effect.map(PubSub.subscribe(pubsub), fromQueue)) + +/** @internal */ +export const fromPubSubScoped = ( + pubsub: PubSub.PubSub>> +): Effect.Effect, never, Scope.Scope> => + Effect.map(PubSub.subscribe(pubsub), fromQueue) + +/** @internal */ +export const fromOption = ( + option: Option.Option +): Channel.Channel, unknown, A, unknown> => + core.suspend(() => + Option.match(option, { + onNone: () => core.fail(Option.none()), + onSome: core.succeed + }) + ) + +/** @internal */ +export const fromQueue = ( + queue: Queue.Dequeue>> +): Channel.Channel => core.suspend(() => fromQueueInternal(queue)) + +/** @internal */ +const fromQueueInternal = ( + queue: Queue.Dequeue>> +): Channel.Channel => + pipe( + core.fromEffect(Queue.take(queue)), + core.flatMap(Either.match({ + onLeft: Exit.match({ + onFailure: core.failCause, + onSuccess: core.succeedNow + }), + onRight: (elem) => + core.flatMap( + core.write(elem), + () => fromQueueInternal(queue) + ) + })) + ) + +/** @internal */ +export const identityChannel = (): Channel.Channel => + core.readWith({ + onInput: (input: Elem) => core.flatMap(core.write(input), () => identityChannel()), + onFailure: core.fail, + onDone: core.succeedNow + }) + +/** @internal */ +export const interruptWhen = dual< + ( + effect: Effect.Effect + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + effect: Effect.Effect + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + effect: Effect.Effect +): Channel.Channel => + mergeWith(self, { + other: core.fromEffect(effect), + onSelfDone: (selfDone) => mergeDecision.Done(Effect.suspend(() => selfDone)), + onOtherDone: (effectDone) => mergeDecision.Done(Effect.suspend(() => effectDone)) + })) + +/** @internal */ +export const interruptWhenDeferred = dual< + ( + deferred: Deferred.Deferred + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + deferred: Deferred.Deferred + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + deferred: Deferred.Deferred +): Channel.Channel => + interruptWhen(self, Deferred.await(deferred))) + +/** @internal */ +export const map = dual< + ( + f: (out: OutDone) => OutDone2 + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (out: OutDone) => OutDone2 + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (out: OutDone) => OutDone2 +): Channel.Channel => + core.flatMap(self, (a) => core.sync(() => f(a)))) + +/** @internal */ +export const mapEffect = dual< + ( + f: (o: OutDone) => Effect.Effect + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (o: OutDone) => Effect.Effect + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (o: OutDone) => Effect.Effect +): Channel.Channel => + core.flatMap(self, (z) => core.fromEffect(f(z)))) + +/** @internal */ +export const mapError = dual< + ( + f: (err: OutErr) => OutErr2 + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (err: OutErr) => OutErr2 + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (err: OutErr) => OutErr2 +): Channel.Channel => mapErrorCause(self, Cause.map(f))) + +/** @internal */ +export const mapErrorCause = dual< + ( + f: (cause: Cause.Cause) => Cause.Cause + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (cause: Cause.Cause) => Cause.Cause + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (cause: Cause.Cause) => Cause.Cause +): Channel.Channel => + core.catchAllCause(self, (cause) => core.failCause(f(cause)))) + +/** @internal */ +export const mapOut = dual< + ( + f: (o: OutElem) => OutElem2 + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (o: OutElem) => OutElem2 + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (o: OutElem) => OutElem2 +): Channel.Channel => { + const reader: Channel.Channel = core + .readWith({ + onInput: (outElem) => core.flatMap(core.write(f(outElem)), () => reader), + onFailure: core.fail, + onDone: core.succeedNow + }) + return core.pipeTo(self, reader) +}) + +/** @internal */ +export const mapOutEffect = dual< + ( + f: (o: OutElem) => Effect.Effect + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (o: OutElem) => Effect.Effect + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (o: OutElem) => Effect.Effect +): Channel.Channel => { + const reader: Channel.Channel = core + .readWithCause({ + onInput: (outElem) => + pipe( + core.fromEffect(f(outElem)), + core.flatMap(core.write), + core.flatMap(() => reader) + ), + onFailure: core.failCause, + onDone: core.succeedNow + }) + return core.pipeTo(self, reader) +}) + +/** @internal */ +export const mapOutEffectPar = dual< + ( + f: (o: OutElem) => Effect.Effect, + n: number + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (o: OutElem) => Effect.Effect, + n: number + ) => Channel.Channel +>(3, ( + self: Channel.Channel, + f: (o: OutElem) => Effect.Effect, + n: number +): Channel.Channel => + unwrapScopedWith( + (scope) => + Effect.gen(function*() { + const input = yield* singleProducerAsyncInput.make() + const queueReader = fromInput(input) + const queue = yield* Queue.bounded, OutErr | OutErr1, Env1>>(n) + yield* Scope.addFinalizer(scope, Queue.shutdown(queue)) + const errorSignal = yield* Deferred.make() + const withPermits = n === Number.POSITIVE_INFINITY ? + ((_: number) => identity) : + (yield* Effect.makeSemaphore(n)).withPermits + const pull = yield* queueReader.pipe(core.pipeTo(self), toPullIn(scope)) + yield* pull.pipe( + Effect.matchCauseEffect({ + onFailure: (cause) => Queue.offer(queue, Effect.failCause(cause)), + onSuccess: Either.match({ + onLeft: (outDone) => + Effect.zipRight( + Effect.interruptible(withPermits(n)(Effect.void)), + Effect.asVoid(Queue.offer(queue, Effect.succeed(Either.left(outDone)))) + ), + onRight: (outElem) => + Effect.gen(function*() { + const deferred = yield* Deferred.make() + const latch = yield* Deferred.make() + yield* Queue.offer(queue, Effect.map(Deferred.await(deferred), Either.right)) + yield* Deferred.succeed(latch, void 0).pipe( + Effect.zipRight( + Effect.uninterruptibleMask((restore) => + Effect.exit(restore(Deferred.await(errorSignal))).pipe( + Effect.raceFirst(Effect.exit(restore(f(outElem)))), + Effect.flatMap(identity) + ) + ).pipe( + Effect.tapErrorCause((cause) => Deferred.failCause(errorSignal, cause)), + Effect.intoDeferred(deferred) + ) + ), + withPermits(1), + Effect.forkIn(scope) + ) + yield* Deferred.await(latch) + }) + }) + }), + Effect.forever, + Effect.interruptible, + Effect.forkIn(scope) + ) + const consumer: Channel.Channel = unwrap( + Effect.matchCause(Effect.flatten(Queue.take(queue)), { + onFailure: core.failCause, + onSuccess: Either.match({ + onLeft: core.succeedNow, + onRight: (outElem) => core.flatMap(core.write(outElem), () => consumer) + }) + }) + ) + return core.embedInput(consumer, input) + }) + )) + +/** @internal */ +export const mergeAll = ( + options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + readonly mergeStrategy?: MergeStrategy.MergeStrategy | undefined + } +) => { + return < + OutElem, + InElem1, + OutErr1, + InErr1, + InDone1, + Env1, + InElem, + OutErr, + InErr, + InDone, + Env + >( + channels: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + unknown, + InDone, + Env + > + ): Channel.Channel< + OutElem, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + unknown, + InDone & InDone1, + Env | Env1 + > => mergeAllWith(options)(channels, constVoid) +} + +/** @internal */ +export const mergeAllUnbounded = < + OutElem, + InElem1, + OutErr1, + InErr1, + InDone1, + Env1, + InElem, + OutErr, + InErr, + InDone, + Env +>( + channels: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + unknown, + InDone, + Env + > +): Channel.Channel< + OutElem, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + unknown, + InDone & InDone1, + Env | Env1 +> => mergeAllWith({ concurrency: "unbounded" })(channels, constVoid) + +/** @internal */ +export const mergeAllUnboundedWith = < + OutElem, + InElem1, + OutErr1, + InErr1, + OutDone, + InDone1, + Env1, + InElem, + OutErr, + InErr, + InDone, + Env +>( + channels: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env + >, + f: (o1: OutDone, o2: OutDone) => OutDone +): Channel.Channel< + OutElem, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone, + InDone & InDone1, + Env | Env1 +> => mergeAllWith({ concurrency: "unbounded" })(channels, f) + +/** @internal */ +export const mergeAllWith = ( + { + bufferSize = 16, + concurrency, + mergeStrategy = mergeStrategy_.BackPressure() + }: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + readonly mergeStrategy?: MergeStrategy.MergeStrategy | undefined + } +) => +( + channels: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env + >, + f: (o1: OutDone, o2: OutDone) => OutDone +): Channel.Channel< + OutElem, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone, + InDone & InDone1, + Env | Env1 +> => + unwrapScopedWith( + (scope) => + Effect.gen(function*() { + const concurrencyN = concurrency === "unbounded" ? Number.MAX_SAFE_INTEGER : concurrency + const input = yield* singleProducerAsyncInput.make< + InErr & InErr1, + InElem & InElem1, + InDone & InDone1 + >() + const queueReader = fromInput(input) + const queue = yield* Queue.bounded, OutErr | OutErr1, Env>>( + bufferSize + ) + yield* Scope.addFinalizer(scope, Queue.shutdown(queue)) + const cancelers = yield* Queue.unbounded>() + yield* Scope.addFinalizer(scope, Queue.shutdown(cancelers)) + const lastDone = yield* Ref.make>(Option.none()) + const errorSignal = yield* Deferred.make() + const withPermits = (yield* Effect.makeSemaphore(concurrencyN)).withPermits + const pull = yield* toPullIn(core.pipeTo(queueReader, channels), scope) + + function evaluatePull( + pull: Effect.Effect< + Either.Either, + OutErr | OutErr1, + Env | Env1 + > + ) { + return pull.pipe( + Effect.flatMap(Either.match({ + onLeft: (done) => Effect.succeed(Option.some(done)), + onRight: (outElem) => + Effect.as( + Queue.offer(queue, Effect.succeed(Either.right(outElem))), + Option.none() + ) + })), + Effect.repeat({ until: (_): _ is Option.Some => Option.isSome(_) }), + Effect.flatMap((outDone) => + Ref.update( + lastDone, + Option.match({ + onNone: () => Option.some(outDone.value), + onSome: (lastDone) => Option.some(f(lastDone, outDone.value)) + }) + ) + ), + Effect.catchAllCause((cause) => + Cause.isInterrupted(cause) + ? Effect.failCause(cause) + : Queue.offer(queue, Effect.failCause(cause)).pipe( + Effect.zipRight(Deferred.succeed(errorSignal, void 0)), + Effect.asVoid + ) + ) + ) + } + + yield* pull.pipe( + Effect.matchCauseEffect({ + onFailure: (cause) => + Queue.offer(queue, Effect.failCause(cause)).pipe( + Effect.zipRight(Effect.succeed(false)) + ), + onSuccess: Either.match({ + onLeft: (outDone) => + Effect.raceWith( + Effect.interruptible(Deferred.await(errorSignal)), + Effect.interruptible(withPermits(concurrencyN)(Effect.void)), + { + onSelfDone: (_, permitAcquisition) => Effect.as(Fiber.interrupt(permitAcquisition), false), + onOtherDone: (_, failureAwait) => + Effect.zipRight( + Fiber.interrupt(failureAwait), + Ref.get(lastDone).pipe( + Effect.flatMap(Option.match({ + onNone: () => Queue.offer(queue, Effect.succeed(Either.left(outDone))), + onSome: (lastDone) => Queue.offer(queue, Effect.succeed(Either.left(f(lastDone, outDone)))) + })), + Effect.as(false) + ) + ) + } + ), + onRight: (channel) => + mergeStrategy_.match(mergeStrategy, { + onBackPressure: () => + Effect.gen(function*() { + const latch = yield* Deferred.make() + const raceEffects = Effect.scopedWith((scope) => + toPullIn(core.pipeTo(queueReader, channel), scope).pipe( + Effect.flatMap((pull) => + Effect.race( + Effect.exit(evaluatePull(pull)), + Effect.exit(Effect.interruptible(Deferred.await(errorSignal))) + ) + ), + Effect.flatMap(identity) + ) + ) + yield* Deferred.succeed(latch, void 0).pipe( + Effect.zipRight(raceEffects), + withPermits(1), + Effect.forkIn(scope) + ) + yield* Deferred.await(latch) + const errored = yield* Deferred.isDone(errorSignal) + return !errored + }), + onBufferSliding: () => + Effect.gen(function*() { + const canceler = yield* Deferred.make() + const latch = yield* Deferred.make() + const size = yield* Queue.size(cancelers) + yield* Queue.take(cancelers).pipe( + Effect.flatMap((canceler) => Deferred.succeed(canceler, void 0)), + Effect.when(() => size >= concurrencyN) + ) + yield* Queue.offer(cancelers, canceler) + const raceEffects = Effect.scopedWith((scope) => + toPullIn(core.pipeTo(queueReader, channel), scope).pipe( + Effect.flatMap((pull) => + Effect.exit(evaluatePull(pull)).pipe( + Effect.race(Effect.exit(Effect.interruptible(Deferred.await(errorSignal)))), + Effect.race(Effect.exit(Effect.interruptible(Deferred.await(canceler)))) + ) + ), + Effect.flatMap(identity) + ) + ) + yield* Deferred.succeed(latch, void 0).pipe( + Effect.zipRight(raceEffects), + withPermits(1), + Effect.forkIn(scope) + ) + yield* Deferred.await(latch) + const errored = yield* Deferred.isDone(errorSignal) + return !errored + }) + }) + }) + }), + Effect.repeat({ while: (_) => _ }), + Effect.forkIn(scope) + ) + + const consumer: Channel.Channel = + pipe( + Queue.take(queue), + Effect.flatten, + Effect.matchCause({ + onFailure: core.failCause, + onSuccess: Either.match({ + onLeft: core.succeedNow, + onRight: (outElem) => core.flatMap(core.write(outElem), () => consumer) + }) + }), + unwrap + ) + + return core.embedInput(consumer, input) + }) + ) + +/** @internal */ +export const mergeMap = dual< + ( + f: (outElem: OutElem) => Channel.Channel, + options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + readonly mergeStrategy?: MergeStrategy.MergeStrategy | undefined + } + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem1, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + unknown, + InDone & InDone1, + Env1 | Env + >, + ( + self: Channel.Channel, + f: (outElem: OutElem) => Channel.Channel, + options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + readonly mergeStrategy?: MergeStrategy.MergeStrategy | undefined + } + ) => Channel.Channel< + OutElem1, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + unknown, + InDone & InDone1, + Env1 | Env + > +>(3, ( + self: Channel.Channel, + f: (outElem: OutElem) => Channel.Channel, + options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + readonly mergeStrategy?: MergeStrategy.MergeStrategy | undefined + } +): Channel.Channel< + OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + unknown, + InDone & InDone1, + Env | Env1 +> => mergeAll(options)(mapOut(self, f))) + +/** @internal */ +export const mergeOut = dual< + ( + n: number + ) => ( + self: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env + > + ) => Channel.Channel< + OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + unknown, + InDone & InDone1, + Env | Env1 + >, + ( + self: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env + >, + n: number + ) => Channel.Channel< + OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + unknown, + InDone & InDone1, + Env | Env1 + > +>(2, ( + self: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env + >, + n: number +): Channel.Channel< + OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + unknown, + InDone & InDone1, + Env | Env1 +> => mergeAll({ concurrency: n })(mapOut(self, identity))) + +/** @internal */ +export const mergeOutWith = dual< + ( + n: number, + f: (o1: OutDone1, o2: OutDone1) => OutDone1 + ) => ( + self: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + OutDone1, + InDone, + Env + > + ) => Channel.Channel< + OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone1, + InDone & InDone1, + Env | Env1 + >, + ( + self: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + OutDone1, + InDone, + Env + >, + n: number, + f: (o1: OutDone1, o2: OutDone1) => OutDone1 + ) => Channel.Channel< + OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone1, + InDone & InDone1, + Env | Env1 + > +>(3, ( + self: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + OutDone1, + InDone, + Env + >, + n: number, + f: (o1: OutDone1, o2: OutDone1) => OutDone1 +): Channel.Channel< + OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone1, + InDone & InDone1, + Env | Env1 +> => mergeAllWith({ concurrency: n })(mapOut(self, identity), f)) + +/** @internal */ +export const mergeWith = dual< + ( + options: { + readonly other: Channel.Channel + readonly onSelfDone: ( + exit: Exit.Exit + ) => MergeDecision.MergeDecision + readonly onOtherDone: ( + ex: Exit.Exit + ) => MergeDecision.MergeDecision + } + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr2 | OutErr3, + InErr & InErr1, + OutDone2 | OutDone3, + InDone & InDone1, + Env1 | Env + >, + < + OutElem, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem1, + InElem1, + OutErr1, + InErr1, + OutDone1, + InDone1, + Env1, + OutErr2, + OutDone2, + OutErr3, + OutDone3 + >( + self: Channel.Channel, + options: { + readonly other: Channel.Channel + readonly onSelfDone: ( + exit: Exit.Exit + ) => MergeDecision.MergeDecision + readonly onOtherDone: ( + ex: Exit.Exit + ) => MergeDecision.MergeDecision + } + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr2 | OutErr3, + InErr & InErr1, + OutDone2 | OutDone3, + InDone & InDone1, + Env1 | Env + > +>(2, < + OutElem, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem1, + InElem1, + OutErr1, + InErr1, + OutDone1, + InDone1, + Env1, + OutErr2, + OutDone2, + OutErr3, + OutDone3 +>( + self: Channel.Channel, + options: { + readonly other: Channel.Channel + readonly onSelfDone: ( + exit: Exit.Exit + ) => MergeDecision.MergeDecision + readonly onOtherDone: ( + ex: Exit.Exit + ) => MergeDecision.MergeDecision + } +): Channel.Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr2 | OutErr3, + InErr & InErr1, + OutDone2 | OutDone3, + InDone & InDone1, + Env1 | Env +> => { + function merge(scope: Scope.Scope) { + return Effect.gen(function*() { + type State = MergeState.MergeState< + Env | Env1, + OutErr, + OutErr1, + OutErr2 | OutErr3, + OutElem | OutElem1, + OutDone, + OutDone1, + OutDone2 | OutDone3 + > + + const input = yield* singleProducerAsyncInput.make< + InErr & InErr1, + InElem & InElem1, + InDone & InDone1 + >() + const queueReader = fromInput(input) + const pullL = yield* toPullIn(core.pipeTo(queueReader, self), scope) + const pullR = yield* toPullIn(core.pipeTo(queueReader, options.other), scope) + + function handleSide( + exit: Exit.Exit, Err>, + fiber: Fiber.Fiber, Err2>, + pull: Effect.Effect, Err, Env | Env1> + ) { + return ( + done: ( + ex: Exit.Exit + ) => MergeDecision.MergeDecision< + Env | Env1, + Err2, + Done2, + OutErr2 | OutErr3, + OutDone2 | OutDone3 + >, + both: ( + f1: Fiber.Fiber, Err>, + f2: Fiber.Fiber, Err2> + ) => State, + single: ( + f: ( + ex: Exit.Exit + ) => Effect.Effect + ) => State + ): Effect.Effect< + Channel.Channel< + OutElem | OutElem1, + unknown, + OutErr2 | OutErr3, + unknown, + OutDone2 | OutDone3, + unknown, + Env | Env1 + >, + never, + Env | Env1 + > => { + function onDecision( + decision: MergeDecision.MergeDecision< + Env | Env1, + Err2, + Done2, + OutErr2 | OutErr3, + OutDone2 | OutDone3 + > + ): Effect.Effect< + Channel.Channel< + OutElem | OutElem1, + unknown, + OutErr2 | OutErr3, + unknown, + OutDone2 | OutDone3, + unknown, + Env | Env1 + > + > { + const op = decision as mergeDecision.Primitive + if (op._tag === MergeDecisionOpCodes.OP_DONE) { + return Effect.succeed( + core.fromEffect( + Effect.zipRight( + Fiber.interrupt(fiber), + op.effect + ) + ) + ) + } + return Effect.map( + Fiber.await(fiber), + Exit.match({ + onFailure: (cause) => core.fromEffect(op.f(Exit.failCause(cause))), + onSuccess: Either.match({ + onLeft: (done) => core.fromEffect(op.f(Exit.succeed(done))), + onRight: (elem) => zipRight(core.write(elem), go(single(op.f))) + }) + }) + ) + } + + return Exit.match(exit, { + onFailure: (cause) => onDecision(done(Exit.failCause(cause))), + onSuccess: Either.match({ + onLeft: (z) => onDecision(done(Exit.succeed(z))), + onRight: (elem) => + Effect.succeed( + core.flatMap(core.write(elem), () => + core.flatMap( + core.fromEffect(Effect.forkIn(Effect.interruptible(pull), scope)), + (leftFiber) => go(both(leftFiber, fiber)) + )) + ) + }) + }) + } + } + + function go( + state: State + ): Channel.Channel< + OutElem | OutElem1, + unknown, + OutErr2 | OutErr3, + unknown, + OutDone2 | OutDone3, + unknown, + Env | Env1 + > { + switch (state._tag) { + case MergeStateOpCodes.OP_BOTH_RUNNING: { + const leftJoin = Effect.interruptible(Fiber.join(state.left)) + const rightJoin = Effect.interruptible(Fiber.join(state.right)) + return unwrap( + Effect.raceWith(leftJoin, rightJoin, { + onSelfDone: (leftExit, rf) => + Effect.zipRight( + Fiber.interrupt(rf), + handleSide(leftExit, state.right, pullL)( + options.onSelfDone, + mergeState.BothRunning, + (f) => mergeState.LeftDone(f) + ) + ), + onOtherDone: (rightExit, lf) => + Effect.zipRight( + Fiber.interrupt(lf), + handleSide(rightExit, state.left, pullR)( + options.onOtherDone as ( + ex: Exit.Exit + ) => MergeDecision.MergeDecision< + Env1 | Env, + OutErr, + OutDone, + OutErr2 | OutErr3, + OutDone2 | OutDone3 + >, + (left, right) => mergeState.BothRunning(right, left), + (f) => mergeState.RightDone(f) + ) + ) + }) + ) + } + case MergeStateOpCodes.OP_LEFT_DONE: { + return unwrap( + Effect.map( + Effect.exit(pullR), + Exit.match({ + onFailure: (cause) => core.fromEffect(state.f(Exit.failCause(cause))), + onSuccess: Either.match({ + onLeft: (done) => core.fromEffect(state.f(Exit.succeed(done))), + onRight: (elem) => + core.flatMap( + core.write(elem), + () => go(mergeState.LeftDone(state.f)) + ) + }) + }) + ) + ) + } + case MergeStateOpCodes.OP_RIGHT_DONE: { + return unwrap( + Effect.map( + Effect.exit(pullL), + Exit.match({ + onFailure: (cause) => core.fromEffect(state.f(Exit.failCause(cause))), + onSuccess: Either.match({ + onLeft: (done) => core.fromEffect(state.f(Exit.succeed(done))), + onRight: (elem) => + core.flatMap( + core.write(elem), + () => go(mergeState.RightDone(state.f)) + ) + }) + }) + ) + ) + } + } + } + + return core.fromEffect( + Effect.withFiberRuntime< + MergeState.MergeState< + Env | Env1, + OutErr, + OutErr1, + OutErr2 | OutErr3, + OutElem | OutElem1, + OutDone, + OutDone1, + OutDone2 | OutDone3 + >, + never, + Env | Env1 + >((parent) => { + const inherit = Effect.withFiberRuntime((state) => { + ;(state as any).transferChildren((parent as any).scope()) + return Effect.void + }) + const leftFiber = Effect.interruptible(pullL).pipe( + Effect.ensuring(inherit), + Effect.forkIn(scope) + ) + const rightFiber = Effect.interruptible(pullR).pipe( + Effect.ensuring(inherit), + Effect.forkIn(scope) + ) + return Effect.zipWith( + leftFiber, + rightFiber, + (left, right): State => + mergeState.BothRunning< + Env | Env1, + OutErr, + OutErr1, + OutErr2 | OutErr3, + OutElem | OutElem1, + OutDone, + OutDone1, + OutDone2 | OutDone3 + >(left, right) + ) + }) + ).pipe( + core.flatMap(go), + core.embedInput(input) + ) + }) + } + return unwrapScopedWith(merge) +}) + +/** @internal */ +export const never: Channel.Channel = core.fromEffect( + Effect.never +) + +/** @internal */ +export const orDie = dual< + ( + error: LazyArg + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + error: LazyArg + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + error: LazyArg +): Channel.Channel => orDieWith(self, error)) + +/** @internal */ +export const orDieWith = dual< + ( + f: (e: OutErr) => unknown + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (e: OutErr) => unknown + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (e: OutErr) => unknown +): Channel.Channel => + catchAll(self, (e) => core.failCauseSync(() => Cause.die(f(e)))) as Channel.Channel< + OutElem, + InElem, + never, + InErr, + OutDone, + InDone, + Env + >) + +/** @internal */ +export const orElse = dual< + ( + that: LazyArg> + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone1 | OutDone, + InDone & InDone1, + Env1 | Env + >, + ( + self: Channel.Channel, + that: LazyArg> + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone1 | OutDone, + InDone & InDone1, + Env1 | Env + > +>( + 2, + ( + self: Channel.Channel, + that: LazyArg> + ): Channel.Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone | OutDone1, + InDone & InDone1, + Env | Env1 + > => catchAll(self, that) +) + +/** @internal */ +export const pipeToOrFail = dual< + ( + that: Channel.Channel + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + that: Channel.Channel + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + that: Channel.Channel +): Channel.Channel => + core.suspend(() => { + let channelException: Channel.ChannelException | undefined = undefined + + const reader: Channel.Channel = core + .readWith({ + onInput: (outElem) => core.flatMap(core.write(outElem), () => reader), + onFailure: (outErr) => { + channelException = ChannelException(outErr) + return core.failCause(Cause.die(channelException)) + }, + onDone: core.succeedNow + }) + + const writer: Channel.Channel< + OutElem2, + OutElem2, + OutErr2, + OutErr2, + OutDone2, + OutDone2, + Env2 + > = core.readWithCause({ + onInput: (outElem) => pipe(core.write(outElem), core.flatMap(() => writer)), + onFailure: (cause) => + Cause.isDieType(cause) && + isChannelException(cause.defect) && + Equal.equals(cause.defect, channelException) + ? core.fail(cause.defect.error as OutErr2) + : core.failCause(cause), + onDone: core.succeedNow + }) + + return core.pipeTo(core.pipeTo(core.pipeTo(self, reader), that), writer) + })) + +/** @internal */ +export const provideService = dual< + ( + tag: Context.Tag, + service: Types.NoInfer + ) => ( + self: Channel.Channel + ) => Channel.Channel>, + ( + self: Channel.Channel, + tag: Context.Tag, + service: Types.NoInfer + ) => Channel.Channel> +>(3, ( + self: Channel.Channel, + tag: Context.Tag, + service: Types.NoInfer +): Channel.Channel> => { + return core.flatMap( + context(), + (context) => core.provideContext(self, Context.add(context, tag, service)) + ) +}) + +/** @internal */ +export const provideLayer = dual< + ( + layer: Layer.Layer + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + layer: Layer.Layer + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + layer: Layer.Layer +): Channel.Channel => + unwrapScopedWith((scope) => + Effect.map(Layer.buildWithScope(layer, scope), (context) => core.provideContext(self, context)) + )) + +/** @internal */ +export const mapInputContext = dual< + ( + f: (env: Context.Context) => Context.Context + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + f: (env: Context.Context) => Context.Context + ) => Channel.Channel +>(2, ( + self: Channel.Channel, + f: (env: Context.Context) => Context.Context +): Channel.Channel => + contextWithChannel((context: Context.Context) => core.provideContext(self, f(context)))) + +/** @internal */ +export const provideSomeLayer = dual< + ( + layer: Layer.Layer + ) => ( + self: Channel.Channel + ) => Channel.Channel>, + ( + self: Channel.Channel, + layer: Layer.Layer + ) => Channel.Channel> +>(2, ( + self: Channel.Channel, + layer: Layer.Layer +): Channel.Channel> => + // @ts-expect-error + provideLayer(self, Layer.merge(Layer.context>(), layer))) + +/** @internal */ +export const read = (): Channel.Channel, unknown, In, unknown> => + core.readOrFail, In>(Option.none()) + +/** @internal */ +export const repeated = ( + self: Channel.Channel +): Channel.Channel => core.flatMap(self, () => repeated(self)) + +/** @internal */ +export const run = ( + self: Channel.Channel +): Effect.Effect => Effect.scopedWith((scope) => executor.runIn(self, scope)) + +/** @internal */ +export const runCollect = ( + self: Channel.Channel +): Effect.Effect<[Chunk.Chunk, OutDone], OutErr, Env> => run(core.collectElements(self)) + +/** @internal */ +export const runDrain = ( + self: Channel.Channel +): Effect.Effect => run(drain(self)) + +/** @internal */ +export const runScoped = ( + self: Channel.Channel +): Effect.Effect => Effect.scopeWith((scope) => executor.runIn(self, scope)) + +/** @internal */ +export const scoped = ( + effect: Effect.Effect +): Channel.Channel> => + unwrap( + Effect.uninterruptibleMask((restore) => + Effect.map(Scope.make(), (scope) => + core.acquireReleaseOut( + Effect.tapErrorCause( + restore(Scope.extend(effect, scope)), + (cause) => Scope.close(scope, Exit.failCause(cause)) + ), + (_, exit) => Scope.close(scope, exit) + )) + ) + ) + +/** @internal */ +export const scopedWith = ( + f: (scope: Scope.Scope) => Effect.Effect +): Channel.Channel => + unwrapScoped(Effect.map(Effect.scope, (scope) => core.flatMap(core.fromEffect(f(scope)), core.write))) + +/** @internal */ +export const service = ( + tag: Context.Tag +): Channel.Channel => core.fromEffect(tag) + +/** @internal */ +export const serviceWith = (tag: Context.Tag) => +( + f: (resource: Types.NoInfer) => OutDone +): Channel.Channel => map(service(tag), f) + +/** @internal */ +export const serviceWithChannel = + (tag: Context.Tag) => + ( + f: (resource: Types.NoInfer) => Channel.Channel + ): Channel.Channel => core.flatMap(service(tag), f) + +/** @internal */ +export const serviceWithEffect = (tag: Context.Tag) => +( + f: (resource: Types.NoInfer) => Effect.Effect +): Channel.Channel => mapEffect(service(tag), f) + +/** @internal */ +export const splitLines = (): Channel.Channel< + Chunk.Chunk, + Chunk.Chunk, + Err, + Err, + Done, + Done, + never +> => + core.suspend(() => { + let stringBuilder = "" + let midCRLF = false + const splitLinesChunk = (chunk: Chunk.Chunk): Chunk.Chunk => { + const chunkBuilder: Array = [] + Chunk.map(chunk, (str) => { + if (str.length !== 0) { + let from = 0 + let indexOfCR = str.indexOf("\r") + let indexOfLF = str.indexOf("\n") + if (midCRLF) { + if (indexOfLF === 0) { + chunkBuilder.push(stringBuilder) + stringBuilder = "" + from = 1 + indexOfLF = str.indexOf("\n", from) + } else { + stringBuilder = stringBuilder + "\r" + } + midCRLF = false + } + while (indexOfCR !== -1 || indexOfLF !== -1) { + if (indexOfCR === -1 || (indexOfLF !== -1 && indexOfLF < indexOfCR)) { + if (stringBuilder.length === 0) { + chunkBuilder.push(str.substring(from, indexOfLF)) + } else { + chunkBuilder.push(stringBuilder + str.substring(from, indexOfLF)) + stringBuilder = "" + } + from = indexOfLF + 1 + indexOfLF = str.indexOf("\n", from) + } else { + if (str.length === indexOfCR + 1) { + midCRLF = true + indexOfCR = -1 + } else { + if (indexOfLF === indexOfCR + 1) { + if (stringBuilder.length === 0) { + chunkBuilder.push(str.substring(from, indexOfCR)) + } else { + stringBuilder = stringBuilder + str.substring(from, indexOfCR) + chunkBuilder.push(stringBuilder) + stringBuilder = "" + } + from = indexOfCR + 2 + indexOfCR = str.indexOf("\r", from) + indexOfLF = str.indexOf("\n", from) + } else { + indexOfCR = str.indexOf("\r", indexOfCR + 1) + } + } + } + } + if (midCRLF) { + stringBuilder = stringBuilder + str.substring(from, str.length - 1) + } else { + stringBuilder = stringBuilder + str.substring(from, str.length) + } + } + }) + return Chunk.unsafeFromArray(chunkBuilder) + } + const loop: Channel.Channel, Chunk.Chunk, Err, Err, Done, Done, never> = core + .readWithCause({ + onInput: (input: Chunk.Chunk) => { + const out = splitLinesChunk(input) + return Chunk.isEmpty(out) + ? loop + : core.flatMap(core.write(out), () => loop) + }, + onFailure: (cause) => + stringBuilder.length === 0 + ? core.failCause(cause) + : core.flatMap(core.write(Chunk.of(stringBuilder)), () => core.failCause(cause)), + onDone: (done) => + stringBuilder.length === 0 + ? core.succeed(done) + : core.flatMap(core.write(Chunk.of(stringBuilder)), () => core.succeed(done)) + }) + return loop + }) + +/** @internal */ +export const toPubSub = ( + pubsub: PubSub.PubSub>> +): Channel.Channel => toQueue(pubsub) + +/** @internal */ +export const toPull = ( + self: Channel.Channel +): Effect.Effect, OutErr, Env>, never, Env | Scope.Scope> => + Effect.flatMap(Effect.scope, (scope) => toPullIn(self, scope)) + +/** @internal */ +export const toPullIn = dual< + (scope: Scope.Scope) => ( + self: Channel.Channel + ) => Effect.Effect, OutErr, Env>, never, Env>, + ( + self: Channel.Channel, + scope: Scope.Scope + ) => Effect.Effect, OutErr, Env>, never, Env> +>(2, ( + self: Channel.Channel, + scope: Scope.Scope +) => + Effect.zip( + Effect.sync(() => new executor.ChannelExecutor(self, void 0, identity)), + Effect.runtime() + ).pipe( + Effect.tap(([executor, runtime]) => + Scope.addFinalizerExit(scope, (exit) => { + const finalizer = executor.close(exit) + return finalizer !== undefined + ? Effect.provide(finalizer, runtime) + : Effect.void + }) + ), + Effect.uninterruptible, + Effect.map(([executor]) => + Effect.suspend(() => + interpretToPull( + executor.run() as ChannelState.ChannelState, + executor + ) + ) + ) + )) + +/** @internal */ +const interpretToPull = ( + channelState: ChannelState.ChannelState, + exec: executor.ChannelExecutor +): Effect.Effect, OutErr, Env> => { + const state = channelState as ChannelState.Primitive + switch (state._tag) { + case ChannelStateOpCodes.OP_DONE: { + return Exit.match(exec.getDone(), { + onFailure: Effect.failCause, + onSuccess: (done): Effect.Effect, OutErr, Env> => + Effect.succeed(Either.left(done)) + }) + } + case ChannelStateOpCodes.OP_EMIT: { + return Effect.succeed(Either.right(exec.getEmit())) + } + case ChannelStateOpCodes.OP_FROM_EFFECT: { + return pipe( + state.effect as Effect.Effect, OutErr, Env>, + Effect.flatMap(() => interpretToPull(exec.run() as ChannelState.ChannelState, exec)) + ) + } + case ChannelStateOpCodes.OP_READ: { + return executor.readUpstream( + state, + () => interpretToPull(exec.run() as ChannelState.ChannelState, exec), + (cause) => Effect.failCause(cause) as Effect.Effect, OutErr, Env> + ) + } + } +} + +/** @internal */ +export const toQueue = ( + queue: Queue.Enqueue>> +): Channel.Channel => core.suspend(() => toQueueInternal(queue)) + +/** @internal */ +const toQueueInternal = ( + queue: Queue.Enqueue>> +): Channel.Channel => { + return core.readWithCause({ + onInput: (elem) => + core.flatMap( + core.fromEffect(Queue.offer(queue, Either.right(elem))), + () => toQueueInternal(queue) + ), + onFailure: (cause) => core.fromEffect(Queue.offer(queue, Either.left(Exit.failCause(cause)))), + onDone: (done) => core.fromEffect(Queue.offer(queue, Either.left(Exit.succeed(done)))) + }) +} + +/** @internal */ +export const unwrap = ( + channel: Effect.Effect, E, R> +): Channel.Channel => flatten(core.fromEffect(channel)) + +/** @internal */ +export const unwrapScoped = ( + self: Effect.Effect, E, R> +): Channel.Channel> => + core.concatAllWith( + scoped(self), + (d, _) => d, + (d, _) => d + ) + +/** @internal */ +export const unwrapScopedWith = ( + f: (scope: Scope.Scope) => Effect.Effect, E, R> +): Channel.Channel => + core.concatAllWith( + scopedWith(f), + (d, _) => d, + (d, _) => d + ) + +/** @internal */ +export const updateService = dual< + ( + tag: Context.Tag, + f: (resource: Types.NoInfer) => Types.NoInfer + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + tag: Context.Tag, + f: (resource: Types.NoInfer) => Types.NoInfer + ) => Channel.Channel +>(3, ( + self: Channel.Channel, + tag: Context.Tag, + f: (resource: Types.NoInfer) => Types.NoInfer +): Channel.Channel => + mapInputContext(self, (context: Context.Context) => + Context.merge( + context, + Context.make(tag, f(Context.unsafeGet(context, tag))) + ))) + +/** @internal */ +export const withSpan: { + ( + name: string, + options?: Tracer.SpanOptions + ): ( + self: Channel.Channel + ) => Channel.Channel> + ( + self: Channel.Channel, + name: string, + options?: Tracer.SpanOptions + ): Channel.Channel> +} = function() { + const dataFirst = typeof arguments[0] !== "string" + const name = dataFirst ? arguments[1] : arguments[0] + const options = tracer.addSpanStackTrace(dataFirst ? arguments[2] : arguments[1]) + const acquire = Effect.all([ + Effect.makeSpan(name, options), + Effect.context(), + Effect.clock, + FiberRef.get(FiberRef.currentTracerTimingEnabled) + ]) + if (dataFirst) { + const self = arguments[0] + return acquireUseRelease( + acquire, + ([span, context]) => core.provideContext(self, Context.add(context, tracer.spanTag, span)), + ([span, , clock, timingEnabled], exit) => coreEffect.endSpan(span, exit, clock, timingEnabled) + ) + } + return (self: Channel.Channel) => + acquireUseRelease( + acquire, + ([span, context]) => core.provideContext(self, Context.add(context, tracer.spanTag, span)), + ([span, , clock, timingEnabled], exit) => coreEffect.endSpan(span, exit, clock, timingEnabled) + ) +} as any + +/** @internal */ +export const writeAll = ( + ...outs: Array +): Channel.Channel => writeChunk(Chunk.fromIterable(outs)) + +/** @internal */ +export const writeChunk = ( + outs: Chunk.Chunk +): Channel.Channel => writeChunkWriter(0, outs.length, outs) + +/** @internal */ +const writeChunkWriter = ( + idx: number, + len: number, + chunk: Chunk.Chunk +): Channel.Channel => { + return idx === len + ? core.void + : pipe( + core.write(pipe(chunk, Chunk.unsafeGet(idx))), + core.flatMap(() => writeChunkWriter(idx + 1, len, chunk)) + ) +} + +/** @internal */ +export const zip = dual< + ( + that: Channel.Channel, + options?: { + readonly concurrent?: boolean | undefined + } + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + readonly [OutDone, OutDone1], + InDone & InDone1, + Env1 | Env + >, + ( + self: Channel.Channel, + that: Channel.Channel, + options?: { + readonly concurrent?: boolean | undefined + } + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + readonly [OutDone, OutDone1], + InDone & InDone1, + Env1 | Env + > +>( + (args) => core.isChannel(args[1]), + ( + self: Channel.Channel, + that: Channel.Channel, + options?: { + readonly concurrent?: boolean | undefined + } + ): Channel.Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + readonly [OutDone, OutDone1], + InDone & InDone1, + Env | Env1 + > => + options?.concurrent ? + mergeWith(self, { + other: that, + onSelfDone: (exit1) => mergeDecision.Await((exit2) => Effect.suspend(() => Exit.zip(exit1, exit2))), + onOtherDone: (exit2) => mergeDecision.Await((exit1) => Effect.suspend(() => Exit.zip(exit1, exit2))) + }) : + core.flatMap(self, (a) => map(that, (b) => [a, b] as const)) +) + +/** @internal */ +export const zipLeft = dual< + ( + that: Channel.Channel, + options?: { + readonly concurrent?: boolean | undefined + } + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + OutDone, + InDone & InDone1, + Env1 | Env + >, + ( + self: Channel.Channel, + that: Channel.Channel, + options?: { + readonly concurrent?: boolean | undefined + } + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + OutDone, + InDone & InDone1, + Env1 | Env + > +>( + (args) => core.isChannel(args[1]), + ( + self: Channel.Channel, + that: Channel.Channel, + options?: { + readonly concurrent?: boolean | undefined + } + ): Channel.Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone, + InDone & InDone1, + Env | Env1 + > => + options?.concurrent ? + map(zip(self, that, { concurrent: true }), (tuple) => tuple[0]) : + core.flatMap(self, (z) => as(that, z)) +) + +/** @internal */ +export const zipRight = dual< + ( + that: Channel.Channel, + options?: { + readonly concurrent?: boolean | undefined + } + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + OutDone1, + InDone & InDone1, + Env1 | Env + >, + ( + self: Channel.Channel, + that: Channel.Channel, + options?: { + readonly concurrent?: boolean | undefined + } + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + OutDone1, + InDone & InDone1, + Env1 | Env + > +>( + (args) => core.isChannel(args[1]), + ( + self: Channel.Channel, + that: Channel.Channel, + options?: { + readonly concurrent?: boolean | undefined + } + ): Channel.Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone1, + InDone & InDone1, + Env | Env1 + > => + options?.concurrent ? + map(zip(self, that, { concurrent: true }), (tuple) => tuple[1]) : + core.flatMap(self, () => that) +) + +/** @internal */ +export const ChannelExceptionTypeId: Channel.ChannelExceptionTypeId = Symbol.for( + "effect/Channel/ChannelException" +) as Channel.ChannelExceptionTypeId + +/** @internal */ +export const ChannelException = (error: E): Channel.ChannelException => ({ + _tag: "ChannelException", + [ChannelExceptionTypeId]: ChannelExceptionTypeId, + error +}) + +/** @internal */ +export const isChannelException = (u: unknown): u is Channel.ChannelException => + hasProperty(u, ChannelExceptionTypeId) diff --git a/backend/node_modules/effect/src/internal/channel/channelExecutor.ts b/backend/node_modules/effect/src/internal/channel/channelExecutor.ts new file mode 100644 index 0000000000000000000000000000000000000000..275e4de21ff195c592de18b14f66cbc1c67314a9 --- /dev/null +++ b/backend/node_modules/effect/src/internal/channel/channelExecutor.ts @@ -0,0 +1,1200 @@ +import * as Cause from "../../Cause.js" +import type * as Channel from "../../Channel.js" +import type * as ChildExecutorDecision from "../../ChildExecutorDecision.js" +import type * as Context from "../../Context.js" +import * as Deferred from "../../Deferred.js" +import * as Effect from "../../Effect.js" +import * as ExecutionStrategy from "../../ExecutionStrategy.js" +import * as Exit from "../../Exit.js" +import * as Fiber from "../../Fiber.js" +import * as FiberId from "../../FiberId.js" +import { dual, identity, pipe } from "../../Function.js" +import * as HashSet from "../../HashSet.js" +import * as Option from "../../Option.js" +import * as Scope from "../../Scope.js" +import type * as UpstreamPullStrategy from "../../UpstreamPullStrategy.js" +import * as core from "../core-stream.js" +import * as ChannelOpCodes from "../opCodes/channel.js" +import * as ChildExecutorDecisionOpCodes from "../opCodes/channelChildExecutorDecision.js" +import * as ChannelStateOpCodes from "../opCodes/channelState.js" +import * as UpstreamPullStrategyOpCodes from "../opCodes/channelUpstreamPullStrategy.js" +import * as ContinuationOpCodes from "../opCodes/continuation.js" +import * as ChannelState from "./channelState.js" +import * as Continuation from "./continuation.js" +import * as Subexecutor from "./subexecutor.js" +import * as upstreamPullRequest from "./upstreamPullRequest.js" + +export type ErasedChannel = Channel.Channel + +/** @internal */ +export type ErasedExecutor = ChannelExecutor + +/** @internal */ +export type ErasedContinuation = Continuation.Continuation< + R, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown +> + +/** @internal */ +export type ErasedFinalizer = (exit: Exit.Exit) => Effect.Effect + +/** @internal */ +export class ChannelExecutor< + out OutElem, + in InElem = unknown, + out OutErr = never, + in InErr = unknown, + out OutDone = void, + in InDone = unknown, + in out Env = never +> { + private _activeSubexecutor: Subexecutor.Subexecutor | undefined = undefined + + private _cancelled: Exit.Exit | undefined = undefined + + private _closeLastSubstream: Effect.Effect | undefined = undefined + + private _currentChannel: core.Primitive | undefined + + private _done: Exit.Exit | undefined = undefined + + private _doneStack: Array> = [] + + private _emitted: unknown | undefined = undefined + + private _executeCloseLastSubstream: ( + effect: Effect.Effect + ) => Effect.Effect + + private _input: ErasedExecutor | undefined = undefined + + private _inProgressFinalizer: Effect.Effect | undefined = undefined + + private _providedEnv: Context.Context | undefined + + constructor( + initialChannel: Channel.Channel, + providedEnv: Context.Context | undefined, + executeCloseLastSubstream: (effect: Effect.Effect) => Effect.Effect + ) { + this._currentChannel = initialChannel as core.Primitive + this._executeCloseLastSubstream = executeCloseLastSubstream + this._providedEnv = providedEnv + } + + run(): ChannelState.ChannelState { + let result: ChannelState.ChannelState | undefined = undefined + while (result === undefined) { + if (this._cancelled !== undefined) { + result = this.processCancellation() + } else if (this._activeSubexecutor !== undefined) { + result = this.runSubexecutor() + } else { + try { + if (this._currentChannel === undefined) { + result = ChannelState.Done() + } else { + if (Effect.isEffect(this._currentChannel)) { + this._currentChannel = core.fromEffect(this._currentChannel) as core.Primitive + } + switch (this._currentChannel._tag) { + case ChannelOpCodes.OP_BRACKET_OUT: { + result = this.runBracketOut(this._currentChannel) + break + } + + case ChannelOpCodes.OP_BRIDGE: { + const bridgeInput = this._currentChannel.input + + // PipeTo(left, Bridge(queue, channel)) + // In a fiber: repeatedly run left and push its outputs to the queue + // Add a finalizer to interrupt the fiber and close the executor + this._currentChannel = this._currentChannel.channel as core.Primitive + + if (this._input !== undefined) { + const inputExecutor = this._input + this._input = undefined + + const drainer = (): Effect.Effect => + Effect.flatMap(bridgeInput.awaitRead(), () => + Effect.suspend(() => { + const state = inputExecutor.run() as ChannelState.Primitive + switch (state._tag) { + case ChannelStateOpCodes.OP_DONE: { + return Exit.match(inputExecutor.getDone(), { + onFailure: (cause) => bridgeInput.error(cause), + onSuccess: (value) => bridgeInput.done(value) + }) + } + case ChannelStateOpCodes.OP_EMIT: { + return Effect.flatMap( + bridgeInput.emit(inputExecutor.getEmit()), + () => drainer() + ) + } + case ChannelStateOpCodes.OP_FROM_EFFECT: { + return Effect.matchCauseEffect(state.effect, { + onFailure: (cause) => bridgeInput.error(cause), + onSuccess: () => drainer() + }) + } + case ChannelStateOpCodes.OP_READ: { + return readUpstream( + state, + () => drainer(), + (cause) => bridgeInput.error(cause) + ) + } + } + })) as Effect.Effect + + result = ChannelState.fromEffect( + Effect.flatMap( + Effect.forkDaemon(Effect.interruptible(drainer())), + (fiber) => + Effect.sync(() => + this.addFinalizer((exit) => + Effect.flatMap(Fiber.interrupt(fiber), () => + Effect.suspend(() => { + const effect = this.restorePipe(exit, inputExecutor) + return effect !== undefined ? effect : Effect.void + })) + ) + ) + ) + ) + } + + break + } + + case ChannelOpCodes.OP_CONCAT_ALL: { + const executor: ErasedExecutor = new ChannelExecutor( + this._currentChannel.value() as Channel.Channel< + never, + unknown, + never, + unknown, + never, + unknown, + Env + >, + this._providedEnv, + (effect) => + Effect.sync(() => { + const prevLastClose = this._closeLastSubstream === undefined + ? Effect.void + : this._closeLastSubstream + this._closeLastSubstream = pipe(prevLastClose, Effect.zipRight(effect)) + }) + ) + executor._input = this._input + + const channel = this._currentChannel + this._activeSubexecutor = new Subexecutor.PullFromUpstream( + executor, + (value) => channel.k(value), + undefined, + [], + (x, y) => channel.combineInners(x, y), + (x, y) => channel.combineAll(x, y), + (request) => channel.onPull(request), + (value) => channel.onEmit(value) + ) + + this._closeLastSubstream = undefined + this._currentChannel = undefined + + break + } + + case ChannelOpCodes.OP_EMIT: { + this._emitted = this._currentChannel.out + this._currentChannel = (this._activeSubexecutor !== undefined ? + undefined : + core.void) as core.Primitive | undefined + result = ChannelState.Emit() + break + } + + case ChannelOpCodes.OP_ENSURING: { + this.runEnsuring(this._currentChannel) + break + } + + case ChannelOpCodes.OP_FAIL: { + result = this.doneHalt(this._currentChannel.error()) + break + } + + case ChannelOpCodes.OP_FOLD: { + this._doneStack.push(this._currentChannel.k as ErasedContinuation) + this._currentChannel = this._currentChannel.channel as core.Primitive + break + } + + case ChannelOpCodes.OP_FROM_EFFECT: { + const effect = this._providedEnv === undefined ? + this._currentChannel.effect() : + pipe( + this._currentChannel.effect(), + Effect.provide(this._providedEnv) + ) + + result = ChannelState.fromEffect( + Effect.matchCauseEffect(effect, { + onFailure: (cause) => { + const state = this.doneHalt(cause) + return state !== undefined && ChannelState.isFromEffect(state) ? + state.effect : + Effect.void + }, + onSuccess: (value) => { + const state = this.doneSucceed(value) + return state !== undefined && ChannelState.isFromEffect(state) ? + state.effect : + Effect.void + } + }) + ) as ChannelState.ChannelState | undefined + + break + } + + case ChannelOpCodes.OP_PIPE_TO: { + const previousInput = this._input + + const leftExec: ErasedExecutor = new ChannelExecutor( + this._currentChannel.left() as Channel.Channel, + this._providedEnv, + (effect) => this._executeCloseLastSubstream(effect) + ) + leftExec._input = previousInput + this._input = leftExec + + this.addFinalizer((exit) => { + const effect = this.restorePipe(exit, previousInput) + return effect !== undefined ? effect : Effect.void + }) + + this._currentChannel = this._currentChannel.right() as core.Primitive + + break + } + + case ChannelOpCodes.OP_PROVIDE: { + const previousEnv = this._providedEnv + this._providedEnv = this._currentChannel.context() + this._currentChannel = this._currentChannel.inner as core.Primitive + this.addFinalizer(() => + Effect.sync(() => { + this._providedEnv = previousEnv + }) + ) + break + } + + case ChannelOpCodes.OP_READ: { + const read = this._currentChannel + result = ChannelState.Read( + this._input!, + identity, + (emitted) => { + try { + this._currentChannel = read.more(emitted) as core.Primitive + } catch (error) { + this._currentChannel = read.done.onExit(Exit.die(error)) as core.Primitive + } + return undefined + }, + (exit) => { + const onExit = (exit: Exit.Exit): core.Primitive => { + return read.done.onExit(exit) as core.Primitive + } + this._currentChannel = onExit(exit) + return undefined + } + ) + break + } + + case ChannelOpCodes.OP_SUCCEED: { + result = this.doneSucceed(this._currentChannel.evaluate()) + break + } + + case ChannelOpCodes.OP_SUCCEED_NOW: { + result = this.doneSucceed(this._currentChannel.terminal) + break + } + + case ChannelOpCodes.OP_SUSPEND: { + this._currentChannel = this._currentChannel.channel() as core.Primitive + break + } + } + } + } catch (error) { + this._currentChannel = core.failCause(Cause.die(error)) as core.Primitive + } + } + } + return result + } + + getDone(): Exit.Exit { + return this._done as Exit.Exit + } + + getEmit(): OutElem { + return this._emitted as OutElem + } + + cancelWith(exit: Exit.Exit): void { + this._cancelled = exit + } + + clearInProgressFinalizer(): void { + this._inProgressFinalizer = undefined + } + + storeInProgressFinalizer(finalizer: Effect.Effect | undefined): void { + this._inProgressFinalizer = finalizer + } + + popAllFinalizers(exit: Exit.Exit): Effect.Effect { + const finalizers: Array> = [] + let next = this._doneStack.pop() as Continuation.Primitive | undefined + while (next) { + if (next._tag === "ContinuationFinalizer") { + finalizers.push(next.finalizer as ErasedFinalizer) + } + next = this._doneStack.pop() as Continuation.Primitive | undefined + } + const effect = (finalizers.length === 0 ? Effect.void : runFinalizers(finalizers, exit)) as Effect.Effect< + unknown, + never, + Env + > + this.storeInProgressFinalizer(effect) + return effect + } + + popNextFinalizers(): Array> { + const builder: Array> = [] + while (this._doneStack.length !== 0) { + const cont = this._doneStack[this._doneStack.length - 1] as Continuation.Primitive + if (cont._tag === ContinuationOpCodes.OP_CONTINUATION_K) { + return builder + } + builder.push(cont as Continuation.ContinuationFinalizer) + this._doneStack.pop() + } + return builder + } + + restorePipe( + exit: Exit.Exit, + prev: ErasedExecutor | undefined + ): Effect.Effect | undefined { + const currInput = this._input + this._input = prev + if (currInput !== undefined) { + const effect = currInput.close(exit) + return effect + } + return Effect.void + } + + close(exit: Exit.Exit): Effect.Effect | undefined { + let runInProgressFinalizers: Effect.Effect | undefined = undefined + const finalizer = this._inProgressFinalizer + if (finalizer !== undefined) { + runInProgressFinalizers = pipe( + finalizer, + Effect.ensuring(Effect.sync(() => this.clearInProgressFinalizer())) + ) + } + + let closeSelf: Effect.Effect | undefined = undefined + const selfFinalizers = this.popAllFinalizers(exit) + if (selfFinalizers !== undefined) { + closeSelf = pipe( + selfFinalizers, + Effect.ensuring(Effect.sync(() => this.clearInProgressFinalizer())) + ) + } + + const closeSubexecutors = this._activeSubexecutor === undefined ? + undefined : + this._activeSubexecutor.close(exit) + + if ( + closeSubexecutors === undefined && + runInProgressFinalizers === undefined && + closeSelf === undefined + ) { + return undefined + } + + return pipe( + Effect.exit(ifNotNull(closeSubexecutors)), + Effect.zip(Effect.exit(ifNotNull(runInProgressFinalizers))), + Effect.zip(Effect.exit(ifNotNull(closeSelf))), + Effect.map(([[exit1, exit2], exit3]) => pipe(exit1, Exit.zipRight(exit2), Exit.zipRight(exit3))), + Effect.uninterruptible, + // TODO: remove + Effect.flatMap((exit) => Effect.suspend(() => exit)) + ) + } + + doneSucceed(value: unknown): ChannelState.ChannelState | undefined { + if (this._doneStack.length === 0) { + this._done = Exit.succeed(value) + this._currentChannel = undefined + return ChannelState.Done() + } + + const head = this._doneStack[this._doneStack.length - 1] as Continuation.Primitive + if (head._tag === ContinuationOpCodes.OP_CONTINUATION_K) { + this._doneStack.pop() + this._currentChannel = head.onSuccess(value) as core.Primitive + return undefined + } + + const finalizers = this.popNextFinalizers() + if (this._doneStack.length === 0) { + this._doneStack = finalizers.reverse() + this._done = Exit.succeed(value) + this._currentChannel = undefined + return ChannelState.Done() + } + + const finalizerEffect = runFinalizers(finalizers.map((f) => f.finalizer), Exit.succeed(value))! + this.storeInProgressFinalizer(finalizerEffect) + + const effect = pipe( + finalizerEffect, + Effect.ensuring(Effect.sync(() => this.clearInProgressFinalizer())), + Effect.uninterruptible, + Effect.flatMap(() => Effect.sync(() => this.doneSucceed(value))) + ) + + return ChannelState.fromEffect(effect) + } + + doneHalt(cause: Cause.Cause): ChannelState.ChannelState | undefined { + if (this._doneStack.length === 0) { + this._done = Exit.failCause(cause) + this._currentChannel = undefined + return ChannelState.Done() + } + + const head = this._doneStack[this._doneStack.length - 1] as Continuation.Primitive + if (head._tag === ContinuationOpCodes.OP_CONTINUATION_K) { + this._doneStack.pop() + try { + this._currentChannel = head.onHalt(cause) as core.Primitive + } catch (error) { + this._currentChannel = core.failCause(Cause.die(error)) as core.Primitive + } + return undefined + } + + const finalizers = this.popNextFinalizers() + if (this._doneStack.length === 0) { + this._doneStack = finalizers.reverse() + this._done = Exit.failCause(cause) + this._currentChannel = undefined + return ChannelState.Done() + } + + const finalizerEffect = runFinalizers(finalizers.map((f) => f.finalizer), Exit.failCause(cause))! + this.storeInProgressFinalizer(finalizerEffect) + + const effect = pipe( + finalizerEffect, + Effect.ensuring(Effect.sync(() => this.clearInProgressFinalizer())), + Effect.uninterruptible, + Effect.flatMap(() => Effect.sync(() => this.doneHalt(cause))) + ) + + return ChannelState.fromEffect(effect) + } + + processCancellation(): ChannelState.ChannelState { + this._currentChannel = undefined + this._done = this._cancelled + this._cancelled = undefined + return ChannelState.Done() + } + + runBracketOut(bracketOut: core.BracketOut): ChannelState.ChannelState { + const effect = Effect.uninterruptible( + Effect.matchCauseEffect(this.provide(bracketOut.acquire() as Effect.Effect), { + onFailure: (cause) => + Effect.sync(() => { + this._currentChannel = core.failCause(cause) as core.Primitive + }), + onSuccess: (out) => + Effect.sync(() => { + this.addFinalizer((exit) => + this.provide(bracketOut.finalizer(out, exit)) as Effect.Effect + ) + this._currentChannel = core.write(out) as core.Primitive + }) + }) + ) + return ChannelState.fromEffect(effect) as ChannelState.ChannelState + } + + provide(effect: Effect.Effect): Effect.Effect { + if (this._providedEnv === undefined) { + return effect + } + return pipe(effect, Effect.provide(this._providedEnv)) + } + + runEnsuring(ensuring: core.Ensuring): void { + this.addFinalizer(ensuring.finalizer as ErasedFinalizer) + this._currentChannel = ensuring.channel as core.Primitive + } + + addFinalizer(f: ErasedFinalizer): void { + this._doneStack.push(new Continuation.ContinuationFinalizerImpl(f)) + } + + runSubexecutor(): ChannelState.ChannelState | undefined { + const subexecutor = this._activeSubexecutor as Subexecutor.Primitive + switch (subexecutor._tag) { + case Subexecutor.OP_PULL_FROM_CHILD: { + return this.pullFromChild( + subexecutor.childExecutor, + subexecutor.parentSubexecutor, + subexecutor.onEmit, + subexecutor + ) + } + case Subexecutor.OP_PULL_FROM_UPSTREAM: { + return this.pullFromUpstream(subexecutor) + } + case Subexecutor.OP_DRAIN_CHILD_EXECUTORS: { + return this.drainChildExecutors(subexecutor) + } + case Subexecutor.OP_EMIT: { + this._emitted = subexecutor.value + this._activeSubexecutor = subexecutor.next + return ChannelState.Emit() + } + } + } + + replaceSubexecutor(nextSubExec: Subexecutor.Subexecutor): void { + this._currentChannel = undefined + this._activeSubexecutor = nextSubExec + } + + finishWithExit(exit: Exit.Exit): Effect.Effect { + const state = Exit.match(exit, { + onFailure: (cause) => this.doneHalt(cause), + onSuccess: (value) => this.doneSucceed(value) + }) + this._activeSubexecutor = undefined + return state === undefined ? + Effect.void : + ChannelState.effect(state) + } + + finishSubexecutorWithCloseEffect( + subexecutorDone: Exit.Exit, + ...closeFuncs: Array<(exit: Exit.Exit) => Effect.Effect | undefined> + ): ChannelState.ChannelState | undefined { + this.addFinalizer(() => + pipe( + closeFuncs, + Effect.forEach((closeFunc) => + pipe( + Effect.sync(() => closeFunc(subexecutorDone)), + Effect.flatMap((closeEffect) => closeEffect !== undefined ? closeEffect : Effect.void) + ), { discard: true }) + ) + ) + const state = pipe( + subexecutorDone, + Exit.match({ + onFailure: (cause) => this.doneHalt(cause), + onSuccess: (value) => this.doneSucceed(value) + }) + ) + this._activeSubexecutor = undefined + return state + } + + applyUpstreamPullStrategy( + upstreamFinished: boolean, + queue: ReadonlyArray | undefined>, + strategy: UpstreamPullStrategy.UpstreamPullStrategy + ): [Option.Option, ReadonlyArray | undefined>] { + switch (strategy._tag) { + case UpstreamPullStrategyOpCodes.OP_PULL_AFTER_NEXT: { + const shouldPrepend = !upstreamFinished || queue.some((subexecutor) => subexecutor !== undefined) + return [strategy.emitSeparator, shouldPrepend ? [undefined, ...queue] : queue] + } + case UpstreamPullStrategyOpCodes.OP_PULL_AFTER_ALL_ENQUEUED: { + const shouldEnqueue = !upstreamFinished || queue.some((subexecutor) => subexecutor !== undefined) + return [strategy.emitSeparator, shouldEnqueue ? [...queue, undefined] : queue] + } + } + } + + pullFromChild( + childExecutor: ErasedExecutor, + parentSubexecutor: Subexecutor.Subexecutor, + onEmitted: (emitted: unknown) => ChildExecutorDecision.ChildExecutorDecision, + subexecutor: Subexecutor.PullFromChild + ): ChannelState.ChannelState | undefined { + return ChannelState.Read( + childExecutor, + identity, + (emitted) => { + const childExecutorDecision = onEmitted(emitted) + switch (childExecutorDecision._tag) { + case ChildExecutorDecisionOpCodes.OP_CONTINUE: { + break + } + case ChildExecutorDecisionOpCodes.OP_CLOSE: { + this.finishWithDoneValue(childExecutor, parentSubexecutor, childExecutorDecision.value) + break + } + case ChildExecutorDecisionOpCodes.OP_YIELD: { + const modifiedParent = parentSubexecutor.enqueuePullFromChild(subexecutor) + this.replaceSubexecutor(modifiedParent) + break + } + } + this._activeSubexecutor = new Subexecutor.Emit(emitted, this._activeSubexecutor!) + return undefined + }, + Exit.match({ + onFailure: (cause) => { + const state = this.handleSubexecutorFailure(childExecutor, parentSubexecutor, cause) + return state === undefined ? + undefined : + ChannelState.effectOrUndefinedIgnored(state) as Effect.Effect + }, + onSuccess: (doneValue) => { + this.finishWithDoneValue(childExecutor, parentSubexecutor, doneValue) + return undefined + } + }) + ) + } + + finishWithDoneValue( + childExecutor: ErasedExecutor, + parentSubexecutor: Subexecutor.Subexecutor, + doneValue: unknown + ): void { + const subexecutor = parentSubexecutor as Subexecutor.Primitive + switch (subexecutor._tag) { + case Subexecutor.OP_PULL_FROM_UPSTREAM: { + const modifiedParent = new Subexecutor.PullFromUpstream( + subexecutor.upstreamExecutor, + subexecutor.createChild, + subexecutor.lastDone !== undefined + ? subexecutor.combineChildResults( + subexecutor.lastDone, + doneValue + ) + : doneValue, + subexecutor.activeChildExecutors, + subexecutor.combineChildResults, + subexecutor.combineWithChildResult, + subexecutor.onPull, + subexecutor.onEmit + ) + this._closeLastSubstream = childExecutor.close(Exit.succeed(doneValue)) + this.replaceSubexecutor(modifiedParent) + break + } + case Subexecutor.OP_DRAIN_CHILD_EXECUTORS: { + const modifiedParent = new Subexecutor.DrainChildExecutors( + subexecutor.upstreamExecutor, + subexecutor.lastDone !== undefined + ? subexecutor.combineChildResults( + subexecutor.lastDone, + doneValue + ) + : doneValue, + subexecutor.activeChildExecutors, + subexecutor.upstreamDone, + subexecutor.combineChildResults, + subexecutor.combineWithChildResult, + subexecutor.onPull + ) + this._closeLastSubstream = childExecutor.close(Exit.succeed(doneValue)) + this.replaceSubexecutor(modifiedParent) + break + } + default: { + break + } + } + } + + handleSubexecutorFailure( + childExecutor: ErasedExecutor, + parentSubexecutor: Subexecutor.Subexecutor, + cause: Cause.Cause + ): ChannelState.ChannelState | undefined { + return this.finishSubexecutorWithCloseEffect( + Exit.failCause(cause), + (exit) => parentSubexecutor.close(exit), + (exit) => childExecutor.close(exit) + ) + } + + pullFromUpstream( + subexecutor: Subexecutor.PullFromUpstream + ): ChannelState.ChannelState | undefined { + if (subexecutor.activeChildExecutors.length === 0) { + return this.performPullFromUpstream(subexecutor) + } + + const activeChild = subexecutor.activeChildExecutors[0] + + const parentSubexecutor = new Subexecutor.PullFromUpstream( + subexecutor.upstreamExecutor, + subexecutor.createChild, + subexecutor.lastDone, + subexecutor.activeChildExecutors.slice(1), + subexecutor.combineChildResults, + subexecutor.combineWithChildResult, + subexecutor.onPull, + subexecutor.onEmit + ) + + if (activeChild === undefined) { + return this.performPullFromUpstream(parentSubexecutor) + } + + this.replaceSubexecutor( + new Subexecutor.PullFromChild( + activeChild.childExecutor, + parentSubexecutor, + activeChild.onEmit + ) + ) + + return undefined + } + + performPullFromUpstream( + subexecutor: Subexecutor.PullFromUpstream + ): ChannelState.ChannelState | undefined { + return ChannelState.Read( + subexecutor.upstreamExecutor, + (effect) => { + const closeLastSubstream = this._closeLastSubstream === undefined ? Effect.void : this._closeLastSubstream + this._closeLastSubstream = undefined + return pipe( + this._executeCloseLastSubstream(closeLastSubstream), + Effect.zipRight(effect) + ) + }, + (emitted) => { + if (this._closeLastSubstream !== undefined) { + const closeLastSubstream = this._closeLastSubstream + this._closeLastSubstream = undefined + return pipe( + this._executeCloseLastSubstream(closeLastSubstream), + Effect.map(() => { + const childExecutor: ErasedExecutor = new ChannelExecutor( + subexecutor.createChild(emitted), + this._providedEnv, + this._executeCloseLastSubstream + ) + + childExecutor._input = this._input + + const [emitSeparator, updatedChildExecutors] = this.applyUpstreamPullStrategy( + false, + subexecutor.activeChildExecutors, + subexecutor.onPull(upstreamPullRequest.Pulled(emitted)) + ) + + this._activeSubexecutor = new Subexecutor.PullFromChild( + childExecutor, + new Subexecutor.PullFromUpstream( + subexecutor.upstreamExecutor, + subexecutor.createChild, + subexecutor.lastDone, + updatedChildExecutors, + subexecutor.combineChildResults, + subexecutor.combineWithChildResult, + subexecutor.onPull, + subexecutor.onEmit + ), + subexecutor.onEmit + ) + + if (Option.isSome(emitSeparator)) { + this._activeSubexecutor = new Subexecutor.Emit(emitSeparator.value, this._activeSubexecutor) + } + + return undefined + }) + ) + } + + const childExecutor: ErasedExecutor = new ChannelExecutor( + subexecutor.createChild(emitted), + this._providedEnv, + this._executeCloseLastSubstream + ) + + childExecutor._input = this._input + + const [emitSeparator, updatedChildExecutors] = this.applyUpstreamPullStrategy( + false, + subexecutor.activeChildExecutors, + subexecutor.onPull(upstreamPullRequest.Pulled(emitted)) + ) + + this._activeSubexecutor = new Subexecutor.PullFromChild( + childExecutor, + new Subexecutor.PullFromUpstream( + subexecutor.upstreamExecutor, + subexecutor.createChild, + subexecutor.lastDone, + updatedChildExecutors, + subexecutor.combineChildResults, + subexecutor.combineWithChildResult, + subexecutor.onPull, + subexecutor.onEmit + ), + subexecutor.onEmit + ) + + if (Option.isSome(emitSeparator)) { + this._activeSubexecutor = new Subexecutor.Emit(emitSeparator.value, this._activeSubexecutor) + } + + return undefined + }, + (exit) => { + if (subexecutor.activeChildExecutors.some((subexecutor) => subexecutor !== undefined)) { + const drain = new Subexecutor.DrainChildExecutors( + subexecutor.upstreamExecutor, + subexecutor.lastDone, + [undefined, ...subexecutor.activeChildExecutors], + subexecutor.upstreamExecutor.getDone(), + subexecutor.combineChildResults, + subexecutor.combineWithChildResult, + subexecutor.onPull + ) + + if (this._closeLastSubstream !== undefined) { + const closeLastSubstream = this._closeLastSubstream + this._closeLastSubstream = undefined + return pipe( + this._executeCloseLastSubstream(closeLastSubstream), + Effect.map(() => this.replaceSubexecutor(drain)) + ) + } + + this.replaceSubexecutor(drain) + + return undefined + } + + const closeLastSubstream = this._closeLastSubstream + const state = this.finishSubexecutorWithCloseEffect( + pipe(exit, Exit.map((a) => subexecutor.combineWithChildResult(subexecutor.lastDone, a))), + () => closeLastSubstream, + (exit) => subexecutor.upstreamExecutor.close(exit) + ) + return state === undefined ? + undefined : + // NOTE: assuming finalizers cannot fail + ChannelState.effectOrUndefinedIgnored(state as ChannelState.ChannelState) + } + ) + } + + drainChildExecutors( + subexecutor: Subexecutor.DrainChildExecutors + ): ChannelState.ChannelState | undefined { + if (subexecutor.activeChildExecutors.length === 0) { + const lastClose = this._closeLastSubstream + if (lastClose !== undefined) { + this.addFinalizer(() => Effect.succeed(lastClose)) + } + return this.finishSubexecutorWithCloseEffect( + subexecutor.upstreamDone, + () => lastClose, + (exit) => subexecutor.upstreamExecutor.close(exit) + ) + } + + const activeChild = subexecutor.activeChildExecutors[0] + const rest = subexecutor.activeChildExecutors.slice(1) + + if (activeChild === undefined) { + const [emitSeparator, remainingExecutors] = this.applyUpstreamPullStrategy( + true, + rest, + subexecutor.onPull( + upstreamPullRequest.NoUpstream(rest.reduce((n, curr) => curr !== undefined ? n + 1 : n, 0)) + ) + ) + + this.replaceSubexecutor( + new Subexecutor.DrainChildExecutors( + subexecutor.upstreamExecutor, + subexecutor.lastDone, + remainingExecutors, + subexecutor.upstreamDone, + subexecutor.combineChildResults, + subexecutor.combineWithChildResult, + subexecutor.onPull + ) + ) + + if (Option.isSome(emitSeparator)) { + this._emitted = emitSeparator.value + return ChannelState.Emit() + } + + return undefined + } + + const parentSubexecutor = new Subexecutor.DrainChildExecutors( + subexecutor.upstreamExecutor, + subexecutor.lastDone, + rest, + subexecutor.upstreamDone, + subexecutor.combineChildResults, + subexecutor.combineWithChildResult, + subexecutor.onPull + ) + + this.replaceSubexecutor( + new Subexecutor.PullFromChild( + activeChild.childExecutor, + parentSubexecutor, + activeChild.onEmit + ) + ) + + return undefined + } +} + +const ifNotNull = (effect: Effect.Effect | undefined): Effect.Effect => + effect !== undefined ? effect : Effect.void + +const runFinalizers = ( + finalizers: Array>, + exit: Exit.Exit +): Effect.Effect => { + return pipe( + Effect.forEach(finalizers, (fin) => Effect.exit(fin(exit))), + Effect.map((exits) => pipe(Exit.all(exits), Option.getOrElse(() => Exit.void))), + Effect.flatMap((exit) => Effect.suspend(() => exit as Exit.Exit)) + ) +} + +/** + * @internal + */ +export const readUpstream = ( + r: ChannelState.Read, + onSuccess: () => Effect.Effect, + onFailure: (cause: Cause.Cause) => Effect.Effect +): Effect.Effect => { + const readStack = [r as ChannelState.Read] + const read = (): Effect.Effect => { + const current = readStack.pop() + if (current === undefined || current.upstream === undefined) { + return Effect.dieMessage("Unexpected end of input for channel execution") + } + const state = current.upstream.run() as ChannelState.Primitive + switch (state._tag) { + case ChannelStateOpCodes.OP_EMIT: { + const emitEffect = current.onEmit(current.upstream.getEmit()) + if (readStack.length === 0) { + if (emitEffect === undefined) { + return Effect.suspend(onSuccess) + } + return pipe( + emitEffect as Effect.Effect, + Effect.matchCauseEffect({ onFailure, onSuccess }) + ) + } + if (emitEffect === undefined) { + return Effect.suspend(() => read()) + } + return pipe( + emitEffect as Effect.Effect, + Effect.matchCauseEffect({ onFailure, onSuccess: () => read() }) + ) + } + + case ChannelStateOpCodes.OP_DONE: { + const doneEffect = current.onDone(current.upstream.getDone()) + if (readStack.length === 0) { + if (doneEffect === undefined) { + return Effect.suspend(onSuccess) + } + return pipe( + doneEffect as Effect.Effect, + Effect.matchCauseEffect({ onFailure, onSuccess }) + ) + } + if (doneEffect === undefined) { + return Effect.suspend(() => read()) + } + return pipe( + doneEffect as Effect.Effect, + Effect.matchCauseEffect({ onFailure, onSuccess: () => read() }) + ) + } + + case ChannelStateOpCodes.OP_FROM_EFFECT: { + readStack.push(current) + return pipe( + current.onEffect(state.effect as Effect.Effect) as Effect.Effect, + Effect.catchAllCause((cause) => + Effect.suspend(() => { + const doneEffect = current.onDone(Exit.failCause(cause)) as Effect.Effect + return doneEffect === undefined ? Effect.void : doneEffect + }) + ), + Effect.matchCauseEffect({ onFailure, onSuccess: () => read() }) + ) + } + + case ChannelStateOpCodes.OP_READ: { + readStack.push(current) + readStack.push(state) + return Effect.suspend(() => read()) + } + } + } + return read() +} + +/** @internal */ +export const runIn = dual< + (scope: Scope.Scope) => ( + self: Channel.Channel + ) => Effect.Effect, + ( + self: Channel.Channel, + scope: Scope.Scope + ) => Effect.Effect +>(2, ( + self: Channel.Channel, + scope: Scope.Scope +) => { + const run = ( + channelDeferred: Deferred.Deferred, + scopeDeferred: Deferred.Deferred, + scope: Scope.Scope + ) => + Effect.acquireUseRelease( + Effect.sync(() => new ChannelExecutor(self, void 0, identity)), + (exec) => + Effect.suspend(() => + runScopedInterpret(exec.run() as ChannelState.ChannelState, exec).pipe( + Effect.intoDeferred(channelDeferred), + Effect.zipRight(Deferred.await(channelDeferred)), + Effect.zipLeft(Deferred.await(scopeDeferred)) + ) + ), + (exec, exit) => { + const finalize = exec.close(exit) + if (finalize === undefined) { + return Effect.void + } + return Effect.tapErrorCause( + finalize, + (cause) => Scope.addFinalizer(scope, Effect.failCause(cause)) + ) + } + ) + return Effect.uninterruptibleMask((restore) => + Effect.all([ + Scope.fork(scope, ExecutionStrategy.sequential), + Deferred.make(), + Deferred.make() + ]).pipe(Effect.flatMap(([child, channelDeferred, scopeDeferred]) => + restore(run(channelDeferred, scopeDeferred, child)).pipe( + Effect.forkIn(scope), + Effect.flatMap((fiber) => + scope.addFinalizer((exit) => { + const interruptors = Exit.isFailure(exit) ? Cause.interruptors(exit.cause) : undefined + return Deferred.isDone(channelDeferred).pipe( + Effect.flatMap((isDone) => + isDone + ? Deferred.succeed(scopeDeferred, void 0).pipe( + Effect.zipRight(Fiber.await(fiber)), + Effect.zipRight(Fiber.inheritAll(fiber)) + ) + : Deferred.succeed(scopeDeferred, void 0).pipe( + Effect.zipRight( + interruptors && HashSet.size(interruptors) > 0 + ? Fiber.interruptAs(fiber, FiberId.combineAll(interruptors)) + : Fiber.interrupt(fiber) + ), + Effect.zipRight(Fiber.inheritAll(fiber)) + ) + ) + ) + }).pipe(Effect.zipRight(restore(Deferred.await(channelDeferred)))) + ) + ) + )) + ) +}) + +/** @internal */ +const runScopedInterpret = ( + channelState: ChannelState.ChannelState, + exec: ChannelExecutor +): Effect.Effect => { + const op = channelState as ChannelState.Primitive + switch (op._tag) { + case ChannelStateOpCodes.OP_FROM_EFFECT: { + return pipe( + op.effect as Effect.Effect, + Effect.flatMap(() => runScopedInterpret(exec.run() as ChannelState.ChannelState, exec)) + ) + } + case ChannelStateOpCodes.OP_EMIT: { + // Can't really happen because Out <:< Nothing. So just skip ahead. + return runScopedInterpret( + exec.run() as ChannelState.ChannelState, + exec + ) + } + case ChannelStateOpCodes.OP_DONE: { + return Effect.suspend(() => exec.getDone()) + } + case ChannelStateOpCodes.OP_READ: { + return readUpstream( + op, + () => runScopedInterpret(exec.run() as ChannelState.ChannelState, exec), + Effect.failCause + ) as Effect.Effect + } + } +} diff --git a/backend/node_modules/effect/src/internal/channel/channelState.ts b/backend/node_modules/effect/src/internal/channel/channelState.ts new file mode 100644 index 0000000000000000000000000000000000000000..20c425bc5594fa1c88de79adb07ad82d9cfc5c2d --- /dev/null +++ b/backend/node_modules/effect/src/internal/channel/channelState.ts @@ -0,0 +1,134 @@ +import * as Effect from "../../Effect.js" +import type * as Exit from "../../Exit.js" +import { hasProperty } from "../../Predicate.js" +import type * as Types from "../../Types.js" +import * as OpCodes from "../opCodes/channelState.js" +import type { ErasedExecutor } from "./channelExecutor.js" + +/** @internal */ +export const ChannelStateTypeId = Symbol.for("effect/ChannelState") + +/** @internal */ +export type ChannelStateTypeId = typeof ChannelStateTypeId + +/** @internal */ +export interface ChannelState extends ChannelState.Variance {} + +/** @internal */ +export declare namespace ChannelState { + export interface Variance { + readonly [ChannelStateTypeId]: { + readonly _E: Types.Covariant + readonly _R: Types.Covariant + } + } +} + +const channelStateVariance = { + /* c8 ignore next */ + _E: (_: never) => _, + /* c8 ignore next */ + _R: (_: never) => _ +} + +/** @internal */ +const proto = { + [ChannelStateTypeId]: channelStateVariance +} + +/** @internal */ +export type Primitive = + | Done + | Emit + | FromEffect + | Read + +/** @internal */ +export type Op = ChannelState & Body & { + readonly _tag: Tag +} + +/** @internal */ +export interface Done extends Op {} + +/** @internal */ +export interface Emit extends Op {} + +/** @internal */ +export interface FromEffect extends + Op + }> +{} + +/** @internal */ +export interface Read extends + Op + onEffect(effect: Effect.Effect): Effect.Effect + onEmit(value: unknown): Effect.Effect + onDone(exit: Exit.Exit): Effect.Effect + }> +{} + +/** @internal */ +export const Done = (): ChannelState => { + const op = Object.create(proto) + op._tag = OpCodes.OP_DONE + return op +} + +/** @internal */ +export const Emit = (): ChannelState => { + const op = Object.create(proto) + op._tag = OpCodes.OP_EMIT + return op +} + +/** @internal */ +export const fromEffect = (effect: Effect.Effect): ChannelState => { + const op = Object.create(proto) + op._tag = OpCodes.OP_FROM_EFFECT + op.effect = effect + return op +} + +/** @internal */ +export const Read = ( + upstream: ErasedExecutor, + onEffect: (effect: Effect.Effect) => Effect.Effect, + onEmit: (value: unknown) => Effect.Effect | undefined, + onDone: (exit: Exit.Exit) => Effect.Effect | undefined +): ChannelState => { + const op = Object.create(proto) + op._tag = OpCodes.OP_READ + op.upstream = upstream + op.onEffect = onEffect + op.onEmit = onEmit + op.onDone = onDone + return op +} + +/** @internal */ +export const isChannelState = (u: unknown): u is ChannelState => hasProperty(u, ChannelStateTypeId) + +/** @internal */ +export const isDone = (self: ChannelState): self is Done => (self as Primitive)._tag === OpCodes.OP_DONE + +/** @internal */ +export const isEmit = (self: ChannelState): self is Emit => (self as Primitive)._tag === OpCodes.OP_EMIT + +/** @internal */ +export const isFromEffect = (self: ChannelState): self is FromEffect => + (self as Primitive)._tag === OpCodes.OP_FROM_EFFECT + +/** @internal */ +export const isRead = (self: ChannelState): self is Read => (self as Primitive)._tag === OpCodes.OP_READ + +/** @internal */ +export const effect = (self: ChannelState): Effect.Effect => + isFromEffect(self) ? self.effect as Effect.Effect : Effect.void + +/** @internal */ +export const effectOrUndefinedIgnored = (self: ChannelState): Effect.Effect | undefined => + isFromEffect(self) ? Effect.ignore(self.effect as Effect.Effect) : undefined diff --git a/backend/node_modules/effect/src/internal/channel/childExecutorDecision.ts b/backend/node_modules/effect/src/internal/channel/childExecutorDecision.ts new file mode 100644 index 0000000000000000000000000000000000000000..930ec74fbce0cd2620d6a917c78be8c620204a40 --- /dev/null +++ b/backend/node_modules/effect/src/internal/channel/childExecutorDecision.ts @@ -0,0 +1,96 @@ +import type * as ChildExecutorDecision from "../../ChildExecutorDecision.js" +import { dual } from "../../Function.js" +import { hasProperty } from "../../Predicate.js" +import * as OpCodes from "../opCodes/channelChildExecutorDecision.js" + +/** @internal */ +const ChildExecutorDecisionSymbolKey = "effect/ChannelChildExecutorDecision" + +/** @internal */ +export const ChildExecutorDecisionTypeId: ChildExecutorDecision.ChildExecutorDecisionTypeId = Symbol.for( + ChildExecutorDecisionSymbolKey +) as ChildExecutorDecision.ChildExecutorDecisionTypeId + +/** @internal */ +const proto = { + [ChildExecutorDecisionTypeId]: ChildExecutorDecisionTypeId +} + +/** @internal */ +export const Continue = (_: void): ChildExecutorDecision.ChildExecutorDecision => { + const op = Object.create(proto) + op._tag = OpCodes.OP_CONTINUE + return op +} + +/** @internal */ +export const Close = (value: unknown): ChildExecutorDecision.ChildExecutorDecision => { + const op = Object.create(proto) + op._tag = OpCodes.OP_CLOSE + op.value = value + return op +} + +/** @internal */ +export const Yield = (_: void): ChildExecutorDecision.ChildExecutorDecision => { + const op = Object.create(proto) + op._tag = OpCodes.OP_YIELD + return op +} + +/** @internal */ +export const isChildExecutorDecision = (u: unknown): u is ChildExecutorDecision.ChildExecutorDecision => + hasProperty(u, ChildExecutorDecisionTypeId) + +/** @internal */ +export const isContinue = ( + self: ChildExecutorDecision.ChildExecutorDecision +): self is ChildExecutorDecision.Continue => self._tag === OpCodes.OP_CONTINUE + +/** @internal */ +export const isClose = ( + self: ChildExecutorDecision.ChildExecutorDecision +): self is ChildExecutorDecision.Close => self._tag === OpCodes.OP_CLOSE + +/** @internal */ +export const isYield = ( + self: ChildExecutorDecision.ChildExecutorDecision +): self is ChildExecutorDecision.Yield => self._tag === OpCodes.OP_YIELD + +/** @internal */ +export const match = dual< + ( + options: { + readonly onContinue: () => A + readonly onClose: (value: unknown) => A + readonly onYield: () => A + } + ) => (self: ChildExecutorDecision.ChildExecutorDecision) => A, + ( + self: ChildExecutorDecision.ChildExecutorDecision, + options: { + readonly onContinue: () => A + readonly onClose: (value: unknown) => A + readonly onYield: () => A + } + ) => A +>(2, ( + self: ChildExecutorDecision.ChildExecutorDecision, + { onClose, onContinue, onYield }: { + readonly onContinue: () => A + readonly onClose: (value: unknown) => A + readonly onYield: () => A + } +): A => { + switch (self._tag) { + case OpCodes.OP_CONTINUE: { + return onContinue() + } + case OpCodes.OP_CLOSE: { + return onClose(self.value) + } + case OpCodes.OP_YIELD: { + return onYield() + } + } +}) diff --git a/backend/node_modules/effect/src/internal/channel/continuation.ts b/backend/node_modules/effect/src/internal/channel/continuation.ts new file mode 100644 index 0000000000000000000000000000000000000000..5578ecd1437fa3092c8cc0a895b9b462246369b0 --- /dev/null +++ b/backend/node_modules/effect/src/internal/channel/continuation.ts @@ -0,0 +1,200 @@ +import type * as Cause from "../../Cause.js" +import type * as Channel from "../../Channel.js" +import type * as Effect from "../../Effect.js" +import * as Exit from "../../Exit.js" +import type * as Types from "../../Types.js" +import * as OpCodes from "../opCodes/continuation.js" + +/** @internal */ +export const ContinuationTypeId = Symbol.for("effect/ChannelContinuation") + +/** @internal */ +export type ContinuationTypeId = typeof ContinuationTypeId + +/** @internal */ +export interface Continuation< + out Env, + in InErr, + in InElem, + in InDone, + out OutErr, + out OutErr2, + out OutElem, + out OutDone, + out OutDone2 +> extends Continuation.Variance {} + +/** @internal */ +export declare namespace Continuation { + /** @internal */ + export interface Variance< + out Env, + in InErr, + in InElem, + in InDone, + out OutErr, + out OutErr2, + out OutElem, + out OutDone, + out OutDone2 + > { + readonly [ContinuationTypeId]: { + readonly _Env: Types.Covariant + readonly _InErr: Types.Contravariant + readonly _InElem: Types.Contravariant + readonly _InDone: Types.Contravariant + readonly _OutErr: Types.Covariant + readonly _OutDone: Types.Covariant + readonly _OutErr2: Types.Covariant + readonly _OutElem: Types.Covariant + readonly _OutDone2: Types.Covariant + } + } +} + +/** @internal */ +export type Primitive = ErasedContinuationK | ErasedContinuationFinalizer + +/** @internal */ +export type ErasedContinuationK = ContinuationK< + unknown, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown +> + +/** @internal */ +export type ErasedContinuationFinalizer = ContinuationFinalizer + +/** @internal */ +export interface ContinuationK< + out Env, + in InErr, + in InElem, + in InDone, + out OutErr, + out OutErr2, + out OutElem, + out OutDone, + out OutDone2 +> extends + Continuation< + Env, + InErr, + InElem, + InDone, + OutErr, + OutErr2, + OutElem, + OutDone, + OutDone2 + > +{ + readonly _tag: OpCodes.OP_CONTINUATION_K + onSuccess( + o: OutDone + ): Channel.Channel + onHalt( + c: Cause.Cause + ): Channel.Channel + onExit( + exit: Exit.Exit + ): Channel.Channel +} + +/** @internal */ +export interface ContinuationFinalizer extends + Continuation< + Env, + unknown, + unknown, + unknown, + OutErr, + never, + never, + OutDone, + never + > +{ + readonly _tag: OpCodes.OP_CONTINUATION_FINALIZER + finalizer(exit: Exit.Exit): Effect.Effect +} + +const continuationVariance = { + /* c8 ignore next */ + _Env: (_: never) => _, + /* c8 ignore next */ + _InErr: (_: unknown) => _, + /* c8 ignore next */ + _InElem: (_: unknown) => _, + /* c8 ignore next */ + _InDone: (_: unknown) => _, + /* c8 ignore next */ + _OutErr: (_: never) => _, + /* c8 ignore next */ + _OutDone: (_: never) => _, + /* c8 ignore next */ + _OutErr2: (_: never) => _, + /* c8 ignore next */ + _OutElem: (_: never) => _, + /* c8 ignore next */ + _OutDone2: (_: never) => _ +} + +/** @internal */ +export class ContinuationKImpl< + out Env, + out Env2, + in InErr, + in InElem, + in InDone, + in out OutErr, + out OutErr2, + out OutElem, + in out OutDone, + out OutDone2 +> implements + ContinuationK< + Env | Env2, + InErr, + InElem, + InDone, + OutErr, + OutErr2, + OutElem, + OutDone, + OutDone2 + > +{ + readonly _tag = OpCodes.OP_CONTINUATION_K + readonly [ContinuationTypeId] = continuationVariance + constructor( + readonly onSuccess: ( + o: OutDone + ) => Channel.Channel, + readonly onHalt: ( + c: Cause.Cause + ) => Channel.Channel + ) { + } + onExit( + exit: Exit.Exit + ): Channel.Channel { + return Exit.isFailure(exit) ? this.onHalt(exit.cause) : this.onSuccess(exit.value) + } +} + +/** @internal */ +export class ContinuationFinalizerImpl + implements ContinuationFinalizer +{ + readonly _tag = OpCodes.OP_CONTINUATION_FINALIZER + readonly [ContinuationTypeId] = continuationVariance + constructor(readonly finalizer: (exit: Exit.Exit) => Effect.Effect) { + } +} diff --git a/backend/node_modules/effect/src/internal/channel/mergeDecision.ts b/backend/node_modules/effect/src/internal/channel/mergeDecision.ts new file mode 100644 index 0000000000000000000000000000000000000000..9a85a821cd8f1d71265fc4e3e8d0d98554d22164 --- /dev/null +++ b/backend/node_modules/effect/src/internal/channel/mergeDecision.ts @@ -0,0 +1,113 @@ +import type * as Effect from "../../Effect.js" +import type * as Exit from "../../Exit.js" +import { dual } from "../../Function.js" +import type * as MergeDecision from "../../MergeDecision.js" +import { hasProperty } from "../../Predicate.js" +import * as OpCodes from "../opCodes/channelMergeDecision.js" + +/** @internal */ +const MergeDecisionSymbolKey = "effect/ChannelMergeDecision" + +/** @internal */ +export const MergeDecisionTypeId: MergeDecision.MergeDecisionTypeId = Symbol.for( + MergeDecisionSymbolKey +) as MergeDecision.MergeDecisionTypeId + +/** @internal */ +const proto = { + [MergeDecisionTypeId]: { + _R: (_: never) => _, + _E0: (_: unknown) => _, + _Z0: (_: unknown) => _, + _E: (_: never) => _, + _Z: (_: never) => _ + } +} + +/** @internal */ +export type Primitive = + | Done + | Await + +/** @internal */ +export type Op = + & MergeDecision.MergeDecision + & Body + & { + readonly _tag: Tag + } + +/** @internal */ +export interface Done extends + Op + }> +{} + +/** @internal */ +export interface Await extends + Op): Effect.Effect + }> +{} + +/** @internal */ +export const Done = ( + effect: Effect.Effect +): MergeDecision.MergeDecision => { + const op = Object.create(proto) + op._tag = OpCodes.OP_DONE + op.effect = effect + return op +} + +/** @internal */ +export const Await = ( + f: (exit: Exit.Exit) => Effect.Effect +): MergeDecision.MergeDecision => { + const op = Object.create(proto) + op._tag = OpCodes.OP_AWAIT + op.f = f + return op +} + +/** @internal */ +export const AwaitConst = ( + effect: Effect.Effect +): MergeDecision.MergeDecision => Await(() => effect) + +/** @internal */ +export const isMergeDecision = ( + u: unknown +): u is MergeDecision.MergeDecision => hasProperty(u, MergeDecisionTypeId) + +/** @internal */ +export const match = dual< + ( + options: { + readonly onDone: (effect: Effect.Effect) => Z2 + readonly onAwait: (f: (exit: Exit.Exit) => Effect.Effect) => Z2 + } + ) => (self: MergeDecision.MergeDecision) => Z2, + ( + self: MergeDecision.MergeDecision, + options: { + readonly onDone: (effect: Effect.Effect) => Z2 + readonly onAwait: (f: (exit: Exit.Exit) => Effect.Effect) => Z2 + } + ) => Z2 +>(2, ( + self: MergeDecision.MergeDecision, + { onAwait, onDone }: { + readonly onDone: (effect: Effect.Effect) => Z2 + readonly onAwait: (f: (exit: Exit.Exit) => Effect.Effect) => Z2 + } +): Z2 => { + const op = self as Primitive + switch (op._tag) { + case OpCodes.OP_DONE: + return onDone(op.effect) + case OpCodes.OP_AWAIT: + return onAwait(op.f) + } +}) diff --git a/backend/node_modules/effect/src/internal/channel/mergeState.ts b/backend/node_modules/effect/src/internal/channel/mergeState.ts new file mode 100644 index 0000000000000000000000000000000000000000..4d26e38627ec1520b2cd031fb333085b88b06828 --- /dev/null +++ b/backend/node_modules/effect/src/internal/channel/mergeState.ts @@ -0,0 +1,120 @@ +import type * as Effect from "../../Effect.js" +import type * as Either from "../../Either.js" +import type * as Exit from "../../Exit.js" +import type * as Fiber from "../../Fiber.js" +import { dual } from "../../Function.js" +import type * as MergeState from "../../MergeState.js" +import { hasProperty } from "../../Predicate.js" +import * as OpCodes from "../opCodes/channelMergeState.js" + +/** @internal */ +const MergeStateSymbolKey = "effect/ChannelMergeState" + +/** @internal */ +export const MergeStateTypeId: MergeState.MergeStateTypeId = Symbol.for( + MergeStateSymbolKey +) as MergeState.MergeStateTypeId + +/** @internal */ +const proto = { + [MergeStateTypeId]: MergeStateTypeId +} + +/** @internal */ +export const BothRunning = ( + left: Fiber.Fiber, Err>, + right: Fiber.Fiber, Err1> +): MergeState.MergeState => { + const op = Object.create(proto) + op._tag = OpCodes.OP_BOTH_RUNNING + op.left = left + op.right = right + return op +} + +/** @internal */ +export const LeftDone = ( + f: (exit: Exit.Exit) => Effect.Effect +): MergeState.MergeState => { + const op = Object.create(proto) + op._tag = OpCodes.OP_LEFT_DONE + op.f = f + return op +} + +/** @internal */ +export const RightDone = ( + f: (exit: Exit.Exit) => Effect.Effect +): MergeState.MergeState => { + const op = Object.create(proto) + op._tag = OpCodes.OP_RIGHT_DONE + op.f = f + return op +} + +/** @internal */ +export const isMergeState = ( + u: unknown +): u is MergeState.MergeState => + hasProperty(u, MergeStateTypeId) + +/** @internal */ +export const isBothRunning = ( + self: MergeState.MergeState +): self is MergeState.BothRunning => { + return self._tag === OpCodes.OP_BOTH_RUNNING +} + +/** @internal */ +export const isLeftDone = ( + self: MergeState.MergeState +): self is MergeState.LeftDone => { + return self._tag === OpCodes.OP_LEFT_DONE +} + +/** @internal */ +export const isRightDone = ( + self: MergeState.MergeState +): self is MergeState.RightDone => { + return self._tag === OpCodes.OP_RIGHT_DONE +} + +/** @internal */ +export const match = dual< + ( + options: { + readonly onBothRunning: ( + left: Fiber.Fiber, Err>, + right: Fiber.Fiber, Err1> + ) => Z + readonly onLeftDone: (f: (exit: Exit.Exit) => Effect.Effect) => Z + readonly onRightDone: (f: (exit: Exit.Exit) => Effect.Effect) => Z + } + ) => (self: MergeState.MergeState) => Z, + ( + self: MergeState.MergeState, + options: { + readonly onBothRunning: ( + left: Fiber.Fiber, Err>, + right: Fiber.Fiber, Err1> + ) => Z + readonly onLeftDone: (f: (exit: Exit.Exit) => Effect.Effect) => Z + readonly onRightDone: (f: (exit: Exit.Exit) => Effect.Effect) => Z + } + ) => Z +>(2, ( + self, + { onBothRunning, onLeftDone, onRightDone } +) => { + switch (self._tag) { + case OpCodes.OP_BOTH_RUNNING: { + return onBothRunning(self.left, self.right) + } + case OpCodes.OP_LEFT_DONE: { + return onLeftDone(self.f) + } + case OpCodes.OP_RIGHT_DONE: { + return onRightDone(self.f) + } + } +}) diff --git a/backend/node_modules/effect/src/internal/channel/mergeStrategy.ts b/backend/node_modules/effect/src/internal/channel/mergeStrategy.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad72977967371064a9e313783e1629843ab5556a --- /dev/null +++ b/backend/node_modules/effect/src/internal/channel/mergeStrategy.ts @@ -0,0 +1,72 @@ +import { dual } from "../../Function.js" +import type * as MergeStrategy from "../../MergeStrategy.js" +import { hasProperty } from "../../Predicate.js" +import * as OpCodes from "../opCodes/channelMergeStrategy.js" + +/** @internal */ +const MergeStrategySymbolKey = "effect/ChannelMergeStrategy" + +/** @internal */ +export const MergeStrategyTypeId: MergeStrategy.MergeStrategyTypeId = Symbol.for( + MergeStrategySymbolKey +) as MergeStrategy.MergeStrategyTypeId + +/** @internal */ +const proto = { + [MergeStrategyTypeId]: MergeStrategyTypeId +} + +/** @internal */ +export const BackPressure = (_: void): MergeStrategy.MergeStrategy => { + const op = Object.create(proto) + op._tag = OpCodes.OP_BACK_PRESSURE + return op +} + +/** @internal */ +export const BufferSliding = (_: void): MergeStrategy.MergeStrategy => { + const op = Object.create(proto) + op._tag = OpCodes.OP_BUFFER_SLIDING + return op +} + +/** @internal */ +export const isMergeStrategy = (u: unknown): u is MergeStrategy.MergeStrategy => hasProperty(u, MergeStrategyTypeId) + +/** @internal */ +export const isBackPressure = (self: MergeStrategy.MergeStrategy): self is MergeStrategy.BackPressure => + self._tag === OpCodes.OP_BACK_PRESSURE + +/** @internal */ +export const isBufferSliding = (self: MergeStrategy.MergeStrategy): self is MergeStrategy.BufferSliding => + self._tag === OpCodes.OP_BUFFER_SLIDING + +/** @internal */ +export const match = dual< + (options: { + readonly onBackPressure: () => A + readonly onBufferSliding: () => A + }) => (self: MergeStrategy.MergeStrategy) => A, + ( + self: MergeStrategy.MergeStrategy, + options: { + readonly onBackPressure: () => A + readonly onBufferSliding: () => A + } + ) => A +>(2, ( + self: MergeStrategy.MergeStrategy, + { onBackPressure, onBufferSliding }: { + readonly onBackPressure: () => A + readonly onBufferSliding: () => A + } +): A => { + switch (self._tag) { + case OpCodes.OP_BACK_PRESSURE: { + return onBackPressure() + } + case OpCodes.OP_BUFFER_SLIDING: { + return onBufferSliding() + } + } +}) diff --git a/backend/node_modules/effect/src/internal/channel/singleProducerAsyncInput.ts b/backend/node_modules/effect/src/internal/channel/singleProducerAsyncInput.ts new file mode 100644 index 0000000000000000000000000000000000000000..17d11960911d1275b067570d01fd4793e38009a1 --- /dev/null +++ b/backend/node_modules/effect/src/internal/channel/singleProducerAsyncInput.ts @@ -0,0 +1,259 @@ +import * as Cause from "../../Cause.js" +import * as Deferred from "../../Deferred.js" +import * as Effect from "../../Effect.js" +import * as Either from "../../Either.js" +import * as Exit from "../../Exit.js" +import { pipe } from "../../Function.js" +import * as Ref from "../../Ref.js" +import type * as SingleProducerAsyncInput from "../../SingleProducerAsyncInput.js" + +/** @internal */ +type State = + | Empty + | Emit + | Error + | Done<_Done> + +/** @internal */ +const OP_STATE_EMPTY = "Empty" as const + +/** @internal */ +type OP_STATE_EMPTY = typeof OP_STATE_EMPTY + +/** @internal */ +const OP_STATE_EMIT = "Emit" as const + +/** @internal */ +type OP_STATE_EMIT = typeof OP_STATE_EMIT + +/** @internal */ +const OP_STATE_ERROR = "Error" as const + +/** @internal */ +type OP_STATE_ERROR = typeof OP_STATE_ERROR + +/** @internal */ +const OP_STATE_DONE = "Done" as const + +/** @internal */ +type OP_STATE_DONE = typeof OP_STATE_DONE + +/** @internal */ +interface Empty { + readonly _tag: OP_STATE_EMPTY + readonly notifyProducer: Deferred.Deferred +} + +/** @internal */ +interface Emit { + readonly _tag: OP_STATE_EMIT + readonly notifyConsumers: ReadonlyArray, Err>> +} + +/** @internal */ +interface Error { + readonly _tag: OP_STATE_ERROR + readonly cause: Cause.Cause +} + +/** @internal */ +interface Done<_Done> { + readonly _tag: OP_STATE_DONE + readonly done: _Done +} + +/** @internal */ +const stateEmpty = (notifyProducer: Deferred.Deferred): State => ({ + _tag: OP_STATE_EMPTY, + notifyProducer +}) + +/** @internal */ +const stateEmit = ( + notifyConsumers: ReadonlyArray, Err>> +): State => ({ + _tag: OP_STATE_EMIT, + notifyConsumers +}) + +/** @internal */ +const stateError = (cause: Cause.Cause): State => ({ + _tag: OP_STATE_ERROR, + cause +}) + +/** @internal */ +const stateDone = (done: Done): State => ({ + _tag: OP_STATE_DONE, + done +}) + +/** @internal */ +class SingleProducerAsyncInputImpl + implements SingleProducerAsyncInput.SingleProducerAsyncInput +{ + constructor(readonly ref: Ref.Ref>) { + } + + awaitRead(): Effect.Effect { + return Effect.flatten( + Ref.modify(this.ref, (state) => + state._tag === OP_STATE_EMPTY ? + [Deferred.await(state.notifyProducer), state as State] : + [Effect.void, state]) + ) + } + + get close(): Effect.Effect { + return Effect.fiberIdWith((fiberId) => this.error(Cause.interrupt(fiberId))) + } + + done(value: Done): Effect.Effect { + return Effect.flatten( + Ref.modify(this.ref, (state) => { + switch (state._tag) { + case OP_STATE_EMPTY: { + return [Deferred.await(state.notifyProducer), state] + } + case OP_STATE_EMIT: { + return [ + Effect.forEach( + state.notifyConsumers, + (deferred) => Deferred.succeed(deferred, Either.left(value)), + { discard: true } + ), + stateDone(value) as State + ] + } + case OP_STATE_ERROR: { + return [Effect.interrupt, state] + } + case OP_STATE_DONE: { + return [Effect.interrupt, state] + } + } + }) + ) + } + + emit(element: Elem): Effect.Effect { + return Effect.flatMap(Deferred.make(), (deferred) => + Effect.flatten( + Ref.modify(this.ref, (state) => { + switch (state._tag) { + case OP_STATE_EMPTY: { + return [Deferred.await(state.notifyProducer), state] + } + case OP_STATE_EMIT: { + const notifyConsumer = state.notifyConsumers[0] + const notifyConsumers = state.notifyConsumers.slice(1) + if (notifyConsumer !== undefined) { + return [ + Deferred.succeed(notifyConsumer, Either.right(element)), + (notifyConsumers.length === 0 ? + stateEmpty(deferred) : + stateEmit(notifyConsumers)) as State + ] + } + throw new Error( + "Bug: Channel.SingleProducerAsyncInput.emit - Queue was empty! please report an issue at https://github.com/Effect-TS/effect/issues" + ) + } + case OP_STATE_ERROR: { + return [Effect.interrupt, state] + } + case OP_STATE_DONE: { + return [Effect.interrupt, state] + } + } + }) + )) + } + + error(cause: Cause.Cause): Effect.Effect { + return Effect.flatten( + Ref.modify(this.ref, (state) => { + switch (state._tag) { + case OP_STATE_EMPTY: { + return [Deferred.await(state.notifyProducer), state] + } + case OP_STATE_EMIT: { + return [ + Effect.forEach( + state.notifyConsumers, + (deferred) => Deferred.failCause(deferred, cause), + { discard: true } + ), + stateError(cause) as State + ] + } + case OP_STATE_ERROR: { + return [Effect.interrupt, state] + } + case OP_STATE_DONE: { + return [Effect.interrupt, state] + } + } + }) + ) + } + + get take(): Effect.Effect>> { + return this.takeWith( + (cause) => Exit.failCause(Cause.map(cause, Either.left)), + (elem) => Exit.succeed(elem) as Exit.Exit>, + (done) => Exit.fail(Either.right(done)) + ) + } + + takeWith( + onError: (cause: Cause.Cause) => A, + onElement: (element: Elem) => A, + onDone: (value: Done) => A + ): Effect.Effect { + return Effect.flatMap(Deferred.make, Err>(), (deferred) => + Effect.flatten( + Ref.modify(this.ref, (state) => { + switch (state._tag) { + case OP_STATE_EMPTY: { + return [ + Effect.zipRight( + Deferred.succeed(state.notifyProducer, void 0), + Effect.matchCause(Deferred.await(deferred), { + onFailure: onError, + onSuccess: Either.match({ onLeft: onDone, onRight: onElement }) + }) + ), + stateEmit([deferred]) + ] + } + case OP_STATE_EMIT: { + return [ + Effect.matchCause(Deferred.await(deferred), { + onFailure: onError, + onSuccess: Either.match({ onLeft: onDone, onRight: onElement }) + }), + stateEmit([...state.notifyConsumers, deferred]) + ] + } + case OP_STATE_ERROR: { + return [Effect.succeed(onError(state.cause)), state] + } + case OP_STATE_DONE: { + return [Effect.succeed(onDone(state.done)), state] + } + } + }) + )) + } +} + +/** @internal */ +export const make = (): Effect.Effect< + SingleProducerAsyncInput.SingleProducerAsyncInput +> => + pipe( + Deferred.make(), + Effect.flatMap((deferred) => Ref.make(stateEmpty(deferred) as State)), + Effect.map((ref) => new SingleProducerAsyncInputImpl(ref)) + ) diff --git a/backend/node_modules/effect/src/internal/channel/subexecutor.ts b/backend/node_modules/effect/src/internal/channel/subexecutor.ts new file mode 100644 index 0000000000000000000000000000000000000000..4cb1d9aa87cf09f01190bc3ab1869a4d8ab8e06d --- /dev/null +++ b/backend/node_modules/effect/src/internal/channel/subexecutor.ts @@ -0,0 +1,229 @@ +import type * as ChildExecutorDecision from "../../ChildExecutorDecision.js" +import * as Effect from "../../Effect.js" +import * as Exit from "../../Exit.js" +import { pipe } from "../../Function.js" +import type * as UpstreamPullRequest from "../../UpstreamPullRequest.js" +import type * as UpstreamPullStrategy from "../../UpstreamPullStrategy.js" +import type { ErasedChannel, ErasedExecutor } from "./channelExecutor.js" + +/** @internal */ +export interface Subexecutor { + close(exit: Exit.Exit): Effect.Effect | undefined + enqueuePullFromChild(child: PullFromChild): Subexecutor +} + +/** @internal */ +export type Primitive = PullFromChild | PullFromUpstream | DrainChildExecutors | Emit + +/** @internal */ +export const OP_PULL_FROM_CHILD = "PullFromChild" as const + +/** @internal */ +export type OP_PULL_FROM_CHILD = typeof OP_PULL_FROM_CHILD + +/** @internal */ +export const OP_PULL_FROM_UPSTREAM = "PullFromUpstream" as const + +/** @internal */ +export type OP_PULL_FROM_UPSTREAM = typeof OP_PULL_FROM_UPSTREAM + +/** @internal */ +export const OP_DRAIN_CHILD_EXECUTORS = "DrainChildExecutors" as const + +/** @internal */ +export type OP_DRAIN_CHILD_EXECUTORS = typeof OP_DRAIN_CHILD_EXECUTORS + +/** @internal */ +export const OP_EMIT = "Emit" as const + +/** @internal */ +export type OP_EMIT = typeof OP_EMIT + +/** + * Execute the `childExecutor` and on each emitted value, decide what to do by + * `onEmit`. + * + * @internal + */ +export class PullFromChild implements Subexecutor { + readonly _tag: OP_PULL_FROM_CHILD = OP_PULL_FROM_CHILD + + constructor( + readonly childExecutor: ErasedExecutor, + readonly parentSubexecutor: Subexecutor, + readonly onEmit: (value: unknown) => ChildExecutorDecision.ChildExecutorDecision + ) { + } + + close(exit: Exit.Exit): Effect.Effect | undefined { + const fin1 = this.childExecutor.close(exit) + const fin2 = this.parentSubexecutor.close(exit) + if (fin1 !== undefined && fin2 !== undefined) { + return Effect.zipWith( + Effect.exit(fin1), + Effect.exit(fin2), + (exit1, exit2) => pipe(exit1, Exit.zipRight(exit2)) + ) + } else if (fin1 !== undefined) { + return fin1 + } else if (fin2 !== undefined) { + return fin2 + } else { + return undefined + } + } + + enqueuePullFromChild(_child: PullFromChild): Subexecutor { + return this + } +} + +/** + * Execute `upstreamExecutor` and for each emitted element, spawn a child + * channel and continue with processing it by `PullFromChild`. + * + * @internal + */ +export class PullFromUpstream implements Subexecutor { + readonly _tag: OP_PULL_FROM_UPSTREAM = OP_PULL_FROM_UPSTREAM + + constructor( + readonly upstreamExecutor: ErasedExecutor, + readonly createChild: (value: unknown) => ErasedChannel, + readonly lastDone: unknown, + readonly activeChildExecutors: ReadonlyArray | undefined>, + readonly combineChildResults: (x: unknown, y: unknown) => unknown, + readonly combineWithChildResult: (x: unknown, y: unknown) => unknown, + readonly onPull: ( + request: UpstreamPullRequest.UpstreamPullRequest + ) => UpstreamPullStrategy.UpstreamPullStrategy, + readonly onEmit: (value: unknown) => ChildExecutorDecision.ChildExecutorDecision + ) { + } + + close(exit: Exit.Exit): Effect.Effect | undefined { + const fin1 = this.upstreamExecutor.close(exit) + const fins = [ + ...this.activeChildExecutors.map((child) => + child !== undefined ? + child.childExecutor.close(exit) : + undefined + ), + fin1 + ] + const result = fins.reduce( + (acc: Effect.Effect, never, R> | undefined, next) => { + if (acc !== undefined && next !== undefined) { + return Effect.zipWith( + acc, + Effect.exit(next), + (exit1, exit2) => Exit.zipRight(exit1, exit2) + ) + } else if (acc !== undefined) { + return acc + } else if (next !== undefined) { + return Effect.exit(next) + } else { + return undefined + } + }, + undefined + ) + return result === undefined ? result : result + } + + enqueuePullFromChild(child: PullFromChild): Subexecutor { + return new PullFromUpstream( + this.upstreamExecutor, + this.createChild, + this.lastDone, + [...this.activeChildExecutors, child], + this.combineChildResults, + this.combineWithChildResult, + this.onPull, + this.onEmit + ) + } +} + +/** + * Transformed from `PullFromUpstream` when upstream has finished but there + * are still active child executors. + * + * @internal + */ +export class DrainChildExecutors implements Subexecutor { + readonly _tag: OP_DRAIN_CHILD_EXECUTORS = OP_DRAIN_CHILD_EXECUTORS + + constructor( + readonly upstreamExecutor: ErasedExecutor, + readonly lastDone: unknown, + readonly activeChildExecutors: ReadonlyArray | undefined>, + readonly upstreamDone: Exit.Exit, + readonly combineChildResults: (x: unknown, y: unknown) => unknown, + readonly combineWithChildResult: (x: unknown, y: unknown) => unknown, + readonly onPull: ( + request: UpstreamPullRequest.UpstreamPullRequest + ) => UpstreamPullStrategy.UpstreamPullStrategy + ) { + } + + close(exit: Exit.Exit): Effect.Effect | undefined { + const fin1 = this.upstreamExecutor.close(exit) + const fins = [ + ...this.activeChildExecutors.map((child) => (child !== undefined ? + child.childExecutor.close(exit) : + undefined) + ), + fin1 + ] + const result = fins.reduce( + (acc: Effect.Effect, never, R> | undefined, next) => { + if (acc !== undefined && next !== undefined) { + return Effect.zipWith( + acc, + Effect.exit(next), + (exit1, exit2) => Exit.zipRight(exit1, exit2) + ) + } else if (acc !== undefined) { + return acc + } else if (next !== undefined) { + return Effect.exit(next) + } else { + return undefined + } + }, + undefined + ) + return result === undefined ? result : result + } + + enqueuePullFromChild(child: PullFromChild): Subexecutor { + return new DrainChildExecutors( + this.upstreamExecutor, + this.lastDone, + [...this.activeChildExecutors, child], + this.upstreamDone, + this.combineChildResults, + this.combineWithChildResult, + this.onPull + ) + } +} + +/** @internal */ +export class Emit implements Subexecutor { + readonly _tag: OP_EMIT = OP_EMIT + + constructor(readonly value: unknown, readonly next: Subexecutor) { + } + + close(exit: Exit.Exit): Effect.Effect | undefined { + const result = this.next.close(exit) + return result === undefined ? result : result + } + + enqueuePullFromChild(_child: PullFromChild): Subexecutor { + return this + } +} diff --git a/backend/node_modules/effect/src/internal/channel/upstreamPullRequest.ts b/backend/node_modules/effect/src/internal/channel/upstreamPullRequest.ts new file mode 100644 index 0000000000000000000000000000000000000000..5f800b245cbec5d4ef5a77a28beba3cac3f2c41f --- /dev/null +++ b/backend/node_modules/effect/src/internal/channel/upstreamPullRequest.ts @@ -0,0 +1,84 @@ +import { dual } from "../../Function.js" +import { hasProperty } from "../../Predicate.js" +import type * as UpstreamPullRequest from "../../UpstreamPullRequest.js" +import * as OpCodes from "../opCodes/channelUpstreamPullRequest.js" + +/** @internal */ +const UpstreamPullRequestSymbolKey = "effect/ChannelUpstreamPullRequest" + +/** @internal */ +export const UpstreamPullRequestTypeId: UpstreamPullRequest.UpstreamPullRequestTypeId = Symbol.for( + UpstreamPullRequestSymbolKey +) as UpstreamPullRequest.UpstreamPullRequestTypeId + +const upstreamPullRequestVariance = { + /* c8 ignore next */ + _A: (_: never) => _ +} + +/** @internal */ +const proto = { + [UpstreamPullRequestTypeId]: upstreamPullRequestVariance +} + +/** @internal */ +export const Pulled = (value: A): UpstreamPullRequest.UpstreamPullRequest => { + const op = Object.create(proto) + op._tag = OpCodes.OP_PULLED + op.value = value + return op +} + +/** @internal */ +export const NoUpstream = (activeDownstreamCount: number): UpstreamPullRequest.UpstreamPullRequest => { + const op = Object.create(proto) + op._tag = OpCodes.OP_NO_UPSTREAM + op.activeDownstreamCount = activeDownstreamCount + return op +} + +/** @internal */ +export const isUpstreamPullRequest = (u: unknown): u is UpstreamPullRequest.UpstreamPullRequest => + hasProperty(u, UpstreamPullRequestTypeId) + +/** @internal */ +export const isPulled = ( + self: UpstreamPullRequest.UpstreamPullRequest +): self is UpstreamPullRequest.Pulled => self._tag === OpCodes.OP_PULLED + +/** @internal */ +export const isNoUpstream = ( + self: UpstreamPullRequest.UpstreamPullRequest +): self is UpstreamPullRequest.NoUpstream => self._tag === OpCodes.OP_NO_UPSTREAM + +/** @internal */ +export const match = dual< + ( + options: { + readonly onPulled: (value: A) => Z + readonly onNoUpstream: (activeDownstreamCount: number) => Z + } + ) => (self: UpstreamPullRequest.UpstreamPullRequest) => Z, + ( + self: UpstreamPullRequest.UpstreamPullRequest, + options: { + readonly onPulled: (value: A) => Z + readonly onNoUpstream: (activeDownstreamCount: number) => Z + } + ) => Z +>(2, ( + self: UpstreamPullRequest.UpstreamPullRequest, + { onNoUpstream, onPulled }: { + readonly onPulled: (value: A) => Z + readonly onNoUpstream: (activeDownstreamCount: number) => Z + } +): Z => { + switch (self._tag) { + case OpCodes.OP_PULLED: { + return onPulled(self.value) + } + case OpCodes.OP_NO_UPSTREAM: { + return onNoUpstream(self.activeDownstreamCount) + } + } +}) diff --git a/backend/node_modules/effect/src/internal/channel/upstreamPullStrategy.ts b/backend/node_modules/effect/src/internal/channel/upstreamPullStrategy.ts new file mode 100644 index 0000000000000000000000000000000000000000..b250f7c99a777e78af74dfb1d606ce7e2bacc13e --- /dev/null +++ b/backend/node_modules/effect/src/internal/channel/upstreamPullStrategy.ts @@ -0,0 +1,87 @@ +import { dual } from "../../Function.js" +import type * as Option from "../../Option.js" +import { hasProperty } from "../../Predicate.js" +import type * as UpstreamPullStrategy from "../../UpstreamPullStrategy.js" +import * as OpCodes from "../opCodes/channelUpstreamPullStrategy.js" + +/** @internal */ +const UpstreamPullStrategySymbolKey = "effect/ChannelUpstreamPullStrategy" + +/** @internal */ +export const UpstreamPullStrategyTypeId: UpstreamPullStrategy.UpstreamPullStrategyTypeId = Symbol.for( + UpstreamPullStrategySymbolKey +) as UpstreamPullStrategy.UpstreamPullStrategyTypeId + +const upstreamPullStrategyVariance = { + /* c8 ignore next */ + _A: (_: never) => _ +} + +/** @internal */ +const proto = { + [UpstreamPullStrategyTypeId]: upstreamPullStrategyVariance +} + +/** @internal */ +export const PullAfterNext = (emitSeparator: Option.Option): UpstreamPullStrategy.UpstreamPullStrategy => { + const op = Object.create(proto) + op._tag = OpCodes.OP_PULL_AFTER_NEXT + op.emitSeparator = emitSeparator + return op +} + +/** @internal */ +export const PullAfterAllEnqueued = ( + emitSeparator: Option.Option +): UpstreamPullStrategy.UpstreamPullStrategy => { + const op = Object.create(proto) + op._tag = OpCodes.OP_PULL_AFTER_ALL_ENQUEUED + op.emitSeparator = emitSeparator + return op +} + +/** @internal */ +export const isUpstreamPullStrategy = (u: unknown): u is UpstreamPullStrategy.UpstreamPullStrategy => + hasProperty(u, UpstreamPullStrategyTypeId) + +/** @internal */ +export const isPullAfterNext = ( + self: UpstreamPullStrategy.UpstreamPullStrategy +): self is UpstreamPullStrategy.PullAfterNext => self._tag === OpCodes.OP_PULL_AFTER_NEXT + +/** @internal */ +export const isPullAfterAllEnqueued = ( + self: UpstreamPullStrategy.UpstreamPullStrategy +): self is UpstreamPullStrategy.PullAfterAllEnqueued => self._tag === OpCodes.OP_PULL_AFTER_ALL_ENQUEUED + +/** @internal */ +export const match = dual< + ( + options: { + readonly onNext: (emitSeparator: Option.Option) => Z + readonly onAllEnqueued: (emitSeparator: Option.Option) => Z + } + ) => (self: UpstreamPullStrategy.UpstreamPullStrategy) => Z, + ( + self: UpstreamPullStrategy.UpstreamPullStrategy, + options: { + readonly onNext: (emitSeparator: Option.Option) => Z + readonly onAllEnqueued: (emitSeparator: Option.Option) => Z + } + ) => Z +>(2, ( + self: UpstreamPullStrategy.UpstreamPullStrategy, + { onAllEnqueued, onNext }: { + readonly onNext: (emitSeparator: Option.Option) => Z + readonly onAllEnqueued: (emitSeparator: Option.Option) => Z + } +): Z => { + switch (self._tag) { + case OpCodes.OP_PULL_AFTER_NEXT: { + return onNext(self.emitSeparator) + } + case OpCodes.OP_PULL_AFTER_ALL_ENQUEUED: { + return onAllEnqueued(self.emitSeparator) + } + } +}) diff --git a/backend/node_modules/effect/src/internal/clock.ts b/backend/node_modules/effect/src/internal/clock.ts new file mode 100644 index 0000000000000000000000000000000000000000..30fc73bfc51193014b4b8ee2969a296dc6cf1ef6 --- /dev/null +++ b/backend/node_modules/effect/src/internal/clock.ts @@ -0,0 +1,95 @@ +import type * as Clock from "../Clock.js" +import * as Context from "../Context.js" +import * as Duration from "../Duration.js" +import type * as Effect from "../Effect.js" +import { constFalse } from "../Function.js" +import * as core from "./core.js" + +/** @internal */ +const ClockSymbolKey = "effect/Clock" + +/** @internal */ +export const ClockTypeId: Clock.ClockTypeId = Symbol.for(ClockSymbolKey) as Clock.ClockTypeId + +/** @internal */ +export const clockTag: Context.Tag = Context.GenericTag("effect/Clock") + +/** @internal */ +export const MAX_TIMER_MILLIS = 2 ** 31 - 1 + +/** @internal */ +export const globalClockScheduler: Clock.ClockScheduler = { + unsafeSchedule(task: Clock.Task, duration: Duration.Duration): Clock.CancelToken { + const millis = Duration.toMillis(duration) + // If the duration is greater than the value allowable by the JS timer + // functions, treat the value as an infinite duration + if (millis > MAX_TIMER_MILLIS) { + return constFalse + } + let completed = false + const handle = setTimeout(() => { + completed = true + task() + }, millis) + return () => { + clearTimeout(handle) + return !completed + } + } +} + +const performanceNowNanos = (function() { + const bigint1e6 = BigInt(1_000_000) + if (typeof performance === "undefined" || typeof performance.now !== "function") { + return () => BigInt(Date.now()) * bigint1e6 + } + let origin: bigint + return () => { + if (origin === undefined) { + origin = (BigInt(Date.now()) * bigint1e6) - BigInt(Math.round(performance.now() * 1_000_000)) + } + return origin + BigInt(Math.round(performance.now() * 1000000)) + } +})() +const processOrPerformanceNow = (function() { + const processHrtime = + typeof process === "object" && "hrtime" in process && typeof process.hrtime.bigint === "function" ? + process.hrtime : + undefined + if (!processHrtime) { + return performanceNowNanos + } + const origin = performanceNowNanos() - processHrtime.bigint() + return () => origin + processHrtime.bigint() +})() + +/** @internal */ +class ClockImpl implements Clock.Clock { + readonly [ClockTypeId]: Clock.ClockTypeId = ClockTypeId + + unsafeCurrentTimeMillis(): number { + return Date.now() + } + + unsafeCurrentTimeNanos(): bigint { + return processOrPerformanceNow() + } + + currentTimeMillis: Effect.Effect = core.sync(() => this.unsafeCurrentTimeMillis()) + + currentTimeNanos: Effect.Effect = core.sync(() => this.unsafeCurrentTimeNanos()) + + scheduler(): Effect.Effect { + return core.succeed(globalClockScheduler) + } + + sleep(duration: Duration.Duration): Effect.Effect { + return core.async((resume) => { + const canceler = globalClockScheduler.unsafeSchedule(() => resume(core.void), duration) + return core.asVoid(core.sync(canceler)) + }) + } +} + +/** @internal */ +export const make = (): Clock.Clock => new ClockImpl() diff --git a/backend/node_modules/effect/src/internal/concurrency.ts b/backend/node_modules/effect/src/internal/concurrency.ts new file mode 100644 index 0000000000000000000000000000000000000000..61ca25454c8aec919e0ee2ba4bd158efab76790c --- /dev/null +++ b/backend/node_modules/effect/src/internal/concurrency.ts @@ -0,0 +1,54 @@ +import type { Effect } from "../Effect.js" +import type { Concurrency } from "../Types.js" +import * as core from "./core.js" + +/** @internal */ +export const match = ( + concurrency: Concurrency | undefined, + sequential: () => Effect, + unbounded: () => Effect, + bounded: (limit: number) => Effect +): Effect => { + switch (concurrency) { + case undefined: + return sequential() + case "unbounded": + return unbounded() + case "inherit": + return core.fiberRefGetWith( + core.currentConcurrency, + (concurrency) => + concurrency === "unbounded" ? + unbounded() : + concurrency > 1 ? + bounded(concurrency) : + sequential() + ) + default: + return concurrency > 1 ? bounded(concurrency) : sequential() + } +} + +/** @internal */ +export const matchSimple = ( + concurrency: Concurrency | undefined, + sequential: () => Effect, + concurrent: () => Effect +): Effect => { + switch (concurrency) { + case undefined: + return sequential() + case "unbounded": + return concurrent() + case "inherit": + return core.fiberRefGetWith( + core.currentConcurrency, + (concurrency) => + concurrency === "unbounded" || concurrency > 1 ? + concurrent() : + sequential() + ) + default: + return concurrency > 1 ? concurrent() : sequential() + } +} diff --git a/backend/node_modules/effect/src/internal/config.ts b/backend/node_modules/effect/src/internal/config.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ac67dc87525c6f9185b66eef2f2b354c956aca7 --- /dev/null +++ b/backend/node_modules/effect/src/internal/config.ts @@ -0,0 +1,716 @@ +import type * as Brand from "../Brand.js" +import * as Chunk from "../Chunk.js" +import type * as Config from "../Config.js" +import * as ConfigError from "../ConfigError.js" +import * as Duration from "../Duration.js" +import * as Either from "../Either.js" +import type { LazyArg } from "../Function.js" +import { constTrue, dual, pipe } from "../Function.js" +import type * as HashMap from "../HashMap.js" +import * as HashSet from "../HashSet.js" +import { formatUnknown } from "../Inspectable.js" +import type * as LogLevel from "../LogLevel.js" +import * as Option from "../Option.js" +import { hasProperty, type Predicate, type Refinement } from "../Predicate.js" +import type * as Redacted from "../Redacted.js" +import type * as Secret from "../Secret.js" +import * as configError from "./configError.js" +import * as core from "./core.js" +import * as defaultServices from "./defaultServices.js" +import * as effectable from "./effectable.js" +import * as OpCodes from "./opCodes/config.js" +import * as redacted_ from "./redacted.js" +import * as InternalSecret from "./secret.js" + +const ConfigSymbolKey = "effect/Config" + +/** @internal */ +export const ConfigTypeId: Config.ConfigTypeId = Symbol.for( + ConfigSymbolKey +) as Config.ConfigTypeId + +/** @internal */ +export type ConfigPrimitive = + | Constant + | Described + | Fallback + | Fail + | Lazy + | MapOrFail + | Nested + | Primitive + | Sequence + | Table + | Zipped + +const configVariance = { + /* c8 ignore next */ + _A: (_: never) => _ +} + +const proto = { + ...effectable.CommitPrototype, + [ConfigTypeId]: configVariance, + commit(this: Config.Config) { + return defaultServices.config(this) + } +} + +/** @internal */ +export type Op = Config.Config & Body & { + readonly _tag: Tag +} + +/** @internal */ +export interface Constant extends + Op + }> +{} + +/** @internal */ +export interface Described extends + Op + readonly description: string + }> +{} + +/** @internal */ +export interface Fallback extends + Op + readonly second: Config.Config + readonly condition: Predicate + }> +{} + +/** @internal */ +export interface Fail extends + Op + }> +{} + +/** @internal */ +export interface Lazy extends + Op + }> +{} + +/** @internal */ +export interface MapOrFail extends + Op + mapOrFail(value: unknown): Either.Either + }> +{} + +/** @internal */ +export interface Nested extends + Op + }> +{} + +/** @internal */ +export interface Primitive extends + Op + }> +{} + +/** @internal */ +export interface Sequence extends + Op + }> +{} + +/** @internal */ +export interface Table extends + Op + }> +{} + +/** @internal */ +export interface Zipped extends + Op + readonly right: Config.Config + zip(a: unknown, b: unknown): unknown + }> +{} + +/** @internal */ +export const boolean = (name?: string): Config.Config => { + const config = primitive( + "a boolean property", + (text) => { + switch (text) { + case "true": + case "yes": + case "on": + case "1": { + return Either.right(true) + } + case "false": + case "no": + case "off": + case "0": { + return Either.right(false) + } + default: { + const error = configError.InvalidData( + [], + `Expected a boolean value but received ${formatUnknown(text)}` + ) + return Either.left(error) + } + } + } + ) + return name === undefined ? config : nested(config, name) +} + +/** @internal */ +export const url = (name?: string): Config.Config => { + const config = primitive( + "an URL property", + (text) => + Either.try({ + try: () => new URL(text), + catch: (_) => configError.InvalidData([], `Expected an URL value but received ${formatUnknown(text)}`) + }) + ) + return name === undefined ? config : nested(config, name) +} + +/** @internal */ +export const port = (name?: string): Config.Config => { + const config = primitive( + "a network port property", + (text) => { + const result = Number(text) + + if ( + Number.isNaN(result) || + result.toString() !== text.toString() || + !Number.isInteger(result) || + result < 1 || + result > 65535 + ) { + return Either.left( + configError.InvalidData( + [], + `Expected a network port value but received ${formatUnknown(text)}` + ) + ) + } + return Either.right(result) + } + ) + return name === undefined ? config : nested(config, name) +} + +/** @internal */ +export const array = (config: Config.Config, name?: string): Config.Config> => { + return pipe(chunk(config, name), map(Chunk.toArray)) +} + +/** @internal */ +export const chunk = (config: Config.Config, name?: string): Config.Config> => { + return map(name === undefined ? repeat(config) : nested(repeat(config), name), Chunk.unsafeFromArray) +} + +/** @internal */ +export const date = (name?: string): Config.Config => { + const config = primitive( + "a date property", + (text) => { + const result = Date.parse(text) + if (Number.isNaN(result)) { + return Either.left( + configError.InvalidData( + [], + `Expected a Date value but received ${formatUnknown(text)}` + ) + ) + } + return Either.right(new Date(result)) + } + ) + return name === undefined ? config : nested(config, name) +} + +/** @internal */ +export const fail = (message: string): Config.Config => { + const fail = Object.create(proto) + fail._tag = OpCodes.OP_FAIL + fail.message = message + fail.parse = () => Either.left(configError.Unsupported([], message)) + return fail +} + +/** @internal */ +export const number = (name?: string): Config.Config => { + const config = primitive( + "a number property", + (text) => { + const result = Number(text) + if (Number.isNaN(result)) { + return Either.left( + configError.InvalidData( + [], + `Expected a number value but received ${formatUnknown(text)}` + ) + ) + } + return Either.right(result) + } + ) + return name === undefined ? config : nested(config, name) +} + +/** @internal */ +export const integer = (name?: string): Config.Config => { + const config = primitive( + "an integer property", + (text) => { + const result = Number(text) + if (!Number.isInteger(result)) { + return Either.left( + configError.InvalidData( + [], + `Expected an integer value but received ${formatUnknown(text)}` + ) + ) + } + return Either.right(result) + } + ) + return name === undefined ? config : nested(config, name) +} + +/** @internal */ +export const literal = >(...literals: Literals) => +( + name?: string +): Config.Config => { + const valuesString = literals.map(String).join(", ") + const config = primitive(`one of (${valuesString})`, (text) => { + const found = literals.find((value) => String(value) === text) + if (found === undefined) { + return Either.left( + configError.InvalidData( + [], + `Expected one of (${valuesString}) but received ${formatUnknown(text)}` + ) + ) + } + return Either.right(found) + }) + return name === undefined ? config : nested(config, name) +} + +/** @internal */ +export const logLevel = (name?: string): Config.Config => { + const config = mapOrFail(string(), (value) => { + const label = value.toUpperCase() + const level = core.allLogLevels.find((level) => level.label === label) + return level === undefined + ? Either.left( + configError.InvalidData([], `Expected a log level but received ${formatUnknown(value)}`) + ) + : Either.right(level) + }) + return name === undefined ? config : nested(config, name) +} + +/** @internal */ +export const duration = (name?: string): Config.Config => { + const config = mapOrFail(string(), (value) => { + const duration = Duration.decodeUnknown(value) + return Either.fromOption( + duration, + () => configError.InvalidData([], `Expected a duration but received ${formatUnknown(value)}`) + ) + }) + return name === undefined ? config : nested(config, name) +} + +/** @internal */ +export const map = dual< + (f: (a: A) => B) => (self: Config.Config) => Config.Config, + (self: Config.Config, f: (a: A) => B) => Config.Config +>(2, (self, f) => mapOrFail(self, (a) => Either.right(f(a)))) + +/** @internal */ +export const mapAttempt = dual< + (f: (a: A) => B) => (self: Config.Config) => Config.Config, + (self: Config.Config, f: (a: A) => B) => Config.Config +>(2, (self, f) => + mapOrFail(self, (a) => { + try { + return Either.right(f(a)) + } catch (error) { + return Either.left( + configError.InvalidData( + [], + error instanceof Error ? error.message : `${error}` + ) + ) + } + })) + +/** @internal */ +export const mapOrFail = dual< + (f: (a: A) => Either.Either) => (self: Config.Config) => Config.Config, + (self: Config.Config, f: (a: A) => Either.Either) => Config.Config +>(2, (self, f) => { + const mapOrFail = Object.create(proto) + mapOrFail._tag = OpCodes.OP_MAP_OR_FAIL + mapOrFail.original = self + mapOrFail.mapOrFail = f + return mapOrFail +}) + +/** @internal */ +export const nested = dual< + (name: string) => (self: Config.Config) => Config.Config, + (self: Config.Config, name: string) => Config.Config +>(2, (self, name) => { + const nested = Object.create(proto) + nested._tag = OpCodes.OP_NESTED + nested.name = name + nested.config = self + return nested +}) + +/** @internal */ +export const orElse = dual< + (that: LazyArg>) => (self: Config.Config) => Config.Config, + (self: Config.Config, that: LazyArg>) => Config.Config +>(2, (self, that) => { + const fallback = Object.create(proto) + fallback._tag = OpCodes.OP_FALLBACK + fallback.first = self + fallback.second = suspend(that) + fallback.condition = constTrue + return fallback +}) + +/** @internal */ +export const orElseIf = dual< + ( + options: { + readonly if: Predicate + readonly orElse: LazyArg> + } + ) => (self: Config.Config) => Config.Config, + ( + self: Config.Config, + options: { + readonly if: Predicate + readonly orElse: LazyArg> + } + ) => Config.Config +>(2, (self, options) => { + const fallback = Object.create(proto) + fallback._tag = OpCodes.OP_FALLBACK + fallback.first = self + fallback.second = suspend(options.orElse) + fallback.condition = options.if + return fallback +}) + +/** @internal */ +export const option = (self: Config.Config): Config.Config> => { + return pipe( + self, + map(Option.some), + orElseIf({ orElse: () => succeed(Option.none()), if: ConfigError.isMissingDataOnly }) + ) +} + +/** @internal */ +export const primitive = ( + description: string, + parse: (text: string) => Either.Either +): Config.Config => { + const primitive = Object.create(proto) + primitive._tag = OpCodes.OP_PRIMITIVE + primitive.description = description + primitive.parse = parse + return primitive +} + +/** @internal */ +export const repeat = (self: Config.Config): Config.Config> => { + const repeat = Object.create(proto) + repeat._tag = OpCodes.OP_SEQUENCE + repeat.config = self + return repeat +} + +/** @internal */ +export const secret = (name?: string): Config.Config => { + const config = primitive( + "a secret property", + (text) => Either.right(InternalSecret.fromString(text)) + ) + return name === undefined ? config : nested(config, name) +} + +/** @internal */ +export const redacted = ( + nameOrConfig?: string | Config.Config +): Config.Config> => { + const config: Config.Config = isConfig(nameOrConfig) ? nameOrConfig : string(nameOrConfig) + return map(config, redacted_.make) +} + +/** @internal */ +export const branded: { + >( + constructor: Brand.Brand.Constructor + ): (config: Config.Config) => Config.Config + >( + name: string | undefined, + constructor: Brand.Brand.Constructor + ): Config.Config + >( + config: Config.Config, + constructor: Brand.Brand.Constructor + ): Config.Config +} = dual(2, >( + nameOrConfig: Config.Config> | string | undefined, + constructor: B +) => { + const config: Config.Config = isConfig(nameOrConfig) ? nameOrConfig : string(nameOrConfig) + + return mapOrFail(config, (a) => + constructor.either(a).pipe( + Either.mapLeft((brandErrors) => + configError.InvalidData([], brandErrors.map((brandError) => brandError.message).join("\n")) + ) + )) +}) + +/** @internal */ +export const hashSet = (config: Config.Config, name?: string): Config.Config> => { + const newConfig = map(chunk(config), HashSet.fromIterable) + return name === undefined ? newConfig : nested(newConfig, name) +} + +/** @internal */ +export const string = (name?: string): Config.Config => { + const config = primitive( + "a text property", + Either.right + ) + return name === undefined ? config : nested(config, name) +} + +/** @internal */ +export const nonEmptyString = (name?: string): Config.Config => { + const config = primitive( + "a non-empty text property", + Either.liftPredicate((text) => text.length > 0, () => configError.MissingData([], "Expected a non-empty string")) + ) + + return name === undefined ? config : nested(config, name) +} + +/** @internal */ +export const all = > | Record>>( + arg: Arg +): Config.Config< + [Arg] extends [ReadonlyArray>] ? { + -readonly [K in keyof Arg]: [Arg[K]] extends [Config.Config] ? A : never + } + : [Arg] extends [Iterable>] ? Array + : [Arg] extends [Record>] ? { + -readonly [K in keyof Arg]: [Arg[K]] extends [Config.Config] ? A : never + } + : never +> => { + if (Array.isArray(arg)) { + return tuple(arg) as any + } else if (Symbol.iterator in arg) { + return tuple([...(arg as Iterable>)]) as any + } + return struct(arg) as any +} + +const struct = >>(r: NER): Config.Config< + { + [K in keyof NER]: [NER[K]] extends [{ [ConfigTypeId]: { _A: (_: never) => infer A } }] ? A : never + } +> => { + const entries = Object.entries(r) + let result = pipe(entries[0][1], map((value) => ({ [entries[0][0]]: value }))) + if (entries.length === 1) { + return result as any + } + const rest = entries.slice(1) + for (const [key, config] of rest) { + result = pipe( + result, + zipWith(config, (record, value) => ({ ...record, [key]: value })) + ) + } + return result as any +} + +/** @internal */ +export const succeed = (value: A): Config.Config => { + const constant = Object.create(proto) + constant._tag = OpCodes.OP_CONSTANT + constant.value = value + constant.parse = () => Either.right(value) + return constant +} + +/** @internal */ +export const suspend = (config: LazyArg>): Config.Config => { + const lazy = Object.create(proto) + lazy._tag = OpCodes.OP_LAZY + lazy.config = config + return lazy +} + +/** @internal */ +export const sync = (value: LazyArg): Config.Config => { + return suspend(() => succeed(value())) +} + +/** @internal */ +export const hashMap = (config: Config.Config, name?: string): Config.Config> => { + const table = Object.create(proto) + table._tag = OpCodes.OP_HASHMAP + table.valueConfig = config + return name === undefined ? table : nested(table, name) +} + +/** @internal */ +export const isConfig = (u: unknown): u is Config.Config => hasProperty(u, ConfigTypeId) + +/** @internal */ +const tuple = >>(tuple: T): Config.Config< + { + [K in keyof T]: [T[K]] extends [Config.Config] ? A : never + } +> => { + if (tuple.length === 0) { + return succeed([]) as any + } + if (tuple.length === 1) { + return map(tuple[0], (x) => [x]) as any + } + let result = map(tuple[0], (x) => [x]) + for (let i = 1; i < tuple.length; i++) { + const config = tuple[i] + result = pipe( + result, + zipWith(config, (tuple, value) => [...tuple, value]) + ) as any + } + return result as any +} + +/** + * @internal + */ +export const unwrap = (wrapped: Config.Config.Wrap): Config.Config => { + if (isConfig(wrapped)) { + return wrapped + } + return struct( + Object.fromEntries( + Object.entries(wrapped).map(([k, a]) => [k, unwrap(a as any)]) + ) + ) as any +} + +/** @internal */ +export const validate = dual< + { + (options: { + readonly message: string + readonly validation: Refinement + }): (self: Config.Config) => Config.Config + (options: { + readonly message: string + readonly validation: Predicate + }): (self: Config.Config) => Config.Config + }, + { + (self: Config.Config, options: { + readonly message: string + readonly validation: Refinement + }): Config.Config + (self: Config.Config, options: { + readonly message: string + readonly validation: Predicate + }): Config.Config + } +>(2, (self: Config.Config, { message, validation }: { + readonly message: string + readonly validation: Predicate +}) => + mapOrFail(self, (a) => { + if (validation(a)) { + return Either.right(a) + } + return Either.left(configError.InvalidData([], message)) + })) + +/** @internal */ +export const withDefault = dual< + (def: A2) => (self: Config.Config) => Config.Config, + (self: Config.Config, def: A2) => Config.Config +>(2, (self, def) => + orElseIf(self, { + orElse: () => succeed(def), + if: ConfigError.isMissingDataOnly + })) + +/** @internal */ +export const withDescription = dual< + (description: string) => (self: Config.Config) => Config.Config, + (self: Config.Config, description: string) => Config.Config +>(2, (self, description) => { + const described = Object.create(proto) + described._tag = OpCodes.OP_DESCRIBED + described.config = self + described.description = description + return described +}) + +/** @internal */ +export const zip = dual< + (that: Config.Config) => (self: Config.Config) => Config.Config<[A, B]>, + (self: Config.Config, that: Config.Config) => Config.Config<[A, B]> +>(2, (self, that) => zipWith(self, that, (a, b) => [a, b])) + +/** @internal */ +export const zipWith = dual< + (that: Config.Config, f: (a: A, b: B) => C) => (self: Config.Config) => Config.Config, + (self: Config.Config, that: Config.Config, f: (a: A, b: B) => C) => Config.Config +>(3, (self, that, f) => { + const zipWith = Object.create(proto) + zipWith._tag = OpCodes.OP_ZIP_WITH + zipWith.left = self + zipWith.right = that + zipWith.zip = f + return zipWith +}) diff --git a/backend/node_modules/effect/src/internal/configError.ts b/backend/node_modules/effect/src/internal/configError.ts new file mode 100644 index 0000000000000000000000000000000000000000..436b1075e2136836a51dd8dabad81730619a8f66 --- /dev/null +++ b/backend/node_modules/effect/src/internal/configError.ts @@ -0,0 +1,304 @@ +import * as RA from "../Array.js" +import type * as Cause from "../Cause.js" +import type * as ConfigError from "../ConfigError.js" +import * as Either from "../Either.js" +import { constFalse, constTrue, dual, pipe } from "../Function.js" +import { hasProperty } from "../Predicate.js" +import * as OpCodes from "./opCodes/configError.js" + +/** @internal */ +const ConfigErrorSymbolKey = "effect/ConfigError" + +/** @internal */ +export const ConfigErrorTypeId: ConfigError.ConfigErrorTypeId = Symbol.for( + ConfigErrorSymbolKey +) as ConfigError.ConfigErrorTypeId + +/** @internal */ +export const proto = { + _tag: "ConfigError", + [ConfigErrorTypeId]: ConfigErrorTypeId +} + +/** @internal */ +export const And = (self: ConfigError.ConfigError, that: ConfigError.ConfigError): ConfigError.ConfigError => { + const error = Object.create(proto) + error._op = OpCodes.OP_AND + error.left = self + error.right = that + Object.defineProperty(error, "toString", { + enumerable: false, + value(this: ConfigError.And) { + return `${this.left} and ${this.right}` + } + }) + Object.defineProperty(error, "message", { + enumerable: false, + get(this: ConfigError.And) { + return this.toString() + } + }) + return error +} + +/** @internal */ +export const Or = (self: ConfigError.ConfigError, that: ConfigError.ConfigError): ConfigError.ConfigError => { + const error = Object.create(proto) + error._op = OpCodes.OP_OR + error.left = self + error.right = that + Object.defineProperty(error, "toString", { + enumerable: false, + value(this: ConfigError.Or) { + return `${this.left} or ${this.right}` + } + }) + Object.defineProperty(error, "message", { + enumerable: false, + get(this: ConfigError.Or) { + return this.toString() + } + }) + return error +} + +/** @internal */ +export const InvalidData = ( + path: ReadonlyArray, + message: string, + options: ConfigError.Options = { pathDelim: "." } +): ConfigError.ConfigError => { + const error = Object.create(proto) + error._op = OpCodes.OP_INVALID_DATA + error.path = path + error.message = message + Object.defineProperty(error, "toString", { + enumerable: false, + value(this: ConfigError.InvalidData) { + const path = pipe(this.path, RA.join(options.pathDelim)) + return `(Invalid data at ${path}: "${this.message}")` + } + }) + return error +} + +/** @internal */ +export const MissingData = ( + path: ReadonlyArray, + message: string, + options: ConfigError.Options = { pathDelim: "." } +): ConfigError.ConfigError => { + const error = Object.create(proto) + error._op = OpCodes.OP_MISSING_DATA + error.path = path + error.message = message + Object.defineProperty(error, "toString", { + enumerable: false, + value(this: ConfigError.MissingData) { + const path = pipe(this.path, RA.join(options.pathDelim)) + return `(Missing data at ${path}: "${this.message}")` + } + }) + return error +} + +/** @internal */ +export const SourceUnavailable = ( + path: ReadonlyArray, + message: string, + cause: Cause.Cause, + options: ConfigError.Options = { pathDelim: "." } +): ConfigError.ConfigError => { + const error = Object.create(proto) + error._op = OpCodes.OP_SOURCE_UNAVAILABLE + error.path = path + error.message = message + error.cause = cause + Object.defineProperty(error, "toString", { + enumerable: false, + value(this: ConfigError.SourceUnavailable) { + const path = pipe(this.path, RA.join(options.pathDelim)) + return `(Source unavailable at ${path}: "${this.message}")` + } + }) + return error +} + +/** @internal */ +export const Unsupported = ( + path: ReadonlyArray, + message: string, + options: ConfigError.Options = { pathDelim: "." } +): ConfigError.ConfigError => { + const error = Object.create(proto) + error._op = OpCodes.OP_UNSUPPORTED + error.path = path + error.message = message + Object.defineProperty(error, "toString", { + enumerable: false, + value(this: ConfigError.Unsupported) { + const path = pipe(this.path, RA.join(options.pathDelim)) + return `(Unsupported operation at ${path}: "${this.message}")` + } + }) + return error +} + +/** @internal */ +export const isConfigError = (u: unknown): u is ConfigError.ConfigError => hasProperty(u, ConfigErrorTypeId) + +/** @internal */ +export const isAnd = (self: ConfigError.ConfigError): self is ConfigError.And => self._op === OpCodes.OP_AND + +/** @internal */ +export const isOr = (self: ConfigError.ConfigError): self is ConfigError.Or => self._op === OpCodes.OP_OR + +/** @internal */ +export const isInvalidData = (self: ConfigError.ConfigError): self is ConfigError.InvalidData => + self._op === OpCodes.OP_INVALID_DATA + +/** @internal */ +export const isMissingData = (self: ConfigError.ConfigError): self is ConfigError.MissingData => + self._op === OpCodes.OP_MISSING_DATA + +/** @internal */ +export const isSourceUnavailable = (self: ConfigError.ConfigError): self is ConfigError.SourceUnavailable => + self._op === OpCodes.OP_SOURCE_UNAVAILABLE + +/** @internal */ +export const isUnsupported = (self: ConfigError.ConfigError): self is ConfigError.Unsupported => + self._op === OpCodes.OP_UNSUPPORTED + +/** @internal */ +export const prefixed: { + (prefix: ReadonlyArray): (self: ConfigError.ConfigError) => ConfigError.ConfigError + (self: ConfigError.ConfigError, prefix: ReadonlyArray): ConfigError.ConfigError +} = dual< + (prefix: ReadonlyArray) => (self: ConfigError.ConfigError) => ConfigError.ConfigError, + (self: ConfigError.ConfigError, prefix: ReadonlyArray) => ConfigError.ConfigError +>(2, (self, prefix) => { + switch (self._op) { + case OpCodes.OP_AND: { + return And(prefixed(self.left, prefix), prefixed(self.right, prefix)) + } + case OpCodes.OP_OR: { + return Or(prefixed(self.left, prefix), prefixed(self.right, prefix)) + } + case OpCodes.OP_INVALID_DATA: { + return InvalidData([...prefix, ...self.path], self.message) + } + case OpCodes.OP_MISSING_DATA: { + return MissingData([...prefix, ...self.path], self.message) + } + case OpCodes.OP_SOURCE_UNAVAILABLE: { + return SourceUnavailable([...prefix, ...self.path], self.message, self.cause) + } + case OpCodes.OP_UNSUPPORTED: { + return Unsupported([...prefix, ...self.path], self.message) + } + } +}) + +/** @internal */ +const IsMissingDataOnlyReducer: ConfigError.ConfigErrorReducer = { + andCase: (_, left, right) => left && right, + orCase: (_, left, right) => left && right, + invalidDataCase: constFalse, + missingDataCase: constTrue, + sourceUnavailableCase: constFalse, + unsupportedCase: constFalse +} + +/** @internal */ +type ConfigErrorCase = AndCase | OrCase + +/** @internal */ +interface AndCase { + readonly _op: "AndCase" +} + +/** @internal */ +interface OrCase { + readonly _op: "OrCase" +} + +/** @internal */ +export const reduceWithContext = dual< + (context: C, reducer: ConfigError.ConfigErrorReducer) => (self: ConfigError.ConfigError) => Z, + (self: ConfigError.ConfigError, context: C, reducer: ConfigError.ConfigErrorReducer) => Z +>(3, (self: ConfigError.ConfigError, context: C, reducer: ConfigError.ConfigErrorReducer) => { + const input: Array = [self] + const output: Array> = [] + while (input.length > 0) { + const error = input.pop()! + switch (error._op) { + case OpCodes.OP_AND: { + input.push(error.right) + input.push(error.left) + output.push(Either.left({ _op: "AndCase" })) + break + } + case OpCodes.OP_OR: { + input.push(error.right) + input.push(error.left) + output.push(Either.left({ _op: "OrCase" })) + break + } + case OpCodes.OP_INVALID_DATA: { + output.push(Either.right(reducer.invalidDataCase(context, error.path, error.message))) + break + } + case OpCodes.OP_MISSING_DATA: { + output.push(Either.right(reducer.missingDataCase(context, error.path, error.message))) + break + } + case OpCodes.OP_SOURCE_UNAVAILABLE: { + output.push(Either.right(reducer.sourceUnavailableCase(context, error.path, error.message, error.cause))) + break + } + case OpCodes.OP_UNSUPPORTED: { + output.push(Either.right(reducer.unsupportedCase(context, error.path, error.message))) + break + } + } + } + const accumulator: Array = [] + while (output.length > 0) { + const either = output.pop()! + switch (either._op) { + case "Left": { + switch (either.left._op) { + case "AndCase": { + const left = accumulator.pop()! + const right = accumulator.pop()! + const value = reducer.andCase(context, left, right) + accumulator.push(value) + break + } + case "OrCase": { + const left = accumulator.pop()! + const right = accumulator.pop()! + const value = reducer.orCase(context, left, right) + accumulator.push(value) + break + } + } + break + } + case "Right": { + accumulator.push(either.right) + break + } + } + } + if (accumulator.length === 0) { + throw new Error( + "BUG: ConfigError.reduceWithContext - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + } + return accumulator.pop()! +}) + +/** @internal */ +export const isMissingDataOnly = (self: ConfigError.ConfigError): boolean => + reduceWithContext(self, void 0, IsMissingDataOnlyReducer) diff --git a/backend/node_modules/effect/src/internal/configProvider.ts b/backend/node_modules/effect/src/internal/configProvider.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6590dc0e2fc4895d56153889c8f764b27696c6b --- /dev/null +++ b/backend/node_modules/effect/src/internal/configProvider.ts @@ -0,0 +1,799 @@ +import * as Arr from "../Array.js" +import type * as Config from "../Config.js" +import type * as ConfigError from "../ConfigError.js" +import type * as ConfigProvider from "../ConfigProvider.js" +import type * as PathPatch from "../ConfigProviderPathPatch.js" +import * as Context from "../Context.js" +import type * as Effect from "../Effect.js" +import * as Either from "../Either.js" +import type { LazyArg } from "../Function.js" +import { dual, pipe } from "../Function.js" +import * as HashMap from "../HashMap.js" +import * as HashSet from "../HashSet.js" +import * as number from "../Number.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import * as Predicate from "../Predicate.js" +import * as regexp from "../RegExp.js" +import type * as _config from "./config.js" +import * as configError from "./configError.js" +import * as pathPatch from "./configProvider/pathPatch.js" +import * as core from "./core.js" +import * as OpCodes from "./opCodes/config.js" +import * as StringUtils from "./string-utils.js" + +type KeyComponent = ConfigProvider.ConfigProvider.KeyComponent + +const concat = (l: ReadonlyArray, r: ReadonlyArray): ReadonlyArray => [...l, ...r] + +/** @internal */ +const ConfigProviderSymbolKey = "effect/ConfigProvider" + +/** @internal */ +export const ConfigProviderTypeId: ConfigProvider.ConfigProviderTypeId = Symbol.for( + ConfigProviderSymbolKey +) as ConfigProvider.ConfigProviderTypeId + +/** @internal */ +export const configProviderTag: Context.Tag = Context + .GenericTag( + "effect/ConfigProvider" + ) + +/** @internal */ +const FlatConfigProviderSymbolKey = "effect/ConfigProviderFlat" + +/** @internal */ +export const FlatConfigProviderTypeId: ConfigProvider.FlatConfigProviderTypeId = Symbol.for( + FlatConfigProviderSymbolKey +) as ConfigProvider.FlatConfigProviderTypeId + +/** @internal */ +export const make = ( + options: { + readonly load: (config: Config.Config) => Effect.Effect + readonly flattened: ConfigProvider.ConfigProvider.Flat + } +): ConfigProvider.ConfigProvider => ({ + [ConfigProviderTypeId]: ConfigProviderTypeId, + pipe() { + return pipeArguments(this, arguments) + }, + ...options +}) + +/** @internal */ +export const makeFlat = ( + options: { + readonly load: ( + path: ReadonlyArray, + config: Config.Config.Primitive, + split: boolean + ) => Effect.Effect, ConfigError.ConfigError> + readonly enumerateChildren: ( + path: ReadonlyArray + ) => Effect.Effect, ConfigError.ConfigError> + readonly patch: PathPatch.PathPatch + } +): ConfigProvider.ConfigProvider.Flat => ({ + [FlatConfigProviderTypeId]: FlatConfigProviderTypeId, + patch: options.patch, + load: (path, config, split = true) => options.load(path, config, split), + enumerateChildren: options.enumerateChildren +}) + +/** @internal */ +export const fromFlat = (flat: ConfigProvider.ConfigProvider.Flat): ConfigProvider.ConfigProvider => + make({ + load: (config) => + core.flatMap(fromFlatLoop(flat, Arr.empty(), config, false), (chunk) => + Option.match(Arr.head(chunk), { + onNone: () => + core.fail( + configError.MissingData( + Arr.empty(), + `Expected a single value having structure: ${config}` + ) + ), + onSome: core.succeed + })), + flattened: flat + }) + +/** @internal */ +export const fromEnv = ( + options?: Partial +): ConfigProvider.ConfigProvider => { + const { pathDelim, seqDelim } = Object.assign({}, { pathDelim: "_", seqDelim: "," }, options) + const makePathString = (path: ReadonlyArray): string => pipe(path, Arr.join(pathDelim)) + const unmakePathString = (pathString: string): ReadonlyArray => pathString.split(pathDelim) + + const getEnv = () => + typeof process !== "undefined" && "env" in process && typeof process.env === "object" ? process.env : {} + + const load = ( + path: ReadonlyArray, + primitive: Config.Config.Primitive, + split = true + ): Effect.Effect, ConfigError.ConfigError> => { + const pathString = makePathString(path) + const current = getEnv() + const valueOpt = pathString in current ? Option.some(current[pathString]!) : Option.none() + return pipe( + valueOpt, + core.mapError(() => configError.MissingData(path, `Expected ${pathString} to exist in the process context`)), + core.flatMap((value) => parsePrimitive(value, path, primitive, seqDelim, split)) + ) + } + + const enumerateChildren = ( + path: ReadonlyArray + ): Effect.Effect, ConfigError.ConfigError> => + core.sync(() => { + const current = getEnv() + const keys = Object.keys(current) + const keyPaths = keys.map((value) => unmakePathString(value.toUpperCase())) + const filteredKeyPaths = keyPaths.filter((keyPath) => { + for (let i = 0; i < path.length; i++) { + const pathComponent = pipe(path, Arr.unsafeGet(i)) + const currentElement = keyPath[i] + if (currentElement === undefined || pathComponent !== currentElement) { + return false + } + } + return true + }).flatMap((keyPath) => keyPath.slice(path.length, path.length + 1)) + return HashSet.fromIterable(filteredKeyPaths) + }) + + return fromFlat(makeFlat({ load, enumerateChildren, patch: pathPatch.empty })) +} + +/** @internal */ +export const fromMap = ( + map: Map, + config?: Partial +): ConfigProvider.ConfigProvider => { + const { pathDelim, seqDelim } = Object.assign({ seqDelim: ",", pathDelim: "." }, config) + const makePathString = (path: ReadonlyArray): string => pipe(path, Arr.join(pathDelim)) + const unmakePathString = (pathString: string): ReadonlyArray => pathString.split(pathDelim) + const mapWithIndexSplit = splitIndexInKeys( + map, + (str) => unmakePathString(str), + makePathString + ) + const load = ( + path: ReadonlyArray, + primitive: Config.Config.Primitive, + split = true + ): Effect.Effect, ConfigError.ConfigError> => { + const pathString = makePathString(path) + const valueOpt = mapWithIndexSplit.has(pathString) ? + Option.some(mapWithIndexSplit.get(pathString)!) : + Option.none() + return pipe( + valueOpt, + core.mapError(() => configError.MissingData(path, `Expected ${pathString} to exist in the provided map`)), + core.flatMap((value) => parsePrimitive(value, path, primitive, seqDelim, split)) + ) + } + const enumerateChildren = ( + path: ReadonlyArray + ): Effect.Effect, ConfigError.ConfigError> => + core.sync(() => { + const keyPaths = Arr.fromIterable(mapWithIndexSplit.keys()).map(unmakePathString) + const filteredKeyPaths = keyPaths.filter((keyPath) => { + for (let i = 0; i < path.length; i++) { + const pathComponent = pipe(path, Arr.unsafeGet(i)) + const currentElement = keyPath[i] + if (currentElement === undefined || pathComponent !== currentElement) { + return false + } + } + return true + }).flatMap((keyPath) => keyPath.slice(path.length, path.length + 1)) + return HashSet.fromIterable(filteredKeyPaths) + }) + + return fromFlat(makeFlat({ load, enumerateChildren, patch: pathPatch.empty })) +} + +const extend = ( + leftDef: (n: number) => A, + rightDef: (n: number) => B, + left: ReadonlyArray, + right: ReadonlyArray +): [ReadonlyArray, ReadonlyArray] => { + const leftPad = Arr.unfold( + left.length, + (index) => + index >= right.length ? + Option.none() : + Option.some([leftDef(index), index + 1]) + ) + const rightPad = Arr.unfold( + right.length, + (index) => + index >= left.length ? + Option.none() : + Option.some([rightDef(index), index + 1]) + ) + const leftExtension = concat(left, leftPad) + const rightExtension = concat(right, rightPad) + return [leftExtension, rightExtension] +} + +const appendConfigPath = (path: ReadonlyArray, config: Config.Config): ReadonlyArray => { + let op = config as _config.ConfigPrimitive + if (op._tag === "Nested") { + const out = path.slice() + while (op._tag === "Nested") { + out.push(op.name) + op = op.config as _config.ConfigPrimitive + } + return out + } + return path +} + +const fromFlatLoop = ( + flat: ConfigProvider.ConfigProvider.Flat, + prefix: ReadonlyArray, + config: Config.Config, + split: boolean +): Effect.Effect, ConfigError.ConfigError> => { + const op = config as _config.ConfigPrimitive + switch (op._tag) { + case OpCodes.OP_CONSTANT: { + return core.succeed(Arr.of(op.value)) as Effect.Effect, ConfigError.ConfigError> + } + case OpCodes.OP_DESCRIBED: { + return core.suspend( + () => fromFlatLoop(flat, prefix, op.config, split) + ) as unknown as Effect.Effect, ConfigError.ConfigError> + } + case OpCodes.OP_FAIL: { + return core.fail(configError.MissingData(prefix, op.message)) as Effect.Effect< + Array, + ConfigError.ConfigError + > + } + case OpCodes.OP_FALLBACK: { + return pipe( + core.suspend(() => fromFlatLoop(flat, prefix, op.first, split)), + core.catchAll((error1) => { + if (op.condition(error1)) { + return pipe( + fromFlatLoop(flat, prefix, op.second, split), + core.catchAll((error2) => core.fail(configError.Or(error1, error2))) + ) + } + return core.fail(error1) + }) + ) as unknown as Effect.Effect, ConfigError.ConfigError> + } + case OpCodes.OP_LAZY: { + return core.suspend(() => fromFlatLoop(flat, prefix, op.config(), split)) as Effect.Effect< + Array, + ConfigError.ConfigError + > + } + case OpCodes.OP_MAP_OR_FAIL: { + return core.suspend(() => + pipe( + fromFlatLoop(flat, prefix, op.original, split), + core.flatMap( + core.forEachSequential((a) => + pipe( + op.mapOrFail(a), + core.mapError(configError.prefixed(appendConfigPath(prefix, op.original))) + ) + ) + ) + ) + ) as unknown as Effect.Effect, ConfigError.ConfigError> + } + case OpCodes.OP_NESTED: { + return core.suspend(() => + fromFlatLoop( + flat, + concat(prefix, Arr.of(op.name)), + op.config, + split + ) + ) as unknown as Effect.Effect, ConfigError.ConfigError> + } + case OpCodes.OP_PRIMITIVE: { + return pipe( + pathPatch.patch(prefix, flat.patch), + core.flatMap((prefix) => + pipe( + flat.load(prefix, op, split), + core.flatMap((values) => { + if (values.length === 0) { + const name = pipe(Arr.last(prefix), Option.getOrElse(() => "")) + return core.fail(configError.MissingData([], `Expected ${op.description} with name ${name}`)) + } + return core.succeed(values) + }) + ) + ) + ) as unknown as Effect.Effect, ConfigError.ConfigError> + } + case OpCodes.OP_SEQUENCE: { + return pipe( + pathPatch.patch(prefix, flat.patch), + core.flatMap((patchedPrefix) => + pipe( + flat.enumerateChildren(patchedPrefix), + core.flatMap(indicesFrom), + core.flatMap((indices) => { + if (indices.length === 0) { + return core.suspend(() => + core.map(fromFlatLoop(flat, prefix, op.config, true), Arr.of) + ) as unknown as Effect.Effect, ConfigError.ConfigError> + } + return pipe( + core.forEachSequential( + indices, + (index) => fromFlatLoop(flat, Arr.append(prefix, `[${index}]`), op.config, true) + ), + core.map((chunkChunk) => { + const flattened = Arr.flatten(chunkChunk) + if (flattened.length === 0) { + return Arr.of(Arr.empty()) + } + return Arr.of(flattened) + }) + ) as unknown as Effect.Effect, ConfigError.ConfigError> + }) + ) + ) + ) + } + case OpCodes.OP_HASHMAP: { + return core.suspend(() => + pipe( + pathPatch.patch(prefix, flat.patch), + core.flatMap((prefix) => + pipe( + flat.enumerateChildren(prefix), + core.flatMap((keys) => { + return pipe( + keys, + core.forEachSequential((key) => + fromFlatLoop( + flat, + concat(prefix, Arr.of(key)), + op.valueConfig, + split + ) + ), + core.map((matrix) => { + if (matrix.length === 0) { + return Arr.of(HashMap.empty()) + } + return pipe( + transpose(matrix), + Arr.map((values) => HashMap.fromIterable(Arr.zip(Arr.fromIterable(keys), values))) + ) + }) + ) + }) + ) + ) + ) + ) as unknown as Effect.Effect, ConfigError.ConfigError> + } + case OpCodes.OP_ZIP_WITH: { + return core.suspend(() => + pipe( + fromFlatLoop(flat, prefix, op.left, split), + core.either, + core.flatMap((left) => + pipe( + fromFlatLoop(flat, prefix, op.right, split), + core.either, + core.flatMap((right) => { + if (Either.isLeft(left) && Either.isLeft(right)) { + return core.fail(configError.And(left.left, right.left)) + } + if (Either.isLeft(left) && Either.isRight(right)) { + return core.fail(left.left) + } + if (Either.isRight(left) && Either.isLeft(right)) { + return core.fail(right.left) + } + if (Either.isRight(left) && Either.isRight(right)) { + const path = pipe(prefix, Arr.join(".")) + const fail = fromFlatLoopFail(prefix, path) + const [lefts, rights] = extend( + fail, + fail, + pipe(left.right, Arr.map(Either.right)), + pipe(right.right, Arr.map(Either.right)) + ) + return pipe( + lefts, + Arr.zip(rights), + core.forEachSequential(([left, right]) => + pipe( + core.zip(left, right), + core.map(([left, right]) => op.zip(left, right)) + ) + ) + ) + } + throw new Error( + "BUG: ConfigProvider.fromFlatLoop - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + }) + ) + ) + ) + ) as unknown as Effect.Effect, ConfigError.ConfigError> + } + } +} + +const fromFlatLoopFail = + (prefix: ReadonlyArray, path: string) => (index: number): Either.Either => + Either.left( + configError.MissingData( + prefix, + `The element at index ${index} in a sequence at path "${path}" was missing` + ) + ) + +/** @internal */ +export const mapInputPath = dual< + (f: (path: string) => string) => (self: ConfigProvider.ConfigProvider) => ConfigProvider.ConfigProvider, + (self: ConfigProvider.ConfigProvider, f: (path: string) => string) => ConfigProvider.ConfigProvider +>(2, (self, f) => fromFlat(mapInputPathFlat(self.flattened, f))) + +const mapInputPathFlat = ( + self: ConfigProvider.ConfigProvider.Flat, + f: (path: string) => string +): ConfigProvider.ConfigProvider.Flat => + makeFlat({ + load: (path, config, split = true) => self.load(path, config, split), + enumerateChildren: (path) => self.enumerateChildren(path), + patch: pathPatch.mapName(self.patch, f) + }) + +/** @internal */ +export const nested = dual< + (name: string) => (self: ConfigProvider.ConfigProvider) => ConfigProvider.ConfigProvider, + (self: ConfigProvider.ConfigProvider, name: string) => ConfigProvider.ConfigProvider +>(2, (self, name) => + fromFlat(makeFlat({ + load: (path, config) => self.flattened.load(path, config, true), + enumerateChildren: (path) => self.flattened.enumerateChildren(path), + patch: pathPatch.nested(self.flattened.patch, name) + }))) + +/** @internal */ +export const unnested = dual< + (name: string) => (self: ConfigProvider.ConfigProvider) => ConfigProvider.ConfigProvider, + (self: ConfigProvider.ConfigProvider, name: string) => ConfigProvider.ConfigProvider +>(2, (self, name) => + fromFlat(makeFlat({ + load: (path, config) => self.flattened.load(path, config, true), + enumerateChildren: (path) => self.flattened.enumerateChildren(path), + patch: pathPatch.unnested(self.flattened.patch, name) + }))) + +/** @internal */ +export const orElse = dual< + ( + that: LazyArg + ) => ( + self: ConfigProvider.ConfigProvider + ) => ConfigProvider.ConfigProvider, + ( + self: ConfigProvider.ConfigProvider, + that: LazyArg + ) => ConfigProvider.ConfigProvider +>(2, (self, that) => fromFlat(orElseFlat(self.flattened, () => that().flattened))) + +const orElseFlat = ( + self: ConfigProvider.ConfigProvider.Flat, + that: LazyArg +): ConfigProvider.ConfigProvider.Flat => + makeFlat({ + load: (path, config, split) => + pipe( + pathPatch.patch(path, self.patch), + core.flatMap((patch) => self.load(patch, config, split)), + core.catchAll((error1) => + pipe( + core.sync(that), + core.flatMap((that) => + pipe( + pathPatch.patch(path, that.patch), + core.flatMap((patch) => that.load(patch, config, split)), + core.catchAll((error2) => core.fail(configError.Or(error1, error2))) + ) + ) + ) + ) + ), + enumerateChildren: (path) => + pipe( + pathPatch.patch(path, self.patch), + core.flatMap((patch) => self.enumerateChildren(patch)), + core.either, + core.flatMap((left) => + pipe( + core.sync(that), + core.flatMap((that) => + pipe( + pathPatch.patch(path, that.patch), + core.flatMap((patch) => that.enumerateChildren(patch)), + core.either, + core.flatMap((right) => { + if (Either.isLeft(left) && Either.isLeft(right)) { + return core.fail(configError.And(left.left, right.left)) + } + if (Either.isLeft(left) && Either.isRight(right)) { + return core.succeed(right.right) + } + if (Either.isRight(left) && Either.isLeft(right)) { + return core.succeed(left.right) + } + if (Either.isRight(left) && Either.isRight(right)) { + return core.succeed(pipe(left.right, HashSet.union(right.right))) + } + throw new Error( + "BUG: ConfigProvider.orElseFlat - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + }) + ) + ) + ) + ) + ), + patch: pathPatch.empty + }) + +/** @internal */ +export const constantCase = (self: ConfigProvider.ConfigProvider): ConfigProvider.ConfigProvider => + mapInputPath(self, StringUtils.constantCase) + +/** @internal */ +export const kebabCase = (self: ConfigProvider.ConfigProvider): ConfigProvider.ConfigProvider => + mapInputPath(self, StringUtils.kebabCase) + +/** @internal */ +export const lowerCase = (self: ConfigProvider.ConfigProvider): ConfigProvider.ConfigProvider => + mapInputPath(self, StringUtils.lowerCase) + +/** @internal */ +export const snakeCase = (self: ConfigProvider.ConfigProvider): ConfigProvider.ConfigProvider => + mapInputPath(self, StringUtils.snakeCase) + +/** @internal */ +export const upperCase = (self: ConfigProvider.ConfigProvider): ConfigProvider.ConfigProvider => + mapInputPath(self, StringUtils.upperCase) + +/** @internal */ +export const within = dual< + ( + path: ReadonlyArray, + f: (self: ConfigProvider.ConfigProvider) => ConfigProvider.ConfigProvider + ) => (self: ConfigProvider.ConfigProvider) => ConfigProvider.ConfigProvider, + ( + self: ConfigProvider.ConfigProvider, + path: ReadonlyArray, + f: (self: ConfigProvider.ConfigProvider) => ConfigProvider.ConfigProvider + ) => ConfigProvider.ConfigProvider +>(3, (self, path, f) => { + const unnest = Arr.reduce(path, self, (provider, name) => unnested(provider, name)) + const nest = Arr.reduceRight(path, f(unnest), (provider, name) => nested(provider, name)) + return orElse(nest, () => self) +}) + +const splitPathString = (text: string, delim: string): Array => { + const split = text.split(new RegExp(`\\s*${regexp.escape(delim)}\\s*`)) + return split +} + +const parsePrimitive = ( + text: string, + path: ReadonlyArray, + primitive: Config.Config.Primitive, + delimiter: string, + split: boolean +): Effect.Effect, ConfigError.ConfigError> => { + if (!split) { + return pipe( + primitive.parse(text), + core.mapBoth({ + onFailure: configError.prefixed(path), + onSuccess: Arr.of + }) + ) + } + return pipe( + splitPathString(text, delimiter), + core.forEachSequential((char) => primitive.parse(char.trim())), + core.mapError(configError.prefixed(path)) + ) +} + +const transpose = (array: ReadonlyArray>): Array> => { + return Object.keys(array[0]).map((column) => array.map((row) => row[column as any])) +} + +const indicesFrom = (quotedIndices: HashSet.HashSet): Effect.Effect> => + pipe( + core.forEachSequential(quotedIndices, parseQuotedIndex), + core.mapBoth({ + onFailure: () => Arr.empty(), + onSuccess: Arr.sort(number.Order) + }), + core.either, + core.map(Either.merge) + ) + +const STR_INDEX_REGEX = /(^.+)(\[(\d+)\])$/ +const QUOTED_INDEX_REGEX = /^(\[(\d+)\])$/ + +const parseQuotedIndex = (str: string): Option.Option => { + const match = str.match(QUOTED_INDEX_REGEX) + if (match !== null) { + const matchedIndex = match[2] + return pipe( + matchedIndex !== undefined && matchedIndex.length > 0 ? + Option.some(matchedIndex) : + Option.none(), + Option.flatMap(parseInteger) + ) + } + return Option.none() +} + +const splitIndexInKeys = ( + map: Map, + unmakePathString: (str: string) => ReadonlyArray, + makePathString: (chunk: ReadonlyArray) => string +): Map => { + const newMap: Map = new Map() + for (const [pathString, value] of map) { + const keyWithIndex = pipe( + unmakePathString(pathString), + Arr.flatMap((key) => + Option.match(splitIndexFrom(key), { + onNone: () => Arr.of(key), + onSome: ([key, index]) => Arr.make(key, `[${index}]`) + }) + ) + ) + newMap.set(makePathString(keyWithIndex), value) + } + return newMap +} + +const splitIndexFrom = (key: string): Option.Option<[string, number]> => { + const match = key.match(STR_INDEX_REGEX) + if (match !== null) { + const matchedString = match[1] + const matchedIndex = match[3] + const optionalString = matchedString !== undefined && matchedString.length > 0 ? + Option.some(matchedString) : + Option.none() + const optionalIndex = pipe( + matchedIndex !== undefined && matchedIndex.length > 0 ? + Option.some(matchedIndex) : + Option.none(), + Option.flatMap(parseInteger) + ) + return Option.all([optionalString, optionalIndex]) + } + return Option.none() +} + +const parseInteger = (str: string): Option.Option => { + const parsedIndex = Number.parseInt(str) + return Number.isNaN(parsedIndex) ? + Option.none() : + Option.some(parsedIndex) +} + +const keyName = (name: string): KeyComponent => ({ + _tag: "KeyName", + name +}) + +const keyIndex = (index: number): KeyComponent => ({ + _tag: "KeyIndex", + index +}) + +interface JsonMap { + [member: string]: string | number | boolean | null | JsonArray | JsonMap +} +interface JsonArray extends Array {} + +/** @internal */ +export const fromJson = (json: unknown): ConfigProvider.ConfigProvider => { + const hiddenDelimiter = "\ufeff" + const indexedEntries = Arr.map( + getIndexedEntries(json as JsonMap), + ([key, value]): [string, string] => [configPathToString(key).join(hiddenDelimiter), value] + ) + return fromMap(new Map(indexedEntries), { + pathDelim: hiddenDelimiter, + seqDelim: hiddenDelimiter + }) +} + +const configPathToString = (path: ReadonlyArray): ReadonlyArray => { + const output: Array = [] + let i = 0 + while (i < path.length) { + const component = path[i] + if (component._tag === "KeyName") { + if (i + 1 < path.length) { + const nextComponent = path[i + 1] + if (nextComponent._tag === "KeyIndex") { + output.push(`${component.name}[${nextComponent.index}]`) + i += 2 + } else { + output.push(component.name) + i += 1 + } + } else { + output.push(component.name) + i += 1 + } + } + } + return output +} + +const getIndexedEntries = ( + config: JsonMap +): ReadonlyArray<[path: ReadonlyArray, value: string]> => { + const loopAny = ( + path: ReadonlyArray, + value: string | number | boolean | JsonMap | JsonArray | null + ): ReadonlyArray<[path: ReadonlyArray, value: string]> => { + if (typeof value === "string") { + return Arr.make([path, value] as [ReadonlyArray, string]) + } + if (typeof value === "number" || typeof value === "boolean") { + return Arr.make([path, String(value)] as [ReadonlyArray, string]) + } + if (Arr.isArray(value)) { + return loopArray(path, value) + } + if (typeof value === "object" && value !== null) { + return loopObject(path, value) + } + return Arr.empty<[ReadonlyArray, string]>() + } + const loopArray = ( + path: ReadonlyArray, + values: JsonArray + ): ReadonlyArray<[path: ReadonlyArray, value: string]> => + Arr.match(values, { + onEmpty: () => Arr.make([path, ""] as [ReadonlyArray, string]), + onNonEmpty: Arr.flatMap((value, index) => loopAny(Arr.append(path, keyIndex(index)), value)) + }) + const loopObject = ( + path: ReadonlyArray, + value: JsonMap + ): ReadonlyArray<[path: ReadonlyArray, value: string]> => + Object.entries(value) + .filter(([, value]) => Predicate.isNotNullable(value)) + .flatMap(([key, value]) => { + const newPath = Arr.append(path, keyName(key)) + const result = loopAny(newPath, value) + if (Arr.isEmptyReadonlyArray(result)) { + return Arr.make([newPath, ""] as [ReadonlyArray, string]) + } + return result + }) + return loopObject(Arr.empty(), config) +} diff --git a/backend/node_modules/effect/src/internal/configProvider/pathPatch.ts b/backend/node_modules/effect/src/internal/configProvider/pathPatch.ts new file mode 100644 index 0000000000000000000000000000000000000000..f4f463a5a712cc7ece144004b3dc71e5883e4e16 --- /dev/null +++ b/backend/node_modules/effect/src/internal/configProvider/pathPatch.ts @@ -0,0 +1,97 @@ +import * as RA from "../../Array.js" +import type * as ConfigError from "../../ConfigError.js" +import type * as PathPatch from "../../ConfigProviderPathPatch.js" +import * as Either from "../../Either.js" +import { dual, pipe } from "../../Function.js" +import * as List from "../../List.js" +import * as Option from "../../Option.js" +import * as configError from "../configError.js" + +/** @internal */ +export const empty: PathPatch.PathPatch = { + _tag: "Empty" +} + +/** @internal */ +export const andThen = dual< + (that: PathPatch.PathPatch) => (self: PathPatch.PathPatch) => PathPatch.PathPatch, + (self: PathPatch.PathPatch, that: PathPatch.PathPatch) => PathPatch.PathPatch +>(2, (self, that) => ({ + _tag: "AndThen", + first: self, + second: that +})) + +/** @internal */ +export const mapName = dual< + (f: (string: string) => string) => (self: PathPatch.PathPatch) => PathPatch.PathPatch, + (self: PathPatch.PathPatch, f: (string: string) => string) => PathPatch.PathPatch +>(2, (self, f) => andThen(self, { _tag: "MapName", f })) + +/** @internal */ +export const nested = dual< + (name: string) => (self: PathPatch.PathPatch) => PathPatch.PathPatch, + (self: PathPatch.PathPatch, name: string) => PathPatch.PathPatch +>(2, (self, name) => andThen(self, { _tag: "Nested", name })) + +/** @internal */ +export const unnested = dual< + (name: string) => (self: PathPatch.PathPatch) => PathPatch.PathPatch, + (self: PathPatch.PathPatch, name: string) => PathPatch.PathPatch +>(2, (self, name) => andThen(self, { _tag: "Unnested", name })) + +/** @internal */ +export const patch = dual< + ( + patch: PathPatch.PathPatch + ) => ( + path: ReadonlyArray + ) => Either.Either, ConfigError.ConfigError>, + ( + path: ReadonlyArray, + patch: PathPatch.PathPatch + ) => Either.Either, ConfigError.ConfigError> +>(2, (path, patch) => { + let input: List.List = List.of(patch) + let output: ReadonlyArray = path + while (List.isCons(input)) { + const patch: PathPatch.PathPatch = input.head + switch (patch._tag) { + case "Empty": { + input = input.tail + break + } + case "AndThen": { + input = List.cons(patch.first, List.cons(patch.second, input.tail)) + break + } + case "MapName": { + output = RA.map(output, patch.f) + input = input.tail + break + } + case "Nested": { + output = RA.prepend(output, patch.name) + input = input.tail + break + } + case "Unnested": { + const containsName = pipe( + RA.head(output), + Option.contains(patch.name) + ) + if (containsName) { + output = RA.tailNonEmpty(output as RA.NonEmptyArray) + input = input.tail + } else { + return Either.left(configError.MissingData( + output, + `Expected ${patch.name} to be in path in ConfigProvider#unnested` + )) + } + break + } + } + } + return Either.right(output) +}) diff --git a/backend/node_modules/effect/src/internal/console.ts b/backend/node_modules/effect/src/internal/console.ts new file mode 100644 index 0000000000000000000000000000000000000000..402e26342a0d6061028a010593e3533c214cc6a1 --- /dev/null +++ b/backend/node_modules/effect/src/internal/console.ts @@ -0,0 +1,153 @@ +import type * as Console from "../Console.js" +import * as Context from "../Context.js" +import type * as Effect from "../Effect.js" +import { dual } from "../Function.js" +import type * as Layer from "../Layer.js" +import type * as Scope from "../Scope.js" +import * as core from "./core.js" +import * as defaultServices from "./defaultServices.js" +import * as defaultConsole from "./defaultServices/console.js" +import * as fiberRuntime from "./fiberRuntime.js" +import * as layer from "./layer.js" + +/** @internal */ +export const console: Effect.Effect = core.map( + core.fiberRefGet(defaultServices.currentServices), + Context.get(defaultConsole.consoleTag) +) + +/** @internal */ +export const consoleWith = (f: (console: Console.Console) => Effect.Effect) => + core.fiberRefGetWith( + defaultServices.currentServices, + (services) => f(Context.get(services, defaultConsole.consoleTag)) + ) + +/** @internal */ +export const withConsole = dual< + (console: C) => (effect: Effect.Effect) => Effect.Effect, + (effect: Effect.Effect, console: C) => Effect.Effect +>(2, (effect, value) => + core.fiberRefLocallyWith( + effect, + defaultServices.currentServices, + Context.add(defaultConsole.consoleTag, value) + )) + +/** @internal */ +export const withConsoleScoped = (console: A): Effect.Effect => + fiberRuntime.fiberRefLocallyScopedWith( + defaultServices.currentServices, + Context.add(defaultConsole.consoleTag, console) + ) + +/** @internal */ +export const setConsole = (console: A): Layer.Layer => + layer.scopedDiscard( + fiberRuntime.fiberRefLocallyScopedWith( + defaultServices.currentServices, + Context.add(defaultConsole.consoleTag, console) + ) + ) + +/** @internal */ +export const assert = (condition: boolean, ...args: ReadonlyArray) => + consoleWith((_) => _.assert(condition, ...args)) + +/** @internal */ +export const clear = consoleWith((_) => _.clear) + +/** @internal */ +export const count = (label?: string) => consoleWith((_) => _.count(label)) + +/** @internal */ +export const countReset = (label?: string) => consoleWith((_) => _.countReset(label)) + +/** @internal */ +export const debug = (...args: ReadonlyArray) => consoleWith((_) => _.debug(...args)) + +/** @internal */ +export const dir = (item: any, options?: any) => consoleWith((_) => _.dir(item, options)) + +/** @internal */ +export const dirxml = (...args: ReadonlyArray) => consoleWith((_) => _.dirxml(...args)) + +/** @internal */ +export const error = (...args: ReadonlyArray) => consoleWith((_) => _.error(...args)) + +/** @internal */ +export const group = (options?: { + label?: string | undefined + collapsed?: boolean | undefined +}) => + consoleWith((_) => + fiberRuntime.acquireRelease( + _.group(options), + () => _.groupEnd + ) + ) + +/** @internal */ +export const info = (...args: ReadonlyArray) => consoleWith((_) => _.info(...args)) + +/** @internal */ +export const log = (...args: ReadonlyArray) => consoleWith((_) => _.log(...args)) + +/** @internal */ +export const table = (tabularData: any, properties?: ReadonlyArray) => + consoleWith((_) => _.table(tabularData, properties)) + +/** @internal */ +export const time = (label?: string) => + consoleWith((_) => + fiberRuntime.acquireRelease( + _.time(label), + () => _.timeEnd(label) + ) + ) + +/** @internal */ +export const timeLog = (label?: string, ...args: ReadonlyArray) => consoleWith((_) => _.timeLog(label, ...args)) + +/** @internal */ +export const trace = (...args: ReadonlyArray) => consoleWith((_) => _.trace(...args)) + +/** @internal */ +export const warn = (...args: ReadonlyArray) => consoleWith((_) => _.warn(...args)) + +/** @internal */ +export const withGroup = dual< + ( + options?: { + readonly label?: string | undefined + readonly collapsed?: boolean | undefined + } + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + options?: { + readonly label?: string | undefined + readonly collapsed?: boolean | undefined + } + ) => Effect.Effect +>((args) => core.isEffect(args[0]), (self, options) => + consoleWith((_) => + core.acquireUseRelease( + _.group(options), + () => self, + () => _.groupEnd + ) + )) + +/** @internal */ +export const withTime = dual< + (label?: string) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, label?: string) => Effect.Effect +>((args) => core.isEffect(args[0]), (self, label) => + consoleWith((_) => + core.acquireUseRelease( + _.time(label), + () => self, + () => _.timeEnd(label) + ) + )) diff --git a/backend/node_modules/effect/src/internal/context.ts b/backend/node_modules/effect/src/internal/context.ts new file mode 100644 index 0000000000000000000000000000000000000000..e77c1cd6634d3d53c599cfefa65dea9f7ee558d8 --- /dev/null +++ b/backend/node_modules/effect/src/internal/context.ts @@ -0,0 +1,337 @@ +import type * as C from "../Context.js" +import * as Equal from "../Equal.js" +import type { LazyArg } from "../Function.js" +import { dual } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import * as Hash from "../Hash.js" +import { format, NodeInspectSymbol, toJSON } from "../Inspectable.js" +import type * as O from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty } from "../Predicate.js" +import type * as STM from "../STM.js" +import type { NoInfer } from "../Types.js" +import { EffectPrototype, effectVariance } from "./effectable.js" +import * as option from "./option.js" + +/** @internal */ +export const TagTypeId: C.TagTypeId = Symbol.for("effect/Context/Tag") as C.TagTypeId + +/** @internal */ +export const ReferenceTypeId: C.ReferenceTypeId = Symbol.for("effect/Context/Reference") as C.ReferenceTypeId + +/** @internal */ +const STMSymbolKey = "effect/STM" + +/** @internal */ +export const STMTypeId: STM.STMTypeId = Symbol.for( + STMSymbolKey +) as STM.STMTypeId + +/** @internal */ +export const TagProto: any = { + ...EffectPrototype, + _op: "Tag", + [STMTypeId]: effectVariance, + [TagTypeId]: { + _Service: (_: unknown) => _, + _Identifier: (_: unknown) => _ + }, + toString() { + return format(this.toJSON()) + }, + toJSON(this: C.Tag) { + return { + _id: "Tag", + key: this.key, + stack: this.stack + } + }, + [NodeInspectSymbol]() { + return this.toJSON() + }, + of(self: Service): Service { + return self + }, + context( + this: C.Tag, + self: Service + ): C.Context { + return make(this, self) + } +} + +export const ReferenceProto: any = { + ...TagProto, + [ReferenceTypeId]: ReferenceTypeId +} + +/** @internal */ +export const makeGenericTag = (key: string): C.Tag => { + const limit = Error.stackTraceLimit + Error.stackTraceLimit = 2 + const creationError = new Error() + Error.stackTraceLimit = limit + const tag = Object.create(TagProto) + Object.defineProperty(tag, "stack", { + get() { + return creationError.stack + } + }) + tag.key = key + return tag +} + +/** @internal */ +export const Tag = (id: Id) => (): C.TagClass => { + const limit = Error.stackTraceLimit + Error.stackTraceLimit = 2 + const creationError = new Error() + Error.stackTraceLimit = limit + + function TagClass() {} + Object.setPrototypeOf(TagClass, TagProto) + TagClass.key = id + Object.defineProperty(TagClass, "stack", { + get() { + return creationError.stack + } + }) + return TagClass as any +} + +/** @internal */ +export const Reference = () => +(id: Id, options: { + readonly defaultValue: () => Service +}): C.ReferenceClass => { + const limit = Error.stackTraceLimit + Error.stackTraceLimit = 2 + const creationError = new Error() + Error.stackTraceLimit = limit + + function ReferenceClass() {} + Object.setPrototypeOf(ReferenceClass, ReferenceProto) + ReferenceClass.key = id + ReferenceClass.defaultValue = options.defaultValue + Object.defineProperty(ReferenceClass, "stack", { + get() { + return creationError.stack + } + }) + return ReferenceClass as any +} + +/** @internal */ +export const TypeId: C.TypeId = Symbol.for("effect/Context") as C.TypeId + +/** @internal */ +export const ContextProto: Omit, "unsafeMap"> = { + [TypeId]: { + _Services: (_: unknown) => _ + }, + [Equal.symbol](this: C.Context, that: unknown): boolean { + if (isContext(that)) { + if (this.unsafeMap.size === that.unsafeMap.size) { + for (const k of this.unsafeMap.keys()) { + if (!that.unsafeMap.has(k) || !Equal.equals(this.unsafeMap.get(k), that.unsafeMap.get(k))) { + return false + } + } + return true + } + } + return false + }, + [Hash.symbol](this: C.Context): number { + return Hash.cached(this, Hash.number(this.unsafeMap.size)) + }, + pipe(this: C.Context) { + return pipeArguments(this, arguments) + }, + toString() { + return format(this.toJSON()) + }, + toJSON(this: C.Context) { + return { + _id: "Context", + services: Array.from(this.unsafeMap).map(toJSON) + } + }, + [NodeInspectSymbol]() { + return (this as any).toJSON() + } +} + +/** @internal */ +export const makeContext = (unsafeMap: Map): C.Context => { + const context = Object.create(ContextProto) + context.unsafeMap = unsafeMap + return context +} + +const serviceNotFoundError = (tag: C.Tag) => { + const error = new Error(`Service not found${tag.key ? `: ${String(tag.key)}` : ""}`) + if (tag.stack) { + const lines = tag.stack.split("\n") + if (lines.length > 2) { + const afterAt = lines[2].match(/at (.*)/) + if (afterAt) { + error.message = error.message + ` (defined at ${afterAt[1]})` + } + } + } + if (error.stack) { + const lines = error.stack.split("\n") + lines.splice(1, 3) + error.stack = lines.join("\n") + } + return error +} + +/** @internal */ +export const isContext = (u: unknown): u is C.Context => hasProperty(u, TypeId) + +/** @internal */ +export const isTag = (u: unknown): u is C.Tag => hasProperty(u, TagTypeId) + +/** @internal */ +export const isReference = (u: unknown): u is C.Reference => hasProperty(u, ReferenceTypeId) + +const _empty = makeContext(new Map()) + +/** @internal */ +export const empty = (): C.Context => _empty + +/** @internal */ +export const make = (tag: C.Tag, service: NoInfer): C.Context => + makeContext(new Map([[tag.key, service]])) + +/** @internal */ +export const add = dual< + ( + tag: C.Tag, + service: NoInfer + ) => ( + self: C.Context + ) => C.Context, + ( + self: C.Context, + tag: C.Tag, + service: NoInfer + ) => C.Context +>(3, (self, tag, service) => { + const map = new Map(self.unsafeMap) + map.set(tag.key, service) + return makeContext(map) +}) + +const defaultValueCache = globalValue("effect/Context/defaultValueCache", () => new Map()) +const getDefaultValue = (tag: C.Reference) => { + if (defaultValueCache.has(tag.key)) { + return defaultValueCache.get(tag.key) + } + const value = tag.defaultValue() + defaultValueCache.set(tag.key, value) + return value +} + +/** @internal */ +export const unsafeGetReference = (self: C.Context, tag: C.Reference): S => { + return self.unsafeMap.has(tag.key) ? self.unsafeMap.get(tag.key) : getDefaultValue(tag) +} + +/** @internal */ +export const unsafeGet = dual< + (tag: C.Tag) => (self: C.Context) => S, + (self: C.Context, tag: C.Tag) => S +>(2, (self, tag) => { + if (!self.unsafeMap.has(tag.key)) { + if (ReferenceTypeId in tag) return getDefaultValue(tag as any) + throw serviceNotFoundError(tag) + } + return self.unsafeMap.get(tag.key)! as any +}) + +/** @internal */ +export const get: { + (tag: C.Reference): (self: C.Context) => S + (tag: C.Tag): (self: C.Context) => S + (self: C.Context, tag: C.Reference): S + (self: C.Context, tag: C.Tag): S +} = unsafeGet as any + +/** @internal */ +export const getOrElse = dual< + (tag: C.Tag, orElse: LazyArg) => (self: C.Context) => S | B, + (self: C.Context, tag: C.Tag, orElse: LazyArg) => S | B +>(3, (self, tag, orElse) => { + if (!self.unsafeMap.has(tag.key)) { + return isReference(tag) ? getDefaultValue(tag) : orElse() + } + return self.unsafeMap.get(tag.key)! as any +}) + +/** @internal */ +export const getOption = dual< + (tag: C.Tag) => (self: C.Context) => O.Option, + (self: C.Context, tag: C.Tag) => O.Option +>(2, (self, tag) => { + if (!self.unsafeMap.has(tag.key)) { + return isReference(tag) ? option.some(getDefaultValue(tag)) : option.none + } + return option.some(self.unsafeMap.get(tag.key)! as any) +}) + +/** @internal */ +export const merge = dual< + (that: C.Context) => (self: C.Context) => C.Context, + (self: C.Context, that: C.Context) => C.Context +>(2, (self, that) => { + const map = new Map(self.unsafeMap) + for (const [tag, s] of that.unsafeMap) { + map.set(tag, s) + } + return makeContext(map) +}) + +/** @internal */ +export const mergeAll = >( + ...ctxs: [...{ [K in keyof T]: C.Context }] +): C.Context => { + const map = new Map() + for (let i = 0; i < ctxs.length; i++) { + ctxs[i].unsafeMap.forEach((value, key) => { + map.set(key, value) + }) + } + return makeContext(map) +} + +/** @internal */ +export const pick = + >>(...tags: Tags) => + (self: C.Context): C.Context< + Services & C.Tag.Identifier + > => { + const tagSet = new Set(tags.map((_) => _.key)) + const newEnv = new Map() + for (const [tag, s] of self.unsafeMap.entries()) { + if (tagSet.has(tag)) { + newEnv.set(tag, s) + } + } + return makeContext(newEnv) + } + +/** @internal */ +export const omit = + >>(...tags: Tags) => + (self: C.Context): C.Context< + Exclude> + > => { + const newEnv = new Map(self.unsafeMap) + for (const tag of tags) { + newEnv.delete(tag.key) + } + return makeContext(newEnv) + } diff --git a/backend/node_modules/effect/src/internal/core-effect.ts b/backend/node_modules/effect/src/internal/core-effect.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ef68fb67b64686929624bf42af93513f2dad31b --- /dev/null +++ b/backend/node_modules/effect/src/internal/core-effect.ts @@ -0,0 +1,2304 @@ +import * as Arr from "../Array.js" +import type * as Cause from "../Cause.js" +import * as Chunk from "../Chunk.js" +import * as Clock from "../Clock.js" +import * as Context from "../Context.js" +import * as Duration from "../Duration.js" +import type * as Effect from "../Effect.js" +import type { Exit } from "../Exit.js" +import type * as Fiber from "../Fiber.js" +import type * as FiberId from "../FiberId.js" +import type * as FiberRef from "../FiberRef.js" +import * as FiberRefs from "../FiberRefs.js" +import type * as FiberRefsPatch from "../FiberRefsPatch.js" +import type { LazyArg } from "../Function.js" +import { constFalse, constTrue, constVoid, dual, identity, pipe } from "../Function.js" +import * as HashMap from "../HashMap.js" +import * as HashSet from "../HashSet.js" +import * as List from "../List.js" +import * as LogLevel from "../LogLevel.js" +import * as LogSpan from "../LogSpan.js" +import type * as Metric from "../Metric.js" +import type * as MetricLabel from "../MetricLabel.js" +import * as Option from "../Option.js" +import * as Predicate from "../Predicate.js" +import type * as Random from "../Random.js" +import * as Ref from "../Ref.js" +import type * as runtimeFlagsPatch from "../RuntimeFlagsPatch.js" +import * as Tracer from "../Tracer.js" +import type * as Types from "../Types.js" +import type { Unify } from "../Unify.js" +import { internalCall } from "../Utils.js" +import * as internalCause from "./cause.js" +import { clockTag } from "./clock.js" +import * as core from "./core.js" +import * as defaultServices from "./defaultServices.js" +import * as doNotation from "./doNotation.js" +import * as fiberRefsPatch from "./fiberRefs/patch.js" +import type { FiberRuntime } from "./fiberRuntime.js" +import * as metricLabel from "./metric/label.js" +import * as runtimeFlags from "./runtimeFlags.js" +import * as internalTracer from "./tracer.js" + +/* @internal */ +export const annotateLogs = dual< + { + (key: string, value: unknown): (effect: Effect.Effect) => Effect.Effect + ( + values: Record + ): (effect: Effect.Effect) => Effect.Effect + }, + { + (effect: Effect.Effect, key: string, value: unknown): Effect.Effect + (effect: Effect.Effect, values: Record): Effect.Effect + } +>( + (args) => core.isEffect(args[0]), + function() { + const args = arguments + return core.fiberRefLocallyWith( + args[0] as Effect.Effect, + core.currentLogAnnotations, + typeof args[1] === "string" + ? HashMap.set(args[1], args[2]) + : (annotations) => + Object.entries(args[1] as Record).reduce( + (acc, [key, value]) => HashMap.set(acc, key, value), + annotations + ) + ) + } +) + +/* @internal */ +export const asSome = (self: Effect.Effect): Effect.Effect, E, R> => + core.map(self, Option.some) + +/* @internal */ +export const asSomeError = (self: Effect.Effect): Effect.Effect, R> => + core.mapError(self, Option.some) + +/* @internal */ +export const try_: { + (options: { + readonly try: LazyArg + readonly catch: (error: unknown) => E + }): Effect.Effect + (thunk: LazyArg): Effect.Effect +} = ( + arg: LazyArg | { + readonly try: LazyArg + readonly catch: (error: unknown) => E + } +) => { + let evaluate: LazyArg + let onFailure: ((error: unknown) => E) | undefined = undefined + if (typeof arg === "function") { + evaluate = arg + } else { + evaluate = arg.try + onFailure = arg.catch + } + return core.suspend(() => { + try { + return core.succeed(internalCall(evaluate)) + } catch (error) { + return core.fail( + onFailure + ? internalCall(() => onFailure(error)) + : new core.UnknownException(error, "An unknown error occurred in Effect.try") + ) + } + }) +} + +/* @internal */ +export const _catch: { + ( + discriminator: N, + options: { + readonly failure: K + readonly onFailure: (error: Extract) => Effect.Effect + } + ): (self: Effect.Effect) => Effect.Effect< + A | A1, + Exclude | E1, + R | R1 + > + ( + self: Effect.Effect, + discriminator: N, + options: { + readonly failure: K + readonly onFailure: (error: Extract) => Effect.Effect + } + ): Effect.Effect | E1, R | R1> +} = dual( + 3, + (self, tag, options) => + core.catchAll(self, (e) => { + if (Predicate.hasProperty(e, tag) && e[tag] === options.failure) { + return options.onFailure(e) + } + return core.fail(e) + }) +) + +/* @internal */ +export const catchAllDefect = dual< + ( + f: (defect: unknown) => Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + f: (defect: unknown) => Effect.Effect + ) => Effect.Effect +>(2, ( + self: Effect.Effect, + f: (defect: unknown) => Effect.Effect +): Effect.Effect => + core.catchAllCause( + self, + (cause): Effect.Effect => { + const option = internalCause.find(cause, (_) => internalCause.isDieType(_) ? Option.some(_) : Option.none()) + switch (option._tag) { + case "None": { + return core.failCause(cause) + } + case "Some": { + return f(option.value.defect) + } + } + } + )) + +/* @internal */ +export const catchSomeCause: { + ( + f: (cause: Cause.Cause>) => Option.Option> + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + f: (cause: Cause.Cause>) => Option.Option> + ): Effect.Effect +} = dual( + 2, + ( + self: Effect.Effect, + f: (cause: Cause.Cause>) => Option.Option> + ): Effect.Effect => + core.matchCauseEffect(self, { + onFailure: (cause): Effect.Effect => { + const option = f(cause) + switch (option._tag) { + case "None": { + return core.failCause(cause) + } + case "Some": { + return option.value + } + } + }, + onSuccess: core.succeed + }) +) + +/* @internal */ +export const catchSomeDefect = dual< + ( + pf: (defect: unknown) => Option.Option> + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + pf: (defect: unknown) => Option.Option> + ) => Effect.Effect +>( + 2, + ( + self: Effect.Effect, + pf: (defect: unknown) => Option.Option> + ): Effect.Effect => + core.catchAllCause( + self, + (cause): Effect.Effect => { + const option = internalCause.find(cause, (_) => internalCause.isDieType(_) ? Option.some(_) : Option.none()) + switch (option._tag) { + case "None": { + return core.failCause(cause) + } + case "Some": { + const optionEffect = pf(option.value.defect) + return optionEffect._tag === "Some" ? optionEffect.value : core.failCause(cause) + } + } + } + ) +) + +/* @internal */ +export const catchTag: { + < + E, + const K extends Arr.NonEmptyReadonlyArray, + A1, + E1, + R1 + >( + ...args: [ + ...tags: K, + f: (e: Extract, { _tag: K[number] }>) => Effect.Effect + ] + ): (self: Effect.Effect) => Effect.Effect | E1, R | R1> + < + A, + E, + R, + const K extends Arr.NonEmptyReadonlyArray, + A1, + E1, + R1 + >( + self: Effect.Effect, + ...args: [ + ...tags: K, + f: (e: Extract, { _tag: K[number] }>) => Effect.Effect + ] + ): Effect.Effect | E1, R | R1> +} = dual( + (args: any) => core.isEffect(args[0]), + , R1, E1, A1>( + self: Effect.Effect, + ...args: [ + ...tags: K & { [I in keyof K]: E extends { _tag: string } ? E["_tag"] : never }, + f: (e: Extract, { _tag: K[number] }>) => Effect.Effect + ] + ): Effect.Effect | E1, R | R1> => { + const f = args[args.length - 1] as any + let predicate: Predicate.Predicate + if (args.length === 2) { + predicate = Predicate.isTagged(args[0] as string) + } else { + predicate = (e) => { + const tag = Predicate.hasProperty(e, "_tag") ? e["_tag"] : undefined + if (!tag) return false + for (let i = 0; i < args.length - 1; i++) { + if (args[i] === tag) return true + } + return false + } + } + return core.catchIf(self, predicate as Predicate.Refinement>, f) as any + } +) as any + +/** @internal */ +export const catchTags: { + < + E, + Cases extends (E extends { _tag: string } ? { + [K in E["_tag"]]+?: (error: Extract) => Effect.Effect + } : + {}) + >( + cases: Cases + ): (self: Effect.Effect) => Effect.Effect< + | A + | { + [K in keyof Cases]: Cases[K] extends ((...args: Array) => Effect.Effect) ? A : never + }[keyof Cases], + | Exclude + | { + [K in keyof Cases]: Cases[K] extends ((...args: Array) => Effect.Effect) ? E : never + }[keyof Cases], + | R + | { + [K in keyof Cases]: Cases[K] extends ((...args: Array) => Effect.Effect) ? R : never + }[keyof Cases] + > + < + R, + E, + A, + Cases extends (E extends { _tag: string } ? { + [K in E["_tag"]]+?: (error: Extract) => Effect.Effect + } : + {}) + >( + self: Effect.Effect, + cases: Cases + ): Effect.Effect< + | A + | { + [K in keyof Cases]: Cases[K] extends ((...args: Array) => Effect.Effect) ? A : never + }[keyof Cases], + | Exclude + | { + [K in keyof Cases]: Cases[K] extends ((...args: Array) => Effect.Effect) ? E : never + }[keyof Cases], + | R + | { + [K in keyof Cases]: Cases[K] extends ((...args: Array) => Effect.Effect) ? R : never + }[keyof Cases] + > +} = dual(2, (self, cases) => { + let keys: Array + return core.catchIf( + self, + (e): e is { readonly _tag: string } => { + keys ??= Object.keys(cases) + return Predicate.hasProperty(e, "_tag") && Predicate.isString(e["_tag"]) && keys.includes(e["_tag"]) + }, + (e) => cases[e["_tag"]](e) + ) +}) + +/* @internal */ +export const cause = (self: Effect.Effect): Effect.Effect, never, R> => + core.matchCause(self, { onFailure: identity, onSuccess: () => internalCause.empty }) + +/* @internal */ +export const clockWith: (f: (clock: Clock.Clock) => Effect.Effect) => Effect.Effect = + Clock.clockWith + +/* @internal */ +export const clock: Effect.Effect = clockWith(core.succeed) + +/* @internal */ +export const delay = dual< + (duration: Duration.DurationInput) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, duration: Duration.DurationInput) => Effect.Effect +>(2, (self, duration) => core.zipRight(Clock.sleep(duration), self)) + +/* @internal */ +export const descriptorWith = ( + f: (descriptor: Fiber.Fiber.Descriptor) => Effect.Effect +): Effect.Effect => + core.withFiberRuntime((state, status) => + f({ + id: state.id(), + status, + interruptors: internalCause.interruptors(state.getFiberRef(core.currentInterruptedCause)) + }) + ) as Effect.Effect + +/* @internal */ +export const allowInterrupt: Effect.Effect = descriptorWith( + (descriptor) => + HashSet.size(descriptor.interruptors) > 0 + ? core.interrupt + : core.void +) + +/* @internal */ +export const descriptor: Effect.Effect = descriptorWith(core.succeed) + +/* @internal */ +export const diffFiberRefs = ( + self: Effect.Effect +): Effect.Effect<[FiberRefsPatch.FiberRefsPatch, A], E, R> => summarized(self, fiberRefs, fiberRefsPatch.diff) + +/* @internal */ +export const diffFiberRefsAndRuntimeFlags = ( + self: Effect.Effect +): Effect.Effect<[[FiberRefsPatch.FiberRefsPatch, runtimeFlagsPatch.RuntimeFlagsPatch], A], E, R> => + summarized( + self, + core.zip(fiberRefs, core.runtimeFlags), + ([refs, flags], [refsNew, flagsNew]) => [fiberRefsPatch.diff(refs, refsNew), runtimeFlags.diff(flags, flagsNew)] + ) + +/* @internal */ +export const Do: Effect.Effect<{}> = core.succeed({}) + +/* @internal */ +export const bind: { + ( + name: Exclude, + f: (a: Types.NoInfer) => Effect.Effect + ): ( + self: Effect.Effect + ) => Effect.Effect<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E2 | E1, R2 | R1> + ( + self: Effect.Effect, + name: Exclude, + f: (a: Types.NoInfer) => Effect.Effect + ): Effect.Effect<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E1 | E2, R1 | R2> +} = doNotation.bind(core.map, core.flatMap) + +/* @internal */ +export const bindTo: { + (name: N): (self: Effect.Effect) => Effect.Effect<{ [K in N]: A }, E, R> + (self: Effect.Effect, name: N): Effect.Effect<{ [K in N]: A }, E, R> +} = doNotation.bindTo(core.map) + +/* @internal */ +export const let_: { + ( + name: Exclude, + f: (a: Types.NoInfer) => B + ): ( + self: Effect.Effect + ) => Effect.Effect<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E, R> + ( + self: Effect.Effect, + name: Exclude, + f: (a: Types.NoInfer) => B + ): Effect.Effect<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E, R> +} = doNotation.let_(core.map) + +/* @internal */ +export const dropUntil: { + ( + predicate: (a: Types.NoInfer, i: number) => Effect.Effect + ): (elements: Iterable) => Effect.Effect, E, R> + ( + elements: Iterable, + predicate: (a: A, i: number) => Effect.Effect + ): Effect.Effect, E, R> +} = dual( + 2, + ( + elements: Iterable, + predicate: (a: A, i: number) => Effect.Effect + ): Effect.Effect, E, R> => + core.suspend(() => { + const iterator = elements[Symbol.iterator]() + const builder: Array = [] + let next: IteratorResult + let dropping: Effect.Effect = core.succeed(false) + let i = 0 + while ((next = iterator.next()) && !next.done) { + const a = next.value + const index = i++ + dropping = core.flatMap(dropping, (bool) => { + if (bool) { + builder.push(a) + return core.succeed(true) + } + return predicate(a, index) + }) + } + return core.map(dropping, () => builder) + }) +) + +/* @internal */ +export const dropWhile: { + ( + predicate: (a: Types.NoInfer, i: number) => Effect.Effect + ): (elements: Iterable) => Effect.Effect, E, R> + ( + elements: Iterable, + predicate: (a: A, i: number) => Effect.Effect + ): Effect.Effect, E, R> +} = dual( + 2, + ( + elements: Iterable, + predicate: (a: A, i: number) => Effect.Effect + ): Effect.Effect, E, R> => + core.suspend(() => { + const iterator = elements[Symbol.iterator]() + const builder: Array = [] + let next + let dropping: Effect.Effect = core.succeed(true) + let i = 0 + while ((next = iterator.next()) && !next.done) { + const a = next.value + const index = i++ + dropping = core.flatMap(dropping, (d) => + core.map(d ? predicate(a, index) : core.succeed(false), (b) => { + if (!b) { + builder.push(a) + } + return b + })) + } + return core.map(dropping, () => builder) + }) +) + +/* @internal */ +export const contextWith = (f: (context: Context.Context) => A): Effect.Effect => + core.map(core.context(), f) + +/* @internal */ +export const eventually = (self: Effect.Effect): Effect.Effect => + core.orElse(self, () => core.flatMap(core.yieldNow(), () => eventually(self))) + +/* @internal */ +export const filterMap = dual< + , B>( + pf: (a: Effect.Effect.Success) => Option.Option + ) => (elements: Iterable) => Effect.Effect, Effect.Effect.Error, Effect.Effect.Context>, + , B>( + elements: Iterable, + pf: (a: Effect.Effect.Success) => Option.Option + ) => Effect.Effect, Effect.Effect.Error, Effect.Effect.Context> +>(2, (elements, pf) => + core.map( + core.forEachSequential(elements, identity), + Arr.filterMap(pf) + )) + +/* @internal */ +export const filterOrDie: { + ( + refinement: Predicate.Refinement, B>, + orDieWith: (a: Types.EqualsWith>) => unknown + ): (self: Effect.Effect) => Effect.Effect + ( + predicate: Predicate.Predicate>, + orDieWith: (a: Types.NoInfer) => unknown + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + refinement: Predicate.Refinement, + orDieWith: (a: Types.EqualsWith>) => unknown + ): Effect.Effect + ( + self: Effect.Effect, + predicate: Predicate.Predicate, + orDieWith: (a: A) => unknown + ): Effect.Effect +} = dual( + 3, + ( + self: Effect.Effect, + predicate: Predicate.Predicate, + orDieWith: (a: A) => unknown + ): Effect.Effect => filterOrElse(self, predicate, (a) => core.dieSync(() => orDieWith(a))) +) + +/* @internal */ +export const filterOrDieMessage: { + ( + refinement: Predicate.Refinement, B>, + message: string + ): (self: Effect.Effect) => Effect.Effect + ( + predicate: Predicate.Predicate>, + message: string + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + refinement: Predicate.Refinement, + message: string + ): Effect.Effect + ( + self: Effect.Effect, + predicate: Predicate.Predicate, + message: string + ): Effect.Effect +} = dual( + 3, + (self: Effect.Effect, predicate: Predicate.Predicate, message: string): Effect.Effect => + filterOrElse(self, predicate, () => core.dieMessage(message)) +) + +/* @internal */ +export const filterOrElse: { + ( + refinement: Predicate.Refinement, B>, + orElse: (a: Types.EqualsWith, Exclude, B>>) => Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + predicate: Predicate.Predicate>, + orElse: (a: Types.NoInfer) => Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + refinement: Predicate.Refinement, + orElse: (a: Types.EqualsWith>) => Effect.Effect + ): Effect.Effect + ( + self: Effect.Effect, + predicate: Predicate.Predicate, + orElse: (a: A) => Effect.Effect + ): Effect.Effect +} = dual(3, ( + self: Effect.Effect, + predicate: Predicate.Predicate, + orElse: (a: A) => Effect.Effect +): Effect.Effect => + core.flatMap( + self, + (a) => predicate(a) ? core.succeed(a) : orElse(a) + )) + +/** @internal */ +export const liftPredicate = dual< + ( + predicate: Predicate.Refinement | Predicate.Predicate, + orFailWith: (a: Types.EqualsWith>) => E + ) => (a: A) => Effect.Effect, E>, + ( + self: A, + predicate: Predicate.Refinement | Predicate.Predicate, + orFailWith: (a: Types.EqualsWith>) => E + ) => Effect.Effect +>( + 3, + ( + self: A, + predicate: Predicate.Refinement | Predicate.Predicate, + orFailWith: (a: Types.EqualsWith>) => E + ): Effect.Effect => + core.suspend(() => predicate(self) ? core.succeed(self as B) : core.fail(orFailWith(self as any))) +) + +/* @internal */ +export const filterOrFail: { + ( + refinement: Predicate.Refinement, B>, + orFailWith: (a: Types.EqualsWith, Exclude, B>>) => E2 + ): (self: Effect.Effect) => Effect.Effect, E2 | E, R> + ( + predicate: Predicate.Predicate>, + orFailWith: (a: Types.NoInfer) => E2 + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + refinement: Predicate.Refinement, + orFailWith: (a: Types.EqualsWith>) => E2 + ): Effect.Effect, E2 | E, R> + ( + self: Effect.Effect, + predicate: Predicate.Predicate, + orFailWith: (a: A) => E2 + ): Effect.Effect + ( + refinement: Predicate.Refinement, B> + ): (self: Effect.Effect) => Effect.Effect, Cause.NoSuchElementException | E, R> + ( + predicate: Predicate.Predicate> + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + refinement: Predicate.Refinement + ): Effect.Effect, E | Cause.NoSuchElementException, R> + ( + self: Effect.Effect, + predicate: Predicate.Predicate + ): Effect.Effect +} = dual((args) => core.isEffect(args[0]), ( + self: Effect.Effect, + predicate: Predicate.Predicate, + orFailWith?: (a: A) => E2 +): Effect.Effect => + filterOrElse( + self, + predicate, + (a): Effect.Effect => + orFailWith === undefined ? core.fail(new core.NoSuchElementException()) : core.failSync(() => orFailWith(a)) + )) + +/* @internal */ +export const findFirst: { + ( + predicate: (a: Types.NoInfer, i: number) => Effect.Effect + ): (elements: Iterable) => Effect.Effect, E, R> + ( + elements: Iterable, + predicate: (a: Types.NoInfer, i: number) => Effect.Effect + ): Effect.Effect, E, R> +} = dual( + 2, + ( + elements: Iterable, + predicate: (a: Types.NoInfer, i: number) => Effect.Effect + ): Effect.Effect, E, R> => + core.suspend(() => { + const iterator = elements[Symbol.iterator]() + const next = iterator.next() + if (!next.done) { + return findLoop(iterator, 0, predicate, next.value) + } + return core.succeed(Option.none()) + }) +) + +const findLoop = ( + iterator: Iterator, + index: number, + f: (a: A, i: number) => Effect.Effect, + value: A +): Effect.Effect, E, R> => + core.flatMap(f(value, index), (result) => { + if (result) { + return core.succeed(Option.some(value)) + } + const next = iterator.next() + if (!next.done) { + return findLoop(iterator, index + 1, f, next.value) + } + return core.succeed(Option.none()) + }) + +/* @internal */ +export const firstSuccessOf = >( + effects: Iterable +): Effect.Effect, Effect.Effect.Error, Effect.Effect.Context> => + core.suspend(() => { + const list = Chunk.fromIterable(effects) + if (!Chunk.isNonEmpty(list)) { + return core.dieSync(() => new core.IllegalArgumentException(`Received an empty collection of effects`)) + } + return pipe( + Chunk.tailNonEmpty(list), + Arr.reduce(Chunk.headNonEmpty(list), (left, right) => core.orElse(left, () => right) as Eff) + ) + }) + +/* @internal */ +export const flipWith: { + ( + f: (effect: Effect.Effect) => Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + f: (effect: Effect.Effect) => Effect.Effect + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + f: (effect: Effect.Effect) => Effect.Effect +): Effect.Effect => core.flip(f(core.flip(self)))) + +/* @internal */ +export const match: { + ( + options: { + readonly onFailure: (error: E) => A2 + readonly onSuccess: (value: A) => A3 + } + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + options: { + readonly onFailure: (error: E) => A2 + readonly onSuccess: (value: A) => A3 + } + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + options: { + readonly onFailure: (error: E) => A2 + readonly onSuccess: (value: A) => A3 + } +): Effect.Effect => + core.matchEffect(self, { + onFailure: (e) => core.succeed(options.onFailure(e)), + onSuccess: (a) => core.succeed(options.onSuccess(a)) + })) + +/* @internal */ +export const every: { + ( + predicate: (a: A, i: number) => Effect.Effect + ): (elements: Iterable) => Effect.Effect + ( + elements: Iterable, + predicate: (a: A, i: number) => Effect.Effect + ): Effect.Effect +} = dual( + 2, + ( + elements: Iterable, + predicate: (a: A, i: number) => Effect.Effect + ): Effect.Effect => core.suspend(() => forAllLoop(elements[Symbol.iterator](), 0, predicate)) +) + +const forAllLoop = ( + iterator: Iterator, + index: number, + f: (a: A, i: number) => Effect.Effect +): Effect.Effect => { + const next = iterator.next() + return next.done + ? core.succeed(true) + : core.flatMap( + f(next.value, index), + (b) => b ? forAllLoop(iterator, index + 1, f) : core.succeed(b) + ) +} + +/* @internal */ +export const forever = (self: Effect.Effect): Effect.Effect => { + const loop: Effect.Effect = core.flatMap(core.flatMap(self, () => core.yieldNow()), () => loop) + return loop +} + +/* @internal */ +export const fiberRefs: Effect.Effect = core.withFiberRuntime((state) => + core.succeed(state.getFiberRefs()) +) + +/* @internal */ +export const head = ( + self: Effect.Effect, E, R> +): Effect.Effect => + core.flatMap(self, (as) => { + const iterator = as[Symbol.iterator]() + const next = iterator.next() + if (next.done) { + return core.fail(new core.NoSuchElementException()) + } + return core.succeed(next.value) + }) + +/* @internal */ +export const ignore = (self: Effect.Effect): Effect.Effect => + match(self, { onFailure: constVoid, onSuccess: constVoid }) + +/* @internal */ +export const ignoreLogged = (self: Effect.Effect): Effect.Effect => + core.matchCauseEffect(self, { + onFailure: (cause) => logDebug(cause, "An error was silently ignored because it is not anticipated to be useful"), + onSuccess: () => core.void + }) + +/* @internal */ +export const inheritFiberRefs = (childFiberRefs: FiberRefs.FiberRefs) => + updateFiberRefs((parentFiberId, parentFiberRefs) => FiberRefs.joinAs(parentFiberRefs, parentFiberId, childFiberRefs)) + +/* @internal */ +export const isFailure = (self: Effect.Effect): Effect.Effect => + match(self, { onFailure: constTrue, onSuccess: constFalse }) + +/* @internal */ +export const isSuccess = (self: Effect.Effect): Effect.Effect => + match(self, { onFailure: constFalse, onSuccess: constTrue }) + +/* @internal */ +export const iterate: { + ( + initial: A, + options: { + readonly while: Predicate.Refinement + readonly body: (b: B) => Effect.Effect + } + ): Effect.Effect + ( + initial: A, + options: { + readonly while: Predicate.Predicate + readonly body: (a: A) => Effect.Effect + } + ): Effect.Effect +} = ( + initial: A, + options: { + readonly while: Predicate.Predicate + readonly body: (z: A) => Effect.Effect + } +): Effect.Effect => + core.suspend(() => { + if (options.while(initial)) { + return core.flatMap(options.body(initial), (z2) => iterate(z2, options)) + } + return core.succeed(initial) + }) + +/** @internal */ +export const logWithLevel = (level?: LogLevel.LogLevel) => +( + ...message: ReadonlyArray +): Effect.Effect => { + const levelOption = Option.fromNullable(level) + let cause: Cause.Cause | undefined = undefined + for (let i = 0, len = message.length; i < len; i++) { + const msg = message[i] + if (internalCause.isCause(msg)) { + if (cause !== undefined) { + cause = internalCause.sequential(cause, msg) + } else { + cause = msg + } + message = [...message.slice(0, i), ...message.slice(i + 1)] + i-- + } + } + if (cause === undefined) { + cause = internalCause.empty + } + return core.withFiberRuntime((fiberState) => { + fiberState.log(message, cause, levelOption) + return core.void + }) +} + +/** @internal */ +export const log: (...message: ReadonlyArray) => Effect.Effect = logWithLevel() + +/** @internal */ +export const logTrace: (...message: ReadonlyArray) => Effect.Effect = logWithLevel( + LogLevel.Trace +) + +/** @internal */ +export const logDebug: (...message: ReadonlyArray) => Effect.Effect = logWithLevel( + LogLevel.Debug +) + +/** @internal */ +export const logInfo: (...message: ReadonlyArray) => Effect.Effect = logWithLevel( + LogLevel.Info +) + +/** @internal */ +export const logWarning: (...message: ReadonlyArray) => Effect.Effect = logWithLevel( + LogLevel.Warning +) + +/** @internal */ +export const logError: (...message: ReadonlyArray) => Effect.Effect = logWithLevel( + LogLevel.Error +) + +/** @internal */ +export const logFatal: (...message: ReadonlyArray) => Effect.Effect = logWithLevel( + LogLevel.Fatal +) + +/* @internal */ +export const withLogSpan = dual< + (label: string) => (effect: Effect.Effect) => Effect.Effect, + (effect: Effect.Effect, label: string) => Effect.Effect +>(2, (effect, label) => + core.flatMap(Clock.currentTimeMillis, (now) => + core.fiberRefLocallyWith( + effect, + core.currentLogSpan, + List.prepend(LogSpan.make(label, now)) + ))) + +/* @internal */ +export const logAnnotations: Effect.Effect> = core + .fiberRefGet( + core.currentLogAnnotations + ) + +/* @internal */ +export const loop: { + ( + initial: A, + options: { + readonly while: Predicate.Refinement + readonly step: (b: B) => A + readonly body: (b: B) => Effect.Effect + readonly discard?: false | undefined + } + ): Effect.Effect, E, R> + ( + initial: A, + options: { + readonly while: (a: A) => boolean + readonly step: (a: A) => A + readonly body: (a: A) => Effect.Effect + readonly discard?: false | undefined + } + ): Effect.Effect, E, R> + ( + initial: A, + options: { + readonly while: Predicate.Refinement + readonly step: (b: B) => A + readonly body: (b: B) => Effect.Effect + readonly discard: true + } + ): Effect.Effect + ( + initial: A, + options: { + readonly while: (a: A) => boolean + readonly step: (a: A) => A + readonly body: (a: A) => Effect.Effect + readonly discard: true + } + ): Effect.Effect +} = ( + initial: A, + options: { + readonly while: Predicate.Predicate + readonly step: (a: A) => A + readonly body: (a: A) => Effect.Effect + readonly discard?: boolean | undefined + } +): any => + options.discard + ? loopDiscard(initial, options.while, options.step, options.body) + : core.map(loopInternal(initial, options.while, options.step, options.body), Arr.fromIterable) + +const loopInternal = ( + initial: Z, + cont: Predicate.Predicate, + inc: (z: Z) => Z, + body: (z: Z) => Effect.Effect +): Effect.Effect, E, R> => + core.suspend(() => + cont(initial) + ? core.flatMap(body(initial), (a) => + core.map( + loopInternal(inc(initial), cont, inc, body), + List.prepend(a) + )) + : core.sync(() => List.empty()) + ) + +const loopDiscard = ( + initial: S, + cont: Predicate.Predicate, + inc: (s: S) => S, + body: (s: S) => Effect.Effect +): Effect.Effect => + core.suspend(() => + cont(initial) + ? core.flatMap( + body(initial), + () => loopDiscard(inc(initial), cont, inc, body) + ) + : core.void + ) + +/* @internal */ +export const mapAccum: { + = Iterable>( + initial: S, + f: (state: S, a: A, i: number) => Effect.Effect + ): (elements: I) => Effect.Effect<[S, Arr.ReadonlyArray.With], E, R> + = Iterable>( + elements: I, + initial: S, + f: (state: S, a: A, i: number) => Effect.Effect + ): Effect.Effect<[S, Arr.ReadonlyArray.With], E, R> +} = dual(3, = Iterable>( + elements: I, + initial: S, + f: (state: S, a: A, i: number) => Effect.Effect +): Effect.Effect<[S, Array], E, R> => + core.suspend(() => { + const iterator = elements[Symbol.iterator]() + const builder: Array = [] + let result: Effect.Effect = core.succeed(initial) + let next: IteratorResult + let i = 0 + while (!(next = iterator.next()).done) { + const index = i++ + const value = next.value + result = core.flatMap(result, (state) => + core.map(f(state, value, index), ([z, b]) => { + builder.push(b) + return z + })) + } + return core.map(result, (z) => [z, builder]) + })) + +/* @internal */ +export const mapErrorCause: { + ( + f: (cause: Cause.Cause) => Cause.Cause + ): (self: Effect.Effect) => Effect.Effect + (self: Effect.Effect, f: (cause: Cause.Cause) => Cause.Cause): Effect.Effect +} = dual( + 2, + (self: Effect.Effect, f: (cause: Cause.Cause) => Cause.Cause): Effect.Effect => + core.matchCauseEffect(self, { + onFailure: (c) => core.failCauseSync(() => f(c)), + onSuccess: core.succeed + }) +) + +/* @internal */ +export const memoize = ( + self: Effect.Effect +): Effect.Effect> => + pipe( + core.deferredMake<[[FiberRefsPatch.FiberRefsPatch, runtimeFlagsPatch.RuntimeFlagsPatch], A], E>(), + core.flatMap((deferred) => + pipe( + diffFiberRefsAndRuntimeFlags(self), + core.intoDeferred(deferred), + once, + core.map((complete) => + core.zipRight( + complete, + pipe( + core.deferredAwait(deferred), + core.flatMap(([patch, a]) => + core.as(core.zip(patchFiberRefs(patch[0]), core.updateRuntimeFlags(patch[1])), a) + ) + ) + ) + ) + ) + ) + ) + +/* @internal */ +export const merge = (self: Effect.Effect): Effect.Effect => + core.matchEffect(self, { + onFailure: (e) => core.succeed(e), + onSuccess: core.succeed + }) + +/* @internal */ +export const negate = (self: Effect.Effect): Effect.Effect => + core.map(self, (b) => !b) + +/* @internal */ +export const none = ( + self: Effect.Effect, E, R> +): Effect.Effect => + core.flatMap(self, (option) => { + switch (option._tag) { + case "None": + return core.void + case "Some": + return core.fail(new core.NoSuchElementException()) + } + }) + +/* @internal */ +export const once = ( + self: Effect.Effect +): Effect.Effect> => + core.map( + Ref.make(true), + (ref) => core.asVoid(core.whenEffect(self, Ref.getAndSet(ref, false))) + ) + +/* @internal */ +export const option = (self: Effect.Effect): Effect.Effect, never, R> => + core.matchEffect(self, { + onFailure: () => core.succeed(Option.none()), + onSuccess: (a) => core.succeed(Option.some(a)) + }) + +/* @internal */ +export const orElseFail = dual< + (evaluate: LazyArg) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, evaluate: LazyArg) => Effect.Effect +>(2, (self, evaluate) => core.orElse(self, () => core.failSync(evaluate))) + +/* @internal */ +export const orElseSucceed = dual< + (evaluate: LazyArg) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, evaluate: LazyArg) => Effect.Effect +>(2, (self, evaluate) => core.orElse(self, () => core.sync(evaluate))) + +/* @internal */ +export const parallelErrors = (self: Effect.Effect): Effect.Effect, R> => + core.matchCauseEffect(self, { + onFailure: (cause) => { + const errors = Arr.fromIterable(internalCause.failures(cause)) + return errors.length === 0 + ? core.failCause(cause as Cause.Cause) + : core.fail(errors) + }, + onSuccess: core.succeed + }) + +/* @internal */ +export const patchFiberRefs = (patch: FiberRefsPatch.FiberRefsPatch): Effect.Effect => + updateFiberRefs((fiberId, fiberRefs) => pipe(patch, fiberRefsPatch.patch(fiberId, fiberRefs))) + +/* @internal */ +export const promise = (evaluate: (signal: AbortSignal) => PromiseLike): Effect.Effect => + evaluate.length >= 1 + ? core.async((resolve, signal) => { + try { + evaluate(signal) + .then((a) => resolve(core.succeed(a)), (e) => resolve(core.die(e))) + } catch (e) { + resolve(core.die(e)) + } + }) + : core.async((resolve) => { + try { + ;(evaluate as LazyArg>)() + .then((a) => resolve(core.succeed(a)), (e) => resolve(core.die(e))) + } catch (e) { + resolve(core.die(e)) + } + }) + +/* @internal */ +export const provideService = dual< + ( + tag: Context.Tag, + service: Types.NoInfer + ) => (self: Effect.Effect) => Effect.Effect>, + ( + self: Effect.Effect, + tag: Context.Tag, + service: Types.NoInfer + ) => Effect.Effect> +>( + 3, + ( + self: Effect.Effect, + tag: Context.Tag, + service: Types.NoInfer + ): Effect.Effect> => + core.contextWithEffect((env) => + core.provideContext( + self as Effect.Effect>, + Context.add(env, tag, service) + ) + ) +) + +/* @internal */ +export const provideServiceEffect = dual< + ( + tag: Context.Tag, + effect: Effect.Effect, E1, R1> + ) => (self: Effect.Effect) => Effect.Effect>, + ( + self: Effect.Effect, + tag: Context.Tag, + effect: Effect.Effect, E1, R1> + ) => Effect.Effect> +>(3, ( + self: Effect.Effect, + tag: Context.Tag, + effect: Effect.Effect, E1, R1> +) => + core.contextWithEffect((env: Context.Context>) => + core.flatMap( + effect, + (service) => core.provideContext(self, pipe(env, Context.add(tag, service)) as Context.Context) + ) + )) + +/* @internal */ +export const random: Effect.Effect = defaultServices.randomWith(core.succeed) + +/* @internal */ +export const reduce = dual< + ( + zero: Z, + f: (z: Z, a: A, i: number) => Effect.Effect + ) => (elements: Iterable) => Effect.Effect, + ( + elements: Iterable, + zero: Z, + f: (z: Z, a: A, i: number) => Effect.Effect + ) => Effect.Effect +>( + 3, + ( + elements: Iterable, + zero: Z, + f: (z: Z, a: A, i: number) => Effect.Effect + ) => + Arr.fromIterable(elements).reduce( + (acc, el, i) => core.flatMap(acc, (a) => f(a, el, i)), + core.succeed(zero) as Effect.Effect + ) +) + +/* @internal */ +export const reduceRight = dual< + ( + zero: Z, + f: (a: A, z: Z, i: number) => Effect.Effect + ) => (elements: Iterable) => Effect.Effect, + ( + elements: Iterable, + zero: Z, + f: (a: A, z: Z, i: number) => Effect.Effect + ) => Effect.Effect +>( + 3, + (elements: Iterable, zero: Z, f: (a: A, z: Z, i: number) => Effect.Effect) => + Arr.fromIterable(elements).reduceRight( + (acc, el, i) => core.flatMap(acc, (a) => f(el, a, i)), + core.succeed(zero) as Effect.Effect + ) +) + +/* @internal */ +export const reduceWhile = dual< + ( + zero: Z, + options: { + readonly while: Predicate.Predicate + readonly body: (s: Z, a: A, i: number) => Effect.Effect + } + ) => (elements: Iterable) => Effect.Effect, + ( + elements: Iterable, + zero: Z, + options: { + readonly while: Predicate.Predicate + readonly body: (s: Z, a: A, i: number) => Effect.Effect + } + ) => Effect.Effect +>(3, ( + elements: Iterable, + zero: Z, + options: { + readonly while: Predicate.Predicate + readonly body: (s: Z, a: A, i: number) => Effect.Effect + } +) => + core.flatMap( + core.sync(() => elements[Symbol.iterator]()), + (iterator) => reduceWhileLoop(iterator, 0, zero, options.while, options.body) + )) + +const reduceWhileLoop = ( + iterator: Iterator, + index: number, + state: Z, + predicate: Predicate.Predicate, + f: (s: Z, a: A, i: number) => Effect.Effect +): Effect.Effect => { + const next = iterator.next() + if (!next.done && predicate(state)) { + return core.flatMap( + f(state, next.value, index), + (nextState) => reduceWhileLoop(iterator, index + 1, nextState, predicate, f) + ) + } + return core.succeed(state) +} + +/* @internal */ +export const repeatN = dual< + (n: number) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, n: number) => Effect.Effect +>(2, (self, n) => core.suspend(() => repeatNLoop(self, n))) + +/* @internal */ +const repeatNLoop = (self: Effect.Effect, n: number): Effect.Effect => + core.flatMap(self, (a) => + n <= 0 + ? core.succeed(a) + : core.zipRight(core.yieldNow(), repeatNLoop(self, n - 1))) + +/* @internal */ +export const sandbox = (self: Effect.Effect): Effect.Effect, R> => + core.matchCauseEffect(self, { + onFailure: core.fail, + onSuccess: core.succeed + }) + +/* @internal */ +export const setFiberRefs = (fiberRefs: FiberRefs.FiberRefs): Effect.Effect => + core.suspend(() => FiberRefs.setAll(fiberRefs)) + +/* @internal */ +export const sleep: (duration: Duration.DurationInput) => Effect.Effect = Clock.sleep + +/* @internal */ +export const succeedNone: Effect.Effect> = core.succeed(Option.none()) + +/* @internal */ +export const succeedSome = (value: A): Effect.Effect> => core.succeed(Option.some(value)) + +/* @internal */ +export const summarized: { + ( + summary: Effect.Effect, + f: (start: B, end: B) => C + ): (self: Effect.Effect) => Effect.Effect<[C, A], E2 | E, R2 | R> + ( + self: Effect.Effect, + summary: Effect.Effect, + f: (start: B, end: B) => C + ): Effect.Effect<[C, A], E2 | E, R2 | R> +} = dual( + 3, + ( + self: Effect.Effect, + summary: Effect.Effect, + f: (start: B, end: B) => C + ): Effect.Effect<[C, A], E2 | E, R2 | R> => + core.flatMap( + summary, + (start) => core.flatMap(self, (value) => core.map(summary, (end) => [f(start, end), value])) + ) +) + +/* @internal */ +export const tagMetrics = dual< + { + (key: string, value: string): (effect: Effect.Effect) => Effect.Effect + ( + values: Record + ): (effect: Effect.Effect) => Effect.Effect + }, + { + (effect: Effect.Effect, key: string, value: string): Effect.Effect + (effect: Effect.Effect, values: Record): Effect.Effect + } +>((args) => core.isEffect(args[0]), function() { + return labelMetrics( + arguments[0], + typeof arguments[1] === "string" + ? [metricLabel.make(arguments[1], arguments[2])] + : Object.entries(arguments[1]).map(([k, v]) => metricLabel.make(k, v)) + ) +}) + +/* @internal */ +export const labelMetrics = dual< + (labels: Iterable) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, labels: Iterable) => Effect.Effect +>( + 2, + (self, labels) => core.fiberRefLocallyWith(self, core.currentMetricLabels, (old) => Arr.union(old, labels)) +) + +/* @internal */ +export const takeUntil: { + ( + predicate: (a: Types.NoInfer, i: number) => Effect.Effect + ): (elements: Iterable) => Effect.Effect, E, R> + ( + elements: Iterable, + predicate: (a: Types.NoInfer, i: number) => Effect.Effect + ): Effect.Effect, E, R> +} = dual( + 2, + ( + elements: Iterable, + predicate: (a: Types.NoInfer, i: number) => Effect.Effect + ): Effect.Effect, E, R> => + core.suspend(() => { + const iterator = elements[Symbol.iterator]() + const builder: Array = [] + let next: IteratorResult + let effect: Effect.Effect = core.succeed(false) + let i = 0 + while ((next = iterator.next()) && !next.done) { + const a = next.value + const index = i++ + effect = core.flatMap(effect, (bool) => { + if (bool) { + return core.succeed(true) + } + builder.push(a) + return predicate(a, index) + }) + } + return core.map(effect, () => builder) + }) +) + +/* @internal */ +export const takeWhile = dual< + ( + predicate: (a: Types.NoInfer, i: number) => Effect.Effect + ) => (elements: Iterable) => Effect.Effect, E, R>, + ( + elements: Iterable, + predicate: (a: Types.NoInfer, i: number) => Effect.Effect + ) => Effect.Effect, E, R> +>( + 2, + (elements: Iterable, predicate: (a: Types.NoInfer, i: number) => Effect.Effect) => + core.suspend(() => { + const iterator = elements[Symbol.iterator]() + const builder: Array = [] + let next: IteratorResult + let taking: Effect.Effect = core.succeed(true) + let i = 0 + while ((next = iterator.next()) && !next.done) { + const a = next.value + const index = i++ + taking = core.flatMap(taking, (taking) => + pipe( + taking ? predicate(a, index) : core.succeed(false), + core.map((bool) => { + if (bool) { + builder.push(a) + } + return bool + }) + )) + } + return core.map(taking, () => builder) + }) +) + +/* @internal */ +export const tapBoth = dual< + ( + options: { + readonly onFailure: (e: Types.NoInfer) => Effect.Effect + readonly onSuccess: (a: Types.NoInfer) => Effect.Effect + } + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + options: { + readonly onFailure: (e: E) => Effect.Effect + readonly onSuccess: (a: A) => Effect.Effect + } + ) => Effect.Effect +>(2, (self, { onFailure, onSuccess }) => + core.matchCauseEffect(self, { + onFailure: (cause) => { + const either = internalCause.failureOrCause(cause) + switch (either._tag) { + case "Left": { + return core.zipRight(onFailure(either.left as any), core.failCause(cause)) + } + case "Right": { + return core.failCause(cause) + } + } + }, + onSuccess: (a) => core.as(onSuccess(a as any), a) + })) + +/* @internal */ +export const tapDefect = dual< + ( + f: (cause: Cause.Cause) => Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + f: (cause: Cause.Cause) => Effect.Effect + ) => Effect.Effect +>(2, (self, f) => + core.catchAllCause(self, (cause) => + Option.match(internalCause.keepDefects(cause), { + onNone: () => core.failCause(cause), + onSome: (a) => core.zipRight(f(a), core.failCause(cause)) + }))) + +/* @internal */ +export const tapError = dual< + ( + f: (e: Types.NoInfer) => Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + f: (e: E) => Effect.Effect + ) => Effect.Effect +>(2, (self, f) => + core.matchCauseEffect(self, { + onFailure: (cause) => { + const either = internalCause.failureOrCause(cause) + switch (either._tag) { + case "Left": + return core.zipRight(f(either.left as any), core.failCause(cause)) + case "Right": + return core.failCause(cause) + } + }, + onSuccess: core.succeed + })) + +/* @internal */ +export const tapErrorTag = dual< + ( + k: K, + f: (e: Extract) => Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + k: K, + f: (e: Extract) => Effect.Effect + ) => Effect.Effect +>(3, (self, k, f) => + tapError(self, (e) => { + if (Predicate.isTagged(e, k)) { + return f(e as any) + } + return core.void as any + })) + +/* @internal */ +export const tapErrorCause = dual< + ( + f: (cause: Cause.Cause>) => Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + f: (cause: Cause.Cause) => Effect.Effect + ) => Effect.Effect +>(2, (self, f) => + core.matchCauseEffect(self, { + onFailure: (cause) => core.zipRight(f(cause), core.failCause(cause)), + onSuccess: core.succeed + })) + +/* @internal */ +export const timed = ( + self: Effect.Effect +): Effect.Effect<[duration: Duration.Duration, result: A], E, R> => timedWith(self, Clock.currentTimeNanos) + +/* @internal */ +export const timedWith = dual< + ( + nanoseconds: Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect<[Duration.Duration, A], E | E1, R | R1>, + ( + self: Effect.Effect, + nanoseconds: Effect.Effect + ) => Effect.Effect<[Duration.Duration, A], E | E1, R | R1> +>( + 2, + (self, nanos) => summarized(self, nanos, (start, end) => Duration.nanos(end - start)) +) + +/* @internal */ +export const tracerWith: (f: (tracer: Tracer.Tracer) => Effect.Effect) => Effect.Effect = + Tracer.tracerWith + +/** @internal */ +export const tracer: Effect.Effect = tracerWith(core.succeed) + +/* @internal */ +export const tryPromise: { + ( + options: { + readonly try: (signal: AbortSignal) => PromiseLike + readonly catch: (error: unknown) => E + } + ): Effect.Effect + (evaluate: (signal: AbortSignal) => PromiseLike): Effect.Effect +} = ( + arg: ((signal: AbortSignal) => PromiseLike) | { + readonly try: (signal: AbortSignal) => PromiseLike + readonly catch: (error: unknown) => E + } +): Effect.Effect => { + let evaluate: (signal?: AbortSignal) => PromiseLike + let catcher: ((error: unknown) => E) | undefined = undefined + if (typeof arg === "function") { + evaluate = arg as (signal?: AbortSignal) => PromiseLike + } else { + evaluate = arg.try as (signal?: AbortSignal) => PromiseLike + catcher = arg.catch + } + const fail = (e: unknown) => + catcher + ? core.failSync(() => catcher(e)) + : core.fail(new core.UnknownException(e, "An unknown error occurred in Effect.tryPromise")) + + if (evaluate.length >= 1) { + return core.async((resolve, signal) => { + try { + evaluate(signal).then( + (a) => resolve(core.succeed(a)), + (e) => resolve(fail(e)) + ) + } catch (e) { + resolve(fail(e)) + } + }) + } + + return core.async((resolve) => { + try { + evaluate() + .then( + (a) => resolve(core.succeed(a)), + (e) => resolve(fail(e)) + ) + } catch (e) { + resolve(fail(e)) + } + }) +} + +/* @internal */ +export const tryMap = dual< + ( + options: { + readonly try: (a: A) => B + readonly catch: (error: unknown) => E1 + } + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + options: { + readonly try: (a: A) => B + readonly catch: (error: unknown) => E1 + } + ) => Effect.Effect +>(2, (self, options) => + core.flatMap(self, (a) => + try_({ + try: () => options.try(a), + catch: options.catch + }))) + +/* @internal */ +export const tryMapPromise = dual< + ( + options: { + readonly try: (a: A, signal: AbortSignal) => PromiseLike + readonly catch: (error: unknown) => E1 + } + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + options: { + readonly try: (a: A, signal: AbortSignal) => PromiseLike + readonly catch: (error: unknown) => E1 + } + ) => Effect.Effect +>(2, ( + self: Effect.Effect, + options: { + readonly try: (a: A, signal: AbortSignal) => PromiseLike + readonly catch: (error: unknown) => E1 + } +) => + core.flatMap(self, (a) => + tryPromise({ + try: options.try.length >= 1 + ? (signal) => options.try(a, signal) + : () => (options.try as (a: A) => PromiseLike)(a), + catch: options.catch + }))) + +/* @internal */ +export const unless = dual< + (condition: LazyArg) => (self: Effect.Effect) => Effect.Effect, E, R>, + (self: Effect.Effect, condition: LazyArg) => Effect.Effect, E, R> +>(2, (self, condition) => + core.suspend(() => + condition() + ? succeedNone + : asSome(self) + )) + +/* @internal */ +export const unlessEffect = dual< + ( + condition: Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect, E | E2, R | R2>, + ( + self: Effect.Effect, + condition: Effect.Effect + ) => Effect.Effect, E | E2, R | R2> +>(2, (self, condition) => core.flatMap(condition, (b) => (b ? succeedNone : asSome(self)))) + +/* @internal */ +export const unsandbox = (self: Effect.Effect, R>) => + mapErrorCause(self, internalCause.flatten) + +/* @internal */ +export const updateFiberRefs = ( + f: (fiberId: FiberId.Runtime, fiberRefs: FiberRefs.FiberRefs) => FiberRefs.FiberRefs +): Effect.Effect => + core.withFiberRuntime((state) => { + state.setFiberRefs(f(state.id(), state.getFiberRefs())) + return core.void + }) + +/* @internal */ +export const updateService = dual< + ( + tag: Context.Tag, + f: (service: Types.NoInfer) => Types.NoInfer + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + tag: Context.Tag, + f: (service: Types.NoInfer) => Types.NoInfer + ) => Effect.Effect +>(3, ( + self: Effect.Effect, + tag: Context.Tag, + f: (service: Types.NoInfer) => Types.NoInfer +) => + core.mapInputContext(self, (context) => + Context.add( + context, + tag, + f(Context.unsafeGet(context, tag)) + )) as Effect.Effect) + +/* @internal */ +export const when = dual< + (condition: LazyArg) => (self: Effect.Effect) => Effect.Effect, E, R>, + (self: Effect.Effect, condition: LazyArg) => Effect.Effect, E, R> +>(2, (self, condition) => + core.suspend(() => + condition() + ? core.map(self, Option.some) + : core.succeed(Option.none()) + )) + +/* @internal */ +export const whenFiberRef = dual< + ( + fiberRef: FiberRef.FiberRef, + predicate: Predicate.Predicate + ) => (self: Effect.Effect) => Effect.Effect<[S, Option.Option], E, R>, + ( + self: Effect.Effect, + fiberRef: FiberRef.FiberRef, + predicate: Predicate.Predicate + ) => Effect.Effect<[S, Option.Option], E, R> +>( + 3, + ( + self: Effect.Effect, + fiberRef: FiberRef.FiberRef, + predicate: Predicate.Predicate + ) => + core.flatMap(core.fiberRefGet(fiberRef), (s) => + predicate(s) + ? core.map(self, (a) => [s, Option.some(a)]) + : core.succeed<[S, Option.Option]>([s, Option.none()])) +) + +/* @internal */ +export const whenRef = dual< + ( + ref: Ref.Ref, + predicate: Predicate.Predicate + ) => (self: Effect.Effect) => Effect.Effect<[S, Option.Option], E, R>, + ( + self: Effect.Effect, + ref: Ref.Ref, + predicate: Predicate.Predicate + ) => Effect.Effect<[S, Option.Option], E, R> +>( + 3, + (self: Effect.Effect, ref: Ref.Ref, predicate: Predicate.Predicate) => + core.flatMap(Ref.get(ref), (s) => + predicate(s) + ? core.map(self, (a) => [s, Option.some(a)]) + : core.succeed<[S, Option.Option]>([s, Option.none()])) +) + +/* @internal */ +export const withMetric = dual< + ( + metric: Metric.Metric + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + metric: Metric.Metric + ) => Effect.Effect +>(2, (self, metric) => metric(self)) + +/** @internal */ +export const serviceFunctionEffect = , Args extends Array, A, E, R>( + getService: T, + f: (_: Effect.Effect.Success) => (...args: Args) => Effect.Effect +) => +(...args: Args): Effect.Effect, R | Effect.Effect.Context> => + core.flatMap(getService, (a) => f(a)(...args)) + +/** @internal */ +export const serviceFunction = , Args extends Array, A>( + getService: T, + f: (_: Effect.Effect.Success) => (...args: Args) => A +) => +(...args: Args): Effect.Effect, Effect.Effect.Context> => + core.map(getService, (a) => f(a)(...args)) + +/** @internal */ +export const serviceFunctions = ( + getService: Effect.Effect +): { + [k in keyof S as S[k] extends (...args: Array) => Effect.Effect ? k : never]: S[k] extends + (...args: infer Args) => Effect.Effect + ? (...args: Args) => Effect.Effect + : never +} => + new Proxy({} as any, { + get(_target: any, prop: any, _receiver) { + return (...args: Array) => core.flatMap(getService, (s: any) => s[prop](...args)) + } + }) + +/** @internal */ +export const serviceConstants = ( + getService: Effect.Effect +): { + [k in { [k in keyof S]: k }[keyof S]]: S[k] extends Effect.Effect ? + Effect.Effect : + Effect.Effect +} => + new Proxy({} as any, { + get(_target: any, prop: any, _receiver) { + return core.flatMap(getService, (s: any) => core.isEffect(s[prop]) ? s[prop] : core.succeed(s[prop])) + } + }) + +/** @internal */ +export const serviceMembers = (getService: Effect.Effect): { + functions: { + [k in keyof S as S[k] extends (...args: Array) => Effect.Effect ? k : never]: S[k] extends + (...args: infer Args) => Effect.Effect + ? (...args: Args) => Effect.Effect + : never + } + constants: { + [k in { [k in keyof S]: k }[keyof S]]: S[k] extends Effect.Effect ? + Effect.Effect : + Effect.Effect + } +} => ({ + functions: serviceFunctions(getService) as any, + constants: serviceConstants(getService) +}) + +/** @internal */ +export const serviceOption = (tag: Context.Tag) => core.map(core.context(), Context.getOption(tag)) + +/** @internal */ +export const serviceOptional = (tag: Context.Tag) => + core.flatMap(core.context(), Context.getOption(tag)) + +// ----------------------------------------------------------------------------- +// tracing +// ----------------------------------------------------------------------------- + +/* @internal */ +export const annotateCurrentSpan: { + (key: string, value: unknown): Effect.Effect + (values: Record): Effect.Effect +} = function(): Effect.Effect { + const args = arguments + return ignore(core.flatMap( + currentPropagatedSpan, + (span) => + core.sync(() => { + if (typeof args[0] === "string") { + span.attribute(args[0], args[1]) + } else { + for (const key in args[0]) { + span.attribute(key, args[0][key]) + } + } + }) + )) +} + +/* @internal */ +export const linkSpanCurrent: { + (span: Tracer.AnySpan, attributes?: Readonly> | undefined): Effect.Effect + (links: ReadonlyArray): Effect.Effect +} = function(): Effect.Effect { + const args = arguments + const links: ReadonlyArray = Array.isArray(args[0]) + ? args[0] + : [{ _tag: "SpanLink", span: args[0], attributes: args[1] ?? {} }] + return ignore(core.flatMap( + currentSpan, + (span) => core.sync(() => span.addLinks(links)) + )) +} + +/* @internal */ +export const annotateSpans = dual< + { + (key: string, value: unknown): (effect: Effect.Effect) => Effect.Effect + ( + values: Record + ): (effect: Effect.Effect) => Effect.Effect + }, + { + (effect: Effect.Effect, key: string, value: unknown): Effect.Effect + (effect: Effect.Effect, values: Record): Effect.Effect + } +>( + (args) => core.isEffect(args[0]), + function() { + const args = arguments + return core.fiberRefLocallyWith( + args[0] as Effect.Effect, + core.currentTracerSpanAnnotations, + typeof args[1] === "string" + ? HashMap.set(args[1], args[2]) + : (annotations) => + Object.entries(args[1] as Record).reduce( + (acc, [key, value]) => HashMap.set(acc, key, value), + annotations + ) + ) + } +) + +/** @internal */ +export const currentParentSpan: Effect.Effect = serviceOptional( + internalTracer.spanTag +) + +/** @internal */ +export const currentSpan: Effect.Effect = core.flatMap( + core.context(), + (context) => { + const span = context.unsafeMap.get(internalTracer.spanTag.key) as Tracer.AnySpan | undefined + return span !== undefined && span._tag === "Span" + ? core.succeed(span) + : core.fail(new core.NoSuchElementException()) + } +) + +export const currentPropagatedSpan: Effect.Effect = core.flatMap( + core.context(), + (context) => { + const span = filterDisablePropagation(Context.getOption(context, internalTracer.spanTag)) + return span._tag === "Some" && span.value._tag === "Span" + ? core.succeed(span.value) + : core.fail(new core.NoSuchElementException()) + } +) + +/* @internal */ +export const linkSpans = dual< + ( + span: Tracer.AnySpan, + attributes?: Record + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + span: Tracer.AnySpan, + attributes?: Record + ) => Effect.Effect +>( + (args) => core.isEffect(args[0]), + (self, span, attributes) => + core.fiberRefLocallyWith( + self, + core.currentTracerSpanLinks, + Chunk.append( + { + _tag: "SpanLink", + span, + attributes: attributes ?? {} + } as const + ) + ) +) + +const bigint0 = BigInt(0) + +export const filterDisablePropagation: (self: Option.Option) => Option.Option = Option + .flatMap( + (span) => + Context.get(span.context, internalTracer.DisablePropagation) + ? span._tag === "Span" ? filterDisablePropagation(span.parent) : Option.none() + : Option.some(span) + ) + +/** @internal */ +export const unsafeMakeSpan = ( + fiber: FiberRuntime, + name: string, + options: Tracer.SpanOptions +) => { + const disablePropagation = !fiber.getFiberRef(core.currentTracerEnabled) || + (options.context && Context.get(options.context, internalTracer.DisablePropagation)) + const context = fiber.getFiberRef(core.currentContext) + const parent = options.parent + ? Option.some(options.parent) + : options.root + ? Option.none() + : filterDisablePropagation(Context.getOption(context, internalTracer.spanTag)) + + let span: Tracer.Span + + if (disablePropagation) { + span = core.noopSpan({ + name, + parent, + context: Context.add(options.context ?? Context.empty(), internalTracer.DisablePropagation, true) + }) + } else { + const services = fiber.getFiberRef(defaultServices.currentServices) + + const tracer = Context.get(services, internalTracer.tracerTag) + const clock = Context.get(services, Clock.Clock) + const timingEnabled = fiber.getFiberRef(core.currentTracerTimingEnabled) + + const fiberRefs = fiber.getFiberRefs() + const annotationsFromEnv = FiberRefs.get(fiberRefs, core.currentTracerSpanAnnotations) + const linksFromEnv = FiberRefs.get(fiberRefs, core.currentTracerSpanLinks) + + const links = linksFromEnv._tag === "Some" ? + options.links !== undefined ? + [ + ...Chunk.toReadonlyArray(linksFromEnv.value), + ...(options.links ?? []) + ] : + Chunk.toReadonlyArray(linksFromEnv.value) : + options.links ?? Arr.empty() + + span = tracer.span( + name, + parent, + options.context ?? Context.empty(), + links, + timingEnabled ? clock.unsafeCurrentTimeNanos() : bigint0, + options.kind ?? "internal", + options + ) + + if (annotationsFromEnv._tag === "Some") { + HashMap.forEach(annotationsFromEnv.value, (value, key) => span.attribute(key, value)) + } + if (options.attributes !== undefined) { + Object.entries(options.attributes).forEach(([k, v]) => span.attribute(k, v)) + } + } + + if (typeof options.captureStackTrace === "function") { + internalCause.spanToTrace.set(span, options.captureStackTrace) + } + + return span +} + +/** @internal */ +export const makeSpan = ( + name: string, + options?: Tracer.SpanOptions +): Effect.Effect => { + options = internalTracer.addSpanStackTrace(options) + return core.withFiberRuntime((fiber) => core.succeed(unsafeMakeSpan(fiber, name, options))) +} + +/* @internal */ +export const spanAnnotations: Effect.Effect> = core + .fiberRefGet(core.currentTracerSpanAnnotations) + +/* @internal */ +export const spanLinks: Effect.Effect> = core + .fiberRefGet(core.currentTracerSpanLinks) + +/** @internal */ +export const endSpan = (span: Tracer.Span, exit: Exit, clock: Clock.Clock, timingEnabled: boolean) => + core.sync(() => { + if (span.status._tag === "Ended") { + return + } + if (core.exitIsFailure(exit) && internalCause.spanToTrace.has(span)) { + // https://opentelemetry.io/docs/specs/semconv/registry/attributes/code/#code-stacktrace + span.attribute("code.stacktrace", internalCause.spanToTrace.get(span)!()) + } + span.end(timingEnabled ? clock.unsafeCurrentTimeNanos() : bigint0, exit) + }) + +/** @internal */ +export const useSpan: { + (name: string, evaluate: (span: Tracer.Span) => Effect.Effect): Effect.Effect + ( + name: string, + options: Tracer.SpanOptions, + evaluate: (span: Tracer.Span) => Effect.Effect + ): Effect.Effect +} = ( + name: string, + ...args: [evaluate: (span: Tracer.Span) => Effect.Effect] | [ + options: any, + evaluate: (span: Tracer.Span) => Effect.Effect + ] +) => { + const options = internalTracer.addSpanStackTrace(args.length === 1 ? undefined : args[0]) + const evaluate: (span: Tracer.Span) => Effect.Effect = args[args.length - 1] + + return core.withFiberRuntime((fiber) => { + const span = unsafeMakeSpan(fiber, name, options) + const timingEnabled = fiber.getFiberRef(core.currentTracerTimingEnabled) + const clock = Context.get(fiber.getFiberRef(defaultServices.currentServices), clockTag) + return core.onExit(evaluate(span), (exit) => endSpan(span, exit, clock, timingEnabled)) + }) +} + +/** @internal */ +export const withParentSpan = dual< + ( + span: Tracer.AnySpan + ) => (self: Effect.Effect) => Effect.Effect>, + (self: Effect.Effect, span: Tracer.AnySpan) => Effect.Effect> +>(2, (self, span) => provideService(self, internalTracer.spanTag, span)) + +/** @internal */ +export const withSpan: { + ( + name: string, + options?: Tracer.SpanOptions | undefined + ): (self: Effect.Effect) => Effect.Effect> + ( + self: Effect.Effect, + name: string, + options?: Tracer.SpanOptions | undefined + ): Effect.Effect> +} = function() { + const dataFirst = typeof arguments[0] !== "string" + const name = dataFirst ? arguments[1] : arguments[0] + const options = internalTracer.addSpanStackTrace(dataFirst ? arguments[2] : arguments[1]) + if (dataFirst) { + const self = arguments[0] + return useSpan(name, options, (span) => withParentSpan(self, span)) + } + return (self: Effect.Effect) => useSpan(name, options, (span) => withParentSpan(self, span)) +} as any + +export const functionWithSpan = , Ret extends Effect.Effect>( + options: { + readonly body: (...args: Args) => Ret + readonly options: Effect.FunctionWithSpanOptions | ((...args: Args) => Effect.FunctionWithSpanOptions) + readonly captureStackTrace?: boolean | undefined + } +): (...args: Args) => Unify => + (function(this: any) { + let captureStackTrace: LazyArg | boolean = options.captureStackTrace ?? false + if (options.captureStackTrace !== false) { + const limit = Error.stackTraceLimit + Error.stackTraceLimit = 2 + const error = new Error() + Error.stackTraceLimit = limit + let cache: false | string = false + captureStackTrace = () => { + if (cache !== false) { + return cache + } + if (error.stack) { + const stack = error.stack.trim().split("\n") + cache = stack.slice(2).join("\n").trim() + return cache + } + } + } + return core.suspend(() => { + const opts = typeof options.options === "function" + ? options.options.apply(null, arguments as any) + : options.options + return withSpan( + core.suspend(() => internalCall(() => options.body.apply(this, arguments as any))), + opts.name, + { + ...opts, + captureStackTrace + } + ) + }) + }) as any + +// ------------------------------------------------------------------------------------- +// optionality +// ------------------------------------------------------------------------------------- + +/* @internal */ +export const fromNullable = (value: A): Effect.Effect, Cause.NoSuchElementException> => + value == null ? core.fail(new core.NoSuchElementException()) : core.succeed(value as NonNullable) + +/* @internal */ +export const optionFromOptional = ( + self: Effect.Effect +): Effect.Effect, Exclude, R> => + core.catchAll( + core.map(self, Option.some), + (error) => + core.isNoSuchElementException(error) ? + succeedNone : + core.fail(error as Exclude) + ) diff --git a/backend/node_modules/effect/src/internal/core-stream.ts b/backend/node_modules/effect/src/internal/core-stream.ts new file mode 100644 index 0000000000000000000000000000000000000000..ebec63ff5cbd54841ef79d66758112611038bd1a --- /dev/null +++ b/backend/node_modules/effect/src/internal/core-stream.ts @@ -0,0 +1,998 @@ +import * as Cause from "../Cause.js" +import type * as Channel from "../Channel.js" +import type * as ChildExecutorDecision from "../ChildExecutorDecision.js" +import * as Chunk from "../Chunk.js" +import type * as Context from "../Context.js" +import * as Effect from "../Effect.js" +import * as Either from "../Either.js" +import type * as Exit from "../Exit.js" +import { constVoid, dual, identity } from "../Function.js" +import type { LazyArg } from "../Function.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty } from "../Predicate.js" +import type * as SingleProducerAsyncInput from "../SingleProducerAsyncInput.js" +import type * as UpstreamPullRequest from "../UpstreamPullRequest.js" +import type * as UpstreamPullStrategy from "../UpstreamPullStrategy.js" +import * as childExecutorDecision from "./channel/childExecutorDecision.js" +import type { ErasedContinuationK } from "./channel/continuation.js" +import { ContinuationKImpl } from "./channel/continuation.js" +import * as upstreamPullStrategy from "./channel/upstreamPullStrategy.js" +import * as OpCodes from "./opCodes/channel.js" + +/** @internal */ +const ChannelSymbolKey = "effect/Channel" + +/** @internal */ +export const ChannelTypeId: Channel.ChannelTypeId = Symbol.for( + ChannelSymbolKey +) as Channel.ChannelTypeId + +const channelVariance = { + /* c8 ignore next */ + _Env: (_: never) => _, + /* c8 ignore next */ + _InErr: (_: unknown) => _, + /* c8 ignore next */ + _InElem: (_: unknown) => _, + /* c8 ignore next */ + _InDone: (_: unknown) => _, + /* c8 ignore next */ + _OutErr: (_: never) => _, + /* c8 ignore next */ + _OutElem: (_: never) => _, + /* c8 ignore next */ + _OutDone: (_: never) => _ +} + +/** @internal */ +const proto = { + [ChannelTypeId]: channelVariance, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +type ErasedChannel = Channel.Channel + +/** @internal */ +export type Op = + & ErasedChannel + & Body + & { readonly _tag: Tag } + +export type Primitive = + | BracketOut + | Bridge + | ConcatAll + | Emit + | Ensuring + | Fail + | Fold + | FromEffect + | PipeTo + | Provide + | Read + | Succeed + | SucceedNow + | Suspend + +/** @internal */ +export interface BracketOut extends + Op + finalizer(resource: unknown, exit: Exit.Exit): Effect.Effect + }> +{} + +/** @internal */ +export interface Bridge extends + Op + readonly channel: ErasedChannel + }> +{} + +/** @internal */ +export interface ConcatAll extends + Op + ): UpstreamPullStrategy.UpstreamPullStrategy + onEmit(outElem: unknown): ChildExecutorDecision.ChildExecutorDecision + value(): ErasedChannel + k(outElem: unknown): ErasedChannel + }> +{} + +/** @internal */ +export interface Emit extends + Op +{} + +/** @internal */ +export interface Ensuring extends + Op): Effect.Effect + }> +{} + +/** @internal */ +export interface Fail extends + Op + }> +{} + +/** @internal */ +export interface Fold extends + Op +{} + +/** @internal */ +export interface FromEffect extends + Op + }> +{} + +/** @internal */ +export interface PipeTo extends + Op +{} + +/** @internal */ +export interface Provide extends + Op + readonly inner: ErasedChannel + }> +{} + +/** @internal */ +export interface Read extends + Op +{} + +/** @internal */ +export interface Succeed extends + Op +{} + +/** @internal */ +export interface SucceedNow extends + Op +{} + +/** @internal */ +export interface Suspend extends + Op +{} + +/** @internal */ +export const isChannel = (u: unknown): u is Channel.Channel< + unknown, + unknown, + unknown, + unknown, + unknown, + unknown, + unknown +> => hasProperty(u, ChannelTypeId) || Effect.isEffect(u) + +/** @internal */ +export const acquireReleaseOut = dual< + ( + release: (z: Z, e: Exit.Exit) => Effect.Effect + ) => (self: Effect.Effect) => Channel.Channel, + ( + self: Effect.Effect, + release: (z: Z, e: Exit.Exit) => Effect.Effect + ) => Channel.Channel +>(2, (self, release) => { + const op = Object.create(proto) + op._tag = OpCodes.OP_BRACKET_OUT + op.acquire = () => self + op.finalizer = release + return op +}) + +/** @internal */ +export const catchAllCause = dual< + ( + f: (cause: Cause.Cause) => Channel.Channel + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone1 | OutDone, + InDone & InDone1, + Env1 | Env + >, + ( + self: Channel.Channel, + f: (cause: Cause.Cause) => Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone1 | OutDone, + InDone & InDone1, + Env1 | Env + > +>( + 2, + ( + self: Channel.Channel, + f: (cause: Cause.Cause) => Channel.Channel + ): Channel.Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr1, + InErr & InErr1, + OutDone | OutDone1, + InDone & InDone1, + Env | Env1 + > => { + const op = Object.create(proto) + op._tag = OpCodes.OP_FOLD + op.channel = self + op.k = new ContinuationKImpl(succeed, f) + return op + } +) + +/** @internal */ +export const collectElements = ( + self: Channel.Channel +): Channel.Channel, OutDone], InDone, Env> => { + return suspend(() => { + const builder: Array = [] + return flatMap( + pipeTo(self, collectElementsReader(builder)), + (value) => sync(() => [Chunk.fromIterable(builder), value]) + ) + }) +} + +/** @internal */ +const collectElementsReader = ( + builder: Array +): Channel.Channel => + readWith({ + onInput: (outElem) => + flatMap( + sync(() => { + builder.push(outElem) + }), + () => collectElementsReader(builder) + ), + onFailure: fail, + onDone: succeedNow + }) + +/** @internal */ +export const concatAll = ( + channels: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + any, + InDone, + Env + > +): Channel.Channel => concatAllWith(channels, constVoid, constVoid) + +/** @internal */ +export const concatAllWith = < + OutElem, + InElem2, + OutErr2, + InErr2, + OutDone, + InDone2, + Env2, + InElem, + OutErr, + InErr, + OutDone2, + InDone, + Env, + OutDone3 +>( + channels: Channel.Channel< + Channel.Channel, + InElem, + OutErr, + InErr, + OutDone2, + InDone, + Env + >, + f: (o: OutDone, o1: OutDone) => OutDone, + g: (o: OutDone, o2: OutDone2) => OutDone3 +): Channel.Channel< + OutElem, + InElem & InElem2, + OutErr | OutErr2, + InErr & InErr2, + OutDone3, + InDone & InDone2, + Env | Env2 +> => { + const op = Object.create(proto) + op._tag = OpCodes.OP_CONCAT_ALL + op.combineInners = f + op.combineAll = g + op.onPull = () => upstreamPullStrategy.PullAfterNext(Option.none()) + op.onEmit = () => childExecutorDecision.Continue + op.value = () => channels + op.k = identity + return op +} + +/** @internal */ +export const concatMapWith = dual< + ( + f: (o: OutElem) => Channel.Channel, + g: (o: OutDone, o1: OutDone) => OutDone, + h: (o: OutDone, o2: OutDone2) => OutDone3 + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem2, + InElem & InElem2, + OutErr2 | OutErr, + InErr & InErr2, + OutDone3, + InDone & InDone2, + Env2 | Env + >, + < + OutElem, + InElem, + OutErr, + InErr, + OutDone2, + InDone, + Env, + OutElem2, + InElem2, + OutErr2, + InErr2, + OutDone, + InDone2, + Env2, + OutDone3 + >( + self: Channel.Channel, + f: (o: OutElem) => Channel.Channel, + g: (o: OutDone, o1: OutDone) => OutDone, + h: (o: OutDone, o2: OutDone2) => OutDone3 + ) => Channel.Channel< + OutElem2, + InElem & InElem2, + OutErr2 | OutErr, + InErr & InErr2, + OutDone3, + InDone & InDone2, + Env2 | Env + > +>(4, < + Env, + InErr, + InElem, + InDone, + OutErr, + OutElem, + OutElem2, + OutDone, + OutDone2, + OutDone3, + Env2, + InErr2, + InElem2, + InDone2, + OutErr2 +>( + self: Channel.Channel, + f: ( + o: OutElem + ) => Channel.Channel, + g: (o: OutDone, o1: OutDone) => OutDone, + h: (o: OutDone, o2: OutDone2) => OutDone3 +): Channel.Channel< + OutElem2, + InElem & InElem2, + OutErr | OutErr2, + InErr & InErr2, + OutDone3, + InDone & InDone2, + Env | Env2 +> => { + const op = Object.create(proto) + op._tag = OpCodes.OP_CONCAT_ALL + op.combineInners = g + op.combineAll = h + op.onPull = () => upstreamPullStrategy.PullAfterNext(Option.none()) + op.onEmit = () => childExecutorDecision.Continue + op.value = () => self + op.k = f + return op +}) + +/** @internal */ +export const concatMapWithCustom = dual< + ( + f: (o: OutElem) => Channel.Channel, + g: (o: OutDone, o1: OutDone) => OutDone, + h: (o: OutDone, o2: OutDone2) => OutDone3, + onPull: ( + upstreamPullRequest: UpstreamPullRequest.UpstreamPullRequest + ) => UpstreamPullStrategy.UpstreamPullStrategy, + onEmit: (elem: OutElem2) => ChildExecutorDecision.ChildExecutorDecision + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem2, + InElem & InElem2, + OutErr2 | OutErr, + InErr & InErr2, + OutDone3, + InDone & InDone2, + Env2 | Env + >, + < + OutElem, + InElem, + OutErr, + InErr, + OutDone2, + InDone, + Env, + OutElem2, + InElem2, + OutErr2, + InErr2, + OutDone, + InDone2, + Env2, + OutDone3 + >( + self: Channel.Channel, + f: (o: OutElem) => Channel.Channel, + g: (o: OutDone, o1: OutDone) => OutDone, + h: (o: OutDone, o2: OutDone2) => OutDone3, + onPull: ( + upstreamPullRequest: UpstreamPullRequest.UpstreamPullRequest + ) => UpstreamPullStrategy.UpstreamPullStrategy, + onEmit: (elem: OutElem2) => ChildExecutorDecision.ChildExecutorDecision + ) => Channel.Channel< + OutElem2, + InElem & InElem2, + OutErr2 | OutErr, + InErr & InErr2, + OutDone3, + InDone & InDone2, + Env2 | Env + > +>(6, < + Env, + InErr, + InElem, + InDone, + OutErr, + OutElem, + OutElem2, + OutDone, + OutDone2, + OutDone3, + Env2, + InErr2, + InElem2, + InDone2, + OutErr2 +>( + self: Channel.Channel, + f: ( + o: OutElem + ) => Channel.Channel, + g: (o: OutDone, o1: OutDone) => OutDone, + h: (o: OutDone, o2: OutDone2) => OutDone3, + onPull: ( + upstreamPullRequest: UpstreamPullRequest.UpstreamPullRequest + ) => UpstreamPullStrategy.UpstreamPullStrategy, + onEmit: (elem: OutElem2) => ChildExecutorDecision.ChildExecutorDecision +): Channel.Channel< + OutElem2, + InElem & InElem2, + OutErr | OutErr2, + InErr & InErr2, + OutDone3, + InDone & InDone2, + Env | Env2 +> => { + const op = Object.create(proto) + op._tag = OpCodes.OP_CONCAT_ALL + op.combineInners = g + op.combineAll = h + op.onPull = onPull + op.onEmit = onEmit + op.value = () => self + op.k = f + return op +}) + +/** @internal */ +export const embedInput = dual< + ( + input: SingleProducerAsyncInput.AsyncInputProducer + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + input: SingleProducerAsyncInput.AsyncInputProducer + ) => Channel.Channel +>( + 2, + ( + self: Channel.Channel, + input: SingleProducerAsyncInput.AsyncInputProducer + ): Channel.Channel => { + const op = Object.create(proto) + op._tag = OpCodes.OP_BRIDGE + op.input = input + op.channel = self + return op + } +) + +/** @internal */ +export const ensuringWith = dual< + ( + finalizer: (e: Exit.Exit) => Effect.Effect + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + finalizer: (e: Exit.Exit) => Effect.Effect + ) => Channel.Channel +>( + 2, + ( + self: Channel.Channel, + finalizer: (e: Exit.Exit) => Effect.Effect + ): Channel.Channel => { + const op = Object.create(proto) + op._tag = OpCodes.OP_ENSURING + op.channel = self + op.finalizer = finalizer + return op + } +) + +/** @internal */ +export const fail = (error: E): Channel.Channel => + failCause(Cause.fail(error)) + +/** @internal */ +export const failSync = ( + evaluate: LazyArg +): Channel.Channel => failCauseSync(() => Cause.fail(evaluate())) + +/** @internal */ +export const failCause = ( + cause: Cause.Cause +): Channel.Channel => failCauseSync(() => cause) + +/** @internal */ +export const failCauseSync = ( + evaluate: LazyArg> +): Channel.Channel => { + const op = Object.create(proto) + op._tag = OpCodes.OP_FAIL + op.error = evaluate + return op +} + +/** @internal */ +export const flatMap = dual< + ( + f: (d: OutDone) => Channel.Channel + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + OutDone2, + InDone & InDone1, + Env1 | Env + >, + ( + self: Channel.Channel, + f: (d: OutDone) => Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem, + InElem & InElem1, + OutErr1 | OutErr, + InErr & InErr1, + OutDone2, + InDone & InDone1, + Env1 | Env + > +>( + 2, + ( + self: Channel.Channel, + f: (d: OutDone) => Channel.Channel + ): Channel.Channel< + OutElem | OutElem1, + InElem & InElem1, + OutErr | OutErr1, + InErr & InErr1, + OutDone2, + InDone & InDone1, + Env | Env1 + > => { + const op = Object.create(proto) + op._tag = OpCodes.OP_FOLD + op.channel = self + op.k = new ContinuationKImpl(f, failCause) + return op + } +) + +/** @internal */ +export const foldCauseChannel = dual< + < + OutErr, + OutElem1, + InElem1, + OutErr2, + InErr1, + OutDone2, + InDone1, + Env1, + OutDone, + OutElem2, + InElem2, + OutErr3, + InErr2, + OutDone3, + InDone2, + Env2 + >( + options: { + readonly onFailure: ( + c: Cause.Cause + ) => Channel.Channel + readonly onSuccess: (o: OutDone) => Channel.Channel + } + ) => ( + self: Channel.Channel + ) => Channel.Channel< + OutElem1 | OutElem2 | OutElem, + InElem & InElem1 & InElem2, + OutErr2 | OutErr3, + InErr & InErr1 & InErr2, + OutDone2 | OutDone3, + InDone & InDone1 & InDone2, + Env1 | Env2 | Env + >, + < + OutElem, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem1, + InElem1, + OutErr2, + InErr1, + OutDone2, + InDone1, + Env1, + OutElem2, + InElem2, + OutErr3, + InErr2, + OutDone3, + InDone2, + Env2 + >( + self: Channel.Channel, + options: { + readonly onFailure: ( + c: Cause.Cause + ) => Channel.Channel + readonly onSuccess: (o: OutDone) => Channel.Channel + } + ) => Channel.Channel< + OutElem1 | OutElem2 | OutElem, + InElem & InElem1 & InElem2, + OutErr2 | OutErr3, + InErr & InErr1 & InErr2, + OutDone2 | OutDone3, + InDone & InDone1 & InDone2, + Env1 | Env2 | Env + > +>( + 2, + < + OutElem, + InElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem1, + InElem1, + OutErr2, + InErr1, + OutDone2, + InDone1, + Env1, + OutElem2, + InElem2, + OutErr3, + InErr2, + OutDone3, + InDone2, + Env2 + >( + self: Channel.Channel, + options: { + readonly onFailure: ( + c: Cause.Cause + ) => Channel.Channel + readonly onSuccess: (o: OutDone) => Channel.Channel + } + ): Channel.Channel< + OutElem | OutElem1 | OutElem2, + InElem & InElem1 & InElem2, + OutErr2 | OutErr3, + InErr & InErr1 & InErr2, + OutDone2 | OutDone3, + InDone & InDone1 & InDone2, + Env | Env1 | Env2 + > => { + const op = Object.create(proto) + op._tag = OpCodes.OP_FOLD + op.channel = self + op.k = new ContinuationKImpl(options.onSuccess, options.onFailure as any) + return op + } +) + +/** @internal */ +export const fromEffect = ( + effect: Effect.Effect +): Channel.Channel => { + const op = Object.create(proto) + op._tag = OpCodes.OP_FROM_EFFECT + op.effect = () => effect + return op +} + +/** @internal */ +export const pipeTo = dual< + ( + that: Channel.Channel + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + that: Channel.Channel + ) => Channel.Channel +>( + 2, + ( + self: Channel.Channel, + that: Channel.Channel + ): Channel.Channel => { + const op = Object.create(proto) + op._tag = OpCodes.OP_PIPE_TO + op.left = () => self + op.right = () => that + return op + } +) + +/** @internal */ +export const provideContext = dual< + ( + env: Context.Context + ) => ( + self: Channel.Channel + ) => Channel.Channel, + ( + self: Channel.Channel, + env: Context.Context + ) => Channel.Channel +>( + 2, + ( + self: Channel.Channel, + env: Context.Context + ): Channel.Channel => { + const op = Object.create(proto) + op._tag = OpCodes.OP_PROVIDE + op.context = () => env + op.inner = self + return op + } +) + +/** @internal */ +export const readOrFail = ( + error: E +): Channel.Channel => { + const op = Object.create(proto) + op._tag = OpCodes.OP_READ + op.more = succeed + op.done = new ContinuationKImpl(() => fail(error), () => fail(error)) + return op +} + +/** @internal */ +export const readWith = < + InElem, + OutElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem2, + OutErr2, + OutDone2, + Env2, + OutElem3, + OutErr3, + OutDone3, + Env3 +>( + options: { + readonly onInput: (input: InElem) => Channel.Channel + readonly onFailure: (error: InErr) => Channel.Channel + readonly onDone: (done: InDone) => Channel.Channel + } +): Channel.Channel< + OutElem | OutElem2 | OutElem3, + InElem, + OutErr | OutErr2 | OutErr3, + InErr, + OutDone | OutDone2 | OutDone3, + InDone, + Env | Env2 | Env3 +> => + readWithCause({ + onInput: options.onInput, + onFailure: (cause) => Either.match(Cause.failureOrCause(cause), { onLeft: options.onFailure, onRight: failCause }), + onDone: options.onDone + }) + +/** @internal */ +export const readWithCause = < + InElem, + OutElem, + OutErr, + InErr, + OutDone, + InDone, + Env, + OutElem2, + OutErr2, + OutDone2, + Env2, + OutElem3, + OutErr3, + OutDone3, + Env3 +>( + options: { + readonly onInput: (input: InElem) => Channel.Channel + readonly onFailure: ( + cause: Cause.Cause + ) => Channel.Channel + readonly onDone: (done: InDone) => Channel.Channel + } +): Channel.Channel< + OutElem | OutElem2 | OutElem3, + InElem, + OutErr | OutErr2 | OutErr3, + InErr, + OutDone | OutDone2 | OutDone3, + InDone, + Env | Env2 | Env3 +> => { + const op = Object.create(proto) + op._tag = OpCodes.OP_READ + op.more = options.onInput + op.done = new ContinuationKImpl(options.onDone, options.onFailure as any) + return op +} + +/** @internal */ +export const succeed = ( + value: A +): Channel.Channel => sync(() => value) + +/** @internal */ +export const succeedNow = ( + result: OutDone +): Channel.Channel => { + const op = Object.create(proto) + op._tag = OpCodes.OP_SUCCEED_NOW + op.terminal = result + return op +} + +/** @internal */ +export const suspend = ( + evaluate: LazyArg> +): Channel.Channel => { + const op = Object.create(proto) + op._tag = OpCodes.OP_SUSPEND + op.channel = evaluate + return op +} + +export const sync = ( + evaluate: LazyArg +): Channel.Channel => { + const op = Object.create(proto) + op._tag = OpCodes.OP_SUCCEED + op.evaluate = evaluate + return op +} + +const void_: Channel.Channel = succeedNow(void 0) +export { + /** @internal */ + void_ as void +} + +/** @internal */ +export const write = ( + out: OutElem +): Channel.Channel => { + const op = Object.create(proto) + op._tag = OpCodes.OP_EMIT + op.out = out + return op +} diff --git a/backend/node_modules/effect/src/internal/core.ts b/backend/node_modules/effect/src/internal/core.ts new file mode 100644 index 0000000000000000000000000000000000000000..55068f13f75fd66970747e46c7090a683bc5d2ba --- /dev/null +++ b/backend/node_modules/effect/src/internal/core.ts @@ -0,0 +1,3166 @@ +import * as Arr from "../Array.js" +import type * as Cause from "../Cause.js" +import * as Chunk from "../Chunk.js" +import * as Context from "../Context.js" +import type * as Deferred from "../Deferred.js" +import type * as Differ from "../Differ.js" +import * as Duration from "../Duration.js" +import type * as Effect from "../Effect.js" +import * as Either from "../Either.js" +import * as Equal from "../Equal.js" +import type * as ExecutionStrategy from "../ExecutionStrategy.js" +import type * as Exit from "../Exit.js" +import type * as Fiber from "../Fiber.js" +import * as FiberId from "../FiberId.js" +import type * as FiberRef from "../FiberRef.js" +import type * as FiberStatus from "../FiberStatus.js" +import type { LazyArg } from "../Function.js" +import { dual, identity, pipe } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import * as Hash from "../Hash.js" +import * as HashMap from "../HashMap.js" +import type * as HashSet from "../HashSet.js" +import { format, NodeInspectSymbol, toJSON } from "../Inspectable.js" +import * as List from "../List.js" +import type * as LogLevel from "../LogLevel.js" +import type * as LogSpan from "../LogSpan.js" +import type * as MetricLabel from "../MetricLabel.js" +import * as MutableRef from "../MutableRef.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty, isObject, isPromiseLike, type Predicate, type Refinement } from "../Predicate.js" +import type * as Request from "../Request.js" +import type * as BlockedRequests from "../RequestBlock.js" +import type * as RequestResolver from "../RequestResolver.js" +import type * as RuntimeFlags from "../RuntimeFlags.js" +import * as RuntimeFlagsPatch from "../RuntimeFlagsPatch.js" +import type * as Scope from "../Scope.js" +import type * as Tracer from "../Tracer.js" +import type { NoInfer, NotFunction } from "../Types.js" +import { internalCall, YieldWrap } from "../Utils.js" +import * as blockedRequests_ from "./blockedRequests.js" +import * as internalCause from "./cause.js" +import * as deferred from "./deferred.js" +import * as internalDiffer from "./differ.js" +import { CommitPrototype, effectVariance, StructuralCommitPrototype } from "./effectable.js" +import { getBugErrorMessage } from "./errors.js" +import type * as FiberRuntime from "./fiberRuntime.js" +import type * as fiberScope from "./fiberScope.js" +import * as DeferredOpCodes from "./opCodes/deferred.js" +import * as OpCodes from "./opCodes/effect.js" +import * as runtimeFlags_ from "./runtimeFlags.js" +import { SingleShotGen } from "./singleShotGen.js" + +// ----------------------------------------------------------------------------- +// Effect +// ----------------------------------------------------------------------------- + +/** + * @internal + */ +export const blocked = ( + blockedRequests: BlockedRequests.RequestBlock, + _continue: Effect.Effect +): Effect.Blocked => { + const effect = new EffectPrimitive("Blocked") as any + effect.effect_instruction_i0 = blockedRequests + effect.effect_instruction_i1 = _continue + return effect +} + +/** + * @internal + */ +export const runRequestBlock = ( + blockedRequests: BlockedRequests.RequestBlock +): Effect.Effect => { + const effect = new EffectPrimitive("RunBlocked") as any + effect.effect_instruction_i0 = blockedRequests + return effect +} + +/** @internal */ +export const EffectTypeId: Effect.EffectTypeId = Symbol.for("effect/Effect") as Effect.EffectTypeId + +/** @internal */ +export type Primitive = + | Async + | Commit + | Failure + | OnFailure + | OnSuccess + | OnStep + | OnSuccessAndFailure + | Success + | Sync + | UpdateRuntimeFlags + | While + | FromIterator + | WithRuntime + | Yield + | OpTag + | Blocked + | RunBlocked + | Either.Either + | Option.Option + +/** @internal */ +export type Continuation = + | OnSuccess + | OnStep + | OnSuccessAndFailure + | OnFailure + | While + | FromIterator + | RevertFlags + +/** @internal */ +export class RevertFlags { + readonly _op = OpCodes.OP_REVERT_FLAGS + constructor( + readonly patch: RuntimeFlagsPatch.RuntimeFlagsPatch, + readonly op: Primitive & { _op: OpCodes.OP_UPDATE_RUNTIME_FLAGS } + ) { + } +} + +class EffectPrimitive { + public effect_instruction_i0 = undefined + public effect_instruction_i1 = undefined + public effect_instruction_i2 = undefined + public trace = undefined; + [EffectTypeId] = effectVariance + constructor(readonly _op: Primitive["_op"]) {} + [Equal.symbol](this: {}, that: unknown) { + return this === that + } + [Hash.symbol](this: {}) { + return Hash.cached(this, Hash.random(this)) + } + pipe() { + return pipeArguments(this, arguments) + } + toJSON() { + return { + _id: "Effect", + _op: this._op, + effect_instruction_i0: toJSON(this.effect_instruction_i0), + effect_instruction_i1: toJSON(this.effect_instruction_i1), + effect_instruction_i2: toJSON(this.effect_instruction_i2) + } + } + toString() { + return format(this.toJSON()) + } + [NodeInspectSymbol]() { + return this.toJSON() + } + [Symbol.iterator]() { + return new SingleShotGen(new YieldWrap(this)) + } +} + +/** @internal */ +class EffectPrimitiveFailure { + public effect_instruction_i0 = undefined + public effect_instruction_i1 = undefined + public effect_instruction_i2 = undefined + public trace = undefined; + [EffectTypeId] = effectVariance + constructor(readonly _op: Primitive["_op"]) { + // @ts-expect-error + this._tag = _op + } + [Equal.symbol](this: {}, that: unknown) { + return exitIsExit(that) && that._op === "Failure" && + // @ts-expect-error + Equal.equals(this.effect_instruction_i0, that.effect_instruction_i0) + } + [Hash.symbol](this: {}) { + return pipe( + // @ts-expect-error + Hash.string(this._tag), + // @ts-expect-error + Hash.combine(Hash.hash(this.effect_instruction_i0)), + Hash.cached(this) + ) + } + get cause() { + return this.effect_instruction_i0 + } + pipe() { + return pipeArguments(this, arguments) + } + toJSON() { + return { + _id: "Exit", + _tag: this._op, + cause: (this.cause as any).toJSON() + } + } + toString() { + return format(this.toJSON()) + } + [NodeInspectSymbol]() { + return this.toJSON() + } + [Symbol.iterator]() { + return new SingleShotGen(new YieldWrap(this)) + } +} + +/** @internal */ +class EffectPrimitiveSuccess { + public effect_instruction_i0 = undefined + public effect_instruction_i1 = undefined + public effect_instruction_i2 = undefined + public trace = undefined; + [EffectTypeId] = effectVariance + constructor(readonly _op: Primitive["_op"]) { + // @ts-expect-error + this._tag = _op + } + [Equal.symbol](this: {}, that: unknown) { + return exitIsExit(that) && that._op === "Success" && + // @ts-expect-error + Equal.equals(this.effect_instruction_i0, that.effect_instruction_i0) + } + [Hash.symbol](this: {}) { + return pipe( + // @ts-expect-error + Hash.string(this._tag), + // @ts-expect-error + Hash.combine(Hash.hash(this.effect_instruction_i0)), + Hash.cached(this) + ) + } + get value() { + return this.effect_instruction_i0 + } + pipe() { + return pipeArguments(this, arguments) + } + toJSON() { + return { + _id: "Exit", + _tag: this._op, + value: toJSON(this.value) + } + } + toString() { + return format(this.toJSON()) + } + [NodeInspectSymbol]() { + return this.toJSON() + } + [Symbol.iterator]() { + return new SingleShotGen(new YieldWrap(this)) + } +} + +/** @internal */ +export type Op = Effect.Effect & Body & { + readonly _op: Tag +} + +/** @internal */ +export interface Async extends + Op void): void + readonly effect_instruction_i1: FiberId.FiberId + }> +{} + +/** @internal */ +export interface Blocked extends + Op<"Blocked", { + readonly effect_instruction_i0: BlockedRequests.RequestBlock + readonly effect_instruction_i1: Effect.Effect + }> +{} + +/** @internal */ +export interface RunBlocked extends + Op<"RunBlocked", { + readonly effect_instruction_i0: BlockedRequests.RequestBlock + }> +{} + +/** @internal */ +export interface Failure extends + Op + }> +{} + +/** @internal */ +export interface OpTag extends Op {} + +/** @internal */ +export interface Commit extends + Op + }> +{} + +/** @internal */ +export interface OnFailure extends + Op): Primitive + }> +{} + +/** @internal */ +export interface OnSuccess extends + Op +{} + +/** @internal */ +export interface OnStep extends Op<"OnStep", { readonly effect_instruction_i0: Primitive }> {} + +/** @internal */ +export interface OnSuccessAndFailure extends + Op): Primitive + effect_instruction_i2(a: unknown): Primitive + }> +{} + +/** @internal */ +export interface Success extends + Op +{} + +/** @internal */ +export interface Sync extends + Op +{} + +/** @internal */ +export interface UpdateRuntimeFlags extends + Op Primitive + }> +{} + +/** @internal */ +export interface While extends + Op +{} + +/** @internal */ +export interface FromIterator extends + Op, any> + }> +{} + +/** @internal */ +export interface WithRuntime extends + Op, status: FiberStatus.Running): Primitive + }> +{} + +/** @internal */ +export interface Yield extends Op {} + +/** @internal */ +export const isEffect = (u: unknown): u is Effect.Effect => hasProperty(u, EffectTypeId) + +/* @internal */ +export const withFiberRuntime = ( + withRuntime: (fiber: FiberRuntime.FiberRuntime, status: FiberStatus.Running) => Effect.Effect +): Effect.Effect => { + const effect = new EffectPrimitive(OpCodes.OP_WITH_RUNTIME) as any + effect.effect_instruction_i0 = withRuntime + return effect +} + +/* @internal */ +export const acquireUseRelease: { + ( + use: (a: A) => Effect.Effect, + release: (a: A, exit: Exit.Exit) => Effect.Effect + ): (acquire: Effect.Effect) => Effect.Effect + ( + acquire: Effect.Effect, + use: (a: A) => Effect.Effect, + release: (a: A, exit: Exit.Exit) => Effect.Effect + ): Effect.Effect +} = dual(3, ( + acquire: Effect.Effect, + use: (a: A) => Effect.Effect, + release: (a: A, exit: Exit.Exit) => Effect.Effect +): Effect.Effect => + uninterruptibleMask((restore) => + flatMap( + acquire, + (a) => + flatMap(exit(suspend(() => restore(use(a)))), (exit): Effect.Effect => { + return suspend(() => release(a, exit)).pipe( + matchCauseEffect({ + onFailure: (cause) => { + switch (exit._tag) { + case OpCodes.OP_FAILURE: + return failCause(internalCause.sequential(exit.effect_instruction_i0, cause)) + case OpCodes.OP_SUCCESS: + return failCause(cause) + } + }, + onSuccess: () => exit + }) + ) + }) + ) + )) + +/* @internal */ +export const as: { + (value: B): (self: Effect.Effect) => Effect.Effect + (self: Effect.Effect, value: B): Effect.Effect +} = dual( + 2, + (self: Effect.Effect, value: B): Effect.Effect => flatMap(self, () => succeed(value)) +) + +/* @internal */ +export const asVoid = (self: Effect.Effect): Effect.Effect => as(self, void 0) + +/* @internal */ +export const custom: { + (i0: X, body: (this: { effect_instruction_i0: X }) => Effect.Effect): Effect.Effect + ( + i0: X, + i1: Y, + body: (this: { effect_instruction_i0: X; effect_instruction_i1: Y }) => Effect.Effect + ): Effect.Effect + ( + i0: X, + i1: Y, + i2: Z, + body: ( + this: { effect_instruction_i0: X; effect_instruction_i1: Y; effect_instruction_i2: Z } + ) => Effect.Effect + ): Effect.Effect +} = function() { + const wrapper = new EffectPrimitive(OpCodes.OP_COMMIT) as any + switch (arguments.length) { + case 2: { + wrapper.effect_instruction_i0 = arguments[0] + wrapper.commit = arguments[1] + break + } + case 3: { + wrapper.effect_instruction_i0 = arguments[0] + wrapper.effect_instruction_i1 = arguments[1] + wrapper.commit = arguments[2] + break + } + case 4: { + wrapper.effect_instruction_i0 = arguments[0] + wrapper.effect_instruction_i1 = arguments[1] + wrapper.effect_instruction_i2 = arguments[2] + wrapper.commit = arguments[3] + break + } + default: { + throw new Error(getBugErrorMessage("you're not supposed to end up here")) + } + } + return wrapper +} + +/* @internal */ +export const unsafeAsync = ( + register: ( + callback: (_: Effect.Effect) => void + ) => void | Effect.Effect, + blockingOn: FiberId.FiberId = FiberId.none +): Effect.Effect => { + const effect = new EffectPrimitive(OpCodes.OP_ASYNC) as any + let cancelerRef: Effect.Effect | void = undefined + effect.effect_instruction_i0 = (resume: (_: Effect.Effect) => void) => { + cancelerRef = register(resume) + } + effect.effect_instruction_i1 = blockingOn + return onInterrupt(effect, (_) => isEffect(cancelerRef) ? cancelerRef : void_) +} + +/* @internal */ +export const asyncInterrupt = ( + register: ( + callback: (_: Effect.Effect) => void + ) => void | Effect.Effect, + blockingOn: FiberId.FiberId = FiberId.none +): Effect.Effect => suspend(() => unsafeAsync(register, blockingOn)) + +const async_ = ( + resume: ( + callback: (_: Effect.Effect) => void, + signal: AbortSignal + ) => void | Effect.Effect, + blockingOn: FiberId.FiberId = FiberId.none +): Effect.Effect => { + return custom(resume, function() { + let backingResume: ((_: Effect.Effect) => void) | undefined = undefined + let pendingEffect: Effect.Effect | undefined = undefined + function proxyResume(effect: Effect.Effect) { + if (backingResume) { + backingResume(effect) + } else if (pendingEffect === undefined) { + pendingEffect = effect + } + } + const effect = new EffectPrimitive(OpCodes.OP_ASYNC) as any + effect.effect_instruction_i0 = (resume: (_: Effect.Effect) => void) => { + backingResume = resume + if (pendingEffect) { + resume(pendingEffect) + } + } + effect.effect_instruction_i1 = blockingOn + let cancelerRef: Effect.Effect | void = undefined + let controllerRef: AbortController | void = undefined + if (this.effect_instruction_i0.length !== 1) { + controllerRef = new AbortController() + cancelerRef = internalCall(() => this.effect_instruction_i0(proxyResume, controllerRef!.signal)) + } else { + cancelerRef = internalCall(() => (this.effect_instruction_i0 as any)(proxyResume)) + } + return (cancelerRef || controllerRef) ? + onInterrupt(effect, (_) => { + if (controllerRef) { + controllerRef.abort() + } + return cancelerRef ?? void_ + }) : + effect + }) +} +export { + /** @internal */ + async_ as async +} + +/* @internal */ +export const catchAllCause = dual< + ( + f: (cause: Cause.Cause) => Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + f: (cause: Cause.Cause) => Effect.Effect + ) => Effect.Effect +>(2, (self, f) => { + const effect = new EffectPrimitive(OpCodes.OP_ON_FAILURE) as any + effect.effect_instruction_i0 = self + effect.effect_instruction_i1 = f + return effect +}) + +/* @internal */ +export const catchAll: { + ( + f: (e: E) => Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + f: (e: E) => Effect.Effect + ): Effect.Effect +} = dual( + 2, + ( + self: Effect.Effect, + f: (e: E) => Effect.Effect + ): Effect.Effect => matchEffect(self, { onFailure: f, onSuccess: succeed }) +) + +/* @internal */ +export const catchIf: { + ( + refinement: Refinement, EB>, + f: (e: EB) => Effect.Effect + ): (self: Effect.Effect) => Effect.Effect, R2 | R> + ( + predicate: Predicate>, + f: (e: NoInfer) => Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + refinement: Refinement, + f: (e: EB) => Effect.Effect + ): Effect.Effect, R2 | R> + ( + self: Effect.Effect, + predicate: Predicate, + f: (e: E) => Effect.Effect + ): Effect.Effect +} = dual(3, ( + self: Effect.Effect, + predicate: Predicate, + f: (e: E) => Effect.Effect +): Effect.Effect => + catchAllCause(self, (cause): Effect.Effect => { + const either = internalCause.failureOrCause(cause) + switch (either._tag) { + case "Left": + return predicate(either.left) ? f(either.left) : failCause(cause) + case "Right": + return failCause(either.right) + } + })) + +/* @internal */ +export const catchSome = dual< + ( + pf: (e: NoInfer) => Option.Option> + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + pf: (e: NoInfer) => Option.Option> + ) => Effect.Effect +>(2, ( + self: Effect.Effect, + pf: (e: NoInfer) => Option.Option> +) => + catchAllCause(self, (cause): Effect.Effect => { + const either = internalCause.failureOrCause(cause) + switch (either._tag) { + case "Left": + return pipe(pf(either.left), Option.getOrElse(() => failCause(cause))) + case "Right": + return failCause(either.right) + } + })) + +/* @internal */ +export const checkInterruptible = ( + f: (isInterruptible: boolean) => Effect.Effect +): Effect.Effect => withFiberRuntime((_, status) => f(runtimeFlags_.interruption(status.runtimeFlags))) + +const originalSymbol = Symbol.for("effect/OriginalAnnotation") + +/* @internal */ +export const originalInstance = (obj: E): E => { + if (hasProperty(obj, originalSymbol)) { + // @ts-expect-error + return obj[originalSymbol] + } + return obj +} + +/* @internal */ +export const capture = (obj: E & object, span: Option.Option): E => { + if (Option.isSome(span)) { + return new Proxy(obj, { + has(target, p) { + return p === internalCause.spanSymbol || p === originalSymbol || p in target + }, + get(target, p) { + if (p === internalCause.spanSymbol) { + return span.value + } + if (p === originalSymbol) { + return obj + } + // @ts-expect-error + return target[p] + } + }) + } + return obj +} + +/* @internal */ +export const die = (defect: unknown): Effect.Effect => + isObject(defect) && !(internalCause.spanSymbol in defect) ? + withFiberRuntime((fiber) => failCause(internalCause.die(capture(defect, currentSpanFromFiber(fiber))))) + : failCause(internalCause.die(defect)) + +/* @internal */ +export const dieMessage = (message: string): Effect.Effect => + failCauseSync(() => internalCause.die(new RuntimeException(message))) + +/* @internal */ +export const dieSync = (evaluate: LazyArg): Effect.Effect => flatMap(sync(evaluate), die) + +/* @internal */ +export const either = (self: Effect.Effect): Effect.Effect, never, R> => + matchEffect(self, { + onFailure: (e) => succeed(Either.left(e)), + onSuccess: (a) => succeed(Either.right(a)) + }) + +/* @internal */ +export const exit = (self: Effect.Effect): Effect.Effect, never, R> => + matchCause(self, { + onFailure: exitFailCause, + onSuccess: exitSucceed + }) + +/* @internal */ +export const fail = (error: E): Effect.Effect => + isObject(error) && !(internalCause.spanSymbol in error) ? + withFiberRuntime((fiber) => failCause(internalCause.fail(capture(error, currentSpanFromFiber(fiber))))) + : failCause(internalCause.fail(error)) + +/* @internal */ +export const failSync = (evaluate: LazyArg): Effect.Effect => flatMap(sync(evaluate), fail) + +/* @internal */ +export const failCause = (cause: Cause.Cause): Effect.Effect => { + const effect = new EffectPrimitiveFailure(OpCodes.OP_FAILURE) as any + effect.effect_instruction_i0 = cause + return effect +} + +/* @internal */ +export const failCauseSync = ( + evaluate: LazyArg> +): Effect.Effect => flatMap(sync(evaluate), failCause) + +/* @internal */ +export const fiberId: Effect.Effect = withFiberRuntime((state) => succeed(state.id())) + +/* @internal */ +export const fiberIdWith = ( + f: (descriptor: FiberId.Runtime) => Effect.Effect +): Effect.Effect => withFiberRuntime((state) => f(state.id())) + +/* @internal */ +export const flatMap = dual< + ( + f: (a: A) => Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + f: (a: A) => Effect.Effect + ) => Effect.Effect +>( + 2, + (self, f) => { + const effect = new EffectPrimitive(OpCodes.OP_ON_SUCCESS) as any + effect.effect_instruction_i0 = self + effect.effect_instruction_i1 = f + return effect + } +) + +/* @internal */ +export const andThen: { + ( + f: (a: NoInfer) => X + ): ( + self: Effect.Effect + ) => [X] extends [Effect.Effect] ? Effect.Effect + : [X] extends [PromiseLike] ? Effect.Effect + : Effect.Effect + ( + f: NotFunction + ): ( + self: Effect.Effect + ) => [X] extends [Effect.Effect] ? Effect.Effect + : [X] extends [PromiseLike] ? Effect.Effect + : Effect.Effect + ( + self: Effect.Effect, + f: (a: NoInfer) => X + ): [X] extends [Effect.Effect] ? Effect.Effect + : [X] extends [PromiseLike] ? Effect.Effect + : Effect.Effect + ( + self: Effect.Effect, + f: NotFunction + ): [X] extends [Effect.Effect] ? Effect.Effect + : [X] extends [PromiseLike] ? Effect.Effect + : Effect.Effect +} = dual(2, (self, f) => + flatMap(self, (a) => { + const b = typeof f === "function" ? (f as any)(a) : f + if (isEffect(b)) { + return b + } else if (isPromiseLike(b)) { + return unsafeAsync((resume) => { + b.then((a) => resume(succeed(a)), (e) => + resume(fail(new UnknownException(e, "An unknown error occurred in Effect.andThen")))) + }) + } + return succeed(b) + })) + +/* @internal */ +export const step = ( + self: Effect.Effect +): Effect.Effect | Effect.Blocked, never, R> => { + const effect = new EffectPrimitive("OnStep") as any + effect.effect_instruction_i0 = self + return effect +} + +/* @internal */ +export const flatten = ( + self: Effect.Effect, E, R> +): Effect.Effect => flatMap(self, identity) + +/* @internal */ +export const flip = (self: Effect.Effect): Effect.Effect => + matchEffect(self, { onFailure: succeed, onSuccess: fail }) + +/* @internal */ +export const matchCause: { + ( + options: { + readonly onFailure: (cause: Cause.Cause) => A2 + readonly onSuccess: (a: A) => A3 + } + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + options: { + readonly onFailure: (cause: Cause.Cause) => A2 + readonly onSuccess: (a: A) => A3 + } + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + options: { + readonly onFailure: (cause: Cause.Cause) => A2 + readonly onSuccess: (a: A) => A3 + } +): Effect.Effect => + matchCauseEffect(self, { + onFailure: (cause) => succeed(options.onFailure(cause)), + onSuccess: (a) => succeed(options.onSuccess(a)) + })) + +/* @internal */ +export const matchCauseEffect: { + ( + options: { + readonly onFailure: (cause: Cause.Cause) => Effect.Effect + readonly onSuccess: (a: A) => Effect.Effect + } + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + options: { + readonly onFailure: (cause: Cause.Cause) => Effect.Effect + readonly onSuccess: (a: A) => Effect.Effect + } + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + options: { + readonly onFailure: (cause: Cause.Cause) => Effect.Effect + readonly onSuccess: (a: A) => Effect.Effect + } +): Effect.Effect => { + const effect = new EffectPrimitive(OpCodes.OP_ON_SUCCESS_AND_FAILURE) as any + effect.effect_instruction_i0 = self + effect.effect_instruction_i1 = options.onFailure + effect.effect_instruction_i2 = options.onSuccess + return effect +}) + +/* @internal */ +export const matchEffect: { + ( + options: { + readonly onFailure: (e: E) => Effect.Effect + readonly onSuccess: (a: A) => Effect.Effect + } + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + options: { + readonly onFailure: (e: E) => Effect.Effect + readonly onSuccess: (a: A) => Effect.Effect + } + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + options: { + readonly onFailure: (e: E) => Effect.Effect + readonly onSuccess: (a: A) => Effect.Effect + } +): Effect.Effect => + matchCauseEffect(self, { + onFailure: (cause) => { + const defects = internalCause.defects(cause) + if (defects.length > 0) { + return failCause(internalCause.electFailures(cause)) + } + const failures = internalCause.failures(cause) + if (failures.length > 0) { + return options.onFailure(Chunk.unsafeHead(failures)) + } + return failCause(cause as Cause.Cause) + }, + onSuccess: options.onSuccess + })) + +/* @internal */ +export const forEachSequential: { + (f: (a: A, i: number) => Effect.Effect): (self: Iterable) => Effect.Effect, E, R> + (self: Iterable, f: (a: A, i: number) => Effect.Effect): Effect.Effect, E, R> +} = dual( + 2, + (self: Iterable, f: (a: A, i: number) => Effect.Effect): Effect.Effect, E, R> => + suspend(() => { + const arr = Arr.fromIterable(self) + const ret = Arr.allocate(arr.length) + let i = 0 + return as( + whileLoop({ + while: () => i < arr.length, + body: () => f(arr[i], i), + step: (b) => { + ret[i++] = b + } + }), + ret as Array + ) + }) +) + +/* @internal */ +export const forEachSequentialDiscard: { + (f: (a: A, i: number) => Effect.Effect): (self: Iterable) => Effect.Effect + (self: Iterable, f: (a: A, i: number) => Effect.Effect): Effect.Effect +} = dual( + 2, + (self: Iterable, f: (a: A, i: number) => Effect.Effect): Effect.Effect => + suspend(() => { + const arr = Arr.fromIterable(self) + let i = 0 + return whileLoop({ + while: () => i < arr.length, + body: () => f(arr[i], i), + step: () => { + i++ + } + }) + }) +) + +/* @internal */ +export const if_ = dual< + ( + options: { + readonly onTrue: LazyArg> + readonly onFalse: LazyArg> + } + ) => ( + self: Effect.Effect | boolean + ) => Effect.Effect, + ( + self: Effect.Effect | boolean, + options: { + readonly onTrue: LazyArg> + readonly onFalse: LazyArg> + } + ) => Effect.Effect +>( + (args) => typeof args[0] === "boolean" || isEffect(args[0]), + ( + self: Effect.Effect | boolean, + options: { + readonly onTrue: LazyArg> + readonly onFalse: LazyArg> + } + ): Effect.Effect => + isEffect(self) + ? flatMap(self, (b): Effect.Effect => (b ? options.onTrue() : options.onFalse())) + : self + ? options.onTrue() + : options.onFalse() +) + +/* @internal */ +export const interrupt: Effect.Effect = flatMap(fiberId, (fiberId) => interruptWith(fiberId)) + +/* @internal */ +export const interruptWith = (fiberId: FiberId.FiberId): Effect.Effect => + failCause(internalCause.interrupt(fiberId)) + +/* @internal */ +export const interruptible = (self: Effect.Effect): Effect.Effect => { + const effect = new EffectPrimitive(OpCodes.OP_UPDATE_RUNTIME_FLAGS) as any + effect.effect_instruction_i0 = RuntimeFlagsPatch.enable(runtimeFlags_.Interruption) + effect.effect_instruction_i1 = () => self + return effect +} + +/* @internal */ +export const interruptibleMask = ( + f: (restore: (effect: Effect.Effect) => Effect.Effect) => Effect.Effect +): Effect.Effect => + custom(f, function() { + const effect = new EffectPrimitive(OpCodes.OP_UPDATE_RUNTIME_FLAGS) as any + effect.effect_instruction_i0 = RuntimeFlagsPatch.enable(runtimeFlags_.Interruption) + effect.effect_instruction_i1 = (oldFlags: RuntimeFlags.RuntimeFlags) => + runtimeFlags_.interruption(oldFlags) + ? internalCall(() => this.effect_instruction_i0(interruptible)) + : internalCall(() => this.effect_instruction_i0(uninterruptible)) + return effect + }) + +/* @internal */ +export const intoDeferred: { + (deferred: Deferred.Deferred): (self: Effect.Effect) => Effect.Effect + (self: Effect.Effect, deferred: Deferred.Deferred): Effect.Effect +} = dual( + 2, + (self: Effect.Effect, deferred: Deferred.Deferred): Effect.Effect => + uninterruptibleMask((restore) => + flatMap( + exit(restore(self)), + (exit) => deferredDone(deferred, exit) + ) + ) +) + +/* @internal */ +export const map: { + (f: (a: A) => B): (self: Effect.Effect) => Effect.Effect + (self: Effect.Effect, f: (a: A) => B): Effect.Effect +} = dual( + 2, + (self: Effect.Effect, f: (a: A) => B): Effect.Effect => + flatMap(self, (a) => sync(() => f(a))) +) + +/* @internal */ +export const mapBoth: { + ( + options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } +): Effect.Effect => + matchEffect(self, { + onFailure: (e) => failSync(() => options.onFailure(e)), + onSuccess: (a) => sync(() => options.onSuccess(a)) + })) + +/* @internal */ +export const mapError: { + (f: (e: E) => E2): (self: Effect.Effect) => Effect.Effect + (self: Effect.Effect, f: (e: E) => E2): Effect.Effect +} = dual( + 2, + (self: Effect.Effect, f: (e: E) => E2): Effect.Effect => + matchCauseEffect(self, { + onFailure: (cause) => { + const either = internalCause.failureOrCause(cause) + switch (either._tag) { + case "Left": { + return failSync(() => f(either.left)) + } + case "Right": { + return failCause(either.right) + } + } + }, + onSuccess: succeed + }) +) + +/* @internal */ +export const onError: { + ( + cleanup: (cause: Cause.Cause) => Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + cleanup: (cause: Cause.Cause) => Effect.Effect + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + cleanup: (cause: Cause.Cause) => Effect.Effect +): Effect.Effect => + onExit(self, (exit) => exitIsSuccess(exit) ? void_ : cleanup(exit.effect_instruction_i0))) + +/* @internal */ +export const onExit: { + ( + cleanup: (exit: Exit.Exit) => Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + cleanup: (exit: Exit.Exit) => Effect.Effect + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + cleanup: (exit: Exit.Exit) => Effect.Effect +): Effect.Effect => + uninterruptibleMask((restore) => + matchCauseEffect(restore(self), { + onFailure: (cause1) => { + const result = exitFailCause(cause1) + return matchCauseEffect(cleanup(result), { + onFailure: (cause2) => exitFailCause(internalCause.sequential(cause1, cause2)), + onSuccess: () => result + }) + }, + onSuccess: (success) => { + const result = exitSucceed(success) + return zipRight(cleanup(result), result) + } + }) + )) + +/* @internal */ +export const onInterrupt: { + ( + cleanup: (interruptors: HashSet.HashSet) => Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + cleanup: (interruptors: HashSet.HashSet) => Effect.Effect + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + cleanup: (interruptors: HashSet.HashSet) => Effect.Effect +): Effect.Effect => + onExit( + self, + exitMatch({ + onFailure: (cause) => + internalCause.isInterruptedOnly(cause) + ? asVoid(cleanup(internalCause.interruptors(cause))) + : void_, + onSuccess: () => void_ + }) + )) + +/* @internal */ +export const orElse: { + ( + that: LazyArg> + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + that: LazyArg> + ): Effect.Effect +} = dual( + 2, + ( + self: Effect.Effect, + that: LazyArg> + ): Effect.Effect => attemptOrElse(self, that, succeed) +) + +/* @internal */ +export const orDie = (self: Effect.Effect): Effect.Effect => orDieWith(self, identity) + +/* @internal */ +export const orDieWith: { + (f: (error: E) => unknown): (self: Effect.Effect) => Effect.Effect + (self: Effect.Effect, f: (error: E) => unknown): Effect.Effect +} = dual( + 2, + (self: Effect.Effect, f: (error: E) => unknown): Effect.Effect => + matchEffect(self, { + onFailure: (e) => die(f(e)), + onSuccess: succeed + }) +) + +/* @internal */ +export const partitionMap: ( + elements: Iterable, + f: (a: A) => Either.Either +) => [left: Array, right: Array] = Arr.partitionMap +/* @internal */ +export const runtimeFlags: Effect.Effect = withFiberRuntime((_, status) => + succeed(status.runtimeFlags) +) + +/* @internal */ +export const succeed = (value: A): Effect.Effect => { + const effect = new EffectPrimitiveSuccess(OpCodes.OP_SUCCESS) as any + effect.effect_instruction_i0 = value + return effect +} + +/* @internal */ +export const suspend = (evaluate: LazyArg>): Effect.Effect => { + const effect = new EffectPrimitive(OpCodes.OP_COMMIT) as any + effect.commit = evaluate + return effect +} + +/* @internal */ +export const sync = (thunk: LazyArg): Effect.Effect => { + const effect = new EffectPrimitive(OpCodes.OP_SYNC) as any + effect.effect_instruction_i0 = thunk + return effect +} + +/* @internal */ +export const tap = dual< + { + ( + f: (a: NoInfer) => X + ): ( + self: Effect.Effect + ) => [X] extends [Effect.Effect] ? Effect.Effect + : [X] extends [PromiseLike] ? Effect.Effect + : Effect.Effect + ( + f: (a: NoInfer) => Effect.Effect, + options: { onlyEffect: true } + ): ( + self: Effect.Effect + ) => Effect.Effect + ( + f: NotFunction + ): ( + self: Effect.Effect + ) => [X] extends [Effect.Effect] ? Effect.Effect + : [X] extends [PromiseLike] ? Effect.Effect + : Effect.Effect + ( + f: Effect.Effect, + options: { onlyEffect: true } + ): ( + self: Effect.Effect + ) => Effect.Effect + }, + { + ( + self: Effect.Effect, + f: (a: NoInfer) => X + ): [X] extends [Effect.Effect] ? Effect.Effect + : [X] extends [PromiseLike] ? Effect.Effect + : Effect.Effect + ( + self: Effect.Effect, + f: (a: NoInfer) => Effect.Effect, + options: { onlyEffect: true } + ): Effect.Effect + ( + self: Effect.Effect, + f: NotFunction + ): [X] extends [Effect.Effect] ? Effect.Effect + : [X] extends [PromiseLike] ? Effect.Effect + : Effect.Effect + ( + self: Effect.Effect, + f: Effect.Effect, + options: { onlyEffect: true } + ): Effect.Effect + } +>( + (args) => args.length === 3 || args.length === 2 && !(isObject(args[1]) && "onlyEffect" in args[1]), + (self: Effect.Effect, f: X) => + flatMap(self, (a) => { + const b = typeof f === "function" ? (f as any)(a) : f + if (isEffect(b)) { + return as(b, a) + } else if (isPromiseLike(b)) { + return unsafeAsync((resume) => { + b.then((_) => resume(succeed(a)), (e) => + resume(fail(new UnknownException(e, "An unknown error occurred in Effect.tap")))) + }) + } + return succeed(a) + }) +) + +/* @internal */ +export const transplant = ( + f: (grafter: (effect: Effect.Effect) => Effect.Effect) => Effect.Effect +): Effect.Effect => + withFiberRuntime((state) => { + const scopeOverride = state.getFiberRef(currentForkScopeOverride) + const scope = pipe(scopeOverride, Option.getOrElse(() => state.scope())) + return f(fiberRefLocally(currentForkScopeOverride, Option.some(scope))) + }) + +/* @internal */ +export const attemptOrElse: { + ( + that: LazyArg>, + onSuccess: (a: A) => Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + that: LazyArg>, + onSuccess: (a: A) => Effect.Effect + ): Effect.Effect +} = dual(3, ( + self: Effect.Effect, + that: LazyArg>, + onSuccess: (a: A) => Effect.Effect +): Effect.Effect => + matchCauseEffect(self, { + onFailure: (cause) => { + const defects = internalCause.defects(cause) + if (defects.length > 0) { + return failCause(Option.getOrThrow(internalCause.keepDefectsAndElectFailures(cause))) + } + return that() + }, + onSuccess + })) + +/* @internal */ +export const uninterruptible: (self: Effect.Effect) => Effect.Effect = ( + self: Effect.Effect +): Effect.Effect => { + const effect = new EffectPrimitive(OpCodes.OP_UPDATE_RUNTIME_FLAGS) as any + effect.effect_instruction_i0 = RuntimeFlagsPatch.disable(runtimeFlags_.Interruption) + effect.effect_instruction_i1 = () => self + return effect +} + +/* @internal */ +export const uninterruptibleMask = ( + f: (restore: (effect: Effect.Effect) => Effect.Effect) => Effect.Effect +): Effect.Effect => + custom(f, function() { + const effect = new EffectPrimitive(OpCodes.OP_UPDATE_RUNTIME_FLAGS) as any + effect.effect_instruction_i0 = RuntimeFlagsPatch.disable(runtimeFlags_.Interruption) + effect.effect_instruction_i1 = (oldFlags: RuntimeFlags.RuntimeFlags) => + runtimeFlags_.interruption(oldFlags) + ? internalCall(() => this.effect_instruction_i0(interruptible)) + : internalCall(() => this.effect_instruction_i0(uninterruptible)) + return effect + }) + +const void_: Effect.Effect = succeed(void 0) +export { + /* @internal */ + void_ as void +} + +/* @internal */ +export const updateRuntimeFlags = (patch: RuntimeFlagsPatch.RuntimeFlagsPatch): Effect.Effect => { + const effect = new EffectPrimitive(OpCodes.OP_UPDATE_RUNTIME_FLAGS) as any + effect.effect_instruction_i0 = patch + effect.effect_instruction_i1 = void 0 + return effect +} + +/* @internal */ +export const whenEffect: { + ( + condition: Effect.Effect + ): ( + effect: Effect.Effect + ) => Effect.Effect, E | E2, R | R2> + ( + self: Effect.Effect, + condition: Effect.Effect + ): Effect.Effect, E | E2, R | R2> +} = dual(2, ( + self: Effect.Effect, + condition: Effect.Effect +): Effect.Effect, E | E2, R | R2> => + flatMap(condition, (b) => { + if (b) { + return pipe(self, map(Option.some)) + } + return succeed(Option.none()) + })) + +/* @internal */ +export const whileLoop = ( + options: { + readonly while: LazyArg + readonly body: LazyArg> + readonly step: (a: A) => void + } +): Effect.Effect => { + const effect = new EffectPrimitive(OpCodes.OP_WHILE) as any + effect.effect_instruction_i0 = options.while + effect.effect_instruction_i1 = options.body + effect.effect_instruction_i2 = options.step + return effect +} + +/* @internal */ +export const fromIterator = >, AEff>( + iterator: LazyArg> +): Effect.Effect< + AEff, + [Eff] extends [never] ? never : [Eff] extends [YieldWrap>] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [YieldWrap>] ? R : never +> => + suspend(() => { + const effect = new EffectPrimitive(OpCodes.OP_ITERATOR) as any + effect.effect_instruction_i0 = iterator() + return effect + }) + +/* @internal */ +export const gen: typeof Effect.gen = function() { + const f = arguments.length === 1 ? arguments[0] : arguments[1].bind(arguments[0]) + return fromIterator(() => f(pipe)) +} + +/** @internal */ +export const fnUntraced: Effect.fn.Untraced = (body: Function, ...pipeables: Array) => + Object.defineProperty( + pipeables.length === 0 + ? function(this: any, ...args: Array) { + return fromIterator(() => body.apply(this, args)) + } + : function(this: any, ...args: Array) { + let effect = fromIterator(() => body.apply(this, args)) + for (const x of pipeables) { + effect = x(effect, ...args) + } + return effect + }, + "length", + { value: body.length, configurable: true } + ) + +/* @internal */ +export const withConcurrency = dual< + (concurrency: number | "unbounded") => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, concurrency: number | "unbounded") => Effect.Effect +>(2, (self, concurrency) => fiberRefLocally(self, currentConcurrency, concurrency)) + +/* @internal */ +export const withRequestBatching = dual< + (requestBatching: boolean) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, requestBatching: boolean) => Effect.Effect +>(2, (self, requestBatching) => fiberRefLocally(self, currentRequestBatching, requestBatching)) + +/* @internal */ +export const withRuntimeFlags = dual< + (update: RuntimeFlagsPatch.RuntimeFlagsPatch) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, update: RuntimeFlagsPatch.RuntimeFlagsPatch) => Effect.Effect +>(2, (self, update) => { + const effect = new EffectPrimitive(OpCodes.OP_UPDATE_RUNTIME_FLAGS) as any + effect.effect_instruction_i0 = update + effect.effect_instruction_i1 = () => self + return effect +}) + +/** @internal */ +export const withTracerEnabled = dual< + (enabled: boolean) => (effect: Effect.Effect) => Effect.Effect, + (effect: Effect.Effect, enabled: boolean) => Effect.Effect +>(2, (effect, enabled) => + fiberRefLocally( + effect, + currentTracerEnabled, + enabled + )) + +/** @internal */ +export const withTracerTiming = dual< + (enabled: boolean) => (effect: Effect.Effect) => Effect.Effect, + (effect: Effect.Effect, enabled: boolean) => Effect.Effect +>(2, (effect, enabled) => + fiberRefLocally( + effect, + currentTracerTimingEnabled, + enabled + )) + +/* @internal */ +export const yieldNow = (options?: { + readonly priority?: number | undefined +}): Effect.Effect => { + const effect = new EffectPrimitive(OpCodes.OP_YIELD) as any + return typeof options?.priority !== "undefined" ? + withSchedulingPriority(effect, options.priority) : + effect +} + +/* @internal */ +export const zip = dual< + ( + that: Effect.Effect + ) => ( + self: Effect.Effect + ) => Effect.Effect<[A, A2], E | E2, R | R2>, + ( + self: Effect.Effect, + that: Effect.Effect + ) => Effect.Effect<[A, A2], E | E2, R | R2> +>(2, ( + self: Effect.Effect, + that: Effect.Effect +): Effect.Effect<[A, A2], E | E2, R | R2> => flatMap(self, (a) => map(that, (b) => [a, b]))) + +/* @internal */ +export const zipFlatten: { + ( + that: Effect.Effect + ): , E, R>( + self: Effect.Effect + ) => Effect.Effect<[...A, A2], E | E2, R | R2> + , E, R, A2, E2, R2>( + self: Effect.Effect, + that: Effect.Effect + ): Effect.Effect<[...A, A2], E | E2, R | R2> +} = dual(2, , E, R, A2, E2, R2>( + self: Effect.Effect, + that: Effect.Effect +): Effect.Effect<[...A, A2], E | E2, R | R2> => flatMap(self, (a) => map(that, (b) => [...a, b]))) + +/* @internal */ +export const zipLeft: { + ( + that: Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + that: Effect.Effect + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + that: Effect.Effect +): Effect.Effect => flatMap(self, (a) => as(that, a))) + +/* @internal */ +export const zipRight: { + ( + that: Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + that: Effect.Effect + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + that: Effect.Effect +): Effect.Effect => flatMap(self, () => that)) + +/* @internal */ +export const zipWith: { + ( + that: Effect.Effect, + f: (a: A, b: A2) => B + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + that: Effect.Effect, + f: (a: A, b: A2) => B + ): Effect.Effect +} = dual(3, ( + self: Effect.Effect, + that: Effect.Effect, + f: (a: A, b: A2) => B +): Effect.Effect => flatMap(self, (a) => map(that, (b) => f(a, b)))) + +/* @internal */ +export const never: Effect.Effect = asyncInterrupt(() => { + const interval = setInterval(() => { + // + }, 2 ** 31 - 1) + return sync(() => clearInterval(interval)) +}) + +// ----------------------------------------------------------------------------- +// Fiber +// ----------------------------------------------------------------------------- + +/* @internal */ +export const interruptFiber = (self: Fiber.Fiber): Effect.Effect> => + flatMap(fiberId, (fiberId) => pipe(self, interruptAsFiber(fiberId))) + +/* @internal */ +export const interruptAsFiber = dual< + (fiberId: FiberId.FiberId) => (self: Fiber.Fiber) => Effect.Effect>, + (self: Fiber.Fiber, fiberId: FiberId.FiberId) => Effect.Effect> +>(2, (self, fiberId) => flatMap(self.interruptAsFork(fiberId), () => self.await)) + +// ----------------------------------------------------------------------------- +// LogLevel +// ----------------------------------------------------------------------------- + +/** @internal */ +export const logLevelAll: LogLevel.LogLevel = { + _tag: "All", + syslog: 0, + label: "ALL", + ordinal: Number.MIN_SAFE_INTEGER, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const logLevelFatal: LogLevel.LogLevel = { + _tag: "Fatal", + syslog: 2, + label: "FATAL", + ordinal: 50000, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const logLevelError: LogLevel.LogLevel = { + _tag: "Error", + syslog: 3, + label: "ERROR", + ordinal: 40000, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const logLevelWarning: LogLevel.LogLevel = { + _tag: "Warning", + syslog: 4, + label: "WARN", + ordinal: 30000, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const logLevelInfo: LogLevel.LogLevel = { + _tag: "Info", + syslog: 6, + label: "INFO", + ordinal: 20000, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const logLevelDebug: LogLevel.LogLevel = { + _tag: "Debug", + syslog: 7, + label: "DEBUG", + ordinal: 10000, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const logLevelTrace: LogLevel.LogLevel = { + _tag: "Trace", + syslog: 7, + label: "TRACE", + ordinal: 0, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const logLevelNone: LogLevel.LogLevel = { + _tag: "None", + syslog: 7, + label: "OFF", + ordinal: Number.MAX_SAFE_INTEGER, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const allLogLevels: ReadonlyArray = [ + logLevelAll, + logLevelTrace, + logLevelDebug, + logLevelInfo, + logLevelWarning, + logLevelError, + logLevelFatal, + logLevelNone +] + +// ----------------------------------------------------------------------------- +// FiberRef +// ----------------------------------------------------------------------------- + +/** @internal */ +const FiberRefSymbolKey = "effect/FiberRef" + +/** @internal */ +export const FiberRefTypeId: FiberRef.FiberRefTypeId = Symbol.for( + FiberRefSymbolKey +) as FiberRef.FiberRefTypeId + +const fiberRefVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +/* @internal */ +export const fiberRefGet = (self: FiberRef.FiberRef): Effect.Effect => + withFiberRuntime((fiber) => exitSucceed(fiber.getFiberRef(self))) + +/* @internal */ +export const fiberRefGetAndSet = dual< + (value: A) => (self: FiberRef.FiberRef) => Effect.Effect, + (self: FiberRef.FiberRef, value: A) => Effect.Effect +>(2, (self, value) => fiberRefModify(self, (v) => [v, value] as const)) + +/* @internal */ +export const fiberRefGetAndUpdate = dual< + (f: (a: A) => A) => (self: FiberRef.FiberRef) => Effect.Effect, + (self: FiberRef.FiberRef, f: (a: A) => A) => Effect.Effect +>(2, (self, f) => fiberRefModify(self, (v) => [v, f(v)] as const)) + +/* @internal */ +export const fiberRefGetAndUpdateSome = dual< + ( + pf: (a: A) => Option.Option + ) => (self: FiberRef.FiberRef) => Effect.Effect, + ( + self: FiberRef.FiberRef, + pf: (a: A) => Option.Option + ) => Effect.Effect +>(2, (self, pf) => fiberRefModify(self, (v) => [v, Option.getOrElse(pf(v), () => v)] as const)) + +/* @internal */ +export const fiberRefGetWith = dual< + (f: (a: A) => Effect.Effect) => (self: FiberRef.FiberRef) => Effect.Effect, + (self: FiberRef.FiberRef, f: (a: A) => Effect.Effect) => Effect.Effect +>(2, (self, f) => flatMap(fiberRefGet(self), f)) + +/* @internal */ +export const fiberRefSet = dual< + (value: A) => (self: FiberRef.FiberRef) => Effect.Effect, + (self: FiberRef.FiberRef, value: A) => Effect.Effect +>(2, (self, value) => fiberRefModify(self, () => [void 0, value] as const)) + +/* @internal */ +export const fiberRefDelete = (self: FiberRef.FiberRef): Effect.Effect => + withFiberRuntime((state) => { + state.unsafeDeleteFiberRef(self) + return void_ + }) + +/* @internal */ +export const fiberRefReset = (self: FiberRef.FiberRef): Effect.Effect => fiberRefSet(self, self.initial) + +/* @internal */ +export const fiberRefModify = dual< + (f: (a: A) => readonly [B, A]) => (self: FiberRef.FiberRef) => Effect.Effect, + (self: FiberRef.FiberRef, f: (a: A) => readonly [B, A]) => Effect.Effect +>(2, ( + self: FiberRef.FiberRef, + f: (a: A) => readonly [B, A] +): Effect.Effect => + withFiberRuntime((state) => { + const [b, a] = f(state.getFiberRef(self) as A) + state.setFiberRef(self, a) + return succeed(b) + })) + +/* @internal */ +export const fiberRefModifySome = ( + self: FiberRef.FiberRef, + def: B, + f: (a: A) => Option.Option +): Effect.Effect => fiberRefModify(self, (v) => Option.getOrElse(f(v), () => [def, v] as const)) + +/* @internal */ +export const fiberRefUpdate = dual< + (f: (a: A) => A) => (self: FiberRef.FiberRef) => Effect.Effect, + (self: FiberRef.FiberRef, f: (a: A) => A) => Effect.Effect +>(2, (self, f) => fiberRefModify(self, (v) => [void 0, f(v)] as const)) + +/* @internal */ +export const fiberRefUpdateSome = dual< + (pf: (a: A) => Option.Option) => (self: FiberRef.FiberRef) => Effect.Effect, + (self: FiberRef.FiberRef, pf: (a: A) => Option.Option) => Effect.Effect +>(2, (self, pf) => fiberRefModify(self, (v) => [void 0, Option.getOrElse(pf(v), () => v)] as const)) + +/* @internal */ +export const fiberRefUpdateAndGet = dual< + (f: (a: A) => A) => (self: FiberRef.FiberRef) => Effect.Effect, + (self: FiberRef.FiberRef, f: (a: A) => A) => Effect.Effect +>(2, (self, f) => + fiberRefModify(self, (v) => { + const result = f(v) + return [result, result] as const + })) + +/* @internal */ +export const fiberRefUpdateSomeAndGet = dual< + (pf: (a: A) => Option.Option) => (self: FiberRef.FiberRef) => Effect.Effect, + (self: FiberRef.FiberRef, pf: (a: A) => Option.Option) => Effect.Effect +>(2, (self, pf) => + fiberRefModify(self, (v) => { + const result = Option.getOrElse(pf(v), () => v) + return [result, result] as const + })) + +// circular +/** @internal */ +const RequestResolverSymbolKey = "effect/RequestResolver" + +/** @internal */ +export const RequestResolverTypeId: RequestResolver.RequestResolverTypeId = Symbol.for( + RequestResolverSymbolKey +) as RequestResolver.RequestResolverTypeId + +const requestResolverVariance = { + /* c8 ignore next */ + _A: (_: unknown) => _, + /* c8 ignore next */ + _R: (_: never) => _ +} + +/** @internal */ +export class RequestResolverImpl implements RequestResolver.RequestResolver { + readonly [RequestResolverTypeId] = requestResolverVariance + constructor( + readonly runAll: ( + requests: Array>> + ) => Effect.Effect, + readonly target?: unknown + ) { + } + [Hash.symbol](): number { + return Hash.cached(this, this.target ? Hash.hash(this.target) : Hash.random(this)) + } + [Equal.symbol](that: unknown): boolean { + return this.target ? + isRequestResolver(that) && Equal.equals(this.target, (that as RequestResolverImpl).target) : + this === that + } + identified(...ids: Array): RequestResolver.RequestResolver { + return new RequestResolverImpl(this.runAll, Chunk.fromIterable(ids)) + } + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const isRequestResolver = (u: unknown): u is RequestResolver.RequestResolver => + hasProperty(u, RequestResolverTypeId) + +// end + +/** @internal */ +export const resolverLocally = dual< + ( + self: FiberRef.FiberRef, + value: A + ) => >( + use: RequestResolver.RequestResolver + ) => RequestResolver.RequestResolver, + , A>( + use: RequestResolver.RequestResolver, + self: FiberRef.FiberRef, + value: A + ) => RequestResolver.RequestResolver +>(3, , A>( + use: RequestResolver.RequestResolver, + self: FiberRef.FiberRef, + value: A +): RequestResolver.RequestResolver => + new RequestResolverImpl( + (requests) => + fiberRefLocally( + use.runAll(requests), + self, + value + ), + Chunk.make("Locally", use, self, value) + )) + +/** @internal */ +export const requestBlockLocally = ( + self: BlockedRequests.RequestBlock, + ref: FiberRef.FiberRef, + value: A +): BlockedRequests.RequestBlock => blockedRequests_.reduce(self, LocallyReducer(ref, value)) + +const LocallyReducer = ( + ref: FiberRef.FiberRef, + value: A +): BlockedRequests.RequestBlock.Reducer => ({ + emptyCase: () => blockedRequests_.empty, + parCase: (left, right) => blockedRequests_.par(left, right), + seqCase: (left, right) => blockedRequests_.seq(left, right), + singleCase: (dataSource, blockedRequest) => + blockedRequests_.single( + resolverLocally(dataSource, ref, value), + blockedRequest as any + ) +}) + +/* @internal */ +export const fiberRefLocally: { + (self: FiberRef.FiberRef, value: A): (use: Effect.Effect) => Effect.Effect + (use: Effect.Effect, self: FiberRef.FiberRef, value: A): Effect.Effect +} = dual( + 3, + (use: Effect.Effect, self: FiberRef.FiberRef, value: A): Effect.Effect => + acquireUseRelease( + zipLeft(fiberRefGet(self), fiberRefSet(self, value)), + () => use, + (oldValue) => fiberRefSet(self, oldValue) + ) +) + +/* @internal */ +export const fiberRefLocallyWith = dual< + (self: FiberRef.FiberRef, f: (a: A) => A) => (use: Effect.Effect) => Effect.Effect, + (use: Effect.Effect, self: FiberRef.FiberRef, f: (a: A) => A) => Effect.Effect +>(3, (use, self, f) => fiberRefGetWith(self, (a) => fiberRefLocally(use, self, f(a)))) + +/** @internal */ +export const fiberRefUnsafeMake = ( + initial: Value, + options?: { + readonly fork?: ((a: Value) => Value) | undefined + readonly join?: ((left: Value, right: Value) => Value) | undefined + } +): FiberRef.FiberRef => + fiberRefUnsafeMakePatch(initial, { + differ: internalDiffer.update(), + fork: options?.fork ?? identity, + join: options?.join + }) + +/** @internal */ +export const fiberRefUnsafeMakeHashSet = ( + initial: HashSet.HashSet +): FiberRef.FiberRef> => { + const differ = internalDiffer.hashSet() + return fiberRefUnsafeMakePatch(initial, { + differ, + fork: differ.empty + }) +} + +/** @internal */ +export const fiberRefUnsafeMakeReadonlyArray = ( + initial: ReadonlyArray +): FiberRef.FiberRef> => { + const differ = internalDiffer.readonlyArray(internalDiffer.update()) + return fiberRefUnsafeMakePatch(initial, { + differ, + fork: differ.empty + }) +} + +/** @internal */ +export const fiberRefUnsafeMakeContext = ( + initial: Context.Context +): FiberRef.FiberRef> => { + const differ = internalDiffer.environment() + return fiberRefUnsafeMakePatch(initial, { + differ, + fork: differ.empty + }) +} + +/** @internal */ +export const fiberRefUnsafeMakePatch = ( + initial: Value, + options: { + readonly differ: Differ.Differ + readonly fork: Patch + readonly join?: ((oldV: Value, newV: Value) => Value) | undefined + } +): FiberRef.FiberRef => { + const _fiberRef = { + ...CommitPrototype, + [FiberRefTypeId]: fiberRefVariance, + initial, + commit() { + return fiberRefGet(this) + }, + diff: (oldValue: Value, newValue: Value) => options.differ.diff(oldValue, newValue), + combine: (first: Patch, second: Patch) => options.differ.combine(first, second), + patch: (patch: Patch) => (oldValue: Value) => options.differ.patch(patch, oldValue), + fork: options.fork, + join: options.join ?? ((_, n) => n) + } + return _fiberRef +} + +/** @internal */ +export const fiberRefUnsafeMakeRuntimeFlags = ( + initial: RuntimeFlags.RuntimeFlags +): FiberRef.FiberRef => + fiberRefUnsafeMakePatch(initial, { + differ: runtimeFlags_.differ, + fork: runtimeFlags_.differ.empty + }) + +/** @internal */ +export const currentContext: FiberRef.FiberRef> = globalValue( + Symbol.for("effect/FiberRef/currentContext"), + () => fiberRefUnsafeMakeContext(Context.empty()) +) + +/** @internal */ +export const currentSchedulingPriority: FiberRef.FiberRef = globalValue( + Symbol.for("effect/FiberRef/currentSchedulingPriority"), + () => fiberRefUnsafeMake(0) +) + +/** @internal */ +export const currentMaxOpsBeforeYield: FiberRef.FiberRef = globalValue( + Symbol.for("effect/FiberRef/currentMaxOpsBeforeYield"), + () => fiberRefUnsafeMake(2048) +) + +/** @internal */ +export const currentLogAnnotations: FiberRef.FiberRef> = globalValue( + Symbol.for("effect/FiberRef/currentLogAnnotation"), + () => fiberRefUnsafeMake(HashMap.empty()) +) + +/** @internal */ +export const currentLogLevel: FiberRef.FiberRef = globalValue( + Symbol.for("effect/FiberRef/currentLogLevel"), + () => fiberRefUnsafeMake(logLevelInfo) +) + +/** @internal */ +export const currentLogSpan: FiberRef.FiberRef> = globalValue( + Symbol.for("effect/FiberRef/currentLogSpan"), + () => fiberRefUnsafeMake(List.empty()) +) + +/** @internal */ +export const withSchedulingPriority = dual< + (priority: number) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, priority: number) => Effect.Effect +>(2, (self, scheduler) => fiberRefLocally(self, currentSchedulingPriority, scheduler)) + +/** @internal */ +export const withMaxOpsBeforeYield = dual< + (priority: number) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, priority: number) => Effect.Effect +>(2, (self, scheduler) => fiberRefLocally(self, currentMaxOpsBeforeYield, scheduler)) + +/** @internal */ +export const currentConcurrency: FiberRef.FiberRef<"unbounded" | number> = globalValue( + Symbol.for("effect/FiberRef/currentConcurrency"), + () => fiberRefUnsafeMake<"unbounded" | number>("unbounded") +) + +/** + * @internal + */ +export const currentRequestBatching = globalValue( + Symbol.for("effect/FiberRef/currentRequestBatching"), + () => fiberRefUnsafeMake(true) +) + +/** @internal */ +export const currentUnhandledErrorLogLevel: FiberRef.FiberRef> = globalValue( + Symbol.for("effect/FiberRef/currentUnhandledErrorLogLevel"), + () => fiberRefUnsafeMake(Option.some(logLevelDebug)) +) + +/** @internal */ +export const currentVersionMismatchErrorLogLevel: FiberRef.FiberRef> = globalValue( + Symbol.for("effect/FiberRef/versionMismatchErrorLogLevel"), + () => fiberRefUnsafeMake(Option.some(logLevelWarning)) +) + +/** @internal */ +export const withUnhandledErrorLogLevel = dual< + (level: Option.Option) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, level: Option.Option) => Effect.Effect +>(2, (self, level) => fiberRefLocally(self, currentUnhandledErrorLogLevel, level)) + +/** @internal */ +export const currentMetricLabels: FiberRef.FiberRef> = globalValue( + Symbol.for("effect/FiberRef/currentMetricLabels"), + () => fiberRefUnsafeMakeReadonlyArray(Arr.empty()) +) + +/* @internal */ +export const metricLabels: Effect.Effect> = fiberRefGet( + currentMetricLabels +) + +/** @internal */ +export const currentForkScopeOverride: FiberRef.FiberRef> = globalValue( + Symbol.for("effect/FiberRef/currentForkScopeOverride"), + () => + fiberRefUnsafeMake(Option.none(), { + fork: () => Option.none() as Option.Option, + join: (parent, _) => parent + }) +) + +/** @internal */ +export const currentInterruptedCause: FiberRef.FiberRef> = globalValue( + Symbol.for("effect/FiberRef/currentInterruptedCause"), + () => + fiberRefUnsafeMake(internalCause.empty, { + fork: () => internalCause.empty, + join: (parent, _) => parent + }) +) + +/** @internal */ +export const currentTracerEnabled: FiberRef.FiberRef = globalValue( + Symbol.for("effect/FiberRef/currentTracerEnabled"), + () => fiberRefUnsafeMake(true) +) + +/** @internal */ +export const currentTracerTimingEnabled: FiberRef.FiberRef = globalValue( + Symbol.for("effect/FiberRef/currentTracerTiming"), + () => fiberRefUnsafeMake(true) +) + +/** @internal */ +export const currentTracerSpanAnnotations: FiberRef.FiberRef> = globalValue( + Symbol.for("effect/FiberRef/currentTracerSpanAnnotations"), + () => fiberRefUnsafeMake(HashMap.empty()) +) + +/** @internal */ +export const currentTracerSpanLinks: FiberRef.FiberRef> = globalValue( + Symbol.for("effect/FiberRef/currentTracerSpanLinks"), + () => fiberRefUnsafeMake(Chunk.empty()) +) + +// ----------------------------------------------------------------------------- +// Scope +// ----------------------------------------------------------------------------- + +/** @internal */ +export const ScopeTypeId: Scope.ScopeTypeId = Symbol.for("effect/Scope") as Scope.ScopeTypeId + +/** @internal */ +export const CloseableScopeTypeId: Scope.CloseableScopeTypeId = Symbol.for( + "effect/CloseableScope" +) as Scope.CloseableScopeTypeId + +/* @internal */ +export const scopeAddFinalizer = ( + self: Scope.Scope, + finalizer: Effect.Effect +): Effect.Effect => self.addFinalizer(() => asVoid(finalizer)) + +/* @internal */ +export const scopeAddFinalizerExit = ( + self: Scope.Scope, + finalizer: Scope.Scope.Finalizer +): Effect.Effect => self.addFinalizer(finalizer) + +/* @internal */ +export const scopeClose = ( + self: Scope.Scope.Closeable, + exit: Exit.Exit +): Effect.Effect => self.close(exit) + +/* @internal */ +export const scopeFork = ( + self: Scope.Scope, + strategy: ExecutionStrategy.ExecutionStrategy +): Effect.Effect => self.fork(strategy) + +// ----------------------------------------------------------------------------- +// Cause +// ----------------------------------------------------------------------------- + +/** @internal */ +export const causeSquash = (self: Cause.Cause): unknown => { + return causeSquashWith(identity)(self) +} + +/** @internal */ +export const causeSquashWith = dual< + (f: (error: E) => unknown) => (self: Cause.Cause) => unknown, + (self: Cause.Cause, f: (error: E) => unknown) => unknown +>(2, (self, f) => { + const option = pipe(self, internalCause.failureOption, Option.map(f)) + switch (option._tag) { + case "None": { + return pipe( + internalCause.defects(self), + Chunk.head, + Option.match({ + onNone: () => { + const interrupts = Arr.fromIterable(internalCause.interruptors(self)).flatMap((fiberId) => + Arr.fromIterable(FiberId.ids(fiberId)).map((id) => `#${id}`) + ) + return new InterruptedException(interrupts ? `Interrupted by fibers: ${interrupts.join(", ")}` : void 0) + }, + onSome: identity + }) + ) + } + case "Some": { + return option.value + } + } +}) + +// ----------------------------------------------------------------------------- +// Errors +// ----------------------------------------------------------------------------- + +/** @internal */ +export const YieldableError: new(message?: string, options?: ErrorOptions) => Cause.YieldableError = (function() { + class YieldableError extends globalThis.Error { + commit() { + return fail(this) + } + toJSON() { + const obj = { ...this } + if (this.message) obj.message = this.message + if (this.cause) obj.cause = this.cause + return obj + } + [NodeInspectSymbol]() { + if (this.toString !== globalThis.Error.prototype.toString) { + return this.stack ? `${this.toString()}\n${this.stack.split("\n").slice(1).join("\n")}` : this.toString() + } else if ("Bun" in globalThis) { + return internalCause.pretty(internalCause.fail(this), { renderErrorCause: true }) + } + return this + } + } + // @effect-diagnostics-next-line floatingEffect:off + Object.assign(YieldableError.prototype, StructuralCommitPrototype) + return YieldableError as any +})() + +const makeException = ( + proto: Omit, + tag: T["_tag"] +): new(message?: string | undefined) => T => { + class Base extends YieldableError { + readonly _tag = tag + } + Object.assign(Base.prototype, proto) + ;(Base.prototype as any).name = tag + return Base as any +} + +/** @internal */ +export const RuntimeExceptionTypeId: Cause.RuntimeExceptionTypeId = Symbol.for( + "effect/Cause/errors/RuntimeException" +) as Cause.RuntimeExceptionTypeId + +/** @internal */ +export const RuntimeException = makeException({ + [RuntimeExceptionTypeId]: RuntimeExceptionTypeId +}, "RuntimeException") + +/** @internal */ +export const isRuntimeException = (u: unknown): u is Cause.RuntimeException => hasProperty(u, RuntimeExceptionTypeId) + +/** @internal */ +export const InterruptedExceptionTypeId: Cause.InterruptedExceptionTypeId = Symbol.for( + "effect/Cause/errors/InterruptedException" +) as Cause.InterruptedExceptionTypeId + +/** @internal */ +export const InterruptedException = makeException({ + [InterruptedExceptionTypeId]: InterruptedExceptionTypeId +}, "InterruptedException") + +/** @internal */ +export const isInterruptedException = (u: unknown): u is Cause.InterruptedException => + hasProperty(u, InterruptedExceptionTypeId) + +/** @internal */ +export const IllegalArgumentExceptionTypeId: Cause.IllegalArgumentExceptionTypeId = Symbol.for( + "effect/Cause/errors/IllegalArgument" +) as Cause.IllegalArgumentExceptionTypeId + +/** @internal */ +export const IllegalArgumentException = makeException({ + [IllegalArgumentExceptionTypeId]: IllegalArgumentExceptionTypeId +}, "IllegalArgumentException") + +/** @internal */ +export const isIllegalArgumentException = (u: unknown): u is Cause.IllegalArgumentException => + hasProperty(u, IllegalArgumentExceptionTypeId) + +/** @internal */ +export const NoSuchElementExceptionTypeId: Cause.NoSuchElementExceptionTypeId = Symbol.for( + "effect/Cause/errors/NoSuchElement" +) as Cause.NoSuchElementExceptionTypeId + +/** @internal */ +export const NoSuchElementException = makeException({ + [NoSuchElementExceptionTypeId]: NoSuchElementExceptionTypeId +}, "NoSuchElementException") + +/** @internal */ +export const isNoSuchElementException = (u: unknown): u is Cause.NoSuchElementException => + hasProperty(u, NoSuchElementExceptionTypeId) + +/** @internal */ +export const InvalidPubSubCapacityExceptionTypeId: Cause.InvalidPubSubCapacityExceptionTypeId = Symbol.for( + "effect/Cause/errors/InvalidPubSubCapacityException" +) as Cause.InvalidPubSubCapacityExceptionTypeId + +/** @internal */ +export const InvalidPubSubCapacityException = makeException({ + [InvalidPubSubCapacityExceptionTypeId]: InvalidPubSubCapacityExceptionTypeId +}, "InvalidPubSubCapacityException") + +/** @internal */ +export const ExceededCapacityExceptionTypeId: Cause.ExceededCapacityExceptionTypeId = Symbol.for( + "effect/Cause/errors/ExceededCapacityException" +) as Cause.ExceededCapacityExceptionTypeId + +/** @internal */ +export const ExceededCapacityException = makeException({ + [ExceededCapacityExceptionTypeId]: ExceededCapacityExceptionTypeId +}, "ExceededCapacityException") + +/** @internal */ +export const isExceededCapacityException = (u: unknown): u is Cause.ExceededCapacityException => + hasProperty(u, ExceededCapacityExceptionTypeId) + +/** @internal */ +export const isInvalidCapacityError = (u: unknown): u is Cause.InvalidPubSubCapacityException => + hasProperty(u, InvalidPubSubCapacityExceptionTypeId) + +/** @internal */ +export const TimeoutExceptionTypeId: Cause.TimeoutExceptionTypeId = Symbol.for( + "effect/Cause/errors/Timeout" +) as Cause.TimeoutExceptionTypeId + +/** @internal */ +export const TimeoutException = makeException({ + [TimeoutExceptionTypeId]: TimeoutExceptionTypeId +}, "TimeoutException") + +/** @internal */ +export const timeoutExceptionFromDuration = (duration: Duration.DurationInput): Cause.TimeoutException => + new TimeoutException(`Operation timed out after '${Duration.format(duration)}'`) + +/** @internal */ +export const isTimeoutException = (u: unknown): u is Cause.TimeoutException => hasProperty(u, TimeoutExceptionTypeId) + +/** @internal */ +export const UnknownExceptionTypeId: Cause.UnknownExceptionTypeId = Symbol.for( + "effect/Cause/errors/UnknownException" +) as Cause.UnknownExceptionTypeId + +/** @internal */ +export const UnknownException: new(cause: unknown, message?: string | undefined) => Cause.UnknownException = + (function() { + class UnknownException extends YieldableError { + readonly _tag = "UnknownException" + readonly error: unknown + constructor(cause: unknown, message?: string) { + super(message ?? "An unknown error occurred", { cause }) + this.error = cause + } + } + Object.assign(UnknownException.prototype, { + [UnknownExceptionTypeId]: UnknownExceptionTypeId, + name: "UnknownException" + }) + return UnknownException as any + })() + +/** @internal */ +export const isUnknownException = (u: unknown): u is Cause.UnknownException => hasProperty(u, UnknownExceptionTypeId) + +// ----------------------------------------------------------------------------- +// Exit +// ----------------------------------------------------------------------------- + +/** @internal */ +export const exitIsExit = (u: unknown): u is Exit.Exit => + isEffect(u) && "_tag" in u && (u._tag === "Success" || u._tag === "Failure") + +/** @internal */ +export const exitIsFailure = (self: Exit.Exit): self is Exit.Failure => self._tag === "Failure" + +/** @internal */ +export const exitIsSuccess = (self: Exit.Exit): self is Exit.Success => self._tag === "Success" + +/** @internal */ +export const exitIsInterrupted = (self: Exit.Exit): boolean => { + switch (self._tag) { + case OpCodes.OP_FAILURE: + return internalCause.isInterrupted(self.effect_instruction_i0) + case OpCodes.OP_SUCCESS: + return false + } +} + +/** @internal */ +export const exitAs = dual< + (value: A2) => (self: Exit.Exit) => Exit.Exit, + (self: Exit.Exit, value: A2) => Exit.Exit +>(2, (self: Exit.Exit, value: A2): Exit.Exit => { + switch (self._tag) { + case OpCodes.OP_FAILURE: { + return exitFailCause(self.effect_instruction_i0) + } + case OpCodes.OP_SUCCESS: { + return exitSucceed(value) as Exit.Exit + } + } +}) + +/** @internal */ +export const exitAsVoid = (self: Exit.Exit): Exit.Exit => exitAs(self, void 0) + +/** @internal */ +export const exitCauseOption = (self: Exit.Exit): Option.Option> => { + switch (self._tag) { + case OpCodes.OP_FAILURE: + return Option.some(self.effect_instruction_i0) + case OpCodes.OP_SUCCESS: + return Option.none() + } +} + +/** @internal */ +export const exitCollectAll = ( + exits: Iterable>, + options?: { + readonly parallel?: boolean | undefined + } +): Option.Option, E>> => + exitCollectAllInternal(exits, options?.parallel ? internalCause.parallel : internalCause.sequential) + +/** @internal */ +export const exitDie = (defect: unknown): Exit.Exit => + exitFailCause(internalCause.die(defect)) as Exit.Exit + +/** @internal */ +export const exitExists: { + (refinement: Refinement, B>): (self: Exit.Exit) => self is Exit.Exit + (predicate: Predicate>): (self: Exit.Exit) => boolean + (self: Exit.Exit, refinement: Refinement): self is Exit.Exit + (self: Exit.Exit, predicate: Predicate): boolean +} = dual(2, (self: Exit.Exit, refinement: Refinement): self is Exit.Exit => { + switch (self._tag) { + case OpCodes.OP_FAILURE: + return false + case OpCodes.OP_SUCCESS: + return refinement(self.effect_instruction_i0) + } +}) + +/** @internal */ +export const exitFail = (error: E): Exit.Exit => + exitFailCause(internalCause.fail(error)) as Exit.Exit + +/** @internal */ +export const exitFailCause = (cause: Cause.Cause): Exit.Exit => { + const effect = new EffectPrimitiveFailure(OpCodes.OP_FAILURE) as any + effect.effect_instruction_i0 = cause + return effect +} + +/** @internal */ +export const exitFlatMap = dual< + (f: (a: A) => Exit.Exit) => (self: Exit.Exit) => Exit.Exit, + (self: Exit.Exit, f: (a: A) => Exit.Exit) => Exit.Exit +>(2, (self: Exit.Exit, f: (a: A) => Exit.Exit): Exit.Exit => { + switch (self._tag) { + case OpCodes.OP_FAILURE: { + return exitFailCause(self.effect_instruction_i0) + } + case OpCodes.OP_SUCCESS: { + return f(self.effect_instruction_i0) + } + } +}) + +/** @internal */ +export const exitFlatMapEffect: { + ( + f: (a: A) => Effect.Effect, E2, R> + ): (self: Exit.Exit) => Effect.Effect, E2, R> + ( + self: Exit.Exit, + f: (a: A) => Effect.Effect, E2, R> + ): Effect.Effect, E2, R> +} = dual(2, ( + self: Exit.Exit, + f: (a: A) => Effect.Effect, E2, R> +): Effect.Effect, E2, R> => { + switch (self._tag) { + case OpCodes.OP_FAILURE: { + return succeed(exitFailCause(self.effect_instruction_i0)) + } + case OpCodes.OP_SUCCESS: { + return f(self.effect_instruction_i0) + } + } +}) + +/** @internal */ +export const exitFlatten = ( + self: Exit.Exit, E2> +): Exit.Exit => pipe(self, exitFlatMap(identity)) + +/** @internal */ +export const exitForEachEffect: { + ( + f: (a: A) => Effect.Effect + ): (self: Exit.Exit) => Effect.Effect, never, R> + ( + self: Exit.Exit, + f: (a: A) => Effect.Effect + ): Effect.Effect, never, R> +} = dual(2, ( + self: Exit.Exit, + f: (a: A) => Effect.Effect +): Effect.Effect, never, R> => { + switch (self._tag) { + case OpCodes.OP_FAILURE: { + return succeed(exitFailCause(self.effect_instruction_i0)) + } + case OpCodes.OP_SUCCESS: { + return exit(f(self.effect_instruction_i0)) + } + } +}) + +/** @internal */ +export const exitFromEither = (either: Either.Either): Exit.Exit => { + switch (either._tag) { + case "Left": + return exitFail(either.left) + case "Right": + return exitSucceed(either.right) + } +} + +/** @internal */ +export const exitFromOption = (option: Option.Option): Exit.Exit => { + switch (option._tag) { + case "None": + return exitFail(void 0) + case "Some": + return exitSucceed(option.value) + } +} + +/** @internal */ +export const exitGetOrElse = dual< + (orElse: (cause: Cause.Cause) => A2) => (self: Exit.Exit) => A | A2, + (self: Exit.Exit, orElse: (cause: Cause.Cause) => A2) => A | A2 +>(2, (self, orElse) => { + switch (self._tag) { + case OpCodes.OP_FAILURE: + return orElse(self.effect_instruction_i0) + case OpCodes.OP_SUCCESS: + return self.effect_instruction_i0 + } +}) + +/** @internal */ +export const exitInterrupt = (fiberId: FiberId.FiberId): Exit.Exit => + exitFailCause(internalCause.interrupt(fiberId)) + +/** @internal */ +export const exitMap = dual< + (f: (a: A) => B) => (self: Exit.Exit) => Exit.Exit, + (self: Exit.Exit, f: (a: A) => B) => Exit.Exit +>(2, (self, f) => { + switch (self._tag) { + case OpCodes.OP_FAILURE: + return exitFailCause(self.effect_instruction_i0) + case OpCodes.OP_SUCCESS: + return exitSucceed(f(self.effect_instruction_i0)) + } +}) + +/** @internal */ +export const exitMapBoth = dual< + ( + options: { + readonly onFailure: (e: E) => E2 + readonly onSuccess: (a: A) => A2 + } + ) => (self: Exit.Exit) => Exit.Exit, + ( + self: Exit.Exit, + options: { + readonly onFailure: (e: E) => E2 + readonly onSuccess: (a: A) => A2 + } + ) => Exit.Exit +>(2, (self, { onFailure, onSuccess }) => { + switch (self._tag) { + case OpCodes.OP_FAILURE: + return exitFailCause(pipe(self.effect_instruction_i0, internalCause.map(onFailure))) + case OpCodes.OP_SUCCESS: + return exitSucceed(onSuccess(self.effect_instruction_i0)) + } +}) + +/** @internal */ +export const exitMapError = dual< + (f: (e: E) => E2) => (self: Exit.Exit) => Exit.Exit, + (self: Exit.Exit, f: (e: E) => E2) => Exit.Exit +>(2, (self, f) => { + switch (self._tag) { + case OpCodes.OP_FAILURE: + return exitFailCause(pipe(self.effect_instruction_i0, internalCause.map(f))) + case OpCodes.OP_SUCCESS: + return exitSucceed(self.effect_instruction_i0) + } +}) + +/** @internal */ +export const exitMapErrorCause = dual< + (f: (cause: Cause.Cause) => Cause.Cause) => (self: Exit.Exit) => Exit.Exit, + (self: Exit.Exit, f: (cause: Cause.Cause) => Cause.Cause) => Exit.Exit +>(2, (self, f) => { + switch (self._tag) { + case OpCodes.OP_FAILURE: + return exitFailCause(f(self.effect_instruction_i0)) + case OpCodes.OP_SUCCESS: + return exitSucceed(self.effect_instruction_i0) + } +}) + +/** @internal */ +export const exitMatch = dual< + (options: { + readonly onFailure: (cause: Cause.Cause) => Z1 + readonly onSuccess: (a: A) => Z2 + }) => (self: Exit.Exit) => Z1 | Z2, + (self: Exit.Exit, options: { + readonly onFailure: (cause: Cause.Cause) => Z1 + readonly onSuccess: (a: A) => Z2 + }) => Z1 | Z2 +>(2, (self, { onFailure, onSuccess }) => { + switch (self._tag) { + case OpCodes.OP_FAILURE: + return onFailure(self.effect_instruction_i0) + case OpCodes.OP_SUCCESS: + return onSuccess(self.effect_instruction_i0) + } +}) + +/** @internal */ +export const exitMatchEffect = dual< + ( + options: { + readonly onFailure: (cause: Cause.Cause) => Effect.Effect + readonly onSuccess: (a: A) => Effect.Effect + } + ) => (self: Exit.Exit) => Effect.Effect, + ( + self: Exit.Exit, + options: { + readonly onFailure: (cause: Cause.Cause) => Effect.Effect + readonly onSuccess: (a: A) => Effect.Effect + } + ) => Effect.Effect +>(2, (self, { onFailure, onSuccess }) => { + switch (self._tag) { + case OpCodes.OP_FAILURE: + return onFailure(self.effect_instruction_i0) + case OpCodes.OP_SUCCESS: + return onSuccess(self.effect_instruction_i0) + } +}) + +/** @internal */ +export const exitSucceed = (value: A): Exit.Exit => { + const effect = new EffectPrimitiveSuccess(OpCodes.OP_SUCCESS) as any + effect.effect_instruction_i0 = value + return effect +} + +/** @internal */ +export const exitVoid: Exit.Exit = exitSucceed(void 0) + +/** @internal */ +export const exitZip = dual< + (that: Exit.Exit) => (self: Exit.Exit) => Exit.Exit<[A, A2], E | E2>, + (self: Exit.Exit, that: Exit.Exit) => Exit.Exit<[A, A2], E | E2> +>(2, (self, that) => + exitZipWith(self, that, { + onSuccess: (a, a2) => [a, a2], + onFailure: internalCause.sequential + })) + +/** @internal */ +export const exitZipLeft = dual< + (that: Exit.Exit) => (self: Exit.Exit) => Exit.Exit, + (self: Exit.Exit, that: Exit.Exit) => Exit.Exit +>(2, (self, that) => + exitZipWith(self, that, { + onSuccess: (a, _) => a, + onFailure: internalCause.sequential + })) + +/** @internal */ +export const exitZipRight = dual< + (that: Exit.Exit) => (self: Exit.Exit) => Exit.Exit, + (self: Exit.Exit, that: Exit.Exit) => Exit.Exit +>(2, (self, that) => + exitZipWith(self, that, { + onSuccess: (_, a2) => a2, + onFailure: internalCause.sequential + })) + +/** @internal */ +export const exitZipPar = dual< + (that: Exit.Exit) => (self: Exit.Exit) => Exit.Exit<[A, A2], E | E2>, + (self: Exit.Exit, that: Exit.Exit) => Exit.Exit<[A, A2], E | E2> +>(2, (self, that) => + exitZipWith(self, that, { + onSuccess: (a, a2) => [a, a2], + onFailure: internalCause.parallel + })) + +/** @internal */ +export const exitZipParLeft = dual< + (that: Exit.Exit) => (self: Exit.Exit) => Exit.Exit, + (self: Exit.Exit, that: Exit.Exit) => Exit.Exit +>(2, (self, that) => + exitZipWith(self, that, { + onSuccess: (a, _) => a, + onFailure: internalCause.parallel + })) + +/** @internal */ +export const exitZipParRight = dual< + (that: Exit.Exit) => (self: Exit.Exit) => Exit.Exit, + (self: Exit.Exit, that: Exit.Exit) => Exit.Exit +>(2, (self, that) => + exitZipWith(self, that, { + onSuccess: (_, a2) => a2, + onFailure: internalCause.parallel + })) + +/** @internal */ +export const exitZipWith = dual< + ( + that: Exit.Exit, + options: { + readonly onSuccess: (a: A, b: B) => C + readonly onFailure: (cause: Cause.Cause, cause2: Cause.Cause) => Cause.Cause + } + ) => (self: Exit.Exit) => Exit.Exit, + ( + self: Exit.Exit, + that: Exit.Exit, + options: { + readonly onSuccess: (a: A, b: B) => C + readonly onFailure: (cause: Cause.Cause, cause2: Cause.Cause) => Cause.Cause + } + ) => Exit.Exit +>(3, ( + self, + that, + { onFailure, onSuccess } +) => { + switch (self._tag) { + case OpCodes.OP_FAILURE: { + switch (that._tag) { + case OpCodes.OP_SUCCESS: + return exitFailCause(self.effect_instruction_i0) + case OpCodes.OP_FAILURE: { + return exitFailCause(onFailure(self.effect_instruction_i0, that.effect_instruction_i0)) + } + } + } + case OpCodes.OP_SUCCESS: { + switch (that._tag) { + case OpCodes.OP_SUCCESS: + return exitSucceed(onSuccess(self.effect_instruction_i0, that.effect_instruction_i0)) + case OpCodes.OP_FAILURE: + return exitFailCause(that.effect_instruction_i0) + } + } + } +}) + +const exitCollectAllInternal = ( + exits: Iterable>, + combineCauses: (causeA: Cause.Cause, causeB: Cause.Cause) => Cause.Cause +): Option.Option, E>> => { + const list = Chunk.fromIterable(exits) + if (!Chunk.isNonEmpty(list)) { + return Option.none() + } + return pipe( + Chunk.tailNonEmpty(list), + Arr.reduce( + pipe(Chunk.headNonEmpty(list), exitMap>(Chunk.of)), + (accumulator, current) => + pipe( + accumulator, + exitZipWith(current, { + onSuccess: (list, value) => pipe(list, Chunk.prepend(value)), + onFailure: combineCauses + }) + ) + ), + exitMap(Chunk.reverse), + exitMap((chunk) => Chunk.toReadonlyArray(chunk) as Array), + Option.some + ) +} + +// ----------------------------------------------------------------------------- +// Deferred +// ----------------------------------------------------------------------------- + +/** @internal */ +export const deferredUnsafeMake = (fiberId: FiberId.FiberId): Deferred.Deferred => { + const _deferred = { + ...CommitPrototype, + [deferred.DeferredTypeId]: deferred.deferredVariance, + state: MutableRef.make(deferred.pending([])), + commit() { + return deferredAwait(this) + }, + blockingOn: fiberId + } + return _deferred +} + +/* @internal */ +export const deferredMake = (): Effect.Effect> => + flatMap(fiberId, (id) => deferredMakeAs(id)) + +/* @internal */ +export const deferredMakeAs = (fiberId: FiberId.FiberId): Effect.Effect> => + sync(() => deferredUnsafeMake(fiberId)) + +/* @internal */ +export const deferredAwait = (self: Deferred.Deferred): Effect.Effect => + asyncInterrupt((resume) => { + const state = MutableRef.get(self.state) + switch (state._tag) { + case DeferredOpCodes.OP_STATE_DONE: { + return resume(state.effect) + } + case DeferredOpCodes.OP_STATE_PENDING: { + // we can push here as the internal state is mutable + state.joiners.push(resume) + return deferredInterruptJoiner(self, resume) + } + } + }, self.blockingOn) + +/* @internal */ +export const deferredComplete: { + (effect: Effect.Effect): (self: Deferred.Deferred) => Effect.Effect + (self: Deferred.Deferred, effect: Effect.Effect): Effect.Effect +} = dual( + 2, + (self: Deferred.Deferred, effect: Effect.Effect): Effect.Effect => + intoDeferred(effect, self) +) + +/* @internal */ +export const deferredCompleteWith = dual< + (effect: Effect.Effect) => (self: Deferred.Deferred) => Effect.Effect, + (self: Deferred.Deferred, effect: Effect.Effect) => Effect.Effect +>(2, (self, effect) => + sync(() => { + const state = MutableRef.get(self.state) + switch (state._tag) { + case DeferredOpCodes.OP_STATE_DONE: { + return false + } + case DeferredOpCodes.OP_STATE_PENDING: { + MutableRef.set(self.state, deferred.done(effect)) + for (let i = 0, len = state.joiners.length; i < len; i++) { + state.joiners[i](effect) + } + return true + } + } + })) + +/* @internal */ +export const deferredDone = dual< + (exit: Exit.Exit) => (self: Deferred.Deferred) => Effect.Effect, + (self: Deferred.Deferred, exit: Exit.Exit) => Effect.Effect +>(2, (self, exit) => deferredCompleteWith(self, exit)) + +/* @internal */ +export const deferredFail = dual< + (error: E) => (self: Deferred.Deferred) => Effect.Effect, + (self: Deferred.Deferred, error: E) => Effect.Effect +>(2, (self, error) => deferredCompleteWith(self, fail(error))) + +/* @internal */ +export const deferredFailSync = dual< + (evaluate: LazyArg) => (self: Deferred.Deferred) => Effect.Effect, + (self: Deferred.Deferred, evaluate: LazyArg) => Effect.Effect +>(2, (self, evaluate) => deferredCompleteWith(self, failSync(evaluate))) + +/* @internal */ +export const deferredFailCause = dual< + (cause: Cause.Cause) => (self: Deferred.Deferred) => Effect.Effect, + (self: Deferred.Deferred, cause: Cause.Cause) => Effect.Effect +>(2, (self, cause) => deferredCompleteWith(self, failCause(cause))) + +/* @internal */ +export const deferredFailCauseSync = dual< + (evaluate: LazyArg>) => (self: Deferred.Deferred) => Effect.Effect, + (self: Deferred.Deferred, evaluate: LazyArg>) => Effect.Effect +>(2, (self, evaluate) => deferredCompleteWith(self, failCauseSync(evaluate))) + +/* @internal */ +export const deferredDie = dual< + (defect: unknown) => (self: Deferred.Deferred) => Effect.Effect, + (self: Deferred.Deferred, defect: unknown) => Effect.Effect +>(2, (self, defect) => deferredCompleteWith(self, die(defect))) + +/* @internal */ +export const deferredDieSync = dual< + (evaluate: LazyArg) => (self: Deferred.Deferred) => Effect.Effect, + (self: Deferred.Deferred, evaluate: LazyArg) => Effect.Effect +>(2, (self, evaluate) => deferredCompleteWith(self, dieSync(evaluate))) + +/* @internal */ +export const deferredInterrupt = (self: Deferred.Deferred): Effect.Effect => + flatMap(fiberId, (fiberId) => deferredCompleteWith(self, interruptWith(fiberId))) + +/* @internal */ +export const deferredInterruptWith = dual< + (fiberId: FiberId.FiberId) => (self: Deferred.Deferred) => Effect.Effect, + (self: Deferred.Deferred, fiberId: FiberId.FiberId) => Effect.Effect +>(2, (self, fiberId) => deferredCompleteWith(self, interruptWith(fiberId))) + +/* @internal */ +export const deferredIsDone = (self: Deferred.Deferred): Effect.Effect => + sync(() => MutableRef.get(self.state)._tag === DeferredOpCodes.OP_STATE_DONE) + +/* @internal */ +export const deferredPoll = ( + self: Deferred.Deferred +): Effect.Effect>> => + sync(() => { + const state = MutableRef.get(self.state) + switch (state._tag) { + case DeferredOpCodes.OP_STATE_DONE: { + return Option.some(state.effect) + } + case DeferredOpCodes.OP_STATE_PENDING: { + return Option.none() + } + } + }) + +/* @internal */ +export const deferredSucceed = dual< + (value: A) => (self: Deferred.Deferred) => Effect.Effect, + (self: Deferred.Deferred, value: A) => Effect.Effect +>(2, (self, value) => deferredCompleteWith(self, succeed(value))) + +/* @internal */ +export const deferredSync = dual< + (evaluate: LazyArg) => (self: Deferred.Deferred) => Effect.Effect, + (self: Deferred.Deferred, evaluate: LazyArg) => Effect.Effect +>(2, (self, evaluate) => deferredCompleteWith(self, sync(evaluate))) + +/** @internal */ +export const deferredUnsafeDone = (self: Deferred.Deferred, effect: Effect.Effect): void => { + const state = MutableRef.get(self.state) + if (state._tag === DeferredOpCodes.OP_STATE_PENDING) { + MutableRef.set(self.state, deferred.done(effect)) + for (let i = 0, len = state.joiners.length; i < len; i++) { + state.joiners[i](effect) + } + } +} + +const deferredInterruptJoiner = ( + self: Deferred.Deferred, + joiner: (effect: Effect.Effect) => void +): Effect.Effect => + sync(() => { + const state = MutableRef.get(self.state) + if (state._tag === DeferredOpCodes.OP_STATE_PENDING) { + const index = state.joiners.indexOf(joiner) + if (index >= 0) { + // we can splice here as the internal state is mutable + state.joiners.splice(index, 1) + } + } + }) + +// ----------------------------------------------------------------------------- +// Context +// ----------------------------------------------------------------------------- + +const constContext = withFiberRuntime((fiber) => exitSucceed(fiber.currentContext)) + +/* @internal */ +export const context = (): Effect.Effect, never, R> => constContext as any + +/* @internal */ +export const contextWith = ( + f: (context: Context.Context) => A +): Effect.Effect => map(context(), f) + +/* @internal */ +export const contextWithEffect = ( + f: (context: Context.Context) => Effect.Effect +): Effect.Effect => flatMap(context(), f) + +/* @internal */ +export const provideContext = dual< + (context: Context.Context) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, context: Context.Context) => Effect.Effect +>(2, (self: Effect.Effect, context: Context.Context) => + fiberRefLocally( + currentContext, + context + )(self as Effect.Effect)) + +/* @internal */ +export const provideSomeContext = dual< + (context: Context.Context) => (self: Effect.Effect) => Effect.Effect>, + (self: Effect.Effect, context: Context.Context) => Effect.Effect> +>(2, (self: Effect.Effect, context: Context.Context) => + fiberRefLocallyWith( + currentContext, + (parent) => Context.merge(parent, context) + )(self as Effect.Effect)) + +/* @internal */ +export const mapInputContext = dual< + ( + f: (context: Context.Context) => Context.Context + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + f: (context: Context.Context) => Context.Context + ) => Effect.Effect +>(2, ( + self: Effect.Effect, + f: (context: Context.Context) => Context.Context +) => contextWithEffect((context: Context.Context) => provideContext(self, f(context)))) + +// ----------------------------------------------------------------------------- +// Filtering +// ----------------------------------------------------------------------------- + +/** @internal */ +export const filterEffectOrElse: { + ( + options: { + readonly predicate: (a: NoInfer) => Effect.Effect + readonly orElse: (a: NoInfer) => Effect.Effect + } + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + options: { + readonly predicate: (a: A) => Effect.Effect + readonly orElse: (a: A) => Effect.Effect + } + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + options: { + readonly predicate: (a: A) => Effect.Effect + readonly orElse: (a: A) => Effect.Effect + } +): Effect.Effect => + flatMap( + self, + (a) => + flatMap( + options.predicate(a), + (pass): Effect.Effect => pass ? succeed(a) : options.orElse(a) + ) + )) + +/** @internal */ +export const filterEffectOrFail: { + ( + options: { + readonly predicate: (a: NoInfer) => Effect.Effect + readonly orFailWith: (a: NoInfer) => E3 + } + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + options: { + readonly predicate: (a: A) => Effect.Effect + readonly orFailWith: (a: A) => E3 + } + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + options: { + readonly predicate: (a: A) => Effect.Effect + readonly orFailWith: (a: A) => E3 + } +): Effect.Effect => + filterEffectOrElse(self, { + predicate: options.predicate, + orElse: (a) => fail(options.orFailWith(a)) + })) + +// ----------------------------------------------------------------------------- +// Tracing +// ----------------------------------------------------------------------------- + +/** @internal */ +export const currentSpanFromFiber = (fiber: Fiber.RuntimeFiber): Option.Option => { + const span = fiber.currentSpan + return span !== undefined && span._tag === "Span" ? Option.some(span) : Option.none() +} + +const NoopSpanProto: Omit = { + _tag: "Span", + spanId: "noop", + traceId: "noop", + sampled: false, + status: { + _tag: "Ended", + startTime: BigInt(0), + endTime: BigInt(0), + exit: exitVoid + }, + attributes: new Map(), + links: [], + kind: "internal", + attribute() {}, + event() {}, + end() {}, + addLinks() {} +} + +/** @internal */ +export const noopSpan = (options: { + readonly name: string + readonly parent: Option.Option + readonly context: Context.Context +}): Tracer.Span => Object.assign(Object.create(NoopSpanProto), options) diff --git a/backend/node_modules/effect/src/internal/data.ts b/backend/node_modules/effect/src/internal/data.ts new file mode 100644 index 0000000000000000000000000000000000000000..7ef534a25c4842fc2558fb1a0bb5415c3e054614 --- /dev/null +++ b/backend/node_modules/effect/src/internal/data.ts @@ -0,0 +1,36 @@ +import * as Equal from "../Equal.js" +import * as Hash from "../Hash.js" +import type * as Types from "../Types.js" +import { StructuralPrototype } from "./effectable.js" + +/** @internal */ +export const ArrayProto: Equal.Equal = Object.assign(Object.create(Array.prototype), { + [Hash.symbol](this: Array) { + return Hash.cached(this, Hash.array(this)) + }, + [Equal.symbol](this: Array, that: Equal.Equal) { + if (Array.isArray(that) && this.length === that.length) { + return this.every((v, i) => Equal.equals(v, (that as Array)[i])) + } else { + return false + } + } +}) + +/** @internal */ +export const Structural: new( + args: Types.Equals, {}> extends true ? void + : { readonly [P in keyof A as P extends keyof Equal.Equal ? never : P]: A[P] } +) => {} = (function() { + function Structural(this: any, args: any) { + if (args) { + Object.assign(this, args) + } + } + Structural.prototype = StructuralPrototype + return Structural as any +})() + +/** @internal */ +export const struct = >>(as: As): As => + Object.assign(Object.create(StructuralPrototype), as) diff --git a/backend/node_modules/effect/src/internal/dataSource.ts b/backend/node_modules/effect/src/internal/dataSource.ts new file mode 100644 index 0000000000000000000000000000000000000000..7dad11288eea737321a04a19831ab85fab4a3ac3 --- /dev/null +++ b/backend/node_modules/effect/src/internal/dataSource.ts @@ -0,0 +1,327 @@ +import * as RA from "../Array.js" +import * as Cause from "../Cause.js" +import * as Chunk from "../Chunk.js" +import type * as Context from "../Context.js" +import * as Effect from "../Effect.js" +import type * as Either from "../Either.js" +import { dual, pipe } from "../Function.js" +import type * as Request from "../Request.js" +import type * as RequestResolver from "../RequestResolver.js" +import type { NoInfer } from "../Types.js" +import * as core from "./core.js" +import { invokeWithInterrupt, zipWithOptions } from "./fiberRuntime.js" +import { complete } from "./request.js" + +/** @internal */ +export const make = ( + runAll: (requests: Array>) => Effect.Effect +): RequestResolver.RequestResolver => + new core.RequestResolverImpl((requests) => runAll(requests.map((_) => _.map((_) => _.request)))) + +/** @internal */ +export const makeWithEntry = ( + runAll: (requests: Array>>) => Effect.Effect +): RequestResolver.RequestResolver => new core.RequestResolverImpl((requests) => runAll(requests)) + +/** @internal */ +export const makeBatched = , R>( + run: (requests: RA.NonEmptyArray) => Effect.Effect +): RequestResolver.RequestResolver => + new core.RequestResolverImpl( + (requests) => { + if (requests.length > 1) { + return core.forEachSequentialDiscard(requests, (block) => { + const filtered = block.filter((_) => !_.state.completed).map((_) => _.request) + if (!RA.isNonEmptyArray(filtered)) { + return core.void + } + return invokeWithInterrupt(run(filtered), block) + }) + } else if (requests.length === 1) { + const filtered = requests[0].filter((_) => !_.state.completed).map((_) => _.request) + if (!RA.isNonEmptyArray(filtered)) { + return core.void + } + return run(filtered) + } + return core.void + } + ) + +/** @internal */ +export const around = dual< + ( + before: Effect.Effect, + after: (a: A2) => Effect.Effect + ) => ( + self: RequestResolver.RequestResolver + ) => RequestResolver.RequestResolver, + ( + self: RequestResolver.RequestResolver, + before: Effect.Effect, + after: (a: A2) => Effect.Effect + ) => RequestResolver.RequestResolver +>(3, (self, before, after) => + new core.RequestResolverImpl( + (requests) => + core.acquireUseRelease( + before, + () => self.runAll(requests), + after + ), + Chunk.make("Around", self, before, after) + )) + +/** @internal */ +export const aroundRequests = dual< + ( + before: (requests: ReadonlyArray>) => Effect.Effect, + after: (requests: ReadonlyArray>, _: A2) => Effect.Effect + ) => ( + self: RequestResolver.RequestResolver + ) => RequestResolver.RequestResolver, + ( + self: RequestResolver.RequestResolver, + before: (requests: ReadonlyArray>) => Effect.Effect, + after: (requests: ReadonlyArray>, _: A2) => Effect.Effect + ) => RequestResolver.RequestResolver +>(3, (self, before, after) => + new core.RequestResolverImpl( + (requests) => { + const flatRequests = requests.flatMap((chunk) => chunk.map((entry) => entry.request)) + return core.acquireUseRelease( + before(flatRequests), + () => self.runAll(requests), + (a2) => after(flatRequests, a2) + ) + }, + Chunk.make("AroundRequests", self, before, after) + )) + +/** @internal */ +export const batchN = dual< + (n: number) => ( + self: RequestResolver.RequestResolver + ) => RequestResolver.RequestResolver, + ( + self: RequestResolver.RequestResolver, + n: number + ) => RequestResolver.RequestResolver +>(2, ( + self: RequestResolver.RequestResolver, + n: number +): RequestResolver.RequestResolver => + new core.RequestResolverImpl( + (requests) => { + return n < 1 + ? core.die(new Cause.IllegalArgumentException("RequestResolver.batchN: n must be at least 1")) + : self.runAll( + Array.from(Chunk.map( + RA.reduce( + requests, + Chunk.empty>>(), + (acc, chunk) => Chunk.appendAll(acc, Chunk.chunksOf(Chunk.unsafeFromArray(chunk), n)) + ), + (chunk) => Array.from(chunk) + )) + ) + }, + Chunk.make("BatchN", self, n) + )) + +/** @internal */ +export const mapInputContext = dual< + ( + f: (context: Context.Context) => Context.Context + ) => >( + self: RequestResolver.RequestResolver + ) => RequestResolver.RequestResolver, + , R0>( + self: RequestResolver.RequestResolver, + f: (context: Context.Context) => Context.Context + ) => RequestResolver.RequestResolver +>(2, , R0>( + self: RequestResolver.RequestResolver, + f: (context: Context.Context) => Context.Context +) => + new core.RequestResolverImpl( + (requests) => + core.mapInputContext( + self.runAll(requests), + (context: Context.Context) => f(context) + ), + Chunk.make("MapInputContext", self, f) + )) + +/** @internal */ +export const eitherWith = dual< + < + A extends Request.Request, + R2, + B extends Request.Request, + C extends Request.Request + >( + that: RequestResolver.RequestResolver, + f: (_: Request.Entry) => Either.Either, Request.Entry> + ) => ( + self: RequestResolver.RequestResolver + ) => RequestResolver.RequestResolver, + < + R, + A extends Request.Request, + R2, + B extends Request.Request, + C extends Request.Request + >( + self: RequestResolver.RequestResolver, + that: RequestResolver.RequestResolver, + f: (_: Request.Entry) => Either.Either, Request.Entry> + ) => RequestResolver.RequestResolver +>(3, < + R, + A extends Request.Request, + R2, + B extends Request.Request, + C extends Request.Request +>( + self: RequestResolver.RequestResolver, + that: RequestResolver.RequestResolver, + f: (_: Request.Entry) => Either.Either, Request.Entry> +) => + new core.RequestResolverImpl( + (batch) => + core.forEachSequential(batch, (requests) => { + const [as, bs] = pipe( + requests, + RA.partitionMap(f) + ) + return zipWithOptions( + self.runAll(Array.of(as)), + that.runAll(Array.of(bs)), + () => void 0, + { concurrent: true } + ) + }), + Chunk.make("EitherWith", self, that, f) + )) + +/** @internal */ +export const fromFunction = >( + f: (request: A) => Request.Request.Success +): RequestResolver.RequestResolver => + makeBatched((requests: RA.NonEmptyArray) => + core.forEachSequentialDiscard( + requests, + (request) => complete(request, core.exitSucceed(f(request)) as any) + ) + ).identified("FromFunction", f) + +/** @internal */ +export const fromFunctionBatched = >( + f: (chunk: RA.NonEmptyArray) => Iterable> +): RequestResolver.RequestResolver => + makeBatched((as: RA.NonEmptyArray) => + Effect.forEach( + f(as), + (res, i) => complete(as[i], core.exitSucceed(res) as any), + { discard: true } + ) + ).identified("FromFunctionBatched", f) + +/** @internal */ +export const fromEffect = >( + f: (a: A) => Effect.Effect, Request.Request.Error, R> +): RequestResolver.RequestResolver => + makeBatched((requests: RA.NonEmptyArray) => + Effect.forEach( + requests, + (a) => Effect.flatMap(Effect.exit(f(a)), (e) => complete(a, e as any)), + { concurrency: "unbounded", discard: true } + ) + ).identified("FromEffect", f) + +/** @internal */ +export const fromEffectTagged = < + A extends Request.Request & { + readonly _tag: string + } +>() => +< + Fns extends { + readonly [Tag in A["_tag"]]: [Extract] extends [infer Req] ? + Req extends Request.Request ? + (requests: Array) => Effect.Effect, ReqE, any> + : never + : never + } +>( + fns: Fns +): RequestResolver.RequestResolver< + A, + ReturnType extends Effect.Effect ? R : never +> => + makeBatched((requests: RA.NonEmptyArray) => { + const grouped: Record> = {} + const tags: Array = [] + for (let i = 0, len = requests.length; i < len; i++) { + if (tags.includes(requests[i]._tag)) { + grouped[requests[i]._tag].push(requests[i]) + } else { + grouped[requests[i]._tag] = [requests[i]] + tags.push(requests[i]._tag) + } + } + return Effect.forEach( + tags, + (tag) => + Effect.matchCauseEffect((fns[tag] as any)(grouped[tag]) as Effect.Effect, unknown, unknown>, { + onFailure: (cause) => + Effect.forEach(grouped[tag], (req) => complete(req, core.exitFail(cause) as any), { discard: true }), + onSuccess: (res) => + Effect.forEach(grouped[tag], (req, i) => complete(req, core.exitSucceed(res[i]) as any), { discard: true }) + }), + { concurrency: "unbounded", discard: true } + ) + }).identified("FromEffectTagged", fns) + +/** @internal */ +export const never: RequestResolver.RequestResolver = make(() => Effect.never).identified("Never") + +/** @internal */ +export const provideContext = dual< + ( + context: Context.Context + ) => >( + self: RequestResolver.RequestResolver + ) => RequestResolver.RequestResolver, + >( + self: RequestResolver.RequestResolver, + context: Context.Context + ) => RequestResolver.RequestResolver +>(2, (self, context) => + mapInputContext( + self, + (_: Context.Context) => context + ).identified("ProvideContext", self, context)) + +/** @internal */ +export const race = dual< + , R2>( + that: RequestResolver.RequestResolver + ) => , R>( + self: RequestResolver.RequestResolver + ) => RequestResolver.RequestResolver, + , R, A2 extends Request.Request, R2>( + self: RequestResolver.RequestResolver, + that: RequestResolver.RequestResolver + ) => RequestResolver.RequestResolver +>(2, ( + self: RequestResolver.RequestResolver, + that: RequestResolver.RequestResolver +) => + new core.RequestResolverImpl((requests) => + Effect.race( + self.runAll(requests as Array>>), + that.runAll(requests as Array>>) + ) + ).identified("Race", self, that)) diff --git a/backend/node_modules/effect/src/internal/dateTime.ts b/backend/node_modules/effect/src/internal/dateTime.ts new file mode 100644 index 0000000000000000000000000000000000000000..544ecd86efad739500adc1bb0d582e4c80c64ad9 --- /dev/null +++ b/backend/node_modules/effect/src/internal/dateTime.ts @@ -0,0 +1,1277 @@ +import { IllegalArgumentException } from "../Cause.js" +import * as Clock from "../Clock.js" +import type * as DateTime from "../DateTime.js" +import * as Duration from "../Duration.js" +import type * as Effect from "../Effect.js" +import * as Either from "../Either.js" +import * as Equal from "../Equal.js" +import * as equivalence from "../Equivalence.js" +import type { LazyArg } from "../Function.js" +import { dual, pipe } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import * as Hash from "../Hash.js" +import * as Inspectable from "../Inspectable.js" +import * as Option from "../Option.js" +import * as order from "../Order.js" +import { pipeArguments } from "../Pipeable.js" +import * as Predicate from "../Predicate.js" +import type { Mutable } from "../Types.js" +import * as internalEffect from "./core-effect.js" +import * as core from "./core.js" + +/** @internal */ +export const TypeId: DateTime.TypeId = Symbol.for("effect/DateTime") as DateTime.TypeId + +/** @internal */ +export const TimeZoneTypeId: DateTime.TimeZoneTypeId = Symbol.for("effect/DateTime/TimeZone") as DateTime.TimeZoneTypeId + +const Proto = { + [TypeId]: TypeId, + pipe() { + return pipeArguments(this, arguments) + }, + [Inspectable.NodeInspectSymbol](this: DateTime.DateTime) { + return this.toString() + }, + toJSON(this: DateTime.DateTime) { + return toDateUtc(this).toJSON() + } +} + +const ProtoUtc = { + ...Proto, + _tag: "Utc", + [Hash.symbol](this: DateTime.Utc) { + return Hash.cached(this, Hash.number(this.epochMillis)) + }, + [Equal.symbol](this: DateTime.Utc, that: unknown) { + return isDateTime(that) && that._tag === "Utc" && this.epochMillis === that.epochMillis + }, + toString(this: DateTime.Utc) { + return `DateTime.Utc(${toDateUtc(this).toJSON()})` + } +} + +const ProtoZoned = { + ...Proto, + _tag: "Zoned", + [Hash.symbol](this: DateTime.Zoned) { + return pipe( + Hash.number(this.epochMillis), + Hash.combine(Hash.hash(this.zone)), + Hash.cached(this) + ) + }, + [Equal.symbol](this: DateTime.Zoned, that: unknown) { + return isDateTime(that) && that._tag === "Zoned" && this.epochMillis === that.epochMillis && + Equal.equals(this.zone, that.zone) + }, + toString(this: DateTime.Zoned) { + return `DateTime.Zoned(${formatIsoZoned(this)})` + } +} + +const ProtoTimeZone = { + [TimeZoneTypeId]: TimeZoneTypeId, + [Inspectable.NodeInspectSymbol](this: DateTime.TimeZone) { + return this.toString() + } +} + +const ProtoTimeZoneNamed = { + ...ProtoTimeZone, + _tag: "Named", + [Hash.symbol](this: DateTime.TimeZone.Named) { + return Hash.cached(this, Hash.string(`Named:${this.id}`)) + }, + [Equal.symbol](this: DateTime.TimeZone.Named, that: unknown) { + return isTimeZone(that) && that._tag === "Named" && this.id === that.id + }, + toString(this: DateTime.TimeZone.Named) { + return `TimeZone.Named(${this.id})` + }, + toJSON(this: DateTime.TimeZone.Named) { + return { + _id: "TimeZone", + _tag: "Named", + id: this.id + } + } +} + +const ProtoTimeZoneOffset = { + ...ProtoTimeZone, + _tag: "Offset", + [Hash.symbol](this: DateTime.TimeZone.Offset) { + return Hash.cached(this, Hash.string(`Offset:${this.offset}`)) + }, + [Equal.symbol](this: DateTime.TimeZone.Offset, that: unknown) { + return isTimeZone(that) && that._tag === "Offset" && this.offset === that.offset + }, + toString(this: DateTime.TimeZone.Offset) { + return `TimeZone.Offset(${offsetToString(this.offset)})` + }, + toJSON(this: DateTime.TimeZone.Offset) { + return { + _id: "TimeZone", + _tag: "Offset", + offset: this.offset + } + } +} + +/** @internal */ +export const makeZonedProto = ( + epochMillis: number, + zone: DateTime.TimeZone, + partsUtc?: DateTime.DateTime.PartsWithWeekday +): DateTime.Zoned => { + const self = Object.create(ProtoZoned) + self.epochMillis = epochMillis + self.zone = zone + Object.defineProperty(self, "partsUtc", { + value: partsUtc, + enumerable: false, + writable: true + }) + Object.defineProperty(self, "adjustedEpochMillis", { + value: undefined, + enumerable: false, + writable: true + }) + Object.defineProperty(self, "partsAdjusted", { + value: undefined, + enumerable: false, + writable: true + }) + return self +} + +// ============================================================================= +// guards +// ============================================================================= + +/** @internal */ +export const isDateTime = (u: unknown): u is DateTime.DateTime => Predicate.hasProperty(u, TypeId) + +const isDateTimeArgs = (args: IArguments) => isDateTime(args[0]) + +/** @internal */ +export const isTimeZone = (u: unknown): u is DateTime.TimeZone => Predicate.hasProperty(u, TimeZoneTypeId) + +/** @internal */ +export const isTimeZoneOffset = (u: unknown): u is DateTime.TimeZone.Offset => isTimeZone(u) && u._tag === "Offset" + +/** @internal */ +export const isTimeZoneNamed = (u: unknown): u is DateTime.TimeZone.Named => isTimeZone(u) && u._tag === "Named" + +/** @internal */ +export const isUtc = (self: DateTime.DateTime): self is DateTime.Utc => self._tag === "Utc" + +/** @internal */ +export const isZoned = (self: DateTime.DateTime): self is DateTime.Zoned => self._tag === "Zoned" + +// ============================================================================= +// instances +// ============================================================================= + +/** @internal */ +export const Equivalence: equivalence.Equivalence = equivalence.make((a, b) => + a.epochMillis === b.epochMillis +) + +/** @internal */ +export const Order: order.Order = order.make((self, that) => + self.epochMillis < that.epochMillis ? -1 : self.epochMillis > that.epochMillis ? 1 : 0 +) + +/** @internal */ +export const clamp: { + ( + options: { readonly minimum: Min; readonly maximum: Max } + ): (self: A) => A | Min | Max + ( + self: A, + options: { readonly minimum: Min; readonly maximum: Max } + ): A | Min | Max +} = order.clamp(Order) + +// ============================================================================= +// constructors +// ============================================================================= + +const makeUtc = (epochMillis: number): DateTime.Utc => { + const self = Object.create(ProtoUtc) + self.epochMillis = epochMillis + Object.defineProperty(self, "partsUtc", { + value: undefined, + enumerable: false, + writable: true + }) + return self +} + +/** @internal */ +export const unsafeFromDate = (date: Date): DateTime.Utc => { + const epochMillis = date.getTime() + if (Number.isNaN(epochMillis)) { + throw new IllegalArgumentException("Invalid date") + } + return makeUtc(epochMillis) +} + +/** @internal */ +export const unsafeMake = (input: A): DateTime.DateTime.PreserveZone => { + if (isDateTime(input)) { + return input as DateTime.DateTime.PreserveZone + } else if (input instanceof Date) { + return unsafeFromDate(input) as DateTime.DateTime.PreserveZone + } else if (typeof input === "object") { + const date = new Date(0) + setPartsDate(date, input) + return unsafeFromDate(date) as DateTime.DateTime.PreserveZone + } else if (typeof input === "string" && !hasZone(input)) { + return unsafeFromDate(new Date(input + "Z")) as DateTime.DateTime.PreserveZone + } + return unsafeFromDate(new Date(input)) as DateTime.DateTime.PreserveZone +} + +const hasZone = (input: string): boolean => /Z|[+-]\d{2}$|[+-]\d{2}:?\d{2}$|\]$/.test(input) + +const minEpochMillis = -8640000000000000 + (12 * 60 * 60 * 1000) +const maxEpochMillis = 8640000000000000 - (14 * 60 * 60 * 1000) + +/** @internal */ +export const unsafeMakeZoned = (input: DateTime.DateTime.Input, options?: { + readonly timeZone?: number | string | DateTime.TimeZone | undefined + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined +}): DateTime.Zoned => { + if (options?.timeZone === undefined && isDateTime(input) && isZoned(input)) { + return input + } + const self = unsafeMake(input) + if (self.epochMillis < minEpochMillis || self.epochMillis > maxEpochMillis) { + throw new RangeError(`Epoch millis out of range: ${self.epochMillis}`) + } + let zone: DateTime.TimeZone + if (options?.timeZone === undefined) { + const offset = new Date(self.epochMillis).getTimezoneOffset() * -60 * 1000 + zone = zoneMakeOffset(offset) + } else if (isTimeZone(options?.timeZone)) { + zone = options.timeZone + } else if (typeof options?.timeZone === "number") { + zone = zoneMakeOffset(options.timeZone) + } else { + const parsedZone = zoneFromString(options.timeZone) + if (Option.isNone(parsedZone)) { + throw new IllegalArgumentException(`Invalid time zone: ${options.timeZone}`) + } + zone = parsedZone.value + } + if (options?.adjustForTimeZone !== true) { + return makeZonedProto(self.epochMillis, zone, self.partsUtc) + } + return makeZonedFromAdjusted(self.epochMillis, zone, options?.disambiguation ?? "compatible") +} + +/** @internal */ +export const makeZoned: ( + input: DateTime.DateTime.Input, + options?: { + readonly timeZone?: number | string | DateTime.TimeZone | undefined + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined + } +) => Option.Option = Option.liftThrowable(unsafeMakeZoned) + +/** @internal */ +export const make: (input: A) => Option.Option> = + Option.liftThrowable(unsafeMake) + +const zonedStringRegex = /^(.{17,35})\[(.+)\]$/ + +/** @internal */ +export const makeZonedFromString = (input: string): Option.Option => { + const match = zonedStringRegex.exec(input) + if (match === null) { + const offset = parseOffset(input) + return offset !== null ? makeZoned(input, { timeZone: offset }) : Option.none() + } + const [, isoString, timeZone] = match + return makeZoned(isoString, { timeZone }) +} + +/** @internal */ +export const now: Effect.Effect = core.map(Clock.currentTimeMillis, makeUtc) + +/** @internal */ +export const nowAsDate: Effect.Effect = core.map(Clock.currentTimeMillis, (millis) => new Date(millis)) + +/** @internal */ +export const unsafeNow: LazyArg = () => makeUtc(Date.now()) + +// ============================================================================= +// time zones +// ============================================================================= + +/** @internal */ +export const toUtc = (self: DateTime.DateTime): DateTime.Utc => makeUtc(self.epochMillis) + +/** @internal */ +export const setZone: { + (zone: DateTime.TimeZone, options?: { + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined + }): (self: DateTime.DateTime) => DateTime.Zoned + (self: DateTime.DateTime, zone: DateTime.TimeZone, options?: { + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined + }): DateTime.Zoned +} = dual(isDateTimeArgs, (self: DateTime.DateTime, zone: DateTime.TimeZone, options?: { + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined +}): DateTime.Zoned => + options?.adjustForTimeZone === true + ? makeZonedFromAdjusted(self.epochMillis, zone, options?.disambiguation ?? "compatible") + : makeZonedProto(self.epochMillis, zone, self.partsUtc)) + +/** @internal */ +export const setZoneOffset: { + (offset: number, options?: { + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined + }): (self: DateTime.DateTime) => DateTime.Zoned + (self: DateTime.DateTime, offset: number, options?: { + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined + }): DateTime.Zoned +} = dual(isDateTimeArgs, (self: DateTime.DateTime, offset: number, options?: { + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined +}): DateTime.Zoned => setZone(self, zoneMakeOffset(offset), options)) + +const validZoneCache = globalValue("effect/DateTime/validZoneCache", () => new Map()) + +const formatOptions: Intl.DateTimeFormatOptions = { + day: "numeric", + month: "numeric", + year: "numeric", + hour: "numeric", + minute: "numeric", + second: "numeric", + timeZoneName: "longOffset", + fractionalSecondDigits: 3, + hourCycle: "h23" +} + +const zoneMakeIntl = (format: Intl.DateTimeFormat): DateTime.TimeZone.Named => { + const zoneId = format.resolvedOptions().timeZone + if (validZoneCache.has(zoneId)) { + return validZoneCache.get(zoneId)! + } + const zone = Object.create(ProtoTimeZoneNamed) + zone.id = zoneId + zone.format = format + validZoneCache.set(zoneId, zone) + return zone +} + +/** @internal */ +export const zoneUnsafeMakeNamed = (zoneId: string): DateTime.TimeZone.Named => { + if (validZoneCache.has(zoneId)) { + return validZoneCache.get(zoneId)! + } + try { + return zoneMakeIntl( + new Intl.DateTimeFormat("en-US", { + ...formatOptions, + timeZone: zoneId + }) + ) + } catch { + throw new IllegalArgumentException(`Invalid time zone: ${zoneId}`) + } +} + +/** @internal */ +export const zoneMakeOffset = (offset: number): DateTime.TimeZone.Offset => { + const zone = Object.create(ProtoTimeZoneOffset) + zone.offset = offset + return zone +} + +/** @internal */ +export const zoneMakeNamed: (zoneId: string) => Option.Option = Option.liftThrowable( + zoneUnsafeMakeNamed +) + +/** @internal */ +export const zoneMakeNamedEffect = (zoneId: string): Effect.Effect => + internalEffect.try_({ + try: () => zoneUnsafeMakeNamed(zoneId), + catch: (e) => e as IllegalArgumentException + }) + +/** @internal */ +export const zoneMakeLocal = (): DateTime.TimeZone.Named => + zoneMakeIntl(new Intl.DateTimeFormat("en-US", formatOptions)) + +const offsetZoneRegex = /^(?:GMT|[+-])/ + +/** @internal */ +export const zoneFromString = (zone: string): Option.Option => { + if (offsetZoneRegex.test(zone)) { + const offset = parseOffset(zone) + return offset === null ? Option.none() : Option.some(zoneMakeOffset(offset)) + } + return zoneMakeNamed(zone) +} + +/** @internal */ +export const zoneToString = (self: DateTime.TimeZone): string => { + if (self._tag === "Offset") { + return offsetToString(self.offset) + } + return self.id +} + +/** @internal */ +export const setZoneNamed: { + (zoneId: string, options?: { + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined + }): (self: DateTime.DateTime) => Option.Option + (self: DateTime.DateTime, zoneId: string, options?: { + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined + }): Option.Option +} = dual( + isDateTimeArgs, + (self: DateTime.DateTime, zoneId: string, options?: { + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined + }): Option.Option => Option.map(zoneMakeNamed(zoneId), (zone) => setZone(self, zone, options)) +) + +/** @internal */ +export const unsafeSetZoneNamed: { + (zoneId: string, options?: { + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined + }): (self: DateTime.DateTime) => DateTime.Zoned + (self: DateTime.DateTime, zoneId: string, options?: { + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined + }): DateTime.Zoned +} = dual(isDateTimeArgs, (self: DateTime.DateTime, zoneId: string, options?: { + readonly adjustForTimeZone?: boolean | undefined + readonly disambiguation?: DateTime.Disambiguation | undefined +}): DateTime.Zoned => setZone(self, zoneUnsafeMakeNamed(zoneId), options)) + +// ============================================================================= +// comparisons +// ============================================================================= + +/** @internal */ +export const distance: { + (other: DateTime.DateTime): (self: DateTime.DateTime) => number + (self: DateTime.DateTime, other: DateTime.DateTime): number +} = dual(2, (self: DateTime.DateTime, other: DateTime.DateTime): number => toEpochMillis(other) - toEpochMillis(self)) + +/** @internal */ +export const distanceDurationEither: { + (other: DateTime.DateTime): (self: DateTime.DateTime) => Either.Either + (self: DateTime.DateTime, other: DateTime.DateTime): Either.Either +} = dual( + 2, + (self: DateTime.DateTime, other: DateTime.DateTime): Either.Either => { + const diffMillis = distance(self, other) + return diffMillis > 0 + ? Either.right(Duration.millis(diffMillis)) + : Either.left(Duration.millis(-diffMillis)) + } +) + +/** @internal */ +export const distanceDuration: { + (other: DateTime.DateTime): (self: DateTime.DateTime) => Duration.Duration + (self: DateTime.DateTime, other: DateTime.DateTime): Duration.Duration +} = dual( + 2, + (self: DateTime.DateTime, other: DateTime.DateTime): Duration.Duration => + Duration.millis(Math.abs(distance(self, other))) +) + +/** @internal */ +export const min: { + (that: That): (self: Self) => Self | That + (self: Self, that: That): Self | That +} = order.min(Order) + +/** @internal */ +export const max: { + (that: That): (self: Self) => Self | That + (self: Self, that: That): Self | That +} = order.max(Order) + +/** @internal */ +export const greaterThan: { + (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean + (self: DateTime.DateTime, that: DateTime.DateTime): boolean +} = order.greaterThan(Order) + +/** @internal */ +export const greaterThanOrEqualTo: { + (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean + (self: DateTime.DateTime, that: DateTime.DateTime): boolean +} = order.greaterThanOrEqualTo(Order) + +/** @internal */ +export const lessThan: { + (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean + (self: DateTime.DateTime, that: DateTime.DateTime): boolean +} = order.lessThan(Order) + +/** @internal */ +export const lessThanOrEqualTo: { + (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean + (self: DateTime.DateTime, that: DateTime.DateTime): boolean +} = order.lessThanOrEqualTo(Order) + +/** @internal */ +export const between: { + (options: { minimum: DateTime.DateTime; maximum: DateTime.DateTime }): (self: DateTime.DateTime) => boolean + (self: DateTime.DateTime, options: { minimum: DateTime.DateTime; maximum: DateTime.DateTime }): boolean +} = order.between(Order) + +/** @internal */ +export const isFuture = (self: DateTime.DateTime): Effect.Effect => core.map(now, lessThan(self)) + +/** @internal */ +export const unsafeIsFuture = (self: DateTime.DateTime): boolean => lessThan(unsafeNow(), self) + +/** @internal */ +export const isPast = (self: DateTime.DateTime): Effect.Effect => core.map(now, greaterThan(self)) + +/** @internal */ +export const unsafeIsPast = (self: DateTime.DateTime): boolean => greaterThan(unsafeNow(), self) + +// ============================================================================= +// conversions +// ============================================================================= + +/** @internal */ +export const toDateUtc = (self: DateTime.DateTime): Date => new Date(self.epochMillis) + +/** @internal */ +export const toDate = (self: DateTime.DateTime): Date => { + if (self._tag === "Utc") { + return new Date(self.epochMillis) + } else if (self.zone._tag === "Offset") { + return new Date(self.epochMillis + self.zone.offset) + } else if (self.adjustedEpochMillis !== undefined) { + return new Date(self.adjustedEpochMillis) + } + const parts = self.zone.format.formatToParts(self.epochMillis).filter((_) => _.type !== "literal") + const date = new Date(0) + date.setUTCFullYear( + Number(parts[2].value), + Number(parts[0].value) - 1, + Number(parts[1].value) + ) + date.setUTCHours( + Number(parts[3].value), + Number(parts[4].value), + Number(parts[5].value), + Number(parts[6].value) + ) + self.adjustedEpochMillis = date.getTime() + return date +} + +/** @internal */ +export const zonedOffset = (self: DateTime.Zoned): number => { + const date = toDate(self) + return date.getTime() - toEpochMillis(self) +} + +const offsetToString = (offset: number): string => { + const abs = Math.abs(offset) + let hours = Math.floor(abs / (60 * 60 * 1000)) + let minutes = Math.round((abs % (60 * 60 * 1000)) / (60 * 1000)) + if (minutes === 60) { + hours += 1 + minutes = 0 + } + return `${offset < 0 ? "-" : "+"}${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}` +} + +/** @internal */ +export const zonedOffsetIso = (self: DateTime.Zoned): string => offsetToString(zonedOffset(self)) + +/** @internal */ +export const toEpochMillis = (self: DateTime.DateTime): number => self.epochMillis + +/** @internal */ +export const removeTime = (self: DateTime.DateTime): DateTime.Utc => + withDate(self, (date) => { + date.setUTCHours(0, 0, 0, 0) + return makeUtc(date.getTime()) + }) + +// ============================================================================= +// parts +// ============================================================================= + +const dateToParts = (date: Date): DateTime.DateTime.PartsWithWeekday => ({ + millis: date.getUTCMilliseconds(), + seconds: date.getUTCSeconds(), + minutes: date.getUTCMinutes(), + hours: date.getUTCHours(), + day: date.getUTCDate(), + weekDay: date.getUTCDay(), + month: date.getUTCMonth() + 1, + year: date.getUTCFullYear() +}) + +/** @internal */ +export const toParts = (self: DateTime.DateTime): DateTime.DateTime.PartsWithWeekday => { + if (self._tag === "Utc") { + return toPartsUtc(self) + } else if (self.partsAdjusted !== undefined) { + return self.partsAdjusted + } + self.partsAdjusted = withDate(self, dateToParts) + return self.partsAdjusted +} + +/** @internal */ +export const toPartsUtc = (self: DateTime.DateTime): DateTime.DateTime.PartsWithWeekday => { + if (self.partsUtc !== undefined) { + return self.partsUtc + } + self.partsUtc = withDateUtc(self, dateToParts) + return self.partsUtc +} + +/** @internal */ +export const getPartUtc: { + (part: keyof DateTime.DateTime.PartsWithWeekday): (self: DateTime.DateTime) => number + (self: DateTime.DateTime, part: keyof DateTime.DateTime.PartsWithWeekday): number +} = dual(2, (self: DateTime.DateTime, part: keyof DateTime.DateTime.PartsWithWeekday): number => toPartsUtc(self)[part]) + +/** @internal */ +export const getPart: { + (part: keyof DateTime.DateTime.PartsWithWeekday): (self: DateTime.DateTime) => number + (self: DateTime.DateTime, part: keyof DateTime.DateTime.PartsWithWeekday): number +} = dual(2, (self: DateTime.DateTime, part: keyof DateTime.DateTime.PartsWithWeekday): number => toParts(self)[part]) + +const setPartsDate = (date: Date, parts: Partial): void => { + if (parts.year !== undefined) { + date.setUTCFullYear(parts.year) + } + if (parts.month !== undefined) { + date.setUTCMonth(parts.month - 1) + } + if (parts.day !== undefined) { + date.setUTCDate(parts.day) + } + if (parts.weekDay !== undefined) { + const diff = parts.weekDay - date.getUTCDay() + date.setUTCDate(date.getUTCDate() + diff) + } + if (parts.hours !== undefined) { + date.setUTCHours(parts.hours) + } + if (parts.minutes !== undefined) { + date.setUTCMinutes(parts.minutes) + } + if (parts.seconds !== undefined) { + date.setUTCSeconds(parts.seconds) + } + if (parts.millis !== undefined) { + date.setUTCMilliseconds(parts.millis) + } +} + +/** @internal */ +export const setParts: { + ( + parts: Partial + ): (self: A) => A + ( + self: A, + parts: Partial + ): A +} = dual( + 2, + (self: DateTime.DateTime, parts: Partial): DateTime.DateTime => + mutate(self, (date) => setPartsDate(date, parts)) +) + +/** @internal */ +export const setPartsUtc: { + ( + parts: Partial + ): (self: A) => A + ( + self: A, + parts: Partial + ): A +} = dual( + 2, + (self: DateTime.DateTime, parts: Partial): DateTime.DateTime => + mutateUtc(self, (date) => setPartsDate(date, parts)) +) + +// ============================================================================= +// mapping +// ============================================================================= + +const constDayMillis = 24 * 60 * 60 * 1000 + +const makeZonedFromAdjusted = ( + adjustedMillis: number, + zone: DateTime.TimeZone, + disambiguation: DateTime.Disambiguation +): DateTime.Zoned => { + if (zone._tag === "Offset") { + return makeZonedProto(adjustedMillis - zone.offset, zone) + } + const beforeOffset = calculateNamedOffset( + adjustedMillis - constDayMillis, + adjustedMillis, + zone + ) + const afterOffset = calculateNamedOffset( + adjustedMillis + constDayMillis, + adjustedMillis, + zone + ) + // If there is no transition, we can return early + if (beforeOffset === afterOffset) { + return makeZonedProto(adjustedMillis - beforeOffset, zone) + } + const isForwards = beforeOffset < afterOffset + const transitionMillis = beforeOffset - afterOffset + // If the transition is forwards, we only need to check if we should move the + // local wall clock time forward if it is inside the gap + if (isForwards) { + const currentAfterOffset = calculateNamedOffset( + adjustedMillis - afterOffset, + adjustedMillis, + zone + ) + if (currentAfterOffset === afterOffset) { + return makeZonedProto(adjustedMillis - afterOffset, zone) + } + const before = makeZonedProto(adjustedMillis - beforeOffset, zone) + const beforeAdjustedMillis = toDate(before).getTime() + // If the wall clock time has changed, we are inside the gap + if (adjustedMillis !== beforeAdjustedMillis) { + switch (disambiguation) { + case "reject": { + const formatted = new Date(adjustedMillis).toISOString() + throw new RangeError(`Gap time: ${formatted} does not exist in time zone ${zone.id}`) + } + case "earlier": + return makeZonedProto(adjustedMillis - afterOffset, zone) + + case "compatible": + case "later": + return before + } + } + // The wall clock time is in the earlier offset, so we use that + return before + } + + const currentBeforeOffset = calculateNamedOffset( + adjustedMillis - beforeOffset, + adjustedMillis, + zone + ) + // The wall clock time is in the earlier offset, so we use that + if (currentBeforeOffset === beforeOffset) { + if (disambiguation === "earlier" || disambiguation === "compatible") { + return makeZonedProto(adjustedMillis - beforeOffset, zone) + } + const laterOffset = calculateNamedOffset( + adjustedMillis - beforeOffset + transitionMillis, + adjustedMillis + transitionMillis, + zone + ) + if (laterOffset === beforeOffset) { + return makeZonedProto(adjustedMillis - beforeOffset, zone) + } + // If the offset changed in this period, then we are inside the period where + // the wall clock time occurs twice, once in the earlier offset and once in + // the later offset. + if (disambiguation === "reject") { + const formatted = new Date(adjustedMillis).toISOString() + throw new RangeError(`Ambiguous time: ${formatted} occurs twice in time zone ${zone.id}`) + } + // If the disambiguation is "later", we return the later offset below + } + return makeZonedProto(adjustedMillis - afterOffset, zone) +} + +const offsetRegex = /([+-])(\d{2}):(\d{2})$/ +const parseOffset = (offset: string): number | null => { + const match = offsetRegex.exec(offset) + if (match === null) { + return null + } + const [, sign, hours, minutes] = match + return (sign === "+" ? 1 : -1) * (Number(hours) * 60 + Number(minutes)) * 60 * 1000 +} + +const calculateNamedOffset = ( + utcMillis: number, + adjustedMillis: number, + zone: DateTime.TimeZone.Named +): number => { + const offset = zone.format.formatToParts(utcMillis).find((_) => _.type === "timeZoneName")?.value ?? "" + if (offset === "GMT") { + return 0 + } + const result = parseOffset(offset) + if (result === null) { + // fallback to using the adjusted date + return zonedOffset(makeZonedProto(adjustedMillis, zone)) + } + return result +} + +/** @internal */ +export const mutate: { + (f: (date: Date) => void, options?: { + readonly disambiguation?: DateTime.Disambiguation | undefined + }): (self: A) => A + (self: A, f: (date: Date) => void, options?: { + readonly disambiguation?: DateTime.Disambiguation | undefined + }): A +} = dual(isDateTimeArgs, (self: DateTime.DateTime, f: (date: Date) => void, options?: { + readonly disambiguation?: DateTime.Disambiguation | undefined +}): DateTime.DateTime => { + if (self._tag === "Utc") { + const date = toDateUtc(self) + f(date) + return makeUtc(date.getTime()) + } + const adjustedDate = toDate(self) + const newAdjustedDate = new Date(adjustedDate.getTime()) + f(newAdjustedDate) + return makeZonedFromAdjusted(newAdjustedDate.getTime(), self.zone, options?.disambiguation ?? "compatible") +}) + +/** @internal */ +export const mutateUtc: { + (f: (date: Date) => void): (self: A) => A + (self: A, f: (date: Date) => void): A +} = dual(2, (self: DateTime.DateTime, f: (date: Date) => void): DateTime.DateTime => + mapEpochMillis(self, (millis) => { + const date = new Date(millis) + f(date) + return date.getTime() + })) + +/** @internal */ +export const mapEpochMillis: { + (f: (millis: number) => number): (self: A) => A + (self: A, f: (millis: number) => number): A +} = dual(2, (self: DateTime.DateTime, f: (millis: number) => number): DateTime.DateTime => { + const millis = f(toEpochMillis(self)) + return self._tag === "Utc" ? makeUtc(millis) : makeZonedProto(millis, self.zone) +}) + +/** @internal */ +export const withDate: { + (f: (date: Date) => A): (self: DateTime.DateTime) => A + (self: DateTime.DateTime, f: (date: Date) => A): A +} = dual(2, (self: DateTime.DateTime, f: (date: Date) => A): A => f(toDate(self))) + +/** @internal */ +export const withDateUtc: { + (f: (date: Date) => A): (self: DateTime.DateTime) => A + (self: DateTime.DateTime, f: (date: Date) => A): A +} = dual(2, (self: DateTime.DateTime, f: (date: Date) => A): A => f(toDateUtc(self))) + +/** @internal */ +export const match: { + (options: { + readonly onUtc: (_: DateTime.Utc) => A + readonly onZoned: (_: DateTime.Zoned) => B + }): (self: DateTime.DateTime) => A | B + (self: DateTime.DateTime, options: { + readonly onUtc: (_: DateTime.Utc) => A + readonly onZoned: (_: DateTime.Zoned) => B + }): A | B +} = dual(2, (self: DateTime.DateTime, options: { + readonly onUtc: (_: DateTime.Utc) => A + readonly onZoned: (_: DateTime.Zoned) => B +}): A | B => self._tag === "Utc" ? options.onUtc(self) : options.onZoned(self)) + +// ============================================================================= +// math +// ============================================================================= + +/** @internal */ +export const addDuration: { + (duration: Duration.DurationInput): (self: A) => A + (self: A, duration: Duration.DurationInput): A +} = dual( + 2, + (self: DateTime.DateTime, duration: Duration.DurationInput): DateTime.DateTime => + mapEpochMillis(self, (millis) => millis + Duration.toMillis(duration)) +) + +/** @internal */ +export const subtractDuration: { + (duration: Duration.DurationInput): (self: A) => A + (self: A, duration: Duration.DurationInput): A +} = dual( + 2, + (self: DateTime.DateTime, duration: Duration.DurationInput): DateTime.DateTime => + mapEpochMillis(self, (millis) => millis - Duration.toMillis(duration)) +) + +const addMillis = (date: Date, amount: number): void => { + date.setTime(date.getTime() + amount) +} + +/** @internal */ +export const add: { + ( + parts: Partial + ): (self: A) => A + ( + self: A, + parts: Partial + ): A +} = dual( + 2, + (self: DateTime.DateTime, parts: Partial): DateTime.DateTime => + mutate(self, (date) => { + if (parts.millis) { + addMillis(date, parts.millis) + } + if (parts.seconds) { + addMillis(date, parts.seconds * 1000) + } + if (parts.minutes) { + addMillis(date, parts.minutes * 60 * 1000) + } + if (parts.hours) { + addMillis(date, parts.hours * 60 * 60 * 1000) + } + if (parts.days) { + date.setUTCDate(date.getUTCDate() + parts.days) + } + if (parts.weeks) { + date.setUTCDate(date.getUTCDate() + parts.weeks * 7) + } + if (parts.months) { + const day = date.getUTCDate() + date.setUTCMonth(date.getUTCMonth() + parts.months + 1, 0) + if (day < date.getUTCDate()) { + date.setUTCDate(day) + } + } + if (parts.years) { + const day = date.getUTCDate() + const month = date.getUTCMonth() + date.setUTCFullYear( + date.getUTCFullYear() + parts.years, + month + 1, + 0 + ) + if (day < date.getUTCDate()) { + date.setUTCDate(day) + } + } + }) +) + +/** @internal */ +export const subtract: { + ( + parts: Partial + ): (self: A) => A + ( + self: A, + parts: Partial + ): A +} = dual(2, (self: DateTime.DateTime, parts: Partial): DateTime.DateTime => { + const newParts = {} as Partial> + for (const key in parts) { + newParts[key as keyof DateTime.DateTime.PartsForMath] = -1 * parts[key as keyof DateTime.DateTime.PartsForMath]! + } + return add(self, newParts) +}) + +const startOfDate = (date: Date, part: DateTime.DateTime.UnitSingular, options?: { + readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined +}) => { + switch (part) { + case "second": { + date.setUTCMilliseconds(0) + break + } + case "minute": { + date.setUTCSeconds(0, 0) + break + } + case "hour": { + date.setUTCMinutes(0, 0, 0) + break + } + case "day": { + date.setUTCHours(0, 0, 0, 0) + break + } + case "week": { + const weekStartsOn = options?.weekStartsOn ?? 0 + const day = date.getUTCDay() + const diff = (day - weekStartsOn + 7) % 7 + date.setUTCDate(date.getUTCDate() - diff) + date.setUTCHours(0, 0, 0, 0) + break + } + case "month": { + date.setUTCDate(1) + date.setUTCHours(0, 0, 0, 0) + break + } + case "year": { + date.setUTCMonth(0, 1) + date.setUTCHours(0, 0, 0, 0) + break + } + } +} + +/** @internal */ +export const startOf: { + (part: DateTime.DateTime.UnitSingular, options?: { + readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined + }): (self: A) => A + (self: A, part: DateTime.DateTime.UnitSingular, options?: { + readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined + }): A +} = dual(isDateTimeArgs, (self: DateTime.DateTime, part: DateTime.DateTime.UnitSingular, options?: { + readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined +}): DateTime.DateTime => mutate(self, (date) => startOfDate(date, part, options))) + +const endOfDate = (date: Date, part: DateTime.DateTime.UnitSingular, options?: { + readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined +}) => { + switch (part) { + case "second": { + date.setUTCMilliseconds(999) + break + } + case "minute": { + date.setUTCSeconds(59, 999) + break + } + case "hour": { + date.setUTCMinutes(59, 59, 999) + break + } + case "day": { + date.setUTCHours(23, 59, 59, 999) + break + } + case "week": { + const weekStartsOn = options?.weekStartsOn ?? 0 + const day = date.getUTCDay() + const diff = (day - weekStartsOn + 7) % 7 + date.setUTCDate(date.getUTCDate() - diff + 6) + date.setUTCHours(23, 59, 59, 999) + break + } + case "month": { + date.setUTCMonth(date.getUTCMonth() + 1, 0) + date.setUTCHours(23, 59, 59, 999) + break + } + case "year": { + date.setUTCMonth(11, 31) + date.setUTCHours(23, 59, 59, 999) + break + } + } +} + +/** @internal */ +export const endOf: { + (part: DateTime.DateTime.UnitSingular, options?: { + readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined + }): (self: A) => A + (self: A, part: DateTime.DateTime.UnitSingular, options?: { + readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined + }): A +} = dual(isDateTimeArgs, (self: DateTime.DateTime, part: DateTime.DateTime.UnitSingular, options?: { + readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined +}): DateTime.DateTime => mutate(self, (date) => endOfDate(date, part, options))) + +/** @internal */ +export const nearest: { + (part: DateTime.DateTime.UnitSingular, options?: { + readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined + }): (self: A) => A + (self: A, part: DateTime.DateTime.UnitSingular, options?: { + readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined + }): A +} = dual(isDateTimeArgs, (self: DateTime.DateTime, part: DateTime.DateTime.UnitSingular, options?: { + readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined +}): DateTime.DateTime => + mutate(self, (date) => { + if (part === "milli") return + const millis = date.getTime() + const start = new Date(millis) + startOfDate(start, part, options) + const startMillis = start.getTime() + const end = new Date(millis) + endOfDate(end, part, options) + const endMillis = end.getTime() + 1 + const diffStart = millis - startMillis + const diffEnd = endMillis - millis + if (diffStart < diffEnd) { + date.setTime(startMillis) + } else { + date.setTime(endMillis) + } + })) + +// ============================================================================= +// formatting +// ============================================================================= + +const intlTimeZone = (self: DateTime.TimeZone): string => { + if (self._tag === "Named") { + return self.id + } + return offsetToString(self.offset) +} + +/** @internal */ +export const format: { + ( + options?: + | Intl.DateTimeFormatOptions & { + readonly locale?: Intl.LocalesArgument + } + | undefined + ): (self: DateTime.DateTime) => string + ( + self: DateTime.DateTime, + options?: + | Intl.DateTimeFormatOptions & { + readonly locale?: Intl.LocalesArgument + } + | undefined + ): string +} = dual(isDateTimeArgs, ( + self: DateTime.DateTime, + options?: + | Intl.DateTimeFormatOptions & { + readonly locale?: Intl.LocalesArgument + } + | undefined +): string => { + try { + return new Intl.DateTimeFormat(options?.locale, { + timeZone: self._tag === "Utc" ? "UTC" : intlTimeZone(self.zone), + ...options + }).format(self.epochMillis) + } catch { + return new Intl.DateTimeFormat(options?.locale, { + timeZone: "UTC", + ...options + }).format(toDate(self)) + } +}) + +/** @internal */ +export const formatLocal: { + ( + options?: + | Intl.DateTimeFormatOptions & { + readonly locale?: Intl.LocalesArgument + } + | undefined + ): (self: DateTime.DateTime) => string + ( + self: DateTime.DateTime, + options?: + | Intl.DateTimeFormatOptions & { + readonly locale?: Intl.LocalesArgument + } + | undefined + ): string +} = dual(isDateTimeArgs, ( + self: DateTime.DateTime, + options?: + | Intl.DateTimeFormatOptions & { + readonly locale?: Intl.LocalesArgument + } + | undefined +): string => new Intl.DateTimeFormat(options?.locale, options).format(self.epochMillis)) + +/** @internal */ +export const formatUtc: { + ( + options?: + | Intl.DateTimeFormatOptions & { + readonly locale?: Intl.LocalesArgument + } + | undefined + ): (self: DateTime.DateTime) => string + ( + self: DateTime.DateTime, + options?: + | Intl.DateTimeFormatOptions & { + readonly locale?: Intl.LocalesArgument + } + | undefined + ): string +} = dual(isDateTimeArgs, ( + self: DateTime.DateTime, + options?: + | Intl.DateTimeFormatOptions & { + readonly locale?: Intl.LocalesArgument + } + | undefined +): string => + new Intl.DateTimeFormat(options?.locale, { + ...options, + timeZone: "UTC" + }).format(self.epochMillis)) + +/** @internal */ +export const formatIntl: { + (format: Intl.DateTimeFormat): (self: DateTime.DateTime) => string + (self: DateTime.DateTime, format: Intl.DateTimeFormat): string +} = dual(2, (self: DateTime.DateTime, format: Intl.DateTimeFormat): string => format.format(self.epochMillis)) + +/** @internal */ +export const formatIso = (self: DateTime.DateTime): string => toDateUtc(self).toISOString() + +/** @internal */ +export const formatIsoDate = (self: DateTime.DateTime): string => toDate(self).toISOString().slice(0, 10) + +/** @internal */ +export const formatIsoDateUtc = (self: DateTime.DateTime): string => toDateUtc(self).toISOString().slice(0, 10) + +/** @internal */ +export const formatIsoOffset = (self: DateTime.DateTime): string => { + const date = toDate(self) + return self._tag === "Utc" ? date.toISOString() : `${date.toISOString().slice(0, -1)}${zonedOffsetIso(self)}` +} + +/** @internal */ +export const formatIsoZoned = (self: DateTime.Zoned): string => + self.zone._tag === "Offset" ? formatIsoOffset(self) : `${formatIsoOffset(self)}[${self.zone.id}]` diff --git a/backend/node_modules/effect/src/internal/defaultServices.ts b/backend/node_modules/effect/src/internal/defaultServices.ts new file mode 100644 index 0000000000000000000000000000000000000000..59398411a9c112a2627407735bc25152f8b6db75 --- /dev/null +++ b/backend/node_modules/effect/src/internal/defaultServices.ts @@ -0,0 +1,163 @@ +import * as Array from "../Array.js" +import type * as Chunk from "../Chunk.js" +import type * as Clock from "../Clock.js" +import type * as Config from "../Config.js" +import type * as ConfigProvider from "../ConfigProvider.js" +import * as Context from "../Context.js" +import type * as DefaultServices from "../DefaultServices.js" +import * as Duration from "../Duration.js" +import type * as Effect from "../Effect.js" +import { dual, pipe } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import type * as Random from "../Random.js" +import type * as Tracer from "../Tracer.js" +import * as clock from "./clock.js" +import * as configProvider from "./configProvider.js" +import * as core from "./core.js" +import * as console_ from "./defaultServices/console.js" +import * as random from "./random.js" +import * as tracer from "./tracer.js" + +/** @internal */ +export const liveServices: Context.Context = pipe( + Context.empty(), + Context.add(clock.clockTag, clock.make()), + Context.add(console_.consoleTag, console_.defaultConsole), + Context.add(random.randomTag, random.make(Math.random())), + Context.add(configProvider.configProviderTag, configProvider.fromEnv()), + Context.add(tracer.tracerTag, tracer.nativeTracer) +) + +/** + * The `FiberRef` holding the default `Effect` services. + * + * @since 2.0.0 + * @category fiberRefs + */ +export const currentServices = globalValue( + Symbol.for("effect/DefaultServices/currentServices"), + () => core.fiberRefUnsafeMakeContext(liveServices) +) + +// circular with Clock + +/** @internal */ +export const sleep = (duration: Duration.DurationInput): Effect.Effect => { + const decodedDuration = Duration.decode(duration) + return clockWith((clock) => clock.sleep(decodedDuration)) +} + +/** @internal */ +export const defaultServicesWith = ( + f: (services: Context.Context) => Effect.Effect +) => core.withFiberRuntime((fiber) => f(fiber.currentDefaultServices)) + +/** @internal */ +export const clockWith = (f: (clock: Clock.Clock) => Effect.Effect): Effect.Effect => + defaultServicesWith((services) => f(services.unsafeMap.get(clock.clockTag.key))) + +/** @internal */ +export const currentTimeMillis: Effect.Effect = clockWith((clock) => clock.currentTimeMillis) + +/** @internal */ +export const currentTimeNanos: Effect.Effect = clockWith((clock) => clock.currentTimeNanos) + +/** @internal */ +export const withClock = dual< + (clock: C) => (effect: Effect.Effect) => Effect.Effect, + (effect: Effect.Effect, clock: C) => Effect.Effect +>(2, (effect, c) => + core.fiberRefLocallyWith( + currentServices, + Context.add(clock.clockTag, c) + )(effect)) + +// circular with ConfigProvider + +/** @internal */ +export const withConfigProvider = dual< + (provider: ConfigProvider.ConfigProvider) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, provider: ConfigProvider.ConfigProvider) => Effect.Effect +>(2, (self, provider) => + core.fiberRefLocallyWith( + currentServices, + Context.add(configProvider.configProviderTag, provider) + )(self)) + +/** @internal */ +export const configProviderWith = ( + f: (provider: ConfigProvider.ConfigProvider) => Effect.Effect +): Effect.Effect => + defaultServicesWith((services) => f(services.unsafeMap.get(configProvider.configProviderTag.key))) + +/** @internal */ +export const config = (config: Config.Config) => configProviderWith((_) => _.load(config)) + +/** @internal */ +export const configOrDie = (config: Config.Config) => core.orDie(configProviderWith((_) => _.load(config))) + +// circular with Random + +/** @internal */ +export const randomWith = (f: (random: Random.Random) => Effect.Effect): Effect.Effect => + defaultServicesWith((services) => f(services.unsafeMap.get(random.randomTag.key))) + +/** @internal */ +export const withRandom = dual< + (value: X) => (effect: Effect.Effect) => Effect.Effect, + (effect: Effect.Effect, value: X) => Effect.Effect +>(2, (effect, value) => + core.fiberRefLocallyWith( + currentServices, + Context.add(random.randomTag, value) + )(effect)) + +/** @internal */ +export const next: Effect.Effect = randomWith((random) => random.next) + +/** @internal */ +export const nextInt: Effect.Effect = randomWith((random) => random.nextInt) + +/** @internal */ +export const nextBoolean: Effect.Effect = randomWith((random) => random.nextBoolean) + +/** @internal */ +export const nextRange = (min: number, max: number): Effect.Effect => + randomWith((random) => random.nextRange(min, max)) + +/** @internal */ +export const nextIntBetween = (min: number, max: number): Effect.Effect => + randomWith((random) => random.nextIntBetween(min, max)) + +/** @internal */ +export const shuffle = (elements: Iterable): Effect.Effect> => + randomWith((random) => random.shuffle(elements)) + +/** @internal */ +export const choice = >( + elements: Self +) => { + const array = Array.fromIterable(elements) + return core.map( + array.length === 0 + ? core.fail(new core.NoSuchElementException("Cannot select a random element from an empty array")) + : randomWith((random) => random.nextIntBetween(0, array.length)), + (i) => array[i] + ) as any +} + +// circular with Tracer + +/** @internal */ +export const tracerWith = (f: (tracer: Tracer.Tracer) => Effect.Effect): Effect.Effect => + defaultServicesWith((services) => f(services.unsafeMap.get(tracer.tracerTag.key))) + +/** @internal */ +export const withTracer = dual< + (value: Tracer.Tracer) => (effect: Effect.Effect) => Effect.Effect, + (effect: Effect.Effect, value: Tracer.Tracer) => Effect.Effect +>(2, (effect, value) => + core.fiberRefLocallyWith( + currentServices, + Context.add(tracer.tracerTag, value) + )(effect)) diff --git a/backend/node_modules/effect/src/internal/defaultServices/console.ts b/backend/node_modules/effect/src/internal/defaultServices/console.ts new file mode 100644 index 0000000000000000000000000000000000000000..8e164206f8a6fa71b129578f715334ee88c35992 --- /dev/null +++ b/backend/node_modules/effect/src/internal/defaultServices/console.ts @@ -0,0 +1,100 @@ +/* eslint-disable no-console */ +import type * as Console from "../../Console.js" +import * as Context from "../../Context.js" +import * as core from "../core.js" + +/** @internal */ +export const TypeId: Console.TypeId = Symbol.for("effect/Console") as Console.TypeId + +/** @internal */ +export const consoleTag: Context.Tag = Context.GenericTag( + "effect/Console" +) + +/** @internal */ +export const defaultConsole: Console.Console = { + [TypeId]: TypeId, + assert(condition, ...args) { + return core.sync(() => { + console.assert(condition, ...args) + }) + }, + clear: core.sync(() => { + console.clear() + }), + count(label) { + return core.sync(() => { + console.count(label) + }) + }, + countReset(label) { + return core.sync(() => { + console.countReset(label) + }) + }, + debug(...args) { + return core.sync(() => { + console.debug(...args) + }) + }, + dir(item, options) { + return core.sync(() => { + console.dir(item, options) + }) + }, + dirxml(...args) { + return core.sync(() => { + console.dirxml(...args) + }) + }, + error(...args) { + return core.sync(() => { + console.error(...args) + }) + }, + group(options) { + return options?.collapsed ? + core.sync(() => console.groupCollapsed(options?.label)) : + core.sync(() => console.group(options?.label)) + }, + groupEnd: core.sync(() => { + console.groupEnd() + }), + info(...args) { + return core.sync(() => { + console.info(...args) + }) + }, + log(...args) { + return core.sync(() => { + console.log(...args) + }) + }, + table(tabularData, properties) { + return core.sync(() => { + console.table(tabularData, properties) + }) + }, + time(label) { + return core.sync(() => console.time(label)) + }, + timeEnd(label) { + return core.sync(() => console.timeEnd(label)) + }, + timeLog(label, ...args) { + return core.sync(() => { + console.timeLog(label, ...args) + }) + }, + trace(...args) { + return core.sync(() => { + console.trace(...args) + }) + }, + warn(...args) { + return core.sync(() => { + console.warn(...args) + }) + }, + unsafe: console +} diff --git a/backend/node_modules/effect/src/internal/deferred.ts b/backend/node_modules/effect/src/internal/deferred.ts new file mode 100644 index 0000000000000000000000000000000000000000..a1c1df1e00ca8912a83c0c053d5fa317485b6b22 --- /dev/null +++ b/backend/node_modules/effect/src/internal/deferred.ts @@ -0,0 +1,46 @@ +import type * as Deferred from "../Deferred.js" +import type * as Effect from "../Effect.js" +import * as OpCodes from "./opCodes/deferred.js" + +/** @internal */ +const DeferredSymbolKey = "effect/Deferred" + +/** @internal */ +export const DeferredTypeId: Deferred.DeferredTypeId = Symbol.for( + DeferredSymbolKey +) as Deferred.DeferredTypeId + +/** @internal */ +export const deferredVariance = { + /* c8 ignore next */ + _E: (_: any) => _, + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +export type State = Pending | Done + +/** @internal */ +export interface Pending { + readonly _tag: "Pending" + readonly joiners: Array<(effect: Effect.Effect) => void> +} + +/** @internal */ +export interface Done { + readonly _tag: "Done" + readonly effect: Effect.Effect +} + +/** @internal */ +export const pending = ( + joiners: Array<(effect: Effect.Effect) => void> +): State => { + return { _tag: OpCodes.OP_STATE_PENDING, joiners } +} + +/** @internal */ +export const done = (effect: Effect.Effect): State => { + return { _tag: OpCodes.OP_STATE_DONE, effect } +} diff --git a/backend/node_modules/effect/src/internal/differ.ts b/backend/node_modules/effect/src/internal/differ.ts new file mode 100644 index 0000000000000000000000000000000000000000..0c07cccd77c9915c93f3be4b6bde0f19a878b85a --- /dev/null +++ b/backend/node_modules/effect/src/internal/differ.ts @@ -0,0 +1,200 @@ +import type { Chunk } from "../Chunk.js" +import type { Context } from "../Context.js" +import type * as Differ from "../Differ.js" +import type { Either } from "../Either.js" +import * as Equal from "../Equal.js" +import * as Dual from "../Function.js" +import { constant, identity } from "../Function.js" +import type { HashMap } from "../HashMap.js" +import type { HashSet } from "../HashSet.js" +import { pipeArguments } from "../Pipeable.js" +import * as ChunkPatch from "./differ/chunkPatch.js" +import * as ContextPatch from "./differ/contextPatch.js" +import * as HashMapPatch from "./differ/hashMapPatch.js" +import * as HashSetPatch from "./differ/hashSetPatch.js" +import * as OrPatch from "./differ/orPatch.js" +import * as ReadonlyArrayPatch from "./differ/readonlyArrayPatch.js" + +/** @internal */ +export const DifferTypeId: Differ.TypeId = Symbol.for("effect/Differ") as Differ.TypeId + +/** @internal */ +export const DifferProto = { + [DifferTypeId]: { + _P: identity, + _V: identity + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const make = ( + params: { + readonly empty: Patch + readonly diff: (oldValue: Value, newValue: Value) => Patch + readonly combine: (first: Patch, second: Patch) => Patch + readonly patch: (patch: Patch, oldValue: Value) => Value + } +): Differ.Differ => { + const differ = Object.create(DifferProto) + differ.empty = params.empty + differ.diff = params.diff + differ.combine = params.combine + differ.patch = params.patch + return differ +} + +/** @internal */ +export const environment = (): Differ.Differ, Differ.Differ.Context.Patch> => + make({ + empty: ContextPatch.empty(), + combine: (first, second) => ContextPatch.combine(second)(first), + diff: (oldValue, newValue) => ContextPatch.diff(oldValue, newValue), + patch: (patch, oldValue) => ContextPatch.patch(oldValue)(patch) + }) + +/** @internal */ +export const chunk = ( + differ: Differ.Differ +): Differ.Differ, Differ.Differ.Chunk.Patch> => + make({ + empty: ChunkPatch.empty(), + combine: (first, second) => ChunkPatch.combine(second)(first), + diff: (oldValue, newValue) => ChunkPatch.diff({ oldValue, newValue, differ }), + patch: (patch, oldValue) => ChunkPatch.patch(oldValue, differ)(patch) + }) + +/** @internal */ +export const hashMap = ( + differ: Differ.Differ +): Differ.Differ, Differ.Differ.HashMap.Patch> => + make({ + empty: HashMapPatch.empty(), + combine: (first, second) => HashMapPatch.combine(second)(first), + diff: (oldValue, newValue) => HashMapPatch.diff({ oldValue, newValue, differ }), + patch: (patch, oldValue) => HashMapPatch.patch(oldValue, differ)(patch) + }) + +/** @internal */ +export const hashSet = (): Differ.Differ, Differ.Differ.HashSet.Patch> => + make({ + empty: HashSetPatch.empty(), + combine: (first, second) => HashSetPatch.combine(second)(first), + diff: (oldValue, newValue) => HashSetPatch.diff(oldValue, newValue), + patch: (patch, oldValue) => HashSetPatch.patch(oldValue)(patch) + }) + +/** @internal */ +export const orElseEither = Dual.dual< + (that: Differ.Differ) => ( + self: Differ.Differ + ) => Differ.Differ, Differ.Differ.Or.Patch>, + ( + self: Differ.Differ, + that: Differ.Differ + ) => Differ.Differ, Differ.Differ.Or.Patch> +>(2, (self, that) => + make({ + empty: OrPatch.empty(), + combine: (first, second) => OrPatch.combine(first, second), + diff: (oldValue, newValue) => + OrPatch.diff({ + oldValue, + newValue, + left: self, + right: that + }), + patch: (patch, oldValue) => + OrPatch.patch(patch, { + oldValue, + left: self, + right: that + }) + })) + +/** @internal */ +export const readonlyArray = ( + differ: Differ.Differ +): Differ.Differ, Differ.Differ.ReadonlyArray.Patch> => + make({ + empty: ReadonlyArrayPatch.empty(), + combine: (first, second) => ReadonlyArrayPatch.combine(first, second), + diff: (oldValue, newValue) => ReadonlyArrayPatch.diff({ oldValue, newValue, differ }), + patch: (patch, oldValue) => ReadonlyArrayPatch.patch(patch, oldValue, differ) + }) + +/** @internal */ +export const transform = Dual.dual< + ( + options: { + readonly toNew: (value: Value) => Value2 + readonly toOld: (value: Value2) => Value + } + ) => (self: Differ.Differ) => Differ.Differ, + ( + self: Differ.Differ, + options: { + readonly toNew: (value: Value) => Value2 + readonly toOld: (value: Value2) => Value + } + ) => Differ.Differ +>(2, (self, { toNew, toOld }) => + make({ + empty: self.empty, + combine: (first, second) => self.combine(first, second), + diff: (oldValue, newValue) => self.diff(toOld(oldValue), toOld(newValue)), + patch: (patch, oldValue) => toNew(self.patch(patch, toOld(oldValue))) + })) + +/** @internal */ +export const update = (): Differ.Differ A> => updateWith((_, a) => a) + +/** @internal */ +export const updateWith = (f: (x: A, y: A) => A): Differ.Differ A> => + make({ + empty: identity, + combine: (first, second) => { + if (first === identity) { + return second + } + if (second === identity) { + return first + } + return (a) => second(first(a)) + }, + diff: (oldValue, newValue) => { + if (Equal.equals(oldValue, newValue)) { + return identity + } + return constant(newValue) + }, + patch: (patch, oldValue) => f(oldValue, patch(oldValue)) + }) + +/** @internal */ +export const zip = Dual.dual< + (that: Differ.Differ) => ( + self: Differ.Differ + ) => Differ.Differ, + ( + self: Differ.Differ, + that: Differ.Differ + ) => Differ.Differ +>(2, (self, that) => + make({ + empty: [self.empty, that.empty] as const, + combine: (first, second) => [ + self.combine(first[0], second[0]), + that.combine(first[1], second[1]) + ], + diff: (oldValue, newValue) => [ + self.diff(oldValue[0], newValue[0]), + that.diff(oldValue[1], newValue[1]) + ], + patch: (patch, oldValue) => [ + self.patch(patch[0], oldValue[0]), + that.patch(patch[1], oldValue[1]) + ] + })) diff --git a/backend/node_modules/effect/src/internal/differ/chunkPatch.ts b/backend/node_modules/effect/src/internal/differ/chunkPatch.ts new file mode 100644 index 0000000000000000000000000000000000000000..336c4c0ac1323e341fe54e0c3451aa66bc2fa031 --- /dev/null +++ b/backend/node_modules/effect/src/internal/differ/chunkPatch.ts @@ -0,0 +1,211 @@ +import * as Chunk from "../../Chunk.js" +import type * as Differ from "../../Differ.js" +import * as Equal from "../../Equal.js" +import * as Dual from "../../Function.js" +import { pipe } from "../../Function.js" +import * as Data from "../data.js" + +/** @internal */ +export const ChunkPatchTypeId: Differ.Differ.Chunk.TypeId = Symbol.for( + "effect/DifferChunkPatch" +) as Differ.Differ.Chunk.TypeId + +function variance(a: A): B { + return a as unknown as B +} + +const PatchProto = { + ...Data.Structural.prototype, + [ChunkPatchTypeId]: { + _Value: variance, + _Patch: variance + } +} + +interface Empty extends Differ.Differ.Chunk.Patch { + readonly _tag: "Empty" +} + +const EmptyProto = Object.assign(Object.create(PatchProto), { + _tag: "Empty" +}) + +const _empty = Object.create(EmptyProto) + +/** + * @internal + */ +export const empty = (): Differ.Differ.Chunk.Patch => _empty + +interface AndThen extends Differ.Differ.Chunk.Patch { + readonly _tag: "AndThen" + readonly first: Differ.Differ.Chunk.Patch + readonly second: Differ.Differ.Chunk.Patch +} + +const AndThenProto = Object.assign(Object.create(PatchProto), { + _tag: "AndThen" +}) + +const makeAndThen = ( + first: Differ.Differ.Chunk.Patch, + second: Differ.Differ.Chunk.Patch +): Differ.Differ.Chunk.Patch => { + const o = Object.create(AndThenProto) + o.first = first + o.second = second + return o +} + +interface Append extends Differ.Differ.Chunk.Patch { + readonly _tag: "Append" + readonly values: Chunk.Chunk +} + +const AppendProto = Object.assign(Object.create(PatchProto), { + _tag: "Append" +}) + +const makeAppend = (values: Chunk.Chunk): Differ.Differ.Chunk.Patch => { + const o = Object.create(AppendProto) + o.values = values + return o +} + +interface Slice extends Differ.Differ.Chunk.Patch { + readonly _tag: "Slice" + readonly from: number + readonly until: number +} + +const SliceProto = Object.assign(Object.create(PatchProto), { + _tag: "Slice" +}) + +const makeSlice = (from: number, until: number): Differ.Differ.Chunk.Patch => { + const o = Object.create(SliceProto) + o.from = from + o.until = until + return o +} + +interface Update extends Differ.Differ.Chunk.Patch { + readonly _tag: "Update" + readonly index: number + readonly patch: Patch +} + +const UpdateProto = Object.assign(Object.create(PatchProto), { + _tag: "Update" +}) + +const makeUpdate = (index: number, patch: Patch): Differ.Differ.Chunk.Patch => { + const o = Object.create(UpdateProto) + o.index = index + o.patch = patch + return o +} + +type Instruction = + | Empty + | AndThen + | Append + | Slice + | Update + +/** @internal */ +export const diff = ( + options: { + readonly oldValue: Chunk.Chunk + readonly newValue: Chunk.Chunk + readonly differ: Differ.Differ + } +): Differ.Differ.Chunk.Patch => { + let i = 0 + let patch = empty() + while (i < options.oldValue.length && i < options.newValue.length) { + const oldElement = Chunk.unsafeGet(i)(options.oldValue) + const newElement = Chunk.unsafeGet(i)(options.newValue) + const valuePatch = options.differ.diff(oldElement, newElement) + if (!Equal.equals(valuePatch, options.differ.empty)) { + patch = pipe(patch, combine(makeUpdate(i, valuePatch))) + } + i = i + 1 + } + if (i < options.oldValue.length) { + patch = pipe(patch, combine(makeSlice(0, i))) + } + if (i < options.newValue.length) { + patch = pipe(patch, combine(makeAppend(Chunk.drop(i)(options.newValue)))) + } + return patch +} + +/** @internal */ +export const combine = Dual.dual< + ( + that: Differ.Differ.Chunk.Patch + ) => ( + self: Differ.Differ.Chunk.Patch + ) => Differ.Differ.Chunk.Patch, + ( + self: Differ.Differ.Chunk.Patch, + that: Differ.Differ.Chunk.Patch + ) => Differ.Differ.Chunk.Patch +>(2, (self, that) => makeAndThen(self, that)) + +/** @internal */ +export const patch = Dual.dual< + ( + oldValue: Chunk.Chunk, + differ: Differ.Differ + ) => (self: Differ.Differ.Chunk.Patch) => Chunk.Chunk, + ( + self: Differ.Differ.Chunk.Patch, + oldValue: Chunk.Chunk, + differ: Differ.Differ + ) => Chunk.Chunk +>(3, ( + self: Differ.Differ.Chunk.Patch, + oldValue: Chunk.Chunk, + differ: Differ.Differ +) => { + if ((self as Instruction)._tag === "Empty") { + return oldValue + } + let chunk = oldValue + let patches: Chunk.Chunk> = Chunk.of(self) + while (Chunk.isNonEmpty(patches)) { + const head: Instruction = Chunk.headNonEmpty(patches) as Instruction + const tail = Chunk.tailNonEmpty(patches) + switch (head._tag) { + case "Empty": { + patches = tail + break + } + case "AndThen": { + patches = Chunk.prepend(head.first)(Chunk.prepend(head.second)(tail)) + break + } + case "Append": { + chunk = Chunk.appendAll(head.values)(chunk) + patches = tail + break + } + case "Slice": { + const array = Chunk.toReadonlyArray(chunk) + chunk = Chunk.unsafeFromArray(array.slice(head.from, head.until)) + patches = tail + break + } + case "Update": { + const array = Chunk.toReadonlyArray(chunk) as Array + array[head.index] = differ.patch(head.patch, array[head.index]!) + chunk = Chunk.unsafeFromArray(array) + patches = tail + break + } + } + } + return chunk +}) diff --git a/backend/node_modules/effect/src/internal/differ/contextPatch.ts b/backend/node_modules/effect/src/internal/differ/contextPatch.ts new file mode 100644 index 0000000000000000000000000000000000000000..90b57366455756ac52990c00815549c96e2492e7 --- /dev/null +++ b/backend/node_modules/effect/src/internal/differ/contextPatch.ts @@ -0,0 +1,232 @@ +import * as Chunk from "../../Chunk.js" +import type { Context } from "../../Context.js" +import type { Differ } from "../../Differ.js" +import * as Equal from "../../Equal.js" +import * as Dual from "../../Function.js" +import { makeContext } from "../context.js" +import { Structural } from "../data.js" + +/** @internal */ +export const ContextPatchTypeId: Differ.Context.TypeId = Symbol.for( + "effect/DifferContextPatch" +) as Differ.Context.TypeId + +function variance(a: A): B { + return a as unknown as B +} + +/** @internal */ +const PatchProto = { + ...Structural.prototype, + [ContextPatchTypeId]: { + _Value: variance, + _Patch: variance + } +} + +interface Empty extends Differ.Context.Patch { + readonly _tag: "Empty" +} + +const EmptyProto = Object.assign(Object.create(PatchProto), { + _tag: "Empty" +}) + +const _empty = Object.create(EmptyProto) + +/** + * @internal + */ +export const empty = (): Differ.Context.Patch => _empty + +/** @internal */ +export interface AndThen extends Differ.Context.Patch { + readonly _tag: "AndThen" + readonly first: Differ.Context.Patch + readonly second: Differ.Context.Patch +} + +const AndThenProto = Object.assign(Object.create(PatchProto), { + _tag: "AndThen" +}) + +const makeAndThen = ( + first: Differ.Context.Patch, + second: Differ.Context.Patch +): Differ.Context.Patch => { + const o = Object.create(AndThenProto) + o.first = first + o.second = second + return o +} + +/** @internal */ +export interface AddService extends Differ.Context.Patch { + readonly _tag: "AddService" + readonly key: string + readonly service: T +} + +const AddServiceProto = Object.assign(Object.create(PatchProto), { + _tag: "AddService" +}) + +const makeAddService = ( + key: string, + service: T +): Differ.Context.Patch => { + const o = Object.create(AddServiceProto) + o.key = key + o.service = service + return o +} + +/** @internal */ +export interface RemoveService extends Differ.Context.Patch> { + readonly _tag: "RemoveService" + readonly key: string +} + +const RemoveServiceProto = Object.assign(Object.create(PatchProto), { + _tag: "RemoveService" +}) + +const makeRemoveService = ( + key: string +): Differ.Context.Patch> => { + const o = Object.create(RemoveServiceProto) + o.key = key + return o +} + +/** @internal */ +export interface UpdateService extends Differ.Context.Patch { + readonly _tag: "UpdateService" + readonly key: string + update(service: T): T +} + +const UpdateServiceProto = Object.assign(Object.create(PatchProto), { + _tag: "UpdateService" +}) + +const makeUpdateService = ( + key: string, + update: (service: T) => T +): Differ.Context.Patch => { + const o = Object.create(UpdateServiceProto) + o.key = key + o.update = update + return o +} + +type Instruction = + | Empty + | AndThen + | AddService + | RemoveService + | UpdateService + +/** @internal */ +export const diff = ( + oldValue: Context, + newValue: Context +): Differ.Context.Patch => { + const missingServices = new Map(oldValue.unsafeMap) + let patch = empty() + for (const [tag, newService] of newValue.unsafeMap.entries()) { + if (missingServices.has(tag)) { + const old = missingServices.get(tag)! + missingServices.delete(tag) + if (!Equal.equals(old, newService)) { + patch = combine(makeUpdateService(tag, () => newService))(patch) + } + } else { + missingServices.delete(tag) + patch = combine(makeAddService(tag, newService))(patch) + } + } + for (const [tag] of missingServices.entries()) { + patch = combine(makeRemoveService(tag))(patch) + } + return patch +} + +/** @internal */ +export const combine = Dual.dual< + ( + that: Differ.Context.Patch + ) => ( + self: Differ.Context.Patch + ) => Differ.Context.Patch, + ( + self: Differ.Context.Patch, + that: Differ.Context.Patch + ) => Differ.Context.Patch +>(2, (self, that) => makeAndThen(self, that)) + +/** @internal */ +export const patch = Dual.dual< + ( + context: Context + ) => ( + self: Differ.Context.Patch + ) => Context, + ( + self: Differ.Context.Patch, + context: Context + ) => Context +>(2, (self: Differ.Context.Patch, context: Context) => { + if ((self as Instruction)._tag === "Empty") { + return context as any + } + let wasServiceUpdated = false + let patches: Chunk.Chunk> = Chunk.of( + self as Differ.Context.Patch + ) + const updatedContext: Map = new Map(context.unsafeMap) + while (Chunk.isNonEmpty(patches)) { + const head: Instruction = Chunk.headNonEmpty(patches) as Instruction + const tail = Chunk.tailNonEmpty(patches) + switch (head._tag) { + case "Empty": { + patches = tail + break + } + case "AddService": { + updatedContext.set(head.key, head.service) + patches = tail + break + } + case "AndThen": { + patches = Chunk.prepend(Chunk.prepend(tail, head.second), head.first) + break + } + case "RemoveService": { + updatedContext.delete(head.key) + patches = tail + break + } + case "UpdateService": { + updatedContext.set(head.key, head.update(updatedContext.get(head.key))) + wasServiceUpdated = true + patches = tail + break + } + } + } + if (!wasServiceUpdated) { + return makeContext(updatedContext) as Context + } + const map = new Map() + for (const [tag] of context.unsafeMap) { + if (updatedContext.has(tag)) { + map.set(tag, updatedContext.get(tag)) + updatedContext.delete(tag) + } + } + for (const [tag, s] of updatedContext) { + map.set(tag, s) + } + return makeContext(map) as Context +}) diff --git a/backend/node_modules/effect/src/internal/differ/hashMapPatch.ts b/backend/node_modules/effect/src/internal/differ/hashMapPatch.ts new file mode 100644 index 0000000000000000000000000000000000000000..0f5bfeeb51d0f091e661e3e75f2e88c8cb1d3c1f --- /dev/null +++ b/backend/node_modules/effect/src/internal/differ/hashMapPatch.ts @@ -0,0 +1,220 @@ +import * as Chunk from "../../Chunk.js" +import type * as Differ from "../../Differ.js" +import * as Equal from "../../Equal.js" +import * as Dual from "../../Function.js" +import * as HashMap from "../../HashMap.js" +import { Structural } from "../data.js" + +/** @internal */ +export const HashMapPatchTypeId: Differ.Differ.HashMap.TypeId = Symbol.for( + "effect/DifferHashMapPatch" +) as Differ.Differ.HashMap.TypeId + +function variance(a: A): B { + return a as unknown as B +} + +/** @internal */ +const PatchProto = { + ...Structural.prototype, + [HashMapPatchTypeId]: { + _Value: variance, + _Key: variance, + _Patch: variance + } +} + +interface Empty extends Differ.Differ.HashMap.Patch { + readonly _tag: "Empty" +} + +const EmptyProto = Object.assign(Object.create(PatchProto), { + _tag: "Empty" +}) + +const _empty = Object.create(EmptyProto) + +/** @internal */ +export const empty = (): Differ.Differ.HashMap.Patch => _empty + +interface AndThen extends Differ.Differ.HashMap.Patch { + readonly _tag: "AndThen" + readonly first: Differ.Differ.HashMap.Patch + readonly second: Differ.Differ.HashMap.Patch +} + +const AndThenProto = Object.assign(Object.create(PatchProto), { + _tag: "AndThen" +}) + +const makeAndThen = ( + first: Differ.Differ.HashMap.Patch, + second: Differ.Differ.HashMap.Patch +): Differ.Differ.HashMap.Patch => { + const o = Object.create(AndThenProto) + o.first = first + o.second = second + return o +} + +interface Add extends Differ.Differ.HashMap.Patch { + readonly _tag: "Add" + readonly key: Key + readonly value: Value +} + +const AddProto = Object.assign(Object.create(PatchProto), { + _tag: "Add" +}) + +const makeAdd = (key: Key, value: Value): Differ.Differ.HashMap.Patch => { + const o = Object.create(AddProto) + o.key = key + o.value = value + return o +} + +interface Remove extends Differ.Differ.HashMap.Patch { + readonly _tag: "Remove" + readonly key: Key +} + +const RemoveProto = Object.assign(Object.create(PatchProto), { + _tag: "Remove" +}) + +const makeRemove = (key: Key): Differ.Differ.HashMap.Patch => { + const o = Object.create(RemoveProto) + o.key = key + return o +} + +interface Update extends Differ.Differ.HashMap.Patch { + readonly _tag: "Update" + readonly key: Key + readonly patch: Patch +} + +const UpdateProto = Object.assign(Object.create(PatchProto), { + _tag: "Update" +}) + +const makeUpdate = (key: Key, patch: Patch): Differ.Differ.HashMap.Patch => { + const o = Object.create(UpdateProto) + o.key = key + o.patch = patch + return o +} + +type Instruction = + | Add + | Remove + | Update + | Empty + | AndThen + +/** @internal */ +export const diff = ( + options: { + readonly oldValue: HashMap.HashMap + readonly newValue: HashMap.HashMap + readonly differ: Differ.Differ + } +): Differ.Differ.HashMap.Patch => { + const [removed, patch] = HashMap.reduce( + [options.oldValue, empty()] as const, + ([map, patch], newValue: Value, key: Key) => { + const option = HashMap.get(key)(map) + switch (option._tag) { + case "Some": { + const valuePatch = options.differ.diff(option.value, newValue) + if (Equal.equals(valuePatch, options.differ.empty)) { + return [HashMap.remove(key)(map), patch] as const + } + return [ + HashMap.remove(key)(map), + combine(makeUpdate(key, valuePatch))(patch) + ] as const + } + case "None": { + return [map, combine(makeAdd(key, newValue))(patch)] as const + } + } + } + )(options.newValue) + return HashMap.reduce( + patch, + (patch, _, key: Key) => combine(makeRemove(key))(patch) + )(removed) +} + +/** @internal */ +export const combine = Dual.dual< + ( + that: Differ.Differ.HashMap.Patch + ) => ( + self: Differ.Differ.HashMap.Patch + ) => Differ.Differ.HashMap.Patch, + ( + self: Differ.Differ.HashMap.Patch, + that: Differ.Differ.HashMap.Patch + ) => Differ.Differ.HashMap.Patch +>(2, (self, that) => makeAndThen(self, that)) + +/** @internal */ +export const patch = Dual.dual< + ( + oldValue: HashMap.HashMap, + differ: Differ.Differ + ) => ( + self: Differ.Differ.HashMap.Patch + ) => HashMap.HashMap, + ( + self: Differ.Differ.HashMap.Patch, + oldValue: HashMap.HashMap, + differ: Differ.Differ + ) => HashMap.HashMap +>(3, ( + self: Differ.Differ.HashMap.Patch, + oldValue: HashMap.HashMap, + differ: Differ.Differ +) => { + if ((self as Instruction)._tag === "Empty") { + return oldValue + } + let map = oldValue + let patches: Chunk.Chunk> = Chunk.of(self) + while (Chunk.isNonEmpty(patches)) { + const head: Instruction = Chunk.headNonEmpty(patches) as Instruction + const tail = Chunk.tailNonEmpty(patches) + switch (head._tag) { + case "Empty": { + patches = tail + break + } + case "AndThen": { + patches = Chunk.prepend(head.first)(Chunk.prepend(head.second)(tail)) + break + } + case "Add": { + map = HashMap.set(head.key, head.value)(map) + patches = tail + break + } + case "Remove": { + map = HashMap.remove(head.key)(map) + patches = tail + break + } + case "Update": { + const option = HashMap.get(head.key)(map) + if (option._tag === "Some") { + map = HashMap.set(head.key, differ.patch(head.patch, option.value))(map) + } + patches = tail + break + } + } + } + return map +}) diff --git a/backend/node_modules/effect/src/internal/differ/hashSetPatch.ts b/backend/node_modules/effect/src/internal/differ/hashSetPatch.ts new file mode 100644 index 0000000000000000000000000000000000000000..e33b3822992c3933ada99b9a8e9dcd3daf26b89b --- /dev/null +++ b/backend/node_modules/effect/src/internal/differ/hashSetPatch.ts @@ -0,0 +1,176 @@ +import * as Chunk from "../../Chunk.js" +import type { Differ } from "../../Differ.js" +import * as Dual from "../../Function.js" +import * as HashSet from "../../HashSet.js" +import { Structural } from "../data.js" + +/** @internal */ +export const HashSetPatchTypeId: Differ.HashSet.TypeId = Symbol.for( + "effect/DifferHashSetPatch" +) as Differ.HashSet.TypeId + +function variance(a: A): B { + return a as unknown as B +} + +/** @internal */ +const PatchProto = { + ...Structural.prototype, + [HashSetPatchTypeId]: { + _Value: variance, + _Key: variance, + _Patch: variance + } +} + +interface Empty extends Differ.HashSet.Patch { + readonly _tag: "Empty" +} + +const EmptyProto = Object.assign(Object.create(PatchProto), { + _tag: "Empty" +}) + +const _empty = Object.create(EmptyProto) + +/** @internal */ +export const empty = (): Differ.HashSet.Patch => _empty + +interface AndThen extends Differ.HashSet.Patch { + readonly _tag: "AndThen" + readonly first: Differ.HashSet.Patch + readonly second: Differ.HashSet.Patch +} + +const AndThenProto = Object.assign(Object.create(PatchProto), { + _tag: "AndThen" +}) + +/** @internal */ +export const makeAndThen = ( + first: Differ.HashSet.Patch, + second: Differ.HashSet.Patch +): Differ.HashSet.Patch => { + const o = Object.create(AndThenProto) + o.first = first + o.second = second + return o +} + +interface Add extends Differ.HashSet.Patch { + readonly _tag: "Add" + readonly value: Value +} + +const AddProto = Object.assign(Object.create(PatchProto), { + _tag: "Add" +}) + +/** @internal */ +export const makeAdd = ( + value: Value +): Differ.HashSet.Patch => { + const o = Object.create(AddProto) + o.value = value + return o +} + +interface Remove extends Differ.HashSet.Patch { + readonly _tag: "Remove" + readonly value: Value +} + +const RemoveProto = Object.assign(Object.create(PatchProto), { + _tag: "Remove" +}) + +/** @internal */ +export const makeRemove = ( + value: Value +): Differ.HashSet.Patch => { + const o = Object.create(RemoveProto) + o.value = value + return o +} + +type Instruction = + | Add + | AndThen + | Empty + | Remove + +/** @internal */ +export const diff = ( + oldValue: HashSet.HashSet, + newValue: HashSet.HashSet +): Differ.HashSet.Patch => { + const [removed, patch] = HashSet.reduce( + [oldValue, empty()] as const, + ([set, patch], value: Value) => { + if (HashSet.has(value)(set)) { + return [HashSet.remove(value)(set), patch] as const + } + return [set, combine(makeAdd(value))(patch)] as const + } + )(newValue) + return HashSet.reduce(patch, (patch, value: Value) => combine(makeRemove(value))(patch))(removed) +} + +/** @internal */ +export const combine = Dual.dual< + ( + that: Differ.HashSet.Patch + ) => ( + self: Differ.HashSet.Patch + ) => Differ.HashSet.Patch, + ( + self: Differ.HashSet.Patch, + that: Differ.HashSet.Patch + ) => Differ.HashSet.Patch +>(2, (self, that) => makeAndThen(self, that)) + +/** @internal */ +export const patch = Dual.dual< + ( + oldValue: HashSet.HashSet + ) => ( + self: Differ.HashSet.Patch + ) => HashSet.HashSet, + ( + self: Differ.HashSet.Patch, + oldValue: HashSet.HashSet + ) => HashSet.HashSet +>(2, ( + self: Differ.HashSet.Patch, + oldValue: HashSet.HashSet +) => { + if ((self as Instruction)._tag === "Empty") { + return oldValue + } + let set = oldValue + let patches: Chunk.Chunk> = Chunk.of(self) + while (Chunk.isNonEmpty(patches)) { + const head: Instruction = Chunk.headNonEmpty(patches) as Instruction + const tail = Chunk.tailNonEmpty(patches) + switch (head._tag) { + case "Empty": { + patches = tail + break + } + case "AndThen": { + patches = Chunk.prepend(head.first)(Chunk.prepend(head.second)(tail)) + break + } + case "Add": { + set = HashSet.add(head.value)(set) + patches = tail + break + } + case "Remove": { + set = HashSet.remove(head.value)(set) + patches = tail + } + } + } + return set +}) diff --git a/backend/node_modules/effect/src/internal/differ/orPatch.ts b/backend/node_modules/effect/src/internal/differ/orPatch.ts new file mode 100644 index 0000000000000000000000000000000000000000..71e71cc67500db48ec13f9b6e32b95cac95edf4f --- /dev/null +++ b/backend/node_modules/effect/src/internal/differ/orPatch.ts @@ -0,0 +1,311 @@ +import * as Chunk from "../../Chunk.js" +import type { Differ } from "../../Differ.js" +import type { Either } from "../../Either.js" +import * as E from "../../Either.js" +import * as Equal from "../../Equal.js" +import * as Dual from "../../Function.js" +import { Structural } from "../data.js" + +/** @internal */ +export const OrPatchTypeId: Differ.Or.TypeId = Symbol.for("effect/DifferOrPatch") as Differ.Or.TypeId + +function variance(a: A): B { + return a as unknown as B +} + +/** @internal */ +const PatchProto = { + ...Structural.prototype, + [OrPatchTypeId]: { + _Value: variance, + _Key: variance, + _Patch: variance + } +} + +/** @internal */ +export interface Empty + extends Differ.Or.Patch +{ + readonly _tag: "Empty" +} + +const EmptyProto = Object.assign(Object.create(PatchProto), { + _tag: "Empty" +}) + +const _empty = Object.create(EmptyProto) + +/** @internal */ +export const empty = (): Differ.Or.Patch< + Value, + Value2, + Patch, + Patch2 +> => _empty + +/** @internal */ +export interface AndThen + extends Differ.Or.Patch +{ + readonly _tag: "AndThen" + readonly first: Differ.Or.Patch + readonly second: Differ.Or.Patch +} + +const AndThenProto = Object.assign(Object.create(PatchProto), { + _tag: "AndThen" +}) + +/** @internal */ +export const makeAndThen = ( + first: Differ.Or.Patch, + second: Differ.Or.Patch +): Differ.Or.Patch< + Value, + Value2, + Patch, + Patch2 +> => { + const o = Object.create(AndThenProto) + o.first = first + o.second = second + return o +} + +/** @internal */ +export interface SetLeft + extends Differ.Or.Patch +{ + readonly _tag: "SetLeft" + readonly value: Value +} + +const SetLeftProto = Object.assign(Object.create(PatchProto), { + _tag: "SetLeft" +}) + +/** @internal */ +export const makeSetLeft = ( + value: Value +): Differ.Or.Patch< + Value, + Value2, + Patch, + Patch2 +> => { + const o = Object.create(SetLeftProto) + o.value = value + return o +} + +/** @internal */ +export interface SetRight + extends Differ.Or.Patch +{ + readonly _tag: "SetRight" + readonly value: Value2 +} + +const SetRightProto = Object.assign(Object.create(PatchProto), { + _tag: "SetRight" +}) + +/** @internal */ +export const makeSetRight = ( + value: Value2 +): Differ.Or.Patch< + Value, + Value2, + Patch, + Patch2 +> => { + const o = Object.create(SetRightProto) + o.value = value + return o +} + +/** @internal */ +export interface UpdateLeft + extends Differ.Or.Patch +{ + readonly _tag: "UpdateLeft" + readonly patch: Patch +} + +const UpdateLeftProto = Object.assign(Object.create(PatchProto), { + _tag: "UpdateLeft" +}) + +/** @internal */ +export const makeUpdateLeft = ( + patch: Patch +): Differ.Or.Patch< + Value, + Value2, + Patch, + Patch2 +> => { + const o = Object.create(UpdateLeftProto) + o.patch = patch + return o +} + +/** @internal */ +export interface UpdateRight + extends Differ.Or.Patch +{ + readonly _tag: "UpdateRight" + readonly patch: Patch2 +} + +const UpdateRightProto = Object.assign(Object.create(PatchProto), { + _tag: "UpdateRight" +}) + +/** @internal */ +export const makeUpdateRight = ( + patch: Patch2 +): Differ.Or.Patch< + Value, + Value2, + Patch, + Patch2 +> => { + const o = Object.create(UpdateRightProto) + o.patch = patch + return o +} + +type Instruction = + | AndThen + | Empty + | SetLeft + | SetRight + | UpdateLeft + | UpdateRight + +/** @internal */ +export const diff = ( + options: { + readonly oldValue: Either + readonly newValue: Either + readonly left: Differ + readonly right: Differ + } +): Differ.Or.Patch => { + switch (options.oldValue._tag) { + case "Left": { + switch (options.newValue._tag) { + case "Left": { + const valuePatch = options.left.diff(options.oldValue.left, options.newValue.left) + if (Equal.equals(valuePatch, options.left.empty)) { + return empty() + } + return makeUpdateLeft(valuePatch) + } + case "Right": { + return makeSetRight(options.newValue.right) + } + } + } + case "Right": { + switch (options.newValue._tag) { + case "Left": { + return makeSetLeft(options.newValue.left) + } + case "Right": { + const valuePatch = options.right.diff(options.oldValue.right, options.newValue.right) + if (Equal.equals(valuePatch, options.right.empty)) { + return empty() + } + return makeUpdateRight(valuePatch) + } + } + } + } +} + +/** @internal */ +export const combine = Dual.dual< + ( + that: Differ.Or.Patch + ) => ( + self: Differ.Or.Patch + ) => Differ.Or.Patch, + ( + self: Differ.Or.Patch, + that: Differ.Or.Patch + ) => Differ.Or.Patch +>(2, (self, that) => makeAndThen(self, that)) + +/** @internal */ +export const patch = Dual.dual< + ( + options: { + readonly oldValue: Either + readonly left: Differ + readonly right: Differ + } + ) => (self: Differ.Or.Patch) => Either, + ( + self: Differ.Or.Patch, + options: { + readonly oldValue: Either + readonly left: Differ + readonly right: Differ + } + ) => Either +>(2, ( + self: Differ.Or.Patch, + { left, oldValue, right }: { + oldValue: Either + left: Differ + right: Differ + } +) => { + if ((self as Instruction)._tag === "Empty") { + return oldValue + } + let patches: Chunk.Chunk> = Chunk.of(self) + let result = oldValue + while (Chunk.isNonEmpty(patches)) { + const head: Instruction = Chunk.headNonEmpty(patches) as Instruction + const tail = Chunk.tailNonEmpty(patches) + switch (head._tag) { + case "Empty": { + patches = tail + break + } + case "AndThen": { + patches = Chunk.prepend(head.first)(Chunk.prepend(head.second)(tail)) + break + } + case "UpdateLeft": { + if (result._tag === "Left") { + result = E.left(left.patch(head.patch, result.left)) + } + patches = tail + break + } + case "UpdateRight": { + if (result._tag === "Right") { + result = E.right(right.patch(head.patch, result.right)) + } + patches = tail + break + } + case "SetLeft": { + result = E.left(head.value) + patches = tail + break + } + case "SetRight": { + result = E.right(head.value) + patches = tail + break + } + } + } + return result +}) diff --git a/backend/node_modules/effect/src/internal/differ/readonlyArrayPatch.ts b/backend/node_modules/effect/src/internal/differ/readonlyArrayPatch.ts new file mode 100644 index 0000000000000000000000000000000000000000..2cf69801b8420d767c89b814a0194a99544f7015 --- /dev/null +++ b/backend/node_modules/effect/src/internal/differ/readonlyArrayPatch.ts @@ -0,0 +1,210 @@ +import * as Arr from "../../Array.js" +import type * as Differ from "../../Differ.js" +import * as Equal from "../../Equal.js" +import * as Dual from "../../Function.js" +import * as Data from "../data.js" + +/** @internal */ +export const ReadonlyArrayPatchTypeId: Differ.Differ.ReadonlyArray.TypeId = Symbol.for( + "effect/DifferReadonlyArrayPatch" +) as Differ.Differ.ReadonlyArray.TypeId + +function variance(a: A): B { + return a as unknown as B +} + +const PatchProto = { + ...Data.Structural.prototype, + [ReadonlyArrayPatchTypeId]: { + _Value: variance, + _Patch: variance + } +} + +interface Empty extends Differ.Differ.ReadonlyArray.Patch { + readonly _tag: "Empty" +} + +const EmptyProto = Object.assign(Object.create(PatchProto), { + _tag: "Empty" +}) + +const _empty = Object.create(EmptyProto) + +/** + * @internal + */ +export const empty = (): Differ.Differ.ReadonlyArray.Patch => _empty + +interface AndThen extends Differ.Differ.ReadonlyArray.Patch { + readonly _tag: "AndThen" + readonly first: Differ.Differ.ReadonlyArray.Patch + readonly second: Differ.Differ.ReadonlyArray.Patch +} + +const AndThenProto = Object.assign(Object.create(PatchProto), { + _tag: "AndThen" +}) + +const makeAndThen = ( + first: Differ.Differ.ReadonlyArray.Patch, + second: Differ.Differ.ReadonlyArray.Patch +): Differ.Differ.ReadonlyArray.Patch => { + const o = Object.create(AndThenProto) + o.first = first + o.second = second + return o +} + +interface Append extends Differ.Differ.ReadonlyArray.Patch { + readonly _tag: "Append" + readonly values: ReadonlyArray +} + +const AppendProto = Object.assign(Object.create(PatchProto), { + _tag: "Append" +}) + +const makeAppend = (values: ReadonlyArray): Differ.Differ.ReadonlyArray.Patch => { + const o = Object.create(AppendProto) + o.values = values + return o +} + +interface Slice extends Differ.Differ.ReadonlyArray.Patch { + readonly _tag: "Slice" + readonly from: number + readonly until: number +} + +const SliceProto = Object.assign(Object.create(PatchProto), { + _tag: "Slice" +}) + +const makeSlice = (from: number, until: number): Differ.Differ.ReadonlyArray.Patch => { + const o = Object.create(SliceProto) + o.from = from + o.until = until + return o +} + +interface Update extends Differ.Differ.ReadonlyArray.Patch { + readonly _tag: "Update" + readonly index: number + readonly patch: Patch +} + +const UpdateProto = Object.assign(Object.create(PatchProto), { + _tag: "Update" +}) + +const makeUpdate = (index: number, patch: Patch): Differ.Differ.ReadonlyArray.Patch => { + const o = Object.create(UpdateProto) + o.index = index + o.patch = patch + return o +} + +type Instruction = + | Empty + | AndThen + | Append + | Slice + | Update + +/** @internal */ +export const diff = ( + options: { + readonly oldValue: ReadonlyArray + readonly newValue: ReadonlyArray + readonly differ: Differ.Differ + } +): Differ.Differ.ReadonlyArray.Patch => { + let i = 0 + let patch = empty() + while (i < options.oldValue.length && i < options.newValue.length) { + const oldElement = options.oldValue[i]! + const newElement = options.newValue[i]! + const valuePatch = options.differ.diff(oldElement, newElement) + if (!Equal.equals(valuePatch, options.differ.empty)) { + patch = combine(patch, makeUpdate(i, valuePatch)) + } + i = i + 1 + } + if (i < options.oldValue.length) { + patch = combine(patch, makeSlice(0, i)) + } + if (i < options.newValue.length) { + patch = combine(patch, makeAppend(Arr.drop(i)(options.newValue))) + } + return patch +} + +/** @internal */ +export const combine = Dual.dual< + ( + that: Differ.Differ.ReadonlyArray.Patch + ) => ( + self: Differ.Differ.ReadonlyArray.Patch + ) => Differ.Differ.ReadonlyArray.Patch, + ( + self: Differ.Differ.ReadonlyArray.Patch, + that: Differ.Differ.ReadonlyArray.Patch + ) => Differ.Differ.ReadonlyArray.Patch +>(2, (self, that) => makeAndThen(self, that)) + +/** @internal */ +export const patch = Dual.dual< + ( + oldValue: ReadonlyArray, + differ: Differ.Differ + ) => (self: Differ.Differ.ReadonlyArray.Patch) => ReadonlyArray, + ( + self: Differ.Differ.ReadonlyArray.Patch, + oldValue: ReadonlyArray, + differ: Differ.Differ + ) => ReadonlyArray +>(3, ( + self: Differ.Differ.ReadonlyArray.Patch, + oldValue: ReadonlyArray, + differ: Differ.Differ +) => { + if ((self as Instruction)._tag === "Empty") { + return oldValue + } + let readonlyArray = oldValue.slice() + let patches: Array> = Arr.of(self) + while (Arr.isNonEmptyArray(patches)) { + const head: Instruction = Arr.headNonEmpty(patches) as Instruction + const tail = Arr.tailNonEmpty(patches) + switch (head._tag) { + case "Empty": { + patches = tail + break + } + case "AndThen": { + tail.unshift(head.first, head.second) + patches = tail + break + } + case "Append": { + for (const value of head.values) { + readonlyArray.push(value) + } + patches = tail + break + } + case "Slice": { + readonlyArray = readonlyArray.slice(head.from, head.until) + patches = tail + break + } + case "Update": { + readonlyArray[head.index] = differ.patch(head.patch, readonlyArray[head.index]!) + patches = tail + break + } + } + } + return readonlyArray +}) diff --git a/backend/node_modules/effect/src/internal/doNotation.ts b/backend/node_modules/effect/src/internal/doNotation.ts new file mode 100644 index 0000000000000000000000000000000000000000..333895e1c2854ec6c43752155d918ea8de5659f9 --- /dev/null +++ b/backend/node_modules/effect/src/internal/doNotation.ts @@ -0,0 +1,80 @@ +import { dual } from "../Function.js" +import type { Kind, TypeLambda } from "../HKT.js" +import type { NoInfer } from "../Types.js" + +type Map = { + (f: (a: A) => B): (self: Kind) => Kind + (self: Kind, f: (a: A) => B): Kind +} + +type FlatMap = { + ( + f: (a: A) => Kind + ): (self: Kind) => Kind + ( + self: Kind, + f: (a: A) => Kind + ): Kind +} + +/** @internal */ +export const let_ = ( + map: Map +): { + ( + name: Exclude, + f: (a: NoInfer) => B + ): ( + self: Kind + ) => Kind + ( + self: Kind, + name: Exclude, + f: (a: NoInfer) => B + ): Kind +} => + dual(3, ( + self: Kind, + name: Exclude, + f: (a: NoInfer) => B + ): Kind => + map(self, (a) => ({ ...a, [name]: f(a) }) as any)) + +/** @internal */ +export const bindTo = (map: Map): { + ( + name: N + ): (self: Kind) => Kind + ( + self: Kind, + name: N + ): Kind +} => + dual(2, ( + self: Kind, + name: N + ): Kind => map(self, (a) => ({ [name]: a } as { [K in N]: A }))) + +/** @internal */ +export const bind = (map: Map, flatMap: FlatMap): { + ( + name: Exclude, + f: (a: NoInfer) => Kind + ): ( + self: Kind + ) => Kind + ( + self: Kind, + name: Exclude, + f: (a: NoInfer) => Kind + ): Kind +} => + dual(3, ( + self: Kind, + name: Exclude, + f: (a: NoInfer) => Kind + ): Kind => + flatMap( + self, + (a) => map(f(a), (b) => ({ ...a, [name]: b }) as { [K in keyof A | N]: K extends keyof A ? A[K] : B }) + )) diff --git a/backend/node_modules/effect/src/internal/effect/circular.ts b/backend/node_modules/effect/src/internal/effect/circular.ts new file mode 100644 index 0000000000000000000000000000000000000000..e64ea05d880675721304871cdd65427d79b4e3ae --- /dev/null +++ b/backend/node_modules/effect/src/internal/effect/circular.ts @@ -0,0 +1,903 @@ +import type * as Cause from "../../Cause.js" +import type * as Deferred from "../../Deferred.js" +import * as Duration from "../../Duration.js" +import type * as Effect from "../../Effect.js" +import * as Effectable from "../../Effectable.js" +import * as Equal from "../../Equal.js" +import type { Equivalence } from "../../Equivalence.js" +import * as Exit from "../../Exit.js" +import type * as Fiber from "../../Fiber.js" +import * as FiberId from "../../FiberId.js" +import type * as FiberRefsPatch from "../../FiberRefsPatch.js" +import type { LazyArg } from "../../Function.js" +import { dual, pipe } from "../../Function.js" +import * as Hash from "../../Hash.js" +import * as MutableHashMap from "../../MutableHashMap.js" +import * as Option from "../../Option.js" +import { pipeArguments } from "../../Pipeable.js" +import * as Predicate from "../../Predicate.js" +import * as Readable from "../../Readable.js" +import type * as Ref from "../../Ref.js" +import { currentScheduler } from "../../Scheduler.js" +import type * as Scope from "../../Scope.js" +import type * as Supervisor from "../../Supervisor.js" +import type * as Synchronized from "../../SynchronizedRef.js" +import type * as Types from "../../Types.js" +import * as internalCause from "../cause.js" +import * as effect from "../core-effect.js" +import * as core from "../core.js" +import * as internalFiber from "../fiber.js" +import * as fiberRuntime from "../fiberRuntime.js" +import { globalScope } from "../fiberScope.js" +import * as internalRef from "../ref.js" +import * as supervisor from "../supervisor.js" + +/** @internal */ +class Semaphore { + public waiters = new Set<() => void>() + public taken = 0 + + constructor(public permits: number) {} + + get free() { + return this.permits - this.taken + } + + readonly take = (n: number): Effect.Effect => + core.asyncInterrupt((resume) => { + if (this.free < n) { + const observer = () => { + if (this.free < n) return + this.waiters.delete(observer) + resume(core.suspend(() => { + if (this.free < n) return this.take(n) + this.taken += n + return core.succeed(n) + })) + } + this.waiters.add(observer) + return core.sync(() => { + this.waiters.delete(observer) + }) + } + resume(core.suspend(() => { + if (this.free < n) return this.take(n) + this.taken += n + return core.succeed(n) + })) + }) + + updateTakenUnsafe(fiber: Fiber.RuntimeFiber, f: (n: number) => number): Effect.Effect { + this.taken = f(this.taken) + if (this.waiters.size > 0) { + fiber.getFiberRef(currentScheduler).scheduleTask( + () => { + const iter = this.waiters.values() + let item = iter.next() + while (item.done === false && this.free > 0) { + item.value() + item = iter.next() + } + }, + fiber.getFiberRef(core.currentSchedulingPriority), + fiber + ) + } + return core.succeed(this.free) + } + + updateTaken(f: (n: number) => number): Effect.Effect { + return core.withFiberRuntime((fiber) => this.updateTakenUnsafe(fiber, f)) + } + + readonly resize = (permits: number) => + core.asVoid( + core.withFiberRuntime((fiber) => { + this.permits = permits + if (this.free < 0) { + return core.void + } + return this.updateTakenUnsafe(fiber, (taken) => taken) + }) + ) + + readonly release = (n: number): Effect.Effect => this.updateTaken((taken) => taken - n) + + readonly releaseAll: Effect.Effect = this.updateTaken((_) => 0) + + readonly withPermits = (n: number) => (self: Effect.Effect) => + core.uninterruptibleMask((restore) => + core.flatMap( + restore(this.take(n)), + (permits) => fiberRuntime.ensuring(restore(self), this.release(permits)) + ) + ) + + readonly withPermitsIfAvailable = (n: number) => (self: Effect.Effect) => + core.uninterruptibleMask((restore) => + core.suspend(() => { + if (this.free < n) { + return effect.succeedNone + } + this.taken += n + return fiberRuntime.ensuring(restore(effect.asSome(self)), this.release(n)) + }) + ) +} + +/** @internal */ +export const unsafeMakeSemaphore = (permits: number): Effect.Semaphore => new Semaphore(permits) + +/** @internal */ +export const makeSemaphore = (permits: number) => core.sync(() => unsafeMakeSemaphore(permits)) + +class Latch extends Effectable.Class implements Effect.Latch { + waiters: Array<(_: Effect.Effect) => void> = [] + scheduled = false + constructor(private isOpen: boolean) { + super() + } + + commit() { + return this.await + } + + private unsafeSchedule(fiber: Fiber.RuntimeFiber) { + if (this.scheduled || this.waiters.length === 0) { + return core.void + } + this.scheduled = true + fiber.currentScheduler.scheduleTask(this.flushWaiters, fiber.getFiberRef(core.currentSchedulingPriority), fiber) + return core.void + } + private flushWaiters = () => { + this.scheduled = false + const waiters = this.waiters + this.waiters = [] + for (let i = 0; i < waiters.length; i++) { + waiters[i](core.exitVoid) + } + } + + open = core.withFiberRuntime((fiber) => { + if (this.isOpen) { + return core.void + } + this.isOpen = true + return this.unsafeSchedule(fiber) + }) + unsafeOpen() { + if (this.isOpen) return + this.isOpen = true + this.flushWaiters() + } + release = core.withFiberRuntime((fiber) => { + if (this.isOpen) { + return core.void + } + return this.unsafeSchedule(fiber) + }) + await = core.asyncInterrupt((resume) => { + if (this.isOpen) { + return resume(core.void) + } + this.waiters.push(resume) + return core.sync(() => { + const index = this.waiters.indexOf(resume) + if (index !== -1) { + this.waiters.splice(index, 1) + } + }) + }) + unsafeClose() { + this.isOpen = false + } + close = core.sync(() => { + this.isOpen = false + }) + whenOpen = (self: Effect.Effect): Effect.Effect => { + return core.zipRight(this.await, self) + } +} + +/** @internal */ +export const unsafeMakeLatch = (open?: boolean | undefined): Effect.Latch => new Latch(open ?? false) + +/** @internal */ +export const makeLatch = (open?: boolean | undefined) => core.sync(() => unsafeMakeLatch(open)) + +/** @internal */ +export const awaitAllChildren = (self: Effect.Effect): Effect.Effect => + ensuringChildren(self, fiberRuntime.fiberAwaitAll) + +/** @internal */ +export const cached: { + ( + timeToLive: Duration.DurationInput + ): (self: Effect.Effect) => Effect.Effect, never, R> + ( + self: Effect.Effect, + timeToLive: Duration.DurationInput + ): Effect.Effect, never, R> +} = dual( + 2, + ( + self: Effect.Effect, + timeToLive: Duration.DurationInput + ): Effect.Effect, never, R> => + core.map(cachedInvalidateWithTTL(self, timeToLive), (tuple) => tuple[0]) +) + +/** @internal */ +export const cachedInvalidateWithTTL: { + (timeToLive: Duration.DurationInput): ( + self: Effect.Effect + ) => Effect.Effect<[Effect.Effect, Effect.Effect], never, R> + ( + self: Effect.Effect, + timeToLive: Duration.DurationInput + ): Effect.Effect<[Effect.Effect, Effect.Effect], never, R> +} = dual( + 2, + ( + self: Effect.Effect, + timeToLive: Duration.DurationInput + ): Effect.Effect<[Effect.Effect, Effect.Effect], never, R> => { + const duration = Duration.decode(timeToLive) + return core.flatMap( + core.context(), + (env) => + core.map( + makeSynchronized]>>(Option.none()), + (cache) => + [ + core.provideContext(getCachedValue(self, duration, cache), env), + invalidateCache(cache) + ] as [Effect.Effect, Effect.Effect] + ) + ) + } +) + +/** @internal */ +const computeCachedValue = ( + self: Effect.Effect, + timeToLive: Duration.DurationInput, + start: number +): Effect.Effect]>, never, R> => { + const timeToLiveMillis = Duration.toMillis(Duration.decode(timeToLive)) + return pipe( + core.deferredMake(), + core.tap((deferred) => core.intoDeferred(self, deferred)), + core.map((deferred) => Option.some([start + timeToLiveMillis, deferred])) + ) +} + +/** @internal */ +const getCachedValue = ( + self: Effect.Effect, + timeToLive: Duration.DurationInput, + cache: Synchronized.SynchronizedRef]>> +): Effect.Effect => + core.uninterruptibleMask((restore) => + pipe( + effect.clockWith((clock) => clock.currentTimeMillis), + core.flatMap((time) => + updateSomeAndGetEffectSynchronized(cache, (option) => { + switch (option._tag) { + case "None": { + return Option.some(computeCachedValue(self, timeToLive, time)) + } + case "Some": { + const [end] = option.value + return end - time <= 0 + ? Option.some(computeCachedValue(self, timeToLive, time)) + : Option.none() + } + } + }) + ), + core.flatMap((option) => + Option.isNone(option) ? + core.dieMessage( + "BUG: Effect.cachedInvalidate - please report an issue at https://github.com/Effect-TS/effect/issues" + ) : + restore(core.deferredAwait(option.value[1])) + ) + ) + ) + +/** @internal */ +const invalidateCache = ( + cache: Synchronized.SynchronizedRef]>> +): Effect.Effect => internalRef.set(cache, Option.none()) + +/** @internal */ +export const ensuringChild = dual< + ( + f: (fiber: Fiber.Fiber, any>) => Effect.Effect + ) => ( + self: Effect.Effect + ) => Effect.Effect, + ( + self: Effect.Effect, + f: (fiber: Fiber.Fiber, any>) => Effect.Effect + ) => Effect.Effect +>(2, (self, f) => ensuringChildren(self, (children) => f(fiberRuntime.fiberAll(children)))) + +/** @internal */ +export const ensuringChildren = dual< + ( + children: (fibers: ReadonlyArray>) => Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + children: (fibers: ReadonlyArray>) => Effect.Effect + ) => Effect.Effect +>(2, (self, children) => + core.flatMap(supervisor.track, (supervisor) => + pipe( + supervised(self, supervisor), + fiberRuntime.ensuring(core.flatMap(supervisor.value, children)) + ))) + +/** @internal */ +export const forkAll: { + ( + options?: { + readonly discard?: false | undefined + } + ): >( + effects: Iterable + ) => Effect.Effect< + Fiber.Fiber>, Effect.Effect.Error>, + never, + Effect.Effect.Context + > + (options: { + readonly discard: true + }): >( + effects: Iterable + ) => Effect.Effect> + >( + effects: Iterable, + options?: { + readonly discard?: false | undefined + } + ): Effect.Effect< + Fiber.Fiber>, Effect.Effect.Error>, + never, + Effect.Effect.Context + > + >(effects: Iterable, options: { + readonly discard: true + }): Effect.Effect> +} = dual((args) => Predicate.isIterable(args[0]), (effects: Iterable>, options: { + readonly discard: true +}): Effect.Effect => + options?.discard ? + core.forEachSequentialDiscard(effects, fiberRuntime.fork) : + core.map(core.forEachSequential(effects, fiberRuntime.fork), fiberRuntime.fiberAll)) + +/** @internal */ +export const forkIn = dual< + (scope: Scope.Scope) => (self: Effect.Effect) => Effect.Effect, never, R>, + (self: Effect.Effect, scope: Scope.Scope) => Effect.Effect, never, R> +>( + 2, + (self, scope) => + core.withFiberRuntime((parent, parentStatus) => { + const scopeImpl = scope as fiberRuntime.ScopeImpl + const fiber = fiberRuntime.unsafeFork(self, parent, parentStatus.runtimeFlags, globalScope) + if (scopeImpl.state._tag === "Open") { + const finalizer = () => + core.fiberIdWith((fiberId) => + Equal.equals(fiberId, fiber.id()) ? + core.void : + core.asVoid(core.interruptFiber(fiber)) + ) + const key = {} + scopeImpl.state.finalizers.set(key, finalizer) + fiber.addObserver(() => { + if (scopeImpl.state._tag === "Closed") return + scopeImpl.state.finalizers.delete(key) + }) + } else { + fiber.unsafeInterruptAsFork(parent.id()) + } + return core.succeed(fiber) + }) +) + +/** @internal */ +export const forkScoped = ( + self: Effect.Effect +): Effect.Effect, never, R | Scope.Scope> => + fiberRuntime.scopeWith((scope) => forkIn(self, scope)) + +/** @internal */ +export const fromFiber = (fiber: Fiber.Fiber): Effect.Effect => internalFiber.join(fiber) + +/** @internal */ +export const fromFiberEffect = (fiber: Effect.Effect, E, R>): Effect.Effect => + core.suspend(() => core.flatMap(fiber, internalFiber.join)) + +const memoKeySymbol = Symbol.for("effect/Effect/memoizeFunction.key") + +class Key implements Equal.Equal { + [memoKeySymbol] = memoKeySymbol + constructor(readonly a: A, readonly eq?: Equivalence) {} + [Equal.symbol](that: Equal.Equal) { + if (Predicate.hasProperty(that, memoKeySymbol)) { + if (this.eq) { + return this.eq(this.a, (that as unknown as Key).a) + } else { + return Equal.equals(this.a, (that as unknown as Key).a) + } + } + return false + } + [Hash.symbol]() { + return this.eq ? 0 : Hash.cached(this, Hash.hash(this.a)) + } +} + +/** @internal */ +export const cachedFunction = ( + f: (a: A) => Effect.Effect, + eq?: Equivalence +): Effect.Effect<(a: A) => Effect.Effect> => { + return pipe( + core.sync(() => MutableHashMap.empty, Deferred.Deferred>()), + core.flatMap(makeSynchronized), + core.map((ref) => (a: A) => + pipe( + ref.modifyEffect((map) => { + const result = pipe(map, MutableHashMap.get(new Key(a, eq))) + if (Option.isNone(result)) { + return pipe( + core.deferredMake(), + core.tap((deferred) => + pipe( + effect.diffFiberRefs(f(a)), + core.intoDeferred(deferred), + fiberRuntime.fork + ) + ), + core.map((deferred) => [deferred, pipe(map, MutableHashMap.set(new Key(a, eq), deferred))] as const) + ) + } + return core.succeed([result.value, map] as const) + }), + core.flatMap(core.deferredAwait), + core.flatMap(([patch, b]) => pipe(effect.patchFiberRefs(patch), core.as(b))) + ) + ) + ) +} + +/** @internal */ +export const raceFirst = dual< + ( + that: Effect.Effect + ) => ( + self: Effect.Effect + ) => Effect.Effect, + ( + self: Effect.Effect, + that: Effect.Effect + ) => Effect.Effect +>(2, ( + self: Effect.Effect, + that: Effect.Effect +) => + pipe( + core.exit(self), + fiberRuntime.race(core.exit(that)), + (effect: Effect.Effect, never, R | R2>) => core.flatten(effect) + )) + +/** @internal */ +export const supervised = dual< + (supervisor: Supervisor.Supervisor) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, supervisor: Supervisor.Supervisor) => Effect.Effect +>(2, (self, supervisor) => { + const supervise = core.fiberRefLocallyWith(fiberRuntime.currentSupervisor, (s) => s.zip(supervisor)) + return supervise(self) +}) + +/** @internal */ +export const timeout = dual< + ( + duration: Duration.DurationInput + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + duration: Duration.DurationInput + ) => Effect.Effect +>(2, (self, duration) => + timeoutFail(self, { + onTimeout: () => core.timeoutExceptionFromDuration(duration), + duration + })) + +/** @internal */ +export const timeoutFail = dual< + ( + options: { + readonly onTimeout: LazyArg + readonly duration: Duration.DurationInput + } + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + options: { + readonly onTimeout: LazyArg + readonly duration: Duration.DurationInput + } + ) => Effect.Effect +>(2, (self, { duration, onTimeout }) => + core.flatten(timeoutTo(self, { + onTimeout: () => core.failSync(onTimeout), + onSuccess: core.succeed, + duration + }))) + +/** @internal */ +export const timeoutFailCause = dual< + ( + options: { + readonly onTimeout: LazyArg> + readonly duration: Duration.DurationInput + } + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + options: { + readonly onTimeout: LazyArg> + readonly duration: Duration.DurationInput + } + ) => Effect.Effect +>(2, (self, { duration, onTimeout }) => + core.flatten(timeoutTo(self, { + onTimeout: () => core.failCauseSync(onTimeout), + onSuccess: core.succeed, + duration + }))) + +/** @internal */ +export const timeoutOption = dual< + ( + duration: Duration.DurationInput + ) => (self: Effect.Effect) => Effect.Effect, E, R>, + ( + self: Effect.Effect, + duration: Duration.DurationInput + ) => Effect.Effect, E, R> +>(2, (self, duration) => + timeoutTo(self, { + duration, + onSuccess: Option.some, + onTimeout: Option.none + })) + +/** @internal */ +export const timeoutTo = dual< + ( + options: { + readonly onTimeout: LazyArg + readonly onSuccess: (a: A) => B + readonly duration: Duration.DurationInput + } + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + options: { + readonly onTimeout: LazyArg + readonly onSuccess: (a: A) => B + readonly duration: Duration.DurationInput + } + ) => Effect.Effect +>( + 2, + (self, { duration, onSuccess, onTimeout }) => + core.fiberIdWith((parentFiberId) => + core.uninterruptibleMask((restore) => + fiberRuntime.raceFibersWith( + restore(self), + core.interruptible(effect.sleep(duration)), + { + onSelfWin: (winner, loser) => + core.flatMap( + winner.await, + (exit) => { + if (exit._tag === "Success") { + return core.flatMap( + winner.inheritAll, + () => + core.as( + core.interruptAsFiber(loser, parentFiberId), + onSuccess(exit.value) + ) + ) + } else { + return core.flatMap( + core.interruptAsFiber(loser, parentFiberId), + () => core.exitFailCause(exit.cause) + ) + } + } + ), + onOtherWin: (winner, loser) => + core.flatMap( + winner.await, + (exit) => { + if (exit._tag === "Success") { + return core.flatMap( + winner.inheritAll, + () => + core.as( + core.interruptAsFiber(loser, parentFiberId), + onTimeout() + ) + ) + } else { + return core.flatMap( + core.interruptAsFiber(loser, parentFiberId), + () => core.exitFailCause(exit.cause) + ) + } + } + ), + otherScope: globalScope + } + ) + ) + ) +) + +// circular with Synchronized + +/** @internal */ +const SynchronizedSymbolKey = "effect/Ref/SynchronizedRef" + +/** @internal */ +export const SynchronizedTypeId: Synchronized.SynchronizedRefTypeId = Symbol.for( + SynchronizedSymbolKey +) as Synchronized.SynchronizedRefTypeId + +/** @internal */ +export const synchronizedVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +class SynchronizedImpl extends Effectable.Class implements Synchronized.SynchronizedRef { + readonly [SynchronizedTypeId] = synchronizedVariance + readonly [internalRef.RefTypeId] = internalRef.refVariance + readonly [Readable.TypeId]: Readable.TypeId = Readable.TypeId + constructor( + readonly ref: Ref.Ref, + readonly withLock: (self: Effect.Effect) => Effect.Effect + ) { + super() + this.get = internalRef.get(this.ref) + } + readonly get: Effect.Effect + commit() { + return this.get + } + modify(f: (a: A) => readonly [B, A]): Effect.Effect { + return this.modifyEffect((a) => core.succeed(f(a))) + } + modifyEffect(f: (a: A) => Effect.Effect): Effect.Effect { + return this.withLock( + pipe( + core.flatMap(internalRef.get(this.ref), f), + core.flatMap(([b, a]) => core.as(internalRef.set(this.ref, a), b)) + ) + ) + } +} + +/** @internal */ +export const makeSynchronized = (value: A): Effect.Effect> => + core.sync(() => unsafeMakeSynchronized(value)) + +/** @internal */ +export const unsafeMakeSynchronized = (value: A): Synchronized.SynchronizedRef => { + const ref = internalRef.unsafeMake(value) + const sem = unsafeMakeSemaphore(1) + return new SynchronizedImpl(ref, sem.withPermits(1)) +} + +/** @internal */ +export const updateSomeAndGetEffectSynchronized = dual< + ( + pf: (a: A) => Option.Option> + ) => (self: Synchronized.SynchronizedRef) => Effect.Effect, + ( + self: Synchronized.SynchronizedRef, + pf: (a: A) => Option.Option> + ) => Effect.Effect +>(2, (self, pf) => + self.modifyEffect((value) => { + const result = pf(value) + switch (result._tag) { + case "None": { + return core.succeed([value, value] as const) + } + case "Some": { + return core.map(result.value, (a) => [a, a] as const) + } + } + })) + +// circular with Fiber + +/** @internal */ +export const zipFiber = dual< + (that: Fiber.Fiber) => (self: Fiber.Fiber) => Fiber.Fiber<[A, A2], E | E2>, + (self: Fiber.Fiber, that: Fiber.Fiber) => Fiber.Fiber<[A, A2], E | E2> +>(2, (self, that) => zipWithFiber(self, that, (a, b) => [a, b])) + +/** @internal */ +export const zipLeftFiber = dual< + (that: Fiber.Fiber) => (self: Fiber.Fiber) => Fiber.Fiber, + (self: Fiber.Fiber, that: Fiber.Fiber) => Fiber.Fiber +>(2, (self, that) => zipWithFiber(self, that, (a, _) => a)) + +/** @internal */ +export const zipRightFiber = dual< + (that: Fiber.Fiber) => (self: Fiber.Fiber) => Fiber.Fiber, + (self: Fiber.Fiber, that: Fiber.Fiber) => Fiber.Fiber +>(2, (self, that) => zipWithFiber(self, that, (_, b) => b)) + +/** @internal */ +export const zipWithFiber = dual< + ( + that: Fiber.Fiber, + f: (a: A, b: B) => C + ) => (self: Fiber.Fiber) => Fiber.Fiber, + ( + self: Fiber.Fiber, + that: Fiber.Fiber, + f: (a: A, b: B) => C + ) => Fiber.Fiber +>(3, (self, that, f) => ({ + ...Effectable.CommitPrototype, + commit() { + return internalFiber.join(this) + }, + [internalFiber.FiberTypeId]: internalFiber.fiberVariance, + id: () => pipe(self.id(), FiberId.getOrElse(that.id())), + await: pipe( + self.await, + core.flatten, + fiberRuntime.zipWithOptions(core.flatten(that.await), f, { concurrent: true }), + core.exit + ), + children: self.children, + inheritAll: core.zipRight( + that.inheritAll, + self.inheritAll + ), + poll: core.zipWith( + self.poll, + that.poll, + (optionA, optionB) => + pipe( + optionA, + Option.flatMap((exitA) => + pipe( + optionB, + Option.map((exitB) => + Exit.zipWith(exitA, exitB, { + onSuccess: f, + onFailure: internalCause.parallel + }) + ) + ) + ) + ) + ), + interruptAsFork: (id) => + core.zipRight( + self.interruptAsFork(id), + that.interruptAsFork(id) + ), + pipe() { + return pipeArguments(this, arguments) + } +})) + +/* @internal */ +export const bindAll: { + < + A extends object, + X extends Record>, + O extends Types.NoExcessProperties<{ + readonly concurrency?: Types.Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly mode?: "default" | "validate" | "either" | undefined + readonly concurrentFinalizers?: boolean | undefined + }, O> + >( + f: (a: A) => [Extract] extends [never] ? X : `Duplicate keys`, + options?: undefined | O + ): ( + self: Effect.Effect + ) => [Effect.All.ReturnObject>] extends + [Effect.Effect] ? Effect.Effect< + { + [K in keyof A | keyof Success]: K extends keyof A ? A[K] + : K extends keyof Success ? Success[K] + : never + }, + | E1 + | Error, + R1 | Context + > + : never + + < + A extends object, + X extends Record>, + O extends Types.NoExcessProperties<{ + readonly concurrency?: Types.Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly mode?: "default" | "validate" | "either" | undefined + readonly concurrentFinalizers?: boolean | undefined + }, O>, + E1, + R1 + >( + self: Effect.Effect, + f: (a: A) => [Extract] extends [never] ? X : `Duplicate keys`, + options?: undefined | { + readonly concurrency?: Types.Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly mode?: "default" | "validate" | "either" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ): [Effect.All.ReturnObject>] extends + [Effect.Effect] ? Effect.Effect< + { + [K in keyof A | keyof Success]: K extends keyof A ? A[K] + : K extends keyof Success ? Success[K] + : never + }, + | E1 + | Error, + R1 | Context + > + : never +} = dual((args) => core.isEffect(args[0]), < + A extends object, + X extends Record>, + O extends Types.NoExcessProperties<{ + readonly concurrency?: Types.Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly mode?: "default" | "validate" | "either" | undefined + readonly concurrentFinalizers?: boolean | undefined + }, O>, + E1, + R1 +>( + self: Effect.Effect, + f: (a: A) => X, + options?: undefined | O +) => + core.flatMap( + self, + (a) => + (fiberRuntime.all(f(a), options) as Effect.All.ReturnObject< + X, + Effect.All.IsDiscard, + Effect.All.ExtractMode + >) + .pipe( + core.map((record) => Object.assign({}, a, record)) + ) + )) diff --git a/backend/node_modules/effect/src/internal/effectable.ts b/backend/node_modules/effect/src/internal/effectable.ts new file mode 100644 index 0000000000000000000000000000000000000000..b5c9f22c6ba205093c6bc98e4520b9f0c1bb74b6 --- /dev/null +++ b/backend/node_modules/effect/src/internal/effectable.ts @@ -0,0 +1,131 @@ +import type * as Channel from "../Channel.js" +import type * as Effect from "../Effect.js" +import type * as Effectable from "../Effectable.js" +import * as Equal from "../Equal.js" +import * as Hash from "../Hash.js" +import { pipeArguments } from "../Pipeable.js" +import type * as Sink from "../Sink.js" +import type * as Stream from "../Stream.js" +import { SingleShotGen, YieldWrap } from "../Utils.js" +import * as OpCodes from "./opCodes/effect.js" +import * as version from "./version.js" + +/** @internal */ +export const EffectTypeId: Effect.EffectTypeId = Symbol.for("effect/Effect") as Effect.EffectTypeId + +/** @internal */ +export const StreamTypeId: Stream.StreamTypeId = Symbol.for("effect/Stream") as Stream.StreamTypeId + +/** @internal */ +export const SinkTypeId: Sink.SinkTypeId = Symbol.for("effect/Sink") as Sink.SinkTypeId + +/** @internal */ +export const ChannelTypeId: Channel.ChannelTypeId = Symbol.for("effect/Channel") as Channel.ChannelTypeId + +/** @internal */ +export const effectVariance = { + /* c8 ignore next */ + _R: (_: never) => _, + /* c8 ignore next */ + _E: (_: never) => _, + /* c8 ignore next */ + _A: (_: never) => _, + + _V: version.getCurrentVersion() +} + +const sinkVariance = { + /* c8 ignore next */ + _A: (_: never) => _, + /* c8 ignore next */ + _In: (_: unknown) => _, + /* c8 ignore next */ + _L: (_: never) => _, + /* c8 ignore next */ + _E: (_: never) => _, + /* c8 ignore next */ + _R: (_: never) => _ +} + +const channelVariance = { + /* c8 ignore next */ + _Env: (_: never) => _, + /* c8 ignore next */ + _InErr: (_: unknown) => _, + /* c8 ignore next */ + _InElem: (_: unknown) => _, + /* c8 ignore next */ + _InDone: (_: unknown) => _, + /* c8 ignore next */ + _OutErr: (_: never) => _, + /* c8 ignore next */ + _OutElem: (_: never) => _, + /* c8 ignore next */ + _OutDone: (_: never) => _ +} + +/** @internal */ +export const EffectPrototype: Effect.Effect & Equal.Equal = { + [EffectTypeId]: effectVariance, + [StreamTypeId]: effectVariance, + [SinkTypeId]: sinkVariance, + [ChannelTypeId]: channelVariance, + [Equal.symbol](that: any) { + return this === that + }, + [Hash.symbol]() { + return Hash.cached(this, Hash.random(this)) + }, + [Symbol.iterator]() { + return new SingleShotGen(new YieldWrap(this)) as any + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const StructuralPrototype: Equal.Equal = { + [Hash.symbol]() { + return Hash.cached(this, Hash.structure(this)) + }, + [Equal.symbol](this: Equal.Equal, that: Equal.Equal) { + const selfKeys = Object.keys(this) + const thatKeys = Object.keys(that as object) + if (selfKeys.length !== thatKeys.length) { + return false + } + for (const key of selfKeys) { + if (!(key in (that as object) && Equal.equals((this as any)[key], (that as any)[key]))) { + return false + } + } + return true + } +} + +/** @internal */ +export const CommitPrototype: Effect.Effect = { + ...EffectPrototype, + _op: OpCodes.OP_COMMIT +} as any + +/** @internal */ +export const StructuralCommitPrototype: Effect.Effect = { + ...CommitPrototype, + ...StructuralPrototype +} as any + +/** @internal */ +export const Base: Effectable.CommitPrimitive = (function() { + function Base() {} + Base.prototype = CommitPrototype + return Base as any +})() + +/** @internal */ +export const StructuralBase: Effectable.CommitPrimitive = (function() { + function Base() {} + Base.prototype = StructuralCommitPrototype + return Base as any +})() diff --git a/backend/node_modules/effect/src/internal/either.ts b/backend/node_modules/effect/src/internal/either.ts new file mode 100644 index 0000000000000000000000000000000000000000..6ef2f5f55f528c6395f8c7e630e417644b24dc63 --- /dev/null +++ b/backend/node_modules/effect/src/internal/either.ts @@ -0,0 +1,110 @@ +/** + * @since 2.0.0 + */ + +import type * as Either from "../Either.js" +import * as Equal from "../Equal.js" +import { dual } from "../Function.js" +import * as Hash from "../Hash.js" +import { format, NodeInspectSymbol, toJSON } from "../Inspectable.js" +import type { Option } from "../Option.js" +import { hasProperty } from "../Predicate.js" +import { EffectPrototype } from "./effectable.js" +import * as option from "./option.js" + +/** + * @internal + */ +export const TypeId: Either.TypeId = Symbol.for("effect/Either") as Either.TypeId + +const CommonProto = { + ...EffectPrototype, + [TypeId]: { + _R: (_: never) => _ + }, + [NodeInspectSymbol](this: Either.Either) { + return this.toJSON() + }, + toString(this: Either.Left) { + return format(this.toJSON()) + } +} + +const RightProto = Object.assign(Object.create(CommonProto), { + _tag: "Right", + _op: "Right", + [Equal.symbol](this: Either.Right, that: unknown): boolean { + return isEither(that) && isRight(that) && Equal.equals(this.right, that.right) + }, + [Hash.symbol](this: Either.Right) { + return Hash.combine(Hash.hash(this._tag))(Hash.hash(this.right)) + }, + toJSON(this: Either.Right) { + return { + _id: "Either", + _tag: this._tag, + right: toJSON(this.right) + } + } +}) + +const LeftProto = Object.assign(Object.create(CommonProto), { + _tag: "Left", + _op: "Left", + [Equal.symbol](this: Either.Left, that: unknown): boolean { + return isEither(that) && isLeft(that) && Equal.equals(this.left, that.left) + }, + [Hash.symbol](this: Either.Left) { + return Hash.combine(Hash.hash(this._tag))(Hash.hash(this.left)) + }, + toJSON(this: Either.Left) { + return { + _id: "Either", + _tag: this._tag, + left: toJSON(this.left) + } + } +}) + +/** @internal */ +export const isEither = (input: unknown): input is Either.Either => hasProperty(input, TypeId) + +/** @internal */ +export const isLeft = (ma: Either.Either): ma is Either.Left => ma._tag === "Left" + +/** @internal */ +export const isRight = (ma: Either.Either): ma is Either.Right => ma._tag === "Right" + +/** @internal */ +export const left = (left: L): Either.Either => { + const a = Object.create(LeftProto) + a.left = left + return a +} + +/** @internal */ +export const right = (right: R): Either.Either => { + const a = Object.create(RightProto) + a.right = right + return a +} + +/** @internal */ +export const getLeft = ( + self: Either.Either +): Option => (isRight(self) ? option.none : option.some(self.left)) + +/** @internal */ +export const getRight = ( + self: Either.Either +): Option => (isLeft(self) ? option.none : option.some(self.right)) + +/** @internal */ +export const fromOption: { + (onNone: () => L): (self: Option) => Either.Either + (self: Option, onNone: () => L): Either.Either +} = dual( + 2, + (self: Option, onNone: () => L): Either.Either => + option.isNone(self) ? left(onNone()) : right(self.value) +) diff --git a/backend/node_modules/effect/src/internal/encoding/base64.ts b/backend/node_modules/effect/src/internal/encoding/base64.ts new file mode 100644 index 0000000000000000000000000000000000000000..e23d0f6949ad9553617dadd6214d5abaa054f99f --- /dev/null +++ b/backend/node_modules/effect/src/internal/encoding/base64.ts @@ -0,0 +1,286 @@ +import * as Either from "../../Either.js" +import type * as Encoding from "../../Encoding.js" +import { DecodeException } from "./common.js" + +/** @internal */ +export const encode = (bytes: Uint8Array) => { + const length = bytes.length + + let result = "" + let i: number + + for (i = 2; i < length; i += 3) { + result += base64abc[bytes[i - 2] >> 2] + result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)] + result += base64abc[((bytes[i - 1] & 0x0f) << 2) | (bytes[i] >> 6)] + result += base64abc[bytes[i] & 0x3f] + } + + if (i === length + 1) { + // 1 octet yet to write + result += base64abc[bytes[i - 2] >> 2] + result += base64abc[(bytes[i - 2] & 0x03) << 4] + result += "==" + } + + if (i === length) { + // 2 octets yet to write + result += base64abc[bytes[i - 2] >> 2] + result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)] + result += base64abc[(bytes[i - 1] & 0x0f) << 2] + result += "=" + } + + return result +} + +/** @internal */ +export const decode = (str: string): Either.Either => { + const stripped = stripCrlf(str) + const length = stripped.length + if (length % 4 !== 0) { + return Either.left( + DecodeException(stripped, `Length must be a multiple of 4, but is ${length}`) + ) + } + + const index = stripped.indexOf("=") + if (index !== -1 && ((index < length - 2) || (index === length - 2 && stripped[length - 1] !== "="))) { + return Either.left( + DecodeException(stripped, "Found a '=' character, but it is not at the end") + ) + } + + try { + const missingOctets = stripped.endsWith("==") ? 2 : stripped.endsWith("=") ? 1 : 0 + const result = new Uint8Array(3 * (length / 4) - missingOctets) + for (let i = 0, j = 0; i < length; i += 4, j += 3) { + const buffer = getBase64Code(stripped.charCodeAt(i)) << 18 | + getBase64Code(stripped.charCodeAt(i + 1)) << 12 | + getBase64Code(stripped.charCodeAt(i + 2)) << 6 | + getBase64Code(stripped.charCodeAt(i + 3)) + + result[j] = buffer >> 16 + result[j + 1] = (buffer >> 8) & 0xff + result[j + 2] = buffer & 0xff + } + + return Either.right(result) + } catch (e) { + return Either.left( + DecodeException(stripped, e instanceof Error ? e.message : "Invalid input") + ) + } +} + +/** @internal */ +export const stripCrlf = (str: string) => str.replace(/[\n\r]/g, "") + +/** @internal */ +function getBase64Code(charCode: number) { + if (charCode >= base64codes.length) { + throw new TypeError(`Invalid character ${String.fromCharCode(charCode)}`) + } + + const code = base64codes[charCode] + if (code === 255) { + throw new TypeError(`Invalid character ${String.fromCharCode(charCode)}`) + } + + return code +} + +/** @internal */ +const base64abc = [ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "+", + "/" +] + +/** @internal */ +const base64codes = [ + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 62, + 255, + 255, + 255, + 63, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 255, + 255, + 255, + 0, + 255, + 255, + 255, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 255, + 255, + 255, + 255, + 255, + 255, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51 +] diff --git a/backend/node_modules/effect/src/internal/encoding/base64Url.ts b/backend/node_modules/effect/src/internal/encoding/base64Url.ts new file mode 100644 index 0000000000000000000000000000000000000000..8906af2e0807e281575150b6028c8c4fea7aa34e --- /dev/null +++ b/backend/node_modules/effect/src/internal/encoding/base64Url.ts @@ -0,0 +1,29 @@ +import * as Either from "../../Either.js" +import type * as Encoding from "../../Encoding.js" +import * as Base64 from "./base64.js" +import { DecodeException } from "./common.js" + +/** @internal */ +export const encode = (data: Uint8Array) => + Base64.encode(data).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_") + +/** @internal */ +export const decode = (str: string): Either.Either => { + const stripped = Base64.stripCrlf(str) + const length = stripped.length + if (length % 4 === 1) { + return Either.left( + DecodeException(stripped, `Length should be a multiple of 4, but is ${length}`) + ) + } + + if (!/^[-_A-Z0-9]*?={0,2}$/i.test(stripped)) { + return Either.left(DecodeException(stripped, "Invalid input")) + } + + // Some variants allow or require omitting the padding '=' signs + let sanitized = length % 4 === 2 ? `${stripped}==` : length % 4 === 3 ? `${stripped}=` : stripped + sanitized = sanitized.replace(/-/g, "+").replace(/_/g, "/") + + return Base64.decode(sanitized) +} diff --git a/backend/node_modules/effect/src/internal/encoding/common.ts b/backend/node_modules/effect/src/internal/encoding/common.ts new file mode 100644 index 0000000000000000000000000000000000000000..580cd6ffae0bb07c1b204aefe2bd5848a91a2b65 --- /dev/null +++ b/backend/node_modules/effect/src/internal/encoding/common.ts @@ -0,0 +1,51 @@ +import type * as Encoding from "../../Encoding.js" +import { hasProperty, isString } from "../../Predicate.js" +import type { Mutable } from "../../Types.js" + +/** @internal */ +export const DecodeExceptionTypeId: Encoding.DecodeExceptionTypeId = Symbol.for( + "effect/Encoding/errors/Decode" +) as Encoding.DecodeExceptionTypeId + +/** @internal */ +export const DecodeException = (input: string, message?: string): Encoding.DecodeException => { + const out: Mutable = { + _tag: "DecodeException", + [DecodeExceptionTypeId]: DecodeExceptionTypeId, + input + } + if (isString(message)) { + out.message = message + } + return out +} + +/** @internal */ +export const isDecodeException = (u: unknown): u is Encoding.DecodeException => hasProperty(u, DecodeExceptionTypeId) + +/** @internal */ +export const EncodeExceptionTypeId: Encoding.EncodeExceptionTypeId = Symbol.for( + "effect/Encoding/errors/Encode" +) as Encoding.EncodeExceptionTypeId + +/** @internal */ +export const EncodeException = (input: string, message?: string): Encoding.EncodeException => { + const out: Mutable = { + _tag: "EncodeException", + [EncodeExceptionTypeId]: EncodeExceptionTypeId, + input + } + if (isString(message)) { + out.message = message + } + return out +} + +/** @internal */ +export const isEncodeException = (u: unknown): u is Encoding.EncodeException => hasProperty(u, EncodeExceptionTypeId) + +/** @interal */ +export const encoder = new TextEncoder() + +/** @interal */ +export const decoder = new TextDecoder() diff --git a/backend/node_modules/effect/src/internal/encoding/hex.ts b/backend/node_modules/effect/src/internal/encoding/hex.ts new file mode 100644 index 0000000000000000000000000000000000000000..a46d71202c5583b2df1c5ce762aab2d4b0dae1cf --- /dev/null +++ b/backend/node_modules/effect/src/internal/encoding/hex.ts @@ -0,0 +1,315 @@ +import * as Either from "../../Either.js" +import type * as Encoding from "../../Encoding.js" +import { DecodeException } from "./common.js" + +/** @internal */ +export const encode = (bytes: Uint8Array) => { + let result = "" + for (let i = 0; i < bytes.length; ++i) { + result += bytesToHex[bytes[i]] + } + + return result +} + +/** @internal */ +export const decode = (str: string): Either.Either => { + const bytes = new TextEncoder().encode(str) + if (bytes.length % 2 !== 0) { + return Either.left(DecodeException(str, `Length must be a multiple of 2, but is ${bytes.length}`)) + } + + try { + const length = bytes.length / 2 + const result = new Uint8Array(length) + for (let i = 0; i < length; i++) { + const a = fromHexChar(bytes[i * 2]) + const b = fromHexChar(bytes[i * 2 + 1]) + result[i] = (a << 4) | b + } + + return Either.right(result) + } catch (e) { + return Either.left(DecodeException(str, e instanceof Error ? e.message : "Invalid input")) + } +} + +/** @internal */ +const bytesToHex = [ + "00", + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "0a", + "0b", + "0c", + "0d", + "0e", + "0f", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "1a", + "1b", + "1c", + "1d", + "1e", + "1f", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "2a", + "2b", + "2c", + "2d", + "2e", + "2f", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "3a", + "3b", + "3c", + "3d", + "3e", + "3f", + "40", + "41", + "42", + "43", + "44", + "45", + "46", + "47", + "48", + "49", + "4a", + "4b", + "4c", + "4d", + "4e", + "4f", + "50", + "51", + "52", + "53", + "54", + "55", + "56", + "57", + "58", + "59", + "5a", + "5b", + "5c", + "5d", + "5e", + "5f", + "60", + "61", + "62", + "63", + "64", + "65", + "66", + "67", + "68", + "69", + "6a", + "6b", + "6c", + "6d", + "6e", + "6f", + "70", + "71", + "72", + "73", + "74", + "75", + "76", + "77", + "78", + "79", + "7a", + "7b", + "7c", + "7d", + "7e", + "7f", + "80", + "81", + "82", + "83", + "84", + "85", + "86", + "87", + "88", + "89", + "8a", + "8b", + "8c", + "8d", + "8e", + "8f", + "90", + "91", + "92", + "93", + "94", + "95", + "96", + "97", + "98", + "99", + "9a", + "9b", + "9c", + "9d", + "9e", + "9f", + "a0", + "a1", + "a2", + "a3", + "a4", + "a5", + "a6", + "a7", + "a8", + "a9", + "aa", + "ab", + "ac", + "ad", + "ae", + "af", + "b0", + "b1", + "b2", + "b3", + "b4", + "b5", + "b6", + "b7", + "b8", + "b9", + "ba", + "bb", + "bc", + "bd", + "be", + "bf", + "c0", + "c1", + "c2", + "c3", + "c4", + "c5", + "c6", + "c7", + "c8", + "c9", + "ca", + "cb", + "cc", + "cd", + "ce", + "cf", + "d0", + "d1", + "d2", + "d3", + "d4", + "d5", + "d6", + "d7", + "d8", + "d9", + "da", + "db", + "dc", + "dd", + "de", + "df", + "e0", + "e1", + "e2", + "e3", + "e4", + "e5", + "e6", + "e7", + "e8", + "e9", + "ea", + "eb", + "ec", + "ed", + "ee", + "ef", + "f0", + "f1", + "f2", + "f3", + "f4", + "f5", + "f6", + "f7", + "f8", + "f9", + "fa", + "fb", + "fc", + "fd", + "fe", + "ff" +] + +/** @internal */ +const fromHexChar = (byte: number) => { + // '0' <= byte && byte <= '9' + if (48 <= byte && byte <= 57) { + return byte - 48 + } + + // 'a' <= byte && byte <= 'f' + if (97 <= byte && byte <= 102) { + return byte - 97 + 10 + } + + // 'A' <= byte && byte <= 'F' + if (65 <= byte && byte <= 70) { + return byte - 65 + 10 + } + + throw new TypeError("Invalid input") +} diff --git a/backend/node_modules/effect/src/internal/errors.ts b/backend/node_modules/effect/src/internal/errors.ts new file mode 100644 index 0000000000000000000000000000000000000000..b471e559431e93afdca6967da8644c7b127c0ee7 --- /dev/null +++ b/backend/node_modules/effect/src/internal/errors.ts @@ -0,0 +1,7 @@ +/** + * @since 2.0.0 + */ + +/** @internal */ +export const getBugErrorMessage = (message: string) => + `BUG: ${message} - please report an issue at https://github.com/Effect-TS/effect/issues` diff --git a/backend/node_modules/effect/src/internal/executionPlan.ts b/backend/node_modules/effect/src/internal/executionPlan.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f625ba6aa405cb88b017feecdfb2b878010427a --- /dev/null +++ b/backend/node_modules/effect/src/internal/executionPlan.ts @@ -0,0 +1,114 @@ +import type { Effect } from "../Effect.js" +import * as Either from "../Either.js" +import type * as Api from "../ExecutionPlan.js" +import { dual } from "../Function.js" +import * as Predicate from "../Predicate.js" +import * as core from "./core.js" +import * as layer from "./layer.js" +import * as InternalSchedule from "./schedule.js" + +/** @internal */ +export const TypeId: Api.TypeId = Symbol.for("effect/ExecutionPlan") as Api.TypeId + +/** @internal */ +export const isExecutionPlan = (u: unknown): u is Api.ExecutionPlan => Predicate.hasProperty(u, TypeId) + +/** @internal */ +export const withExecutionPlan: { + ( + plan: Api.ExecutionPlan<{ + provides: Provides + input: Input + error: PlanE + requirements: PlanR + }> + ): (effect: Effect) => Effect< + A, + E | PlanE, + Exclude | PlanR + > + ( + effect: Effect, + plan: Api.ExecutionPlan<{ + provides: Provides + input: Input + error: PlanE + requirements: PlanR + }> + ): Effect< + A, + E | PlanE, + Exclude | PlanR + > +} = dual(2, ( + effect: Effect, + plan: Api.ExecutionPlan<{ + provides: Provides + input: Input + error: PlanE + requirements: PlanR + }> +) => + core.suspend(() => { + let i = 0 + let result: Either.Either | undefined + return core.flatMap( + core.whileLoop({ + while: () => i < plan.steps.length && (result === undefined || Either.isLeft(result)), + body: () => { + const step = plan.steps[i] + let nextEffect: Effect = layer.effect_provide(effect, step.provide as any) + if (result) { + let attempted = false + const wrapped = nextEffect + // ensure the schedule is applied at least once + nextEffect = core.suspend(() => { + if (attempted) return wrapped + attempted = true + return result! + }) + nextEffect = InternalSchedule.scheduleDefectRefail( + InternalSchedule.retry_Effect(nextEffect, scheduleFromStep(step, false)!) + ) + } else { + const schedule = scheduleFromStep(step, true) + nextEffect = schedule + ? InternalSchedule.scheduleDefectRefail(InternalSchedule.retry_Effect(nextEffect, schedule)) + : nextEffect + } + return core.either(nextEffect) + }, + step: (either) => { + result = either + i++ + } + }), + () => result! + ) + })) + +/** @internal */ +export const scheduleFromStep = ( + step: Api.ExecutionPlan<{ + provides: Provides + input: In + error: PlanE + requirements: PlanR + }>["steps"][number], + first: boolean +) => { + if (!first) { + return InternalSchedule.fromRetryOptions({ + schedule: step.schedule ? step.schedule : step.attempts ? undefined : InternalSchedule.once, + times: step.attempts, + while: step.while + }) + } else if (step.attempts === 1 || !(step.schedule || step.attempts)) { + return undefined + } + return InternalSchedule.fromRetryOptions({ + schedule: step.schedule, + while: step.while, + times: step.attempts ? step.attempts - 1 : undefined + }) +} diff --git a/backend/node_modules/effect/src/internal/executionStrategy.ts b/backend/node_modules/effect/src/internal/executionStrategy.ts new file mode 100644 index 0000000000000000000000000000000000000000..426a0d93d2111eb7f23d84c717361cc883751492 --- /dev/null +++ b/backend/node_modules/effect/src/internal/executionStrategy.ts @@ -0,0 +1,74 @@ +import type * as ExecutionStrategy from "../ExecutionStrategy.js" +import { dual } from "../Function.js" +import type { LazyArg } from "../Function.js" + +/** @internal */ +export const OP_SEQUENTIAL = "Sequential" as const + +/** @internal */ +export type OP_SEQUENTIAL = typeof OP_SEQUENTIAL + +/** @internal */ +export const OP_PARALLEL = "Parallel" as const + +/** @internal */ +export type OP_PARALLEL = typeof OP_PARALLEL + +/** @internal */ +export const OP_PARALLEL_N = "ParallelN" as const + +/** @internal */ +export type OP_PARALLEL_N = typeof OP_PARALLEL_N + +/** @internal */ +export const sequential: ExecutionStrategy.ExecutionStrategy = { _tag: OP_SEQUENTIAL } + +/** @internal */ +export const parallel: ExecutionStrategy.ExecutionStrategy = { _tag: OP_PARALLEL } + +/** @internal */ +export const parallelN = (parallelism: number): ExecutionStrategy.ExecutionStrategy => ({ + _tag: OP_PARALLEL_N, + parallelism +}) + +/** @internal */ +export const isSequential = (self: ExecutionStrategy.ExecutionStrategy): self is ExecutionStrategy.Sequential => + self._tag === OP_SEQUENTIAL + +/** @internal */ +export const isParallel = (self: ExecutionStrategy.ExecutionStrategy): self is ExecutionStrategy.Parallel => + self._tag === OP_PARALLEL + +/** @internal */ +export const isParallelN = (self: ExecutionStrategy.ExecutionStrategy): self is ExecutionStrategy.ParallelN => + self._tag === OP_PARALLEL_N + +/** @internal */ +export const match = dual< + (options: { + readonly onSequential: LazyArg + readonly onParallel: LazyArg + readonly onParallelN: (n: number) => A + }) => (self: ExecutionStrategy.ExecutionStrategy) => A, + ( + self: ExecutionStrategy.ExecutionStrategy, + options: { + readonly onSequential: LazyArg + readonly onParallel: LazyArg + readonly onParallelN: (n: number) => A + } + ) => A +>(2, (self, options) => { + switch (self._tag) { + case OP_SEQUENTIAL: { + return options.onSequential() + } + case OP_PARALLEL: { + return options.onParallel() + } + case OP_PARALLEL_N: { + return options.onParallelN(self.parallelism) + } + } +}) diff --git a/backend/node_modules/effect/src/internal/fiberId.ts b/backend/node_modules/effect/src/internal/fiberId.ts new file mode 100644 index 0000000000000000000000000000000000000000..c2f16666678f55678041a116262c009ffeab5839 --- /dev/null +++ b/backend/node_modules/effect/src/internal/fiberId.ts @@ -0,0 +1,267 @@ +import * as Equal from "../Equal.js" +import type * as FiberId from "../FiberId.js" +import { dual, pipe } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import * as Hash from "../Hash.js" +import * as HashSet from "../HashSet.js" +import { format, NodeInspectSymbol, toJSON } from "../Inspectable.js" +import * as MutableRef from "../MutableRef.js" +import * as Option from "../Option.js" +import { hasProperty } from "../Predicate.js" + +/** @internal */ +const FiberIdSymbolKey = "effect/FiberId" + +/** @internal */ +export const FiberIdTypeId: FiberId.FiberIdTypeId = Symbol.for( + FiberIdSymbolKey +) as FiberId.FiberIdTypeId + +/** @internal */ +const OP_NONE = "None" as const + +/** @internal */ +export type OP_NONE = typeof OP_NONE + +/** @internal */ +const OP_RUNTIME = "Runtime" as const + +/** @internal */ +export type OP_RUNTIME = typeof OP_RUNTIME + +/** @internal */ +const OP_COMPOSITE = "Composite" as const + +/** @internal */ +export type OP_COMPOSITE = typeof OP_COMPOSITE + +const emptyHash = Hash.string(`${FiberIdSymbolKey}-${OP_NONE}`) + +/** @internal */ +class None implements FiberId.None { + readonly [FiberIdTypeId]: FiberId.FiberIdTypeId = FiberIdTypeId + readonly _tag = OP_NONE + readonly id = -1 + readonly startTimeMillis = -1; + [Hash.symbol](): number { + return emptyHash + } + [Equal.symbol](that: unknown): boolean { + return isFiberId(that) && that._tag === OP_NONE + } + toString() { + return format(this.toJSON()) + } + toJSON() { + return { + _id: "FiberId", + _tag: this._tag + } + } + [NodeInspectSymbol]() { + return this.toJSON() + } +} + +/** @internal */ +class Runtime implements FiberId.Runtime { + readonly [FiberIdTypeId]: FiberId.FiberIdTypeId = FiberIdTypeId + readonly _tag = OP_RUNTIME + constructor( + readonly id: number, + readonly startTimeMillis: number + ) {} + [Hash.symbol](): number { + return Hash.cached(this, Hash.string(`${FiberIdSymbolKey}-${this._tag}-${this.id}-${this.startTimeMillis}`)) + } + [Equal.symbol](that: unknown): boolean { + return isFiberId(that) && + that._tag === OP_RUNTIME && + this.id === that.id && + this.startTimeMillis === that.startTimeMillis + } + toString() { + return format(this.toJSON()) + } + toJSON() { + return { + _id: "FiberId", + _tag: this._tag, + id: this.id, + startTimeMillis: this.startTimeMillis + } + } + [NodeInspectSymbol]() { + return this.toJSON() + } +} + +/** @internal */ +class Composite implements FiberId.Composite { + readonly [FiberIdTypeId]: FiberId.FiberIdTypeId = FiberIdTypeId + readonly _tag = OP_COMPOSITE + constructor( + readonly left: FiberId.FiberId, + readonly right: FiberId.FiberId + ) { + } + _hash: number | undefined; + [Hash.symbol](): number { + return pipe( + Hash.string(`${FiberIdSymbolKey}-${this._tag}`), + Hash.combine(Hash.hash(this.left)), + Hash.combine(Hash.hash(this.right)), + Hash.cached(this) + ) + } + [Equal.symbol](that: unknown): boolean { + return isFiberId(that) && + that._tag === OP_COMPOSITE && + Equal.equals(this.left, that.left) && + Equal.equals(this.right, that.right) + } + toString() { + return format(this.toJSON()) + } + toJSON() { + return { + _id: "FiberId", + _tag: this._tag, + left: toJSON(this.left), + right: toJSON(this.right) + } + } + [NodeInspectSymbol]() { + return this.toJSON() + } +} + +/** @internal */ +export const none: FiberId.None = new None() + +/** @internal */ +export const runtime = (id: number, startTimeMillis: number): FiberId.Runtime => { + return new Runtime(id, startTimeMillis) +} + +/** @internal */ +export const composite = (left: FiberId.FiberId, right: FiberId.FiberId): FiberId.Composite => { + return new Composite(left, right) +} + +/** @internal */ +export const isFiberId = (self: unknown): self is FiberId.FiberId => hasProperty(self, FiberIdTypeId) + +/** @internal */ +export const isNone = (self: FiberId.FiberId): self is FiberId.None => { + return self._tag === OP_NONE || pipe(toSet(self), HashSet.every((id) => isNone(id))) +} + +/** @internal */ +export const isRuntime = (self: FiberId.FiberId): self is FiberId.Runtime => { + return self._tag === OP_RUNTIME +} + +/** @internal */ +export const isComposite = (self: FiberId.FiberId): self is FiberId.Composite => { + return self._tag === OP_COMPOSITE +} + +/** @internal */ +export const combine = dual< + (that: FiberId.FiberId) => (self: FiberId.FiberId) => FiberId.FiberId, + (self: FiberId.FiberId, that: FiberId.FiberId) => FiberId.FiberId +>(2, (self, that) => { + if (self._tag === OP_NONE) { + return that + } + if (that._tag === OP_NONE) { + return self + } + return new Composite(self, that) +}) + +/** @internal */ +export const combineAll = (fiberIds: HashSet.HashSet): FiberId.FiberId => { + return pipe(fiberIds, HashSet.reduce(none as FiberId.FiberId, (a, b) => combine(b)(a))) +} + +/** @internal */ +export const getOrElse = dual< + (that: FiberId.FiberId) => (self: FiberId.FiberId) => FiberId.FiberId, + (self: FiberId.FiberId, that: FiberId.FiberId) => FiberId.FiberId +>(2, (self, that) => isNone(self) ? that : self) + +/** @internal */ +export const ids = (self: FiberId.FiberId): HashSet.HashSet => { + switch (self._tag) { + case OP_NONE: { + return HashSet.empty() + } + case OP_RUNTIME: { + return HashSet.make(self.id) + } + case OP_COMPOSITE: { + return pipe(ids(self.left), HashSet.union(ids(self.right))) + } + } +} + +const _fiberCounter = globalValue( + Symbol.for("effect/Fiber/Id/_fiberCounter"), + () => MutableRef.make(0) +) + +/** @internal */ +export const make = (id: number, startTimeSeconds: number): FiberId.FiberId => { + return new Runtime(id, startTimeSeconds) +} + +/** @internal */ +export const threadName = (self: FiberId.FiberId): string => { + const identifiers = Array.from(ids(self)).map((n) => `#${n}`).join(",") + return identifiers +} + +/** @internal */ +export const toOption = (self: FiberId.FiberId): Option.Option => { + const fiberIds = toSet(self) + if (HashSet.size(fiberIds) === 0) { + return Option.none() + } + let first = true + let acc: FiberId.FiberId + for (const fiberId of fiberIds) { + if (first) { + acc = fiberId + first = false + } else { + // @ts-expect-error + acc = pipe(acc, combine(fiberId)) + } + } + // @ts-expect-error + return Option.some(acc) +} + +/** @internal */ +export const toSet = (self: FiberId.FiberId): HashSet.HashSet => { + switch (self._tag) { + case OP_NONE: { + return HashSet.empty() + } + case OP_RUNTIME: { + return HashSet.make(self) + } + case OP_COMPOSITE: { + return pipe(toSet(self.left), HashSet.union(toSet(self.right))) + } + } +} + +/** @internal */ +export const unsafeMake = (): FiberId.Runtime => { + const id = MutableRef.get(_fiberCounter) + pipe(_fiberCounter, MutableRef.set(id + 1)) + return new Runtime(id, Date.now()) +} diff --git a/backend/node_modules/effect/src/internal/fiberMessage.ts b/backend/node_modules/effect/src/internal/fiberMessage.ts new file mode 100644 index 0000000000000000000000000000000000000000..bad70df4ed16a98250383a5652260489a9238922 --- /dev/null +++ b/backend/node_modules/effect/src/internal/fiberMessage.ts @@ -0,0 +1,82 @@ +import type * as Cause from "../Cause.js" +import type * as Effect from "../Effect.js" +import type * as FiberStatus from "../FiberStatus.js" +import type * as FiberRuntime from "./fiberRuntime.js" + +/** @internal */ +export type FiberMessage = InterruptSignal | Stateful | Resume | YieldNow + +/** @internal */ +export const OP_INTERRUPT_SIGNAL = "InterruptSignal" as const + +/** @internal */ +export type OP_INTERRUPT_SIGNAL = typeof OP_INTERRUPT_SIGNAL + +/** @internal */ +export const OP_STATEFUL = "Stateful" as const + +/** @internal */ +export type OP_STATEFUL = typeof OP_STATEFUL + +/** @internal */ +export const OP_RESUME = "Resume" as const + +/** @internal */ +export type OP_RESUME = typeof OP_RESUME + +/** @internal */ +export const OP_YIELD_NOW = "YieldNow" as const + +/** @internal */ +export type OP_YIELD_NOW = typeof OP_YIELD_NOW + +/** @internal */ +export interface InterruptSignal { + readonly _tag: OP_INTERRUPT_SIGNAL + readonly cause: Cause.Cause +} + +/** @internal */ +export interface Stateful { + readonly _tag: OP_STATEFUL + onFiber(fiber: FiberRuntime.FiberRuntime, status: FiberStatus.FiberStatus): void +} + +/** @internal */ +export interface Resume { + readonly _tag: OP_RESUME + readonly effect: Effect.Effect +} + +/** @internal */ +export interface YieldNow { + readonly _tag: OP_YIELD_NOW +} + +/** @internal */ +export const interruptSignal = (cause: Cause.Cause): FiberMessage => ({ + _tag: OP_INTERRUPT_SIGNAL, + cause +}) + +/** @internal */ +export const stateful = ( + onFiber: ( + fiber: FiberRuntime.FiberRuntime, + status: FiberStatus.FiberStatus + ) => void +): FiberMessage => ({ + _tag: OP_STATEFUL, + onFiber +}) + +/** @internal */ +export const resume = (effect: Effect.Effect): FiberMessage => ({ + _tag: OP_RESUME, + effect +}) + +/** @internal */ +export const yieldNow = (): FiberMessage => ({ + _tag: OP_YIELD_NOW +}) diff --git a/backend/node_modules/effect/src/internal/fiberRefs.ts b/backend/node_modules/effect/src/internal/fiberRefs.ts new file mode 100644 index 0000000000000000000000000000000000000000..8d9ece313bb6d2cb2dfb6bb55273dc8f71aae90a --- /dev/null +++ b/backend/node_modules/effect/src/internal/fiberRefs.ts @@ -0,0 +1,297 @@ +import * as Arr from "../Array.js" +import type * as Effect from "../Effect.js" +import * as Equal from "../Equal.js" +import type * as FiberId from "../FiberId.js" +import type * as FiberRef from "../FiberRef.js" +import type * as FiberRefs from "../FiberRefs.js" +import { dual, pipe } from "../Function.js" +import * as HashSet from "../HashSet.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import * as core from "./core.js" + +/** @internal */ +export function unsafeMake( + fiberRefLocals: Map, Arr.NonEmptyReadonlyArray> +): FiberRefs.FiberRefs { + return new FiberRefsImpl(fiberRefLocals) +} + +/** @internal */ +export function empty(): FiberRefs.FiberRefs { + return unsafeMake(new Map()) +} + +/** @internal */ +export const FiberRefsSym: FiberRefs.FiberRefsSym = Symbol.for("effect/FiberRefs") as FiberRefs.FiberRefsSym + +/** @internal */ +export class FiberRefsImpl implements FiberRefs.FiberRefs { + readonly [FiberRefsSym]: FiberRefs.FiberRefsSym = FiberRefsSym + constructor( + readonly locals: Map< + FiberRef.FiberRef, + Arr.NonEmptyReadonlyArray + > + ) {} + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +const findAncestor = ( + _ref: FiberRef.FiberRef, + _parentStack: ReadonlyArray, + _childStack: ReadonlyArray, + _childModified = false +): readonly [unknown, boolean] => { + const ref = _ref + let parentStack = _parentStack + let childStack = _childStack + let childModified = _childModified + let ret: readonly [unknown, boolean] | undefined = undefined + while (ret === undefined) { + if (Arr.isNonEmptyReadonlyArray(parentStack) && Arr.isNonEmptyReadonlyArray(childStack)) { + const parentFiberId = Arr.headNonEmpty(parentStack)[0] + const parentAncestors = Arr.tailNonEmpty(parentStack) + const childFiberId = Arr.headNonEmpty(childStack)[0] + const childRefValue = Arr.headNonEmpty(childStack)[1] + const childAncestors = Arr.tailNonEmpty(childStack) + if (parentFiberId.startTimeMillis < childFiberId.startTimeMillis) { + childStack = childAncestors + childModified = true + } else if (parentFiberId.startTimeMillis > childFiberId.startTimeMillis) { + parentStack = parentAncestors + } else { + if (parentFiberId.id < childFiberId.id) { + childStack = childAncestors + childModified = true + } else if (parentFiberId.id > childFiberId.id) { + parentStack = parentAncestors + } else { + ret = [childRefValue, childModified] as const + } + } + } else { + ret = [ref.initial, true] as const + } + } + return ret +} + +/** @internal */ +export const joinAs = dual< + (fiberId: FiberId.Single, that: FiberRefs.FiberRefs) => (self: FiberRefs.FiberRefs) => FiberRefs.FiberRefs, + (self: FiberRefs.FiberRefs, fiberId: FiberId.Single, that: FiberRefs.FiberRefs) => FiberRefs.FiberRefs +>(3, (self, fiberId, that) => { + const parentFiberRefs = new Map(self.locals) + that.locals.forEach((childStack, fiberRef) => { + const childValue = childStack[0][1] + if (!childStack[0][0][Equal.symbol](fiberId)) { + if (!parentFiberRefs.has(fiberRef)) { + if (Equal.equals(childValue, fiberRef.initial)) { + return + } + parentFiberRefs.set( + fiberRef, + [[fiberId, fiberRef.join(fiberRef.initial, childValue)]] + ) + return + } + const parentStack = parentFiberRefs.get(fiberRef)! + const [ancestor, wasModified] = findAncestor( + fiberRef, + parentStack, + childStack + ) + if (wasModified) { + const patch = fiberRef.diff(ancestor, childValue) + const oldValue = parentStack[0][1] + const newValue = fiberRef.join(oldValue, fiberRef.patch(patch)(oldValue)) + if (!Equal.equals(oldValue, newValue)) { + let newStack: Arr.NonEmptyReadonlyArray + const parentFiberId = parentStack[0][0] + if (parentFiberId[Equal.symbol](fiberId)) { + newStack = [[parentFiberId, newValue] as const, ...parentStack.slice(1)] + } else { + newStack = [[fiberId, newValue] as const, ...parentStack] + } + parentFiberRefs.set(fiberRef, newStack) + } + } + } + }) + return new FiberRefsImpl(parentFiberRefs) +}) + +/** @internal */ +export const forkAs = dual< + (childId: FiberId.Single) => (self: FiberRefs.FiberRefs) => FiberRefs.FiberRefs, + (self: FiberRefs.FiberRefs, childId: FiberId.Single) => FiberRefs.FiberRefs +>(2, (self, childId) => { + const map = new Map, Arr.NonEmptyReadonlyArray>() + unsafeForkAs(self, map, childId) + return new FiberRefsImpl(map) +}) + +const unsafeForkAs = ( + self: FiberRefs.FiberRefs, + map: Map, Arr.NonEmptyReadonlyArray>, + fiberId: FiberId.Single +) => { + self.locals.forEach((stack, fiberRef) => { + const oldValue = stack[0][1] + const newValue = fiberRef.patch(fiberRef.fork)(oldValue) + if (Equal.equals(oldValue, newValue)) { + map.set(fiberRef, stack) + } else { + map.set(fiberRef, [[fiberId, newValue] as const, ...stack]) + } + }) +} + +/** @internal */ +export const fiberRefs = (self: FiberRefs.FiberRefs) => HashSet.fromIterable(self.locals.keys()) + +/** @internal */ +export const setAll = (self: FiberRefs.FiberRefs): Effect.Effect => + core.forEachSequentialDiscard( + fiberRefs(self), + (fiberRef) => core.fiberRefSet(fiberRef, getOrDefault(self, fiberRef)) + ) + +/** @internal */ +export const delete_ = dual< + (fiberRef: FiberRef.FiberRef) => (self: FiberRefs.FiberRefs) => FiberRefs.FiberRefs, + (self: FiberRefs.FiberRefs, fiberRef: FiberRef.FiberRef) => FiberRefs.FiberRefs +>(2, (self, fiberRef) => { + const locals = new Map(self.locals) + locals.delete(fiberRef) + return new FiberRefsImpl(locals) +}) + +/** @internal */ +export const get = dual< + (fiberRef: FiberRef.FiberRef) => (self: FiberRefs.FiberRefs) => Option.Option, + (self: FiberRefs.FiberRefs, fiberRef: FiberRef.FiberRef) => Option.Option +>(2, (self, fiberRef) => { + if (!self.locals.has(fiberRef)) { + return Option.none() + } + return Option.some(Arr.headNonEmpty(self.locals.get(fiberRef)!)[1]) +}) + +/** @internal */ +export const getOrDefault = dual< + (fiberRef: FiberRef.FiberRef) => (self: FiberRefs.FiberRefs) => A, + (self: FiberRefs.FiberRefs, fiberRef: FiberRef.FiberRef) => A +>(2, (self, fiberRef) => pipe(get(self, fiberRef), Option.getOrElse(() => fiberRef.initial))) + +/** @internal */ +export const updateAs = dual< + ( + options: { + readonly fiberId: FiberId.Single + readonly fiberRef: FiberRef.FiberRef + readonly value: A + } + ) => (self: FiberRefs.FiberRefs) => FiberRefs.FiberRefs, + ( + self: FiberRefs.FiberRefs, + options: { + readonly fiberId: FiberId.Single + readonly fiberRef: FiberRef.FiberRef + readonly value: A + } + ) => FiberRefs.FiberRefs +>(2, (self: FiberRefs.FiberRefs, { fiberId, fiberRef, value }: { + readonly fiberId: FiberId.Single + readonly fiberRef: FiberRef.FiberRef + readonly value: A +}) => { + if (self.locals.size === 0) { + return new FiberRefsImpl(new Map([[fiberRef, [[fiberId, value] as const]]])) + } + const locals = new Map(self.locals) + unsafeUpdateAs(locals, fiberId, fiberRef, value) + return new FiberRefsImpl(locals) +}) + +const unsafeUpdateAs = ( + locals: Map, Arr.NonEmptyReadonlyArray>, + fiberId: FiberId.Single, + fiberRef: FiberRef.FiberRef, + value: any +) => { + const oldStack: ReadonlyArray = locals.get(fiberRef) ?? [] + let newStack: Arr.NonEmptyReadonlyArray | undefined + + if (Arr.isNonEmptyReadonlyArray(oldStack)) { + const [currentId, currentValue] = Arr.headNonEmpty(oldStack) + if (currentId[Equal.symbol](fiberId)) { + if (Equal.equals(currentValue, value)) { + return + } else { + newStack = [ + [fiberId, value] as const, + ...oldStack.slice(1) + ] + } + } else { + newStack = [ + [fiberId, value] as const, + ...oldStack + ] + } + } else { + newStack = [[fiberId, value] as const] + } + + locals.set(fiberRef, newStack) +} + +/** @internal */ +export const updateManyAs = dual< + ( + options: { + readonly forkAs?: FiberId.Single | undefined + readonly entries: Arr.NonEmptyReadonlyArray< + readonly [FiberRef.FiberRef, Arr.NonEmptyReadonlyArray] + > + } + ) => (self: FiberRefs.FiberRefs) => FiberRefs.FiberRefs, + ( + self: FiberRefs.FiberRefs, + options: { + readonly forkAs?: FiberId.Single | undefined + readonly entries: Arr.NonEmptyReadonlyArray< + readonly [FiberRef.FiberRef, Arr.NonEmptyReadonlyArray] + > + } + ) => FiberRefs.FiberRefs +>(2, (self: FiberRefs.FiberRefs, { entries, forkAs }: { + readonly forkAs?: FiberId.Single | undefined + readonly entries: Arr.NonEmptyReadonlyArray< + readonly [FiberRef.FiberRef, Arr.NonEmptyReadonlyArray] + > +}) => { + if (self.locals.size === 0) { + return new FiberRefsImpl(new Map(entries)) + } + + const locals = new Map(self.locals) + if (forkAs !== undefined) { + unsafeForkAs(self, locals, forkAs) + } + entries.forEach(([fiberRef, values]) => { + if (values.length === 1) { + unsafeUpdateAs(locals, values[0][0], fiberRef, values[0][1]) + } else { + values.forEach(([fiberId, value]) => { + unsafeUpdateAs(locals, fiberId, fiberRef, value) + }) + } + }) + return new FiberRefsImpl(locals) +}) diff --git a/backend/node_modules/effect/src/internal/fiberRefs/patch.ts b/backend/node_modules/effect/src/internal/fiberRefs/patch.ts new file mode 100644 index 0000000000000000000000000000000000000000..7a9115e2e0d0148b3fd13d2c15c6fbad6a5c0f0d --- /dev/null +++ b/backend/node_modules/effect/src/internal/fiberRefs/patch.ts @@ -0,0 +1,144 @@ +import * as Arr from "../../Array.js" +import { equals } from "../../Equal.js" +import type * as FiberId from "../../FiberId.js" +import type * as FiberRefs from "../../FiberRefs.js" +import type * as FiberRefsPatch from "../../FiberRefsPatch.js" +import { dual } from "../../Function.js" +import * as fiberRefs_ from "../fiberRefs.js" + +/** @internal */ +export const OP_EMPTY = "Empty" as const + +/** @internal */ +export type OP_EMPTY = typeof OP_EMPTY + +/** @internal */ +export const OP_ADD = "Add" as const + +/** @internal */ +export type OP_ADD = typeof OP_ADD + +/** @internal */ +export const OP_REMOVE = "Remove" as const + +/** @internal */ +export type OP_REMOVE = typeof OP_REMOVE + +/** @internal */ +export const OP_UPDATE = "Update" as const + +/** @internal */ +export type OP_UPDATE = typeof OP_UPDATE + +/** @internal */ +export const OP_AND_THEN = "AndThen" as const + +/** @internal */ +export type OP_AND_THEN = typeof OP_AND_THEN + +/** @internal */ +export const empty: FiberRefsPatch.FiberRefsPatch = ({ + _tag: OP_EMPTY +}) as FiberRefsPatch.FiberRefsPatch + +/** @internal */ +export const diff = ( + oldValue: FiberRefs.FiberRefs, + newValue: FiberRefs.FiberRefs +): FiberRefsPatch.FiberRefsPatch => { + const missingLocals = new Map(oldValue.locals) + let patch = empty + for (const [fiberRef, pairs] of newValue.locals.entries()) { + const newValue = Arr.headNonEmpty(pairs)[1] + const old = missingLocals.get(fiberRef) + if (old !== undefined) { + const oldValue = Arr.headNonEmpty(old)[1] + if (!equals(oldValue, newValue)) { + patch = combine({ + _tag: OP_UPDATE, + fiberRef, + patch: fiberRef.diff(oldValue, newValue) + })(patch) + } + } else { + patch = combine({ + _tag: OP_ADD, + fiberRef, + value: newValue + })(patch) + } + missingLocals.delete(fiberRef) + } + for (const [fiberRef] of missingLocals.entries()) { + patch = combine({ + _tag: OP_REMOVE, + fiberRef + })(patch) + } + return patch +} + +/** @internal */ +export const combine = dual< + (that: FiberRefsPatch.FiberRefsPatch) => (self: FiberRefsPatch.FiberRefsPatch) => FiberRefsPatch.FiberRefsPatch, + (self: FiberRefsPatch.FiberRefsPatch, that: FiberRefsPatch.FiberRefsPatch) => FiberRefsPatch.FiberRefsPatch +>(2, (self, that) => ({ + _tag: OP_AND_THEN, + first: self, + second: that +})) + +/** @internal */ +export const patch = dual< + ( + fiberId: FiberId.Runtime, + oldValue: FiberRefs.FiberRefs + ) => (self: FiberRefsPatch.FiberRefsPatch) => FiberRefs.FiberRefs, + ( + self: FiberRefsPatch.FiberRefsPatch, + fiberId: FiberId.Runtime, + oldValue: FiberRefs.FiberRefs + ) => FiberRefs.FiberRefs +>(3, (self, fiberId, oldValue) => { + let fiberRefs: FiberRefs.FiberRefs = oldValue + let patches: ReadonlyArray = Arr.of(self) + while (Arr.isNonEmptyReadonlyArray(patches)) { + const head = Arr.headNonEmpty(patches) + const tail = Arr.tailNonEmpty(patches) + switch (head._tag) { + case OP_EMPTY: { + patches = tail + break + } + case OP_ADD: { + fiberRefs = fiberRefs_.updateAs(fiberRefs, { + fiberId, + fiberRef: head.fiberRef, + value: head.value + }) + patches = tail + break + } + case OP_REMOVE: { + fiberRefs = fiberRefs_.delete_(fiberRefs, head.fiberRef) + patches = tail + break + } + case OP_UPDATE: { + const value = fiberRefs_.getOrDefault(fiberRefs, head.fiberRef) + fiberRefs = fiberRefs_.updateAs(fiberRefs, { + fiberId, + fiberRef: head.fiberRef, + value: head.fiberRef.patch(head.patch)(value) + }) + patches = tail + break + } + case OP_AND_THEN: { + patches = Arr.prepend(head.first)(Arr.prepend(head.second)(tail)) + break + } + } + } + return fiberRefs +}) diff --git a/backend/node_modules/effect/src/internal/fiberRuntime.ts b/backend/node_modules/effect/src/internal/fiberRuntime.ts new file mode 100644 index 0000000000000000000000000000000000000000..eaa1e32b9733020f799ed490db02041b98671e00 --- /dev/null +++ b/backend/node_modules/effect/src/internal/fiberRuntime.ts @@ -0,0 +1,3861 @@ +import * as RA from "../Array.js" +import * as Boolean from "../Boolean.js" +import type * as Cause from "../Cause.js" +import * as Chunk from "../Chunk.js" +import type * as Clock from "../Clock.js" +import type { ConfigProvider } from "../ConfigProvider.js" +import * as Context from "../Context.js" +import type { DefaultServices } from "../DefaultServices.js" +import type * as Duration from "../Duration.js" +import type * as Effect from "../Effect.js" +import * as Effectable from "../Effectable.js" +import type * as Either from "../Either.js" +import * as ExecutionStrategy from "../ExecutionStrategy.js" +import type * as Exit from "../Exit.js" +import type * as Fiber from "../Fiber.js" +import * as FiberId from "../FiberId.js" +import type * as FiberRef from "../FiberRef.js" +import * as FiberRefs from "../FiberRefs.js" +import * as FiberRefsPatch from "../FiberRefsPatch.js" +import * as FiberStatus from "../FiberStatus.js" +import type { LazyArg } from "../Function.js" +import { dual, identity, pipe } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import * as HashMap from "../HashMap.js" +import * as HashSet from "../HashSet.js" +import * as Inspectable from "../Inspectable.js" +import type { Logger } from "../Logger.js" +import * as LogLevel from "../LogLevel.js" +import type * as MetricLabel from "../MetricLabel.js" +import * as Micro from "../Micro.js" +import * as MRef from "../MutableRef.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import * as Predicate from "../Predicate.js" +import type * as Random from "../Random.js" +import * as Ref from "../Ref.js" +import type { Entry, Request } from "../Request.js" +import type * as RequestBlock from "../RequestBlock.js" +import type * as RuntimeFlags from "../RuntimeFlags.js" +import * as RuntimeFlagsPatch from "../RuntimeFlagsPatch.js" +import { currentScheduler, type Scheduler } from "../Scheduler.js" +import type * as Scope from "../Scope.js" +import type * as Supervisor from "../Supervisor.js" +import type * as Tracer from "../Tracer.js" +import type { Concurrency, NoExcessProperties, NoInfer } from "../Types.js" +import { internalCall, yieldWrapGet } from "../Utils.js" +import * as RequestBlock_ from "./blockedRequests.js" +import * as internalCause from "./cause.js" +import * as clock from "./clock.js" +import { currentRequestMap } from "./completedRequestMap.js" +import * as concurrency from "./concurrency.js" +import { configProviderTag } from "./configProvider.js" +import * as internalEffect from "./core-effect.js" +import * as core from "./core.js" +import * as defaultServices from "./defaultServices.js" +import { consoleTag } from "./defaultServices/console.js" +import * as executionStrategy from "./executionStrategy.js" +import * as internalFiber from "./fiber.js" +import * as FiberMessage from "./fiberMessage.js" +import * as fiberRefs from "./fiberRefs.js" +import * as fiberScope from "./fiberScope.js" +import * as internalLogger from "./logger.js" +import * as metric from "./metric.js" +import * as metricBoundaries from "./metric/boundaries.js" +import * as metricLabel from "./metric/label.js" +import * as OpCodes from "./opCodes/effect.js" +import { randomTag } from "./random.js" +import { complete } from "./request.js" +import * as runtimeFlags_ from "./runtimeFlags.js" +import { OpSupervision } from "./runtimeFlags.js" +import * as supervisor from "./supervisor.js" +import * as SupervisorPatch from "./supervisor/patch.js" +import * as tracer from "./tracer.js" +import * as version from "./version.js" + +/** @internal */ +export const fiberStarted = metric.counter("effect_fiber_started", { incremental: true }) +/** @internal */ +export const fiberActive = metric.counter("effect_fiber_active") +/** @internal */ +export const fiberSuccesses = metric.counter("effect_fiber_successes", { incremental: true }) +/** @internal */ +export const fiberFailures = metric.counter("effect_fiber_failures", { incremental: true }) +/** @internal */ +export const fiberLifetimes = metric.tagged( + metric.histogram( + "effect_fiber_lifetimes", + metricBoundaries.exponential({ + start: 0.5, + factor: 2, + count: 35 + }) + ), + "time_unit", + "milliseconds" +) + +/** @internal */ +type EvaluationSignal = + | EvaluationSignalContinue + | EvaluationSignalDone + | EvaluationSignalYieldNow + +/** @internal */ +const EvaluationSignalContinue = "Continue" as const + +/** @internal */ +type EvaluationSignalContinue = typeof EvaluationSignalContinue + +/** @internal */ +const EvaluationSignalDone = "Done" as const + +/** @internal */ +type EvaluationSignalDone = typeof EvaluationSignalDone + +/** @internal */ +const EvaluationSignalYieldNow = "Yield" as const + +/** @internal */ +type EvaluationSignalYieldNow = typeof EvaluationSignalYieldNow + +const runtimeFiberVariance = { + /* c8 ignore next */ + _E: (_: never) => _, + /* c8 ignore next */ + _A: (_: never) => _ +} + +const absurd = (_: never): never => { + throw new Error( + `BUG: FiberRuntime - ${ + Inspectable.toStringUnknown(_) + } - please report an issue at https://github.com/Effect-TS/effect/issues` + ) +} + +const YieldedOp = Symbol.for("effect/internal/fiberRuntime/YieldedOp") +type YieldedOp = typeof YieldedOp +const yieldedOpChannel: { + currentOp: core.Primitive | null +} = globalValue("effect/internal/fiberRuntime/yieldedOpChannel", () => ({ + currentOp: null +})) + +const contOpSuccess = { + [OpCodes.OP_ON_SUCCESS]: ( + _: FiberRuntime, + cont: core.OnSuccess, + value: unknown + ) => { + return internalCall(() => cont.effect_instruction_i1(value)) + }, + ["OnStep"]: ( + _: FiberRuntime, + _cont: core.OnStep, + value: unknown + ) => { + return core.exitSucceed(core.exitSucceed(value)) + }, + [OpCodes.OP_ON_SUCCESS_AND_FAILURE]: ( + _: FiberRuntime, + cont: core.OnSuccessAndFailure, + value: unknown + ) => { + return internalCall(() => cont.effect_instruction_i2(value)) + }, + [OpCodes.OP_REVERT_FLAGS]: ( + self: FiberRuntime, + cont: core.RevertFlags, + value: unknown + ) => { + self.patchRuntimeFlags(self.currentRuntimeFlags, cont.patch) + if (runtimeFlags_.interruptible(self.currentRuntimeFlags) && self.isInterrupted()) { + return core.exitFailCause(self.getInterruptedCause()) + } else { + return core.exitSucceed(value) + } + }, + [OpCodes.OP_WHILE]: ( + self: FiberRuntime, + cont: core.While, + value: unknown + ) => { + internalCall(() => cont.effect_instruction_i2(value)) + if (internalCall(() => cont.effect_instruction_i0())) { + self.pushStack(cont) + return internalCall(() => cont.effect_instruction_i1()) + } else { + return core.void + } + }, + [OpCodes.OP_ITERATOR]: ( + self: FiberRuntime, + cont: core.FromIterator, + value: unknown + ) => { + while (true) { + const state = internalCall(() => cont.effect_instruction_i0.next(value)) + if (state.done) { + return core.exitSucceed(state.value) + } + const primitive = yieldWrapGet(state.value) + if (!core.exitIsExit(primitive)) { + self.pushStack(cont) + return primitive + } else if (primitive._tag === "Failure") { + return primitive + } + value = primitive.value + } + } +} + +const drainQueueWhileRunningTable = { + [FiberMessage.OP_INTERRUPT_SIGNAL]: ( + self: FiberRuntime, + runtimeFlags: RuntimeFlags.RuntimeFlags, + cur: Effect.Effect, + message: FiberMessage.FiberMessage & { _tag: FiberMessage.OP_INTERRUPT_SIGNAL } + ) => { + self.processNewInterruptSignal(message.cause) + return runtimeFlags_.interruptible(runtimeFlags) ? core.exitFailCause(message.cause) : cur + }, + [FiberMessage.OP_RESUME]: ( + _self: FiberRuntime, + _runtimeFlags: RuntimeFlags.RuntimeFlags, + _cur: Effect.Effect, + _message: FiberMessage.FiberMessage + ) => { + throw new Error("It is illegal to have multiple concurrent run loops in a single fiber") + }, + [FiberMessage.OP_STATEFUL]: ( + self: FiberRuntime, + runtimeFlags: RuntimeFlags.RuntimeFlags, + cur: Effect.Effect, + message: FiberMessage.FiberMessage & { _tag: FiberMessage.OP_STATEFUL } + ) => { + message.onFiber(self, FiberStatus.running(runtimeFlags)) + return cur + }, + [FiberMessage.OP_YIELD_NOW]: ( + _self: FiberRuntime, + _runtimeFlags: RuntimeFlags.RuntimeFlags, + cur: Effect.Effect, + _message: FiberMessage.FiberMessage & { _tag: FiberMessage.OP_YIELD_NOW } + ) => { + return core.flatMap(core.yieldNow(), () => cur) + } +} + +/** + * Executes all requests, submitting requests to each data source in parallel. + */ +const runBlockedRequests = (self: RequestBlock.RequestBlock) => + core.forEachSequentialDiscard( + RequestBlock_.flatten(self), + (requestsByRequestResolver) => + forEachConcurrentDiscard( + RequestBlock_.sequentialCollectionToChunk(requestsByRequestResolver), + ([dataSource, sequential]) => { + const map = new Map, Entry>() + const arr: Array>> = [] + for (const block of sequential) { + arr.push(Chunk.toReadonlyArray(block) as any) + for (const entry of block) { + map.set(entry.request as Request, entry) + } + } + const flat = arr.flat() + return core.fiberRefLocally( + invokeWithInterrupt(dataSource.runAll(arr), flat, () => + flat.forEach((entry) => { + entry.listeners.interrupted = true + })), + currentRequestMap, + map + ) + }, + false, + false + ) + ) + +/** @internal */ +export interface Snapshot { + refs: FiberRefs.FiberRefs + flags: RuntimeFlags.RuntimeFlags +} + +const _version = version.getCurrentVersion() + +/** @internal */ +export class FiberRuntime extends Effectable.Class + implements Fiber.RuntimeFiber +{ + readonly [internalFiber.FiberTypeId] = internalFiber.fiberVariance + readonly [internalFiber.RuntimeFiberTypeId] = runtimeFiberVariance + private _fiberRefs: FiberRefs.FiberRefs + private _fiberId: FiberId.Runtime + private _queue = new Array() + private _children: Set> | null = null + private _observers = new Array<(exit: Exit.Exit) => void>() + private _running = false + private _stack: Array = [] + private _asyncInterruptor: ((effect: Effect.Effect) => any) | null = null + private _asyncBlockingOn: FiberId.FiberId | null = null + private _exitValue: Exit.Exit | null = null + private _steps: Array = [] + private _isYielding = false + + public currentRuntimeFlags: RuntimeFlags.RuntimeFlags + public currentOpCount: number = 0 + public currentSupervisor!: Supervisor.Supervisor + public currentScheduler!: Scheduler + public currentTracer!: Tracer.Tracer + public currentSpan!: Tracer.AnySpan | undefined + public currentContext!: Context.Context + public currentDefaultServices!: Context.Context + + constructor( + fiberId: FiberId.Runtime, + fiberRefs0: FiberRefs.FiberRefs, + runtimeFlags0: RuntimeFlags.RuntimeFlags + ) { + super() + this.currentRuntimeFlags = runtimeFlags0 + this._fiberId = fiberId + this._fiberRefs = fiberRefs0 + if (runtimeFlags_.runtimeMetrics(runtimeFlags0)) { + const tags = this.getFiberRef(core.currentMetricLabels) + fiberStarted.unsafeUpdate(1, tags) + fiberActive.unsafeUpdate(1, tags) + } + this.refreshRefCache() + } + + commit(): Effect.Effect { + return internalFiber.join(this) + } + + /** + * The identity of the fiber. + */ + id(): FiberId.Runtime { + return this._fiberId + } + + /** + * Begins execution of the effect associated with this fiber on in the + * background. This can be called to "kick off" execution of a fiber after + * it has been created. + */ + resume(effect: Effect.Effect): void { + this.tell(FiberMessage.resume(effect)) + } + + /** + * The status of the fiber. + */ + get status(): Effect.Effect { + return this.ask((_, status) => status) + } + + /** + * Gets the fiber runtime flags. + */ + get runtimeFlags(): Effect.Effect { + return this.ask((state, status) => { + if (FiberStatus.isDone(status)) { + return state.currentRuntimeFlags + } + return status.runtimeFlags + }) + } + + /** + * Returns the current `FiberScope` for the fiber. + */ + scope(): fiberScope.FiberScope { + return fiberScope.unsafeMake(this) + } + + /** + * Retrieves the immediate children of the fiber. + */ + get children(): Effect.Effect>> { + return this.ask((fiber) => Array.from(fiber.getChildren())) + } + + /** + * Gets the fiber's set of children. + */ + getChildren(): Set> { + if (this._children === null) { + this._children = new Set() + } + return this._children + } + + /** + * Retrieves the interrupted cause of the fiber, which will be `Cause.empty` + * if the fiber has not been interrupted. + * + * **NOTE**: This method is safe to invoke on any fiber, but if not invoked + * on this fiber, then values derived from the fiber's state (including the + * log annotations and log level) may not be up-to-date. + */ + getInterruptedCause() { + return this.getFiberRef(core.currentInterruptedCause) + } + + /** + * Retrieves the whole set of fiber refs. + */ + fiberRefs(): Effect.Effect { + return this.ask((fiber) => fiber.getFiberRefs()) + } + + /** + * Returns an effect that will contain information computed from the fiber + * state and status while running on the fiber. + * + * This allows the outside world to interact safely with mutable fiber state + * without locks or immutable data. + */ + ask( + f: (runtime: FiberRuntime, status: FiberStatus.FiberStatus) => Z + ): Effect.Effect { + return core.suspend(() => { + const deferred = core.deferredUnsafeMake(this._fiberId) + this.tell( + FiberMessage.stateful((fiber, status) => { + core.deferredUnsafeDone(deferred, core.sync(() => f(fiber, status))) + }) + ) + return core.deferredAwait(deferred) + }) + } + + /** + * Adds a message to be processed by the fiber on the fiber. + */ + tell(message: FiberMessage.FiberMessage): void { + this._queue.push(message) + if (!this._running) { + this._running = true + this.drainQueueLaterOnExecutor() + } + } + + get await(): Effect.Effect> { + return core.async((resume) => { + const cb = (exit: Exit.Exit) => resume(core.succeed(exit)) + if (this._exitValue !== null) { + cb(this._exitValue!) + return + } + this.tell( + FiberMessage.stateful((fiber, _) => { + if (fiber._exitValue !== null) { + cb(this._exitValue!) + } else { + fiber.addObserver(cb) + } + }) + ) + return core.sync(() => + this.tell( + FiberMessage.stateful((fiber, _) => { + fiber.removeObserver(cb) + }) + ) + ) + }, this.id()) + } + + get inheritAll(): Effect.Effect { + return core.withFiberRuntime((parentFiber, parentStatus) => { + const parentFiberId = parentFiber.id() + const parentFiberRefs = parentFiber.getFiberRefs() + const parentRuntimeFlags = parentStatus.runtimeFlags + const childFiberRefs = this.getFiberRefs() + const updatedFiberRefs = fiberRefs.joinAs(parentFiberRefs, parentFiberId, childFiberRefs) + + parentFiber.setFiberRefs(updatedFiberRefs) + + const updatedRuntimeFlags = parentFiber.getFiberRef(currentRuntimeFlags) + + const patch = pipe( + runtimeFlags_.diff(parentRuntimeFlags, updatedRuntimeFlags), + // Do not inherit WindDown or Interruption! + RuntimeFlagsPatch.exclude(runtimeFlags_.Interruption), + RuntimeFlagsPatch.exclude(runtimeFlags_.WindDown) + ) + + return core.updateRuntimeFlags(patch) + }) + } + + /** + * Tentatively observes the fiber, but returns immediately if it is not + * already done. + */ + get poll(): Effect.Effect>> { + return core.sync(() => Option.fromNullable(this._exitValue)) + } + + /** + * Unsafely observes the fiber, but returns immediately if it is not + * already done. + */ + unsafePoll(): Exit.Exit | null { + return this._exitValue + } + + /** + * In the background, interrupts the fiber as if interrupted from the specified fiber. + */ + interruptAsFork(fiberId: FiberId.FiberId): Effect.Effect { + return core.sync(() => this.tell(FiberMessage.interruptSignal(internalCause.interrupt(fiberId)))) + } + + /** + * In the background, interrupts the fiber as if interrupted from the specified fiber. + */ + unsafeInterruptAsFork(fiberId: FiberId.FiberId) { + this.tell(FiberMessage.interruptSignal(internalCause.interrupt(fiberId))) + } + + /** + * Adds an observer to the list of observers. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + addObserver(observer: (exit: Exit.Exit) => void): void { + if (this._exitValue !== null) { + observer(this._exitValue!) + } else { + this._observers.push(observer) + } + } + + /** + * Removes the specified observer from the list of observers that will be + * notified when the fiber exits. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + removeObserver(observer: (exit: Exit.Exit) => void): void { + this._observers = this._observers.filter((o) => o !== observer) + } + /** + * Retrieves all fiber refs of the fiber. + * + * **NOTE**: This method is safe to invoke on any fiber, but if not invoked + * on this fiber, then values derived from the fiber's state (including the + * log annotations and log level) may not be up-to-date. + */ + getFiberRefs(): FiberRefs.FiberRefs { + this.setFiberRef(currentRuntimeFlags, this.currentRuntimeFlags) + return this._fiberRefs + } + + /** + * Deletes the specified fiber ref. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + unsafeDeleteFiberRef(fiberRef: FiberRef.FiberRef): void { + this._fiberRefs = fiberRefs.delete_(this._fiberRefs, fiberRef) + } + + /** + * Retrieves the state of the fiber ref, or else its initial value. + * + * **NOTE**: This method is safe to invoke on any fiber, but if not invoked + * on this fiber, then values derived from the fiber's state (including the + * log annotations and log level) may not be up-to-date. + */ + getFiberRef(fiberRef: FiberRef.FiberRef): X { + if (this._fiberRefs.locals.has(fiberRef)) { + return this._fiberRefs.locals.get(fiberRef)![0][1] as X + } + return fiberRef.initial + } + + /** + * Sets the fiber ref to the specified value. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + setFiberRef(fiberRef: FiberRef.FiberRef, value: X): void { + this._fiberRefs = fiberRefs.updateAs(this._fiberRefs, { + fiberId: this._fiberId, + fiberRef, + value + }) + this.refreshRefCache() + } + + refreshRefCache() { + this.currentDefaultServices = this.getFiberRef(defaultServices.currentServices) + this.currentTracer = this.currentDefaultServices.unsafeMap.get(tracer.tracerTag.key) + this.currentSupervisor = this.getFiberRef(currentSupervisor) + this.currentScheduler = this.getFiberRef(currentScheduler) + this.currentContext = this.getFiberRef(core.currentContext) + this.currentSpan = this.currentContext.unsafeMap.get(tracer.spanTag.key) + } + + /** + * Wholesale replaces all fiber refs of this fiber. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + setFiberRefs(fiberRefs: FiberRefs.FiberRefs): void { + this._fiberRefs = fiberRefs + this.refreshRefCache() + } + + /** + * Adds a reference to the specified fiber inside the children set. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + addChild(child: FiberRuntime) { + this.getChildren().add(child) + } + + /** + * Removes a reference to the specified fiber inside the children set. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + removeChild(child: FiberRuntime) { + this.getChildren().delete(child) + } + + /** + * Transfers all children of this fiber that are currently running to the + * specified fiber scope. + * + * **NOTE**: This method must be invoked by the fiber itself after it has + * evaluated the effects but prior to exiting. + */ + transferChildren(scope: fiberScope.FiberScope) { + const children = this._children + // Clear the children of the current fiber + this._children = null + if (children !== null && children.size > 0) { + for (const child of children) { + // If the child is still running, add it to the scope + if (child._exitValue === null) { + scope.add(this.currentRuntimeFlags, child) + } + } + } + } + + /** + * On the current thread, executes all messages in the fiber's inbox. This + * method may return before all work is done, in the event the fiber executes + * an asynchronous operation. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + drainQueueOnCurrentThread() { + let recurse = true + while (recurse) { + let evaluationSignal: EvaluationSignal = EvaluationSignalContinue + const prev = (globalThis as any)[internalFiber.currentFiberURI] + ;(globalThis as any)[internalFiber.currentFiberURI] = this + try { + while (evaluationSignal === EvaluationSignalContinue) { + evaluationSignal = this._queue.length === 0 ? + EvaluationSignalDone : + this.evaluateMessageWhileSuspended(this._queue.splice(0, 1)[0]!) + } + } finally { + this._running = false + ;(globalThis as any)[internalFiber.currentFiberURI] = prev + } + // Maybe someone added something to the queue between us checking, and us + // giving up the drain. If so, we need to restart the draining, but only + // if we beat everyone else to the restart: + if (this._queue.length > 0 && !this._running) { + this._running = true + if (evaluationSignal === EvaluationSignalYieldNow) { + this.drainQueueLaterOnExecutor() + recurse = false + } else { + recurse = true + } + } else { + recurse = false + } + } + } + + /** + * Schedules the execution of all messages in the fiber's inbox. + * + * This method will return immediately after the scheduling + * operation is completed, but potentially before such messages have been + * executed. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + drainQueueLaterOnExecutor() { + this.currentScheduler.scheduleTask( + this.run, + this.getFiberRef(core.currentSchedulingPriority), + this + ) + } + + /** + * Drains the fiber's message queue while the fiber is actively running, + * returning the next effect to execute, which may be the input effect if no + * additional effect needs to be executed. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + drainQueueWhileRunning( + runtimeFlags: RuntimeFlags.RuntimeFlags, + cur0: Effect.Effect + ) { + let cur = cur0 + while (this._queue.length > 0) { + const message = this._queue.splice(0, 1)[0] + // @ts-expect-error + cur = drainQueueWhileRunningTable[message._tag](this, runtimeFlags, cur, message) + } + return cur + } + + /** + * Determines if the fiber is interrupted. + * + * **NOTE**: This method is safe to invoke on any fiber, but if not invoked + * on this fiber, then values derived from the fiber's state (including the + * log annotations and log level) may not be up-to-date. + */ + isInterrupted(): boolean { + return !internalCause.isEmpty(this.getFiberRef(core.currentInterruptedCause)) + } + + /** + * Adds an interruptor to the set of interruptors that are interrupting this + * fiber. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + addInterruptedCause(cause: Cause.Cause) { + const oldSC = this.getFiberRef(core.currentInterruptedCause) + this.setFiberRef(core.currentInterruptedCause, internalCause.sequential(oldSC, cause)) + } + + /** + * Processes a new incoming interrupt signal. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + processNewInterruptSignal(cause: Cause.Cause): void { + this.addInterruptedCause(cause) + this.sendInterruptSignalToAllChildren() + } + + /** + * Interrupts all children of the current fiber, returning an effect that will + * await the exit of the children. This method will return null if the fiber + * has no children. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + sendInterruptSignalToAllChildren(): boolean { + if (this._children === null || this._children.size === 0) { + return false + } + let told = false + for (const child of this._children) { + child.tell(FiberMessage.interruptSignal(internalCause.interrupt(this.id()))) + told = true + } + return told + } + + /** + * Interrupts all children of the current fiber, returning an effect that will + * await the exit of the children. This method will return null if the fiber + * has no children. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + interruptAllChildren() { + if (this.sendInterruptSignalToAllChildren()) { + const it = this._children!.values() + this._children = null + let isDone = false + const body = () => { + const next = it.next() + if (!next.done) { + return core.asVoid(next.value.await) + } else { + return core.sync(() => { + isDone = true + }) + } + } + return core.whileLoop({ + while: () => !isDone, + body, + step: () => { + // + } + }) + } + return null + } + + reportExitValue(exit: Exit.Exit) { + if (runtimeFlags_.runtimeMetrics(this.currentRuntimeFlags)) { + const tags = this.getFiberRef(core.currentMetricLabels) + const startTimeMillis = this.id().startTimeMillis + const endTimeMillis = Date.now() + fiberLifetimes.unsafeUpdate(endTimeMillis - startTimeMillis, tags) + fiberActive.unsafeUpdate(-1, tags) + switch (exit._tag) { + case OpCodes.OP_SUCCESS: { + fiberSuccesses.unsafeUpdate(1, tags) + break + } + case OpCodes.OP_FAILURE: { + fiberFailures.unsafeUpdate(1, tags) + break + } + } + } + if (exit._tag === "Failure") { + const level = this.getFiberRef(core.currentUnhandledErrorLogLevel) + if (!internalCause.isInterruptedOnly(exit.cause) && level._tag === "Some") { + this.log("Fiber terminated with an unhandled error", exit.cause, level) + } + } + } + + setExitValue(exit: Exit.Exit) { + this._exitValue = exit + this.reportExitValue(exit) + for (let i = this._observers.length - 1; i >= 0; i--) { + this._observers[i](exit) + } + this._observers = [] + } + + getLoggers() { + return this.getFiberRef(currentLoggers) + } + + log( + message: unknown, + cause: Cause.Cause, + overrideLogLevel: Option.Option + ): void { + const logLevel = Option.isSome(overrideLogLevel) ? + overrideLogLevel.value : + this.getFiberRef(core.currentLogLevel) + const minimumLogLevel = this.getFiberRef(currentMinimumLogLevel) + if (LogLevel.greaterThan(minimumLogLevel, logLevel)) { + return + } + const spans = this.getFiberRef(core.currentLogSpan) + const annotations = this.getFiberRef(core.currentLogAnnotations) + const loggers = this.getLoggers() + const contextMap = this.getFiberRefs() + if (HashSet.size(loggers) > 0) { + const clockService = Context.get(this.getFiberRef(defaultServices.currentServices), clock.clockTag) + const date = new Date(clockService.unsafeCurrentTimeMillis()) + Inspectable.withRedactableContext(contextMap, () => { + for (const logger of loggers) { + logger.log({ + fiberId: this.id(), + logLevel, + message, + cause, + context: contextMap, + spans, + annotations, + date + }) + } + }) + } + } + + /** + * Evaluates a single message on the current thread, while the fiber is + * suspended. This method should only be called while evaluation of the + * fiber's effect is suspended due to an asynchronous operation. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + evaluateMessageWhileSuspended(message: FiberMessage.FiberMessage): EvaluationSignal { + switch (message._tag) { + case FiberMessage.OP_YIELD_NOW: { + return EvaluationSignalYieldNow + } + case FiberMessage.OP_INTERRUPT_SIGNAL: { + this.processNewInterruptSignal(message.cause) + if (this._asyncInterruptor !== null) { + this._asyncInterruptor(core.exitFailCause(message.cause)) + this._asyncInterruptor = null + } + return EvaluationSignalContinue + } + case FiberMessage.OP_RESUME: { + this._asyncInterruptor = null + this._asyncBlockingOn = null + this.evaluateEffect(message.effect) + return EvaluationSignalContinue + } + case FiberMessage.OP_STATEFUL: { + message.onFiber( + this, + this._exitValue !== null ? + FiberStatus.done : + FiberStatus.suspended(this.currentRuntimeFlags, this._asyncBlockingOn!) + ) + return EvaluationSignalContinue + } + default: { + return absurd(message) + } + } + } + + /** + * Evaluates an effect until completion, potentially asynchronously. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + evaluateEffect(effect0: Effect.Effect) { + this.currentSupervisor.onResume(this) + try { + let effect: Effect.Effect | null = + runtimeFlags_.interruptible(this.currentRuntimeFlags) && this.isInterrupted() ? + core.exitFailCause(this.getInterruptedCause()) : + effect0 + while (effect !== null) { + const eff: Effect.Effect = effect + const exit = this.runLoop(eff) + if (exit === YieldedOp) { + const op = yieldedOpChannel.currentOp! + yieldedOpChannel.currentOp = null + if (op._op === OpCodes.OP_YIELD) { + if (runtimeFlags_.cooperativeYielding(this.currentRuntimeFlags)) { + this.tell(FiberMessage.yieldNow()) + this.tell(FiberMessage.resume(core.exitVoid)) + effect = null + } else { + effect = core.exitVoid + } + } else if (op._op === OpCodes.OP_ASYNC) { + // Terminate this evaluation, async resumption will continue evaluation: + effect = null + } + } else { + this.currentRuntimeFlags = pipe(this.currentRuntimeFlags, runtimeFlags_.enable(runtimeFlags_.WindDown)) + const interruption = this.interruptAllChildren() + if (interruption !== null) { + effect = core.flatMap(interruption, () => exit) + } else { + if (this._queue.length === 0) { + // No more messages to process, so we will allow the fiber to end life: + this.setExitValue(exit) + } else { + // There are messages, possibly added by the final op executed by + // the fiber. To be safe, we should execute those now before we + // allow the fiber to end life: + this.tell(FiberMessage.resume(exit)) + } + effect = null + } + } + } + } finally { + this.currentSupervisor.onSuspend(this) + } + } + + /** + * Begins execution of the effect associated with this fiber on the current + * thread. This can be called to "kick off" execution of a fiber after it has + * been created, in hopes that the effect can be executed synchronously. + * + * This is not the normal way of starting a fiber, but it is useful when the + * express goal of executing the fiber is to synchronously produce its exit. + */ + start(effect: Effect.Effect): void { + if (!this._running) { + this._running = true + const prev = (globalThis as any)[internalFiber.currentFiberURI] + ;(globalThis as any)[internalFiber.currentFiberURI] = this + try { + this.evaluateEffect(effect) + } finally { + this._running = false + ;(globalThis as any)[internalFiber.currentFiberURI] = prev + // Because we're special casing `start`, we have to be responsible + // for spinning up the fiber if there were new messages added to + // the queue between the completion of the effect and the transition + // to the not running state. + if (this._queue.length > 0) { + this.drainQueueLaterOnExecutor() + } + } + } else { + this.tell(FiberMessage.resume(effect)) + } + } + + /** + * Begins execution of the effect associated with this fiber on in the + * background, and on the correct thread pool. This can be called to "kick + * off" execution of a fiber after it has been created, in hopes that the + * effect can be executed synchronously. + */ + startFork(effect: Effect.Effect): void { + this.tell(FiberMessage.resume(effect)) + } + + /** + * Takes the current runtime flags, patches them to return the new runtime + * flags, and then makes any changes necessary to fiber state based on the + * specified patch. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + patchRuntimeFlags(oldRuntimeFlags: RuntimeFlags.RuntimeFlags, patch: RuntimeFlagsPatch.RuntimeFlagsPatch) { + const newRuntimeFlags = runtimeFlags_.patch(oldRuntimeFlags, patch) + ;(globalThis as any)[internalFiber.currentFiberURI] = this + this.currentRuntimeFlags = newRuntimeFlags + return newRuntimeFlags + } + + /** + * Initiates an asynchronous operation, by building a callback that will + * resume execution, and then feeding that callback to the registration + * function, handling error cases and repeated resumptions appropriately. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + initiateAsync( + runtimeFlags: RuntimeFlags.RuntimeFlags, + asyncRegister: (resume: (effect: Effect.Effect) => void) => void + ) { + let alreadyCalled = false + const callback = (effect: Effect.Effect) => { + if (!alreadyCalled) { + alreadyCalled = true + this.tell(FiberMessage.resume(effect)) + } + } + if (runtimeFlags_.interruptible(runtimeFlags)) { + this._asyncInterruptor = callback + } + try { + asyncRegister(callback) + } catch (e) { + callback(core.failCause(internalCause.die(e))) + } + } + + pushStack(cont: core.Continuation) { + this._stack.push(cont) + if (cont._op === "OnStep") { + this._steps.push({ refs: this.getFiberRefs(), flags: this.currentRuntimeFlags }) + } + } + + popStack() { + const item = this._stack.pop() + if (item) { + if (item._op === "OnStep") { + this._steps.pop() + } + return item + } + return + } + + getNextSuccessCont() { + let frame = this.popStack() + while (frame) { + if (frame._op !== OpCodes.OP_ON_FAILURE) { + return frame + } + frame = this.popStack() + } + } + + getNextFailCont() { + let frame = this.popStack() + while (frame) { + if (frame._op !== OpCodes.OP_ON_SUCCESS && frame._op !== OpCodes.OP_WHILE && frame._op !== OpCodes.OP_ITERATOR) { + return frame + } + frame = this.popStack() + } + } + + [OpCodes.OP_TAG](op: core.Primitive & { _op: OpCodes.OP_SYNC }) { + return core.sync(() => Context.unsafeGet(this.currentContext, op as unknown as Context.Tag)) + } + + ["Left"](op: core.Primitive & { _op: "Left" }) { + return core.fail(op.left) + } + + ["None"](_: core.Primitive & { _op: "None" }) { + return core.fail(new core.NoSuchElementException()) + } + + ["Right"](op: core.Primitive & { _op: "Right" }) { + return core.exitSucceed(op.right) + } + + ["Some"](op: core.Primitive & { _op: "Some" }) { + return core.exitSucceed(op.value) + } + + ["Micro"](op: Micro.Micro & { _op: "Micro" }) { + return core.unsafeAsync((microResume) => { + let resume = microResume + const fiber = Micro.runFork(Micro.provideContext(op, this.currentContext)) + fiber.addObserver((exit) => { + if (exit._tag === "Success") { + return resume(core.exitSucceed(exit.value)) + } + switch (exit.cause._tag) { + case "Interrupt": { + return resume(core.exitFailCause(internalCause.interrupt(FiberId.none))) + } + case "Fail": { + return resume(core.fail(exit.cause.error)) + } + case "Die": { + return resume(core.die(exit.cause.defect)) + } + } + }) + return core.unsafeAsync((abortResume) => { + resume = (_: any) => { + abortResume(core.void) + } + fiber.unsafeInterrupt() + }) + }) + } + + [OpCodes.OP_SYNC](op: core.Primitive & { _op: OpCodes.OP_SYNC }) { + const value = internalCall(() => op.effect_instruction_i0()) + const cont = this.getNextSuccessCont() + if (cont !== undefined) { + if (!(cont._op in contOpSuccess)) { + // @ts-expect-error + absurd(cont) + } + // @ts-expect-error + return contOpSuccess[cont._op](this, cont, value) + } else { + yieldedOpChannel.currentOp = core.exitSucceed(value) as any + return YieldedOp + } + } + + [OpCodes.OP_SUCCESS](op: core.Primitive & { _op: OpCodes.OP_SUCCESS }) { + const oldCur = op + const cont = this.getNextSuccessCont() + if (cont !== undefined) { + if (!(cont._op in contOpSuccess)) { + // @ts-expect-error + absurd(cont) + } + // @ts-expect-error + return contOpSuccess[cont._op](this, cont, oldCur.effect_instruction_i0) + } else { + yieldedOpChannel.currentOp = oldCur + return YieldedOp + } + } + + [OpCodes.OP_FAILURE](op: core.Primitive & { _op: OpCodes.OP_FAILURE }) { + const cause = op.effect_instruction_i0 + const cont = this.getNextFailCont() + if (cont !== undefined) { + switch (cont._op) { + case OpCodes.OP_ON_FAILURE: + case OpCodes.OP_ON_SUCCESS_AND_FAILURE: { + if (!(runtimeFlags_.interruptible(this.currentRuntimeFlags) && this.isInterrupted())) { + return internalCall(() => cont.effect_instruction_i1(cause)) + } else { + return core.exitFailCause(internalCause.stripFailures(cause)) + } + } + case "OnStep": { + if (!(runtimeFlags_.interruptible(this.currentRuntimeFlags) && this.isInterrupted())) { + return core.exitSucceed(core.exitFailCause(cause)) + } else { + return core.exitFailCause(internalCause.stripFailures(cause)) + } + } + case OpCodes.OP_REVERT_FLAGS: { + this.patchRuntimeFlags(this.currentRuntimeFlags, cont.patch) + if (runtimeFlags_.interruptible(this.currentRuntimeFlags) && this.isInterrupted()) { + return core.exitFailCause(internalCause.sequential(cause, this.getInterruptedCause())) + } else { + return core.exitFailCause(cause) + } + } + default: { + absurd(cont) + } + } + } else { + yieldedOpChannel.currentOp = core.exitFailCause(cause) as any + return YieldedOp + } + } + + [OpCodes.OP_WITH_RUNTIME](op: core.Primitive & { _op: OpCodes.OP_WITH_RUNTIME }) { + return internalCall(() => + op.effect_instruction_i0( + this as FiberRuntime, + FiberStatus.running(this.currentRuntimeFlags) as FiberStatus.Running + ) + ) + } + + ["Blocked"](op: core.Primitive & { _op: "Blocked" }) { + const refs = this.getFiberRefs() + const flags = this.currentRuntimeFlags + if (this._steps.length > 0) { + const frames: Array = [] + const snap = this._steps[this._steps.length - 1] + let frame = this.popStack() + while (frame && frame._op !== "OnStep") { + frames.push(frame) + frame = this.popStack() + } + this.setFiberRefs(snap.refs) + this.currentRuntimeFlags = snap.flags + const patchRefs = FiberRefsPatch.diff(snap.refs, refs) + const patchFlags = runtimeFlags_.diff(snap.flags, flags) + return core.exitSucceed(core.blocked( + op.effect_instruction_i0, + core.withFiberRuntime((newFiber) => { + while (frames.length > 0) { + newFiber.pushStack(frames.pop()!) + } + newFiber.setFiberRefs( + FiberRefsPatch.patch(newFiber.id(), newFiber.getFiberRefs())(patchRefs) + ) + newFiber.currentRuntimeFlags = runtimeFlags_.patch(patchFlags)(newFiber.currentRuntimeFlags) + return op.effect_instruction_i1 + }) + )) + } + return core.uninterruptibleMask((restore) => + core.flatMap( + forkDaemon(core.runRequestBlock(op.effect_instruction_i0)), + () => restore(op.effect_instruction_i1) + ) + ) + } + + ["RunBlocked"](op: core.Primitive & { _op: "RunBlocked" }) { + return runBlockedRequests(op.effect_instruction_i0) + } + + [OpCodes.OP_UPDATE_RUNTIME_FLAGS](op: core.Primitive & { _op: OpCodes.OP_UPDATE_RUNTIME_FLAGS }) { + const updateFlags = op.effect_instruction_i0 + const oldRuntimeFlags = this.currentRuntimeFlags + const newRuntimeFlags = runtimeFlags_.patch(oldRuntimeFlags, updateFlags) + // One more chance to short circuit: if we're immediately going + // to interrupt. Interruption will cause immediate reversion of + // the flag, so as long as we "peek ahead", there's no need to + // set them to begin with. + if (runtimeFlags_.interruptible(newRuntimeFlags) && this.isInterrupted()) { + return core.exitFailCause(this.getInterruptedCause()) + } else { + // Impossible to short circuit, so record the changes + this.patchRuntimeFlags(this.currentRuntimeFlags, updateFlags) + if (op.effect_instruction_i1) { + // Since we updated the flags, we need to revert them + const revertFlags = runtimeFlags_.diff(newRuntimeFlags, oldRuntimeFlags) + this.pushStack(new core.RevertFlags(revertFlags, op)) + return internalCall(() => op.effect_instruction_i1!(oldRuntimeFlags)) + } else { + return core.exitVoid + } + } + } + + [OpCodes.OP_ON_SUCCESS](op: core.Primitive & { _op: OpCodes.OP_ON_SUCCESS }) { + this.pushStack(op) + return op.effect_instruction_i0 + } + + ["OnStep"](op: core.Primitive & { _op: "OnStep" }) { + this.pushStack(op) + return op.effect_instruction_i0 + } + + [OpCodes.OP_ON_FAILURE](op: core.Primitive & { _op: OpCodes.OP_ON_FAILURE }) { + this.pushStack(op) + return op.effect_instruction_i0 + } + + [OpCodes.OP_ON_SUCCESS_AND_FAILURE](op: core.Primitive & { _op: OpCodes.OP_ON_SUCCESS_AND_FAILURE }) { + this.pushStack(op) + return op.effect_instruction_i0 + } + + [OpCodes.OP_ASYNC](op: core.Primitive & { _op: OpCodes.OP_ASYNC }) { + this._asyncBlockingOn = op.effect_instruction_i1 + this.initiateAsync(this.currentRuntimeFlags, op.effect_instruction_i0) + yieldedOpChannel.currentOp = op + return YieldedOp + } + + [OpCodes.OP_YIELD](op: core.Primitive & { op: OpCodes.OP_YIELD }) { + this._isYielding = false + yieldedOpChannel.currentOp = op + return YieldedOp + } + + [OpCodes.OP_WHILE](op: core.Primitive & { _op: OpCodes.OP_WHILE }) { + const check = op.effect_instruction_i0 + const body = op.effect_instruction_i1 + if (check()) { + this.pushStack(op) + return body() + } else { + return core.exitVoid + } + } + + [OpCodes.OP_ITERATOR](op: core.Primitive & { _op: OpCodes.OP_ITERATOR }) { + return contOpSuccess[OpCodes.OP_ITERATOR](this, op, undefined) + } + + [OpCodes.OP_COMMIT](op: core.Primitive & { _op: OpCodes.OP_COMMIT }) { + return internalCall(() => op.commit()) + } + + /** + * The main run-loop for evaluating effects. + * + * **NOTE**: This method must be invoked by the fiber itself. + */ + runLoop(effect0: Effect.Effect): Exit.Exit | YieldedOp { + let cur: Effect.Effect | YieldedOp = effect0 + this.currentOpCount = 0 + + while (true) { + if ((this.currentRuntimeFlags & OpSupervision) !== 0) { + this.currentSupervisor.onEffect(this, cur) + } + if (this._queue.length > 0) { + cur = this.drainQueueWhileRunning(this.currentRuntimeFlags, cur) + } + if (!this._isYielding) { + this.currentOpCount += 1 + const shouldYield = this.currentScheduler.shouldYield(this) + if (shouldYield !== false) { + this._isYielding = true + this.currentOpCount = 0 + const oldCur = cur + cur = core.flatMap(core.yieldNow({ priority: shouldYield }), () => oldCur) + } + } + try { + // @ts-expect-error + cur = this.currentTracer.context( + () => { + if (_version !== (cur as core.Primitive)[core.EffectTypeId]._V) { + const level = this.getFiberRef(core.currentVersionMismatchErrorLogLevel) + if (level._tag === "Some") { + const effectVersion = (cur as core.Primitive)[core.EffectTypeId]._V + this.log( + `Executing an Effect versioned ${effectVersion} with a Runtime of version ${version.getCurrentVersion()}, you may want to dedupe the effect dependencies, you can use the language service plugin to detect this at compile time: https://github.com/Effect-TS/language-service`, + internalCause.empty, + level + ) + } + } + // @ts-expect-error + return this[(cur as core.Primitive)._op](cur as core.Primitive) + }, + this + ) + + if (cur === YieldedOp) { + const op = yieldedOpChannel.currentOp! + if ( + op._op === OpCodes.OP_YIELD || + op._op === OpCodes.OP_ASYNC + ) { + return YieldedOp + } + + yieldedOpChannel.currentOp = null + return ( + op._op === OpCodes.OP_SUCCESS || + op._op === OpCodes.OP_FAILURE + ) ? + op as unknown as Exit.Exit : + core.exitFailCause(internalCause.die(op)) + } + } catch (e) { + if (cur !== YieldedOp && !Predicate.hasProperty(cur, "_op") || !((cur as core.Primitive)._op in this)) { + cur = core.dieMessage(`Not a valid effect: ${Inspectable.toStringUnknown(cur)}`) + } else if (core.isInterruptedException(e)) { + cur = core.exitFailCause( + internalCause.sequential(internalCause.die(e), internalCause.interrupt(FiberId.none)) + ) + } else { + cur = core.die(e) + } + } + } + } + + run = () => { + this.drainQueueOnCurrentThread() + } +} + +// circular with Logger + +/** @internal */ +export const currentMinimumLogLevel: FiberRef.FiberRef = globalValue( + "effect/FiberRef/currentMinimumLogLevel", + () => core.fiberRefUnsafeMake(LogLevel.fromLiteral("Info")) +) + +/** @internal */ +export const loggerWithConsoleLog = (self: Logger): Logger => + internalLogger.makeLogger((opts) => { + const services = FiberRefs.getOrDefault(opts.context, defaultServices.currentServices) + Context.get(services, consoleTag).unsafe.log(self.log(opts)) + }) + +/** @internal */ +export const loggerWithLeveledLog = (self: Logger): Logger => + internalLogger.makeLogger((opts) => { + const services = FiberRefs.getOrDefault(opts.context, defaultServices.currentServices) + const unsafeLogger = Context.get(services, consoleTag).unsafe + switch (opts.logLevel._tag) { + case "Debug": + return unsafeLogger.debug(self.log(opts)) + case "Info": + return unsafeLogger.info(self.log(opts)) + case "Trace": + return unsafeLogger.trace(self.log(opts)) + case "Warning": + return unsafeLogger.warn(self.log(opts)) + case "Error": + case "Fatal": + return unsafeLogger.error(self.log(opts)) + default: + return unsafeLogger.log(self.log(opts)) + } + }) + +/** @internal */ +export const loggerWithConsoleError = (self: Logger): Logger => + internalLogger.makeLogger((opts) => { + const services = FiberRefs.getOrDefault(opts.context, defaultServices.currentServices) + Context.get(services, consoleTag).unsafe.error(self.log(opts)) + }) + +/** @internal */ +export const defaultLogger: Logger = globalValue( + Symbol.for("effect/Logger/defaultLogger"), + () => loggerWithConsoleLog(internalLogger.stringLogger) +) + +/** @internal */ +export const jsonLogger: Logger = globalValue( + Symbol.for("effect/Logger/jsonLogger"), + () => loggerWithConsoleLog(internalLogger.jsonLogger) +) + +/** @internal */ +export const logFmtLogger: Logger = globalValue( + Symbol.for("effect/Logger/logFmtLogger"), + () => loggerWithConsoleLog(internalLogger.logfmtLogger) +) + +/** @internal */ +export const prettyLogger: Logger = globalValue( + Symbol.for("effect/Logger/prettyLogger"), + () => internalLogger.prettyLoggerDefault +) + +/** @internal */ +export const structuredLogger: Logger = globalValue( + Symbol.for("effect/Logger/structuredLogger"), + () => loggerWithConsoleLog(internalLogger.structuredLogger) +) + +/** @internal */ +export const tracerLogger = globalValue( + Symbol.for("effect/Logger/tracerLogger"), + () => + internalLogger.makeLogger(({ + annotations, + cause, + context, + fiberId, + logLevel, + message + }) => { + const span = internalEffect.filterDisablePropagation(Context.getOption( + fiberRefs.getOrDefault(context, core.currentContext), + tracer.spanTag + )) + + if (span._tag === "None" || span.value._tag === "ExternalSpan") { + return + } + + const clockService = Context.unsafeGet( + fiberRefs.getOrDefault(context, defaultServices.currentServices), + clock.clockTag + ) + + const attributes: Record = {} + for (const [key, value] of annotations) { + attributes[key] = value + } + attributes["effect.fiberId"] = FiberId.threadName(fiberId) + attributes["effect.logLevel"] = logLevel.label + + if (cause !== null && cause._tag !== "Empty") { + attributes["effect.cause"] = internalCause.pretty(cause, { renderErrorCause: true }) + } + + span.value.event( + Inspectable.toStringUnknown(Array.isArray(message) && message.length === 1 ? message[0] : message), + clockService.unsafeCurrentTimeNanos(), + attributes + ) + }) +) + +/** @internal */ +export const loggerWithSpanAnnotations = (self: Logger): Logger => + internalLogger.mapInputOptions(self, (options: Logger.Options) => { + const span = Option.flatMap(fiberRefs.get(options.context, core.currentContext), Context.getOption(tracer.spanTag)) + if (span._tag === "None") { + return options + } + return { + ...options, + annotations: pipe( + options.annotations, + HashMap.set("effect.traceId", span.value.traceId as unknown), + HashMap.set("effect.spanId", span.value.spanId as unknown), + span.value._tag === "Span" ? HashMap.set("effect.spanName", span.value.name as unknown) : identity + ) + } + }) + +/** @internal */ +export const currentLoggers: FiberRef.FiberRef< + HashSet.HashSet> +> = globalValue( + Symbol.for("effect/FiberRef/currentLoggers"), + () => core.fiberRefUnsafeMakeHashSet(HashSet.make(defaultLogger, tracerLogger)) +) + +/** @internal */ +export const batchedLogger = dual< + ( + window: Duration.DurationInput, + f: (messages: Array>) => Effect.Effect + ) => ( + self: Logger + ) => Effect.Effect, never, Scope.Scope | R>, + ( + self: Logger, + window: Duration.DurationInput, + f: (messages: Array>) => Effect.Effect + ) => Effect.Effect, never, Scope.Scope | R> +>(3, ( + self: Logger, + window: Duration.DurationInput, + f: (messages: Array>) => Effect.Effect +): Effect.Effect, never, Scope.Scope | R> => + core.flatMap(scope, (scope) => { + let buffer: Array = [] + const flush = core.suspend(() => { + if (buffer.length === 0) { + return core.void + } + const arr = buffer + buffer = [] + return f(arr) + }) + + return core.uninterruptibleMask((restore) => + pipe( + internalEffect.sleep(window), + core.zipRight(flush), + internalEffect.forever, + restore, + forkDaemon, + core.flatMap((fiber) => core.scopeAddFinalizer(scope, core.interruptFiber(fiber))), + core.zipRight(addFinalizer(() => flush)), + core.as( + internalLogger.makeLogger((options) => { + buffer.push(self.log(options)) + }) + ) + ) + ) + })) + +export const annotateLogsScoped: { + (key: string, value: unknown): Effect.Effect + (values: Record): Effect.Effect +} = function() { + if (typeof arguments[0] === "string") { + return fiberRefLocallyScopedWith( + core.currentLogAnnotations, + HashMap.set(arguments[0], arguments[1]) + ) + } + const entries = Object.entries(arguments[0]) + return fiberRefLocallyScopedWith( + core.currentLogAnnotations, + HashMap.mutate((annotations) => { + for (let i = 0; i < entries.length; i++) { + const [key, value] = entries[i] + HashMap.set(annotations, key, value) + } + return annotations + }) + ) +} + +/** @internal */ +export const whenLogLevel = dual< + ( + level: LogLevel.LogLevel | LogLevel.Literal + ) => (effect: Effect.Effect) => Effect.Effect, E, R>, + ( + effect: Effect.Effect, + level: LogLevel.LogLevel | LogLevel.Literal + ) => Effect.Effect, E, R> +>(2, (effect, level) => { + const requiredLogLevel = typeof level === "string" ? LogLevel.fromLiteral(level) : level + + return core.withFiberRuntime((fiberState) => { + const minimumLogLevel = fiberState.getFiberRef(currentMinimumLogLevel) + + // Imitate the behaviour of `FiberRuntime.log` + if (LogLevel.greaterThan(minimumLogLevel, requiredLogLevel)) { + return core.succeed(Option.none()) + } + + return core.map(effect, Option.some) + }) +}) + +// circular with Effect + +/* @internal */ +export const acquireRelease: { + ( + release: (a: A, exit: Exit.Exit) => Effect.Effect + ): (acquire: Effect.Effect) => Effect.Effect + ( + acquire: Effect.Effect, + release: (a: A, exit: Exit.Exit) => Effect.Effect + ): Effect.Effect +} = dual((args) => core.isEffect(args[0]), (acquire, release) => + core.uninterruptible( + core.tap(acquire, (a) => addFinalizer((exit) => release(a, exit))) + )) + +/* @internal */ +export const acquireReleaseInterruptible: { + ( + release: (exit: Exit.Exit) => Effect.Effect + ): (acquire: Effect.Effect) => Effect.Effect + ( + acquire: Effect.Effect, + release: (exit: Exit.Exit) => Effect.Effect + ): Effect.Effect +} = dual((args) => core.isEffect(args[0]), (acquire, release) => + ensuring( + acquire, + addFinalizer((exit) => release(exit)) + )) + +/* @internal */ +export const addFinalizer = ( + finalizer: (exit: Exit.Exit) => Effect.Effect +): Effect.Effect => + core.withFiberRuntime( + (runtime) => { + const acquireRefs = runtime.getFiberRefs() + const acquireFlags = runtimeFlags_.disable(runtime.currentRuntimeFlags, runtimeFlags_.Interruption) + return core.flatMap(scope, (scope) => + core.scopeAddFinalizerExit(scope, (exit) => + core.withFiberRuntime((runtimeFinalizer) => { + const preRefs = runtimeFinalizer.getFiberRefs() + const preFlags = runtimeFinalizer.currentRuntimeFlags + const patchRefs = FiberRefsPatch.diff(preRefs, acquireRefs) + const patchFlags = runtimeFlags_.diff(preFlags, acquireFlags) + const inverseRefs = FiberRefsPatch.diff(acquireRefs, preRefs) + runtimeFinalizer.setFiberRefs( + FiberRefsPatch.patch(patchRefs, runtimeFinalizer.id(), acquireRefs) + ) + + return ensuring( + core.withRuntimeFlags(finalizer(exit) as Effect.Effect, patchFlags), + core.sync(() => { + runtimeFinalizer.setFiberRefs( + FiberRefsPatch.patch(inverseRefs, runtimeFinalizer.id(), runtimeFinalizer.getFiberRefs()) + ) + }) + ) + }))) + } + ) + +/* @internal */ +export const daemonChildren = (self: Effect.Effect): Effect.Effect => { + const forkScope = core.fiberRefLocally(core.currentForkScopeOverride, Option.some(fiberScope.globalScope)) + return forkScope(self) +} + +/** @internal */ +const _existsParFound = Symbol.for("effect/Effect/existsPar/found") + +/* @internal */ +export const exists: { + (predicate: (a: A, i: number) => Effect.Effect, options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + }): (elements: Iterable) => Effect.Effect + (elements: Iterable, predicate: (a: A, i: number) => Effect.Effect, options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + }): Effect.Effect +} = dual( + (args) => Predicate.isIterable(args[0]) && !core.isEffect(args[0]), + (elements: Iterable, predicate: (a: A, i: number) => Effect.Effect, options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + }) => + concurrency.matchSimple( + options?.concurrency, + () => core.suspend(() => existsLoop(elements[Symbol.iterator](), 0, predicate)), + () => + core.matchEffect( + forEach( + elements, + (a, i) => core.if_(predicate(a, i), { onTrue: () => core.fail(_existsParFound), onFalse: () => core.void }), + options + ), + { + onFailure: (e) => e === _existsParFound ? core.succeed(true) : core.fail(e), + onSuccess: () => core.succeed(false) + } + ) + ) +) + +const existsLoop = ( + iterator: Iterator, + index: number, + f: (a: A, i: number) => Effect.Effect +): Effect.Effect => { + const next = iterator.next() + if (next.done) { + return core.succeed(false) + } + return core.flatMap( + f(next.value, index), + (b) => b ? core.succeed(b) : existsLoop(iterator, index + 1, f) + ) +} + +/* @internal */ +export const filter = dual< + ( + predicate: (a: NoInfer, i: number) => Effect.Effect, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly negate?: boolean | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => (elements: Iterable) => Effect.Effect, E, R>, + (elements: Iterable, predicate: (a: NoInfer, i: number) => Effect.Effect, options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly negate?: boolean | undefined + readonly concurrentFinalizers?: boolean | undefined + }) => Effect.Effect, E, R> +>( + (args) => Predicate.isIterable(args[0]) && !core.isEffect(args[0]), + (elements: Iterable, predicate: (a: NoInfer, i: number) => Effect.Effect, options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly negate?: boolean | undefined + readonly concurrentFinalizers?: boolean | undefined + }) => { + const predicate_ = options?.negate ? (a: A, i: number) => core.map(predicate(a, i), Boolean.not) : predicate + return concurrency.matchSimple( + options?.concurrency, + () => + core.suspend(() => + RA.fromIterable(elements).reduceRight( + (effect, a, i) => + core.zipWith( + effect, + core.suspend(() => predicate_(a, i)), + (list, b) => b ? [a, ...list] : list + ), + core.sync(() => new Array()) as Effect.Effect, E, R> + ) + ), + () => + core.map( + forEach( + elements, + (a, i) => core.map(predicate_(a, i), (b) => (b ? Option.some(a) : Option.none())), + options + ), + RA.getSomes + ) + ) + } +) + +// === all + +const allResolveInput = ( + input: Iterable> | Record> +): [Iterable>, Option.Option<(as: ReadonlyArray) => any>] => { + if (Array.isArray(input) || Predicate.isIterable(input)) { + return [input, Option.none()] + } + const keys = Object.keys(input) + const size = keys.length + return [ + keys.map((k) => input[k]), + Option.some((values: ReadonlyArray) => { + const res = {} + for (let i = 0; i < size; i++) { + ;(res as any)[keys[i]] = values[i] + } + return res + }) + ] +} + +const allValidate = ( + effects: Iterable>, + reconcile: Option.Option<(as: ReadonlyArray) => any>, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: boolean | undefined + readonly mode?: "default" | "validate" | "either" | undefined + readonly concurrentFinalizers?: boolean | undefined + } +) => { + const eitherEffects: Array, never, unknown>> = [] + for (const effect of effects) { + eitherEffects.push(core.either(effect)) + } + return core.flatMap( + forEach(eitherEffects, identity, { + concurrency: options?.concurrency, + batching: options?.batching, + concurrentFinalizers: options?.concurrentFinalizers + }), + (eithers) => { + const none = Option.none() + const size = eithers.length + const errors: Array = new Array(size) + const successes: Array = new Array(size) + let errored = false + for (let i = 0; i < size; i++) { + const either = eithers[i] as Either.Either + if (either._tag === "Left") { + errors[i] = Option.some(either.left) + errored = true + } else { + successes[i] = either.right + errors[i] = none + } + } + if (errored) { + return reconcile._tag === "Some" ? + core.fail(reconcile.value(errors)) : + core.fail(errors) + } else if (options?.discard) { + return core.void + } + return reconcile._tag === "Some" ? + core.succeed(reconcile.value(successes)) : + core.succeed(successes) + } + ) +} + +const allEither = ( + effects: Iterable>, + reconcile: Option.Option<(as: ReadonlyArray) => any>, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: boolean | undefined + readonly mode?: "default" | "validate" | "either" | undefined + readonly concurrentFinalizers?: boolean | undefined + } +) => { + const eitherEffects: Array, never, unknown>> = [] + for (const effect of effects) { + eitherEffects.push(core.either(effect)) + } + + if (options?.discard) { + return forEach(eitherEffects, identity, { + concurrency: options?.concurrency, + batching: options?.batching, + discard: true, + concurrentFinalizers: options?.concurrentFinalizers + }) + } + + return core.map( + forEach(eitherEffects, identity, { + concurrency: options?.concurrency, + batching: options?.batching, + concurrentFinalizers: options?.concurrentFinalizers + }), + (eithers) => + reconcile._tag === "Some" ? + reconcile.value(eithers) : + eithers + ) +} + +/* @internal */ +export const all = < + const Arg extends Iterable> | Record>, + O extends NoExcessProperties<{ + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: boolean | undefined + readonly mode?: "default" | "validate" | "either" | undefined + readonly concurrentFinalizers?: boolean | undefined + }, O> +>( + arg: Arg, + options?: O +): Effect.All.Return => { + const [effects, reconcile] = allResolveInput(arg) + + if (options?.mode === "validate") { + return allValidate(effects, reconcile, options) as any + } else if (options?.mode === "either") { + return allEither(effects, reconcile, options) as any + } + + return options?.discard !== true && reconcile._tag === "Some" + ? core.map( + forEach(effects, identity, options as any), + reconcile.value + ) as any + : forEach(effects, identity, options as any) as any +} + +/* @internal */ +export const allWith = < + O extends NoExcessProperties<{ + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: boolean | undefined + readonly mode?: "default" | "validate" | "either" | undefined + readonly concurrentFinalizers?: boolean | undefined + }, O> +>(options?: O) => +> | Record>>( + arg: Arg +): Effect.All.Return => all(arg, options) + +/* @internal */ +export const allSuccesses = >( + elements: Iterable, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } +): Effect.Effect>, never, Effect.Effect.Context> => + core.map( + all(RA.fromIterable(elements).map(core.exit), options), + RA.filterMap((exit) => core.exitIsSuccess(exit) ? Option.some(exit.effect_instruction_i0) : Option.none()) + ) + +/* @internal */ +export const replicate = dual< + (n: number) => (self: Effect.Effect) => Array>, + (self: Effect.Effect, n: number) => Array> +>(2, (self, n) => Array.from({ length: n }, () => self)) + +/* @internal */ +export const replicateEffect: { + ( + n: number, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: false | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ): (self: Effect.Effect) => Effect.Effect, E, R> + ( + n: number, + options: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard: true + readonly concurrentFinalizers?: boolean | undefined + } + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + n: number, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: false | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ): Effect.Effect, E, R> + ( + self: Effect.Effect, + n: number, + options: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard: true + readonly concurrentFinalizers?: boolean | undefined + } + ): Effect.Effect +} = dual( + (args) => core.isEffect(args[0]), + (self, n, options) => all(replicate(self, n), options) +) + +/* @internal */ +export const forEach: { + >( + f: (a: RA.ReadonlyArray.Infer, i: number) => Effect.Effect, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: false | undefined + readonly concurrentFinalizers?: boolean | undefined + } | undefined + ): ( + self: S + ) => Effect.Effect, E, R> + ( + f: (a: A, i: number) => Effect.Effect, + options: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard: true + readonly concurrentFinalizers?: boolean | undefined + } + ): (self: Iterable) => Effect.Effect + ( + self: RA.NonEmptyReadonlyArray, + f: (a: A, i: number) => Effect.Effect, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: false | undefined + readonly concurrentFinalizers?: boolean | undefined + } | undefined + ): Effect.Effect, E, R> + ( + self: Iterable, + f: (a: A, i: number) => Effect.Effect, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: false | undefined + readonly concurrentFinalizers?: boolean | undefined + } | undefined + ): Effect.Effect, E, R> + ( + self: Iterable, + f: (a: A, i: number) => Effect.Effect, + options: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard: true + readonly concurrentFinalizers?: boolean | undefined + } + ): Effect.Effect +} = dual((args) => Predicate.isIterable(args[0]), ( + self: Iterable, + f: (a: A, i: number) => Effect.Effect, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: boolean | undefined + readonly concurrentFinalizers?: boolean | undefined + } +) => + core.withFiberRuntime((r) => { + const isRequestBatchingEnabled = options?.batching === true || + (options?.batching === "inherit" && r.getFiberRef(core.currentRequestBatching)) + + if (options?.discard) { + return concurrency.match( + options.concurrency, + () => + finalizersMaskInternal(ExecutionStrategy.sequential, options?.concurrentFinalizers)((restore) => + isRequestBatchingEnabled + ? forEachConcurrentDiscard(self, (a, i) => restore(f(a, i)), true, false, 1) + : core.forEachSequentialDiscard(self, (a, i) => restore(f(a, i))) + ), + () => + finalizersMaskInternal(ExecutionStrategy.parallel, options?.concurrentFinalizers)((restore) => + forEachConcurrentDiscard(self, (a, i) => restore(f(a, i)), isRequestBatchingEnabled, false) + ), + (n) => + finalizersMaskInternal(ExecutionStrategy.parallelN(n), options?.concurrentFinalizers)((restore) => + forEachConcurrentDiscard(self, (a, i) => restore(f(a, i)), isRequestBatchingEnabled, false, n) + ) + ) + } + + return concurrency.match( + options?.concurrency, + () => + finalizersMaskInternal(ExecutionStrategy.sequential, options?.concurrentFinalizers)((restore) => + isRequestBatchingEnabled + ? forEachParN(self, 1, (a, i) => restore(f(a, i)), true) + : core.forEachSequential(self, (a, i) => restore(f(a, i))) + ), + () => + finalizersMaskInternal(ExecutionStrategy.parallel, options?.concurrentFinalizers)((restore) => + forEachParUnbounded(self, (a, i) => restore(f(a, i)), isRequestBatchingEnabled) + ), + (n) => + finalizersMaskInternal(ExecutionStrategy.parallelN(n), options?.concurrentFinalizers)((restore) => + forEachParN(self, n, (a, i) => restore(f(a, i)), isRequestBatchingEnabled) + ) + ) + })) + +/* @internal */ +export const forEachParUnbounded = ( + self: Iterable, + f: (a: A, i: number) => Effect.Effect, + batching: boolean +): Effect.Effect, E, R> => + core.suspend(() => { + const as = RA.fromIterable(self) + const array = new Array(as.length) + const fn = (a: A, i: number) => core.flatMap(f(a, i), (b) => core.sync(() => array[i] = b)) + return core.zipRight(forEachConcurrentDiscard(as, fn, batching, false), core.succeed(array)) + }) + +/** @internal */ +export const forEachConcurrentDiscard = ( + self: Iterable, + f: (a: A, i: number) => Effect.Effect, + batching: boolean, + processAll: boolean, + n?: number +): Effect.Effect => + core.uninterruptibleMask((restore) => + core.transplant((graft) => + core.withFiberRuntime((parent) => { + let todos = Array.from(self).reverse() + let target = todos.length + if (target === 0) { + return core.void + } + let counter = 0 + let interrupted = false + const fibersCount = n ? Math.min(todos.length, n) : todos.length + const fibers = new Set | Effect.Blocked>>() + const results = new Array() + const interruptAll = () => + fibers.forEach((fiber) => { + fiber.currentScheduler.scheduleTask( + () => { + fiber.unsafeInterruptAsFork(parent.id()) + }, + 0, + fiber + ) + }) + const startOrder = new Array | Effect.Blocked>>() + const joinOrder = new Array | Effect.Blocked>>() + const residual = new Array() + const collectExits = () => { + const exits: Array> = results + .filter(({ exit }) => exit._tag === "Failure") + .sort((a, b) => a.index < b.index ? -1 : a.index === b.index ? 0 : 1) + .map(({ exit }) => exit) + if (exits.length === 0) { + exits.push(core.exitVoid) + } + return exits + } + const runFiber = (eff: Effect.Effect, interruptImmediately = false) => { + const runnable = core.uninterruptible(graft(eff)) + const fiber = unsafeForkUnstarted( + runnable, + parent, + parent.currentRuntimeFlags, + fiberScope.globalScope + ) + parent.currentScheduler.scheduleTask( + () => { + if (interruptImmediately) { + fiber.unsafeInterruptAsFork(parent.id()) + } + fiber.resume(runnable) + }, + 0, + fiber + ) + return fiber + } + const onInterruptSignal = () => { + if (!processAll) { + target -= todos.length + todos = [] + } + interrupted = true + interruptAll() + } + const stepOrExit = batching ? core.step : core.exit + const processingFiber = runFiber( + core.async((resume) => { + const pushResult = (res: Exit.Exit | Effect.Blocked, index: number) => { + if (res._op === "Blocked") { + residual.push(res as core.Blocked) + } else { + results.push({ index, exit: res }) + if (res._op === "Failure" && !interrupted) { + onInterruptSignal() + } + } + } + const next = () => { + if (todos.length > 0) { + const a = todos.pop()! + let index = counter++ + const returnNextElement = () => { + const a = todos.pop()! + index = counter++ + return core.flatMap(core.yieldNow(), () => + core.flatMap( + stepOrExit(restore(f(a, index))), + onRes + )) + } + const onRes = ( + res: Exit.Exit | Effect.Blocked + ): Effect.Effect | Effect.Blocked, never, R> => { + if (todos.length > 0) { + pushResult(res, index) + if (todos.length > 0) { + return returnNextElement() + } + } + return core.succeed(res) + } + const todo = core.flatMap( + stepOrExit(restore(f(a, index))), + onRes + ) + const fiber = runFiber(todo) + startOrder.push(fiber) + fibers.add(fiber) + if (interrupted) { + fiber.currentScheduler.scheduleTask( + () => { + fiber.unsafeInterruptAsFork(parent.id()) + }, + 0, + fiber + ) + } + fiber.addObserver((wrapped) => { + let exit: Exit.Exit | core.Blocked + if (wrapped._op === "Failure") { + exit = wrapped + } else { + exit = wrapped.effect_instruction_i0 as any + } + joinOrder.push(fiber) + fibers.delete(fiber) + pushResult(exit, index) + if (results.length === target) { + resume(core.succeed(Option.getOrElse( + core.exitCollectAll(collectExits(), { parallel: true }), + () => core.exitVoid + ))) + } else if (residual.length + results.length === target) { + const exits = collectExits() + const requests = residual.map((blocked) => blocked.effect_instruction_i0).reduce(RequestBlock_.par) + resume(core.succeed(core.blocked( + requests, + forEachConcurrentDiscard( + [ + Option.getOrElse( + core.exitCollectAll(exits, { parallel: true }), + () => core.exitVoid + ), + ...residual.map((blocked) => blocked.effect_instruction_i1) + ], + (i) => i, + batching, + true, + n + ) + ))) + } else { + next() + } + }) + } + } + for (let i = 0; i < fibersCount; i++) { + next() + } + }) + ) + return core.asVoid( + core.onExit( + core.flatten(restore(internalFiber.join(processingFiber))), + core.exitMatch({ + onFailure: (cause) => { + onInterruptSignal() + const target = residual.length + 1 + const concurrency = Math.min(typeof n === "number" ? n : residual.length, residual.length) + const toPop = Array.from(residual) + return core.async((cb) => { + const exits: Array> = [] + let count = 0 + let index = 0 + const check = (index: number, hitNext: boolean) => (exit: Exit.Exit) => { + exits[index] = exit + count++ + if (count === target) { + cb(core.exitSucceed(core.exitFailCause(cause))) + } + if (toPop.length > 0 && hitNext) { + next() + } + } + const next = () => { + runFiber(toPop.pop()!, true).addObserver(check(index, true)) + index++ + } + processingFiber.addObserver(check(index, false)) + index++ + for (let i = 0; i < concurrency; i++) { + next() + } + }) as any + }, + onSuccess: () => core.forEachSequential(joinOrder, (f) => f.inheritAll) + }) + ) + ) + }) + ) + ) + +/* @internal */ +export const forEachParN = ( + self: Iterable, + n: number, + f: (a: A, i: number) => Effect.Effect, + batching: boolean +): Effect.Effect, E, R> => + core.suspend(() => { + const as = RA.fromIterable(self) + const array = new Array(as.length) + const fn = (a: A, i: number) => core.map(f(a, i), (b) => array[i] = b) + return core.zipRight(forEachConcurrentDiscard(as, fn, batching, false, n), core.succeed(array)) + }) + +/* @internal */ +export const fork = (self: Effect.Effect): Effect.Effect, never, R> => + core.withFiberRuntime((state, status) => core.succeed(unsafeFork(self, state, status.runtimeFlags))) + +/* @internal */ +export const forkDaemon = (self: Effect.Effect): Effect.Effect, never, R> => + forkWithScopeOverride(self, fiberScope.globalScope) + +/* @internal */ +export const forkWithErrorHandler = dual< + ( + handler: (e: E) => Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect, never, R>, + ( + self: Effect.Effect, + handler: (e: E) => Effect.Effect + ) => Effect.Effect, never, R> +>(2, (self, handler) => + fork(core.onError(self, (cause) => { + const either = internalCause.failureOrCause(cause) + switch (either._tag) { + case "Left": + return handler(either.left) + case "Right": + return core.failCause(either.right) + } + }))) + +/** @internal */ +export const unsafeFork = ( + effect: Effect.Effect, + parentFiber: FiberRuntime, + parentRuntimeFlags: RuntimeFlags.RuntimeFlags, + overrideScope: fiberScope.FiberScope | null = null +): FiberRuntime => { + const childFiber = unsafeMakeChildFiber(effect, parentFiber, parentRuntimeFlags, overrideScope) + childFiber.resume(effect) + return childFiber +} + +/** @internal */ +export const unsafeForkUnstarted = ( + effect: Effect.Effect, + parentFiber: FiberRuntime, + parentRuntimeFlags: RuntimeFlags.RuntimeFlags, + overrideScope: fiberScope.FiberScope | null = null +): FiberRuntime => { + const childFiber = unsafeMakeChildFiber(effect, parentFiber, parentRuntimeFlags, overrideScope) + return childFiber +} + +/** @internal */ +export const unsafeMakeChildFiber = ( + effect: Effect.Effect, + parentFiber: FiberRuntime, + parentRuntimeFlags: RuntimeFlags.RuntimeFlags, + overrideScope: fiberScope.FiberScope | null = null +): FiberRuntime => { + const childId = FiberId.unsafeMake() + const parentFiberRefs = parentFiber.getFiberRefs() + const childFiberRefs = fiberRefs.forkAs(parentFiberRefs, childId) + const childFiber = new FiberRuntime(childId, childFiberRefs, parentRuntimeFlags) + const childContext = fiberRefs.getOrDefault( + childFiberRefs, + core.currentContext as unknown as FiberRef.FiberRef> + ) + const supervisor = childFiber.currentSupervisor + + supervisor.onStart( + childContext, + effect, + Option.some(parentFiber), + childFiber + ) + + childFiber.addObserver((exit) => supervisor.onEnd(exit, childFiber)) + + const parentScope = overrideScope !== null ? overrideScope : pipe( + parentFiber.getFiberRef(core.currentForkScopeOverride), + Option.getOrElse(() => parentFiber.scope()) + ) + + parentScope.add(parentRuntimeFlags, childFiber) + + return childFiber +} + +/* @internal */ +const forkWithScopeOverride = ( + self: Effect.Effect, + scopeOverride: fiberScope.FiberScope +): Effect.Effect, never, R> => + core.withFiberRuntime((parentFiber, parentStatus) => + core.succeed(unsafeFork(self, parentFiber, parentStatus.runtimeFlags, scopeOverride)) + ) + +/* @internal */ +export const mergeAll = dual< + >( + zero: Z, + f: (z: Z, a: Effect.Effect.Success, i: number) => Z, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => (elements: Iterable) => Effect.Effect, Effect.Effect.Context>, + , Z>( + elements: Iterable, + zero: Z, + f: (z: Z, a: Effect.Effect.Success, i: number) => Z, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => Effect.Effect, Effect.Effect.Context> +>( + (args) => Predicate.isFunction(args[2]), + (elements: Iterable>, zero: Z, f: (z: Z, a: A, i: number) => Z, options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + }) => + concurrency.matchSimple( + options?.concurrency, + () => + RA.fromIterable(elements).reduce( + (acc, a, i) => core.zipWith(acc, a, (acc, a) => f(acc, a, i)), + core.succeed(zero) as Effect.Effect + ), + () => + core.flatMap(Ref.make(zero), (acc) => + core.flatMap( + forEach( + elements, + (effect, i) => core.flatMap(effect, (a) => Ref.update(acc, (b) => f(b, a, i))), + options + ), + () => Ref.get(acc) + )) + ) +) + +/* @internal */ +export const partition = dual< + ( + f: (a: A, i: number) => Effect.Effect, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => (elements: Iterable) => Effect.Effect<[excluded: Array, satisfying: Array], never, R>, + ( + elements: Iterable, + f: (a: A, i: number) => Effect.Effect, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => Effect.Effect<[excluded: Array, satisfying: Array], never, R> +>((args) => Predicate.isIterable(args[0]), (elements, f, options) => + pipe( + forEach(elements, (a, i) => core.either(f(a, i)), options), + core.map((chunk) => core.partitionMap(chunk, identity)) + )) + +/* @internal */ +export const validateAll = dual< + { + ( + f: (a: A, i: number) => Effect.Effect, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: false | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ): (elements: Iterable) => Effect.Effect, RA.NonEmptyArray, R> + ( + f: (a: A, i: number) => Effect.Effect, + options: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard: true + readonly concurrentFinalizers?: boolean | undefined + } + ): (elements: Iterable) => Effect.Effect, R> + }, + { + ( + elements: Iterable, + f: (a: A, i: number) => Effect.Effect, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: false | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ): Effect.Effect, RA.NonEmptyArray, R> + ( + elements: Iterable, + f: (a: A, i: number) => Effect.Effect, + options: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard: true + readonly concurrentFinalizers?: boolean | undefined + } + ): Effect.Effect, R> + } +>( + (args) => Predicate.isIterable(args[0]), + (elements: Iterable, f: (a: A, i: number) => Effect.Effect, options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly discard?: boolean | undefined + readonly concurrentFinalizers?: boolean | undefined + }): Effect.Effect, R> => + core.flatMap( + partition(elements, f, { + concurrency: options?.concurrency, + batching: options?.batching, + concurrentFinalizers: options?.concurrentFinalizers + }), + ([es, bs]) => + RA.isNonEmptyArray(es) + ? core.fail(es) + : options?.discard + ? core.void + : core.succeed(bs) + ) +) + +/* @internal */ +export const raceAll: >( + all: Iterable +) => Effect.Effect, Effect.Effect.Error, Effect.Effect.Context> = < + A, + E, + R +>(all: Iterable>): Effect.Effect => + core.withFiberRuntime((state, status) => + core.async((resume) => { + const fibers = new Set>() + let winner: FiberRuntime | undefined + let failures: Cause.Cause = internalCause.empty + const interruptAll = () => { + for (const fiber of fibers) { + fiber.unsafeInterruptAsFork(state.id()) + } + } + let latch = false + let empty = true + for (const self of all) { + empty = false + const fiber = unsafeFork( + core.interruptible(self), + state, + status.runtimeFlags + ) + fibers.add(fiber) + fiber.addObserver((exit) => { + fibers.delete(fiber) + if (!winner) { + if (exit._tag === "Success") { + latch = true + winner = fiber + failures = internalCause.empty + interruptAll() + } else { + failures = internalCause.parallel(exit.cause, failures) + } + } + if (latch && fibers.size === 0) { + resume( + winner ? core.zipRight(internalFiber.inheritAll(winner), winner.unsafePoll()!) : core.failCause(failures) + ) + } + }) + if (winner) break + } + if (empty) { + return resume(core.dieSync(() => new core.IllegalArgumentException(`Received an empty collection of effects`))) + } + latch = true + return internalFiber.interruptAllAs(fibers, state.id()) + }) + ) + +/* @internal */ +export const reduceEffect = dual< + >( + zero: Effect.Effect, + f: (z: NoInfer, a: Effect.Effect.Success, i: number) => Z, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => (elements: Iterable) => Effect.Effect, R | Effect.Effect.Context>, + , Z, E, R>( + elements: Iterable, + zero: Effect.Effect, + f: (z: NoInfer, a: Effect.Effect.Success, i: number) => Z, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => Effect.Effect, R | Effect.Effect.Context> +>((args) => Predicate.isIterable(args[0]) && !core.isEffect(args[0]), ( + elements: Iterable>, + zero: Effect.Effect, + f: (z: NoInfer, a: NoInfer, i: number) => Z, + options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } +) => + concurrency.matchSimple( + options?.concurrency, + () => RA.fromIterable(elements).reduce((acc, a, i) => core.zipWith(acc, a, (acc, a) => f(acc, a, i)), zero), + () => + core.suspend(() => + pipe( + mergeAll( + [zero, ...elements], + Option.none(), + (acc, elem, i) => { + switch (acc._tag) { + case "None": { + return Option.some(elem as Z) + } + case "Some": { + return Option.some(f(acc.value, elem as A, i)) + } + } + }, + options + ), + core.map((option) => { + switch (option._tag) { + case "None": { + throw new Error( + "BUG: Effect.reduceEffect - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + } + case "Some": { + return option.value + } + } + }) + ) + ) + )) + +/* @internal */ +export const parallelFinalizers = (self: Effect.Effect): Effect.Effect => + core.contextWithEffect((context) => + Option.match(Context.getOption(context, scopeTag), { + onNone: () => self, + onSome: (scope) => { + switch (scope.strategy._tag) { + case "Parallel": + return self + case "Sequential": + case "ParallelN": + return core.flatMap( + core.scopeFork(scope, ExecutionStrategy.parallel), + (inner) => scopeExtend(self, inner) + ) + } + } + }) + ) + +/* @internal */ +export const parallelNFinalizers = + (parallelism: number) => (self: Effect.Effect): Effect.Effect => + core.contextWithEffect((context) => + Option.match(Context.getOption(context, scopeTag), { + onNone: () => self, + onSome: (scope) => { + if (scope.strategy._tag === "ParallelN" && scope.strategy.parallelism === parallelism) { + return self + } + return core.flatMap( + core.scopeFork(scope, ExecutionStrategy.parallelN(parallelism)), + (inner) => scopeExtend(self, inner) + ) + } + }) + ) + +/* @internal */ +export const finalizersMask = (strategy: ExecutionStrategy.ExecutionStrategy) => +( + self: ( + restore: (self: Effect.Effect) => Effect.Effect + ) => Effect.Effect +): Effect.Effect => finalizersMaskInternal(strategy, true)(self) + +/* @internal */ +export const finalizersMaskInternal = + (strategy: ExecutionStrategy.ExecutionStrategy, concurrentFinalizers?: boolean | undefined) => + ( + self: ( + restore: (self: Effect.Effect) => Effect.Effect + ) => Effect.Effect + ): Effect.Effect => + core.contextWithEffect((context) => + Option.match(Context.getOption(context, scopeTag), { + onNone: () => self(identity), + onSome: (scope) => { + if (concurrentFinalizers === true) { + const patch = strategy._tag === "Parallel" + ? parallelFinalizers + : strategy._tag === "Sequential" + ? sequentialFinalizers + : parallelNFinalizers(strategy.parallelism) + switch (scope.strategy._tag) { + case "Parallel": + return patch(self(parallelFinalizers)) + case "Sequential": + return patch(self(sequentialFinalizers)) + case "ParallelN": + return patch(self(parallelNFinalizers(scope.strategy.parallelism))) + } + } else { + return self(identity) + } + } + }) + ) + +/* @internal */ +export const scopeWith = ( + f: (scope: Scope.Scope) => Effect.Effect +): Effect.Effect => core.flatMap(scopeTag, f) + +/** @internal */ +export const scopedWith = ( + f: (scope: Scope.Scope) => Effect.Effect +): Effect.Effect => core.flatMap(scopeMake(), (scope) => core.onExit(f(scope), (exit) => scope.close(exit))) + +/* @internal */ +export const scopedEffect = (effect: Effect.Effect): Effect.Effect> => + core.flatMap(scopeMake(), (scope) => scopeUse(effect, scope)) + +/* @internal */ +export const sequentialFinalizers = (self: Effect.Effect): Effect.Effect => + core.contextWithEffect((context) => + Option.match(Context.getOption(context, scopeTag), { + onNone: () => self, + onSome: (scope) => { + switch (scope.strategy._tag) { + case "Sequential": + return self + case "Parallel": + case "ParallelN": + return core.flatMap( + core.scopeFork(scope, ExecutionStrategy.sequential), + (inner) => scopeExtend(self, inner) + ) + } + } + }) + ) + +/* @internal */ +export const tagMetricsScoped = (key: string, value: string): Effect.Effect => + labelMetricsScoped([metricLabel.make(key, value)]) + +/* @internal */ +export const labelMetricsScoped = ( + labels: Iterable +): Effect.Effect => + fiberRefLocallyScopedWith(core.currentMetricLabels, (old) => RA.union(old, labels)) + +/* @internal */ +export const using = dual< + ( + use: (a: A) => Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect | R2>, + ( + self: Effect.Effect, + use: (a: A) => Effect.Effect + ) => Effect.Effect | R2> +>(2, (self, use) => scopedWith((scope) => core.flatMap(scopeExtend(self, scope), use))) + +/** @internal */ +export const validate = dual< + ( + that: Effect.Effect, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => (self: Effect.Effect) => Effect.Effect<[A, B], E | E1, R | R1>, + ( + self: Effect.Effect, + that: Effect.Effect, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => Effect.Effect<[A, B], E | E1, R | R1> +>( + (args) => core.isEffect(args[1]), + (self, that, options) => validateWith(self, that, (a, b) => [a, b], options) +) + +/** @internal */ +export const validateWith = dual< + ( + that: Effect.Effect, + f: (a: A, b: B) => C, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + that: Effect.Effect, + f: (a: A, b: B) => C, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => Effect.Effect +>((args) => core.isEffect(args[1]), (self, that, f, options) => + core.flatten(zipWithOptions( + core.exit(self), + core.exit(that), + (ea, eb) => + core.exitZipWith(ea, eb, { + onSuccess: f, + onFailure: (ca, cb) => options?.concurrent ? internalCause.parallel(ca, cb) : internalCause.sequential(ca, cb) + }), + options + ))) + +/* @internal */ +export const validateAllPar = dual< + ( + f: (a: A) => Effect.Effect + ) => (elements: Iterable) => Effect.Effect, Array, R>, + ( + elements: Iterable, + f: (a: A) => Effect.Effect + ) => Effect.Effect, Array, R> +>(2, (elements, f) => + core.flatMap( + partition(elements, f), + ([es, bs]) => + es.length === 0 + ? core.succeed(bs) + : core.fail(es) + )) + +/* @internal */ +export const validateAllParDiscard = dual< + ( + f: (a: A) => Effect.Effect + ) => (elements: Iterable) => Effect.Effect, R>, + (elements: Iterable, f: (a: A) => Effect.Effect) => Effect.Effect, R> +>(2, (elements, f) => + core.flatMap( + partition(elements, f), + ([es, _]) => + es.length === 0 + ? core.void + : core.fail(es) + )) + +/* @internal */ +export const validateFirst = dual< + (f: (a: A, i: number) => Effect.Effect, options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + }) => (elements: Iterable) => Effect.Effect, R>, + (elements: Iterable, f: (a: A, i: number) => Effect.Effect, options?: { + readonly concurrency?: Concurrency | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + }) => Effect.Effect, R> +>( + (args) => Predicate.isIterable(args[0]), + (elements, f, options) => core.flip(forEach(elements, (a, i) => core.flip(f(a, i)), options)) +) + +/* @internal */ +export const withClockScoped = (c: C) => + fiberRefLocallyScopedWith(defaultServices.currentServices, Context.add(clock.clockTag, c)) + +/* @internal */ +export const withRandomScoped = (value: A) => + fiberRefLocallyScopedWith(defaultServices.currentServices, Context.add(randomTag, value)) + +/* @internal */ +export const withConfigProviderScoped = (provider: ConfigProvider) => + fiberRefLocallyScopedWith(defaultServices.currentServices, Context.add(configProviderTag, provider)) + +/* @internal */ +export const withEarlyRelease = ( + self: Effect.Effect +): Effect.Effect<[Effect.Effect, A], E, R | Scope.Scope> => + scopeWith((parent) => + core.flatMap(core.scopeFork(parent, executionStrategy.sequential), (child) => + pipe( + self, + scopeExtend(child), + core.map((value) => [ + core.fiberIdWith((fiberId) => core.scopeClose(child, core.exitInterrupt(fiberId))), + value + ]) + )) + ) + +/** @internal */ +export const zipOptions = dual< + ( + that: Effect.Effect, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => ( + self: Effect.Effect + ) => Effect.Effect<[A, A2], E | E2, R | R2>, + ( + self: Effect.Effect, + that: Effect.Effect, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => Effect.Effect<[A, A2], E | E2, R | R2> +>((args) => core.isEffect(args[1]), ( + self, + that, + options +) => zipWithOptions(self, that, (a, b) => [a, b], options)) + +/** @internal */ +export const zipLeftOptions = dual< + ( + that: Effect.Effect, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => ( + self: Effect.Effect + ) => Effect.Effect, + ( + self: Effect.Effect, + that: Effect.Effect, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ) => Effect.Effect +>( + (args) => core.isEffect(args[1]), + (self, that, options) => { + if (options?.concurrent !== true && (options?.batching === undefined || options.batching === false)) { + return core.zipLeft(self, that) + } + return zipWithOptions(self, that, (a, _) => a, options) + } +) + +/** @internal */ +export const zipRightOptions: { + ( + that: Effect.Effect, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + that: Effect.Effect, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ): Effect.Effect +} = dual((args) => core.isEffect(args[1]), ( + self: Effect.Effect, + that: Effect.Effect, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } +): Effect.Effect => { + if (options?.concurrent !== true && (options?.batching === undefined || options.batching === false)) { + return core.zipRight(self, that) + } + return zipWithOptions(self, that, (_, b) => b, options) +}) + +/** @internal */ +export const zipWithOptions: { + ( + that: Effect.Effect, + f: (a: A, b: A2) => B, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + that: Effect.Effect, + f: (a: A, b: A2) => B, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } + ): Effect.Effect +} = dual((args) => core.isEffect(args[1]), ( + self: Effect.Effect, + that: Effect.Effect, + f: (a: A, b: A2) => B, + options?: { + readonly concurrent?: boolean | undefined + readonly batching?: boolean | "inherit" | undefined + readonly concurrentFinalizers?: boolean | undefined + } +): Effect.Effect => + core.map( + all([self, that], { + concurrency: options?.concurrent ? 2 : 1, + batching: options?.batching, + concurrentFinalizers: options?.concurrentFinalizers + }), + ([a, a2]) => f(a, a2) + )) + +/* @internal */ +export const withRuntimeFlagsScoped = ( + update: RuntimeFlagsPatch.RuntimeFlagsPatch +): Effect.Effect => { + if (update === RuntimeFlagsPatch.empty) { + return core.void + } + return pipe( + core.runtimeFlags, + core.flatMap((runtimeFlags) => { + const updatedRuntimeFlags = runtimeFlags_.patch(runtimeFlags, update) + const revertRuntimeFlags = runtimeFlags_.diff(updatedRuntimeFlags, runtimeFlags) + return pipe( + core.updateRuntimeFlags(update), + core.zipRight(addFinalizer(() => core.updateRuntimeFlags(revertRuntimeFlags))), + core.asVoid + ) + }), + core.uninterruptible + ) +} + +// circular with Scope + +/** @internal */ +export const scopeTag = Context.GenericTag("effect/Scope") + +/* @internal */ +export const scope: Effect.Effect = scopeTag + +/** @internal */ +export interface ScopeImpl extends Scope.CloseableScope { + state: { + readonly _tag: "Open" + readonly finalizers: Map<{}, Scope.Scope.Finalizer> + } | { + readonly _tag: "Closed" + readonly exit: Exit.Exit + } +} + +const scopeUnsafeAddFinalizer = (scope: ScopeImpl, fin: Scope.Scope.Finalizer): void => { + if (scope.state._tag === "Open") { + scope.state.finalizers.set({}, fin) + } +} + +const ScopeImplProto: Omit = { + [core.ScopeTypeId]: core.ScopeTypeId, + [core.CloseableScopeTypeId]: core.CloseableScopeTypeId, + pipe() { + return pipeArguments(this, arguments) + }, + fork(this: ScopeImpl, strategy) { + return core.sync(() => { + const newScope = scopeUnsafeMake(strategy) + if (this.state._tag === "Closed") { + newScope.state = this.state + return newScope + } + const key = {} + const fin = (exit: Exit.Exit) => newScope.close(exit) + this.state.finalizers.set(key, fin) + scopeUnsafeAddFinalizer(newScope, (_) => + core.sync(() => { + if (this.state._tag === "Open") { + this.state.finalizers.delete(key) + } + })) + return newScope + }) + }, + close(this: ScopeImpl, exit) { + return core.suspend(() => { + if (this.state._tag === "Closed") { + return core.void + } + const finalizers = Array.from(this.state.finalizers.values()).reverse() + this.state = { _tag: "Closed", exit } + if (finalizers.length === 0) { + return core.void + } + return executionStrategy.isSequential(this.strategy) ? + pipe( + core.forEachSequential(finalizers, (fin) => core.exit(fin(exit))), + core.flatMap((results) => + pipe( + core.exitCollectAll(results), + Option.map(core.exitAsVoid), + Option.getOrElse(() => core.exitVoid) + ) + ) + ) : + executionStrategy.isParallel(this.strategy) ? + pipe( + forEachParUnbounded(finalizers, (fin) => core.exit(fin(exit)), false), + core.flatMap((results) => + pipe( + core.exitCollectAll(results, { parallel: true }), + Option.map(core.exitAsVoid), + Option.getOrElse(() => core.exitVoid) + ) + ) + ) : + pipe( + forEachParN(finalizers, this.strategy.parallelism, (fin) => core.exit(fin(exit)), false), + core.flatMap((results) => + pipe( + core.exitCollectAll(results, { parallel: true }), + Option.map(core.exitAsVoid), + Option.getOrElse(() => core.exitVoid) + ) + ) + ) + }) + }, + addFinalizer(this: ScopeImpl, fin) { + return core.suspend(() => { + if (this.state._tag === "Closed") { + return fin(this.state.exit) + } + this.state.finalizers.set({}, fin) + return core.void + }) + } +} + +const scopeUnsafeMake = ( + strategy: ExecutionStrategy.ExecutionStrategy = executionStrategy.sequential +): ScopeImpl => { + const scope = Object.create(ScopeImplProto) + scope.strategy = strategy + scope.state = { _tag: "Open", finalizers: new Map() } + return scope +} + +/* @internal */ +export const scopeMake = ( + strategy: ExecutionStrategy.ExecutionStrategy = executionStrategy.sequential +): Effect.Effect => core.sync(() => scopeUnsafeMake(strategy)) + +/* @internal */ +export const scopeExtend = dual< + (scope: Scope.Scope) => (effect: Effect.Effect) => Effect.Effect>, + (effect: Effect.Effect, scope: Scope.Scope) => Effect.Effect> +>( + 2, + (effect: Effect.Effect, scope: Scope.Scope) => + core.mapInputContext>( + effect, + // @ts-expect-error + Context.merge(Context.make(scopeTag, scope)) + ) +) + +/* @internal */ +export const scopeUse = dual< + ( + scope: Scope.Scope.Closeable + ) => (effect: Effect.Effect) => Effect.Effect>, + ( + effect: Effect.Effect, + scope: Scope.Scope.Closeable + ) => Effect.Effect> +>(2, (effect, scope) => + pipe( + effect, + scopeExtend(scope), + core.onExit((exit) => scope.close(exit)) + )) + +// circular with Supervisor + +/** @internal */ +export const fiberRefUnsafeMakeSupervisor = ( + initial: Supervisor.Supervisor +): FiberRef.FiberRef> => + core.fiberRefUnsafeMakePatch(initial, { + differ: SupervisorPatch.differ, + fork: SupervisorPatch.empty + }) + +// circular with FiberRef + +/* @internal */ +export const fiberRefLocallyScoped = dual< + (value: A) => (self: FiberRef.FiberRef) => Effect.Effect, + (self: FiberRef.FiberRef, value: A) => Effect.Effect +>(2, (self, value) => + core.asVoid( + acquireRelease( + core.flatMap( + core.fiberRefGet(self), + (oldValue) => core.as(core.fiberRefSet(self, value), oldValue) + ), + (oldValue) => core.fiberRefSet(self, oldValue) + ) + )) + +/* @internal */ +export const fiberRefLocallyScopedWith = dual< + (f: (a: A) => A) => (self: FiberRef.FiberRef) => Effect.Effect, + (self: FiberRef.FiberRef, f: (a: A) => A) => Effect.Effect +>(2, (self, f) => core.fiberRefGetWith(self, (a) => fiberRefLocallyScoped(self, f(a)))) + +/* @internal */ +export const fiberRefMake = ( + initial: A, + options?: { + readonly fork?: ((a: A) => A) | undefined + readonly join?: ((left: A, right: A) => A) | undefined + } +): Effect.Effect, never, Scope.Scope> => + fiberRefMakeWith(() => core.fiberRefUnsafeMake(initial, options)) + +/* @internal */ +export const fiberRefMakeWith = ( + ref: LazyArg> +): Effect.Effect, never, Scope.Scope> => + acquireRelease( + core.tap(core.sync(ref), (ref) => core.fiberRefUpdate(ref, identity)), + (fiberRef) => core.fiberRefDelete(fiberRef) + ) + +/* @internal */ +export const fiberRefMakeContext = ( + initial: Context.Context +): Effect.Effect>, never, Scope.Scope> => + fiberRefMakeWith(() => core.fiberRefUnsafeMakeContext(initial)) + +/* @internal */ +export const fiberRefMakeRuntimeFlags = ( + initial: RuntimeFlags.RuntimeFlags +): Effect.Effect, never, Scope.Scope> => + fiberRefMakeWith(() => core.fiberRefUnsafeMakeRuntimeFlags(initial)) + +/** @internal */ +export const currentRuntimeFlags: FiberRef.FiberRef = core.fiberRefUnsafeMakeRuntimeFlags( + runtimeFlags_.none +) + +/** @internal */ +export const currentSupervisor: FiberRef.FiberRef> = fiberRefUnsafeMakeSupervisor( + supervisor.none +) + +// circular with Fiber + +/* @internal */ +export const fiberAwaitAll = >>( + fibers: T +): Effect.Effect< + [T] extends [ReadonlyArray] + ? number extends T["length"] ? Array ? Exit.Exit : never> + : { -readonly [K in keyof T]: T[K] extends Fiber.Fiber ? Exit.Exit : never } + : Array ? U extends Fiber.Fiber ? Exit.Exit : never : never> +> => forEach(fibers, internalFiber._await) as any + +/** @internal */ +export const fiberAll = (fibers: Iterable>): Fiber.Fiber, E> => { + const _fiberAll = { + ...Effectable.CommitPrototype, + commit() { + return internalFiber.join(this) + }, + [internalFiber.FiberTypeId]: internalFiber.fiberVariance, + id: () => + RA.fromIterable(fibers).reduce((id, fiber) => FiberId.combine(id, fiber.id()), FiberId.none as FiberId.FiberId), + await: core.exit(forEachParUnbounded(fibers, (fiber) => core.flatten(fiber.await), false)), + children: core.map(forEachParUnbounded(fibers, (fiber) => fiber.children, false), RA.flatten), + inheritAll: core.forEachSequentialDiscard(fibers, (fiber) => fiber.inheritAll), + poll: core.map( + core.forEachSequential(fibers, (fiber) => fiber.poll), + RA.reduceRight( + Option.some, E>>(core.exitSucceed(new Array())), + (optionB, optionA) => { + switch (optionA._tag) { + case "None": { + return Option.none() + } + case "Some": { + switch (optionB._tag) { + case "None": { + return Option.none() + } + case "Some": { + return Option.some( + core.exitZipWith(optionA.value, optionB.value, { + onSuccess: (a, chunk) => [a, ...chunk], + onFailure: internalCause.parallel + }) + ) + } + } + } + } + } + ) + ), + interruptAsFork: (fiberId: FiberId.FiberId) => + core.forEachSequentialDiscard(fibers, (fiber) => fiber.interruptAsFork(fiberId)) + } + return _fiberAll +} + +/* @internal */ +export const fiberInterruptFork = (self: Fiber.Fiber): Effect.Effect => + core.asVoid(forkDaemon(core.interruptFiber(self))) + +/* @internal */ +export const fiberJoinAll = (fibers: Iterable>): Effect.Effect, E> => + internalFiber.join(fiberAll(fibers)) + +/* @internal */ +export const fiberScoped = (self: Fiber.Fiber): Effect.Effect, never, Scope.Scope> => + acquireRelease(core.succeed(self), core.interruptFiber) + +// +// circular race +// + +/** @internal */ +export const raceWith = dual< + ( + other: Effect.Effect, + options: { + readonly onSelfDone: (exit: Exit.Exit, fiber: Fiber.Fiber) => Effect.Effect + readonly onOtherDone: (exit: Exit.Exit, fiber: Fiber.Fiber) => Effect.Effect + } + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + other: Effect.Effect, + options: { + readonly onSelfDone: (exit: Exit.Exit, fiber: Fiber.Fiber) => Effect.Effect + readonly onOtherDone: (exit: Exit.Exit, fiber: Fiber.Fiber) => Effect.Effect + } + ) => Effect.Effect +>(3, (self, other, options) => + raceFibersWith(self, other, { + onSelfWin: (winner, loser) => + core.flatMap(winner.await, (exit) => { + switch (exit._tag) { + case OpCodes.OP_SUCCESS: { + return core.flatMap( + winner.inheritAll, + () => options.onSelfDone(exit, loser) + ) + } + case OpCodes.OP_FAILURE: { + return options.onSelfDone(exit, loser) + } + } + }), + onOtherWin: (winner, loser) => + core.flatMap(winner.await, (exit) => { + switch (exit._tag) { + case OpCodes.OP_SUCCESS: { + return core.flatMap( + winner.inheritAll, + () => options.onOtherDone(exit, loser) + ) + } + case OpCodes.OP_FAILURE: { + return options.onOtherDone(exit, loser) + } + } + }) + })) + +/** @internal */ +export const disconnect = (self: Effect.Effect): Effect.Effect => + core.uninterruptibleMask((restore) => + core.fiberIdWith((fiberId) => + core.flatMap(forkDaemon(restore(self)), (fiber) => + pipe( + restore(internalFiber.join(fiber)), + core.onInterrupt(() => pipe(fiber, internalFiber.interruptAsFork(fiberId))) + )) + ) + ) + +/** @internal */ +export const race = dual< + ( + that: Effect.Effect + ) => ( + self: Effect.Effect + ) => Effect.Effect, + ( + self: Effect.Effect, + that: Effect.Effect + ) => Effect.Effect +>( + 2, + (self, that) => + core.fiberIdWith((parentFiberId) => + raceWith(self, that, { + onSelfDone: (exit, right) => + core.exitMatchEffect(exit, { + onFailure: (cause) => + pipe( + internalFiber.join(right), + internalEffect.mapErrorCause((cause2) => internalCause.parallel(cause, cause2)) + ), + onSuccess: (value) => + pipe( + right, + core.interruptAsFiber(parentFiberId), + core.as(value) + ) + }), + onOtherDone: (exit, left) => + core.exitMatchEffect(exit, { + onFailure: (cause) => + pipe( + internalFiber.join(left), + internalEffect.mapErrorCause((cause2) => internalCause.parallel(cause2, cause)) + ), + onSuccess: (value) => + pipe( + left, + core.interruptAsFiber(parentFiberId), + core.as(value) + ) + }) + }) + ) +) + +/** @internal */ +export const raceFibersWith = dual< + ( + other: Effect.Effect, + options: { + readonly onSelfWin: ( + winner: Fiber.RuntimeFiber, + loser: Fiber.RuntimeFiber + ) => Effect.Effect + readonly onOtherWin: ( + winner: Fiber.RuntimeFiber, + loser: Fiber.RuntimeFiber + ) => Effect.Effect + readonly selfScope?: fiberScope.FiberScope | undefined + readonly otherScope?: fiberScope.FiberScope | undefined + } + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + other: Effect.Effect, + options: { + readonly onSelfWin: ( + winner: Fiber.RuntimeFiber, + loser: Fiber.RuntimeFiber + ) => Effect.Effect + readonly onOtherWin: ( + winner: Fiber.RuntimeFiber, + loser: Fiber.RuntimeFiber + ) => Effect.Effect + readonly selfScope?: fiberScope.FiberScope | undefined + readonly otherScope?: fiberScope.FiberScope | undefined + } + ) => Effect.Effect +>(3, ( + self: Effect.Effect, + other: Effect.Effect, + options: { + readonly onSelfWin: ( + winner: Fiber.RuntimeFiber, + loser: Fiber.RuntimeFiber + ) => Effect.Effect + readonly onOtherWin: ( + winner: Fiber.RuntimeFiber, + loser: Fiber.RuntimeFiber + ) => Effect.Effect + readonly selfScope?: fiberScope.FiberScope | undefined + readonly otherScope?: fiberScope.FiberScope | undefined + } +) => + core.withFiberRuntime((parentFiber, parentStatus) => { + const parentRuntimeFlags = parentStatus.runtimeFlags + const raceIndicator = MRef.make(true) + const leftFiber: FiberRuntime = unsafeMakeChildFiber( + self, + parentFiber, + parentRuntimeFlags, + options.selfScope + ) + const rightFiber: FiberRuntime = unsafeMakeChildFiber( + other, + parentFiber, + parentRuntimeFlags, + options.otherScope + ) + return core.async((cb) => { + leftFiber.addObserver(() => completeRace(leftFiber, rightFiber, options.onSelfWin, raceIndicator, cb)) + rightFiber.addObserver(() => completeRace(rightFiber, leftFiber, options.onOtherWin, raceIndicator, cb)) + leftFiber.startFork(self) + rightFiber.startFork(other) + }, FiberId.combine(leftFiber.id(), rightFiber.id())) + })) + +const completeRace = ( + winner: Fiber.RuntimeFiber, + loser: Fiber.RuntimeFiber, + cont: (winner: Fiber.RuntimeFiber, loser: Fiber.RuntimeFiber) => Effect.Effect, + ab: MRef.MutableRef, + cb: (_: Effect.Effect) => void +): void => { + if (MRef.compareAndSet(true, false)(ab)) { + cb(cont(winner, loser)) + } +} + +/** @internal */ +export const ensuring: { + ( + finalizer: Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + (self: Effect.Effect, finalizer: Effect.Effect): Effect.Effect +} = dual( + 2, + (self: Effect.Effect, finalizer: Effect.Effect): Effect.Effect => + core.uninterruptibleMask((restore) => + core.matchCauseEffect(restore(self), { + onFailure: (cause1) => + core.matchCauseEffect(finalizer, { + onFailure: (cause2) => core.failCause(internalCause.sequential(cause1, cause2)), + onSuccess: () => core.failCause(cause1) + }), + onSuccess: (a) => core.as(finalizer, a) + }) + ) +) + +/** @internal */ +export const invokeWithInterrupt: ( + self: Effect.Effect, + entries: ReadonlyArray>, + onInterrupt?: () => void +) => Effect.Effect = ( + self: Effect.Effect, + entries: ReadonlyArray>, + onInterrupt?: () => void +) => + core.fiberIdWith((id) => + core.flatMap( + core.flatMap( + forkDaemon(core.interruptible(self)), + (processing) => + core.async((cb) => { + const counts = entries.map((_) => _.listeners.count) + const checkDone = () => { + if (counts.every((count) => count === 0)) { + if ( + entries.every((_) => { + if (_.result.state.current._tag === "Pending") { + return true + } else if ( + _.result.state.current._tag === "Done" && + core.exitIsExit(_.result.state.current.effect) && + _.result.state.current.effect._tag === "Failure" && + internalCause.isInterrupted(_.result.state.current.effect.cause) + ) { + return true + } else { + return false + } + }) + ) { + cleanup.forEach((f) => f()) + onInterrupt?.() + cb(core.interruptFiber(processing)) + } + } + } + processing.addObserver((exit) => { + cleanup.forEach((f) => f()) + cb(exit) + }) + const cleanup = entries.map((r, i) => { + const observer = (count: number) => { + counts[i] = count + checkDone() + } + r.listeners.addObserver(observer) + return () => r.listeners.removeObserver(observer) + }) + checkDone() + return core.sync(() => { + cleanup.forEach((f) => f()) + }) + }) + ), + () => + core.suspend(() => { + const residual = entries.flatMap((entry) => { + if (!entry.state.completed) { + return [entry] + } + return [] + }) + return core.forEachSequentialDiscard( + residual, + (entry) => complete(entry.request as any, core.exitInterrupt(id)) + ) + }) + ) + ) + +/** @internal */ +export const interruptWhenPossible = dual< + (all: Iterable>) => ( + self: Effect.Effect + ) => Effect.Effect, + ( + self: Effect.Effect, + all: Iterable> + ) => Effect.Effect +>(2, (self, all) => + core.fiberRefGetWith( + currentRequestMap, + (map) => + core.suspend(() => { + const entries = RA.fromIterable(all).flatMap((_) => map.has(_) ? [map.get(_)!] : []) + return invokeWithInterrupt(self, entries) + }) + )) + +// circular Tracer + +/** @internal */ +export const makeSpanScoped = ( + name: string, + options?: Tracer.SpanOptions | undefined +): Effect.Effect => { + options = tracer.addSpanStackTrace(options) + return core.uninterruptible( + core.withFiberRuntime((fiber) => { + const scope = Context.unsafeGet(fiber.getFiberRef(core.currentContext), scopeTag) + const span = internalEffect.unsafeMakeSpan(fiber, name, options) + const timingEnabled = fiber.getFiberRef(core.currentTracerTimingEnabled) + const clock_ = Context.get(fiber.getFiberRef(defaultServices.currentServices), clock.clockTag) + return core.as( + core.scopeAddFinalizerExit(scope, (exit) => internalEffect.endSpan(span, exit, clock_, timingEnabled)), + span + ) + }) + ) +} + +/* @internal */ +export const withTracerScoped = (value: Tracer.Tracer): Effect.Effect => + fiberRefLocallyScopedWith(defaultServices.currentServices, Context.add(tracer.tracerTag, value)) + +/** @internal */ +export const withSpanScoped: { + ( + name: string, + options?: Tracer.SpanOptions + ): (self: Effect.Effect) => Effect.Effect> + ( + self: Effect.Effect, + name: string, + options?: Tracer.SpanOptions + ): Effect.Effect> +} = function() { + const dataFirst = typeof arguments[0] !== "string" + const name = dataFirst ? arguments[1] : arguments[0] + const options = tracer.addSpanStackTrace(dataFirst ? arguments[2] : arguments[1]) + if (dataFirst) { + const self = arguments[0] + return core.flatMap( + makeSpanScoped(name, tracer.addSpanStackTrace(options)), + (span) => internalEffect.provideService(self, tracer.spanTag, span) + ) + } + return (self: Effect.Effect) => + core.flatMap( + makeSpanScoped(name, tracer.addSpanStackTrace(options)), + (span) => internalEffect.provideService(self, tracer.spanTag, span) + ) +} as any diff --git a/backend/node_modules/effect/src/internal/fiberScope.ts b/backend/node_modules/effect/src/internal/fiberScope.ts new file mode 100644 index 0000000000000000000000000000000000000000..194873a635044aeb45e32854f1c35dc4c03666e3 --- /dev/null +++ b/backend/node_modules/effect/src/internal/fiberScope.ts @@ -0,0 +1,71 @@ +import * as FiberId from "../FiberId.js" +import { globalValue } from "../GlobalValue.js" +import type * as RuntimeFlags from "../RuntimeFlags.js" +import * as FiberMessage from "./fiberMessage.js" +import type * as FiberRuntime from "./fiberRuntime.js" + +/** @internal */ +const FiberScopeSymbolKey = "effect/FiberScope" + +/** @internal */ +export const FiberScopeTypeId = Symbol.for(FiberScopeSymbolKey) + +export type FiberScopeTypeId = typeof FiberScopeTypeId + +/** + * A `FiberScope` represents the scope of a fiber lifetime. The scope of a + * fiber can be retrieved using `Effect.descriptor`, and when forking fibers, + * you can specify a custom scope to fork them on by using the `forkIn`. + * + * @since 2.0.0 + * @category models + */ +export interface FiberScope { + readonly [FiberScopeTypeId]: FiberScopeTypeId + get fiberId(): FiberId.FiberId + add(runtimeFlags: RuntimeFlags.RuntimeFlags, child: FiberRuntime.FiberRuntime): void +} + +/** @internal */ +class Global implements FiberScope { + readonly [FiberScopeTypeId]: FiberScopeTypeId = FiberScopeTypeId + readonly fiberId = FiberId.none + readonly roots = new Set>() + add(_runtimeFlags: RuntimeFlags.RuntimeFlags, child: FiberRuntime.FiberRuntime): void { + this.roots.add(child) + child.addObserver(() => { + this.roots.delete(child) + }) + } +} + +/** @internal */ +class Local implements FiberScope { + readonly [FiberScopeTypeId]: FiberScopeTypeId = FiberScopeTypeId + constructor( + readonly fiberId: FiberId.FiberId, + readonly parent: FiberRuntime.FiberRuntime + ) { + } + add(_runtimeFlags: RuntimeFlags.RuntimeFlags, child: FiberRuntime.FiberRuntime): void { + this.parent.tell( + FiberMessage.stateful((parentFiber) => { + parentFiber.addChild(child) + child.addObserver(() => { + parentFiber.removeChild(child) + }) + }) + ) + } +} + +/** @internal */ +export const unsafeMake = (fiber: FiberRuntime.FiberRuntime): FiberScope => { + return new Local(fiber.id(), fiber) +} + +/** @internal */ +export const globalScope = globalValue( + Symbol.for("effect/FiberScope/Global"), + () => new Global() +) diff --git a/backend/node_modules/effect/src/internal/fiberStatus.ts b/backend/node_modules/effect/src/internal/fiberStatus.ts new file mode 100644 index 0000000000000000000000000000000000000000..a2ce8dcd95768686cc5fd302cc65434482fa6b79 --- /dev/null +++ b/backend/node_modules/effect/src/internal/fiberStatus.ts @@ -0,0 +1,119 @@ +import * as Equal from "../Equal.js" +import type { FiberId } from "../FiberId.js" +import type * as FiberStatus from "../FiberStatus.js" +import { pipe } from "../Function.js" +import * as Hash from "../Hash.js" +import { hasProperty } from "../Predicate.js" +import type { RuntimeFlags } from "../RuntimeFlags.js" + +const FiberStatusSymbolKey = "effect/FiberStatus" + +/** @internal */ +export const FiberStatusTypeId: FiberStatus.FiberStatusTypeId = Symbol.for( + FiberStatusSymbolKey +) as FiberStatus.FiberStatusTypeId + +/** @internal */ +export const OP_DONE = "Done" as const + +/** @internal */ +export type OP_DONE = typeof OP_DONE + +/** @internal */ +export const OP_RUNNING = "Running" as const + +/** @internal */ +export type OP_RUNNING = typeof OP_RUNNING + +/** @internal */ +export const OP_SUSPENDED = "Suspended" as const + +/** @internal */ +export type OP_SUSPENDED = typeof OP_SUSPENDED + +const DoneHash = Hash.string(`${FiberStatusSymbolKey}-${OP_DONE}`) + +/** @internal */ +class Done implements FiberStatus.Done { + readonly [FiberStatusTypeId]: FiberStatus.FiberStatusTypeId = FiberStatusTypeId + readonly _tag = OP_DONE; + [Hash.symbol](): number { + return DoneHash + } + [Equal.symbol](that: unknown): boolean { + return isFiberStatus(that) && that._tag === OP_DONE + } +} + +/** @internal */ +class Running implements FiberStatus.Running { + readonly [FiberStatusTypeId]: FiberStatus.FiberStatusTypeId = FiberStatusTypeId + readonly _tag = OP_RUNNING + constructor(readonly runtimeFlags: RuntimeFlags) {} + [Hash.symbol](): number { + return pipe( + Hash.hash(FiberStatusSymbolKey), + Hash.combine(Hash.hash(this._tag)), + Hash.combine(Hash.hash(this.runtimeFlags)), + Hash.cached(this) + ) + } + [Equal.symbol](that: unknown): boolean { + return ( + isFiberStatus(that) && + that._tag === OP_RUNNING && + this.runtimeFlags === that.runtimeFlags + ) + } +} + +/** @internal */ +class Suspended implements FiberStatus.Suspended { + readonly [FiberStatusTypeId]: FiberStatus.FiberStatusTypeId = FiberStatusTypeId + readonly _tag = OP_SUSPENDED + constructor( + readonly runtimeFlags: RuntimeFlags, + readonly blockingOn: FiberId + ) {} + [Hash.symbol](): number { + return pipe( + Hash.hash(FiberStatusSymbolKey), + Hash.combine(Hash.hash(this._tag)), + Hash.combine(Hash.hash(this.runtimeFlags)), + Hash.combine(Hash.hash(this.blockingOn)), + Hash.cached(this) + ) + } + [Equal.symbol](that: unknown): boolean { + return ( + isFiberStatus(that) && + that._tag === OP_SUSPENDED && + this.runtimeFlags === that.runtimeFlags && + Equal.equals(this.blockingOn, that.blockingOn) + ) + } +} + +/** @internal */ +export const done: FiberStatus.FiberStatus = new Done() + +/** @internal */ +export const running = (runtimeFlags: RuntimeFlags): FiberStatus.FiberStatus => new Running(runtimeFlags) + +/** @internal */ +export const suspended = ( + runtimeFlags: RuntimeFlags, + blockingOn: FiberId +): FiberStatus.FiberStatus => new Suspended(runtimeFlags, blockingOn) + +/** @internal */ +export const isFiberStatus = (u: unknown): u is FiberStatus.FiberStatus => hasProperty(u, FiberStatusTypeId) + +/** @internal */ +export const isDone = (self: FiberStatus.FiberStatus): self is FiberStatus.Done => self._tag === OP_DONE + +/** @internal */ +export const isRunning = (self: FiberStatus.FiberStatus): self is FiberStatus.Running => self._tag === OP_RUNNING + +/** @internal */ +export const isSuspended = (self: FiberStatus.FiberStatus): self is FiberStatus.Suspended => self._tag === OP_SUSPENDED diff --git a/backend/node_modules/effect/src/internal/groupBy.ts b/backend/node_modules/effect/src/internal/groupBy.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad53fc036e521aab312e4c3c0a6c93c37dee76d1 --- /dev/null +++ b/backend/node_modules/effect/src/internal/groupBy.ts @@ -0,0 +1,530 @@ +import * as Cause from "../Cause.js" +import type * as Channel from "../Channel.js" +import * as Chunk from "../Chunk.js" +import * as Deferred from "../Deferred.js" +import * as Effect from "../Effect.js" +import * as Effectable from "../Effectable.js" +import * as Exit from "../Exit.js" +import { dual, pipe } from "../Function.js" +import type * as GroupBy from "../GroupBy.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty, type Predicate } from "../Predicate.js" +import * as Queue from "../Queue.js" +import * as Ref from "../Ref.js" +import * as Scope from "../Scope.js" +import type * as Stream from "../Stream.js" +import type * as Take from "../Take.js" +import type { NoInfer } from "../Types.js" +import * as channel from "./channel.js" +import * as channelExecutor from "./channel/channelExecutor.js" +import * as core from "./core-stream.js" +import * as stream from "./stream.js" +import * as take from "./take.js" + +/** @internal */ +const GroupBySymbolKey = "effect/GroupBy" + +/** @internal */ +export const GroupByTypeId: GroupBy.GroupByTypeId = Symbol.for( + GroupBySymbolKey +) as GroupBy.GroupByTypeId + +const groupByVariance = { + /* c8 ignore next */ + _R: (_: never) => _, + /* c8 ignore next */ + _E: (_: never) => _, + /* c8 ignore next */ + _K: (_: never) => _, + /* c8 ignore next */ + _V: (_: never) => _ +} + +/** @internal */ +export const isGroupBy = (u: unknown): u is GroupBy.GroupBy => + hasProperty(u, GroupByTypeId) + +/** @internal */ +export const evaluate = dual< + ( + f: (key: K, stream: Stream.Stream) => Stream.Stream, + options?: { + readonly bufferSize?: number | undefined + } + ) => (self: GroupBy.GroupBy) => Stream.Stream, + ( + self: GroupBy.GroupBy, + f: (key: K, stream: Stream.Stream) => Stream.Stream, + options?: { + readonly bufferSize?: number | undefined + } + ) => Stream.Stream +>( + (args) => isGroupBy(args[0]), + ( + self: GroupBy.GroupBy, + f: (key: K, stream: Stream.Stream) => Stream.Stream, + options?: { + readonly bufferSize?: number | undefined + } + ): Stream.Stream => + stream.flatMap( + self.grouped, + ([key, queue]) => f(key, stream.flattenTake(stream.fromQueue(queue, { shutdown: true }))), + { concurrency: "unbounded", bufferSize: options?.bufferSize ?? 16 } + ) +) + +/** @internal */ +export const filter = dual< + (predicate: Predicate>) => (self: GroupBy.GroupBy) => GroupBy.GroupBy, + (self: GroupBy.GroupBy, predicate: Predicate) => GroupBy.GroupBy +>(2, (self: GroupBy.GroupBy, predicate: Predicate): GroupBy.GroupBy => + make( + pipe( + self.grouped, + stream.filterEffect((tuple) => { + if (predicate(tuple[0])) { + return pipe(Effect.succeed(tuple), Effect.as(true)) + } + return pipe(Queue.shutdown(tuple[1]), Effect.as(false)) + }) + ) + )) + +/** @internal */ +export const first = dual< + (n: number) => (self: GroupBy.GroupBy) => GroupBy.GroupBy, + (self: GroupBy.GroupBy, n: number) => GroupBy.GroupBy +>(2, (self: GroupBy.GroupBy, n: number): GroupBy.GroupBy => + make( + pipe( + stream.zipWithIndex(self.grouped), + stream.filterEffect((tuple) => { + const index = tuple[1] + const queue = tuple[0][1] + if (index < n) { + return pipe(Effect.succeed(tuple), Effect.as(true)) + } + return pipe(Queue.shutdown(queue), Effect.as(false)) + }), + stream.map((tuple) => tuple[0]) + ) + )) + +/** @internal */ +export const make = ( + grouped: Stream.Stream>], E, R> +): GroupBy.GroupBy => ({ + [GroupByTypeId]: groupByVariance, + pipe() { + return pipeArguments(this, arguments) + }, + grouped +}) + +// Circular with Stream + +/** @internal */ +export const groupBy = dual< + ( + f: (a: A) => Effect.Effect, + options?: { + readonly bufferSize?: number | undefined + } + ) => (self: Stream.Stream) => GroupBy.GroupBy, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect, + options?: { + readonly bufferSize?: number | undefined + } + ) => GroupBy.GroupBy +>( + (args) => stream.isStream(args[0]), + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect, + options?: { + readonly bufferSize?: number | undefined + } + ): GroupBy.GroupBy => + make( + stream.unwrapScoped( + Effect.gen(function*() { + const decider = yield* Deferred.make<(key: K, value: V) => Effect.Effect>>() + const output = yield* Effect.acquireRelease( + Queue.bounded>], Option.Option>>( + options?.bufferSize ?? 16 + ), + (queue) => Queue.shutdown(queue) + ) + const ref = yield* Ref.make>(new Map()) + const add = yield* pipe( + stream.mapEffectSequential(self, f), + stream.distributedWithDynamicCallback( + options?.bufferSize ?? 16, + ([key, value]) => Effect.flatMap(Deferred.await(decider), (f) => f(key, value)), + (exit) => Queue.offer(output, exit) + ) + ) + yield* Deferred.succeed(decider, (key, _) => + pipe( + Ref.get(ref), + Effect.map((map) => Option.fromNullable(map.get(key))), + Effect.flatMap(Option.match({ + onNone: () => + Effect.flatMap(add, ([index, queue]) => + Effect.zipRight( + Ref.update(ref, (map) => map.set(key, index)), + pipe( + Queue.offer( + output, + Exit.succeed( + [ + key, + mapDequeue(queue, (exit) => + new take.TakeImpl(pipe( + exit, + Exit.map((tuple) => Chunk.of(tuple[1])) + ))) + ] as const + ) + ), + Effect.as>((n: number) => n === index) + ) + )), + onSome: (index) => Effect.succeed>((n: number) => n === index) + })) + )) + return stream.flattenExitOption(stream.fromQueue(output, { shutdown: true })) + }) + ) + ) +) + +/** @internal */ +export const mapEffectOptions = dual< + { + ( + f: (a: A) => Effect.Effect, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly unordered?: boolean | undefined + } + ): (self: Stream.Stream) => Stream.Stream + ( + f: (a: A) => Effect.Effect, + options: { + readonly key: (a: A) => K + readonly bufferSize?: number | undefined + } + ): (self: Stream.Stream) => Stream.Stream + }, + { + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly unordered?: boolean | undefined + } + ): Stream.Stream + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect, + options: { + readonly key: (a: A) => K + readonly bufferSize?: number | undefined + } + ): Stream.Stream + } +>( + (args) => typeof args[0] !== "function", + (( + self: Stream.Stream, + f: (a: A) => Effect.Effect, + options?: { + readonly key?: ((a: A) => K) | undefined + readonly concurrency?: number | "unbounded" | undefined + readonly unordered?: boolean | undefined + readonly bufferSize?: number | undefined + } + ): Stream.Stream => { + if (options?.key) { + return evaluate( + groupByKey(self, options.key, { bufferSize: options.bufferSize }), + (_, s) => stream.mapEffectSequential(s, f) + ) + } + + return stream.matchConcurrency( + options?.concurrency, + () => stream.mapEffectSequential(self, f), + (n) => + options?.unordered ? + stream.flatMap(self, (a) => stream.fromEffect(f(a)), { concurrency: n }) : + stream.mapEffectPar(self, n, f) + ) + }) as any +) + +/** @internal */ +export const bindEffect = dual< + ( + tag: Exclude, + f: (_: NoInfer) => Effect.Effect, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly bufferSize?: number | undefined + } + ) => (self: Stream.Stream) => Stream.Stream< + { [K in keyof A | N]: K extends keyof A ? A[K] : B }, + E | E2, + R | R2 + >, + ( + self: Stream.Stream, + tag: Exclude, + f: (_: NoInfer) => Effect.Effect, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly unordered?: boolean | undefined + } + ) => Stream.Stream< + { [K in keyof A | N]: K extends keyof A ? A[K] : B }, + E | E2, + R | R2 + > +>((args) => typeof args[0] !== "string", ( + self: Stream.Stream, + tag: Exclude, + f: (_: A) => Effect.Effect, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly unordered?: boolean | undefined + } +) => + mapEffectOptions(self, (k) => + Effect.map( + f(k), + (a) => ({ ...k, [tag]: a } as { [K in keyof A | N]: K extends keyof A ? A[K] : B }) + ), options)) + +const mapDequeue = (dequeue: Queue.Dequeue, f: (a: A) => B): Queue.Dequeue => new MapDequeue(dequeue, f) + +class MapDequeue extends Effectable.Class implements Queue.Dequeue { + readonly [Queue.DequeueTypeId] = { + _Out: (_: never) => _ + } + + constructor( + readonly dequeue: Queue.Dequeue, + readonly f: (a: A) => B + ) { + super() + } + + capacity(): number { + return Queue.capacity(this.dequeue) + } + + get size(): Effect.Effect { + return Queue.size(this.dequeue) + } + + unsafeSize(): Option.Option { + return this.dequeue.unsafeSize() + } + + get awaitShutdown(): Effect.Effect { + return Queue.awaitShutdown(this.dequeue) + } + + isActive(): boolean { + return this.dequeue.isActive() + } + + get isShutdown(): Effect.Effect { + return Queue.isShutdown(this.dequeue) + } + + get shutdown(): Effect.Effect { + return Queue.shutdown(this.dequeue) + } + + get isFull(): Effect.Effect { + return Queue.isFull(this.dequeue) + } + + get isEmpty(): Effect.Effect { + return Queue.isEmpty(this.dequeue) + } + + get take(): Effect.Effect { + return pipe(Queue.take(this.dequeue), Effect.map((a) => this.f(a))) + } + + get takeAll(): Effect.Effect> { + return pipe(Queue.takeAll(this.dequeue), Effect.map(Chunk.map((a) => this.f(a)))) + } + + takeUpTo(max: number): Effect.Effect> { + return pipe(Queue.takeUpTo(this.dequeue, max), Effect.map(Chunk.map((a) => this.f(a)))) + } + + takeBetween(min: number, max: number): Effect.Effect> { + return pipe(Queue.takeBetween(this.dequeue, min, max), Effect.map(Chunk.map((a) => this.f(a)))) + } + + takeN(n: number): Effect.Effect> { + return pipe(Queue.takeN(this.dequeue, n), Effect.map(Chunk.map((a) => this.f(a)))) + } + + poll(): Effect.Effect> { + return pipe(Queue.poll(this.dequeue), Effect.map(Option.map((a) => this.f(a)))) + } + + pipe() { + return pipeArguments(this, arguments) + } + + commit() { + return this.take + } +} + +/** @internal */ +export const groupByKey = dual< + ( + f: (a: A) => K, + options?: { + readonly bufferSize?: number | undefined + } + ) => (self: Stream.Stream) => GroupBy.GroupBy, + ( + self: Stream.Stream, + f: (a: A) => K, + options?: { + readonly bufferSize?: number | undefined + } + ) => GroupBy.GroupBy +>( + (args) => typeof args[0] !== "function", + ( + self: Stream.Stream, + f: (a: A) => K, + options?: { + readonly bufferSize?: number | undefined + } + ): GroupBy.GroupBy => { + const loop = ( + map: Map>>, + outerQueue: Queue.Queue>], E>> + ): Channel.Channel, E, E, unknown, unknown, R> => + core.readWithCause({ + onInput: (input: Chunk.Chunk) => + core.flatMap( + core.fromEffect( + Effect.forEach(groupByIterable(input, f), ([key, values]) => { + const innerQueue = map.get(key) + if (innerQueue === undefined) { + return pipe( + Queue.bounded>(options?.bufferSize ?? 16), + Effect.flatMap((innerQueue) => + pipe( + Effect.sync(() => { + map.set(key, innerQueue) + }), + Effect.zipRight( + Queue.offer(outerQueue, take.of([key, innerQueue] as const)) + ), + Effect.zipRight( + pipe( + Queue.offer(innerQueue, take.chunk(values)), + Effect.catchSomeCause((cause) => + Cause.isInterruptedOnly(cause) ? + Option.some(Effect.void) : + Option.none() + ) + ) + ) + ) + ) + ) + } + return Effect.catchSomeCause( + Queue.offer(innerQueue, take.chunk(values)), + (cause) => + Cause.isInterruptedOnly(cause) ? + Option.some(Effect.void) : + Option.none() + ) + }, { discard: true }) + ), + () => loop(map, outerQueue) + ), + onFailure: (cause) => core.fromEffect(Queue.offer(outerQueue, take.failCause(cause))), + onDone: () => + core.fromEffect( + pipe( + Effect.forEach(map.entries(), ([_, innerQueue]) => + pipe( + Queue.offer(innerQueue, take.end), + Effect.catchSomeCause((cause) => + Cause.isInterruptedOnly(cause) ? + Option.some(Effect.void) : + Option.none() + ) + ), { discard: true }), + Effect.zipRight(Queue.offer(outerQueue, take.end)) + ) + ) + }) + return make(stream.unwrapScopedWith((scope) => + Effect.gen(function*() { + const map = new Map>>() + const queue = yield* Queue.unbounded>], E>>() + yield* Scope.addFinalizer(scope, Queue.shutdown(queue)) + return yield* stream.toChannel(self).pipe( + core.pipeTo(loop(map, queue)), + channel.drain, + channelExecutor.runIn(scope), + Effect.forkIn(scope), + Effect.as(stream.flattenTake(stream.fromQueue(queue, { shutdown: true }))) + ) + }) + )) + } +) + +/** + * A variant of `groupBy` that retains the insertion order of keys. + * + * @internal + */ +const groupByIterable = dual< + (f: (value: V) => K) => (iterable: Iterable) => Chunk.Chunk<[K, Chunk.Chunk]>, + (iterable: Iterable, f: (value: V) => K) => Chunk.Chunk<[K, Chunk.Chunk]> +>(2, (iterable: Iterable, f: (value: V) => K): Chunk.Chunk<[K, Chunk.Chunk]> => { + const builder: Array<[K, Array]> = [] + const iterator = iterable[Symbol.iterator]() + const map = new Map>() + let next: IteratorResult + while ((next = iterator.next()) && !next.done) { + const value = next.value + const key = f(value) + if (map.has(key)) { + const innerBuilder = map.get(key)! + innerBuilder.push(value) + } else { + const innerBuilder: Array = [value] + builder.push([key, innerBuilder]) + map.set(key, innerBuilder) + } + } + return Chunk.unsafeFromArray( + builder.map((tuple) => [tuple[0], Chunk.unsafeFromArray(tuple[1])]) + ) +}) diff --git a/backend/node_modules/effect/src/internal/hashMap/array.ts b/backend/node_modules/effect/src/internal/hashMap/array.ts new file mode 100644 index 0000000000000000000000000000000000000000..45b3e4071c1e39651a8c27aec877076c4898982e --- /dev/null +++ b/backend/node_modules/effect/src/internal/hashMap/array.ts @@ -0,0 +1,49 @@ +/** @internal */ +export function arrayUpdate(mutate: boolean, at: number, v: A, arr: Array) { + let out = arr + if (!mutate) { + const len = arr.length + out = new Array(len) + for (let i = 0; i < len; ++i) out[i] = arr[i]! + } + out[at] = v + return out +} + +/** @internal */ +export function arraySpliceOut(mutate: boolean, at: number, arr: Array) { + const newLen = arr.length - 1 + let i = 0 + let g = 0 + let out = arr + if (mutate) { + i = g = at + } else { + out = new Array(newLen) + while (i < at) out[g++] = arr[i++]! + } + ++i + while (i <= newLen) out[g++] = arr[i++]! + if (mutate) { + out.length = newLen + } + return out +} + +/** @internal */ +export function arraySpliceIn(mutate: boolean, at: number, v: A, arr: Array) { + const len = arr.length + if (mutate) { + let i = len + while (i >= at) arr[i--] = arr[i]! + arr[at] = v + return arr + } + let i = 0, + g = 0 + const out = new Array(len + 1) + while (i < at) out[g++] = arr[i++]! + out[at] = v + while (i < len) out[++g] = arr[i++]! + return out +} diff --git a/backend/node_modules/effect/src/internal/hashMap/bitwise.ts b/backend/node_modules/effect/src/internal/hashMap/bitwise.ts new file mode 100644 index 0000000000000000000000000000000000000000..1381dcddaaa93408627c1e504bdf08f613255e3a --- /dev/null +++ b/backend/node_modules/effect/src/internal/hashMap/bitwise.ts @@ -0,0 +1,32 @@ +import { MASK } from "./config.js" + +/** + * Hamming weight. + * + * Taken from: http://jsperf.com/hamming-weight + * + * @internal + */ +export function popcount(x: number) { + x -= (x >> 1) & 0x55555555 + x = (x & 0x33333333) + ((x >> 2) & 0x33333333) + x = (x + (x >> 4)) & 0x0f0f0f0f + x += x >> 8 + x += x >> 16 + return x & 0x7f +} + +/** @internal */ +export function hashFragment(shift: number, h: number) { + return (h >>> shift) & MASK +} + +/** @internal */ +export function toBitmap(x: number) { + return 1 << x +} + +/** @internal */ +export function fromBitmap(bitmap: number, bit: number) { + return popcount(bitmap & (bit - 1)) +} diff --git a/backend/node_modules/effect/src/internal/hashMap/config.ts b/backend/node_modules/effect/src/internal/hashMap/config.ts new file mode 100644 index 0000000000000000000000000000000000000000..20f0940ed323e05e3f099ef876dfb9cd6fa5296a --- /dev/null +++ b/backend/node_modules/effect/src/internal/hashMap/config.ts @@ -0,0 +1,14 @@ +/** @internal */ +export const SIZE = 5 + +/** @internal */ +export const BUCKET_SIZE = Math.pow(2, SIZE) + +/** @internal */ +export const MASK = BUCKET_SIZE - 1 + +/** @internal */ +export const MAX_INDEX_NODE = BUCKET_SIZE / 2 + +/** @internal */ +export const MIN_ARRAY_NODE = BUCKET_SIZE / 4 diff --git a/backend/node_modules/effect/src/internal/hashMap/keySet.ts b/backend/node_modules/effect/src/internal/hashMap/keySet.ts new file mode 100644 index 0000000000000000000000000000000000000000..f49e132332f1fd7da04f83c10c343cca7128e032 --- /dev/null +++ b/backend/node_modules/effect/src/internal/hashMap/keySet.ts @@ -0,0 +1,8 @@ +import type { HashMap } from "../../HashMap.js" +import type { HashSet } from "../../HashSet.js" +import { makeImpl } from "../hashSet.js" + +/** @internal */ +export function keySet(self: HashMap): HashSet { + return makeImpl(self) +} diff --git a/backend/node_modules/effect/src/internal/hashMap/node.ts b/backend/node_modules/effect/src/internal/hashMap/node.ts new file mode 100644 index 0000000000000000000000000000000000000000..681ca99548e3edea0cd92fed0633c9f2c799a4d6 --- /dev/null +++ b/backend/node_modules/effect/src/internal/hashMap/node.ts @@ -0,0 +1,391 @@ +import { equals } from "../../Equal.js" +import type { HashMap } from "../../HashMap.js" +import * as O from "../../Option.js" +import { isTagged } from "../../Predicate.js" +import * as Stack from "../stack.js" +import { arraySpliceIn, arraySpliceOut, arrayUpdate } from "./array.js" +import { fromBitmap, hashFragment, toBitmap } from "./bitwise.js" +import { MAX_INDEX_NODE, MIN_ARRAY_NODE, SIZE } from "./config.js" + +/** @internal */ +export type Node = + | EmptyNode + | LeafNode + | CollisionNode + | IndexedNode + | ArrayNode + +/** @internal */ +export interface SizeRef { + value: number // mutable by design +} + +/** @internal */ +export class EmptyNode { + readonly _tag = "EmptyNode" + + modify( + edit: number, + _shift: number, + f: HashMap.UpdateFn, + hash: number, + key: K, + size: SizeRef + ): Node { + const v = f(O.none()) + if (O.isNone(v)) return new EmptyNode() + ++size.value + return new LeafNode(edit, hash, key, v) + } +} + +/** @internal */ +export function isEmptyNode(a: unknown): a is EmptyNode { + return isTagged(a, "EmptyNode") +} + +/** @internal */ +export function isLeafNode( + node: Node +): node is EmptyNode | LeafNode | CollisionNode { + return isEmptyNode(node) || node._tag === "LeafNode" || node._tag === "CollisionNode" +} + +/** @internal */ +export function canEditNode(node: Node, edit: number): boolean { + return isEmptyNode(node) ? false : edit === node.edit +} + +/** @internal */ +export class LeafNode { + readonly _tag = "LeafNode" + + constructor( + readonly edit: number, + readonly hash: number, + readonly key: K, + public value: O.Option + ) {} + + modify( + edit: number, + shift: number, + f: HashMap.UpdateFn, + hash: number, + key: K, + size: SizeRef + ): Node { + if (equals(key, this.key)) { + const v = f(this.value) + if (v === this.value) return this + else if (O.isNone(v)) { + --size.value + return new EmptyNode() + } + if (canEditNode(this, edit)) { + this.value = v + return this + } + return new LeafNode(edit, hash, key, v) + } + const v = f(O.none()) + if (O.isNone(v)) return this + ++size.value + return mergeLeaves( + edit, + shift, + this.hash, + this, + hash, + new LeafNode(edit, hash, key, v) + ) + } +} + +/** @internal */ +export class CollisionNode { + readonly _tag = "CollisionNode" + + constructor( + readonly edit: number, + readonly hash: number, + readonly children: Array> + ) {} + + modify( + edit: number, + shift: number, + f: HashMap.UpdateFn, + hash: number, + key: K, + size: SizeRef + ): Node { + if (hash === this.hash) { + const canEdit = canEditNode(this, edit) + const list = this.updateCollisionList( + canEdit, + edit, + this.hash, + this.children, + f, + key, + size + ) + if (list === this.children) return this + + return list.length > 1 ? new CollisionNode(edit, this.hash, list) : list[0]! // collapse single element collision list + } + const v = f(O.none()) + if (O.isNone(v)) return this + ++size.value + return mergeLeaves( + edit, + shift, + this.hash, + this, + hash, + new LeafNode(edit, hash, key, v) + ) + } + + updateCollisionList( + mutate: boolean, + edit: number, + hash: number, + list: Array>, + f: HashMap.UpdateFn, + key: K, + size: SizeRef + ) { + const len = list.length + for (let i = 0; i < len; ++i) { + const child = list[i]! + if ("key" in child && equals(key, child.key)) { + const value = child.value + const newValue = f(value) + if (newValue === value) return list + if (O.isNone(newValue)) { + --size.value + return arraySpliceOut(mutate, i, list) + } + return arrayUpdate(mutate, i, new LeafNode(edit, hash, key, newValue), list) + } + } + + const newValue = f(O.none()) + if (O.isNone(newValue)) return list + ++size.value + return arrayUpdate(mutate, len, new LeafNode(edit, hash, key, newValue), list) + } +} + +/** @internal */ +export class IndexedNode { + readonly _tag = "IndexedNode" + + constructor( + readonly edit: number, + public mask: number, + public children: Array> + ) {} + + modify( + edit: number, + shift: number, + f: HashMap.UpdateFn, + hash: number, + key: K, + size: SizeRef + ): Node { + const mask = this.mask + const children = this.children + const frag = hashFragment(shift, hash) + const bit = toBitmap(frag) + const indx = fromBitmap(mask, bit) + const exists = mask & bit + const canEdit = canEditNode(this, edit) + + if (!exists) { + const _newChild = new EmptyNode().modify(edit, shift + SIZE, f, hash, key, size) + if (!_newChild) return this + return children.length >= MAX_INDEX_NODE ? + expand(edit, frag, _newChild, mask, children) : + new IndexedNode(edit, mask | bit, arraySpliceIn(canEdit, indx, _newChild, children)) + } + + const current = children[indx]! + const child = current.modify(edit, shift + SIZE, f, hash, key, size) + + if (current === child) return this + let bitmap = mask + let newChildren + if (isEmptyNode(child)) { + // remove + bitmap &= ~bit + if (!bitmap) return new EmptyNode() + if (children.length <= 2 && isLeafNode(children[indx ^ 1]!)) { + return children[indx ^ 1]! // collapse + } + + newChildren = arraySpliceOut(canEdit, indx, children) + } else { + // modify + newChildren = arrayUpdate(canEdit, indx, child, children) + } + + if (canEdit) { + this.mask = bitmap + this.children = newChildren + return this + } + + return new IndexedNode(edit, bitmap, newChildren) + } +} + +/** @internal */ +export class ArrayNode { + readonly _tag = "ArrayNode" + + constructor( + readonly edit: number, + public size: number, + public children: Array> + ) {} + + modify( + edit: number, + shift: number, + f: HashMap.UpdateFn, + hash: number, + key: K, + size: SizeRef + ): Node { + let count = this.size + const children = this.children + const frag = hashFragment(shift, hash) + const child = children[frag] + const newChild = (child || new EmptyNode()).modify( + edit, + shift + SIZE, + f, + hash, + key, + size + ) + + if (child === newChild) return this + + const canEdit = canEditNode(this, edit) + let newChildren + if (isEmptyNode(child) && !isEmptyNode(newChild)) { + // add + ++count + newChildren = arrayUpdate(canEdit, frag, newChild, children) + } else if (!isEmptyNode(child) && isEmptyNode(newChild)) { + // remove + --count + if (count <= MIN_ARRAY_NODE) { + return pack(edit, count, frag, children) + } + newChildren = arrayUpdate(canEdit, frag, new EmptyNode(), children) + } else { + // modify + newChildren = arrayUpdate(canEdit, frag, newChild, children) + } + + if (canEdit) { + this.size = count + this.children = newChildren + return this + } + return new ArrayNode(edit, count, newChildren) + } +} + +function pack( + edit: number, + count: number, + removed: number, + elements: Array> +) { + const children = new Array>(count - 1) + let g = 0 + let bitmap = 0 + for (let i = 0, len = elements.length; i < len; ++i) { + if (i !== removed) { + const elem = elements[i] + if (elem && !isEmptyNode(elem)) { + children[g++] = elem + bitmap |= 1 << i + } + } + } + return new IndexedNode(edit, bitmap, children) +} + +function expand( + edit: number, + frag: number, + child: Node, + bitmap: number, + subNodes: Array> +) { + const arr = [] + let bit = bitmap + let count = 0 + for (let i = 0; bit; ++i) { + if (bit & 1) arr[i] = subNodes[count++]! + bit >>>= 1 + } + arr[frag] = child + return new ArrayNode(edit, count + 1, arr) +} + +function mergeLeavesInner( + edit: number, + shift: number, + h1: number, + n1: Node, + h2: number, + n2: Node +): Node | ((child: Node) => Node) { + if (h1 === h2) return new CollisionNode(edit, h1, [n2, n1]) + const subH1 = hashFragment(shift, h1) + const subH2 = hashFragment(shift, h2) + + if (subH1 === subH2) { + return (child) => new IndexedNode(edit, toBitmap(subH1) | toBitmap(subH2), [child]) + } else { + const children = subH1 < subH2 ? [n1, n2] : [n2, n1] + return new IndexedNode(edit, toBitmap(subH1) | toBitmap(subH2), children) + } +} + +function mergeLeaves( + edit: number, + shift: number, + h1: number, + n1: Node, + h2: number, + n2: Node +): Node { + let stack: Stack.Stack<(node: Node) => Node> | undefined = undefined + let currentShift = shift + + while (true) { + const res = mergeLeavesInner(edit, currentShift, h1, n1, h2, n2) + + if (typeof res === "function") { + stack = Stack.make(res, stack) + currentShift = currentShift + SIZE + } else { + let final = res + while (stack != null) { + final = stack.value(final) + stack = stack.previous + } + return final + } + } +} diff --git a/backend/node_modules/effect/src/internal/hashSet.ts b/backend/node_modules/effect/src/internal/hashSet.ts new file mode 100644 index 0000000000000000000000000000000000000000..a60b87d075884039493723bcc2ced094d364e5bf --- /dev/null +++ b/backend/node_modules/effect/src/internal/hashSet.ts @@ -0,0 +1,323 @@ +import * as Equal from "../Equal.js" +import { dual } from "../Function.js" +import * as Hash from "../Hash.js" +import type { HashMap } from "../HashMap.js" +import type * as HS from "../HashSet.js" +import { format, NodeInspectSymbol, toJSON } from "../Inspectable.js" +import { pipeArguments } from "../Pipeable.js" +import type { Predicate, Refinement } from "../Predicate.js" +import { hasProperty } from "../Predicate.js" +import type { NoInfer } from "../Types.js" +import * as HM from "./hashMap.js" + +const HashSetSymbolKey = "effect/HashSet" + +/** @internal */ +export const HashSetTypeId: HS.TypeId = Symbol.for(HashSetSymbolKey) as HS.TypeId + +/** @internal */ +export interface HashSetImpl extends HS.HashSet { + readonly _keyMap: HashMap +} + +const HashSetProto: Omit, "_keyMap"> = { + [HashSetTypeId]: HashSetTypeId, + [Symbol.iterator](this: HashSetImpl): Iterator { + return HM.keys(this._keyMap) + }, + [Hash.symbol](this: HashSetImpl): number { + return Hash.cached( + this, + Hash.combine(Hash.hash(this._keyMap))(Hash.hash(HashSetSymbolKey)) + ) + }, + [Equal.symbol](this: HashSetImpl, that: unknown): boolean { + if (isHashSet(that)) { + return ( + HM.size(this._keyMap) === HM.size((that as HashSetImpl)._keyMap) && + Equal.equals(this._keyMap, (that as HashSetImpl)._keyMap) + ) + } + return false + }, + toString() { + return format(this.toJSON()) + }, + toJSON() { + return { + _id: "HashSet", + values: Array.from(this).map(toJSON) + } + }, + [NodeInspectSymbol]() { + return this.toJSON() + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const makeImpl = (keyMap: HashMap): HashSetImpl => { + const set = Object.create(HashSetProto) + set._keyMap = keyMap + return set +} + +/** @internal */ +export const isHashSet: { + (u: Iterable): u is HS.HashSet + (u: unknown): u is HS.HashSet +} = (u: unknown): u is HS.HashSet => hasProperty(u, HashSetTypeId) + +const _empty = makeImpl(HM.empty()) + +/** @internal */ +export const empty = (): HS.HashSet => _empty + +/** @internal */ +export const fromIterable = (elements: Iterable): HS.HashSet => { + const set = beginMutation(empty()) + for (const value of elements) { + add(set, value) + } + return endMutation(set) +} + +/** @internal */ +export const make = >(...elements: As): HS.HashSet => { + const set = beginMutation(empty()) + for (const value of elements) { + add(set, value) + } + return endMutation(set) +} + +/** @internal */ +export const has = dual< + (value: A) => (self: HS.HashSet) => boolean, + (self: HS.HashSet, value: A) => boolean +>(2, (self: HS.HashSet, value: A) => HM.has((self as HashSetImpl)._keyMap, value)) + +/** @internal */ +export const some = dual< + (f: Predicate) => (self: HS.HashSet) => boolean, + (self: HS.HashSet, f: Predicate) => boolean +>(2, (self, f) => { + let found = false + for (const value of self) { + found = f(value) + if (found) { + break + } + } + return found +}) + +/** @internal */ +export const every: { + (refinement: Refinement, B>): (self: HS.HashSet) => self is HS.HashSet + (predicate: Predicate): (self: HS.HashSet) => boolean + (self: HS.HashSet, refinement: Refinement): self is HS.HashSet + (self: HS.HashSet, predicate: Predicate): boolean +} = dual( + 2, + (self: HS.HashSet, refinement: Refinement): self is HS.HashSet => + !some(self, (a) => !refinement(a)) +) + +/** @internal */ +export const isSubset = dual< + (that: HS.HashSet) => (self: HS.HashSet) => boolean, + (self: HS.HashSet, that: HS.HashSet) => boolean +>(2, (self, that) => every(self, (value) => has(that, value))) + +/** @internal */ +export const values = (self: HS.HashSet): IterableIterator => HM.keys((self as HashSetImpl)._keyMap) + +/** @internal */ +export const size = (self: HS.HashSet): number => HM.size((self as HashSetImpl)._keyMap) + +/** @internal */ +export const beginMutation = (self: HS.HashSet): HS.HashSet => + makeImpl(HM.beginMutation((self as HashSetImpl)._keyMap)) + +/** @internal */ +export const endMutation = (self: HS.HashSet): HS.HashSet => { + ;((self as HashSetImpl)._keyMap as HM.HashMapImpl)._editable = false + return self +} + +/** @internal */ +export const mutate = dual< + (f: (set: HS.HashSet) => void) => (self: HS.HashSet) => HS.HashSet, + (self: HS.HashSet, f: (set: HS.HashSet) => void) => HS.HashSet +>(2, (self, f) => { + const transient = beginMutation(self) + f(transient) + return endMutation(transient) +}) + +/** @internal */ +export const add = dual< + (value: A) => (self: HS.HashSet) => HS.HashSet, + (self: HS.HashSet, value: A) => HS.HashSet +>( + 2, + (self: HS.HashSet, value: A) => + ((self as HashSetImpl)._keyMap as HM.HashMapImpl)._editable + ? (HM.set(value as A, true as unknown)((self as HashSetImpl)._keyMap), self) + : makeImpl(HM.set(value as A, true as unknown)((self as HashSetImpl)._keyMap)) +) + +/** @internal */ +export const remove = dual< + (value: A) => (self: HS.HashSet) => HS.HashSet, + (self: HS.HashSet, value: A) => HS.HashSet +>( + 2, + (self: HS.HashSet, value: A) => + (((self as HashSetImpl)._keyMap) as HM.HashMapImpl)._editable + ? (HM.remove(value)((self as HashSetImpl)._keyMap), self) + : makeImpl(HM.remove(value)((self as HashSetImpl)._keyMap)) +) + +/** @internal */ +export const difference = dual< + (that: Iterable) => (self: HS.HashSet) => HS.HashSet, + (self: HS.HashSet, that: Iterable) => HS.HashSet +>(2, (self, that) => + mutate(self, (set) => { + for (const value of that) { + remove(set, value) + } + })) + +/** @internal */ +export const intersection = dual< + (that: Iterable) => (self: HS.HashSet) => HS.HashSet, + (self: HS.HashSet, that: Iterable) => HS.HashSet +>(2, (self, that) => + mutate(empty(), (set) => { + for (const value of that) { + if (has(value)(self)) { + add(value)(set) + } + } + })) + +/** @internal */ +export const union = dual< + (that: Iterable) => (self: HS.HashSet) => HS.HashSet, + (self: HS.HashSet, that: Iterable) => HS.HashSet +>(2, (self, that) => + mutate(empty(), (set) => { + forEach(self, (value) => add(set, value)) + for (const value of that) { + add(set, value) + } + })) + +/** @internal */ +export const toggle = dual< + (value: A) => (self: HS.HashSet) => HS.HashSet, + (self: HS.HashSet, value: A) => HS.HashSet +>(2, (self, value) => has(self, value) ? remove(self, value) : add(self, value)) + +/** @internal */ +export const map = dual< + (f: (a: A) => B) => (self: HS.HashSet) => HS.HashSet, + (self: HS.HashSet, f: (a: A) => B) => HS.HashSet +>(2, (self, f) => + mutate(empty(), (set) => { + forEach(self, (a) => { + const b = f(a) + if (!has(set, b)) { + add(set, b) + } + }) + })) + +/** @internal */ +export const flatMap = dual< + (f: (a: A) => Iterable) => (self: HS.HashSet) => HS.HashSet, + (self: HS.HashSet, f: (a: A) => Iterable) => HS.HashSet +>(2, (self, f) => + mutate(empty(), (set) => { + forEach(self, (a) => { + for (const b of f(a)) { + if (!has(set, b)) { + add(set, b) + } + } + }) + })) + +/** @internal */ +export const forEach = dual< + (f: (value: A) => void) => (self: HS.HashSet) => void, + (self: HS.HashSet, f: (value: A) => void) => void +>(2, (self: HS.HashSet, f: (value: A) => void) => + HM.forEach( + (self as HashSetImpl)._keyMap, + (_, k) => f(k) + )) + +/** @internal */ +export const reduce = dual< + (zero: Z, f: (accumulator: Z, value: A) => Z) => (self: HS.HashSet) => Z, + (self: HS.HashSet, zero: Z, f: (accumulator: Z, value: A) => Z) => Z +>(3, (self: HS.HashSet, zero: Z, f: (accumulator: Z, value: A) => Z) => + HM.reduce( + (self as HashSetImpl)._keyMap, + zero, + (z, _, a) => f(z, a) + )) + +/** @internal */ +export const filter: { + (refinement: Refinement, B>): (self: HS.HashSet) => HS.HashSet + (predicate: Predicate>): (self: HS.HashSet) => HS.HashSet + (self: HS.HashSet, refinement: Refinement): HS.HashSet + (self: HS.HashSet, predicate: Predicate): HS.HashSet +} = dual(2, (self: HS.HashSet, f: Predicate) => { + return mutate(empty(), (set) => { + const iterator = values(self) + let next: IteratorResult + while (!(next = iterator.next()).done) { + const value = next.value + if (f(value)) { + add(set, value) + } + } + }) +}) + +/** @internal */ +export const partition: { + ( + refinement: Refinement, B> + ): (self: HS.HashSet) => [excluded: HS.HashSet>, satisfying: HS.HashSet] + ( + predicate: Predicate> + ): (self: HS.HashSet) => [excluded: HS.HashSet, satisfying: HS.HashSet] + ( + self: HS.HashSet, + refinement: Refinement + ): [excluded: HS.HashSet>, satisfying: HS.HashSet] + (self: HS.HashSet, predicate: Predicate): [excluded: HS.HashSet, satisfying: HS.HashSet] +} = dual(2, (self: HS.HashSet, predicate: Predicate): [excluded: HS.HashSet, satisfying: HS.HashSet] => { + const iterator = values(self) + let next: IteratorResult + const right = beginMutation(empty()) + const left = beginMutation(empty()) + while (!(next = iterator.next()).done) { + const value = next.value + if (predicate(value)) { + add(right, value) + } else { + add(left, value) + } + } + return [endMutation(left), endMutation(right)] +}) diff --git a/backend/node_modules/effect/src/internal/keyedPool.ts b/backend/node_modules/effect/src/internal/keyedPool.ts new file mode 100644 index 0000000000000000000000000000000000000000..b91988b077157d76442fc0fc5a0e45869968116e --- /dev/null +++ b/backend/node_modules/effect/src/internal/keyedPool.ts @@ -0,0 +1,244 @@ +import type * as Deferred from "../Deferred.js" +import * as Duration from "../Duration.js" +import type * as Effect from "../Effect.js" +import * as Equal from "../Equal.js" +import { dual, pipe } from "../Function.js" +import * as Hash from "../Hash.js" +import * as HashMap from "../HashMap.js" +import type * as KeyedPool from "../KeyedPool.js" +import * as MutableRef from "../MutableRef.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import type * as Pool from "../Pool.js" +import * as Predicate from "../Predicate.js" +import type * as Scope from "../Scope.js" +import * as core from "./core.js" +import * as fiberRuntime from "./fiberRuntime.js" +import * as pool from "./pool.js" + +/** @internal */ +const KeyedPoolSymbolKey = "effect/KeyedPool" + +/** @internal */ +export const KeyedPoolTypeId: KeyedPool.KeyedPoolTypeId = Symbol.for( + KeyedPoolSymbolKey +) as KeyedPool.KeyedPoolTypeId + +const KeyedPoolMapValueSymbol = Symbol.for("effect/KeyedPool/MapValue") +type KeyedPoolMapValueSymbol = typeof KeyedPoolMapValueSymbol + +const keyedPoolVariance = { + /* c8 ignore next */ + _K: (_: unknown) => _, + /* c8 ignore next */ + _E: (_: never) => _, + /* c8 ignore next */ + _A: (_: any) => _ +} + +class KeyedPoolImpl implements KeyedPool.KeyedPool { + readonly [KeyedPoolTypeId] = keyedPoolVariance + constructor( + readonly getOrCreatePool: (key: K) => Effect.Effect>, + readonly activePools: Effect.Effect>> + ) {} + get(key: K): Effect.Effect { + return core.flatMap(this.getOrCreatePool(key), pool.get) + } + invalidate(item: A): Effect.Effect { + return core.flatMap(this.activePools, core.forEachSequentialDiscard((pool) => pool.invalidate(item))) + } + pipe() { + return pipeArguments(this, arguments) + } +} + +type MapValue = Complete | Pending + +class Complete implements Equal.Equal { + readonly _tag = "Complete" + readonly [KeyedPoolMapValueSymbol]: KeyedPoolMapValueSymbol = KeyedPoolMapValueSymbol + constructor(readonly pool: Pool.Pool) {} + [Hash.symbol](): number { + return pipe( + Hash.string("effect/KeyedPool/Complete"), + Hash.combine(Hash.hash(this.pool)), + Hash.cached(this) + ) + } + [Equal.symbol](u: unknown): boolean { + return isComplete(u) && Equal.equals(this.pool, u.pool) + } +} + +const isComplete = (u: unknown): u is Complete => + Predicate.isTagged(u, "Complete") && KeyedPoolMapValueSymbol in u + +class Pending implements Equal.Equal { + readonly _tag = "Pending" + readonly [KeyedPoolMapValueSymbol]: KeyedPoolMapValueSymbol = KeyedPoolMapValueSymbol + constructor(readonly deferred: Deferred.Deferred>) {} + [Hash.symbol](): number { + return pipe( + Hash.string("effect/KeyedPool/Pending"), + Hash.combine(Hash.hash(this.deferred)), + Hash.cached(this) + ) + } + [Equal.symbol](u: unknown): boolean { + return isPending(u) && Equal.equals(this.deferred, u.deferred) + } +} + +const isPending = (u: unknown): u is Pending => + Predicate.isTagged(u, "Pending") && KeyedPoolMapValueSymbol in u + +const makeImpl = ( + get: (key: K) => Effect.Effect, + min: (key: K) => number, + max: (key: K) => number, + timeToLive: (key: K) => Option.Option +): Effect.Effect, never, R | Scope.Scope> => + pipe( + fiberRuntime.all([ + core.context(), + core.fiberId, + core.sync(() => MutableRef.make(HashMap.empty>())), + fiberRuntime.scopeMake() + ]), + core.map(([context, fiberId, map, scope]) => { + const getOrCreatePool = (key: K): Effect.Effect> => + core.suspend(() => { + let value: MapValue | undefined = Option.getOrUndefined(HashMap.get(MutableRef.get(map), key)) + if (value === undefined) { + return core.uninterruptibleMask((restore) => { + const deferred = core.deferredUnsafeMake>(fiberId) + value = new Pending(deferred) + let previous: MapValue | undefined = undefined + if (HashMap.has(MutableRef.get(map), key)) { + previous = Option.getOrUndefined(HashMap.get(MutableRef.get(map), key)) + } else { + MutableRef.update(map, HashMap.set(key, value as MapValue)) + } + if (previous === undefined) { + return pipe( + restore( + fiberRuntime.scopeExtend( + pool.makeWithTTL({ + acquire: core.provideContext(get(key), context), + min: min(key), + max: max(key), + timeToLive: Option.getOrElse(timeToLive(key), () => Duration.infinity) + }), + scope + ) + ), + core.matchCauseEffect({ + onFailure: (cause) => { + const current = Option.getOrUndefined(HashMap.get(MutableRef.get(map), key)) + if (Equal.equals(current, value)) { + MutableRef.update(map, HashMap.remove(key)) + } + return core.zipRight( + core.deferredFailCause(deferred, cause), + core.failCause(cause) + ) + }, + onSuccess: (pool) => { + MutableRef.update(map, HashMap.set(key, new Complete(pool) as MapValue)) + return core.as( + core.deferredSucceed(deferred, pool), + pool + ) + } + }) + ) + } + switch (previous._tag) { + case "Complete": { + return core.succeed(previous.pool) + } + case "Pending": { + return restore(core.deferredAwait(previous.deferred)) + } + } + }) + } + switch (value._tag) { + case "Complete": { + return core.succeed(value.pool) + } + case "Pending": { + return core.deferredAwait(value.deferred) + } + } + }) + const activePools: Effect.Effect>> = core.suspend(() => + core.forEachSequential(HashMap.toValues(MutableRef.get(map)), (value) => { + switch (value._tag) { + case "Complete": { + return core.succeed(value.pool) + } + case "Pending": { + return core.deferredAwait(value.deferred) + } + } + }) + ) + return new KeyedPoolImpl(getOrCreatePool, activePools) + }) + ) + +/** @internal */ +export const make = ( + options: { + readonly acquire: (key: K) => Effect.Effect + readonly size: number + } +): Effect.Effect, never, R | Scope.Scope> => + makeImpl(options.acquire, () => options.size, () => options.size, () => Option.none()) + +/** @internal */ +export const makeWith = ( + options: { + readonly acquire: (key: K) => Effect.Effect + readonly size: (key: K) => number + } +): Effect.Effect, never, R | Scope.Scope> => + makeImpl(options.acquire, options.size, options.size, () => Option.none()) + +/** @internal */ +export const makeWithTTL = ( + options: { + readonly acquire: (key: K) => Effect.Effect + readonly min: (key: K) => number + readonly max: (key: K) => number + readonly timeToLive: Duration.DurationInput + } +): Effect.Effect, never, R | Scope.Scope> => { + const timeToLive = Duration.decode(options.timeToLive) + return makeImpl(options.acquire, options.min, options.max, () => Option.some(timeToLive)) +} + +/** @internal */ +export const makeWithTTLBy = ( + options: { + readonly acquire: (key: K) => Effect.Effect + readonly min: (key: K) => number + readonly max: (key: K) => number + readonly timeToLive: (key: K) => Duration.DurationInput + } +): Effect.Effect, never, R | Scope.Scope> => + makeImpl(options.acquire, options.min, options.max, (key) => Option.some(Duration.decode(options.timeToLive(key)))) + +/** @internal */ +export const get = dual< + (key: K) => (self: KeyedPool.KeyedPool) => Effect.Effect, + (self: KeyedPool.KeyedPool, key: K) => Effect.Effect +>(2, (self, key) => self.get(key)) + +/** @internal */ +export const invalidate = dual< + (item: A) => (self: KeyedPool.KeyedPool) => Effect.Effect, + (self: KeyedPool.KeyedPool, item: A) => Effect.Effect +>(2, (self, item) => self.invalidate(item)) diff --git a/backend/node_modules/effect/src/internal/layer.ts b/backend/node_modules/effect/src/internal/layer.ts new file mode 100644 index 0000000000000000000000000000000000000000..685766c32917ea1c534be1740d1372d056c708b4 --- /dev/null +++ b/backend/node_modules/effect/src/internal/layer.ts @@ -0,0 +1,1483 @@ +import type * as Arr from "../Array.js" +import * as Cause from "../Cause.js" +import * as Clock from "../Clock.js" +import * as Context from "../Context.js" +import * as Duration from "../Duration.js" +import type * as Effect from "../Effect.js" +import type * as Exit from "../Exit.js" +import type { FiberRef } from "../FiberRef.js" +import * as FiberRefsPatch from "../FiberRefsPatch.js" +import type { LazyArg } from "../Function.js" +import { constTrue, dual, pipe } from "../Function.js" +import * as HashMap from "../HashMap.js" +import type * as Layer from "../Layer.js" +import type * as ManagedRuntime from "../ManagedRuntime.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty } from "../Predicate.js" +import type * as Runtime from "../Runtime.js" +import type * as Schedule from "../Schedule.js" +import * as ScheduleDecision from "../ScheduleDecision.js" +import * as Intervals from "../ScheduleIntervals.js" +import * as Scope from "../Scope.js" +import type * as Synchronized from "../SynchronizedRef.js" +import type * as Tracer from "../Tracer.js" +import type * as Types from "../Types.js" +import * as effect from "./core-effect.js" +import * as core from "./core.js" +import * as circular from "./effect/circular.js" +import * as ExecutionStrategy from "./executionStrategy.js" +import * as fiberRuntime from "./fiberRuntime.js" +import * as circularManagedRuntime from "./managedRuntime/circular.js" +import * as EffectOpCodes from "./opCodes/effect.js" +import * as OpCodes from "./opCodes/layer.js" +import * as ref from "./ref.js" +import * as runtime from "./runtime.js" +import * as runtimeFlags from "./runtimeFlags.js" +import * as synchronized from "./synchronizedRef.js" +import * as tracer from "./tracer.js" + +/** @internal */ +const LayerSymbolKey = "effect/Layer" + +/** @internal */ +export const LayerTypeId: Layer.LayerTypeId = Symbol.for( + LayerSymbolKey +) as Layer.LayerTypeId + +const layerVariance = { + /* c8 ignore next */ + _RIn: (_: never) => _, + /* c8 ignore next */ + _E: (_: never) => _, + /* c8 ignore next */ + _ROut: (_: unknown) => _ +} + +/** @internal */ +export const proto = { + [LayerTypeId]: layerVariance, + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +const MemoMapTypeIdKey = "effect/Layer/MemoMap" + +/** @internal */ +export const MemoMapTypeId: Layer.MemoMapTypeId = Symbol.for( + MemoMapTypeIdKey +) as Layer.MemoMapTypeId + +/** @internal */ +export const CurrentMemoMap = Context.Reference()("effect/Layer/CurrentMemoMap", { + defaultValue: () => unsafeMakeMemoMap() +}) + +/** @internal */ +export type Primitive = + | ExtendScope + | Fold + | Fresh + | FromEffect + | Scoped + | Suspend + | Locally + | ProvideTo + | ZipWith + | ZipWithPar + | MergeAll + +/** @internal */ +export type Op = Layer.Layer & Body & { + readonly _op_layer: Tag +} + +/** @internal */ +export interface ExtendScope extends + Op + }> +{} + +/** @internal */ +export interface Fold extends + Op + failureK(cause: Cause.Cause): Layer.Layer + successK(context: Context.Context): Layer.Layer + }> +{} + +/** @internal */ +export interface Fresh extends + Op + }> +{} + +/** @internal */ +export interface FromEffect extends + Op> + }> +{} + +/** @internal */ +export interface Scoped extends + Op> + }> +{} + +/** @internal */ +export interface Suspend extends + Op + }> +{} + +/** @internal */ +export interface Locally extends + Op<"Locally", { + readonly self: Layer.Layer + f(_: Effect.Effect): Effect.Effect + }> +{} + +/** @internal */ +export interface ProvideTo extends + Op + readonly second: Layer.Layer + }> +{} + +/** @internal */ +export interface ZipWith extends + Op + readonly second: Layer.Layer + zipK(left: Context.Context, right: Context.Context): Context.Context + }> +{} + +/** @internal */ +export interface ZipWithPar extends + Op + readonly second: Layer.Layer + zipK(left: Context.Context, right: Context.Context): Context.Context + }> +{} + +/** @internal */ +export interface MergeAll extends + Op> + }> +{} + +/** @internal */ +export const isLayer = (u: unknown): u is Layer.Layer => hasProperty(u, LayerTypeId) + +/** @internal */ +export const isFresh = (self: Layer.Layer): boolean => { + return (self as Primitive)._op_layer === OpCodes.OP_FRESH +} + +// ----------------------------------------------------------------------------- +// MemoMap +// ----------------------------------------------------------------------------- + +/** @internal */ +class MemoMapImpl implements Layer.MemoMap { + readonly [MemoMapTypeId]: Layer.MemoMapTypeId + constructor( + readonly ref: Synchronized.SynchronizedRef< + Map< + Layer.Layer, + readonly [Effect.Effect, Scope.Scope.Finalizer] + > + > + ) { + this[MemoMapTypeId] = MemoMapTypeId + } + + /** + * Checks the memo map to see if a layer exists. If it is, immediately + * returns it. Otherwise, obtains the layer, stores it in the memo map, + * and adds a finalizer to the `Scope`. + */ + getOrElseMemoize( + layer: Layer.Layer, + scope: Scope.Scope + ): Effect.Effect, E, RIn> { + return pipe( + synchronized.modifyEffect(this.ref, (map) => { + const inMap = map.get(layer) + if (inMap !== undefined) { + const [acquire, release] = inMap + const cached: Effect.Effect, E> = pipe( + acquire as Effect.Effect], E>, + core.flatMap(([patch, b]) => pipe(effect.patchFiberRefs(patch), core.as(b))), + core.onExit(core.exitMatch({ + onFailure: () => core.void, + onSuccess: () => core.scopeAddFinalizerExit(scope, release) + })) + ) + return core.succeed([cached, map] as const) + } + return pipe( + ref.make(0), + core.flatMap((observers) => + pipe( + core.deferredMake], E>(), + core.flatMap((deferred) => + pipe( + ref.make(() => core.void), + core.map((finalizerRef) => { + const resource = core.uninterruptibleMask((restore) => + pipe( + fiberRuntime.scopeMake(), + core.flatMap((innerScope) => + pipe( + restore(core.flatMap( + makeBuilder(layer, innerScope, true), + (f) => effect.diffFiberRefs(f(this)) + )), + core.exit, + core.flatMap((exit) => { + switch (exit._tag) { + case EffectOpCodes.OP_FAILURE: { + return pipe( + core.deferredFailCause(deferred, exit.effect_instruction_i0), + core.zipRight(core.scopeClose(innerScope, exit)), + core.zipRight(core.failCause(exit.effect_instruction_i0)) + ) + } + case EffectOpCodes.OP_SUCCESS: { + return pipe( + ref.set(finalizerRef, (exit) => + pipe( + core.scopeClose(innerScope, exit), + core.whenEffect( + ref.modify(observers, (n) => [n === 1, n - 1] as const) + ), + core.asVoid + )), + core.zipRight(ref.update(observers, (n) => n + 1)), + core.zipRight( + core.scopeAddFinalizerExit(scope, (exit) => + pipe( + core.sync(() => map.delete(layer)), + core.zipRight(ref.get(finalizerRef)), + core.flatMap((finalizer) => finalizer(exit)) + )) + ), + core.zipRight(core.deferredSucceed(deferred, exit.effect_instruction_i0)), + core.as(exit.effect_instruction_i0[1]) + ) + } + } + }) + ) + ) + ) + ) + const memoized = [ + pipe( + core.deferredAwait(deferred), + core.onExit(core.exitMatchEffect({ + onFailure: () => core.void, + onSuccess: () => ref.update(observers, (n) => n + 1) + })) + ), + (exit: Exit.Exit) => + pipe( + ref.get(finalizerRef), + core.flatMap((finalizer) => finalizer(exit)) + ) + ] as const + return [ + resource, + isFresh(layer) ? map : map.set(layer, memoized) + ] as const + }) + ) + ) + ) + ) + ) + }), + core.flatten + ) + } +} + +/** @internal */ +export const makeMemoMap: Effect.Effect = core.suspend(() => + core.map( + circular.makeSynchronized< + Map< + Layer.Layer, + readonly [ + Effect.Effect, + Scope.Scope.Finalizer + ] + > + >(new Map()), + (ref) => new MemoMapImpl(ref) + ) +) + +/** @internal */ +export const unsafeMakeMemoMap = (): Layer.MemoMap => new MemoMapImpl(circular.unsafeMakeSynchronized(new Map())) + +/** @internal */ +export const build = ( + self: Layer.Layer +): Effect.Effect, E, RIn | Scope.Scope> => + fiberRuntime.scopeWith((scope) => buildWithScope(self, scope)) + +/** @internal */ +export const buildWithScope = dual< + ( + scope: Scope.Scope + ) => (self: Layer.Layer) => Effect.Effect, E, RIn>, + ( + self: Layer.Layer, + scope: Scope.Scope + ) => Effect.Effect, E, RIn> +>(2, (self, scope) => + core.flatMap( + makeMemoMap, + (memoMap) => buildWithMemoMap(self, memoMap, scope) + )) + +/** @internal */ +export const buildWithMemoMap = dual< + ( + memoMap: Layer.MemoMap, + scope: Scope.Scope + ) => (self: Layer.Layer) => Effect.Effect, E, RIn>, + ( + self: Layer.Layer, + memoMap: Layer.MemoMap, + scope: Scope.Scope + ) => Effect.Effect, E, RIn> +>( + 3, + (self, memoMap, scope) => + core.flatMap( + makeBuilder(self, scope), + (run) => effect.provideService(run(memoMap), CurrentMemoMap, memoMap) + ) +) + +const makeBuilder = ( + self: Layer.Layer, + scope: Scope.Scope, + inMemoMap = false +): Effect.Effect<(memoMap: Layer.MemoMap) => Effect.Effect, E, RIn>> => { + const op = self as Primitive + switch (op._op_layer) { + case "Locally": { + return core.sync(() => (memoMap: Layer.MemoMap) => op.f(memoMap.getOrElseMemoize(op.self, scope))) + } + case "ExtendScope": { + return core.sync(() => (memoMap: Layer.MemoMap) => + fiberRuntime.scopeWith( + (scope) => memoMap.getOrElseMemoize(op.layer, scope) + ) as unknown as Effect.Effect, E, RIn> + ) + } + case "Fold": { + return core.sync(() => (memoMap: Layer.MemoMap) => + pipe( + memoMap.getOrElseMemoize(op.layer, scope), + core.matchCauseEffect({ + onFailure: (cause) => memoMap.getOrElseMemoize(op.failureK(cause), scope), + onSuccess: (value) => memoMap.getOrElseMemoize(op.successK(value), scope) + }) + ) + ) + } + case "Fresh": { + return core.sync(() => (_: Layer.MemoMap) => pipe(op.layer, buildWithScope(scope))) + } + case "FromEffect": { + return inMemoMap + ? core.sync(() => (_: Layer.MemoMap) => op.effect as Effect.Effect, E, RIn>) + : core.sync(() => (memoMap: Layer.MemoMap) => memoMap.getOrElseMemoize(self, scope)) + } + case "Provide": { + return core.sync(() => (memoMap: Layer.MemoMap) => + pipe( + memoMap.getOrElseMemoize(op.first, scope), + core.flatMap((env) => + pipe( + memoMap.getOrElseMemoize(op.second, scope), + core.provideContext(env) + ) + ) + ) + ) + } + case "Scoped": { + return inMemoMap + ? core.sync(() => (_: Layer.MemoMap) => + fiberRuntime.scopeExtend( + op.effect as Effect.Effect, E, RIn>, + scope + ) + ) + : core.sync(() => (memoMap: Layer.MemoMap) => memoMap.getOrElseMemoize(self, scope)) + } + case "Suspend": { + return core.sync(() => (memoMap: Layer.MemoMap) => + memoMap.getOrElseMemoize( + op.evaluate(), + scope + ) + ) + } + case "ProvideMerge": { + return core.sync(() => (memoMap: Layer.MemoMap) => + pipe( + memoMap.getOrElseMemoize(op.first, scope), + core.zipWith( + memoMap.getOrElseMemoize(op.second, scope), + op.zipK + ) + ) + ) + } + case "ZipWith": { + return core.gen(function*() { + const parallelScope = yield* core.scopeFork(scope, ExecutionStrategy.parallel) + const firstScope = yield* core.scopeFork(parallelScope, ExecutionStrategy.sequential) + const secondScope = yield* core.scopeFork(parallelScope, ExecutionStrategy.sequential) + return (memoMap: Layer.MemoMap) => + pipe( + memoMap.getOrElseMemoize(op.first, firstScope), + fiberRuntime.zipWithOptions( + memoMap.getOrElseMemoize(op.second, secondScope), + op.zipK, + { concurrent: true } + ) + ) + }) + } + case "MergeAll": { + const layers = op.layers + return core.map( + core.scopeFork(scope, ExecutionStrategy.parallel), + (parallelScope) => (memoMap: Layer.MemoMap) => { + const contexts = new Array>(layers.length) + return core.map( + fiberRuntime.forEachConcurrentDiscard( + layers, + core.fnUntraced(function*(layer, i) { + const scope = yield* core.scopeFork(parallelScope, ExecutionStrategy.sequential) + const context = yield* memoMap.getOrElseMemoize(layer, scope) + contexts[i] = context + }), + false, + false + ), + () => Context.mergeAll(...contexts) + ) + } + ) + } + } +} + +// ----------------------------------------------------------------------------- +// Layer +// ----------------------------------------------------------------------------- + +/** @internal */ +export const catchAll = dual< + ( + onError: (error: E) => Layer.Layer + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + onError: (error: E) => Layer.Layer + ) => Layer.Layer +>(2, (self, onFailure) => match(self, { onFailure, onSuccess: succeedContext })) + +/** @internal */ +export const catchAllCause = dual< + ( + onError: (cause: Cause.Cause) => Layer.Layer + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + onError: (cause: Cause.Cause) => Layer.Layer + ) => Layer.Layer +>(2, (self, onFailure) => matchCause(self, { onFailure, onSuccess: succeedContext })) + +/** @internal */ +export const die = (defect: unknown): Layer.Layer => failCause(Cause.die(defect)) + +/** @internal */ +export const dieSync = (evaluate: LazyArg): Layer.Layer => failCauseSync(() => Cause.die(evaluate())) + +/** @internal */ +export const discard = (self: Layer.Layer): Layer.Layer => + map(self, () => Context.empty()) + +/** @internal */ +export const context = (): Layer.Layer => fromEffectContext(core.context()) + +/** @internal */ +export const extendScope = ( + self: Layer.Layer +): Layer.Layer => { + const extendScope = Object.create(proto) + extendScope._op_layer = OpCodes.OP_EXTEND_SCOPE + extendScope.layer = self + return extendScope +} + +/** @internal */ +export const fail = (error: E): Layer.Layer => failCause(Cause.fail(error)) + +/** @internal */ +export const failSync = (evaluate: LazyArg): Layer.Layer => + failCauseSync(() => Cause.fail(evaluate())) + +/** @internal */ +export const failCause = (cause: Cause.Cause): Layer.Layer => fromEffectContext(core.failCause(cause)) + +/** @internal */ +export const failCauseSync = (evaluate: LazyArg>): Layer.Layer => + fromEffectContext(core.failCauseSync(evaluate)) + +/** @internal */ +export const flatMap = dual< + ( + f: (context: Context.Context) => Layer.Layer + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + f: (context: Context.Context) => Layer.Layer + ) => Layer.Layer +>(2, (self, f) => match(self, { onFailure: fail, onSuccess: f })) + +/** @internal */ +export const flatten = dual< + ( + tag: Context.Tag> + ) => ( + self: Layer.Layer + ) => Layer.Layer, + ( + self: Layer.Layer, + tag: Context.Tag> + ) => Layer.Layer +>(2, (self, tag) => flatMap(self, Context.get(tag as any) as any)) + +/** @internal */ +export const fresh = (self: Layer.Layer): Layer.Layer => { + const fresh = Object.create(proto) + fresh._op_layer = OpCodes.OP_FRESH + fresh.layer = self + return fresh +} + +/** @internal */ +export const fromEffect = dual< + ( + tag: Context.Tag + ) => ( + effect: Effect.Effect, E, R> + ) => Layer.Layer, + ( + tag: Context.Tag, + effect: Effect.Effect, E, R> + ) => Layer.Layer +>(2, (a, b) => { + const tagFirst = Context.isTag(a) + const tag = (tagFirst ? a : b) as Context.Tag + const effect = tagFirst ? b : a + return fromEffectContext(core.map(effect, (service) => Context.make(tag, service))) +}) + +/** @internal */ +export const fromEffectDiscard = (effect: Effect.Effect) => + fromEffectContext(core.map(effect, () => Context.empty())) + +/** @internal */ +export function fromEffectContext( + effect: Effect.Effect, E, R> +): Layer.Layer { + const fromEffect = Object.create(proto) + fromEffect._op_layer = OpCodes.OP_FROM_EFFECT + fromEffect.effect = effect + return fromEffect +} + +/** @internal */ +export const fiberRefLocally = dual< + (ref: FiberRef, value: X) => (self: Layer.Layer) => Layer.Layer, + (self: Layer.Layer, ref: FiberRef, value: X) => Layer.Layer +>(3, (self, ref, value) => locallyEffect(self, core.fiberRefLocally(ref, value))) + +/** @internal */ +export const locallyEffect = dual< + ( + f: (_: Effect.Effect>) => Effect.Effect> + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + f: (_: Effect.Effect>) => Effect.Effect> + ) => Layer.Layer +>(2, (self, f) => { + const locally = Object.create(proto) + locally._op_layer = "Locally" + locally.self = self + locally.f = f + return locally +}) + +/** @internal */ +export const fiberRefLocallyWith = dual< + (ref: FiberRef, value: (_: X) => X) => (self: Layer.Layer) => Layer.Layer, + (self: Layer.Layer, ref: FiberRef, value: (_: X) => X) => Layer.Layer +>(3, (self, ref, value) => locallyEffect(self, core.fiberRefLocallyWith(ref, value))) + +/** @internal */ +export const fiberRefLocallyScoped = (self: FiberRef, value: A): Layer.Layer => + scopedDiscard(fiberRuntime.fiberRefLocallyScoped(self, value)) + +/** @internal */ +export const fiberRefLocallyScopedWith = (self: FiberRef, value: (_: A) => A): Layer.Layer => + scopedDiscard(fiberRuntime.fiberRefLocallyScopedWith(self, value)) + +/** @internal */ +export const fromFunction = ( + tagA: Context.Tag, + tagB: Context.Tag, + f: (a: Types.NoInfer) => Types.NoInfer +): Layer.Layer => fromEffectContext(core.map(tagA, (a) => Context.make(tagB, f(a)))) + +/** @internal */ +export const launch = (self: Layer.Layer): Effect.Effect => + fiberRuntime.scopedEffect( + core.zipRight( + fiberRuntime.scopeWith((scope) => pipe(self, buildWithScope(scope))), + core.never + ) + ) + +/** @internal */ +export const mock: { + (tag: Context.Tag): (service: Layer.PartialEffectful) => Layer.Layer + (tag: Context.Tag, service: Layer.PartialEffectful): Layer.Layer +} = function() { + if (arguments.length === 1) { + return (service: Layer.PartialEffectful) => mockImpl(arguments[0], service) + } + return mockImpl(arguments[0], arguments[1]) +} as any + +const mockImpl = (tag: Context.Tag, service: Layer.PartialEffectful): Layer.Layer => + succeed( + tag, + new Proxy({ ...service as object } as S, { + get(target, prop, _receiver) { + if (prop in target) { + return target[prop as keyof S] + } + const prevLimit = Error.stackTraceLimit + Error.stackTraceLimit = 2 + const error = new Error(`${tag.key}: Unimplemented method "${prop.toString()}"`) + Error.stackTraceLimit = prevLimit + error.name = "UnimplementedError" + return makeUnimplemented(error) + }, + has: constTrue + }) + ) + +const makeUnimplemented = (error: Error) => { + const dead = core.die(error) + function unimplemented() { + return dead + } + // @effect-diagnostics-next-line floatingEffect:off + Object.assign(unimplemented, dead) + Object.setPrototypeOf(unimplemented, Object.getPrototypeOf(dead)) + return unimplemented +} + +/** @internal */ +export const map = dual< + ( + f: (context: Context.Context) => Context.Context + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + f: (context: Context.Context) => Context.Context + ) => Layer.Layer +>(2, (self, f) => flatMap(self, (context) => succeedContext(f(context)))) + +/** @internal */ +export const mapError = dual< + (f: (error: E) => E2) => (self: Layer.Layer) => Layer.Layer, + (self: Layer.Layer, f: (error: E) => E2) => Layer.Layer +>(2, (self, f) => catchAll(self, (error) => failSync(() => f(error)))) + +/** @internal */ +export const matchCause = dual< + ( + options: { + readonly onFailure: (cause: Cause.Cause) => Layer.Layer + readonly onSuccess: (context: Context.Context) => Layer.Layer + } + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + options: { + readonly onFailure: (cause: Cause.Cause) => Layer.Layer + readonly onSuccess: (context: Context.Context) => Layer.Layer + } + ) => Layer.Layer +>(2, (self, { onFailure, onSuccess }) => { + const fold = Object.create(proto) + fold._op_layer = OpCodes.OP_FOLD + fold.layer = self + fold.failureK = onFailure + fold.successK = onSuccess + return fold +}) + +/** @internal */ +export const match = dual< + ( + options: { + readonly onFailure: (error: E) => Layer.Layer + readonly onSuccess: (context: Context.Context) => Layer.Layer + } + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + options: { + readonly onFailure: (error: E) => Layer.Layer + readonly onSuccess: (context: Context.Context) => Layer.Layer + } + ) => Layer.Layer +>(2, (self, { onFailure, onSuccess }) => + matchCause(self, { + onFailure: (cause) => { + const failureOrCause = Cause.failureOrCause(cause) + switch (failureOrCause._tag) { + case "Left": { + return onFailure(failureOrCause.left) + } + case "Right": { + return failCause(failureOrCause.right) + } + } + }, + onSuccess + })) + +/** @internal */ +export const memoize = ( + self: Layer.Layer +): Effect.Effect, never, Scope.Scope> => + fiberRuntime.scopeWith((scope) => + core.map( + effect.memoize(buildWithScope(self, scope)), + fromEffectContext + ) + ) + +/** @internal */ +export const merge = dual< + ( + that: Layer.Layer + ) => (self: Layer.Layer) => Layer.Layer< + ROut | ROut2, + E1 | E2, + RIn | RIn2 + >, + (self: Layer.Layer, that: Layer.Layer) => Layer.Layer< + ROut | ROut2, + E1 | E2, + RIn | RIn2 + > +>(2, (self, that) => zipWith(self, that, (a, b) => Context.merge(a, b))) + +/** @internal */ +export const mergeAll = < + Layers extends readonly [Layer.Layer, ...Array>] +>( + ...layers: Layers +): Layer.Layer< + { [k in keyof Layers]: Layer.Layer.Success }[number], + { [k in keyof Layers]: Layer.Layer.Error }[number], + { [k in keyof Layers]: Layer.Layer.Context }[number] +> => { + const mergeAll = Object.create(proto) + mergeAll._op_layer = OpCodes.OP_MERGE_ALL + mergeAll.layers = layers + return mergeAll as any +} + +/** @internal */ +export const orDie = (self: Layer.Layer): Layer.Layer => + catchAll(self, (defect) => die(defect)) + +/** @internal */ +export const orElse = dual< + ( + that: LazyArg> + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + that: LazyArg> + ) => Layer.Layer +>(2, (self, that) => catchAll(self, that)) + +/** @internal */ +export const passthrough = (self: Layer.Layer): Layer.Layer => + merge(context(), self) + +/** @internal */ +export const project = dual< + ( + tagA: Context.Tag, + tagB: Context.Tag, + f: (a: Types.NoInfer) => Types.NoInfer + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + tagA: Context.Tag, + tagB: Context.Tag, + f: (a: Types.NoInfer) => Types.NoInfer + ) => Layer.Layer +>(4, (self, tagA, tagB, f) => map(self, (context) => Context.make(tagB, f(Context.unsafeGet(context, tagA))))) + +/** @internal */ +export const retry = dual< + ( + schedule: Schedule.Schedule + ) => ( + self: Layer.Layer + ) => Layer.Layer, + ( + self: Layer.Layer, + schedule: Schedule.Schedule + ) => Layer.Layer +>(2, (self, schedule) => + suspend(() => { + const stateTag = Context.GenericTag<{ state: unknown }>("effect/Layer/retry/{ state: unknown }") + return pipe( + succeed(stateTag, { state: schedule.initial }), + flatMap((env: Context.Context<{ state: unknown }>) => + retryLoop(self, schedule, stateTag, pipe(env, Context.get(stateTag)).state) + ) + ) + })) + +const retryLoop = ( + self: Layer.Layer, + schedule: Schedule.Schedule, + stateTag: Context.Tag<{ state: unknown }, { state: unknown }>, + state: unknown +): Layer.Layer => { + return pipe( + self, + catchAll((error) => + pipe( + retryUpdate(schedule, stateTag, error, state), + flatMap((env) => fresh(retryLoop(self, schedule, stateTag, pipe(env, Context.get(stateTag)).state))) + ) + ) + ) +} + +const retryUpdate = ( + schedule: Schedule.Schedule, + stateTag: Context.Tag<{ state: unknown }, { state: unknown }>, + error: E, + state: unknown +): Layer.Layer<{ state: unknown }, E, RIn> => { + return fromEffect( + stateTag, + pipe( + Clock.currentTimeMillis, + core.flatMap((now) => + pipe( + schedule.step(now, error, state), + core.flatMap(([state, _, decision]) => + ScheduleDecision.isDone(decision) ? + core.fail(error) : + pipe( + Clock.sleep(Duration.millis(Intervals.start(decision.intervals) - now)), + core.as({ state }) + ) + ) + ) + ) + ) + ) +} + +/** @internal */ +export const scoped = dual< + ( + tag: Context.Tag + ) => ( + effect: Effect.Effect, E, R> + ) => Layer.Layer>, + ( + tag: Context.Tag, + effect: Effect.Effect, E, R> + ) => Layer.Layer> +>(2, (a, b) => { + const tagFirst = Context.isTag(a) + const tag = (tagFirst ? a : b) as Context.Tag + const effect = tagFirst ? b : a + return scopedContext(core.map(effect, (service) => Context.make(tag, service))) +}) + +/** @internal */ +export const scopedDiscard = ( + effect: Effect.Effect +): Layer.Layer> => scopedContext(pipe(effect, core.as(Context.empty()))) + +/** @internal */ +export const scopedContext = ( + effect: Effect.Effect, E, R> +): Layer.Layer> => { + const scoped = Object.create(proto) + scoped._op_layer = OpCodes.OP_SCOPED + scoped.effect = effect + return scoped +} + +/** @internal */ +export const scope: Layer.Layer = scopedContext( + core.map( + fiberRuntime.acquireRelease( + fiberRuntime.scopeMake(), + (scope, exit) => scope.close(exit) + ), + (scope) => Context.make(Scope.Scope, scope) + ) +) + +/** @internal */ +export const service = ( + tag: Context.Tag +): Layer.Layer => fromEffect(tag, tag) + +/** @internal */ +export const succeed = dual< + ( + tag: Context.Tag + ) => ( + resource: Types.NoInfer + ) => Layer.Layer, + ( + tag: Context.Tag, + resource: Types.NoInfer + ) => Layer.Layer +>(2, (a, b) => { + const tagFirst = Context.isTag(a) + const tag = (tagFirst ? a : b) as Context.Tag + const resource = tagFirst ? b : a + return fromEffectContext(core.succeed(Context.make(tag, resource))) +}) + +/** @internal */ +export const succeedContext = ( + context: Context.Context +): Layer.Layer => { + return fromEffectContext(core.succeed(context)) +} + +/** @internal */ +export const empty = succeedContext(Context.empty()) + +/** @internal */ +export const suspend = ( + evaluate: LazyArg> +): Layer.Layer => { + const suspend = Object.create(proto) + suspend._op_layer = OpCodes.OP_SUSPEND + suspend.evaluate = evaluate + return suspend +} + +/** @internal */ +export const sync = dual< + ( + tag: Context.Tag + ) => ( + evaluate: LazyArg> + ) => Layer.Layer, + ( + tag: Context.Tag, + evaluate: LazyArg> + ) => Layer.Layer +>(2, (a, b) => { + const tagFirst = Context.isTag(a) + const tag = (tagFirst ? a : b) as Context.Tag + const evaluate = tagFirst ? b : a + return fromEffectContext(core.sync(() => Context.make(tag, evaluate()))) +}) + +/** @internal */ +export const syncContext = (evaluate: LazyArg>): Layer.Layer => { + return fromEffectContext(core.sync(evaluate)) +} + +/** @internal */ +export const tap = dual< + ( + f: (context: Context.Context) => Effect.Effect + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + f: (context: Context.Context) => Effect.Effect + ) => Layer.Layer +>(2, (self, f) => flatMap(self, (context) => fromEffectContext(core.as(f(context), context)))) + +/** @internal */ +export const tapError = dual< + ( + f: (e: XE) => Effect.Effect + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + f: (e: XE) => Effect.Effect + ) => Layer.Layer +>(2, (self, f) => + catchAll( + self, + (e) => fromEffectContext(core.flatMap(f(e as any), () => core.fail(e))) + )) + +/** @internal */ +export const tapErrorCause = dual< + ( + f: (cause: Cause.Cause) => Effect.Effect + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + f: (cause: Cause.Cause) => Effect.Effect + ) => Layer.Layer +>(2, (self, f) => + catchAllCause( + self, + (cause) => fromEffectContext(core.flatMap(f(cause as any), () => core.failCause(cause))) + )) + +/** @internal */ +export const toRuntime = ( + self: Layer.Layer +): Effect.Effect, E, RIn | Scope.Scope> => + pipe( + fiberRuntime.scopeWith((scope) => buildWithScope(self, scope)), + core.flatMap((context) => + pipe( + runtime.runtime(), + core.provideContext(context) + ) + ) + ) + +/** @internal */ +export const toRuntimeWithMemoMap = dual< + ( + memoMap: Layer.MemoMap + ) => (self: Layer.Layer) => Effect.Effect, E, RIn | Scope.Scope>, + ( + self: Layer.Layer, + memoMap: Layer.MemoMap + ) => Effect.Effect, E, RIn | Scope.Scope> +>(2, (self, memoMap) => + core.flatMap( + fiberRuntime.scopeWith((scope) => buildWithMemoMap(self, memoMap, scope)), + (context) => + pipe( + runtime.runtime(), + core.provideContext(context) + ) + )) + +/** @internal */ +export const provide = dual< + { + ( + that: Layer.Layer + ): ( + self: Layer.Layer + ) => Layer.Layer> + ]>( + that: Layers + ): ( + self: Layer.Layer + ) => Layer.Layer< + A, + E | { [k in keyof Layers]: Layer.Layer.Error }[number], + | { [k in keyof Layers]: Layer.Layer.Context }[number] + | Exclude }[number]> + > + }, + { + ( + self: Layer.Layer, + that: Layer.Layer + ): Layer.Layer> + ]>( + self: Layer.Layer, + that: Layers + ): Layer.Layer< + A, + E | { [k in keyof Layers]: Layer.Layer.Error }[number], + | { [k in keyof Layers]: Layer.Layer.Context }[number] + | Exclude }[number]> + > + } +>(2, ( + self: Layer.Layer.Any, + that: Layer.Layer.Any | ReadonlyArray +) => + suspend(() => { + const provideTo = Object.create(proto) + provideTo._op_layer = OpCodes.OP_PROVIDE + provideTo.first = Object.create(proto, { + _op_layer: { value: OpCodes.OP_PROVIDE_MERGE, enumerable: true }, + first: { value: context(), enumerable: true }, + second: { value: Array.isArray(that) ? mergeAll(...that as any) : that }, + zipK: { value: (a: Context.Context, b: Context.Context) => pipe(a, Context.merge(b)) } + }) + provideTo.second = self + return provideTo + })) + +/** @internal */ +export const provideMerge = dual< + ( + self: Layer.Layer + ) => ( + that: Layer.Layer + ) => Layer.Layer>, + ( + that: Layer.Layer, + self: Layer.Layer + ) => Layer.Layer> +>(2, (that: Layer.Layer, self: Layer.Layer) => { + const zipWith = Object.create(proto) + zipWith._op_layer = OpCodes.OP_PROVIDE_MERGE + zipWith.first = self + zipWith.second = provide(that, self) + zipWith.zipK = (a: Context.Context, b: Context.Context): Context.Context => { + return pipe(a, Context.merge(b)) + } + return zipWith +}) + +/** @internal */ +export const zipWith = dual< + ( + that: Layer.Layer, + f: (a: Context.Context, b: Context.Context) => Context.Context + ) => (self: Layer.Layer) => Layer.Layer, + ( + self: Layer.Layer, + that: Layer.Layer, + f: (a: Context.Context, b: Context.Context) => Context.Context + ) => Layer.Layer +>(3, (self, that, f) => + suspend(() => { + const zipWith = Object.create(proto) + zipWith._op_layer = OpCodes.OP_ZIP_WITH + zipWith.first = self + zipWith.second = that + zipWith.zipK = f + return zipWith + })) + +/** @internal */ +export const unwrapEffect = ( + self: Effect.Effect, E, R> +): Layer.Layer => { + const tag = Context.GenericTag>("effect/Layer/unwrapEffect/Layer.Layer") + return flatMap(fromEffect(tag, self), (context) => Context.get(context, tag)) +} + +/** @internal */ +export const unwrapScoped = ( + self: Effect.Effect, E, R> +): Layer.Layer> => { + const tag = Context.GenericTag>("effect/Layer/unwrapScoped/Layer.Layer") + return flatMap(scoped(tag, self), (context) => Context.get(context, tag)) +} + +// ----------------------------------------------------------------------------- +// logging +// ----------------------------------------------------------------------------- + +export const annotateLogs = dual< + { + (key: string, value: unknown): (self: Layer.Layer) => Layer.Layer + ( + values: Record + ): (self: Layer.Layer) => Layer.Layer + }, + { + (self: Layer.Layer, key: string, value: unknown): Layer.Layer + (self: Layer.Layer, values: Record): Layer.Layer + } +>( + (args) => isLayer(args[0]), + function() { + const args = arguments + return fiberRefLocallyWith( + args[0] as Layer.Layer, + core.currentLogAnnotations, + typeof args[1] === "string" + ? HashMap.set(args[1], args[2]) + : (annotations) => + Object.entries(args[1] as Record).reduce( + (acc, [key, value]) => HashMap.set(acc, key, value), + annotations + ) + ) + } +) + +// ----------------------------------------------------------------------------- +// tracing +// ----------------------------------------------------------------------------- + +export const annotateSpans = dual< + { + (key: string, value: unknown): (self: Layer.Layer) => Layer.Layer + ( + values: Record + ): (self: Layer.Layer) => Layer.Layer + }, + { + (self: Layer.Layer, key: string, value: unknown): Layer.Layer + (self: Layer.Layer, values: Record): Layer.Layer + } +>( + (args) => isLayer(args[0]), + function() { + const args = arguments + return fiberRefLocallyWith( + args[0] as Layer.Layer, + core.currentTracerSpanAnnotations, + typeof args[1] === "string" + ? HashMap.set(args[1], args[2]) + : (annotations) => + Object.entries(args[1] as Record).reduce( + (acc, [key, value]) => HashMap.set(acc, key, value), + annotations + ) + ) + } +) + +/** @internal */ +export const withSpan: { + ( + name: string, + options?: Tracer.SpanOptions & { + readonly onEnd?: + | ((span: Tracer.Span, exit: Exit.Exit) => Effect.Effect) + | undefined + } + ): (self: Layer.Layer) => Layer.Layer> + ( + self: Layer.Layer, + name: string, + options?: Tracer.SpanOptions & { + readonly onEnd?: + | ((span: Tracer.Span, exit: Exit.Exit) => Effect.Effect) + | undefined + } + ): Layer.Layer> +} = function() { + const dataFirst = typeof arguments[0] !== "string" + const name = dataFirst ? arguments[1] : arguments[0] + const options = tracer.addSpanStackTrace(dataFirst ? arguments[2] : arguments[1]) as Tracer.SpanOptions & { + readonly onEnd?: + | ((span: Tracer.Span, exit: Exit.Exit) => Effect.Effect) + | undefined + } + if (dataFirst) { + const self = arguments[0] + return unwrapScoped( + core.map( + options?.onEnd + ? core.tap( + fiberRuntime.makeSpanScoped(name, options), + (span) => fiberRuntime.addFinalizer((exit) => options.onEnd!(span, exit)) + ) + : fiberRuntime.makeSpanScoped(name, options), + (span) => withParentSpan(self, span) + ) + ) + } + return (self: Layer.Layer) => + unwrapScoped( + core.map( + options?.onEnd + ? core.tap( + fiberRuntime.makeSpanScoped(name, options), + (span) => fiberRuntime.addFinalizer((exit) => options.onEnd!(span, exit)) + ) + : fiberRuntime.makeSpanScoped(name, options), + (span) => withParentSpan(self, span) + ) + ) +} as any + +/** @internal */ +export const withParentSpan = dual< + ( + span: Tracer.AnySpan + ) => (self: Layer.Layer) => Layer.Layer>, + (self: Layer.Layer, span: Tracer.AnySpan) => Layer.Layer> +>(2, (self, span) => provide(self, succeedContext(Context.make(tracer.spanTag, span)))) + +// circular with Effect + +const provideSomeLayer = dual< + ( + layer: Layer.Layer + ) => (self: Effect.Effect) => Effect.Effect>, + ( + self: Effect.Effect, + layer: Layer.Layer + ) => Effect.Effect> +>(2, (self, layer) => + fiberRuntime.scopedWith((scope) => + core.flatMap( + buildWithScope(layer, scope), + (context) => core.provideSomeContext(self, context) + ) + )) + +const provideSomeRuntime = dual< + (context: Runtime.Runtime) => (self: Effect.Effect) => Effect.Effect>, + (self: Effect.Effect, context: Runtime.Runtime) => Effect.Effect> +>(2, (self, rt) => { + const patchRefs = FiberRefsPatch.diff(runtime.defaultRuntime.fiberRefs, rt.fiberRefs) + const patchFlags = runtimeFlags.diff(runtime.defaultRuntime.runtimeFlags, rt.runtimeFlags) + return core.uninterruptibleMask((restore) => + core.withFiberRuntime((fiber) => { + const oldContext = fiber.getFiberRef(core.currentContext) + const oldRefs = fiber.getFiberRefs() + const newRefs = FiberRefsPatch.patch(fiber.id(), oldRefs)(patchRefs) + const oldFlags = fiber.currentRuntimeFlags + const newFlags = runtimeFlags.patch(patchFlags)(oldFlags) + const rollbackRefs = FiberRefsPatch.diff(newRefs, oldRefs) + const rollbackFlags = runtimeFlags.diff(newFlags, oldFlags) + fiber.setFiberRefs(newRefs) + fiber.currentRuntimeFlags = newFlags + return fiberRuntime.ensuring( + core.provideSomeContext(restore(self), Context.merge(oldContext, rt.context)), + core.withFiberRuntime((fiber) => { + fiber.setFiberRefs(FiberRefsPatch.patch(fiber.id(), fiber.getFiberRefs())(rollbackRefs)) + fiber.currentRuntimeFlags = runtimeFlags.patch(rollbackFlags)(fiber.currentRuntimeFlags) + return core.void + }) + ) + }) + ) +}) + +/** @internal */ +export const effect_provide = dual< + { + ]>( + layers: Layers + ): ( + self: Effect.Effect + ) => Effect.Effect< + A, + E | { [k in keyof Layers]: Layer.Layer.Error }[number], + | { [k in keyof Layers]: Layer.Layer.Context }[number] + | Exclude }[number]> + > + ( + layer: Layer.Layer + ): (self: Effect.Effect) => Effect.Effect> + ( + context: Context.Context + ): (self: Effect.Effect) => Effect.Effect> + ( + runtime: Runtime.Runtime + ): (self: Effect.Effect) => Effect.Effect> + ( + managedRuntime: ManagedRuntime.ManagedRuntime + ): (self: Effect.Effect) => Effect.Effect> + }, + { + ]>( + self: Effect.Effect, + layers: Layers + ): Effect.Effect< + A, + E | { [k in keyof Layers]: Layer.Layer.Error }[number], + | { [k in keyof Layers]: Layer.Layer.Context }[number] + | Exclude }[number]> + > + ( + self: Effect.Effect, + layer: Layer.Layer + ): Effect.Effect> + ( + self: Effect.Effect, + context: Context.Context + ): Effect.Effect> + ( + self: Effect.Effect, + runtime: Runtime.Runtime + ): Effect.Effect> + ( + self: Effect.Effect, + managedRuntime: ManagedRuntime.ManagedRuntime + ): Effect.Effect> + } +>( + 2, + ( + self: Effect.Effect, + source: + | Layer.Layer + | Context.Context + | Runtime.Runtime + | ManagedRuntime.ManagedRuntime + | Array + ): Effect.Effect> => { + if (Array.isArray(source)) { + // @ts-expect-error + return provideSomeLayer(self, mergeAll(...source)) + } else if (isLayer(source)) { + return provideSomeLayer(self, source as Layer.Layer) + } else if (Context.isContext(source)) { + return core.provideSomeContext(self, source) + } else if (circularManagedRuntime.TypeId in source) { + return core.flatMap( + (source as ManagedRuntime.ManagedRuntime).runtimeEffect, + (rt) => provideSomeRuntime(self, rt) + ) + } else { + return provideSomeRuntime(self, source as Runtime.Runtime) + } + } +) diff --git a/backend/node_modules/effect/src/internal/logSpan.ts b/backend/node_modules/effect/src/internal/logSpan.ts new file mode 100644 index 0000000000000000000000000000000000000000..f58acfb08f9519e1b6f016f113742a058974d4ee --- /dev/null +++ b/backend/node_modules/effect/src/internal/logSpan.ts @@ -0,0 +1,20 @@ +import type * as LogSpan from "../LogSpan.js" + +/** @internal */ +export const make = (label: string, startTime: number): LogSpan.LogSpan => ({ + label, + startTime +}) + +/** + * Sanitize a given string by replacing spaces, equal signs, and double quotes with underscores. + * + * @internal + */ +export const formatLabel = (key: string) => key.replace(/[\s="]/g, "_") + +/** @internal */ +export const render = (now: number) => (self: LogSpan.LogSpan): string => { + const label = formatLabel(self.label) + return `${label}=${now - self.startTime}ms` +} diff --git a/backend/node_modules/effect/src/internal/logger-circular.ts b/backend/node_modules/effect/src/internal/logger-circular.ts new file mode 100644 index 0000000000000000000000000000000000000000..ba72e75f693375d486f3d13c5287de80c563f561 --- /dev/null +++ b/backend/node_modules/effect/src/internal/logger-circular.ts @@ -0,0 +1,24 @@ +import * as Cause from "../Cause.js" +import { dual } from "../Function.js" +import * as HashMap from "../HashMap.js" +import * as List from "../List.js" +import type * as Logger from "../Logger.js" +import * as core from "./core.js" +import * as fiberId_ from "./fiberId.js" +import * as fiberRefs from "./fiberRefs.js" + +/** @internal */ +export const test = dual< + (input: Message) => (self: Logger.Logger) => Output, + (self: Logger.Logger, input: Message) => Output +>(2, (self, input) => + self.log({ + fiberId: fiberId_.none, + logLevel: core.logLevelInfo, + message: input, + cause: Cause.empty, + context: fiberRefs.empty(), + spans: List.empty(), + annotations: HashMap.empty(), + date: new Date() + })) diff --git a/backend/node_modules/effect/src/internal/logger.ts b/backend/node_modules/effect/src/internal/logger.ts new file mode 100644 index 0000000000000000000000000000000000000000..b416a8fa3543c59061b46e181eff4d344927965a --- /dev/null +++ b/backend/node_modules/effect/src/internal/logger.ts @@ -0,0 +1,485 @@ +import * as Arr from "../Array.js" +import * as Context from "../Context.js" +import * as FiberRefs from "../FiberRefs.js" +import type { LazyArg } from "../Function.js" +import { constVoid, dual } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import * as HashMap from "../HashMap.js" +import * as Inspectable from "../Inspectable.js" +import * as List from "../List.js" +import type * as Logger from "../Logger.js" +import type * as LogLevel from "../LogLevel.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import * as Cause from "./cause.js" +import * as defaultServices from "./defaultServices.js" +import { consoleTag } from "./defaultServices/console.js" +import * as fiberId_ from "./fiberId.js" +import * as logSpan_ from "./logSpan.js" + +/** @internal */ +const LoggerSymbolKey = "effect/Logger" + +/** @internal */ +export const LoggerTypeId: Logger.LoggerTypeId = Symbol.for( + LoggerSymbolKey +) as Logger.LoggerTypeId + +const loggerVariance = { + /* c8 ignore next */ + _Message: (_: unknown) => _, + /* c8 ignore next */ + _Output: (_: never) => _ +} + +/** @internal */ +export const makeLogger = ( + log: (options: Logger.Logger.Options) => Output +): Logger.Logger => ({ + [LoggerTypeId]: loggerVariance, + log, + pipe() { + return pipeArguments(this, arguments) + } +}) + +/** @internal */ +export const mapInput = dual< + ( + f: (message: Message2) => Message + ) => (self: Logger.Logger) => Logger.Logger, + ( + self: Logger.Logger, + f: (message: Message2) => Message + ) => Logger.Logger +>(2, (self, f) => + makeLogger( + (options) => self.log({ ...options, message: f(options.message) }) + )) + +/** @internal */ +export const mapInputOptions = dual< + ( + f: (options: Logger.Logger.Options) => Logger.Logger.Options + ) => (self: Logger.Logger) => Logger.Logger, + ( + self: Logger.Logger, + f: (options: Logger.Logger.Options) => Logger.Logger.Options + ) => Logger.Logger +>(2, (self, f) => makeLogger((options) => self.log(f(options)))) + +/** @internal */ +export const filterLogLevel = dual< + ( + f: (logLevel: LogLevel.LogLevel) => boolean + ) => (self: Logger.Logger) => Logger.Logger>, + ( + self: Logger.Logger, + f: (logLevel: LogLevel.LogLevel) => boolean + ) => Logger.Logger> +>(2, (self, f) => + makeLogger((options) => + f(options.logLevel) + ? Option.some(self.log(options)) + : Option.none() + )) + +/** @internal */ +export const map = dual< + ( + f: (output: Output) => Output2 + ) => (self: Logger.Logger) => Logger.Logger, + ( + self: Logger.Logger, + f: (output: Output) => Output2 + ) => Logger.Logger +>(2, (self, f) => makeLogger((options) => f(self.log(options)))) + +/** @internal */ +export const none: Logger.Logger = { + [LoggerTypeId]: loggerVariance, + log: constVoid, + pipe() { + return pipeArguments(this, arguments) + } +} as Logger.Logger + +/** @internal */ +export const simple = (log: (a: A) => B): Logger.Logger => ({ + [LoggerTypeId]: loggerVariance, + log: ({ message }) => log(message), + pipe() { + return pipeArguments(this, arguments) + } +}) + +/** @internal */ +export const succeed = (value: A): Logger.Logger => { + return simple(() => value) +} + +/** @internal */ +export const sync = (evaluate: LazyArg): Logger.Logger => { + return simple(evaluate) +} + +/** @internal */ +export const zip = dual< + ( + that: Logger.Logger + ) => ( + self: Logger.Logger + ) => Logger.Logger, + ( + self: Logger.Logger, + that: Logger.Logger + ) => Logger.Logger +>(2, (self, that) => makeLogger((options) => [self.log(options), that.log(options)])) + +/** @internal */ +export const zipLeft = dual< + ( + that: Logger.Logger + ) => ( + self: Logger.Logger + ) => Logger.Logger, + ( + self: Logger.Logger, + that: Logger.Logger + ) => Logger.Logger +>(2, (self, that) => map(zip(self, that), (tuple) => tuple[0])) + +/** @internal */ +export const zipRight = dual< + ( + that: Logger.Logger + ) => ( + self: Logger.Logger + ) => Logger.Logger, + ( + self: Logger.Logger, + that: Logger.Logger + ) => Logger.Logger +>(2, (self, that) => map(zip(self, that), (tuple) => tuple[1])) + +/** + * Match strings that do not contain any whitespace characters, double quotes, + * or equal signs. + * + * @internal + */ +const textOnly = /^[^\s"=]*$/ + +/** + * Used by both {@link stringLogger} and {@link logfmtLogger} to render a log + * message. + * + * @internal + */ +const format = (quoteValue: (s: string) => string, whitespace?: number | string | undefined) => +( + { annotations, cause, date, fiberId, logLevel, message, spans }: Logger.Logger.Options +): string => { + const formatValue = (value: string): string => value.match(textOnly) ? value : quoteValue(value) + const format = (label: string, value: string): string => `${logSpan_.formatLabel(label)}=${formatValue(value)}` + const append = (label: string, value: string): string => " " + format(label, value) + + let out = format("timestamp", date.toISOString()) + out += append("level", logLevel.label) + out += append("fiber", fiberId_.threadName(fiberId)) + + const messages = Arr.ensure(message) + for (let i = 0; i < messages.length; i++) { + out += append("message", Inspectable.toStringUnknown(messages[i], whitespace)) + } + + if (!Cause.isEmptyType(cause)) { + out += append("cause", Cause.pretty(cause, { renderErrorCause: true })) + } + + for (const span of spans) { + out += " " + logSpan_.render(date.getTime())(span) + } + + for (const [label, value] of annotations) { + out += append(label, Inspectable.toStringUnknown(value, whitespace)) + } + + return out +} + +/** @internal */ +const escapeDoubleQuotes = (s: string) => `"${s.replace(/\\([\s\S])|(")/g, "\\$1$2")}"` + +/** @internal */ +export const stringLogger: Logger.Logger = makeLogger(format(escapeDoubleQuotes)) + +/** @internal */ +export const logfmtLogger: Logger.Logger = makeLogger(format(JSON.stringify, 0)) + +/** @internal */ +export const structuredLogger = makeLogger + readonly spans: Record +}>( + ({ annotations, cause, date, fiberId, logLevel, message, spans }) => { + const now = date.getTime() + const annotationsObj: Record = {} + const spansObj: Record = {} + + if (HashMap.size(annotations) > 0) { + for (const [k, v] of annotations) { + annotationsObj[k] = structuredMessage(v) + } + } + + if (List.isCons(spans)) { + for (const span of spans) { + spansObj[span.label] = now - span.startTime + } + } + + const messageArr = Arr.ensure(message) + return { + message: messageArr.length === 1 ? structuredMessage(messageArr[0]) : messageArr.map(structuredMessage), + logLevel: logLevel.label, + timestamp: date.toISOString(), + cause: Cause.isEmpty(cause) ? undefined : Cause.pretty(cause, { renderErrorCause: true }), + annotations: annotationsObj, + spans: spansObj, + fiberId: fiberId_.threadName(fiberId) + } + } +) + +/** @internal */ +export const structuredMessage = (u: unknown): unknown => { + switch (typeof u) { + case "bigint": + case "function": + case "symbol": { + return String(u) + } + default: { + return Inspectable.toJSON(u) + } + } +} + +/** @internal */ +export const jsonLogger = map(structuredLogger, Inspectable.stringifyCircular) + +/** @internal */ +export const isLogger = (u: unknown): u is Logger.Logger => { + return typeof u === "object" && u != null && LoggerTypeId in u +} + +const withColor = (text: string, ...colors: ReadonlyArray) => { + let out = "" + for (let i = 0; i < colors.length; i++) { + out += `\x1b[${colors[i]}m` + } + return out + text + "\x1b[0m" +} +const withColorNoop = (text: string, ..._colors: ReadonlyArray) => text +const colors = { + bold: "1", + red: "31", + green: "32", + yellow: "33", + blue: "34", + cyan: "36", + white: "37", + gray: "90", + black: "30", + bgBrightRed: "101" +} as const + +const logLevelColors: Record> = { + None: [], + All: [], + Trace: [colors.gray], + Debug: [colors.blue], + Info: [colors.green], + Warning: [colors.yellow], + Error: [colors.red], + Fatal: [colors.bgBrightRed, colors.black] +} +const logLevelStyle: Record = { + None: "", + All: "", + Trace: "color:gray", + Debug: "color:blue", + Info: "color:green", + Warning: "color:orange", + Error: "color:red", + Fatal: "background-color:red;color:white" +} + +const defaultDateFormat = (date: Date): string => + `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}:${ + date.getSeconds().toString().padStart(2, "0") + }.${date.getMilliseconds().toString().padStart(3, "0")}` + +const hasProcessStdout = typeof process === "object" && + process !== null && + typeof process.stdout === "object" && + process.stdout !== null +const processStdoutIsTTY = hasProcessStdout && + process.stdout.isTTY === true +const hasProcessStdoutOrDeno = hasProcessStdout || "Deno" in globalThis + +/** @internal */ +export const prettyLogger = (options?: { + readonly colors?: "auto" | boolean | undefined + readonly stderr?: boolean | undefined + readonly formatDate?: ((date: Date) => string) | undefined + readonly mode?: "browser" | "tty" | "auto" | undefined +}) => { + const mode_ = options?.mode ?? "auto" + const mode = mode_ === "auto" ? (hasProcessStdoutOrDeno ? "tty" : "browser") : mode_ + const isBrowser = mode === "browser" + const showColors = typeof options?.colors === "boolean" ? options.colors : processStdoutIsTTY || isBrowser + const formatDate = options?.formatDate ?? defaultDateFormat + return isBrowser + ? prettyLoggerBrowser({ colors: showColors, formatDate }) + : prettyLoggerTty({ colors: showColors, formatDate, stderr: options?.stderr === true }) +} + +const prettyLoggerTty = (options: { + readonly colors: boolean + readonly stderr: boolean + readonly formatDate: (date: Date) => string +}) => { + const color = options.colors ? withColor : withColorNoop + return makeLogger( + ({ annotations, cause, context, date, fiberId, logLevel, message: message_, spans }) => { + const services = FiberRefs.getOrDefault(context, defaultServices.currentServices) + const console = Context.get(services, consoleTag).unsafe + const log = options.stderr === true ? console.error : console.log + + const message = Arr.ensure(message_) + + let firstLine = color(`[${options.formatDate(date)}]`, colors.white) + + ` ${color(logLevel.label, ...logLevelColors[logLevel._tag])}` + + ` (${fiberId_.threadName(fiberId)})` + + if (List.isCons(spans)) { + const now = date.getTime() + const render = logSpan_.render(now) + for (const span of spans) { + firstLine += " " + render(span) + } + } + + firstLine += ":" + let messageIndex = 0 + if (message.length > 0) { + const firstMaybeString = structuredMessage(message[0]) + if (typeof firstMaybeString === "string") { + firstLine += " " + color(firstMaybeString, colors.bold, colors.cyan) + messageIndex++ + } + } + + log(firstLine) + console.group() + + if (!Cause.isEmpty(cause)) { + log(Cause.pretty(cause, { renderErrorCause: true })) + } + + if (messageIndex < message.length) { + for (; messageIndex < message.length; messageIndex++) { + log(Inspectable.redact(message[messageIndex])) + } + } + + if (HashMap.size(annotations) > 0) { + for (const [key, value] of annotations) { + log(color(`${key}:`, colors.bold, colors.white), Inspectable.redact(value)) + } + } + + console.groupEnd() + } + ) +} + +const prettyLoggerBrowser = (options: { + readonly colors: boolean + readonly formatDate: (date: Date) => string +}) => { + const color = options.colors ? "%c" : "" + return makeLogger( + ({ annotations, cause, context, date, fiberId, logLevel, message: message_, spans }) => { + const services = FiberRefs.getOrDefault(context, defaultServices.currentServices) + const console = Context.get(services, consoleTag).unsafe + const message = Arr.ensure(message_) + + let firstLine = `${color}[${options.formatDate(date)}]` + const firstParams = [] + if (options.colors) { + firstParams.push("color:gray") + } + firstLine += ` ${color}${logLevel.label}${color} (${fiberId_.threadName(fiberId)})` + if (options.colors) { + firstParams.push(logLevelStyle[logLevel._tag], "") + } + if (List.isCons(spans)) { + const now = date.getTime() + const render = logSpan_.render(now) + for (const span of spans) { + firstLine += " " + render(span) + } + } + + firstLine += ":" + + let messageIndex = 0 + if (message.length > 0) { + const firstMaybeString = structuredMessage(message[0]) + if (typeof firstMaybeString === "string") { + firstLine += ` ${color}${firstMaybeString}` + if (options.colors) { + firstParams.push("color:deepskyblue") + } + messageIndex++ + } + } + + console.groupCollapsed(firstLine, ...firstParams) + + if (!Cause.isEmpty(cause)) { + console.error(...Cause.prettyErrors(cause)) + } + + if (messageIndex < message.length) { + for (; messageIndex < message.length; messageIndex++) { + console.log(Inspectable.redact(message[messageIndex])) + } + } + + if (HashMap.size(annotations) > 0) { + for (const [key, value] of annotations) { + const redacted = Inspectable.redact(value) + if (options.colors) { + console.log(`%c${key}:`, "color:gray", redacted) + } else { + console.log(`${key}:`, redacted) + } + } + } + + console.groupEnd() + } + ) +} + +/** @internal */ +export const prettyLoggerDefault = globalValue("effect/Logger/prettyLoggerDefault", () => prettyLogger()) diff --git a/backend/node_modules/effect/src/internal/mailbox.ts b/backend/node_modules/effect/src/internal/mailbox.ts new file mode 100644 index 0000000000000000000000000000000000000000..57f5b778a6bd882304825c7740bb0b40b7c3be43 --- /dev/null +++ b/backend/node_modules/effect/src/internal/mailbox.ts @@ -0,0 +1,561 @@ +import * as Arr from "../Array.js" +import type { Cause } from "../Cause.js" +import { NoSuchElementException } from "../Cause.js" +import type { Channel } from "../Channel.js" +import * as Chunk from "../Chunk.js" +import type { Effect } from "../Effect.js" +import * as Effectable from "../Effectable.js" +import type { Exit } from "../Exit.js" +import { dual } from "../Function.js" +import * as Inspectable from "../Inspectable.js" +import type * as Api from "../Mailbox.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty } from "../Predicate.js" +import type { Scheduler } from "../Scheduler.js" +import type { Scope } from "../Scope.js" +import type { Stream } from "../Stream.js" +import * as channel from "./channel.js" +import * as channelExecutor from "./channel/channelExecutor.js" +import * as coreChannel from "./core-stream.js" +import * as core from "./core.js" +import * as circular from "./effect/circular.js" +import * as fiberRuntime from "./fiberRuntime.js" +import * as stream from "./stream.js" + +/** @internal */ +export const TypeId: Api.TypeId = Symbol.for("effect/Mailbox") as Api.TypeId + +/** @internal */ +export const ReadonlyTypeId: Api.ReadonlyTypeId = Symbol.for("effect/Mailbox/ReadonlyMailbox") as Api.ReadonlyTypeId + +/** @internal */ +export const isMailbox = (u: unknown): u is Api.Mailbox => hasProperty(u, TypeId) + +/** @internal */ +export const isReadonlyMailbox = (u: unknown): u is Api.ReadonlyMailbox => + hasProperty(u, ReadonlyTypeId) + +type MailboxState = { + readonly _tag: "Open" + readonly takers: Set<(_: Effect) => void> + readonly offers: Set> + readonly awaiters: Set<(_: Effect) => void> +} | { + readonly _tag: "Closing" + readonly takers: Set<(_: Effect) => void> + readonly offers: Set> + readonly awaiters: Set<(_: Effect) => void> + readonly exit: Exit +} | { + readonly _tag: "Done" + readonly exit: Exit +} + +type OfferEntry = { + readonly _tag: "Array" + readonly remaining: Array + offset: number + readonly resume: (_: Effect>) => void +} | { + readonly _tag: "Single" + readonly message: A + readonly resume: (_: Effect) => void +} + +const empty = Chunk.empty() +const exitEmpty = core.exitSucceed(empty) +const exitFalse = core.exitSucceed(false) +const exitTrue = core.exitSucceed(true) +const constDone = [empty, true] as const + +class MailboxImpl extends Effectable.Class, done: boolean], E> + implements Api.Mailbox +{ + readonly [TypeId]: Api.TypeId = TypeId + readonly [ReadonlyTypeId]: Api.ReadonlyTypeId = ReadonlyTypeId + private state: MailboxState = { + _tag: "Open", + takers: new Set(), + offers: new Set(), + awaiters: new Set() + } + private messages: Array = [] + private messagesChunk = Chunk.empty() + constructor( + readonly scheduler: Scheduler, + private capacity: number, + readonly strategy: "suspend" | "dropping" | "sliding" + ) { + super() + } + + offer(message: A): Effect { + return core.suspend(() => { + if (this.state._tag !== "Open") { + return exitFalse + } else if (this.messages.length + this.messagesChunk.length >= this.capacity) { + switch (this.strategy) { + case "dropping": + return exitFalse + case "suspend": + if (this.capacity <= 0 && this.state.takers.size > 0) { + this.messages.push(message) + this.releaseTaker() + return exitTrue + } + return this.offerRemainingSingle(message) + case "sliding": + this.unsafeTake() + this.messages.push(message) + return exitTrue + } + } + this.messages.push(message) + this.scheduleReleaseTaker() + return exitTrue + }) + } + unsafeOffer(message: A): boolean { + if (this.state._tag !== "Open") { + return false + } else if (this.messages.length + this.messagesChunk.length >= this.capacity) { + if (this.strategy === "sliding") { + this.unsafeTake() + this.messages.push(message) + return true + } else if (this.capacity <= 0 && this.state.takers.size > 0) { + this.messages.push(message) + this.releaseTaker() + return true + } + return false + } + this.messages.push(message) + this.scheduleReleaseTaker() + return true + } + offerAll(messages: Iterable): Effect> { + return core.suspend(() => { + if (this.state._tag !== "Open") { + return core.succeed(Chunk.fromIterable(messages)) + } + const remaining = this.unsafeOfferAllArray(messages) + if (remaining.length === 0) { + return exitEmpty + } else if (this.strategy === "dropping") { + return core.succeed(Chunk.unsafeFromArray(remaining)) + } + return this.offerRemainingArray(remaining) + }) + } + unsafeOfferAll(messages: Iterable): Chunk.Chunk { + return Chunk.unsafeFromArray(this.unsafeOfferAllArray(messages)) + } + unsafeOfferAllArray(messages: Iterable): Array { + if (this.state._tag !== "Open") { + return Arr.fromIterable(messages) + } else if (this.capacity === Number.POSITIVE_INFINITY || this.strategy === "sliding") { + if (this.messages.length > 0) { + this.messagesChunk = Chunk.appendAll(this.messagesChunk, Chunk.unsafeFromArray(this.messages)) + } + if (this.strategy === "sliding") { + this.messagesChunk = this.messagesChunk.pipe( + Chunk.appendAll(Chunk.fromIterable(messages)), + Chunk.takeRight(this.capacity) + ) + } else if (Chunk.isChunk(messages)) { + this.messagesChunk = Chunk.appendAll(this.messagesChunk, messages) + } else { + this.messages = Arr.fromIterable(messages) + } + this.scheduleReleaseTaker() + return [] + } + const free = this.capacity <= 0 + ? this.state.takers.size + : this.capacity - this.messages.length - this.messagesChunk.length + if (free === 0) { + return Arr.fromIterable(messages) + } + const remaining: Array = [] + let i = 0 + for (const message of messages) { + if (i < free) { + this.messages.push(message) + } else { + remaining.push(message) + } + i++ + } + this.scheduleReleaseTaker() + return remaining + } + fail(error: E) { + return this.done(core.exitFail(error)) + } + failCause(cause: Cause) { + return this.done(core.exitFailCause(cause)) + } + unsafeDone(exit: Exit): boolean { + if (this.state._tag !== "Open") { + return false + } else if (this.state.offers.size === 0 && this.messages.length === 0 && this.messagesChunk.length === 0) { + this.finalize(exit) + return true + } + this.state = { ...this.state, _tag: "Closing", exit } + return true + } + shutdown: Effect = core.sync(() => { + if (this.state._tag === "Done") { + return true + } + this.messages = [] + this.messagesChunk = empty + const offers = this.state.offers + this.finalize(this.state._tag === "Open" ? core.exitVoid : this.state.exit) + if (offers.size > 0) { + for (const entry of offers) { + if (entry._tag === "Single") { + entry.resume(exitFalse) + } else { + entry.resume(core.exitSucceed(Chunk.unsafeFromArray(entry.remaining.slice(entry.offset)))) + } + } + offers.clear() + } + return true + }) + done(exit: Exit) { + return core.sync(() => this.unsafeDone(exit)) + } + end = this.done(core.exitVoid) + clear: Effect, E> = core.suspend(() => { + if (this.state._tag === "Done") { + return core.exitAs(this.state.exit, empty) + } + const messages = this.unsafeTakeAll() + this.releaseCapacity() + return core.succeed(messages) + }) + takeAll: Effect, done: boolean], E> = core.suspend(() => { + if (this.state._tag === "Done") { + return core.exitAs(this.state.exit, constDone) + } + const messages = this.unsafeTakeAll() + if (messages.length === 0) { + return core.zipRight(this.awaitTake, this.takeAll) + } + return core.succeed([messages, this.releaseCapacity()]) + }) + takeN(n: number): Effect, done: boolean], E> { + return core.suspend(() => { + if (this.state._tag === "Done") { + return core.exitAs(this.state.exit, constDone) + } else if (n <= 0) { + return core.succeed([empty, false]) + } + n = Math.min(n, this.capacity) + let messages: Chunk.Chunk + if (n <= this.messagesChunk.length) { + messages = Chunk.take(this.messagesChunk, n) + this.messagesChunk = Chunk.drop(this.messagesChunk, n) + } else if (n <= this.messages.length + this.messagesChunk.length) { + this.messagesChunk = Chunk.appendAll(this.messagesChunk, Chunk.unsafeFromArray(this.messages)) + this.messages = [] + messages = Chunk.take(this.messagesChunk, n) + this.messagesChunk = Chunk.drop(this.messagesChunk, n) + } else { + return core.zipRight(this.awaitTake, this.takeN(n)) + } + return core.succeed([messages, this.releaseCapacity()]) + }) + } + unsafeTake(): Exit | undefined { + if (this.state._tag === "Done") { + return core.exitZipRight(this.state.exit, core.exitFail(new NoSuchElementException())) + } + let message: A + if (this.messagesChunk.length > 0) { + message = Chunk.unsafeHead(this.messagesChunk) + this.messagesChunk = Chunk.drop(this.messagesChunk, 1) + } else if (this.messages.length > 0) { + message = this.messages[0] + this.messagesChunk = Chunk.drop(Chunk.unsafeFromArray(this.messages), 1) + this.messages = [] + } else if (this.capacity <= 0 && this.state.offers.size > 0) { + this.capacity = 1 + this.releaseCapacity() + this.capacity = 0 + return this.messages.length > 0 ? core.exitSucceed(this.messages.pop()!) : undefined + } else { + return undefined + } + this.releaseCapacity() + return core.exitSucceed(message) + } + take: Effect = core.suspend(() => + this.unsafeTake() ?? core.zipRight(this.awaitTake, this.take) + ) + await: Effect = core.asyncInterrupt((resume) => { + if (this.state._tag === "Done") { + return resume(this.state.exit) + } + this.state.awaiters.add(resume) + return core.sync(() => { + if (this.state._tag !== "Done") { + this.state.awaiters.delete(resume) + } + }) + }) + unsafeSize(): Option.Option { + const size = this.messages.length + this.messagesChunk.length + return this.state._tag === "Done" ? Option.none() : Option.some(size) + } + size = core.sync(() => this.unsafeSize()) + + commit() { + return this.takeAll + } + pipe() { + return pipeArguments(this, arguments) + } + toJSON() { + return { + _id: "effect/Mailbox", + state: this.state._tag, + size: this.unsafeSize().toJSON() + } + } + toString(): string { + return Inspectable.format(this) + } + [Inspectable.NodeInspectSymbol]() { + return Inspectable.format(this) + } + + private offerRemainingSingle(message: A) { + return core.asyncInterrupt((resume) => { + if (this.state._tag !== "Open") { + return resume(exitFalse) + } + const entry: OfferEntry = { _tag: "Single", message, resume } + this.state.offers.add(entry) + return core.sync(() => { + if (this.state._tag === "Open") { + this.state.offers.delete(entry) + } + }) + }) + } + private offerRemainingArray(remaining: Array) { + return core.asyncInterrupt>((resume) => { + if (this.state._tag !== "Open") { + return resume(core.exitSucceed(Chunk.unsafeFromArray(remaining))) + } + const entry: OfferEntry = { _tag: "Array", remaining, offset: 0, resume } + this.state.offers.add(entry) + return core.sync(() => { + if (this.state._tag === "Open") { + this.state.offers.delete(entry) + } + }) + }) + } + private releaseCapacity(): boolean { + if (this.state._tag === "Done") { + return this.state.exit._tag === "Success" + } else if (this.state.offers.size === 0) { + if (this.state._tag === "Closing" && this.messages.length === 0 && this.messagesChunk.length === 0) { + this.finalize(this.state.exit) + return this.state.exit._tag === "Success" + } + return false + } + let n = this.capacity - this.messages.length - this.messagesChunk.length + for (const entry of this.state.offers) { + if (n === 0) return false + else if (entry._tag === "Single") { + this.messages.push(entry.message) + n-- + entry.resume(exitTrue) + this.state.offers.delete(entry) + } else { + for (; entry.offset < entry.remaining.length; entry.offset++) { + if (n === 0) return false + this.messages.push(entry.remaining[entry.offset]) + n-- + } + entry.resume(exitEmpty) + this.state.offers.delete(entry) + } + } + return false + } + private awaitTake = core.asyncInterrupt((resume) => { + if (this.state._tag === "Done") { + return resume(this.state.exit) + } + this.state.takers.add(resume) + return core.sync(() => { + if (this.state._tag !== "Done") { + this.state.takers.delete(resume) + } + }) + }) + + private scheduleRunning = false + private scheduleReleaseTaker() { + if (this.scheduleRunning) { + return + } + this.scheduleRunning = true + this.scheduler.scheduleTask(this.releaseTaker, 0) + } + private releaseTaker = () => { + this.scheduleRunning = false + if (this.state._tag === "Done") { + return + } else if (this.state.takers.size === 0) { + return + } + for (const taker of this.state.takers) { + this.state.takers.delete(taker) + taker(core.exitVoid) + if (this.messages.length + this.messagesChunk.length === 0) { + break + } + } + } + + private unsafeTakeAll() { + if (this.messagesChunk.length > 0) { + const messages = this.messages.length > 0 ? + Chunk.appendAll(this.messagesChunk, Chunk.unsafeFromArray(this.messages)) : + this.messagesChunk + this.messagesChunk = empty + this.messages = [] + return messages + } else if (this.messages.length > 0) { + const messages = Chunk.unsafeFromArray(this.messages) + this.messages = [] + return messages + } else if (this.state._tag !== "Done" && this.state.offers.size > 0) { + this.capacity = 1 + this.releaseCapacity() + this.capacity = 0 + return Chunk.of(this.messages.pop()!) + } + return empty + } + + private finalize(exit: Exit) { + if (this.state._tag === "Done") { + return + } + const openState = this.state + this.state = { _tag: "Done", exit } + for (const taker of openState.takers) { + taker(exit) + } + openState.takers.clear() + for (const awaiter of openState.awaiters) { + awaiter(exit) + } + openState.awaiters.clear() + } +} + +/** @internal */ +export const make = ( + capacity?: number | { + readonly capacity?: number | undefined + readonly strategy?: "suspend" | "dropping" | "sliding" | undefined + } | undefined +): Effect> => + core.withFiberRuntime((fiber) => + core.succeed( + new MailboxImpl( + fiber.currentScheduler, + typeof capacity === "number" ? capacity : capacity?.capacity ?? Number.POSITIVE_INFINITY, + typeof capacity === "number" ? "suspend" : capacity?.strategy ?? "suspend" + ) + ) + ) + +/** @internal */ +export const into: { + ( + self: Api.Mailbox + ): (effect: Effect) => Effect + ( + effect: Effect, + self: Api.Mailbox + ): Effect +} = dual( + 2, + ( + effect: Effect, + self: Api.Mailbox + ): Effect => + core.uninterruptibleMask((restore) => + core.matchCauseEffect(restore(effect), { + onFailure: (cause) => self.failCause(cause), + onSuccess: (_) => self.end + }) + ) +) + +/** @internal */ +export const toChannel = (self: Api.ReadonlyMailbox): Channel, unknown, E> => { + const loop: Channel, unknown, E> = coreChannel.flatMap(self.takeAll, ([messages, done]) => + done + ? messages.length === 0 ? coreChannel.void : coreChannel.write(messages) + : channel.zipRight(coreChannel.write(messages), loop)) + return loop +} + +/** @internal */ +export const toStream = (self: Api.ReadonlyMailbox): Stream => stream.fromChannel(toChannel(self)) + +/** @internal */ +export const fromStream: { + (options?: { + readonly capacity?: number | undefined + readonly strategy?: "suspend" | "dropping" | "sliding" | undefined + }): (self: Stream) => Effect, never, R | Scope> + ( + self: Stream, + options?: { + readonly capacity?: number | undefined + readonly strategy?: "suspend" | "dropping" | "sliding" | undefined + } + ): Effect, never, R | Scope> +} = dual((args) => stream.isStream(args[0]), ( + self: Stream, + options?: { + readonly capacity?: number | undefined + readonly strategy?: "suspend" | "dropping" | "sliding" | undefined + } +): Effect, never, R | Scope> => + core.tap( + fiberRuntime.acquireRelease( + make(options), + (mailbox) => mailbox.shutdown + ), + (mailbox) => { + const writer: Channel, never, E> = coreChannel.readWithCause({ + onInput: (input: Chunk.Chunk) => coreChannel.flatMap(mailbox.offerAll(input), () => writer), + onFailure: (cause: Cause) => mailbox.failCause(cause), + onDone: () => mailbox.end + }) + return fiberRuntime.scopeWith((scope) => + stream.toChannel(self).pipe( + coreChannel.pipeTo(writer), + channelExecutor.runIn(scope), + circular.forkIn(scope) + ) + ) + } + )) diff --git a/backend/node_modules/effect/src/internal/managedRuntime.ts b/backend/node_modules/effect/src/internal/managedRuntime.ts new file mode 100644 index 0000000000000000000000000000000000000000..8addd717cde292291b91deb348abaeeede6b64e5 --- /dev/null +++ b/backend/node_modules/effect/src/internal/managedRuntime.ts @@ -0,0 +1,137 @@ +import type * as Effect from "../Effect.js" +import * as Effectable from "../Effectable.js" +import type { Exit } from "../Exit.js" +import type * as Fiber from "../Fiber.js" +import type * as Layer from "../Layer.js" +import type * as M from "../ManagedRuntime.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty } from "../Predicate.js" +import type * as Runtime from "../Runtime.js" +import * as Scheduler from "../Scheduler.js" +import * as Scope from "../Scope.js" +import type { Mutable } from "../Types.js" +import * as core from "./core.js" +import * as fiberRuntime from "./fiberRuntime.js" +import * as internalLayer from "./layer.js" +import * as circular from "./managedRuntime/circular.js" +import * as internalRuntime from "./runtime.js" + +interface ManagedRuntimeImpl extends M.ManagedRuntime { + readonly scope: Scope.CloseableScope + cachedRuntime: Runtime.Runtime | undefined +} + +/** @internal */ +export const isManagedRuntime = (u: unknown): u is M.ManagedRuntime => hasProperty(u, circular.TypeId) + +function provide( + managed: ManagedRuntimeImpl, + effect: Effect.Effect +): Effect.Effect { + return core.flatMap( + managed.runtimeEffect, + (rt) => + core.withFiberRuntime((fiber) => { + fiber.setFiberRefs(rt.fiberRefs) + fiber.currentRuntimeFlags = rt.runtimeFlags + return core.provideContext(effect, rt.context) + }) + ) +} + +const ManagedRuntimeProto = { + ...Effectable.CommitPrototype, + [circular.TypeId]: circular.TypeId, + pipe() { + return pipeArguments(this, arguments) + }, + commit(this: ManagedRuntimeImpl) { + return this.runtimeEffect + } +} + +/** @internal */ +export const make = ( + layer: Layer.Layer, + memoMap?: Layer.MemoMap +): M.ManagedRuntime => { + memoMap = memoMap ?? internalLayer.unsafeMakeMemoMap() + const scope = internalRuntime.unsafeRunSyncEffect(fiberRuntime.scopeMake()) + let buildFiber: Fiber.RuntimeFiber, ER> | undefined + const runtimeEffect = core.suspend(() => { + if (!buildFiber) { + const scheduler = new Scheduler.SyncScheduler() + buildFiber = internalRuntime.unsafeForkEffect( + core.tap( + Scope.extend( + internalLayer.toRuntimeWithMemoMap(layer, memoMap), + scope + ), + (rt) => { + self.cachedRuntime = rt + } + ), + { scope, scheduler } + ) + scheduler.flush() + } + return core.flatten(buildFiber.await) + }) + const self: ManagedRuntimeImpl = Object.assign(Object.create(ManagedRuntimeProto), { + memoMap, + scope, + runtimeEffect, + cachedRuntime: undefined, + runtime() { + return self.cachedRuntime === undefined ? + internalRuntime.unsafeRunPromiseEffect(self.runtimeEffect) : + Promise.resolve(self.cachedRuntime) + }, + dispose(): Promise { + return internalRuntime.unsafeRunPromiseEffect(self.disposeEffect) + }, + disposeEffect: core.suspend(() => { + ;(self as Mutable>).runtimeEffect = core.die("ManagedRuntime disposed") + self.cachedRuntime = undefined + return Scope.close(self.scope, core.exitVoid) + }), + runFork(effect: Effect.Effect, options?: Runtime.RunForkOptions): Fiber.RuntimeFiber { + return self.cachedRuntime === undefined ? + internalRuntime.unsafeForkEffect(provide(self, effect), options) : + internalRuntime.unsafeFork(self.cachedRuntime)(effect, options) + }, + runSyncExit(effect: Effect.Effect): Exit { + return self.cachedRuntime === undefined ? + internalRuntime.unsafeRunSyncExitEffect(provide(self, effect)) : + internalRuntime.unsafeRunSyncExit(self.cachedRuntime)(effect) + }, + runSync(effect: Effect.Effect): A { + return self.cachedRuntime === undefined ? + internalRuntime.unsafeRunSyncEffect(provide(self, effect)) : + internalRuntime.unsafeRunSync(self.cachedRuntime)(effect) + }, + runPromiseExit(effect: Effect.Effect, options?: { + readonly signal?: AbortSignal | undefined + }): Promise> { + return self.cachedRuntime === undefined ? + internalRuntime.unsafeRunPromiseExitEffect(provide(self, effect), options) : + internalRuntime.unsafeRunPromiseExit(self.cachedRuntime)(effect, options) + }, + runCallback( + effect: Effect.Effect, + options?: Runtime.RunCallbackOptions | undefined + ): Runtime.Cancel { + return self.cachedRuntime === undefined ? + internalRuntime.unsafeRunCallback(internalRuntime.defaultRuntime)(provide(self, effect), options) : + internalRuntime.unsafeRunCallback(self.cachedRuntime)(effect, options) + }, + runPromise(effect: Effect.Effect, options?: { + readonly signal?: AbortSignal | undefined + }): Promise { + return self.cachedRuntime === undefined ? + internalRuntime.unsafeRunPromiseEffect(provide(self, effect), options) : + internalRuntime.unsafeRunPromise(self.cachedRuntime)(effect, options) + } + }) + return self +} diff --git a/backend/node_modules/effect/src/internal/managedRuntime/circular.ts b/backend/node_modules/effect/src/internal/managedRuntime/circular.ts new file mode 100644 index 0000000000000000000000000000000000000000..9abefc95c1f7552aea0ce0f8bc2be1e34970b52a --- /dev/null +++ b/backend/node_modules/effect/src/internal/managedRuntime/circular.ts @@ -0,0 +1,6 @@ +import type * as M from "../../ManagedRuntime.js" + +// circular with Layer + +/** @internal */ +export const TypeId: M.TypeId = Symbol.for("effect/ManagedRuntime") as M.TypeId diff --git a/backend/node_modules/effect/src/internal/matcher.ts b/backend/node_modules/effect/src/internal/matcher.ts new file mode 100644 index 0000000000000000000000000000000000000000..3bfe589de556678c2f85e4b59e3cd845852e2e39 --- /dev/null +++ b/backend/node_modules/effect/src/internal/matcher.ts @@ -0,0 +1,652 @@ +import * as Either from "../Either.js" +import { dual, identity } from "../Function.js" +import type { + Case, + Matcher, + MatcherTypeId, + Not, + SafeRefinement, + TypeMatcher, + Types, + ValueMatcher, + When +} from "../Match.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import type * as Predicate from "../Predicate.js" +import type { Unify } from "../Unify.js" + +/** @internal */ +export const TypeId: MatcherTypeId = Symbol.for( + "@effect/matcher/Matcher" +) as MatcherTypeId + +const TypeMatcherProto: Omit, "cases"> = { + [TypeId]: { + _input: identity, + _filters: identity, + _remaining: identity, + _result: identity, + _return: identity + }, + _tag: "TypeMatcher", + add( + this: TypeMatcher, + _case: Case + ): TypeMatcher { + return makeTypeMatcher([...this.cases, _case]) + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +function makeTypeMatcher( + cases: ReadonlyArray +): TypeMatcher { + const matcher = Object.create(TypeMatcherProto) + matcher.cases = cases + return matcher +} + +const ValueMatcherProto: Omit< + ValueMatcher, + "provided" | "value" +> = { + [TypeId]: { + _input: identity, + _filters: identity, + _remaining: identity, + _result: identity, + _provided: identity, + _return: identity + }, + _tag: "ValueMatcher", + add( + this: ValueMatcher, + _case: Case + ): ValueMatcher { + if (this.value._tag === "Right") { + return this + } + + if (_case._tag === "When" && _case.guard(this.provided) === true) { + return makeValueMatcher( + this.provided, + Either.right(_case.evaluate(this.provided)) + ) + } else if (_case._tag === "Not" && _case.guard(this.provided) === false) { + return makeValueMatcher( + this.provided, + Either.right(_case.evaluate(this.provided)) + ) + } + + return this + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +function makeValueMatcher( + provided: Pr, + value: Either.Either +): ValueMatcher { + const matcher = Object.create(ValueMatcherProto) + matcher.provided = provided + matcher.value = value + return matcher +} + +const makeWhen = ( + guard: (u: unknown) => boolean, + evaluate: (input: unknown) => any +): When => ({ + _tag: "When", + guard, + evaluate +}) + +const makeNot = ( + guard: (u: unknown) => boolean, + evaluate: (input: unknown) => any +): Not => ({ + _tag: "Not", + guard, + evaluate +}) + +const makePredicate = (pattern: unknown): Predicate.Predicate => { + if (typeof pattern === "function") { + return pattern as Predicate.Predicate + } else if (Array.isArray(pattern)) { + const predicates = pattern.map(makePredicate) + const len = predicates.length + + return (u: unknown) => { + if (!Array.isArray(u)) { + return false + } + + for (let i = 0; i < len; i++) { + if (predicates[i](u[i]) === false) { + return false + } + } + + return true + } + } else if (pattern !== null && typeof pattern === "object") { + const keysAndPredicates = Object.entries(pattern).map( + ([k, p]) => [k, makePredicate(p)] as const + ) + const len = keysAndPredicates.length + + return (u: unknown) => { + if (typeof u !== "object" || u === null) { + return false + } + + for (let i = 0; i < len; i++) { + const [key, predicate] = keysAndPredicates[i] + if (!(key in u) || predicate((u as any)[key]) === false) { + return false + } + } + + return true + } + } + + return (u: unknown) => u === pattern +} + +const makeOrPredicate = ( + patterns: ReadonlyArray +): Predicate.Predicate => { + const predicates = patterns.map(makePredicate) + const len = predicates.length + + return (u: unknown) => { + for (let i = 0; i < len; i++) { + if (predicates[i](u) === true) { + return true + } + } + + return false + } +} + +const makeAndPredicate = ( + patterns: ReadonlyArray +): Predicate.Predicate => { + const predicates = patterns.map(makePredicate) + const len = predicates.length + + return (u: unknown) => { + for (let i = 0; i < len; i++) { + if (predicates[i](u) === false) { + return false + } + } + + return true + } +} + +/** @internal */ +export const type = (): Matcher< + I, + Types.Without, + I, + never, + never +> => makeTypeMatcher([]) + +/** @internal */ +export const value = ( + i: I +): Matcher, I, never, I> => makeValueMatcher(i, Either.left(i)) + +/** @internal */ +export const valueTags: { + < + const I, + P extends + & { readonly [Tag in Types.Tags<"_tag", I> & string]: (_: Extract) => any } + & { readonly [Tag in Exclude>]: never } + >(fields: P): (input: I) => Unify> + < + const I, + P extends + & { readonly [Tag in Types.Tags<"_tag", I> & string]: (_: Extract) => any } + & { readonly [Tag in Exclude>]: never } + >(input: I, fields: P): Unify> +} = dual( + 2, + < + const I, + P extends + & { readonly [Tag in Types.Tags<"_tag", I> & string]: (_: Extract) => any } + & { readonly [Tag in Exclude>]: never } + >(input: I, fields: P): Unify> => { + const match: any = tagsExhaustive(fields as any)(makeTypeMatcher([])) + return match(input) + } +) + +/** @internal */ +export const typeTags = () => +< + P extends { + readonly [Tag in Types.Tags<"_tag", I> & string]: ( + _: Extract + ) => any + } +>( + fields: P +) => { + const match: any = tagsExhaustive(fields as any)(makeTypeMatcher([])) + return (input: I): Unify> => match(input) +} + +/** @internal */ +export const withReturnType = () => +(self: Matcher): [Ret] extends [ + [A] extends [never] ? any : A +] ? Matcher + : "withReturnType constraint does not extend Result type" => self as any + +/** @internal */ +export const when = < + R, + const P extends Types.PatternPrimitive | Types.PatternBase, + Ret, + Fn extends (_: Types.WhenMatch) => Ret +>( + pattern: P, + f: Fn +) => +( + self: Matcher +): Matcher< + I, + Types.AddWithout>, + Types.ApplyFilters>>, + A | ReturnType, + Pr, + Ret +> => (self as any).add(makeWhen(makePredicate(pattern), f as any)) + +/** @internal */ +export const whenOr = < + R, + const P extends ReadonlyArray< + Types.PatternPrimitive | Types.PatternBase + >, + Ret, + Fn extends (_: Types.WhenMatch) => Ret +>( + ...args: [...patterns: P, f: Fn] +) => +( + self: Matcher +): Matcher< + I, + Types.AddWithout>, + Types.ApplyFilters>>, + A | ReturnType, + Pr, + Ret +> => { + const onMatch = args[args.length - 1] as any + const patterns = args.slice(0, -1) as unknown as P + return (self as any).add(makeWhen(makeOrPredicate(patterns), onMatch)) +} + +/** @internal */ +export const whenAnd = < + R, + const P extends ReadonlyArray< + Types.PatternPrimitive | Types.PatternBase + >, + Ret, + Fn extends (_: Types.WhenMatch>) => Ret +>( + ...args: [...patterns: P, f: Fn] +) => +( + self: Matcher +): Matcher< + I, + Types.AddWithout>>, + Types.ApplyFilters< + I, + Types.AddWithout>> + >, + A | ReturnType, + Pr +> => { + const onMatch = args[args.length - 1] as any + const patterns = args.slice(0, -1) as unknown as P + return (self as any).add(makeWhen(makeAndPredicate(patterns), onMatch)) +} + +/** @internal */ +export const discriminator = (field: D) => +< + R, + P extends Types.Tags & string, + Ret, + Fn extends (_: Extract>) => Ret +>( + ...pattern: [ + first: P, + ...values: Array

, + f: Fn + ] +) => { + const f = pattern[pattern.length - 1] + const values: Array

= pattern.slice(0, -1) as any + const pred = values.length === 1 + ? (_: any) => _ != null && _[field] === values[0] + : (_: any) => _ != null && values.includes(_[field]) + + return ( + self: Matcher + ): Matcher< + I, + Types.AddWithout>>, + Types.ApplyFilters>>>, + A | ReturnType, + Pr, + Ret + > => (self as any).add(makeWhen(pred, f as any)) as any +} + +/** @internal */ +export const discriminatorStartsWith = (field: D) => +< + R, + P extends string, + Ret, + Fn extends (_: Extract>) => Ret +>( + pattern: P, + f: Fn +) => { + const pred = (_: any) => _ != null && typeof _[field] === "string" && _[field].startsWith(pattern) + + return ( + self: Matcher + ): Matcher< + I, + Types.AddWithout>>, + Types.ApplyFilters< + I, + Types.AddWithout>> + >, + A | ReturnType, + Pr, + Ret + > => (self as any).add(makeWhen(pred, f as any)) as any +} + +/** @internal */ +export const discriminators = (field: D) => +< + R, + Ret, + P extends + & { + readonly [Tag in Types.Tags & string]?: + | ((_: Extract>) => Ret) + | undefined + } + & { readonly [Tag in Exclude>]: never } +>( + fields: P +) => { + const predicate = makeWhen( + (arg: any) => arg != null && arg[field] in fields, + (data: any) => (fields as any)[data[field]](data) + ) + + return ( + self: Matcher + ): Matcher< + I, + Types.AddWithout>>, + Types.ApplyFilters>>>, + A | ReturnType, + Pr, + Ret + > => (self as any).add(predicate) +} + +/** @internal */ +export const discriminatorsExhaustive: ( + field: D +) => < + R, + Ret, + P extends + & { + readonly [Tag in Types.Tags & string]: ( + _: Extract> + ) => Ret + } + & { readonly [Tag in Exclude>]: never } +>( + fields: P +) => ( + self: Matcher +) => [Pr] extends [never] ? (u: I) => Unify> + : Unify> = (field: string) => (fields: object) => { + const addCases = discriminators(field)(fields) + return (matcher: any) => exhaustive(addCases(matcher)) + } + +/** @internal */ +export const tag: < + R, + P extends Types.Tags<"_tag", R> & string, + Ret, + Fn extends (_: Extract>) => Ret +>( + ...pattern: [ + first: P, + ...values: Array

, + f: Fn + ] +) => ( + self: Matcher +) => Matcher< + I, + Types.AddWithout>>, + Types.ApplyFilters>>>, + ReturnType | A, + Pr, + Ret +> = discriminator("_tag") + +/** @internal */ +export const tagStartsWith = discriminatorStartsWith("_tag") + +/** @internal */ +export const tags = discriminators("_tag") + +/** @internal */ +export const tagsExhaustive = discriminatorsExhaustive("_tag") + +/** @internal */ +export const not = < + R, + const P extends Types.PatternPrimitive | Types.PatternBase, + Ret, + Fn extends (_: Types.NotMatch) => Ret +>( + pattern: P, + f: Fn +) => +( + self: Matcher +): Matcher< + I, + Types.AddOnly>, + Types.ApplyFilters>>, + A | ReturnType, + Pr, + Ret +> => (self as any).add(makeNot(makePredicate(pattern), f as any)) + +/** @internal */ +export const nonEmptyString: SafeRefinement = + ((u: unknown) => typeof u === "string" && u.length > 0) as any + +/** @internal */ +export const is: < + Literals extends ReadonlyArray +>( + ...literals: Literals +) => SafeRefinement = (...literals): any => { + const len = literals.length + return (u: unknown) => { + for (let i = 0; i < len; i++) { + if (u === literals[i]) { + return true + } + } + return false + } +} + +/** @internal */ +export const any: SafeRefinement = (() => true) as any + +/** @internal */ +export const defined = (u: A): u is A & {} => (u !== undefined && u !== null) as any + +/** @internal */ +export const instanceOf = any>( + constructor: A +): SafeRefinement, never> => ((u: unknown) => u instanceof constructor) as any + +/** @internal */ +export const instanceOfUnsafe: any>( + constructor: A +) => SafeRefinement, InstanceType> = instanceOf + +/** @internal */ +export const orElse = + Ret>(f: F) => + (self: Matcher): [Pr] extends [never] ? (input: I) => Unify | A> + : Unify | A> => + { + const result = either(self) + + if (Either.isEither(result)) { + // @ts-expect-error + return result._tag === "Right" ? result.right : f(result.left) + } + + // @ts-expect-error + return (input: I) => { + const a = result(input) + return a._tag === "Right" ? a.right : f(a.left) + } + } + +/** @internal */ +export const orElseAbsurd = ( + self: Matcher +): [Pr] extends [never] ? (input: I) => Unify : Unify => + orElse(() => { + throw new Error("effect/Match/orElseAbsurd: absurd") + })(self) + +/** @internal */ +export const either: ( + self: Matcher +) => [Pr] extends [never] ? (input: I) => Either.Either, R> + : Either.Either, R> = ((self: Matcher) => { + if (self._tag === "ValueMatcher") { + return self.value + } + + const len = self.cases.length + if (len === 1) { + const _case = self.cases[0] + return (input: I): Either.Either => { + if (_case._tag === "When" && _case.guard(input) === true) { + return Either.right(_case.evaluate(input)) + } else if (_case._tag === "Not" && _case.guard(input) === false) { + return Either.right(_case.evaluate(input)) + } + return Either.left(input as any) + } + } + return (input: I): Either.Either => { + for (let i = 0; i < len; i++) { + const _case = self.cases[i] + if (_case._tag === "When" && _case.guard(input) === true) { + return Either.right(_case.evaluate(input)) + } else if (_case._tag === "Not" && _case.guard(input) === false) { + return Either.right(_case.evaluate(input)) + } + } + + return Either.left(input as any) + } + }) as any + +/** @internal */ +export const option: ( + self: Matcher +) => [Pr] extends [never] ? (input: I) => Option.Option> + : Option.Option> = ((self: Matcher) => { + const toEither = either(self) + if (Either.isEither(toEither)) { + return Either.match(toEither, { + onLeft: () => Option.none(), + onRight: Option.some + }) + } + return (input: I): Option.Option => + Either.match((toEither as any)(input), { + onLeft: () => Option.none(), + onRight: Option.some as any + }) + }) as any + +const getExhaustiveAbsurdErrorMessage = "effect/Match/exhaustive: absurd" + +/** @internal */ +export const exhaustive: ( + self: Matcher +) => [Pr] extends [never] ? (u: I) => Unify : Unify = (( + self: Matcher +) => { + const toEither = either(self as any) + + if (Either.isEither(toEither)) { + if (toEither._tag === "Right") { + return toEither.right + } + + throw new Error(getExhaustiveAbsurdErrorMessage) + } + + return (u: I): A => { + // @ts-expect-error + const result = toEither(u) + + if (result._tag === "Right") { + return result.right as any + } + + throw new Error(getExhaustiveAbsurdErrorMessage) + } +}) as any diff --git a/backend/node_modules/effect/src/internal/metric.ts b/backend/node_modules/effect/src/internal/metric.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4fcb23f309e3b62320f8abf937e516a5e2fa61a --- /dev/null +++ b/backend/node_modules/effect/src/internal/metric.ts @@ -0,0 +1,577 @@ +import * as Arr from "../Array.js" +import * as Clock from "../Clock.js" +import * as Duration from "../Duration.js" +import type * as Effect from "../Effect.js" +import type { LazyArg } from "../Function.js" +import { constVoid, dual, identity, pipe } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import type * as Metric from "../Metric.js" +import type * as MetricBoundaries from "../MetricBoundaries.js" +import type * as MetricHook from "../MetricHook.js" +import type * as MetricKey from "../MetricKey.js" +import type * as MetricKeyType from "../MetricKeyType.js" +import type * as MetricLabel from "../MetricLabel.js" +import type * as MetricPair from "../MetricPair.js" +import type * as MetricRegistry from "../MetricRegistry.js" +import type * as MetricState from "../MetricState.js" +import { pipeArguments } from "../Pipeable.js" +import * as Cause from "./cause.js" +import * as effect_ from "./core-effect.js" +import * as core from "./core.js" +import * as metricBoundaries from "./metric/boundaries.js" +import * as metricKey from "./metric/key.js" +import * as metricKeyType from "./metric/keyType.js" +import * as metricLabel from "./metric/label.js" +import * as metricRegistry from "./metric/registry.js" + +/** @internal */ +const MetricSymbolKey = "effect/Metric" + +/** @internal */ +export const MetricTypeId: Metric.MetricTypeId = Symbol.for( + MetricSymbolKey +) as Metric.MetricTypeId + +const metricVariance = { + /* c8 ignore next */ + _Type: (_: any) => _, + /* c8 ignore next */ + _In: (_: unknown) => _, + /* c8 ignore next */ + _Out: (_: never) => _ +} + +/** @internal */ +export const globalMetricRegistry: MetricRegistry.MetricRegistry = globalValue( + Symbol.for("effect/Metric/globalMetricRegistry"), + () => metricRegistry.make() +) + +/** @internal */ +export const make: Metric.MetricApply = function( + keyType: Type, + unsafeUpdate: (input: In, extraTags: ReadonlyArray) => void, + unsafeValue: (extraTags: ReadonlyArray) => Out, + unsafeModify: (input: In, extraTags: ReadonlyArray) => void +): Metric.Metric { + const metric: Metric.Metric = Object.assign( + (effect: Effect.Effect): Effect.Effect => + core.tap(effect, (a) => update(metric, a)), + { + [MetricTypeId]: metricVariance, + keyType, + unsafeUpdate, + unsafeValue, + unsafeModify, + register() { + this.unsafeValue([]) + return this as any + }, + pipe() { + return pipeArguments(this, arguments) + } + } as const + ) + return metric +} + +/** @internal */ +export const mapInput = dual< + (f: (input: In2) => In) => (self: Metric.Metric) => Metric.Metric, + (self: Metric.Metric, f: (input: In2) => In) => Metric.Metric +>(2, (self, f) => + make( + self.keyType, + (input, extraTags) => self.unsafeUpdate(f(input), extraTags), + self.unsafeValue, + (input, extraTags) => self.unsafeModify(f(input), extraTags) + )) + +/** @internal */ +export const counter: { + (name: string, options?: { + readonly description?: string | undefined + readonly bigint?: false | undefined + readonly incremental?: boolean | undefined + }): Metric.Metric.Counter + (name: string, options: { + readonly description?: string | undefined + readonly bigint: true + readonly incremental?: boolean | undefined + }): Metric.Metric.Counter +} = (name, options) => fromMetricKey(metricKey.counter(name, options as any)) as any + +/** @internal */ +export const frequency = (name: string, options?: { + readonly description?: string | undefined + readonly preregisteredWords?: ReadonlyArray | undefined +}): Metric.Metric.Frequency => fromMetricKey(metricKey.frequency(name, options)) + +/** @internal */ +export const withConstantInput = dual< + (input: In) => (self: Metric.Metric) => Metric.Metric, + (self: Metric.Metric, input: In) => Metric.Metric +>(2, (self, input) => mapInput(self, () => input)) + +/** @internal */ +export const fromMetricKey = >( + key: MetricKey.MetricKey +): Metric.Metric< + Type, + MetricKeyType.MetricKeyType.InType, + MetricKeyType.MetricKeyType.OutType +> => { + let untaggedHook: + | MetricHook.MetricHook< + MetricKeyType.MetricKeyType.InType, + MetricKeyType.MetricKeyType.OutType + > + | undefined + const hookCache = new WeakMap, MetricHook.MetricHook>() + + const hook = (extraTags: ReadonlyArray): MetricHook.MetricHook< + MetricKeyType.MetricKeyType.InType, + MetricKeyType.MetricKeyType.OutType + > => { + if (extraTags.length === 0) { + if (untaggedHook !== undefined) { + return untaggedHook + } + untaggedHook = globalMetricRegistry.get(key) + return untaggedHook + } + + let hook = hookCache.get(extraTags) + if (hook !== undefined) { + return hook + } + hook = globalMetricRegistry.get(metricKey.taggedWithLabels(key, extraTags)) + hookCache.set(extraTags, hook) + return hook + } + + return make( + key.keyType, + (input, extraTags) => hook(extraTags).update(input), + (extraTags) => hook(extraTags).get(), + (input, extraTags) => hook(extraTags).modify(input) + ) +} + +/** @internal */ +export const gauge: { + (name: string, options?: { + readonly description?: string | undefined + readonly bigint?: false | undefined + }): Metric.Metric.Gauge + (name: string, options: { + readonly description?: string | undefined + readonly bigint: true + }): Metric.Metric.Gauge +} = (name, options) => fromMetricKey(metricKey.gauge(name, options as any)) as any + +/** @internal */ +export const histogram = (name: string, boundaries: MetricBoundaries.MetricBoundaries, description?: string) => + fromMetricKey(metricKey.histogram(name, boundaries, description)) + +/* @internal */ +export const increment = ( + self: + | Metric.Metric.Counter + | Metric.Metric.Counter + | Metric.Metric.Gauge + | Metric.Metric.Gauge +): Effect.Effect => + metricKeyType.isCounterKey(self.keyType) + ? update(self as Metric.Metric.Counter, self.keyType.bigint ? BigInt(1) as any : 1) + : modify(self as Metric.Metric.Gauge, self.keyType.bigint ? BigInt(1) as any : 1) + +/* @internal */ +export const incrementBy = dual< + { + (amount: number): (self: Metric.Metric.Counter | Metric.Metric.Counter) => Effect.Effect + (amount: bigint): (self: Metric.Metric.Counter | Metric.Metric.Gauge) => Effect.Effect + }, + { + (self: Metric.Metric.Counter | Metric.Metric.Gauge, amount: number): Effect.Effect + (self: Metric.Metric.Counter | Metric.Metric.Gauge, amount: bigint): Effect.Effect + } +>(2, (self, amount) => + metricKeyType.isCounterKey(self.keyType) + ? update(self as any, amount) + : modify(self as any, amount)) + +/** @internal */ +export const map = dual< + (f: (out: Out) => Out2) => (self: Metric.Metric) => Metric.Metric, + (self: Metric.Metric, f: (out: Out) => Out2) => Metric.Metric +>(2, (self, f) => + make( + self.keyType, + self.unsafeUpdate, + (extraTags) => f(self.unsafeValue(extraTags)), + self.unsafeModify + )) + +/** @internal */ +export const mapType = dual< + ( + f: (type: Type) => Type2 + ) => ( + self: Metric.Metric + ) => Metric.Metric, + ( + self: Metric.Metric, + f: (type: Type) => Type2 + ) => Metric.Metric +>(2, (self, f) => + make( + f(self.keyType), + self.unsafeUpdate, + self.unsafeValue, + self.unsafeModify + )) + +/** @internal */ +export const modify = dual< + (input: In) => (self: Metric.Metric) => Effect.Effect, + (self: Metric.Metric, input: In) => Effect.Effect +>(2, (self, input) => + core.fiberRefGetWith( + core.currentMetricLabels, + (tags) => core.sync(() => self.unsafeModify(input, tags)) + )) + +/* @internal */ +export const set = dual< + { + (value: number): (self: Metric.Metric.Gauge) => Effect.Effect + (value: bigint): (self: Metric.Metric.Gauge) => Effect.Effect + }, + { + (self: Metric.Metric.Gauge, value: number): Effect.Effect + (self: Metric.Metric.Gauge, value: bigint): Effect.Effect + } +>(2, (self, value) => update(self as any, value)) + +/** @internal */ +export const succeed = (out: Out): Metric.Metric => + make(void 0 as void, constVoid, () => out, constVoid) + +/** @internal */ +export const sync = (evaluate: LazyArg): Metric.Metric => + make(void 0 as void, constVoid, evaluate, constVoid) + +/** @internal */ +export const summary = ( + options: { + readonly name: string + readonly maxAge: Duration.DurationInput + readonly maxSize: number + readonly error: number + readonly quantiles: ReadonlyArray + readonly description?: string | undefined + } +): Metric.Metric.Summary => withNow(summaryTimestamp(options)) + +/** @internal */ +export const summaryTimestamp = ( + options: { + readonly name: string + readonly maxAge: Duration.DurationInput + readonly maxSize: number + readonly error: number + readonly quantiles: ReadonlyArray + readonly description?: string | undefined + } +): Metric.Metric.Summary => fromMetricKey(metricKey.summary(options)) + +/** @internal */ +export const tagged = dual< + (key: string, value: string) => (self: Metric.Metric) => Metric.Metric, + (self: Metric.Metric, key: string, value: string) => Metric.Metric +>(3, (self, key, value) => taggedWithLabels(self, [metricLabel.make(key, value)])) + +/** @internal */ +export const taggedWithLabelsInput = dual< + ( + f: (input: In) => Iterable + ) => (self: Metric.Metric) => Metric.Metric, + ( + self: Metric.Metric, + f: (input: In) => Iterable + ) => Metric.Metric +>(2, (self, f) => + map( + make( + self.keyType, + (input, extraTags) => + self.unsafeUpdate( + input, + Arr.union(f(input), extraTags) + ), + self.unsafeValue, + (input, extraTags) => + self.unsafeModify( + input, + Arr.union(f(input), extraTags) + ) + ), + constVoid + )) + +/** @internal */ +export const taggedWithLabels = dual< + ( + extraTags: Iterable + ) => (self: Metric.Metric) => Metric.Metric, + ( + self: Metric.Metric, + extraTags: Iterable + ) => Metric.Metric +>(2, (self, extraTags) => { + return make( + self.keyType, + (input, extraTags1) => self.unsafeUpdate(input, Arr.union(extraTags, extraTags1)), + (extraTags1) => self.unsafeValue(Arr.union(extraTags, extraTags1)), + (input, extraTags1) => self.unsafeModify(input, Arr.union(extraTags, extraTags1)) + ) +}) + +/** @internal */ +export const timer = (name: string, description?: string): Metric.Metric< + MetricKeyType.MetricKeyType.Histogram, + Duration.Duration, + MetricState.MetricState.Histogram +> => { + const boundaries = metricBoundaries.exponential({ + start: 0.5, + factor: 2, + count: 35 + }) + const base = pipe(histogram(name, boundaries, description), tagged("time_unit", "milliseconds")) + return mapInput(base, Duration.toMillis) +} + +/** @internal */ +export const timerWithBoundaries = ( + name: string, + boundaries: ReadonlyArray, + description?: string +): Metric.Metric< + MetricKeyType.MetricKeyType.Histogram, + Duration.Duration, + MetricState.MetricState.Histogram +> => { + const base = pipe( + histogram(name, metricBoundaries.fromIterable(boundaries), description), + tagged("time_unit", "milliseconds") + ) + return mapInput(base, Duration.toMillis) +} + +/* @internal */ +export const trackAll = dual< + ( + input: In + ) => ( + self: Metric.Metric + ) => (effect: Effect.Effect) => Effect.Effect, + ( + self: Metric.Metric, + input: In + ) => (effect: Effect.Effect) => Effect.Effect +>(2, (self, input) => (effect) => + core.matchCauseEffect(effect, { + onFailure: (cause) => core.zipRight(update(self, input), core.failCause(cause)), + onSuccess: (value) => core.zipRight(update(self, input), core.succeed(value)) + })) + +/* @internal */ +export const trackDefect = dual< + ( + metric: Metric.Metric + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + metric: Metric.Metric + ) => Effect.Effect +>(2, (self, metric) => trackDefectWith(self, metric, identity)) + +/* @internal */ +export const trackDefectWith = dual< + ( + metric: Metric.Metric, + f: (defect: unknown) => In + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + metric: Metric.Metric, + f: (defect: unknown) => In + ) => Effect.Effect +>(3, (self, metric, f) => { + const updater = (defect: unknown) => update(metric, f(defect)) + return effect_.tapDefect(self, (cause) => core.forEachSequentialDiscard(Cause.defects(cause), updater)) +}) + +/* @internal */ +export const trackDuration = dual< + ( + metric: Metric.Metric + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + metric: Metric.Metric + ) => Effect.Effect +>(2, (self, metric) => trackDurationWith(self, metric, identity)) + +/* @internal */ +export const trackDurationWith = dual< + ( + metric: Metric.Metric, + f: (duration: Duration.Duration) => In + ) => (effect: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + metric: Metric.Metric, + f: (duration: Duration.Duration) => In + ) => Effect.Effect +>(3, (self, metric, f) => + Clock.clockWith((clock) => { + const startTime = clock.unsafeCurrentTimeNanos() + return core.tap(self, (_) => { + const endTime = clock.unsafeCurrentTimeNanos() + const duration = Duration.nanos(endTime - startTime) + return update(metric, f(duration)) + }) + })) + +/* @internal */ +export const trackError = dual< + ( + metric: Metric.Metric + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + metric: Metric.Metric + ) => Effect.Effect +>(2, ( + self: Effect.Effect, + metric: Metric.Metric +) => trackErrorWith(self, metric, (a: In) => a)) + +/* @internal */ +export const trackErrorWith = dual< + ( + metric: Metric.Metric, + f: (error: In2) => In + ) => (effect: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + metric: Metric.Metric, + f: (error: In2) => In + ) => Effect.Effect +>(3, ( + self: Effect.Effect, + metric: Metric.Metric, + f: (error: In2) => In +) => { + const updater = (error: E): Effect.Effect => update(metric, f(error)) + return effect_.tapError(self, updater) +}) + +/* @internal */ +export const trackSuccess = dual< + ( + metric: Metric.Metric + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + metric: Metric.Metric + ) => Effect.Effect +>(2, ( + self: Effect.Effect, + metric: Metric.Metric +) => trackSuccessWith(self, metric, (a: In) => a)) + +/* @internal */ +export const trackSuccessWith = dual< + ( + metric: Metric.Metric, + f: (value: In2) => In + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + metric: Metric.Metric, + f: (value: In2) => In + ) => Effect.Effect +>(3, ( + self: Effect.Effect, + metric: Metric.Metric, + f: (value: In2) => In +) => { + const updater = (value: A): Effect.Effect => update(metric, f(value)) + return core.tap(self, updater) +}) + +/* @internal */ +export const update = dual< + (input: In) => (self: Metric.Metric) => Effect.Effect, + (self: Metric.Metric, input: In) => Effect.Effect +>(2, (self, input) => + core.fiberRefGetWith( + core.currentMetricLabels, + (tags) => core.sync(() => self.unsafeUpdate(input, tags)) + )) + +/* @internal */ +export const value = ( + self: Metric.Metric +): Effect.Effect => + core.fiberRefGetWith( + core.currentMetricLabels, + (tags) => core.sync(() => self.unsafeValue(tags)) + ) + +/** @internal */ +export const withNow = ( + self: Metric.Metric +): Metric.Metric => mapInput(self, (input: In) => [input, Date.now()] as const) + +/** @internal */ +export const zip = dual< + ( + that: Metric.Metric + ) => ( + self: Metric.Metric + ) => Metric.Metric, + ( + self: Metric.Metric, + that: Metric.Metric + ) => Metric.Metric +>( + 2, + (self: Metric.Metric, that: Metric.Metric) => + make( + [self.keyType, that.keyType] as const, + (input: readonly [In, In2], extraTags) => { + const [l, r] = input + self.unsafeUpdate(l, extraTags) + that.unsafeUpdate(r, extraTags) + }, + (extraTags) => [self.unsafeValue(extraTags), that.unsafeValue(extraTags)], + (input: readonly [In, In2], extraTags) => { + const [l, r] = input + self.unsafeModify(l, extraTags) + that.unsafeModify(r, extraTags) + } + ) +) + +/** @internal */ +export const unsafeSnapshot = (): Array => globalMetricRegistry.snapshot() + +/** @internal */ +export const snapshot: Effect.Effect> = core.sync( + unsafeSnapshot +) diff --git a/backend/node_modules/effect/src/internal/option.ts b/backend/node_modules/effect/src/internal/option.ts new file mode 100644 index 0000000000000000000000000000000000000000..7d97adee2cd102c33440bf69b75e6500823763ec --- /dev/null +++ b/backend/node_modules/effect/src/internal/option.ts @@ -0,0 +1,80 @@ +/** + * @since 2.0.0 + */ + +import * as Equal from "../Equal.js" +import * as Hash from "../Hash.js" +import { format, NodeInspectSymbol, toJSON } from "../Inspectable.js" +import type * as Option from "../Option.js" +import { hasProperty } from "../Predicate.js" +import { EffectPrototype } from "./effectable.js" + +const TypeId: Option.TypeId = Symbol.for("effect/Option") as Option.TypeId + +const CommonProto = { + ...EffectPrototype, + [TypeId]: { + _A: (_: never) => _ + }, + [NodeInspectSymbol](this: Option.Option) { + return this.toJSON() + }, + toString(this: Option.Option) { + return format(this.toJSON()) + } +} + +const SomeProto = Object.assign(Object.create(CommonProto), { + _tag: "Some", + _op: "Some", + [Equal.symbol](this: Option.Some, that: unknown): boolean { + return isOption(that) && isSome(that) && Equal.equals(this.value, that.value) + }, + [Hash.symbol](this: Option.Some) { + return Hash.cached(this, Hash.combine(Hash.hash(this._tag))(Hash.hash(this.value))) + }, + toJSON(this: Option.Some) { + return { + _id: "Option", + _tag: this._tag, + value: toJSON(this.value) + } + } +}) + +const NoneHash = Hash.hash("None") +const NoneProto = Object.assign(Object.create(CommonProto), { + _tag: "None", + _op: "None", + [Equal.symbol](this: Option.None, that: unknown): boolean { + return isOption(that) && isNone(that) + }, + [Hash.symbol](this: Option.None) { + return NoneHash + }, + toJSON(this: Option.None) { + return { + _id: "Option", + _tag: this._tag + } + } +}) + +/** @internal */ +export const isOption = (input: unknown): input is Option.Option => hasProperty(input, TypeId) + +/** @internal */ +export const isNone = (fa: Option.Option): fa is Option.None => fa._tag === "None" + +/** @internal */ +export const isSome = (fa: Option.Option): fa is Option.Some => fa._tag === "Some" + +/** @internal */ +export const none: Option.Option = Object.create(NoneProto) + +/** @internal */ +export const some = (value: A): Option.Option => { + const a = Object.create(SomeProto) + a.value = value + return a +} diff --git a/backend/node_modules/effect/src/internal/pool.ts b/backend/node_modules/effect/src/internal/pool.ts new file mode 100644 index 0000000000000000000000000000000000000000..98a70756f97ac093efd2661010b74e63a04227e5 --- /dev/null +++ b/backend/node_modules/effect/src/internal/pool.ts @@ -0,0 +1,432 @@ +import type { Cause } from "../Cause.js" +import * as Context from "../Context.js" +import * as Duration from "../Duration.js" +import type { Effect, Semaphore } from "../Effect.js" +import * as Effectable from "../Effectable.js" +import type { Exit } from "../Exit.js" +import { dual, identity } from "../Function.js" +import * as Iterable from "../Iterable.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import type { Pool, PoolTypeId as PoolTypeId_ } from "../Pool.js" +import { hasProperty } from "../Predicate.js" +import type { Scope } from "../Scope.js" +import * as coreEffect from "./core-effect.js" +import * as core from "./core.js" +import * as defaultServices from "./defaultServices.js" +import * as circular from "./effect/circular.js" +import * as fiberRuntime from "./fiberRuntime.js" +import * as internalQueue from "./queue.js" + +/** @internal */ +export const PoolTypeId: PoolTypeId_ = Symbol.for("effect/Pool") as PoolTypeId_ + +const poolVariance = { + /* c8 ignore next */ + _E: (_: never) => _, + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +export const isPool = (u: unknown): u is Pool => hasProperty(u, PoolTypeId) + +/** @internal */ +export const makeWith = (options: { + readonly acquire: Effect + readonly min: number + readonly max: number + readonly concurrency?: number | undefined + readonly targetUtilization?: number | undefined + readonly strategy: Strategy +}): Effect, never, Scope | R> => + core.uninterruptibleMask((restore) => + core.flatMap(core.context(), (context) => { + const scope = Context.get(context, fiberRuntime.scopeTag) + const acquire = core.mapInputContext( + options.acquire, + (input) => Context.merge(context, input) + ) as Effect< + A, + E, + Scope + > + const pool = new PoolImpl( + scope, + acquire, + options.concurrency ?? 1, + options.min, + options.max, + options.strategy, + Math.min(Math.max(options.targetUtilization ?? 1, 0.1), 1) + ) + const initialize = core.tap(fiberRuntime.forkDaemon(restore(pool.resize)), (fiber) => + scope.addFinalizer(() => core.interruptFiber(fiber))) + const runStrategy = core.tap(fiberRuntime.forkDaemon(restore(options.strategy.run(pool))), (fiber) => + scope.addFinalizer(() => + core.interruptFiber(fiber) + )) + return core.succeed(pool).pipe( + core.zipLeft(scope.addFinalizer(() => + pool.shutdown + )), + core.zipLeft(initialize), + core.zipLeft(runStrategy) + ) + }) + ) + +/** @internal */ +export const make = (options: { + readonly acquire: Effect + readonly size: number + readonly concurrency?: number | undefined + readonly targetUtilization?: number | undefined +}): Effect, never, R | Scope> => + makeWith({ ...options, min: options.size, max: options.size, strategy: strategyNoop() }) + +/** @internal */ +export const makeWithTTL = (options: { + readonly acquire: Effect + readonly min: number + readonly max: number + readonly concurrency?: number | undefined + readonly targetUtilization?: number | undefined + readonly timeToLive: Duration.DurationInput + readonly timeToLiveStrategy?: "creation" | "usage" | undefined +}): Effect, never, R | Scope> => + core.flatMap( + options.timeToLiveStrategy === "creation" ? + strategyCreationTTL(options.timeToLive) : + strategyUsageTTL(options.timeToLive), + (strategy) => makeWith({ ...options, strategy }) + ) + +/** @internal */ +export const get = (self: Pool): Effect => self.get + +/** @internal */ +export const invalidate: { + (item: A): (self: Pool) => Effect + (self: Pool, item: A): Effect +} = dual(2, (self: Pool, item: A): Effect => self.invalidate(item)) + +interface PoolItem { + readonly exit: Exit + finalizer: Effect + refCount: number + disableReclaim: boolean +} + +interface Strategy { + readonly run: (pool: PoolImpl) => Effect + readonly onAcquire: (item: PoolItem) => Effect + readonly reclaim: (pool: PoolImpl) => Effect>> +} + +class PoolImpl extends Effectable.Class implements Pool { + readonly [PoolTypeId]: Pool.Variance[PoolTypeId_] + + isShuttingDown = false + readonly semaphore: Semaphore + readonly items = new Set>() + readonly available = new Set>() + readonly availableLatch = circular.unsafeMakeLatch(false) + readonly invalidated = new Set>() + waiters = 0 + + constructor( + readonly scope: Scope, + readonly acquire: Effect, + readonly concurrency: number, + readonly minSize: number, + readonly maxSize: number, + readonly strategy: Strategy, + readonly targetUtilization: number + ) { + super() + this[PoolTypeId] = poolVariance + this.semaphore = circular.unsafeMakeSemaphore(concurrency * maxSize) + } + + readonly allocate: Effect> = core.acquireUseRelease( + fiberRuntime.scopeMake(), + (scope) => + this.acquire.pipe( + fiberRuntime.scopeExtend(scope), + core.exit, + core.flatMap((exit) => { + const item: PoolItem = { + exit, + finalizer: core.catchAllCause(scope.close(exit), reportUnhandledError), + refCount: 0, + disableReclaim: false + } + this.items.add(item) + this.available.add(item) + return core.as( + exit._tag === "Success" + ? this.strategy.onAcquire(item) + : core.zipRight(item.finalizer, this.strategy.onAcquire(item)), + item + ) + }) + ), + (scope, exit) => exit._tag === "Failure" ? scope.close(exit) : core.void + ) + + get currentUsage() { + let count = this.waiters + for (const item of this.items) { + count += item.refCount + } + return count + } + + get targetSize() { + if (this.isShuttingDown) return 0 + const utilization = this.currentUsage / this.targetUtilization + const target = Math.ceil(utilization / this.concurrency) + return Math.min(Math.max(this.minSize, target), this.maxSize) + } + + get activeSize() { + return this.items.size - this.invalidated.size + } + + readonly resizeLoop: Effect = core.suspend(() => { + if (this.activeSize >= this.targetSize) { + return core.void + } + const toAcquire = this.targetSize - this.activeSize + return this.strategy.reclaim(this).pipe( + core.flatMap(Option.match({ + onNone: () => this.allocate, + onSome: core.succeed + })), + fiberRuntime.replicateEffect(toAcquire, { concurrency: toAcquire }), + core.zipLeft(this.availableLatch.open), + core.flatMap((items) => items.some((_) => _.exit._tag === "Failure") ? core.void : this.resizeLoop) + ) + }) + readonly resizeSemaphore = circular.unsafeMakeSemaphore(1) + readonly resize = this.resizeSemaphore.withPermits(1)(this.resizeLoop) + + readonly getPoolItem: Effect, never, Scope> = core.uninterruptibleMask((restore) => + restore(this.semaphore.take(1)).pipe( + core.zipRight(fiberRuntime.scopeTag), + core.flatMap((scope) => + core.suspend(() => { + this.waiters++ + if (this.isShuttingDown) { + return core.interrupt + } else if (this.targetSize > this.activeSize) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this + return core.flatMap( + this.resizeSemaphore.withPermitsIfAvailable(1)( + circular.forkIn(core.interruptible(this.resize), this.scope) + ), + function loop(): Effect> { + if (self.isShuttingDown) { + return core.interrupt + } else if (self.available.size > 0) { + return core.succeed(Iterable.unsafeHead(self.available)) + } + self.availableLatch.unsafeClose() + return core.flatMap(self.availableLatch.await, loop) + } + ) + } + return core.succeed(Iterable.unsafeHead(this.available)) + }).pipe( + fiberRuntime.ensuring(core.sync(() => this.waiters--)), + core.tap((item) => { + if (item.exit._tag === "Failure") { + this.items.delete(item) + this.invalidated.delete(item) + this.available.delete(item) + return this.semaphore.release(1) + } + item.refCount++ + this.available.delete(item) + if (item.refCount < this.concurrency) { + this.available.add(item) + } + return scope.addFinalizer(() => + core.zipRight( + core.suspend(() => { + item.refCount-- + if (this.invalidated.has(item)) { + return this.invalidatePoolItem(item) + } + this.available.add(item) + return core.exitVoid + }), + this.semaphore.release(1) + ) + ) + }), + core.onInterrupt(() => this.semaphore.release(1)) + ) + ) + ) + ) + + commit() { + return this.get + } + + readonly get: Effect = core.flatMap( + core.suspend(() => this.isShuttingDown ? core.interrupt : this.getPoolItem), + (_) => _.exit + ) + + invalidate(item: A): Effect { + return core.suspend(() => { + if (this.isShuttingDown) return core.void + for (const poolItem of this.items) { + if (poolItem.exit._tag === "Success" && poolItem.exit.value === item) { + poolItem.disableReclaim = true + return core.uninterruptible(this.invalidatePoolItem(poolItem)) + } + } + return core.void + }) + } + + invalidatePoolItem(poolItem: PoolItem): Effect { + return core.suspend(() => { + if (!this.items.has(poolItem)) { + return core.void + } else if (poolItem.refCount === 0) { + this.items.delete(poolItem) + this.available.delete(poolItem) + this.invalidated.delete(poolItem) + return core.zipRight( + poolItem.finalizer, + circular.forkIn(core.interruptible(this.resize), this.scope) + ) + } + this.invalidated.add(poolItem) + this.available.delete(poolItem) + return core.void + }) + } + + get shutdown(): Effect { + return core.suspend(() => { + if (this.isShuttingDown) return core.void + this.isShuttingDown = true + const size = this.items.size + const semaphore = circular.unsafeMakeSemaphore(size) + return core.forEachSequentialDiscard(this.items, (item) => { + if (item.refCount > 0) { + item.finalizer = core.zipLeft(item.finalizer, semaphore.release(1)) + this.invalidated.add(item) + return semaphore.take(1) + } + this.items.delete(item) + this.available.delete(item) + this.invalidated.delete(item) + return item.finalizer + }).pipe( + core.zipRight(this.semaphore.releaseAll), + core.zipRight(this.availableLatch.open), + core.zipRight(semaphore.take(size)) + ) + }) + } + + pipe() { + return pipeArguments(this, arguments) + } +} + +const strategyNoop = (): Strategy => ({ + run: (_) => core.void, + onAcquire: (_) => core.void, + reclaim: (_) => coreEffect.succeedNone +}) + +const strategyCreationTTL = (ttl: Duration.DurationInput) => + defaultServices.clockWith((clock) => + core.map(internalQueue.unbounded>(), (queue) => { + const ttlMillis = Duration.toMillis(ttl) + const creationTimes = new WeakMap, number>() + return identity>({ + run: (pool) => { + const process = (item: PoolItem): Effect => + core.suspend(() => { + if (!pool.items.has(item) || pool.invalidated.has(item)) { + return core.void + } + const now = clock.unsafeCurrentTimeMillis() + const created = creationTimes.get(item)! + const remaining = ttlMillis - (now - created) + return remaining > 0 + ? coreEffect.delay(process(item), remaining) + : pool.invalidatePoolItem(item) + }) + return queue.take.pipe( + core.tap(process), + coreEffect.forever + ) + }, + onAcquire: (item) => + core.suspend(() => { + creationTimes.set(item, clock.unsafeCurrentTimeMillis()) + return queue.offer(item) + }), + reclaim: (_) => coreEffect.succeedNone + }) + }) + ) + +const strategyUsageTTL = (ttl: Duration.DurationInput) => + core.map(internalQueue.unbounded>(), (queue) => { + return identity>({ + run: (pool) => { + const process: Effect = core.suspend(() => { + const excess = pool.activeSize - pool.targetSize + if (excess <= 0) return core.void + return queue.take.pipe( + core.tap((item) => pool.invalidatePoolItem(item)), + core.zipRight(process) + ) + }) + return process.pipe( + coreEffect.delay(ttl), + coreEffect.forever + ) + }, + onAcquire: (item) => queue.offer(item), + reclaim(pool) { + return core.suspend((): Effect>> => { + if (pool.invalidated.size === 0) { + return coreEffect.succeedNone + } + const item = Iterable.head( + Iterable.filter(pool.invalidated, (item) => !item.disableReclaim) + ) + if (item._tag === "None") { + return coreEffect.succeedNone + } + pool.invalidated.delete(item.value) + if (item.value.refCount < pool.concurrency) { + pool.available.add(item.value) + } + return core.as(queue.offer(item.value), item) + }) + } + }) + }) + +const reportUnhandledError = (cause: Cause) => + core.withFiberRuntime((fiber) => { + const unhandledLogLevel = fiber.getFiberRef(core.currentUnhandledErrorLogLevel) + if (unhandledLogLevel._tag === "Some") { + fiber.log("Unhandled error in pool finalizer", cause, unhandledLogLevel) + } + return core.void + }) diff --git a/backend/node_modules/effect/src/internal/pubsub.ts b/backend/node_modules/effect/src/internal/pubsub.ts new file mode 100644 index 0000000000000000000000000000000000000000..e736c18bb767469338b4b18a0a239f7db29d1633 --- /dev/null +++ b/backend/node_modules/effect/src/internal/pubsub.ts @@ -0,0 +1,1762 @@ +import * as Chunk from "../Chunk.js" +import type * as Deferred from "../Deferred.js" +import type * as Effect from "../Effect.js" +import * as Effectable from "../Effectable.js" +import { dual, pipe } from "../Function.js" +import * as MutableQueue from "../MutableQueue.js" +import * as MutableRef from "../MutableRef.js" +import { nextPow2 } from "../Number.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import type * as PubSub from "../PubSub.js" +import type * as Queue from "../Queue.js" +import type * as Scope from "../Scope.js" +import * as core from "./core.js" +import * as executionStrategy from "./executionStrategy.js" +import * as fiberRuntime from "./fiberRuntime.js" +import * as queue from "./queue.js" + +const AbsentValue = Symbol.for("effect/PubSub/AbsentValue") +type AbsentValue = typeof AbsentValue + +/** @internal */ +export interface AtomicPubSub { + readonly capacity: number + isEmpty(): boolean + isFull(): boolean + size(): number + publish(value: A): boolean + publishAll(elements: Iterable): Chunk.Chunk + slide(): void + subscribe(): Subscription + replayWindow(): ReplayWindow +} + +/** @internal */ +interface Subscription { + isEmpty(): boolean + size(): number + poll(default_: D): A | D + pollUpTo(n: number): Chunk.Chunk + unsubscribe(): void +} + +/** @internal */ +type Subscribers = Map< + Subscription, + Set>> +> + +const addSubscribers = ( + subscription: Subscription, + pollers: MutableQueue.MutableQueue> +) => +(subscribers: Subscribers) => { + if (!subscribers.has(subscription)) { + subscribers.set(subscription, new Set()) + } + const set = subscribers.get(subscription)! + set.add(pollers) +} + +const removeSubscribers = ( + subscription: Subscription, + pollers: MutableQueue.MutableQueue> +) => +(subscribers: Subscribers) => { + if (!subscribers.has(subscription)) { + return + } + const set = subscribers.get(subscription)! + set.delete(pollers) + if (set.size === 0) { + subscribers.delete(subscription) + } +} + +/** @internal */ +export const bounded = ( + capacity: number | { + readonly capacity: number + readonly replay?: number | undefined + } +): Effect.Effect> => + core.suspend(() => { + const pubsub = makeBoundedPubSub(capacity) + return makePubSub(pubsub, new BackPressureStrategy()) + }) + +/** @internal */ +export const dropping = ( + capacity: number | { + readonly capacity: number + readonly replay?: number | undefined + } +): Effect.Effect> => + core.suspend(() => { + const pubsub = makeBoundedPubSub(capacity) + return makePubSub(pubsub, new DroppingStrategy()) + }) + +/** @internal */ +export const sliding = ( + capacity: number | { + readonly capacity: number + readonly replay?: number | undefined + } +): Effect.Effect> => + core.suspend(() => { + const pubsub = makeBoundedPubSub(capacity) + return makePubSub(pubsub, new SlidingStrategy()) + }) + +/** @internal */ +export const unbounded = (options?: { + readonly replay?: number | undefined +}): Effect.Effect> => + core.suspend(() => { + const pubsub = makeUnboundedPubSub(options) + return makePubSub(pubsub, new DroppingStrategy()) + }) + +/** @internal */ +export const capacity = (self: PubSub.PubSub): number => self.capacity() + +/** @internal */ +export const size = (self: PubSub.PubSub): Effect.Effect => self.size + +/** @internal */ +export const isFull = (self: PubSub.PubSub): Effect.Effect => self.isFull + +/** @internal */ +export const isEmpty = (self: PubSub.PubSub): Effect.Effect => self.isEmpty + +/** @internal */ +export const shutdown = (self: PubSub.PubSub): Effect.Effect => self.shutdown + +/** @internal */ +export const isShutdown = (self: PubSub.PubSub): Effect.Effect => self.isShutdown + +/** @internal */ +export const awaitShutdown = (self: PubSub.PubSub): Effect.Effect => self.awaitShutdown + +/** @internal */ +export const publish = dual< + (value: A) => (self: PubSub.PubSub) => Effect.Effect, + (self: PubSub.PubSub, value: A) => Effect.Effect +>(2, (self, value) => self.publish(value)) + +/** @internal */ +export const publishAll = dual< + (elements: Iterable) => (self: PubSub.PubSub) => Effect.Effect, + (self: PubSub.PubSub, elements: Iterable) => Effect.Effect +>(2, (self, elements) => self.publishAll(elements)) + +/** @internal */ +export const subscribe = (self: PubSub.PubSub): Effect.Effect, never, Scope.Scope> => + self.subscribe + +/** @internal */ +const makeBoundedPubSub = ( + capacity: number | { + readonly capacity: number + readonly replay?: number | undefined + } +): AtomicPubSub => { + const options = typeof capacity === "number" ? { capacity } : capacity + ensureCapacity(options.capacity) + const replayBuffer = options.replay && options.replay > 0 ? new ReplayBuffer(Math.ceil(options.replay)) : undefined + if (options.capacity === 1) { + return new BoundedPubSubSingle(replayBuffer) + } else if (nextPow2(options.capacity) === options.capacity) { + return new BoundedPubSubPow2(options.capacity, replayBuffer) + } else { + return new BoundedPubSubArb(options.capacity, replayBuffer) + } +} + +/** @internal */ +const makeUnboundedPubSub = (options?: { + readonly replay?: number | undefined +}): AtomicPubSub => new UnboundedPubSub(options?.replay ? new ReplayBuffer(options.replay) : undefined) + +/** @internal */ +const makeSubscription = ( + pubsub: AtomicPubSub, + subscribers: Subscribers, + strategy: PubSubStrategy +): Effect.Effect> => + core.map(core.deferredMake(), (deferred) => + unsafeMakeSubscription( + pubsub, + subscribers, + pubsub.subscribe(), + MutableQueue.unbounded>(), + deferred, + MutableRef.make(false), + strategy + )) + +/** @internal */ +export const unsafeMakeSubscription = ( + pubsub: AtomicPubSub, + subscribers: Subscribers, + subscription: Subscription, + pollers: MutableQueue.MutableQueue>, + shutdownHook: Deferred.Deferred, + shutdownFlag: MutableRef.MutableRef, + strategy: PubSubStrategy +): Queue.Dequeue => + new SubscriptionImpl( + pubsub, + subscribers, + subscription, + pollers, + shutdownHook, + shutdownFlag, + strategy, + pubsub.replayWindow() + ) + +/** @internal */ +class BoundedPubSubArb implements AtomicPubSub { + array: Array + publisherIndex = 0 + subscribers: Array + subscriberCount = 0 + subscribersIndex = 0 + + constructor(readonly capacity: number, readonly replayBuffer: ReplayBuffer | undefined) { + this.array = Array.from({ length: capacity }) + this.subscribers = Array.from({ length: capacity }) + } + + replayWindow(): ReplayWindow { + return this.replayBuffer ? new ReplayWindowImpl(this.replayBuffer) : emptyReplayWindow + } + + isEmpty(): boolean { + return this.publisherIndex === this.subscribersIndex + } + + isFull(): boolean { + return this.publisherIndex === this.subscribersIndex + this.capacity + } + + size(): number { + return this.publisherIndex - this.subscribersIndex + } + + publish(value: A): boolean { + if (this.isFull()) { + return false + } + if (this.subscriberCount !== 0) { + const index = this.publisherIndex % this.capacity + this.array[index] = value + this.subscribers[index] = this.subscriberCount + this.publisherIndex += 1 + } + if (this.replayBuffer) { + this.replayBuffer.offer(value) + } + return true + } + + publishAll(elements: Iterable): Chunk.Chunk { + if (this.subscriberCount === 0) { + if (this.replayBuffer) { + this.replayBuffer.offerAll(elements) + } + return Chunk.empty() + } + const chunk = Chunk.fromIterable(elements) + const n = chunk.length + const size = this.publisherIndex - this.subscribersIndex + const available = this.capacity - size + const forPubSub = Math.min(n, available) + if (forPubSub === 0) { + return chunk + } + let iteratorIndex = 0 + const publishAllIndex = this.publisherIndex + forPubSub + while (this.publisherIndex !== publishAllIndex) { + const a = Chunk.unsafeGet(chunk, iteratorIndex++) + const index = this.publisherIndex % this.capacity + this.array[index] = a + this.subscribers[index] = this.subscriberCount + this.publisherIndex += 1 + if (this.replayBuffer) { + this.replayBuffer.offer(a) + } + } + return Chunk.drop(chunk, iteratorIndex) + } + + slide(): void { + if (this.subscribersIndex !== this.publisherIndex) { + const index = this.subscribersIndex % this.capacity + this.array[index] = AbsentValue as unknown as A + this.subscribers[index] = 0 + this.subscribersIndex += 1 + } + if (this.replayBuffer) { + this.replayBuffer.slide() + } + } + + subscribe(): Subscription { + this.subscriberCount += 1 + return new BoundedPubSubArbSubscription(this, this.publisherIndex, false) + } +} + +class BoundedPubSubArbSubscription implements Subscription { + constructor( + private self: BoundedPubSubArb, + private subscriberIndex: number, + private unsubscribed: boolean + ) { + } + + isEmpty(): boolean { + return ( + this.unsubscribed || + this.self.publisherIndex === this.subscriberIndex || + this.self.publisherIndex === this.self.subscribersIndex + ) + } + + size() { + if (this.unsubscribed) { + return 0 + } + return this.self.publisherIndex - Math.max(this.subscriberIndex, this.self.subscribersIndex) + } + + poll(default_: D): A | D { + if (this.unsubscribed) { + return default_ + } + this.subscriberIndex = Math.max(this.subscriberIndex, this.self.subscribersIndex) + if (this.subscriberIndex !== this.self.publisherIndex) { + const index = this.subscriberIndex % this.self.capacity + const elem = this.self.array[index]! + this.self.subscribers[index] -= 1 + if (this.self.subscribers[index] === 0) { + this.self.array[index] = AbsentValue as unknown as A + this.self.subscribersIndex += 1 + } + this.subscriberIndex += 1 + return elem + } + return default_ + } + + pollUpTo(n: number): Chunk.Chunk { + if (this.unsubscribed) { + return Chunk.empty() + } + this.subscriberIndex = Math.max(this.subscriberIndex, this.self.subscribersIndex) + const size = this.self.publisherIndex - this.subscriberIndex + const toPoll = Math.min(n, size) + if (toPoll <= 0) { + return Chunk.empty() + } + const builder: Array = [] + const pollUpToIndex = this.subscriberIndex + toPoll + while (this.subscriberIndex !== pollUpToIndex) { + const index = this.subscriberIndex % this.self.capacity + const a = this.self.array[index] as A + this.self.subscribers[index] -= 1 + if (this.self.subscribers[index] === 0) { + this.self.array[index] = AbsentValue as unknown as A + this.self.subscribersIndex += 1 + } + builder.push(a) + this.subscriberIndex += 1 + } + + return Chunk.fromIterable(builder) + } + + unsubscribe(): void { + if (!this.unsubscribed) { + this.unsubscribed = true + this.self.subscriberCount -= 1 + this.subscriberIndex = Math.max(this.subscriberIndex, this.self.subscribersIndex) + while (this.subscriberIndex !== this.self.publisherIndex) { + const index = this.subscriberIndex % this.self.capacity + this.self.subscribers[index] -= 1 + if (this.self.subscribers[index] === 0) { + this.self.array[index] = AbsentValue as unknown as A + this.self.subscribersIndex += 1 + } + this.subscriberIndex += 1 + } + } + } +} + +/** @internal */ +class BoundedPubSubPow2 implements AtomicPubSub { + array: Array + mask: number + publisherIndex = 0 + subscribers: Array + subscriberCount = 0 + subscribersIndex = 0 + + constructor(readonly capacity: number, readonly replayBuffer: ReplayBuffer | undefined) { + this.array = Array.from({ length: capacity }) + this.mask = capacity - 1 + this.subscribers = Array.from({ length: capacity }) + } + + replayWindow(): ReplayWindow { + return this.replayBuffer ? new ReplayWindowImpl(this.replayBuffer) : emptyReplayWindow + } + + isEmpty(): boolean { + return this.publisherIndex === this.subscribersIndex + } + + isFull(): boolean { + return this.publisherIndex === this.subscribersIndex + this.capacity + } + + size(): number { + return this.publisherIndex - this.subscribersIndex + } + + publish(value: A): boolean { + if (this.isFull()) { + return false + } + if (this.subscriberCount !== 0) { + const index = this.publisherIndex & this.mask + this.array[index] = value + this.subscribers[index] = this.subscriberCount + this.publisherIndex += 1 + } + if (this.replayBuffer) { + this.replayBuffer.offer(value) + } + return true + } + + publishAll(elements: Iterable): Chunk.Chunk { + if (this.subscriberCount === 0) { + if (this.replayBuffer) { + this.replayBuffer.offerAll(elements) + } + return Chunk.empty() + } + const chunk = Chunk.fromIterable(elements) + const n = chunk.length + const size = this.publisherIndex - this.subscribersIndex + const available = this.capacity - size + const forPubSub = Math.min(n, available) + if (forPubSub === 0) { + return chunk + } + let iteratorIndex = 0 + const publishAllIndex = this.publisherIndex + forPubSub + while (this.publisherIndex !== publishAllIndex) { + const elem = Chunk.unsafeGet(chunk, iteratorIndex++) + const index = this.publisherIndex & this.mask + this.array[index] = elem + this.subscribers[index] = this.subscriberCount + this.publisherIndex += 1 + if (this.replayBuffer) { + this.replayBuffer.offer(elem) + } + } + return Chunk.drop(chunk, iteratorIndex) + } + + slide(): void { + if (this.subscribersIndex !== this.publisherIndex) { + const index = this.subscribersIndex & this.mask + this.array[index] = AbsentValue as unknown as A + this.subscribers[index] = 0 + this.subscribersIndex += 1 + } + if (this.replayBuffer) { + this.replayBuffer.slide() + } + } + + subscribe(): Subscription { + this.subscriberCount += 1 + return new BoundedPubSubPow2Subscription(this, this.publisherIndex, false) + } +} + +/** @internal */ +class BoundedPubSubPow2Subscription implements Subscription { + constructor( + private self: BoundedPubSubPow2, + private subscriberIndex: number, + private unsubscribed: boolean + ) { + } + + isEmpty(): boolean { + return ( + this.unsubscribed || + this.self.publisherIndex === this.subscriberIndex || + this.self.publisherIndex === this.self.subscribersIndex + ) + } + + size() { + if (this.unsubscribed) { + return 0 + } + return this.self.publisherIndex - Math.max(this.subscriberIndex, this.self.subscribersIndex) + } + + poll(default_: D): A | D { + if (this.unsubscribed) { + return default_ + } + this.subscriberIndex = Math.max(this.subscriberIndex, this.self.subscribersIndex) + if (this.subscriberIndex !== this.self.publisherIndex) { + const index = this.subscriberIndex & this.self.mask + const elem = this.self.array[index]! + this.self.subscribers[index] -= 1 + if (this.self.subscribers[index] === 0) { + this.self.array[index] = AbsentValue as unknown as A + this.self.subscribersIndex += 1 + } + this.subscriberIndex += 1 + return elem + } + return default_ + } + + pollUpTo(n: number): Chunk.Chunk { + if (this.unsubscribed) { + return Chunk.empty() + } + this.subscriberIndex = Math.max(this.subscriberIndex, this.self.subscribersIndex) + const size = this.self.publisherIndex - this.subscriberIndex + const toPoll = Math.min(n, size) + if (toPoll <= 0) { + return Chunk.empty() + } + const builder: Array = [] + const pollUpToIndex = this.subscriberIndex + toPoll + while (this.subscriberIndex !== pollUpToIndex) { + const index = this.subscriberIndex & this.self.mask + const elem = this.self.array[index] as A + this.self.subscribers[index] -= 1 + if (this.self.subscribers[index] === 0) { + this.self.array[index] = AbsentValue as unknown as A + this.self.subscribersIndex += 1 + } + builder.push(elem) + this.subscriberIndex += 1 + } + return Chunk.fromIterable(builder) + } + + unsubscribe(): void { + if (!this.unsubscribed) { + this.unsubscribed = true + this.self.subscriberCount -= 1 + this.subscriberIndex = Math.max(this.subscriberIndex, this.self.subscribersIndex) + while (this.subscriberIndex !== this.self.publisherIndex) { + const index = this.subscriberIndex & this.self.mask + this.self.subscribers[index] -= 1 + if (this.self.subscribers[index] === 0) { + this.self.array[index] = AbsentValue as unknown as A + this.self.subscribersIndex += 1 + } + this.subscriberIndex += 1 + } + } + } +} + +/** @internal */ +class BoundedPubSubSingle implements AtomicPubSub { + publisherIndex = 0 + subscriberCount = 0 + subscribers = 0 + value: A = AbsentValue as unknown as A + + readonly capacity = 1 + constructor(readonly replayBuffer: ReplayBuffer | undefined) {} + + replayWindow(): ReplayWindow { + return this.replayBuffer ? new ReplayWindowImpl(this.replayBuffer) : emptyReplayWindow + } + + pipe() { + return pipeArguments(this, arguments) + } + + isEmpty(): boolean { + return this.subscribers === 0 + } + + isFull(): boolean { + return !this.isEmpty() + } + + size(): number { + return this.isEmpty() ? 0 : 1 + } + + publish(value: A): boolean { + if (this.isFull()) { + return false + } + if (this.subscriberCount !== 0) { + this.value = value + this.subscribers = this.subscriberCount + this.publisherIndex += 1 + } + if (this.replayBuffer) { + this.replayBuffer.offer(value) + } + return true + } + + publishAll(elements: Iterable): Chunk.Chunk { + if (this.subscriberCount === 0) { + if (this.replayBuffer) { + this.replayBuffer.offerAll(elements) + } + return Chunk.empty() + } + const chunk = Chunk.fromIterable(elements) + if (Chunk.isEmpty(chunk)) { + return chunk + } + if (this.publish(Chunk.unsafeHead(chunk))) { + return Chunk.drop(chunk, 1) + } else { + return chunk + } + } + + slide(): void { + if (this.isFull()) { + this.subscribers = 0 + this.value = AbsentValue as unknown as A + } + if (this.replayBuffer) { + this.replayBuffer.slide() + } + } + + subscribe(): Subscription { + this.subscriberCount += 1 + return new BoundedPubSubSingleSubscription(this, this.publisherIndex, false) + } +} + +/** @internal */ +class BoundedPubSubSingleSubscription implements Subscription { + constructor( + private self: BoundedPubSubSingle, + private subscriberIndex: number, + private unsubscribed: boolean + ) { + } + + isEmpty(): boolean { + return ( + this.unsubscribed || + this.self.subscribers === 0 || + this.subscriberIndex === this.self.publisherIndex + ) + } + + size() { + return this.isEmpty() ? 0 : 1 + } + + poll(default_: D): A | D { + if (this.isEmpty()) { + return default_ + } + const elem = this.self.value + this.self.subscribers -= 1 + if (this.self.subscribers === 0) { + this.self.value = AbsentValue as unknown as A + } + this.subscriberIndex += 1 + return elem + } + + pollUpTo(n: number): Chunk.Chunk { + if (this.isEmpty() || n < 1) { + return Chunk.empty() + } + const a = this.self.value + this.self.subscribers -= 1 + if (this.self.subscribers === 0) { + this.self.value = AbsentValue as unknown as A + } + this.subscriberIndex += 1 + return Chunk.of(a) + } + + unsubscribe(): void { + if (!this.unsubscribed) { + this.unsubscribed = true + this.self.subscriberCount -= 1 + if (this.subscriberIndex !== this.self.publisherIndex) { + this.self.subscribers -= 1 + if (this.self.subscribers === 0) { + this.self.value = AbsentValue as unknown as A + } + } + } + } +} + +/** @internal */ +interface Node { + value: A | AbsentValue + subscribers: number + next: Node | null +} + +/** @internal */ +class UnboundedPubSub implements AtomicPubSub { + publisherHead: Node = { + value: AbsentValue, + subscribers: 0, + next: null + } + publisherTail = this.publisherHead + publisherIndex = 0 + subscribersIndex = 0 + + readonly capacity = Number.MAX_SAFE_INTEGER + constructor(readonly replayBuffer: ReplayBuffer | undefined) {} + + replayWindow(): ReplayWindow { + return this.replayBuffer ? new ReplayWindowImpl(this.replayBuffer) : emptyReplayWindow + } + + isEmpty(): boolean { + return this.publisherHead === this.publisherTail + } + + isFull(): boolean { + return false + } + + size(): number { + return this.publisherIndex - this.subscribersIndex + } + + publish(value: A): boolean { + const subscribers = this.publisherTail.subscribers + if (subscribers !== 0) { + this.publisherTail.next = { + value, + subscribers, + next: null + } + this.publisherTail = this.publisherTail.next + this.publisherIndex += 1 + } + if (this.replayBuffer) { + this.replayBuffer.offer(value) + } + return true + } + + publishAll(elements: Iterable): Chunk.Chunk { + if (this.publisherTail.subscribers !== 0) { + for (const a of elements) { + this.publish(a) + } + } else if (this.replayBuffer) { + this.replayBuffer.offerAll(elements) + } + return Chunk.empty() + } + + slide(): void { + if (this.publisherHead !== this.publisherTail) { + this.publisherHead = this.publisherHead.next! + this.publisherHead.value = AbsentValue + this.subscribersIndex += 1 + } + if (this.replayBuffer) { + this.replayBuffer.slide() + } + } + + subscribe(): Subscription { + this.publisherTail.subscribers += 1 + return new UnboundedPubSubSubscription( + this, + this.publisherTail, + this.publisherIndex, + false + ) + } +} + +/** @internal */ +class UnboundedPubSubSubscription implements Subscription { + constructor( + private self: UnboundedPubSub, + private subscriberHead: Node, + private subscriberIndex: number, + private unsubscribed: boolean + ) { + } + + isEmpty(): boolean { + if (this.unsubscribed) { + return true + } + let empty = true + let loop = true + while (loop) { + if (this.subscriberHead === this.self.publisherTail) { + loop = false + } else { + if (this.subscriberHead.next!.value !== AbsentValue) { + empty = false + loop = false + } else { + this.subscriberHead = this.subscriberHead.next! + this.subscriberIndex += 1 + } + } + } + return empty + } + + size() { + if (this.unsubscribed) { + return 0 + } + return this.self.publisherIndex - Math.max(this.subscriberIndex, this.self.subscribersIndex) + } + + poll(default_: D): A | D { + if (this.unsubscribed) { + return default_ + } + let loop = true + let polled: A | D = default_ + while (loop) { + if (this.subscriberHead === this.self.publisherTail) { + loop = false + } else { + const elem = this.subscriberHead.next!.value + if (elem !== AbsentValue) { + polled = elem + this.subscriberHead.subscribers -= 1 + if (this.subscriberHead.subscribers === 0) { + this.self.publisherHead = this.self.publisherHead.next! + this.self.publisherHead.value = AbsentValue + this.self.subscribersIndex += 1 + } + loop = false + } + this.subscriberHead = this.subscriberHead.next! + this.subscriberIndex += 1 + } + } + return polled + } + + pollUpTo(n: number): Chunk.Chunk { + const builder: Array = [] + const default_ = AbsentValue + let i = 0 + while (i !== n) { + const a = this.poll(default_ as unknown as A) + if (a === default_) { + i = n + } else { + builder.push(a) + i += 1 + } + } + return Chunk.fromIterable(builder) + } + + unsubscribe(): void { + if (!this.unsubscribed) { + this.unsubscribed = true + this.self.publisherTail.subscribers -= 1 + while (this.subscriberHead !== this.self.publisherTail) { + if (this.subscriberHead.next!.value !== AbsentValue) { + this.subscriberHead.subscribers -= 1 + if (this.subscriberHead.subscribers === 0) { + this.self.publisherHead = this.self.publisherHead.next! + this.self.publisherHead.value = AbsentValue + this.self.subscribersIndex += 1 + } + } + this.subscriberHead = this.subscriberHead.next! + } + } + } +} + +/** @internal */ +class SubscriptionImpl extends Effectable.Class implements Queue.Dequeue { + [queue.DequeueTypeId] = queue.dequeueVariance + + constructor( + readonly pubsub: AtomicPubSub, + readonly subscribers: Subscribers, + readonly subscription: Subscription, + readonly pollers: MutableQueue.MutableQueue>, + readonly shutdownHook: Deferred.Deferred, + readonly shutdownFlag: MutableRef.MutableRef, + readonly strategy: PubSubStrategy, + readonly replayWindow: ReplayWindow + ) { + super() + } + + commit() { + return this.take + } + + pipe() { + return pipeArguments(this, arguments) + } + + capacity(): number { + return this.pubsub.capacity + } + + isActive(): boolean { + return !MutableRef.get(this.shutdownFlag) + } + + get size(): Effect.Effect { + return core.suspend(() => + MutableRef.get(this.shutdownFlag) + ? core.interrupt + : core.succeed(this.subscription.size() + this.replayWindow.remaining) + ) + } + + unsafeSize(): Option.Option { + if (MutableRef.get(this.shutdownFlag)) { + return Option.none() + } + return Option.some(this.subscription.size() + this.replayWindow.remaining) + } + + get isFull(): Effect.Effect { + return core.suspend(() => + MutableRef.get(this.shutdownFlag) + ? core.interrupt + : core.succeed(this.subscription.size() === this.capacity()) + ) + } + + get isEmpty(): Effect.Effect { + return core.map(this.size, (size) => size === 0) + } + + get shutdown(): Effect.Effect { + return core.uninterruptible( + core.withFiberRuntime((state) => { + MutableRef.set(this.shutdownFlag, true) + return pipe( + fiberRuntime.forEachParUnbounded( + unsafePollAllQueue(this.pollers), + (d) => core.deferredInterruptWith(d, state.id()), + false + ), + core.zipRight(core.sync(() => { + this.subscribers.delete(this.subscription) + this.subscription.unsubscribe() + this.strategy.unsafeOnPubSubEmptySpace(this.pubsub, this.subscribers) + })), + core.whenEffect(core.deferredSucceed(this.shutdownHook, void 0)), + core.asVoid + ) + }) + ) + } + + get isShutdown(): Effect.Effect { + return core.sync(() => MutableRef.get(this.shutdownFlag)) + } + + get awaitShutdown(): Effect.Effect { + return core.deferredAwait(this.shutdownHook) + } + + get take(): Effect.Effect { + return core.withFiberRuntime((state) => { + if (MutableRef.get(this.shutdownFlag)) { + return core.interrupt + } + if (this.replayWindow.remaining > 0) { + const message = this.replayWindow.take()! + return core.succeed(message) + } + const message = MutableQueue.isEmpty(this.pollers) + ? this.subscription.poll(MutableQueue.EmptyMutableQueue) + : MutableQueue.EmptyMutableQueue + if (message === MutableQueue.EmptyMutableQueue) { + const deferred = core.deferredUnsafeMake(state.id()) + return pipe( + core.suspend(() => { + pipe(this.pollers, MutableQueue.offer(deferred)) + pipe(this.subscribers, addSubscribers(this.subscription, this.pollers)) + this.strategy.unsafeCompletePollers( + this.pubsub, + this.subscribers, + this.subscription, + this.pollers + ) + return MutableRef.get(this.shutdownFlag) ? core.interrupt : core.deferredAwait(deferred) + }), + core.onInterrupt(() => core.sync(() => unsafeRemove(this.pollers, deferred))) + ) + } else { + this.strategy.unsafeOnPubSubEmptySpace(this.pubsub, this.subscribers) + return core.succeed(message) + } + }) + } + + get takeAll(): Effect.Effect> { + return core.suspend(() => { + if (MutableRef.get(this.shutdownFlag)) { + return core.interrupt + } + const as = MutableQueue.isEmpty(this.pollers) + ? unsafePollAllSubscription(this.subscription) + : Chunk.empty() + this.strategy.unsafeOnPubSubEmptySpace(this.pubsub, this.subscribers) + if (this.replayWindow.remaining > 0) { + return core.succeed(Chunk.appendAll(this.replayWindow.takeAll(), as)) + } + return core.succeed(as) + }) + } + + takeUpTo(this: this, max: number): Effect.Effect> { + return core.suspend(() => { + if (MutableRef.get(this.shutdownFlag)) { + return core.interrupt + } + let replay: Chunk.Chunk | undefined = undefined + if (this.replayWindow.remaining >= max) { + const as = this.replayWindow.takeN(max) + return core.succeed(as) + } else if (this.replayWindow.remaining > 0) { + replay = this.replayWindow.takeAll() + max = max - replay.length + } + const as = MutableQueue.isEmpty(this.pollers) + ? unsafePollN(this.subscription, max) + : Chunk.empty() + this.strategy.unsafeOnPubSubEmptySpace(this.pubsub, this.subscribers) + return replay ? core.succeed(Chunk.appendAll(replay, as)) : core.succeed(as) + }) + } + + takeBetween(min: number, max: number): Effect.Effect> { + return core.suspend(() => takeRemainderLoop(this, min, max, Chunk.empty())) + } +} + +/** @internal */ +const takeRemainderLoop = ( + self: Queue.Dequeue, + min: number, + max: number, + acc: Chunk.Chunk +): Effect.Effect> => { + if (max < min) { + return core.succeed(acc) + } + return pipe( + self.takeUpTo(max), + core.flatMap((bs) => { + const remaining = min - bs.length + if (remaining === 1) { + return pipe(self.take, core.map((b) => pipe(acc, Chunk.appendAll(bs), Chunk.append(b)))) + } + if (remaining > 1) { + return pipe( + self.take, + core.flatMap((b) => + takeRemainderLoop( + self, + remaining - 1, + max - bs.length - 1, + pipe(acc, Chunk.appendAll(bs), Chunk.append(b)) + ) + ) + ) + } + return core.succeed(pipe(acc, Chunk.appendAll(bs))) + }) + ) +} + +/** @internal */ +class PubSubImpl implements PubSub.PubSub { + readonly [queue.EnqueueTypeId] = queue.enqueueVariance + readonly [queue.DequeueTypeId] = queue.dequeueVariance + + constructor( + readonly pubsub: AtomicPubSub, + readonly subscribers: Subscribers, + readonly scope: Scope.Scope.Closeable, + readonly shutdownHook: Deferred.Deferred, + readonly shutdownFlag: MutableRef.MutableRef, + readonly strategy: PubSubStrategy + ) {} + + capacity(): number { + return this.pubsub.capacity + } + + get size(): Effect.Effect { + return core.suspend(() => + MutableRef.get(this.shutdownFlag) ? + core.interrupt : + core.sync(() => this.pubsub.size()) + ) + } + + unsafeSize(): Option.Option { + if (MutableRef.get(this.shutdownFlag)) { + return Option.none() + } + return Option.some(this.pubsub.size()) + } + + get isFull(): Effect.Effect { + return core.map(this.size, (size) => size === this.capacity()) + } + + get isEmpty(): Effect.Effect { + return core.map(this.size, (size) => size === 0) + } + + get awaitShutdown(): Effect.Effect { + return core.deferredAwait(this.shutdownHook) + } + + get isShutdown(): Effect.Effect { + return core.sync(() => MutableRef.get(this.shutdownFlag)) + } + + get shutdown(): Effect.Effect { + return core.uninterruptible(core.withFiberRuntime((state) => { + pipe(this.shutdownFlag, MutableRef.set(true)) + return pipe( + this.scope.close(core.exitInterrupt(state.id())), + core.zipRight(this.strategy.shutdown), + core.whenEffect(core.deferredSucceed(this.shutdownHook, void 0)), + core.asVoid + ) + })) + } + + publish(value: A): Effect.Effect { + return core.suspend(() => { + if (MutableRef.get(this.shutdownFlag)) { + return core.interrupt + } + + if (this.pubsub.publish(value)) { + this.strategy.unsafeCompleteSubscribers(this.pubsub, this.subscribers) + return core.succeed(true) + } + + return this.strategy.handleSurplus( + this.pubsub, + this.subscribers, + Chunk.of(value), + this.shutdownFlag + ) + }) + } + + isActive(): boolean { + return !MutableRef.get(this.shutdownFlag) + } + + unsafeOffer(value: A): boolean { + if (MutableRef.get(this.shutdownFlag)) { + return false + } + + if ((this.pubsub as AtomicPubSub).publish(value)) { + this.strategy.unsafeCompleteSubscribers(this.pubsub, this.subscribers) + return true + } + + return false + } + + publishAll(elements: Iterable): Effect.Effect { + return core.suspend(() => { + if (MutableRef.get(this.shutdownFlag)) { + return core.interrupt + } + const surplus = unsafePublishAll(this.pubsub, elements) + this.strategy.unsafeCompleteSubscribers(this.pubsub, this.subscribers) + if (Chunk.isEmpty(surplus)) { + return core.succeed(true) + } + return this.strategy.handleSurplus( + this.pubsub, + this.subscribers, + surplus, + this.shutdownFlag + ) + }) + } + + get subscribe(): Effect.Effect, never, Scope.Scope> { + const acquire = core.tap( + fiberRuntime.all([ + this.scope.fork(executionStrategy.sequential), + makeSubscription(this.pubsub, this.subscribers, this.strategy) + ]), + (tuple) => tuple[0].addFinalizer(() => tuple[1].shutdown) + ) + return core.map( + fiberRuntime.acquireRelease(acquire, (tuple, exit) => tuple[0].close(exit)), + (tuple) => tuple[1] + ) + } + + offer(value: A): Effect.Effect { + return this.publish(value) + } + + offerAll(elements: Iterable): Effect.Effect { + return this.publishAll(elements) + } + + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const makePubSub = ( + pubsub: AtomicPubSub, + strategy: PubSubStrategy +): Effect.Effect> => + core.flatMap( + fiberRuntime.scopeMake(), + (scope) => + core.map(core.deferredMake(), (deferred) => + unsafeMakePubSub( + pubsub, + new Map(), + scope, + deferred, + MutableRef.make(false), + strategy + )) + ) + +/** @internal */ +export const unsafeMakePubSub = ( + pubsub: AtomicPubSub, + subscribers: Subscribers, + scope: Scope.Scope.Closeable, + shutdownHook: Deferred.Deferred, + shutdownFlag: MutableRef.MutableRef, + strategy: PubSubStrategy +): PubSub.PubSub => new PubSubImpl(pubsub, subscribers, scope, shutdownHook, shutdownFlag, strategy) + +/** @internal */ +const ensureCapacity = (capacity: number): void => { + if (capacity <= 0) { + throw new core.InvalidPubSubCapacityException(`Cannot construct PubSub with capacity of ${capacity}`) + } +} + +/** @internal */ +const unsafeCompleteDeferred = (deferred: Deferred.Deferred, a: A): void => { + core.deferredUnsafeDone(deferred, core.succeed(a)) +} + +/** @internal */ +const unsafeOfferAll = (queue: MutableQueue.MutableQueue, as: Iterable): Chunk.Chunk => { + return pipe(queue, MutableQueue.offerAll(as)) +} + +/** @internal */ +const unsafePollAllQueue = (queue: MutableQueue.MutableQueue): Chunk.Chunk => { + return pipe(queue, MutableQueue.pollUpTo(Number.POSITIVE_INFINITY)) +} + +/** @internal */ +const unsafePollAllSubscription = (subscription: Subscription): Chunk.Chunk => { + return subscription.pollUpTo(Number.POSITIVE_INFINITY) +} + +/** @internal */ +const unsafePollN = (subscription: Subscription, max: number): Chunk.Chunk => { + return subscription.pollUpTo(max) +} + +/** @internal */ +const unsafePublishAll = (pubsub: AtomicPubSub, as: Iterable): Chunk.Chunk => { + return pubsub.publishAll(as) +} + +/** @internal */ +const unsafeRemove = (queue: MutableQueue.MutableQueue, value: A): void => { + unsafeOfferAll( + queue, + pipe(unsafePollAllQueue(queue), Chunk.filter((elem) => elem !== value)) + ) +} + +// ----------------------------------------------------------------------------- +// PubSub.Strategy +// ----------------------------------------------------------------------------- + +/** + * A `PubSubStrategy` describes the protocol for how publishers and subscribers + * will communicate with each other through the `PubSub`. + * + * @internal + */ +export interface PubSubStrategy { + /** + * Describes any finalization logic associated with this strategy. + */ + readonly shutdown: Effect.Effect + + /** + * Describes how publishers should signal to subscribers that they are + * waiting for space to become available in the `PubSub`. + */ + handleSurplus( + pubsub: AtomicPubSub, + subscribers: Subscribers, + elements: Iterable, + isShutdown: MutableRef.MutableRef + ): Effect.Effect + + /** + * Describes how subscribers should signal to publishers waiting for space + * to become available in the `PubSub` that space may be available. + */ + unsafeOnPubSubEmptySpace( + pubsub: AtomicPubSub, + subscribers: Subscribers + ): void + + /** + * Describes how subscribers waiting for additional values from the `PubSub` + * should take those values and signal to publishers that they are no + * longer waiting for additional values. + */ + unsafeCompletePollers( + pubsub: AtomicPubSub, + subscribers: Subscribers, + subscription: Subscription, + pollers: MutableQueue.MutableQueue> + ): void + + /** + * Describes how publishers should signal to subscribers waiting for + * additional values from the `PubSub` that new values are available. + */ + unsafeCompleteSubscribers( + pubsub: AtomicPubSub, + subscribers: Subscribers + ): void +} + +/** + * A strategy that applies back pressure to publishers when the `PubSub` is at + * capacity. This guarantees that all subscribers will receive all messages + * published to the `PubSub` while they are subscribed. However, it creates the + * risk that a slow subscriber will slow down the rate at which messages + * are published and received by other subscribers. + * + * @internal + */ +class BackPressureStrategy implements PubSubStrategy { + publishers: MutableQueue.MutableQueue< + readonly [A, Deferred.Deferred, boolean] + > = MutableQueue.unbounded() + + get shutdown(): Effect.Effect { + return core.flatMap(core.fiberId, (fiberId) => + core.flatMap( + core.sync(() => unsafePollAllQueue(this.publishers)), + (publishers) => + fiberRuntime.forEachConcurrentDiscard( + publishers, + ([_, deferred, last]) => + last ? + pipe(core.deferredInterruptWith(deferred, fiberId), core.asVoid) : + core.void, + false, + false + ) + )) + } + + handleSurplus( + pubsub: AtomicPubSub, + subscribers: Subscribers, + elements: Iterable, + isShutdown: MutableRef.MutableRef + ): Effect.Effect { + return core.withFiberRuntime((state) => { + const deferred = core.deferredUnsafeMake(state.id()) + return pipe( + core.suspend(() => { + this.unsafeOffer(elements, deferred) + this.unsafeOnPubSubEmptySpace(pubsub, subscribers) + this.unsafeCompleteSubscribers(pubsub, subscribers) + return MutableRef.get(isShutdown) ? + core.interrupt : + core.deferredAwait(deferred) + }), + core.onInterrupt(() => core.sync(() => this.unsafeRemove(deferred))) + ) + }) + } + + unsafeOnPubSubEmptySpace( + pubsub: AtomicPubSub, + subscribers: Subscribers + ): void { + let keepPolling = true + while (keepPolling && !pubsub.isFull()) { + const publisher = pipe(this.publishers, MutableQueue.poll(MutableQueue.EmptyMutableQueue)) + if (publisher === MutableQueue.EmptyMutableQueue) { + keepPolling = false + } else { + const published = pubsub.publish(publisher[0]) + if (published && publisher[2]) { + unsafeCompleteDeferred(publisher[1], true) + } else if (!published) { + unsafeOfferAll( + this.publishers, + pipe(unsafePollAllQueue(this.publishers), Chunk.prepend(publisher)) + ) + } + this.unsafeCompleteSubscribers(pubsub, subscribers) + } + } + } + + unsafeCompletePollers( + pubsub: AtomicPubSub, + subscribers: Subscribers, + subscription: Subscription, + pollers: MutableQueue.MutableQueue> + ): void { + return unsafeStrategyCompletePollers(this, pubsub, subscribers, subscription, pollers) + } + + unsafeCompleteSubscribers(pubsub: AtomicPubSub, subscribers: Subscribers): void { + return unsafeStrategyCompleteSubscribers(this, pubsub, subscribers) + } + + private unsafeOffer(elements: Iterable, deferred: Deferred.Deferred): void { + const iterator = elements[Symbol.iterator]() + let next: IteratorResult = iterator.next() + if (!next.done) { + // eslint-disable-next-line no-constant-condition + while (1) { + const value = next.value + next = iterator.next() + if (next.done) { + pipe( + this.publishers, + MutableQueue.offer([value, deferred, true as boolean] as const) + ) + break + } + pipe( + this.publishers, + MutableQueue.offer([value, deferred, false as boolean] as const) + ) + } + } + } + + unsafeRemove(deferred: Deferred.Deferred): void { + unsafeOfferAll( + this.publishers, + pipe(unsafePollAllQueue(this.publishers), Chunk.filter(([_, a]) => a !== deferred)) + ) + } +} + +/** + * A strategy that drops new messages when the `PubSub` is at capacity. This + * guarantees that a slow subscriber will not slow down the rate at which + * messages are published. However, it creates the risk that a slow + * subscriber will slow down the rate at which messages are received by + * other subscribers and that subscribers may not receive all messages + * published to the `PubSub` while they are subscribed. + * + * @internal + */ +export class DroppingStrategy implements PubSubStrategy { + get shutdown(): Effect.Effect { + return core.void + } + + handleSurplus( + _pubsub: AtomicPubSub, + _subscribers: Subscribers, + _elements: Iterable, + _isShutdown: MutableRef.MutableRef + ): Effect.Effect { + return core.succeed(false) + } + + unsafeOnPubSubEmptySpace( + _pubsub: AtomicPubSub, + _subscribers: Subscribers + ): void { + // + } + + unsafeCompletePollers( + pubsub: AtomicPubSub, + subscribers: Subscribers, + subscription: Subscription, + pollers: MutableQueue.MutableQueue> + ): void { + return unsafeStrategyCompletePollers(this, pubsub, subscribers, subscription, pollers) + } + + unsafeCompleteSubscribers(pubsub: AtomicPubSub, subscribers: Subscribers): void { + return unsafeStrategyCompleteSubscribers(this, pubsub, subscribers) + } +} + +/** + * A strategy that adds new messages and drops old messages when the `PubSub` is + * at capacity. This guarantees that a slow subscriber will not slow down + * the rate at which messages are published and received by other + * subscribers. However, it creates the risk that a slow subscriber will + * not receive some messages published to the `PubSub` while it is subscribed. + * + * @internal + */ +export class SlidingStrategy implements PubSubStrategy { + get shutdown(): Effect.Effect { + return core.void + } + + handleSurplus( + pubsub: AtomicPubSub, + subscribers: Subscribers, + elements: Iterable, + _isShutdown: MutableRef.MutableRef + ): Effect.Effect { + return core.sync(() => { + this.unsafeSlidingPublish(pubsub, elements) + this.unsafeCompleteSubscribers(pubsub, subscribers) + return true + }) + } + + unsafeOnPubSubEmptySpace( + _pubsub: AtomicPubSub, + _subscribers: Subscribers + ): void { + // + } + + unsafeCompletePollers( + pubsub: AtomicPubSub, + subscribers: Subscribers, + subscription: Subscription, + pollers: MutableQueue.MutableQueue> + ): void { + return unsafeStrategyCompletePollers(this, pubsub, subscribers, subscription, pollers) + } + + unsafeCompleteSubscribers(pubsub: AtomicPubSub, subscribers: Subscribers): void { + return unsafeStrategyCompleteSubscribers(this, pubsub, subscribers) + } + + unsafeSlidingPublish(pubsub: AtomicPubSub, elements: Iterable): void { + const it = elements[Symbol.iterator]() + let next = it.next() + if (!next.done && pubsub.capacity > 0) { + let a = next.value + let loop = true + while (loop) { + pubsub.slide() + const pub = pubsub.publish(a) + if (pub && (next = it.next()) && !next.done) { + a = next.value + } else if (pub) { + loop = false + } + } + } + } +} + +/** @internal */ +const unsafeStrategyCompletePollers = ( + strategy: PubSubStrategy, + pubsub: AtomicPubSub, + subscribers: Subscribers, + subscription: Subscription, + pollers: MutableQueue.MutableQueue> +): void => { + let keepPolling = true + while (keepPolling && !subscription.isEmpty()) { + const poller = pipe(pollers, MutableQueue.poll(MutableQueue.EmptyMutableQueue)) + if (poller === MutableQueue.EmptyMutableQueue) { + pipe(subscribers, removeSubscribers(subscription, pollers)) + if (MutableQueue.isEmpty(pollers)) { + keepPolling = false + } else { + pipe(subscribers, addSubscribers(subscription, pollers)) + } + } else { + const pollResult = subscription.poll(MutableQueue.EmptyMutableQueue) + if (pollResult === MutableQueue.EmptyMutableQueue) { + unsafeOfferAll(pollers, pipe(unsafePollAllQueue(pollers), Chunk.prepend(poller))) + } else { + unsafeCompleteDeferred(poller, pollResult) + strategy.unsafeOnPubSubEmptySpace(pubsub, subscribers) + } + } + } +} + +/** @internal */ +const unsafeStrategyCompleteSubscribers = ( + strategy: PubSubStrategy, + pubsub: AtomicPubSub, + subscribers: Subscribers +): void => { + for ( + const [subscription, pollersSet] of subscribers + ) { + for (const pollers of pollersSet) { + strategy.unsafeCompletePollers(pubsub, subscribers, subscription, pollers) + } + } +} + +interface ReplayNode { + value: A | AbsentValue + next: ReplayNode | null +} + +class ReplayBuffer { + constructor(readonly capacity: number) {} + + head: ReplayNode = { value: AbsentValue, next: null } + tail: ReplayNode = this.head + size = 0 + index = 0 + + slide() { + this.index++ + } + offer(a: A): void { + this.tail.value = a + this.tail.next = { + value: AbsentValue, + next: null + } + this.tail = this.tail.next + if (this.size === this.capacity) { + this.head = this.head.next! + } else { + this.size += 1 + } + } + offerAll(as: Iterable): void { + for (const a of as) { + this.offer(a) + } + } +} + +interface ReplayWindow { + take(): A | undefined + takeN(n: number): Chunk.Chunk + takeAll(): Chunk.Chunk + readonly remaining: number +} + +class ReplayWindowImpl implements ReplayWindow { + head: ReplayNode + index: number + remaining: number + constructor(readonly buffer: ReplayBuffer) { + this.index = buffer.index + this.remaining = buffer.size + this.head = buffer.head + } + fastForward() { + while (this.index < this.buffer.index) { + this.head = this.head.next! + this.index++ + } + } + take(): A | undefined { + if (this.remaining === 0) { + return undefined + } else if (this.index < this.buffer.index) { + this.fastForward() + } + this.remaining-- + const value = this.head.value + this.head = this.head.next! + return value as A + } + takeN(n: number): Chunk.Chunk { + if (this.remaining === 0) { + return Chunk.empty() + } else if (this.index < this.buffer.index) { + this.fastForward() + } + const len = Math.min(n, this.remaining) + const items = new Array(len) + for (let i = 0; i < len; i++) { + const value = this.head.value as A + this.head = this.head.next! + items[i] = value + } + this.remaining -= len + return Chunk.unsafeFromArray(items) + } + takeAll(): Chunk.Chunk { + return this.takeN(this.remaining) + } +} + +const emptyReplayWindow: ReplayWindow = { + remaining: 0, + take: () => undefined, + takeN: () => Chunk.empty(), + takeAll: () => Chunk.empty() +} diff --git a/backend/node_modules/effect/src/internal/query.ts b/backend/node_modules/effect/src/internal/query.ts new file mode 100644 index 0000000000000000000000000000000000000000..3ba13226c2c45b8f5a58a6c26aac31443f33666b --- /dev/null +++ b/backend/node_modules/effect/src/internal/query.ts @@ -0,0 +1,204 @@ +import type * as Cache from "../Cache.js" +import type { Deferred } from "../Deferred.js" +import { seconds } from "../Duration.js" +import type * as Effect from "../Effect.js" +import { dual } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import type * as Request from "../Request.js" +import type * as RequestResolver from "../RequestResolver.js" +import * as BlockedRequests from "./blockedRequests.js" +import { unsafeMakeWith } from "./cache.js" +import * as core from "./core.js" +import { ensuring } from "./fiberRuntime.js" +import { Listeners } from "./request.js" + +type RequestCache = Cache.Cache, { + listeners: Request.Listeners + handle: Deferred +}> + +/** @internal */ +export const currentCache = globalValue( + Symbol.for("effect/FiberRef/currentCache"), + () => + core.fiberRefUnsafeMake(unsafeMakeWith, { + listeners: Request.Listeners + handle: Deferred + }>( + 65536, + () => core.map(core.deferredMake(), (handle) => ({ listeners: new Listeners(), handle })), + () => seconds(60) + )) +) + +/** @internal */ +export const currentCacheEnabled = globalValue( + Symbol.for("effect/FiberRef/currentCacheEnabled"), + () => core.fiberRefUnsafeMake(false) +) + +/** @internal */ +export const fromRequest = < + A extends Request.Request, + Ds extends + | RequestResolver.RequestResolver + | Effect.Effect, any, any> +>( + request: A, + dataSource: Ds +): Effect.Effect< + Request.Request.Success, + Request.Request.Error, + [Ds] extends [Effect.Effect] ? Effect.Effect.Context : never +> => + core.flatMap( + (core.isEffect(dataSource) ? dataSource : core.succeed(dataSource)) as Effect.Effect< + RequestResolver.RequestResolver + >, + (ds) => + core.fiberIdWith((id) => { + const proxy = new Proxy(request, {}) + return core.fiberRefGetWith(currentCacheEnabled, (cacheEnabled) => { + if (cacheEnabled) { + const cached: Effect.Effect = core.fiberRefGetWith(currentCache, (cache) => + core.flatMap(cache.getEither(proxy), (orNew) => { + switch (orNew._tag) { + case "Left": { + if (orNew.left.listeners.interrupted) { + return core.flatMap( + cache.invalidateWhen(proxy, (entry) => entry.handle === orNew.left.handle), + () => cached + ) + } + orNew.left.listeners.increment() + return core.uninterruptibleMask((restore) => + core.flatMap( + core.exit(core.blocked( + BlockedRequests.empty, + restore(core.deferredAwait(orNew.left.handle)) + )), + (exit) => { + orNew.left.listeners.decrement() + return exit + } + ) + ) + } + case "Right": { + orNew.right.listeners.increment() + return core.uninterruptibleMask((restore) => + core.flatMap( + core.exit( + core.blocked( + BlockedRequests.single( + ds as RequestResolver.RequestResolver, + BlockedRequests.makeEntry({ + request: proxy, + result: orNew.right.handle, + listeners: orNew.right.listeners, + ownerId: id, + state: { completed: false } + }) + ), + restore(core.deferredAwait(orNew.right.handle)) + ) + ), + () => { + orNew.right.listeners.decrement() + return core.deferredAwait(orNew.right.handle) + } + ) + ) + } + } + })) + return cached + } + const listeners = new Listeners() + listeners.increment() + return core.flatMap( + core.deferredMake, Request.Request.Error>(), + (ref) => + ensuring( + core.blocked( + BlockedRequests.single( + ds as RequestResolver.RequestResolver, + BlockedRequests.makeEntry({ + request: proxy, + result: ref, + listeners, + ownerId: id, + state: { completed: false } + }) + ), + core.deferredAwait(ref) + ), + core.sync(() => + listeners.decrement() + ) + ) + ) + }) + }) + ) + +/** @internal */ +export const cacheRequest = >( + request: A, + result: Request.Request.Result +): Effect.Effect => { + return core.fiberRefGetWith(currentCacheEnabled, (cacheEnabled) => { + if (cacheEnabled) { + return core.fiberRefGetWith(currentCache, (cache) => + core.flatMap(cache.getEither(request), (orNew) => { + switch (orNew._tag) { + case "Left": { + return core.void + } + case "Right": { + return core.deferredComplete(orNew.right.handle, result) + } + } + })) + } + return core.void + }) +} + +/** @internal */ +export const withRequestCaching: { + (strategy: boolean): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + strategy: boolean + ): Effect.Effect +} = dual< + ( + strategy: boolean + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + strategy: boolean + ) => Effect.Effect +>(2, (self, strategy) => core.fiberRefLocally(self, currentCacheEnabled, strategy)) + +/** @internal */ +export const withRequestCache: { + (cache: Request.Cache): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + cache: Request.Cache + ): Effect.Effect +} = dual< + ( + cache: Request.Cache + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + cache: Request.Cache + ) => Effect.Effect +>( + 2, + // @ts-expect-error + (self, cache) => core.fiberRefLocally(self, currentCache, cache) +) diff --git a/backend/node_modules/effect/src/internal/queue.ts b/backend/node_modules/effect/src/internal/queue.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a40a4b15006c00fef06f551574e87225ffc6981 --- /dev/null +++ b/backend/node_modules/effect/src/internal/queue.ts @@ -0,0 +1,766 @@ +import * as Arr from "../Array.js" +import * as Chunk from "../Chunk.js" +import type * as Deferred from "../Deferred.js" +import type * as Effect from "../Effect.js" +import * as Effectable from "../Effectable.js" +import { dual, pipe } from "../Function.js" +import * as MutableQueue from "../MutableQueue.js" +import * as MutableRef from "../MutableRef.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty } from "../Predicate.js" +import type * as Queue from "../Queue.js" +import * as core from "./core.js" +import * as fiberRuntime from "./fiberRuntime.js" + +/** @internal */ +const EnqueueSymbolKey = "effect/QueueEnqueue" + +/** @internal */ +export const EnqueueTypeId: Queue.EnqueueTypeId = Symbol.for(EnqueueSymbolKey) as Queue.EnqueueTypeId + +/** @internal */ +const DequeueSymbolKey = "effect/QueueDequeue" + +/** @internal */ +export const DequeueTypeId: Queue.DequeueTypeId = Symbol.for(DequeueSymbolKey) as Queue.DequeueTypeId + +/** @internal */ +const QueueStrategySymbolKey = "effect/QueueStrategy" + +/** @internal */ +export const QueueStrategyTypeId: Queue.QueueStrategyTypeId = Symbol.for( + QueueStrategySymbolKey +) as Queue.QueueStrategyTypeId + +/** @internal */ +const BackingQueueSymbolKey = "effect/BackingQueue" + +/** @internal */ +export const BackingQueueTypeId: Queue.BackingQueueTypeId = Symbol.for( + BackingQueueSymbolKey +) as Queue.BackingQueueTypeId + +const queueStrategyVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +const backingQueueVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +export const enqueueVariance = { + /* c8 ignore next */ + _In: (_: unknown) => _ +} + +/** @internal */ +export const dequeueVariance = { + /* c8 ignore next */ + _Out: (_: never) => _ +} + +/** @internal */ +class QueueImpl extends Effectable.Class implements Queue.Queue { + readonly [EnqueueTypeId] = enqueueVariance + readonly [DequeueTypeId] = dequeueVariance + + constructor( + /** @internal */ + readonly queue: Queue.BackingQueue, + /** @internal */ + readonly takers: MutableQueue.MutableQueue>, + /** @internal */ + readonly shutdownHook: Deferred.Deferred, + /** @internal */ + readonly shutdownFlag: MutableRef.MutableRef, + /** @internal */ + readonly strategy: Queue.Strategy + ) { + super() + } + + pipe() { + return pipeArguments(this, arguments) + } + + commit() { + return this.take + } + + capacity(): number { + return this.queue.capacity() + } + + get size(): Effect.Effect { + return core.suspend(() => core.catchAll(this.unsafeSize(), () => core.interrupt)) + } + + unsafeSize() { + if (MutableRef.get(this.shutdownFlag)) { + return Option.none() + } + return Option.some( + this.queue.length() - + MutableQueue.length(this.takers) + + this.strategy.surplusSize() + ) + } + + get isEmpty(): Effect.Effect { + return core.map(this.size, (size) => size <= 0) + } + + get isFull(): Effect.Effect { + return core.map(this.size, (size) => size >= this.capacity()) + } + + get shutdown(): Effect.Effect { + return core.uninterruptible( + core.withFiberRuntime((state) => { + pipe(this.shutdownFlag, MutableRef.set(true)) + return pipe( + fiberRuntime.forEachConcurrentDiscard( + unsafePollAll(this.takers), + (d) => core.deferredInterruptWith(d, state.id()), + false, + false + ), + core.zipRight(this.strategy.shutdown), + core.whenEffect(core.deferredSucceed(this.shutdownHook, void 0)), + core.asVoid + ) + }) + ) + } + + get isShutdown(): Effect.Effect { + return core.sync(() => MutableRef.get(this.shutdownFlag)) + } + + get awaitShutdown(): Effect.Effect { + return core.deferredAwait(this.shutdownHook) + } + + isActive() { + return !MutableRef.get(this.shutdownFlag) + } + + unsafeOffer(value: A): boolean { + if (MutableRef.get(this.shutdownFlag)) { + return false + } + let noRemaining: boolean + if (this.queue.length() === 0) { + const taker = pipe( + this.takers, + MutableQueue.poll(MutableQueue.EmptyMutableQueue) + ) + if (taker !== MutableQueue.EmptyMutableQueue) { + unsafeCompleteDeferred(taker, value) + noRemaining = true + } else { + noRemaining = false + } + } else { + noRemaining = false + } + if (noRemaining) { + return true + } + // Not enough takers, offer to the queue + const succeeded = this.queue.offer(value) + unsafeCompleteTakers(this.strategy, this.queue, this.takers) + return succeeded + } + + offer(value: A): Effect.Effect { + return core.suspend(() => { + if (MutableRef.get(this.shutdownFlag)) { + return core.interrupt + } + let noRemaining: boolean + if (this.queue.length() === 0) { + const taker = pipe( + this.takers, + MutableQueue.poll(MutableQueue.EmptyMutableQueue) + ) + if (taker !== MutableQueue.EmptyMutableQueue) { + unsafeCompleteDeferred(taker, value) + noRemaining = true + } else { + noRemaining = false + } + } else { + noRemaining = false + } + if (noRemaining) { + return core.succeed(true) + } + // Not enough takers, offer to the queue + const succeeded = this.queue.offer(value) + unsafeCompleteTakers(this.strategy, this.queue, this.takers) + return succeeded + ? core.succeed(true) + : this.strategy.handleSurplus([value], this.queue, this.takers, this.shutdownFlag) + }) + } + + offerAll(iterable: Iterable): Effect.Effect { + return core.suspend(() => { + if (MutableRef.get(this.shutdownFlag)) { + return core.interrupt + } + const values = Arr.fromIterable(iterable) + const pTakers = this.queue.length() === 0 + ? Arr.fromIterable(unsafePollN(this.takers, values.length)) + : Arr.empty + const [forTakers, remaining] = pipe(values, Arr.splitAt(pTakers.length)) + for (let i = 0; i < pTakers.length; i++) { + const taker = (pTakers as any)[i] + const item = forTakers[i] + unsafeCompleteDeferred(taker, item) + } + if (remaining.length === 0) { + return core.succeed(true) + } + // Not enough takers, offer to the queue + const surplus = this.queue.offerAll(remaining) + unsafeCompleteTakers(this.strategy, this.queue, this.takers) + return Chunk.isEmpty(surplus) + ? core.succeed(true) + : this.strategy.handleSurplus(surplus, this.queue, this.takers, this.shutdownFlag) + }) + } + + get take(): Effect.Effect { + return core.withFiberRuntime((state) => { + if (MutableRef.get(this.shutdownFlag)) { + return core.interrupt + } + const item = this.queue.poll(MutableQueue.EmptyMutableQueue) + if (item !== MutableQueue.EmptyMutableQueue) { + this.strategy.unsafeOnQueueEmptySpace(this.queue, this.takers) + return core.succeed(item) + } else { + // Add the deferred to takers, then: + // - Try to take again in case a value was added since + // - Wait for the deferred to be completed + // - Clean up resources in case of interruption + const deferred = core.deferredUnsafeMake(state.id()) + return pipe( + core.suspend(() => { + pipe(this.takers, MutableQueue.offer(deferred)) + unsafeCompleteTakers(this.strategy, this.queue, this.takers) + return MutableRef.get(this.shutdownFlag) ? + core.interrupt : + core.deferredAwait(deferred) + }), + core.onInterrupt(() => { + return core.sync(() => unsafeRemove(this.takers, deferred)) + }) + ) + } + }) + } + + get takeAll(): Effect.Effect> { + return core.suspend(() => { + return MutableRef.get(this.shutdownFlag) + ? core.interrupt + : core.sync(() => { + const values = this.queue.pollUpTo(Number.POSITIVE_INFINITY) + this.strategy.unsafeOnQueueEmptySpace(this.queue, this.takers) + return Chunk.fromIterable(values) + }) + }) + } + + takeUpTo(max: number): Effect.Effect> { + return core.suspend(() => + MutableRef.get(this.shutdownFlag) + ? core.interrupt + : core.sync(() => { + const values = this.queue.pollUpTo(max) + this.strategy.unsafeOnQueueEmptySpace(this.queue, this.takers) + return Chunk.fromIterable(values) + }) + ) + } + + takeBetween(min: number, max: number): Effect.Effect> { + return core.suspend(() => + takeRemainderLoop( + this, + min, + max, + Chunk.empty() + ) + ) + } +} + +/** @internal */ +const takeRemainderLoop = ( + self: Queue.Dequeue, + min: number, + max: number, + acc: Chunk.Chunk +): Effect.Effect> => { + if (max < min) { + return core.succeed(acc) + } + return pipe( + takeUpTo(self, max), + core.flatMap((bs) => { + const remaining = min - bs.length + if (remaining === 1) { + return pipe( + take(self), + core.map((b) => pipe(acc, Chunk.appendAll(bs), Chunk.append(b))) + ) + } + if (remaining > 1) { + return pipe( + take(self), + core.flatMap((b) => + takeRemainderLoop( + self, + remaining - 1, + max - bs.length - 1, + pipe(acc, Chunk.appendAll(bs), Chunk.append(b)) + ) + ) + ) + } + return core.succeed(pipe(acc, Chunk.appendAll(bs))) + }) + ) +} + +/** @internal */ +export const isQueue = (u: unknown): u is Queue.Queue => isEnqueue(u) && isDequeue(u) + +/** @internal */ +export const isEnqueue = (u: unknown): u is Queue.Enqueue => hasProperty(u, EnqueueTypeId) + +/** @internal */ +export const isDequeue = (u: unknown): u is Queue.Dequeue => hasProperty(u, DequeueTypeId) + +/** @internal */ +export const bounded = (requestedCapacity: number): Effect.Effect> => + pipe( + core.sync(() => MutableQueue.bounded(requestedCapacity)), + core.flatMap((queue) => make(backingQueueFromMutableQueue(queue), backPressureStrategy())) + ) + +/** @internal */ +export const dropping = (requestedCapacity: number): Effect.Effect> => + pipe( + core.sync(() => MutableQueue.bounded(requestedCapacity)), + core.flatMap((queue) => make(backingQueueFromMutableQueue(queue), droppingStrategy())) + ) + +/** @internal */ +export const sliding = (requestedCapacity: number): Effect.Effect> => + pipe( + core.sync(() => MutableQueue.bounded(requestedCapacity)), + core.flatMap((queue) => make(backingQueueFromMutableQueue(queue), slidingStrategy())) + ) + +/** @internal */ +export const unbounded = (): Effect.Effect> => + pipe( + core.sync(() => MutableQueue.unbounded()), + core.flatMap((queue) => make(backingQueueFromMutableQueue(queue), droppingStrategy())) + ) + +/** @internal */ +const unsafeMake = ( + queue: Queue.BackingQueue, + takers: MutableQueue.MutableQueue>, + shutdownHook: Deferred.Deferred, + shutdownFlag: MutableRef.MutableRef, + strategy: Queue.Strategy +): Queue.Queue => { + return new QueueImpl(queue, takers, shutdownHook, shutdownFlag, strategy) +} + +/** @internal */ +export const make = ( + queue: Queue.BackingQueue, + strategy: Queue.Strategy +): Effect.Effect> => + pipe( + core.deferredMake(), + core.map((deferred) => + unsafeMake( + queue, + MutableQueue.unbounded(), + deferred, + MutableRef.make(false), + strategy + ) + ) + ) + +/** @internal */ +export class BackingQueueFromMutableQueue implements Queue.BackingQueue { + readonly [BackingQueueTypeId] = backingQueueVariance + constructor(readonly mutable: MutableQueue.MutableQueue) {} + poll(def: Def): A | Def { + return MutableQueue.poll(this.mutable, def) + } + pollUpTo(limit: number): Chunk.Chunk { + return MutableQueue.pollUpTo(this.mutable, limit) + } + offerAll(elements: Iterable): Chunk.Chunk { + return MutableQueue.offerAll(this.mutable, elements) + } + offer(element: A): boolean { + return MutableQueue.offer(this.mutable, element) + } + capacity(): number { + return MutableQueue.capacity(this.mutable) + } + length(): number { + return MutableQueue.length(this.mutable) + } +} + +/** @internal */ +export const backingQueueFromMutableQueue = (mutable: MutableQueue.MutableQueue): Queue.BackingQueue => + new BackingQueueFromMutableQueue(mutable) + +/** @internal */ +export const capacity = (self: Queue.Dequeue | Queue.Enqueue): number => self.capacity() + +/** @internal */ +export const size = (self: Queue.Dequeue | Queue.Enqueue): Effect.Effect => self.size + +/** @internal */ +export const isFull = (self: Queue.Dequeue | Queue.Enqueue): Effect.Effect => self.isFull + +/** @internal */ +export const isEmpty = (self: Queue.Dequeue | Queue.Enqueue): Effect.Effect => self.isEmpty + +/** @internal */ +export const isShutdown = (self: Queue.Dequeue | Queue.Enqueue): Effect.Effect => self.isShutdown + +/** @internal */ +export const awaitShutdown = (self: Queue.Dequeue | Queue.Enqueue): Effect.Effect => self.awaitShutdown + +/** @internal */ +export const shutdown = (self: Queue.Dequeue | Queue.Enqueue): Effect.Effect => self.shutdown + +/** @internal */ +export const offer = dual< + (value: A) => (self: Queue.Enqueue) => Effect.Effect, + (self: Queue.Enqueue, value: A) => Effect.Effect +>(2, (self, value) => self.offer(value)) + +/** @internal */ +export const unsafeOffer = dual< + (value: A) => (self: Queue.Enqueue) => boolean, + (self: Queue.Enqueue, value: A) => boolean +>(2, (self, value) => self.unsafeOffer(value)) + +/** @internal */ +export const offerAll = dual< + ( + iterable: Iterable + ) => (self: Queue.Enqueue) => Effect.Effect, + ( + self: Queue.Enqueue, + iterable: Iterable + ) => Effect.Effect +>(2, (self, iterable) => self.offerAll(iterable)) + +/** @internal */ +export const poll = (self: Queue.Dequeue): Effect.Effect> => + core.map(self.takeUpTo(1), Chunk.head) + +/** @internal */ +export const take = (self: Queue.Dequeue): Effect.Effect => self.take + +/** @internal */ +export const takeAll = (self: Queue.Dequeue): Effect.Effect> => self.takeAll + +/** @internal */ +export const takeUpTo = dual< + (max: number) => (self: Queue.Dequeue) => Effect.Effect>, + (self: Queue.Dequeue, max: number) => Effect.Effect> +>(2, (self, max) => self.takeUpTo(max)) + +/** @internal */ +export const takeBetween = dual< + (min: number, max: number) => (self: Queue.Dequeue) => Effect.Effect>, + (self: Queue.Dequeue, min: number, max: number) => Effect.Effect> +>(3, (self, min, max) => self.takeBetween(min, max)) + +/** @internal */ +export const takeN = dual< + (n: number) => (self: Queue.Dequeue) => Effect.Effect>, + (self: Queue.Dequeue, n: number) => Effect.Effect> +>(2, (self, n) => self.takeBetween(n, n)) + +// ----------------------------------------------------------------------------- +// Strategy +// ----------------------------------------------------------------------------- + +/** @internal */ +export const backPressureStrategy = (): Queue.Strategy => new BackPressureStrategy() + +/** @internal */ +export const droppingStrategy = (): Queue.Strategy => new DroppingStrategy() + +/** @internal */ +export const slidingStrategy = (): Queue.Strategy => new SlidingStrategy() + +/** @internal */ +class BackPressureStrategy implements Queue.Strategy { + readonly [QueueStrategyTypeId] = queueStrategyVariance + + readonly putters = MutableQueue.unbounded, boolean]>() + + surplusSize(): number { + return MutableQueue.length(this.putters) + } + + onCompleteTakersWithEmptyQueue(takers: MutableQueue.MutableQueue>): void { + while (!MutableQueue.isEmpty(this.putters) && !MutableQueue.isEmpty(takers)) { + const taker = MutableQueue.poll(takers, void 0)! + const putter = MutableQueue.poll(this.putters, void 0)! + if (putter[2]) { + unsafeCompleteDeferred(putter[1], true) + } + unsafeCompleteDeferred(taker, putter[0]) + } + } + + get shutdown(): Effect.Effect { + return pipe( + core.fiberId, + core.flatMap((fiberId) => + pipe( + core.sync(() => unsafePollAll(this.putters)), + core.flatMap((putters) => + fiberRuntime.forEachConcurrentDiscard( + putters, + ([_, deferred, isLastItem]) => + isLastItem ? + pipe( + core.deferredInterruptWith(deferred, fiberId), + core.asVoid + ) : + core.void, + false, + false + ) + ) + ) + ) + ) + } + + handleSurplus( + iterable: Iterable, + queue: Queue.BackingQueue, + takers: MutableQueue.MutableQueue>, + isShutdown: MutableRef.MutableRef + ): Effect.Effect { + return core.withFiberRuntime((state) => { + const deferred = core.deferredUnsafeMake(state.id()) + return pipe( + core.suspend(() => { + this.unsafeOffer(iterable, deferred) + this.unsafeOnQueueEmptySpace(queue, takers) + unsafeCompleteTakers(this, queue, takers) + return MutableRef.get(isShutdown) ? core.interrupt : core.deferredAwait(deferred) + }), + core.onInterrupt(() => core.sync(() => this.unsafeRemove(deferred))) + ) + }) + } + + unsafeOnQueueEmptySpace( + queue: Queue.BackingQueue, + takers: MutableQueue.MutableQueue> + ): void { + let keepPolling = true + while (keepPolling && (queue.capacity() === Number.POSITIVE_INFINITY || queue.length() < queue.capacity())) { + const putter = pipe(this.putters, MutableQueue.poll(MutableQueue.EmptyMutableQueue)) + if (putter === MutableQueue.EmptyMutableQueue) { + keepPolling = false + } else { + const offered = queue.offer(putter[0]) + if (offered && putter[2]) { + unsafeCompleteDeferred(putter[1], true) + } else if (!offered) { + unsafeOfferAll(this.putters, pipe(unsafePollAll(this.putters), Chunk.prepend(putter))) + } + unsafeCompleteTakers(this, queue, takers) + } + } + } + + unsafeOffer(iterable: Iterable, deferred: Deferred.Deferred): void { + const stuff = Arr.fromIterable(iterable) + for (let i = 0; i < stuff.length; i++) { + const value = stuff[i] + if (i === stuff.length - 1) { + pipe(this.putters, MutableQueue.offer([value, deferred, true as boolean] as const)) + } else { + pipe(this.putters, MutableQueue.offer([value, deferred, false as boolean] as const)) + } + } + } + + unsafeRemove(deferred: Deferred.Deferred): void { + unsafeOfferAll( + this.putters, + pipe(unsafePollAll(this.putters), Chunk.filter(([, _]) => _ !== deferred)) + ) + } +} + +/** @internal */ +class DroppingStrategy implements Queue.Strategy { + readonly [QueueStrategyTypeId] = queueStrategyVariance + + surplusSize(): number { + return 0 + } + + get shutdown(): Effect.Effect { + return core.void + } + + onCompleteTakersWithEmptyQueue(): void { + } + + handleSurplus( + _iterable: Iterable, + _queue: Queue.BackingQueue, + _takers: MutableQueue.MutableQueue>, + _isShutdown: MutableRef.MutableRef + ): Effect.Effect { + return core.succeed(false) + } + + unsafeOnQueueEmptySpace( + _queue: Queue.BackingQueue, + _takers: MutableQueue.MutableQueue> + ): void { + // + } +} + +/** @internal */ +class SlidingStrategy implements Queue.Strategy { + readonly [QueueStrategyTypeId] = queueStrategyVariance + + surplusSize(): number { + return 0 + } + + get shutdown(): Effect.Effect { + return core.void + } + + onCompleteTakersWithEmptyQueue(): void { + } + + handleSurplus( + iterable: Iterable, + queue: Queue.BackingQueue, + takers: MutableQueue.MutableQueue>, + _isShutdown: MutableRef.MutableRef + ): Effect.Effect { + return core.sync(() => { + this.unsafeOffer(queue, iterable) + unsafeCompleteTakers(this, queue, takers) + return true + }) + } + + unsafeOnQueueEmptySpace( + _queue: Queue.BackingQueue, + _takers: MutableQueue.MutableQueue> + ): void { + // + } + + unsafeOffer(queue: Queue.BackingQueue, iterable: Iterable): void { + const iterator = iterable[Symbol.iterator]() + let next: IteratorResult + let offering = true + while (!(next = iterator.next()).done && offering) { + if (queue.capacity() === 0) { + return + } + // Poll 1 and retry + queue.poll(MutableQueue.EmptyMutableQueue) + offering = queue.offer(next.value) + } + } +} + +/** @internal */ +const unsafeCompleteDeferred = (deferred: Deferred.Deferred, a: A): void => { + return core.deferredUnsafeDone(deferred, core.succeed(a)) +} + +/** @internal */ +const unsafeOfferAll = (queue: MutableQueue.MutableQueue, as: Iterable): Chunk.Chunk => { + return pipe(queue, MutableQueue.offerAll(as)) +} + +/** @internal */ +const unsafePollAll = (queue: MutableQueue.MutableQueue): Chunk.Chunk => { + return pipe(queue, MutableQueue.pollUpTo(Number.POSITIVE_INFINITY)) +} + +/** @internal */ +const unsafePollN = (queue: MutableQueue.MutableQueue, max: number): Chunk.Chunk => { + return pipe(queue, MutableQueue.pollUpTo(max)) +} + +/** @internal */ +export const unsafeRemove = (queue: MutableQueue.MutableQueue, a: A): void => { + unsafeOfferAll( + queue, + pipe(unsafePollAll(queue), Chunk.filter((b) => a !== b)) + ) +} + +/** @internal */ +export const unsafeCompleteTakers = ( + strategy: Queue.Strategy, + queue: Queue.BackingQueue, + takers: MutableQueue.MutableQueue> +): void => { + // Check both a taker and an item are in the queue, starting with the taker + let keepPolling = true + while (keepPolling && queue.length() !== 0) { + const taker = pipe(takers, MutableQueue.poll(MutableQueue.EmptyMutableQueue)) + if (taker !== MutableQueue.EmptyMutableQueue) { + const element = queue.poll(MutableQueue.EmptyMutableQueue) + if (element !== MutableQueue.EmptyMutableQueue) { + unsafeCompleteDeferred(taker, element) + strategy.unsafeOnQueueEmptySpace(queue, takers) + } else { + unsafeOfferAll(takers, pipe(unsafePollAll(takers), Chunk.prepend(taker))) + } + keepPolling = true + } else { + keepPolling = false + } + } + if (keepPolling && queue.length() === 0 && !MutableQueue.isEmpty(takers)) { + strategy.onCompleteTakersWithEmptyQueue(takers) + } +} diff --git a/backend/node_modules/effect/src/internal/random.ts b/backend/node_modules/effect/src/internal/random.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd79e2a10aa002f379d4048890de85bb84ccb385 --- /dev/null +++ b/backend/node_modules/effect/src/internal/random.ts @@ -0,0 +1,161 @@ +import type * as Arr from "../Array.js" +import * as Chunk from "../Chunk.js" +import * as Context from "../Context.js" +import type * as Effect from "../Effect.js" +import { pipe } from "../Function.js" +import * as Hash from "../Hash.js" +import type * as Random from "../Random.js" +import * as PCGRandom from "../Utils.js" +import * as core from "./core.js" + +/** @internal */ +const RandomSymbolKey = "effect/Random" + +/** @internal */ +export const RandomTypeId: Random.RandomTypeId = Symbol.for( + RandomSymbolKey +) as Random.RandomTypeId + +/** @internal */ +export const randomTag: Context.Tag = Context.GenericTag("effect/Random") +/** @internal */ +class RandomImpl implements Random.Random { + readonly [RandomTypeId]: Random.RandomTypeId = RandomTypeId + + readonly PRNG: PCGRandom.PCGRandom + + constructor(readonly seed: number) { + this.PRNG = new PCGRandom.PCGRandom(seed) + } + + get next(): Effect.Effect { + return core.sync(() => this.PRNG.number()) + } + + get nextBoolean(): Effect.Effect { + return core.map(this.next, (n) => n > 0.5) + } + + get nextInt(): Effect.Effect { + return core.sync(() => this.PRNG.integer(Number.MAX_SAFE_INTEGER)) + } + + nextRange(min: number, max: number): Effect.Effect { + return core.map(this.next, (n) => (max - min) * n + min) + } + + nextIntBetween(min: number, max: number): Effect.Effect { + return core.sync(() => this.PRNG.integer(max - min) + min) + } + + shuffle(elements: Iterable): Effect.Effect> { + return shuffleWith(elements, (n) => this.nextIntBetween(0, n)) + } +} + +const shuffleWith = ( + elements: Iterable, + nextIntBounded: (n: number) => Effect.Effect +): Effect.Effect> => { + return core.suspend(() => + pipe( + core.sync(() => Array.from(elements)), + core.flatMap((buffer) => { + const numbers: Array = [] + for (let i = buffer.length; i >= 2; i = i - 1) { + numbers.push(i) + } + return pipe( + numbers, + core.forEachSequentialDiscard((n) => + pipe( + nextIntBounded(n), + core.map((k) => swap(buffer, n - 1, k)) + ) + ), + core.as(Chunk.fromIterable(buffer)) + ) + }) + ) + ) +} + +const swap = (buffer: Array, index1: number, index2: number): Array => { + const tmp = buffer[index1]! + buffer[index1] = buffer[index2]! + buffer[index2] = tmp + return buffer +} + +export const make = (seed: A): Random.Random => new RandomImpl(Hash.hash(seed)) + +/** @internal */ +class FixedRandomImpl implements Random.Random { + readonly [RandomTypeId]: Random.RandomTypeId = RandomTypeId + + private index = 0 + + constructor(readonly values: Arr.NonEmptyArray) { + if (values.length === 0) { + throw new Error("Requires at least one value") + } + } + + private getNextValue(): any { + const value = this.values[this.index] + this.index = (this.index + 1) % this.values.length + return value + } + + get next(): Effect.Effect { + return core.sync(() => { + const value = this.getNextValue() + if (typeof value === "number") { + return Math.max(0, Math.min(1, value)) + } + return Hash.hash(value) / 2147483647 + }) + } + + get nextBoolean(): Effect.Effect { + return core.sync(() => { + const value = this.getNextValue() + if (typeof value === "boolean") { + return value + } + return Hash.hash(value) % 2 === 0 + }) + } + + get nextInt(): Effect.Effect { + return core.sync(() => { + const value = this.getNextValue() + if (typeof value === "number" && Number.isFinite(value)) { + return Math.round(value) + } + return Math.abs(Hash.hash(value)) + }) + } + + nextRange(min: number, max: number): Effect.Effect { + return core.map(this.next, (n) => (max - min) * n + min) + } + + nextIntBetween(min: number, max: number): Effect.Effect { + return core.sync(() => { + const value = this.getNextValue() + if (typeof value === "number" && Number.isFinite(value)) { + return Math.max(min, Math.min(max - 1, Math.round(value))) + } + const hash = Math.abs(Hash.hash(value)) + return min + (hash % (max - min)) + }) + } + + shuffle(elements: Iterable): Effect.Effect> { + return shuffleWith(elements, (n) => this.nextIntBetween(0, n)) + } +} + +/** @internal */ +export const fixed = >(values: T): Random.Random => new FixedRandomImpl(values) diff --git a/backend/node_modules/effect/src/internal/rateLimiter.ts b/backend/node_modules/effect/src/internal/rateLimiter.ts new file mode 100644 index 0000000000000000000000000000000000000000..120737033d8b3f16e29db9c760f248e156aa12fd --- /dev/null +++ b/backend/node_modules/effect/src/internal/rateLimiter.ts @@ -0,0 +1,93 @@ +import type { DurationInput } from "../Duration.js" +import * as Duration from "../Duration.js" +import * as Effect from "../Effect.js" +import * as FiberRef from "../FiberRef.js" +import { pipe } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import type * as RateLimiter from "../RateLimiter.js" +import type * as Scope from "../Scope.js" + +/** @internal */ +export const make = ({ + algorithm = "token-bucket", + interval, + limit +}: RateLimiter.RateLimiter.Options): Effect.Effect< + RateLimiter.RateLimiter, + never, + Scope.Scope +> => { + switch (algorithm) { + case "fixed-window": { + return fixedWindow(limit, interval) + } + case "token-bucket": { + return tokenBucket(limit, interval) + } + } +} + +const tokenBucket = (limit: number, window: DurationInput): Effect.Effect< + RateLimiter.RateLimiter, + never, + Scope.Scope +> => + Effect.gen(function*() { + const millisPerToken = Math.ceil(Duration.toMillis(window) / limit) + const semaphore = yield* Effect.makeSemaphore(limit) + const latch = yield* Effect.makeSemaphore(0) + const refill: Effect.Effect = Effect.sleep(millisPerToken).pipe( + Effect.zipRight(latch.releaseAll), + Effect.zipRight(semaphore.release(1)), + Effect.flatMap((free) => free === limit ? Effect.void : refill) + ) + yield* pipe( + latch.take(1), + Effect.zipRight(refill), + Effect.forever, + Effect.forkScoped, + Effect.interruptible + ) + const take = Effect.uninterruptibleMask((restore) => + Effect.flatMap( + FiberRef.get(currentCost), + (cost) => Effect.zipRight(restore(semaphore.take(cost)), latch.release(1)) + ) + ) + return (effect) => Effect.zipRight(take, effect) + }) + +const fixedWindow = (limit: number, window: DurationInput): Effect.Effect< + RateLimiter.RateLimiter, + never, + Scope.Scope +> => + Effect.gen(function*() { + const semaphore = yield* Effect.makeSemaphore(limit) + const latch = yield* Effect.makeSemaphore(0) + yield* pipe( + latch.take(1), + Effect.zipRight(Effect.sleep(window)), + Effect.zipRight(latch.releaseAll), + Effect.zipRight(semaphore.releaseAll), + Effect.forever, + Effect.forkScoped, + Effect.interruptible + ) + const take = Effect.uninterruptibleMask((restore) => + Effect.flatMap( + FiberRef.get(currentCost), + (cost) => Effect.zipRight(restore(semaphore.take(cost)), latch.release(1)) + ) + ) + return (effect) => Effect.zipRight(take, effect) + }) + +/** @internal */ +const currentCost = globalValue( + Symbol.for("effect/RateLimiter/currentCost"), + () => FiberRef.unsafeMake(1) +) + +/** @internal */ +export const withCost = (cost: number) => Effect.locally(currentCost, cost) diff --git a/backend/node_modules/effect/src/internal/rcMap.ts b/backend/node_modules/effect/src/internal/rcMap.ts new file mode 100644 index 0000000000000000000000000000000000000000..b5980220b3a325581474d3408029dedd302c9728 --- /dev/null +++ b/backend/node_modules/effect/src/internal/rcMap.ts @@ -0,0 +1,295 @@ +import type * as Cause from "../Cause.js" +import * as Context from "../Context.js" +import type * as Deferred from "../Deferred.js" +import * as Duration from "../Duration.js" +import type { Effect } from "../Effect.js" +import type { RuntimeFiber } from "../Fiber.js" +import { constant, dual, flow, identity } from "../Function.js" +import * as MutableHashMap from "../MutableHashMap.js" +import { pipeArguments } from "../Pipeable.js" +import type * as RcMap from "../RcMap.js" +import type * as Scope from "../Scope.js" +import * as coreEffect from "./core-effect.js" +import * as core from "./core.js" +import * as circular from "./effect/circular.js" +import * as fiberRuntime from "./fiberRuntime.js" + +/** @internal */ +export const TypeId: RcMap.TypeId = Symbol.for("effect/RcMap") as RcMap.TypeId + +type State = State.Open | State.Closed + +declare namespace State { + interface Open { + readonly _tag: "Open" + readonly map: MutableHashMap.MutableHashMap> + } + + interface Closed { + readonly _tag: "Closed" + } + + interface Entry { + readonly deferred: Deferred.Deferred + readonly scope: Scope.CloseableScope + readonly finalizer: Effect + readonly idleTimeToLive: Duration.Duration + fiber: RuntimeFiber | undefined + expiresAt: number + refCount: number + } +} + +const variance: RcMap.RcMap.Variance = { + _K: identity, + _A: identity, + _E: identity +} + +class RcMapImpl implements RcMap.RcMap { + readonly [TypeId]: RcMap.RcMap.Variance + + state: State = { + _tag: "Open", + map: MutableHashMap.empty() + } + readonly semaphore = circular.unsafeMakeSemaphore(1) + + constructor( + readonly lookup: (key: K) => Effect, + readonly context: Context.Context, + readonly scope: Scope.Scope, + readonly idleTimeToLive: ((key: K) => Duration.Duration) | undefined, + readonly capacity: number + ) { + this[TypeId] = variance + } + + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const make: { + (options: { + readonly lookup: (key: K) => Effect + readonly idleTimeToLive?: Duration.DurationInput | ((key: K) => Duration.DurationInput) | undefined + readonly capacity?: undefined + }): Effect, never, Scope.Scope | R> + (options: { + readonly lookup: (key: K) => Effect + readonly idleTimeToLive?: Duration.DurationInput | ((key: K) => Duration.DurationInput) | undefined + readonly capacity: number + }): Effect, never, Scope.Scope | R> +} = (options: { + readonly lookup: (key: K) => Effect + readonly idleTimeToLive?: Duration.DurationInput | ((key: K) => Duration.DurationInput) | undefined + readonly capacity?: number | undefined +}) => + core.withFiberRuntime, never, R | Scope.Scope>((fiber) => { + const context = fiber.getFiberRef(core.currentContext) as Context.Context + const scope = Context.get(context, fiberRuntime.scopeTag) + const idleTimeToLive = options.idleTimeToLive === undefined + ? undefined + : typeof options.idleTimeToLive === "function" + ? flow(options.idleTimeToLive, Duration.decode) + : constant(Duration.decode(options.idleTimeToLive)) + const self = new RcMapImpl( + options.lookup as any, + context, + scope, + idleTimeToLive, + Math.max(options.capacity ?? Number.POSITIVE_INFINITY, 0) + ) + return core.as( + scope.addFinalizer(() => + core.suspend(() => { + if (self.state._tag === "Closed") { + return core.void + } + const map = self.state.map + self.state = { _tag: "Closed" } + return core.forEachSequentialDiscard( + map, + ([, entry]) => core.scopeClose(entry.scope, core.exitVoid) + ).pipe( + core.tap(() => { + MutableHashMap.clear(map) + }), + self.semaphore.withPermits(1) + ) + }) + ), + self + ) + }) + +/** @internal */ +export const get: { + (key: K): (self: RcMap.RcMap) => Effect + (self: RcMap.RcMap, key: K): Effect +} = dual(2, (self_: RcMap.RcMap, key: K): Effect => { + const self = self_ as RcMapImpl + return core.uninterruptibleMask((restore) => getImpl(self, key, restore as any)) +}) + +const getImpl = core.fnUntraced(function*(self: RcMapImpl, key: K, restore: (a: A) => A) { + if (self.state._tag === "Closed") { + return yield* core.interrupt + } + const state = self.state + const o = MutableHashMap.get(state.map, key) + let entry: State.Entry + if (o._tag === "Some") { + entry = o.value + entry.refCount++ + } else if (Number.isFinite(self.capacity) && MutableHashMap.size(self.state.map) >= self.capacity) { + return yield* core.fail( + new core.ExceededCapacityException(`RcMap attempted to exceed capacity of ${self.capacity}`) + ) as Effect + } else { + entry = yield* self.semaphore.withPermits(1)(acquire(self, key, restore)) + } + const scope = yield* fiberRuntime.scopeTag + yield* scope.addFinalizer(() => entry.finalizer) + return yield* restore(core.deferredAwait(entry.deferred)) +}) + +const acquire = core.fnUntraced(function*(self: RcMapImpl, key: K, restore: (a: A) => A) { + const scope = yield* fiberRuntime.scopeMake() + const deferred = yield* core.deferredMake() + const acquire = self.lookup(key) + const contextMap = new Map(self.context.unsafeMap) + yield* restore(core.mapInputContext( + acquire as Effect, + (inputContext: Context.Context) => { + inputContext.unsafeMap.forEach((value, key) => { + contextMap.set(key, value) + }) + contextMap.set(fiberRuntime.scopeTag.key, scope) + return Context.unsafeMake(contextMap) + } + )).pipe( + core.exit, + core.flatMap((exit) => core.deferredDone(deferred, exit)), + circular.forkIn(scope) + ) + const idleTimeToLive = self.idleTimeToLive ? self.idleTimeToLive(key) : Duration.zero + const entry: State.Entry = { + deferred, + scope, + finalizer: undefined as any, + idleTimeToLive, + fiber: undefined, + expiresAt: 0, + refCount: 1 + } + ;(entry as any).finalizer = release(self, key, entry) + if (self.state._tag === "Open") { + MutableHashMap.set(self.state.map, key, entry) + } + return entry +}) + +const release = (self: RcMapImpl, key: K, entry: State.Entry) => + coreEffect.clockWith((clock) => { + entry.refCount-- + if (entry.refCount > 0) { + return core.void + } else if ( + self.state._tag === "Closed" + || !MutableHashMap.has(self.state.map, key) + || Duration.isZero(entry.idleTimeToLive) + ) { + if (self.state._tag === "Open") { + MutableHashMap.remove(self.state.map, key) + } + return core.scopeClose(entry.scope, core.exitVoid) + } + + if (!Duration.isFinite(entry.idleTimeToLive)) { + return core.void + } + + entry.expiresAt = clock.unsafeCurrentTimeMillis() + Duration.toMillis(entry.idleTimeToLive) + if (entry.fiber) return core.void + + return core.interruptibleMask(function loop(restore): Effect { + const now = clock.unsafeCurrentTimeMillis() + const remaining = entry.expiresAt - now + if (remaining <= 0) { + if (self.state._tag === "Closed" || entry.refCount > 0) return core.void + MutableHashMap.remove(self.state.map, key) + return restore(core.scopeClose(entry.scope, core.exitVoid)) + } + return core.flatMap(clock.sleep(Duration.millis(remaining)), () => loop(restore)) + }).pipe( + fiberRuntime.ensuring(core.sync(() => { + entry.fiber = undefined + })), + circular.forkIn(self.scope), + core.tap((fiber) => { + entry.fiber = fiber + }), + self.semaphore.withPermits(1) + ) + }) + +/** @internal */ +export const keys = (self: RcMap.RcMap): Effect> => { + const impl = self as RcMapImpl + return core.suspend(() => + impl.state._tag === "Closed" ? core.interrupt : core.succeed(MutableHashMap.keys(impl.state.map)) + ) +} + +/** @internal */ +export const invalidate: { + (key: K): (self: RcMap.RcMap) => Effect + (self: RcMap.RcMap, key: K): Effect +} = dual( + 2, + core.fnUntraced(function*(self_: RcMap.RcMap, key: K) { + const self = self_ as RcMapImpl + if (self.state._tag === "Closed") return + const o = MutableHashMap.get(self.state.map, key) + if (o._tag === "None") return + const entry = o.value + MutableHashMap.remove(self.state.map, key) + if (entry.refCount > 0) return + yield* core.scopeClose(entry.scope, core.exitVoid) + if (entry.fiber) yield* core.interruptFiber(entry.fiber) + }) +) + +/** @internal */ +export const has: { + (key: K): (self: RcMap.RcMap) => Effect + (self: RcMap.RcMap, key: K): Effect +} = dual(2, (self_: RcMap.RcMap, key: K): Effect => { + const self = self_ as RcMapImpl + return core.sync(() => { + if (self.state._tag === "Closed") return false + return MutableHashMap.has(self.state.map, key) + }) +}) + +/** @internal */ +export const touch: { + (key: K): (self: RcMap.RcMap) => Effect + (self: RcMap.RcMap, key: K): Effect +} = dual( + 2, + (self_: RcMap.RcMap, key: K) => + coreEffect.clockWith((clock) => { + const self = self_ as RcMapImpl + if (self.state._tag === "Closed") return core.void + const o = MutableHashMap.get(self.state.map, key) + if (o._tag === "None") return core.void + const entry = o.value + if (Duration.isZero(entry.idleTimeToLive)) return core.void + entry.expiresAt = clock.unsafeCurrentTimeMillis() + Duration.toMillis(entry.idleTimeToLive) + return core.void + }) +) diff --git a/backend/node_modules/effect/src/internal/rcRef.ts b/backend/node_modules/effect/src/internal/rcRef.ts new file mode 100644 index 0000000000000000000000000000000000000000..8ff2ca4de40798e6735fbb9820c17cdd68fb6093 --- /dev/null +++ b/backend/node_modules/effect/src/internal/rcRef.ts @@ -0,0 +1,192 @@ +import * as Context from "../Context.js" +import * as Duration from "../Duration.js" +import type { Effect } from "../Effect.js" +import * as Effectable from "../Effectable.js" +import type { RuntimeFiber } from "../Fiber.js" +import { identity } from "../Function.js" +import type * as RcRef from "../RcRef.js" +import * as Readable from "../Readable.js" +import type * as Scope from "../Scope.js" +import * as coreEffect from "./core-effect.js" +import * as core from "./core.js" +import * as circular from "./effect/circular.js" +import * as fiberRuntime from "./fiberRuntime.js" + +/** @internal */ +export const TypeId: RcRef.TypeId = Symbol.for("effect/RcRef") as RcRef.TypeId + +type State = State.Empty | State.Acquired | State.Closed + +declare namespace State { + interface Empty { + readonly _tag: "Empty" + } + + interface Acquired { + readonly _tag: "Acquired" + readonly value: A + readonly scope: Scope.CloseableScope + fiber: RuntimeFiber | undefined + refCount: number + } + + interface Closed { + readonly _tag: "Closed" + } +} + +const stateEmpty: State = { _tag: "Empty" } +const stateClosed: State = { _tag: "Closed" } + +const variance: RcRef.RcRef.Variance = { + _A: identity, + _E: identity +} + +class RcRefImpl extends Effectable.Class implements RcRef.RcRef { + readonly [TypeId]: RcRef.RcRef.Variance = variance + readonly [Readable.TypeId]: Readable.TypeId = Readable.TypeId + + state: State = stateEmpty + readonly semaphore = circular.unsafeMakeSemaphore(1) + + constructor( + readonly acquire: Effect, + readonly context: Context.Context, + readonly scope: Scope.Scope, + readonly idleTimeToLive: Duration.Duration | undefined + ) { + super() + this.get = get(this) + } + readonly get: Effect + + commit() { + return this.get + } +} + +/** @internal */ +export const make = (options: { + readonly acquire: Effect + readonly idleTimeToLive?: Duration.DurationInput | undefined +}) => + core.withFiberRuntime, never, R | Scope.Scope>((fiber) => { + const context = fiber.getFiberRef(core.currentContext) as Context.Context + const scope = Context.get(context, fiberRuntime.scopeTag) + const ref = new RcRefImpl( + options.acquire as Effect, + context, + scope, + options.idleTimeToLive ? Duration.decode(options.idleTimeToLive) : undefined + ) + return core.as( + scope.addFinalizer(() => + ref.semaphore.withPermits(1)(core.suspend(() => { + const close = ref.state._tag === "Acquired" + ? core.scopeClose(ref.state.scope, core.exitVoid) + : core.void + ref.state = stateClosed + return close + })) + ), + ref + ) + }) + +/** @internal */ +export const get = ( + self_: RcRef.RcRef +): Effect => { + const self = self_ as RcRefImpl + const isInfinite = self.idleTimeToLive && !Duration.isFinite(self.idleTimeToLive) + return core.uninterruptibleMask((restore) => + core.suspend(() => { + switch (self.state._tag) { + case "Closed": { + return core.interrupt + } + case "Acquired": { + self.state.refCount++ + return self.state.fiber + ? core.as(core.interruptFiber(self.state.fiber), self.state) + : core.succeed(self.state) + } + case "Empty": { + return fiberRuntime.scopeMake().pipe( + coreEffect.bindTo("scope"), + coreEffect.bind("value", ({ scope }) => + restore(core.fiberRefLocally( + self.acquire as Effect, + core.currentContext, + Context.add(self.context, fiberRuntime.scopeTag, scope) + ))), + core.map(({ scope, value }) => { + const state: State.Acquired = { + _tag: "Acquired", + value, + scope, + fiber: undefined, + refCount: 1 + } + self.state = state + return state + }) + ) + } + } + }) + ).pipe( + self.semaphore.withPermits(1), + coreEffect.bindTo("state"), + coreEffect.bind("scope", () => fiberRuntime.scopeTag), + core.tap(({ scope, state }) => + scope.addFinalizer(() => + core.suspend(() => { + state.refCount-- + if (state.refCount > 0 || isInfinite) { + return core.void + } + if (self.idleTimeToLive === undefined) { + self.state = stateEmpty + return core.scopeClose(state.scope, core.exitVoid) + } + return coreEffect.sleep(self.idleTimeToLive).pipe( + core.interruptible, + core.zipRight(core.suspend(() => { + if (self.state._tag === "Acquired" && self.state.refCount === 0) { + self.state = stateEmpty + return core.scopeClose(state.scope, core.exitVoid) + } + return core.void + })), + fiberRuntime.ensuring(core.sync(() => { + state.fiber = undefined + })), + circular.forkIn(self.scope), + core.tap((fiber) => { + state.fiber = fiber + }), + self.semaphore.withPermits(1) + ) + }) + ) + ), + core.map(({ state }) => state.value) + ) +} + +/** @internal */ +export const invalidate = ( + self_: RcRef.RcRef +): Effect => { + const self = self_ as RcRefImpl + return core.uninterruptible(core.suspend(() => { + if (self.state._tag !== "Acquired") { + return core.void + } + const state = self.state + self.state = stateEmpty + return state.scope.close(core.exitVoid) + })) +} diff --git a/backend/node_modules/effect/src/internal/redBlackTree.ts b/backend/node_modules/effect/src/internal/redBlackTree.ts new file mode 100644 index 0000000000000000000000000000000000000000..9076ff9d3b66febb3ed32f90bcfc4beee64132c2 --- /dev/null +++ b/backend/node_modules/effect/src/internal/redBlackTree.ts @@ -0,0 +1,1245 @@ +import * as Chunk from "../Chunk.js" +import * as Equal from "../Equal.js" +import { dual, pipe } from "../Function.js" +import * as Hash from "../Hash.js" +import { format, NodeInspectSymbol, toJSON } from "../Inspectable.js" +import * as Option from "../Option.js" +import type * as Order from "../Order.js" +import type * as Ordering from "../Ordering.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty } from "../Predicate.js" +import type * as RBT from "../RedBlackTree.js" +import { Direction, RedBlackTreeIterator } from "./redBlackTree/iterator.js" +import * as Node from "./redBlackTree/node.js" +import * as Stack from "./stack.js" + +const RedBlackTreeSymbolKey = "effect/RedBlackTree" + +/** @internal */ +export const RedBlackTreeTypeId: RBT.TypeId = Symbol.for(RedBlackTreeSymbolKey) as RBT.TypeId + +/** @internal */ +export interface RedBlackTreeImpl extends RBT.RedBlackTree { + readonly _ord: Order.Order + readonly _root: Node.Node | undefined +} + +const redBlackTreeVariance = { + /* c8 ignore next */ + _Key: (_: any) => _, + /* c8 ignore next */ + _Value: (_: never) => _ +} + +const RedBlackTreeProto: RBT.RedBlackTree = { + [RedBlackTreeTypeId]: redBlackTreeVariance, + [Hash.symbol](this: RBT.RedBlackTree): number { + let hash = Hash.hash(RedBlackTreeSymbolKey) + for (const item of this) { + hash ^= pipe(Hash.hash(item[0]), Hash.combine(Hash.hash(item[1]))) + } + return Hash.cached(this, hash) + }, + [Equal.symbol](this: RedBlackTreeImpl, that: unknown): boolean { + if (isRedBlackTree(that)) { + if ((this._root?.count ?? 0) !== ((that as RedBlackTreeImpl)._root?.count ?? 0)) { + return false + } + const entries = Array.from(that) + return Array.from(this).every((itemSelf, i) => { + const itemThat = entries[i] + return Equal.equals(itemSelf[0], itemThat[0]) && Equal.equals(itemSelf[1], itemThat[1]) + }) + } + return false + }, + [Symbol.iterator](this: RedBlackTreeImpl): RedBlackTreeIterator { + const stack: Array> = [] + let n = this._root + while (n != null) { + stack.push(n) + n = n.left + } + return new RedBlackTreeIterator(this, stack, Direction.Forward) + }, + toString() { + return format(this.toJSON()) + }, + toJSON() { + return { + _id: "RedBlackTree", + values: Array.from(this).map(toJSON) + } + }, + [NodeInspectSymbol]() { + return this.toJSON() + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +const makeImpl = (ord: Order.Order, root: Node.Node | undefined): RedBlackTreeImpl => { + const tree = Object.create(RedBlackTreeProto) + tree._ord = ord + tree._root = root + return tree +} + +/** @internal */ +export const isRedBlackTree: { + (u: Iterable): u is RBT.RedBlackTree + (u: unknown): u is RBT.RedBlackTree +} = (u: unknown): u is RBT.RedBlackTree => hasProperty(u, RedBlackTreeTypeId) + +/** @internal */ +export const empty = (ord: Order.Order): RBT.RedBlackTree => makeImpl(ord, undefined) + +/** @internal */ +export const fromIterable = dual< + (ord: Order.Order) => (entries: Iterable) => RBT.RedBlackTree, + (entries: Iterable, ord: Order.Order) => RBT.RedBlackTree +>(2, (entries: Iterable, ord: Order.Order) => { + let tree = empty(ord) + for (const [key, value] of entries) { + tree = insert(tree, key, value) + } + return tree +}) + +/** @internal */ +export const make = + (ord: Order.Order) => + >(...entries: Entries): RBT.RedBlackTree< + K, + Entries[number] extends readonly [any, infer V] ? V : never + > => { + return fromIterable(entries, ord) + } + +/** @internal */ +export const atBackwards = dual< + (index: number) => (self: RBT.RedBlackTree) => Iterable<[K, V]>, + (self: RBT.RedBlackTree, index: number) => Iterable<[K, V]> +>(2, (self, index) => at(self, index, Direction.Backward)) + +/** @internal */ +export const atForwards = dual< + (index: number) => (self: RBT.RedBlackTree) => Iterable<[K, V]>, + (self: RBT.RedBlackTree, index: number) => Iterable<[K, V]> +>(2, (self, index) => at(self, index, Direction.Forward)) + +const at = ( + self: RBT.RedBlackTree, + index: number, + direction: RBT.RedBlackTree.Direction +): Iterable<[K, V]> => { + return { + [Symbol.iterator]: () => { + if (index < 0) { + return new RedBlackTreeIterator(self, [], direction) + } + let node = (self as RedBlackTreeImpl)._root + const stack: Array> = [] + while (node !== undefined) { + stack.push(node) + if (node.left !== undefined) { + if (index < node.left.count) { + node = node.left + continue + } + index -= node.left.count + } + if (!index) { + return new RedBlackTreeIterator(self, stack, direction) + } + index -= 1 + if (node.right !== undefined) { + if (index >= node.right.count) { + break + } + node = node.right + } else { + break + } + } + return new RedBlackTreeIterator(self, [], direction) + } + } +} + +/** @internal */ +export const findAll = dual< + (key: K) => (self: RBT.RedBlackTree) => Chunk.Chunk, + (self: RBT.RedBlackTree, key: K) => Chunk.Chunk +>(2, (self: RBT.RedBlackTree, key: K) => { + const stack: Array> = [] + let node = (self as RedBlackTreeImpl)._root + let result = Chunk.empty() + while (node !== undefined || stack.length > 0) { + if (node) { + stack.push(node) + node = node.left + } else { + const current = stack.pop()! + if (Equal.equals(key, current.key)) { + result = Chunk.prepend(current.value)(result) + } + node = current.right + } + } + return result +}) + +/** @internal */ +export const findFirst = dual< + (key: K) => (self: RBT.RedBlackTree) => Option.Option, + (self: RBT.RedBlackTree, key: K) => Option.Option +>(2, (self: RBT.RedBlackTree, key: K) => { + const cmp = (self as RedBlackTreeImpl)._ord + let node = (self as RedBlackTreeImpl)._root + while (node !== undefined) { + const d = cmp(key, node.key) + if (Equal.equals(key, node.key)) { + return Option.some(node.value) + } + if (d <= 0) { + node = node.left + } else { + node = node.right + } + } + return Option.none() +}) + +/** @internal */ +export const first = (self: RBT.RedBlackTree): Option.Option<[K, V]> => { + let node: Node.Node | undefined = (self as RedBlackTreeImpl)._root + let current: Node.Node | undefined = (self as RedBlackTreeImpl)._root + while (node !== undefined) { + current = node + node = node.left + } + return current ? Option.some([current.key, current.value]) : Option.none() +} + +/** @internal */ +export const getAt = dual< + (index: number) => (self: RBT.RedBlackTree) => Option.Option<[K, V]>, + (self: RBT.RedBlackTree, index: number) => Option.Option<[K, V]> +>(2, (self: RBT.RedBlackTree, index: number) => { + if (index < 0) { + return Option.none() + } + let root = (self as RedBlackTreeImpl)._root + let node: Node.Node | undefined = undefined + while (root !== undefined) { + node = root + if (root.left) { + if (index < root.left.count) { + root = root.left + continue + } + index -= root.left.count + } + if (!index) { + return Option.some([node.key, node.value]) + } + index -= 1 + if (root.right) { + if (index >= root.right.count) { + break + } + root = root.right + } else { + break + } + } + return Option.none() +}) + +/** @internal */ +export const getOrder = (tree: RBT.RedBlackTree): Order.Order => (tree as RedBlackTreeImpl)._ord + +/** @internal */ +export const has = dual< + (key: K) => (self: RBT.RedBlackTree) => boolean, + (self: RBT.RedBlackTree, key: K) => boolean +>(2, (self, key) => Option.isSome(findFirst(self, key))) + +/** @internal */ +export const insert = dual< + (key: K, value: V) => (self: RBT.RedBlackTree) => RBT.RedBlackTree, + (self: RBT.RedBlackTree, key: K, value: V) => RBT.RedBlackTree +>(3, (self: RBT.RedBlackTree, key: K, value: V) => { + const cmp = (self as RedBlackTreeImpl)._ord + // Find point to insert new node at + let n: Node.Node | undefined = (self as RedBlackTreeImpl)._root + const n_stack: Array> = [] + const d_stack: Array = [] + while (n != null) { + const d = cmp(key, n.key) + n_stack.push(n) + d_stack.push(d) + if (d <= 0) { + n = n.left + } else { + n = n.right + } + } + // Rebuild path to leaf node + n_stack.push({ + color: Node.Color.Red, + key, + value, + left: undefined, + right: undefined, + count: 1 + }) + for (let s = n_stack.length - 2; s >= 0; --s) { + const n2 = n_stack[s]! + if (d_stack[s]! <= 0) { + n_stack[s] = { + color: n2.color, + key: n2.key, + value: n2.value, + left: n_stack[s + 1], + right: n2.right, + count: n2.count + 1 + } + } else { + n_stack[s] = { + color: n2.color, + key: n2.key, + value: n2.value, + left: n2.left, + right: n_stack[s + 1], + count: n2.count + 1 + } + } + } + // Rebalance tree using rotations + for (let s = n_stack.length - 1; s > 1; --s) { + const p = n_stack[s - 1]! + const n3 = n_stack[s]! + if (p.color === Node.Color.Black || n3.color === Node.Color.Black) { + break + } + const pp = n_stack[s - 2]! + if (pp.left === p) { + if (p.left === n3) { + const y = pp.right + if (y && y.color === Node.Color.Red) { + p.color = Node.Color.Black + pp.right = Node.repaint(y, Node.Color.Black) + pp.color = Node.Color.Red + s -= 1 + } else { + pp.color = Node.Color.Red + pp.left = p.right + p.color = Node.Color.Black + p.right = pp + n_stack[s - 2] = p + n_stack[s - 1] = n3 + Node.recount(pp) + Node.recount(p) + if (s >= 3) { + const ppp = n_stack[s - 3]! + if (ppp.left === pp) { + ppp.left = p + } else { + ppp.right = p + } + } + break + } + } else { + const y = pp.right + if (y && y.color === Node.Color.Red) { + p.color = Node.Color.Black + pp.right = Node.repaint(y, Node.Color.Black) + pp.color = Node.Color.Red + s -= 1 + } else { + p.right = n3.left + pp.color = Node.Color.Red + pp.left = n3.right + n3.color = Node.Color.Black + n3.left = p + n3.right = pp + n_stack[s - 2] = n3 + n_stack[s - 1] = p + Node.recount(pp) + Node.recount(p) + Node.recount(n3) + if (s >= 3) { + const ppp = n_stack[s - 3]! + if (ppp.left === pp) { + ppp.left = n3 + } else { + ppp.right = n3 + } + } + break + } + } + } else { + if (p.right === n3) { + const y = pp.left + if (y && y.color === Node.Color.Red) { + p.color = Node.Color.Black + pp.left = Node.repaint(y, Node.Color.Black) + pp.color = Node.Color.Red + s -= 1 + } else { + pp.color = Node.Color.Red + pp.right = p.left + p.color = Node.Color.Black + p.left = pp + n_stack[s - 2] = p + n_stack[s - 1] = n3 + Node.recount(pp) + Node.recount(p) + if (s >= 3) { + const ppp = n_stack[s - 3]! + if (ppp.right === pp) { + ppp.right = p + } else { + ppp.left = p + } + } + break + } + } else { + const y = pp.left + if (y && y.color === Node.Color.Red) { + p.color = Node.Color.Black + pp.left = Node.repaint(y, Node.Color.Black) + pp.color = Node.Color.Red + s -= 1 + } else { + p.left = n3.right + pp.color = Node.Color.Red + pp.right = n3.left + n3.color = Node.Color.Black + n3.right = p + n3.left = pp + n_stack[s - 2] = n3 + n_stack[s - 1] = p + Node.recount(pp) + Node.recount(p) + Node.recount(n3) + if (s >= 3) { + const ppp = n_stack[s - 3]! + if (ppp.right === pp) { + ppp.right = n3 + } else { + ppp.left = n3 + } + } + break + } + } + } + } + // Return new tree + n_stack[0]!.color = Node.Color.Black + return makeImpl((self as RedBlackTreeImpl)._ord, n_stack[0]) +}) + +/** @internal */ +export const keysForward = (self: RBT.RedBlackTree): IterableIterator => keys(self, Direction.Forward) + +/** @internal */ +export const keysBackward = (self: RBT.RedBlackTree): IterableIterator => keys(self, Direction.Backward) + +const keys = ( + self: RBT.RedBlackTree, + direction: RBT.RedBlackTree.Direction +): IterableIterator => { + const begin: RedBlackTreeIterator = self[Symbol.iterator]() as RedBlackTreeIterator + let count = 0 + return { + [Symbol.iterator]: () => keys(self, direction), + next: (): IteratorResult => { + count++ + const entry = begin.key + if (direction === Direction.Forward) { + begin.moveNext() + } else { + begin.movePrev() + } + switch (entry._tag) { + case "None": { + return { done: true, value: count } + } + case "Some": { + return { done: false, value: entry.value } + } + } + } + } +} + +/** @internal */ +export const last = (self: RBT.RedBlackTree): Option.Option<[K, V]> => { + let node: Node.Node | undefined = (self as RedBlackTreeImpl)._root + let current: Node.Node | undefined = (self as RedBlackTreeImpl)._root + while (node !== undefined) { + current = node + node = node.right + } + return current ? Option.some([current.key, current.value]) : Option.none() +} + +/** @internal */ +export const reversed = (self: RBT.RedBlackTree): Iterable<[K, V]> => { + return { + [Symbol.iterator]: () => { + const stack: Array> = [] + let node = (self as RedBlackTreeImpl)._root + while (node !== undefined) { + stack.push(node) + node = node.right + } + return new RedBlackTreeIterator(self, stack, Direction.Backward) + } + } +} + +/** @internal */ +export const greaterThanBackwards = dual< + (key: K) => (self: RBT.RedBlackTree) => Iterable<[K, V]>, + (self: RBT.RedBlackTree, key: K) => Iterable<[K, V]> +>(2, (self, key) => greaterThan(self, key, Direction.Backward)) + +/** @internal */ +export const greaterThanForwards = dual< + (key: K) => (self: RBT.RedBlackTree) => Iterable<[K, V]>, + (self: RBT.RedBlackTree, key: K) => Iterable<[K, V]> +>(2, (self, key) => greaterThan(self, key, Direction.Forward)) + +const greaterThan = ( + self: RBT.RedBlackTree, + key: K, + direction: RBT.RedBlackTree.Direction +): Iterable<[K, V]> => { + return { + [Symbol.iterator]: () => { + const cmp = (self as RedBlackTreeImpl)._ord + let node = (self as RedBlackTreeImpl)._root + const stack = [] + let last_ptr = 0 + while (node !== undefined) { + const d = cmp(key, node.key) + stack.push(node) + if (d < 0) { + last_ptr = stack.length + } + if (d < 0) { + node = node.left + } else { + node = node.right + } + } + stack.length = last_ptr + return new RedBlackTreeIterator(self, stack, direction) + } + } +} + +/** @internal */ +export const greaterThanEqualBackwards = dual< + (key: K) => (self: RBT.RedBlackTree) => Iterable<[K, V]>, + (self: RBT.RedBlackTree, key: K) => Iterable<[K, V]> +>(2, (self, key) => greaterThanEqual(self, key, Direction.Backward)) + +/** @internal */ +export const greaterThanEqualForwards = dual< + (key: K) => (self: RBT.RedBlackTree) => Iterable<[K, V]>, + (self: RBT.RedBlackTree, key: K) => Iterable<[K, V]> +>(2, (self, key) => greaterThanEqual(self, key, Direction.Forward)) + +const greaterThanEqual = ( + self: RBT.RedBlackTree, + key: K, + direction: RBT.RedBlackTree.Direction = Direction.Forward +): Iterable<[K, V]> => { + return { + [Symbol.iterator]: () => { + const cmp = (self as RedBlackTreeImpl)._ord + let node = (self as RedBlackTreeImpl)._root + const stack = [] + let last_ptr = 0 + while (node !== undefined) { + const d = cmp(key, node.key) + stack.push(node) + if (d <= 0) { + last_ptr = stack.length + } + if (d <= 0) { + node = node.left + } else { + node = node.right + } + } + stack.length = last_ptr + return new RedBlackTreeIterator(self, stack, direction) + } + } +} + +/** @internal */ +export const lessThanBackwards = dual< + (key: K) => (self: RBT.RedBlackTree) => Iterable<[K, V]>, + (self: RBT.RedBlackTree, key: K) => Iterable<[K, V]> +>(2, (self, key) => lessThan(self, key, Direction.Backward)) + +/** @internal */ +export const lessThanForwards = dual< + (key: K) => (self: RBT.RedBlackTree) => Iterable<[K, V]>, + (self: RBT.RedBlackTree, key: K) => Iterable<[K, V]> +>(2, (self, key) => lessThan(self, key, Direction.Forward)) + +const lessThan = ( + self: RBT.RedBlackTree, + key: K, + direction: RBT.RedBlackTree.Direction +): Iterable<[K, V]> => { + return { + [Symbol.iterator]: () => { + const cmp = (self as RedBlackTreeImpl)._ord + let node = (self as RedBlackTreeImpl)._root + const stack = [] + let last_ptr = 0 + while (node !== undefined) { + const d = cmp(key, node.key) + stack.push(node) + if (d > 0) { + last_ptr = stack.length + } + if (d <= 0) { + node = node.left + } else { + node = node.right + } + } + stack.length = last_ptr + return new RedBlackTreeIterator(self, stack, direction) + } + } +} + +/** @internal */ +export const lessThanEqualBackwards = dual< + (key: K) => (self: RBT.RedBlackTree) => Iterable<[K, V]>, + (self: RBT.RedBlackTree, key: K) => Iterable<[K, V]> +>(2, (self, key) => lessThanEqual(self, key, Direction.Backward)) + +/** @internal */ +export const lessThanEqualForwards = dual< + (key: K) => (self: RBT.RedBlackTree) => Iterable<[K, V]>, + (self: RBT.RedBlackTree, key: K) => Iterable<[K, V]> +>(2, (self, key) => lessThanEqual(self, key, Direction.Forward)) + +const lessThanEqual = ( + self: RBT.RedBlackTree, + key: K, + direction: RBT.RedBlackTree.Direction +): Iterable<[K, V]> => { + return { + [Symbol.iterator]: () => { + const cmp = (self as RedBlackTreeImpl)._ord + let node = (self as RedBlackTreeImpl)._root + const stack = [] + let last_ptr = 0 + while (node !== undefined) { + const d = cmp(key, node.key) + stack.push(node) + if (d >= 0) { + last_ptr = stack.length + } + if (d < 0) { + node = node.left + } else { + node = node.right + } + } + stack.length = last_ptr + return new RedBlackTreeIterator(self, stack, direction) + } + } +} + +/** @internal */ +export const forEach = dual< + (f: (key: K, value: V) => void) => (self: RBT.RedBlackTree) => void, + (self: RBT.RedBlackTree, f: (key: K, value: V) => void) => void +>(2, (self: RBT.RedBlackTree, f: (key: K, value: V) => void) => { + const root = (self as RedBlackTreeImpl)._root + if (root !== undefined) { + visitFull(root, (key, value) => { + f(key, value) + return Option.none() + }) + } +}) + +/** @internal */ +export const forEachGreaterThanEqual = dual< + (min: K, f: (key: K, value: V) => void) => (self: RBT.RedBlackTree) => void, + (self: RBT.RedBlackTree, min: K, f: (key: K, value: V) => void) => void +>(3, (self: RBT.RedBlackTree, min: K, f: (key: K, value: V) => void) => { + const root = (self as RedBlackTreeImpl)._root + const ord = (self as RedBlackTreeImpl)._ord + if (root !== undefined) { + visitGreaterThanEqual(root, min, ord, (key, value) => { + f(key, value) + return Option.none() + }) + } +}) + +/** @internal */ +export const forEachLessThan = dual< + (max: K, f: (key: K, value: V) => void) => (self: RBT.RedBlackTree) => void, + (self: RBT.RedBlackTree, max: K, f: (key: K, value: V) => void) => void +>(3, (self: RBT.RedBlackTree, max: K, f: (key: K, value: V) => void) => { + const root = (self as RedBlackTreeImpl)._root + const ord = (self as RedBlackTreeImpl)._ord + if (root !== undefined) { + visitLessThan(root, max, ord, (key, value) => { + f(key, value) + return Option.none() + }) + } +}) + +/** @internal */ +export const forEachBetween = dual< + (options: { + readonly min: K + readonly max: K + readonly body: (key: K, value: V) => void + }) => (self: RBT.RedBlackTree) => void, + (self: RBT.RedBlackTree, options: { + readonly min: K + readonly max: K + readonly body: (key: K, value: V) => void + }) => void +>(2, (self: RBT.RedBlackTree, { body, max, min }: { + readonly min: K + readonly max: K + readonly body: (key: K, value: V) => void +}) => { + const root = (self as RedBlackTreeImpl)._root + const ord = (self as RedBlackTreeImpl)._ord + if (root) { + visitBetween(root, min, max, ord, (key, value) => { + body(key, value) + return Option.none() + }) + } +}) + +/** @internal */ +export const reduce = dual< + ( + zero: Z, + f: (accumulator: Z, value: V, key: K) => Z + ) => (self: RBT.RedBlackTree) => Z, + (self: RBT.RedBlackTree, zero: Z, f: (accumulator: Z, value: V, key: K) => Z) => Z +>(3, (self, zero, f) => { + let accumulator = zero + for (const entry of self) { + accumulator = f(accumulator, entry[1], entry[0]) + } + return accumulator +}) + +/** @internal */ +export const removeFirst = dual< + (key: K) => (self: RBT.RedBlackTree) => RBT.RedBlackTree, + (self: RBT.RedBlackTree, key: K) => RBT.RedBlackTree +>(2, (self: RBT.RedBlackTree, key: K) => { + if (!has(self, key)) { + return self + } + const ord = (self as RedBlackTreeImpl)._ord + const cmp = ord + let node: Node.Node | undefined = (self as RedBlackTreeImpl)._root + const stack = [] + while (node !== undefined) { + const d = cmp(key, node.key) + stack.push(node) + if (Equal.equals(key, node.key)) { + node = undefined + } else if (d <= 0) { + node = node.left + } else { + node = node.right + } + } + if (stack.length === 0) { + return self + } + const cstack = new Array>(stack.length) + let n = stack[stack.length - 1]! + cstack[cstack.length - 1] = { + color: n.color, + key: n.key, + value: n.value, + left: n.left, + right: n.right, + count: n.count + } + for (let i = stack.length - 2; i >= 0; --i) { + n = stack[i]! + if (n.left === stack[i + 1]) { + cstack[i] = { + color: n.color, + key: n.key, + value: n.value, + left: cstack[i + 1], + right: n.right, + count: n.count + } + } else { + cstack[i] = { + color: n.color, + key: n.key, + value: n.value, + left: n.left, + right: cstack[i + 1], + count: n.count + } + } + } + // Get node + n = cstack[cstack.length - 1]! + // If not leaf, then swap with previous node + if (n.left !== undefined && n.right !== undefined) { + // First walk to previous leaf + const split = cstack.length + n = n.left + while (n.right != null) { + cstack.push(n) + n = n.right + } + // Copy path to leaf + const v = cstack[split - 1] + cstack.push({ + color: n.color, + key: v!.key, + value: v!.value, + left: n.left, + right: n.right, + count: n.count + }) + cstack[split - 1]!.key = n.key + cstack[split - 1]!.value = n.value + // Fix up stack + for (let i = cstack.length - 2; i >= split; --i) { + n = cstack[i]! + cstack[i] = { + color: n.color, + key: n.key, + value: n.value, + left: n.left, + right: cstack[i + 1], + count: n.count + } + } + cstack[split - 1]!.left = cstack[split] + } + + // Remove leaf node + n = cstack[cstack.length - 1]! + if (n.color === Node.Color.Red) { + // Easy case: removing red leaf + const p = cstack[cstack.length - 2]! + if (p.left === n) { + p.left = undefined + } else if (p.right === n) { + p.right = undefined + } + cstack.pop() + for (let i = 0; i < cstack.length; ++i) { + cstack[i]!.count-- + } + return makeImpl(ord, cstack[0]) + } else { + if (n.left !== undefined || n.right !== undefined) { + // Second easy case: Single child black parent + if (n.left !== undefined) { + Node.swap(n, n.left) + } else if (n.right !== undefined) { + Node.swap(n, n.right) + } + // Child must be red, so repaint it black to balance color + n.color = Node.Color.Black + for (let i = 0; i < cstack.length - 1; ++i) { + cstack[i]!.count-- + } + return makeImpl(ord, cstack[0]) + } else if (cstack.length === 1) { + // Third easy case: root + return makeImpl(ord, undefined) + } else { + // Hard case: Repaint n, and then do some nasty stuff + for (let i = 0; i < cstack.length; ++i) { + cstack[i]!.count-- + } + const parent = cstack[cstack.length - 2] + fixDoubleBlack(cstack) + // Fix up links + if (parent!.left === n) { + parent!.left = undefined + } else { + parent!.right = undefined + } + } + } + return makeImpl(ord, cstack[0]) +}) + +/** @internal */ +export const size = (self: RBT.RedBlackTree): number => (self as RedBlackTreeImpl)._root?.count ?? 0 + +/** @internal */ +export const valuesForward = (self: RBT.RedBlackTree): IterableIterator => + values(self, Direction.Forward) + +/** @internal */ +export const valuesBackward = (self: RBT.RedBlackTree): IterableIterator => + values(self, Direction.Backward) + +/** @internal */ +const values = ( + self: RBT.RedBlackTree, + direction: RBT.RedBlackTree.Direction +): IterableIterator => { + const begin: RedBlackTreeIterator = self[Symbol.iterator]() as RedBlackTreeIterator + let count = 0 + return { + [Symbol.iterator]: () => values(self, direction), + next: (): IteratorResult => { + count++ + const entry = begin.value + if (direction === Direction.Forward) { + begin.moveNext() + } else { + begin.movePrev() + } + switch (entry._tag) { + case "None": { + return { done: true, value: count } + } + case "Some": { + return { done: false, value: entry.value } + } + } + } + } +} + +const visitFull = ( + node: Node.Node, + visit: (key: K, value: V) => Option.Option +): Option.Option => { + let current: Node.Node | undefined = node + let stack: Stack.Stack> | undefined = undefined + let done = false + while (!done) { + if (current != null) { + stack = Stack.make(current, stack) + current = current.left + } else if (stack != null) { + const value = visit(stack.value.key, stack.value.value) + if (Option.isSome(value)) { + return value + } + current = stack.value.right + stack = stack.previous + } else { + done = true + } + } + return Option.none() +} + +const visitGreaterThanEqual = ( + node: Node.Node, + min: K, + ord: Order.Order, + visit: (key: K, value: V) => Option.Option +): Option.Option => { + let current: Node.Node | undefined = node + let stack: Stack.Stack> | undefined = undefined + let done = false + while (!done) { + if (current !== undefined) { + stack = Stack.make(current, stack) + if (ord(min, current.key) <= 0) { + current = current.left + } else { + current = undefined + } + } else if (stack !== undefined) { + if (ord(min, stack.value.key) <= 0) { + const value = visit(stack.value.key, stack.value.value) + if (Option.isSome(value)) { + return value + } + } + current = stack.value.right + stack = stack.previous + } else { + done = true + } + } + return Option.none() +} + +const visitLessThan = ( + node: Node.Node, + max: K, + ord: Order.Order, + visit: (key: K, value: V) => Option.Option +): Option.Option => { + let current: Node.Node | undefined = node + let stack: Stack.Stack> | undefined = undefined + let done = false + while (!done) { + if (current !== undefined) { + stack = Stack.make(current, stack) + current = current.left + } else if (stack !== undefined && ord(max, stack.value.key) > 0) { + const value = visit(stack.value.key, stack.value.value) + if (Option.isSome(value)) { + return value + } + current = stack.value.right + stack = stack.previous + } else { + done = true + } + } + return Option.none() +} + +const visitBetween = ( + node: Node.Node, + min: K, + max: K, + ord: Order.Order, + visit: (key: K, value: V) => Option.Option +): Option.Option => { + let current: Node.Node | undefined = node + let stack: Stack.Stack> | undefined = undefined + let done = false + while (!done) { + if (current !== undefined) { + stack = Stack.make(current, stack) + if (ord(min, current.key) <= 0) { + current = current.left + } else { + current = undefined + } + } else if (stack !== undefined && ord(max, stack.value.key) > 0) { + if (ord(min, stack.value.key) <= 0) { + const value = visit(stack.value.key, stack.value.value) + if (Option.isSome(value)) { + return value + } + } + current = stack.value.right + stack = stack.previous + } else { + done = true + } + } + return Option.none() +} + +/** + * Fix up a double black node in a Red-Black Tree. + */ +const fixDoubleBlack = (stack: Array>) => { + let n, p, s, z + for (let i = stack.length - 1; i >= 0; --i) { + n = stack[i]! + if (i === 0) { + n.color = Node.Color.Black + return + } + p = stack[i - 1]! + if (p.left === n) { + s = p.right + if (s !== undefined && s.right !== undefined && s.right.color === Node.Color.Red) { + s = p.right = Node.clone(s) + z = s.right = Node.clone(s.right!) + p.right = s.left + s.left = p + s.right = z + s.color = p.color + n.color = Node.Color.Black + p.color = Node.Color.Black + z.color = Node.Color.Black + Node.recount(p) + Node.recount(s) + if (i > 1) { + const pp = stack[i - 2]! + if (pp.left === p) { + pp.left = s + } else { + pp.right = s + } + } + stack[i - 1] = s + return + } else if (s !== undefined && s.left !== undefined && s.left.color === Node.Color.Red) { + s = p.right = Node.clone(s) + z = s.left = Node.clone(s.left!) + p.right = z.left + s.left = z.right + z.left = p + z.right = s + z.color = p.color + p.color = Node.Color.Black + s.color = Node.Color.Black + n.color = Node.Color.Black + Node.recount(p) + Node.recount(s) + Node.recount(z) + if (i > 1) { + const pp = stack[i - 2]! + if (pp.left === p) { + pp.left = z + } else { + pp.right = z + } + } + stack[i - 1] = z + return + } + if (s !== undefined && s.color === Node.Color.Black) { + if (p.color === Node.Color.Red) { + p.color = Node.Color.Black + p.right = Node.repaint(s, Node.Color.Red) + return + } else { + p.right = Node.repaint(s, Node.Color.Red) + continue + } + } else if (s !== undefined) { + s = Node.clone(s) + p.right = s.left + s.left = p + s.color = p.color + p.color = Node.Color.Red + Node.recount(p) + Node.recount(s) + if (i > 1) { + const pp = stack[i - 2]! + if (pp.left === p) { + pp.left = s + } else { + pp.right = s + } + } + stack[i - 1] = s + stack[i] = p + if (i + 1 < stack.length) { + stack[i + 1] = n + } else { + stack.push(n) + } + i = i + 2 + } + } else { + s = p.left + if (s !== undefined && s.left !== undefined && s.left.color === Node.Color.Red) { + s = p.left = Node.clone(s) + z = s.left = Node.clone(s.left!) + p.left = s.right + s.right = p + s.left = z + s.color = p.color + n.color = Node.Color.Black + p.color = Node.Color.Black + z.color = Node.Color.Black + Node.recount(p) + Node.recount(s) + if (i > 1) { + const pp = stack[i - 2]! + if (pp.right === p) { + pp.right = s + } else { + pp.left = s + } + } + stack[i - 1] = s + return + } else if (s !== undefined && s.right !== undefined && s.right.color === Node.Color.Red) { + s = p.left = Node.clone(s) + z = s.right = Node.clone(s.right!) + p.left = z.right + s.right = z.left + z.right = p + z.left = s + z.color = p.color + p.color = Node.Color.Black + s.color = Node.Color.Black + n.color = Node.Color.Black + Node.recount(p) + Node.recount(s) + Node.recount(z) + if (i > 1) { + const pp = stack[i - 2]! + if (pp.right === p) { + pp.right = z + } else { + pp.left = z + } + } + stack[i - 1] = z + return + } + if (s !== undefined && s.color === Node.Color.Black) { + if (p.color === Node.Color.Red) { + p.color = Node.Color.Black + p.left = Node.repaint(s, Node.Color.Red) + return + } else { + p.left = Node.repaint(s, Node.Color.Red) + continue + } + } else if (s !== undefined) { + s = Node.clone(s) + p.left = s.right + s.right = p + s.color = p.color + p.color = Node.Color.Red + Node.recount(p) + Node.recount(s) + if (i > 1) { + const pp = stack[i - 2]! + if (pp.right === p) { + pp.right = s + } else { + pp.left = s + } + } + stack[i - 1] = s + stack[i] = p + if (i + 1 < stack.length) { + stack[i + 1] = n + } else { + stack.push(n) + } + i = i + 2 + } + } + } +} diff --git a/backend/node_modules/effect/src/internal/redBlackTree/iterator.ts b/backend/node_modules/effect/src/internal/redBlackTree/iterator.ts new file mode 100644 index 0000000000000000000000000000000000000000..ae240c7dfac7b46831c24b015f43bb2900a11be9 --- /dev/null +++ b/backend/node_modules/effect/src/internal/redBlackTree/iterator.ts @@ -0,0 +1,200 @@ +import * as Arr from "../../Array.js" +import * as Option from "../../Option.js" +import type * as RBT from "../../RedBlackTree.js" +import type { RedBlackTreeImpl } from "../redBlackTree.js" +import type * as Node from "./node.js" + +/** @internal */ +export const Direction = { + Forward: 0 as RBT.RedBlackTree.Direction, + Backward: 1 << 0 as RBT.RedBlackTree.Direction +} as const + +/** @internal */ +export class RedBlackTreeIterator implements Iterator<[K, V]> { + private count = 0 + + constructor( + readonly self: RBT.RedBlackTree, + readonly stack: Array>, + readonly direction: RBT.RedBlackTree.Direction + ) {} + + /** + * Clones the iterator + */ + clone(): RedBlackTreeIterator { + return new RedBlackTreeIterator(this.self, this.stack.slice(), this.direction) + } + + /** + * Reverse the traversal direction + */ + reversed(): RedBlackTreeIterator { + return new RedBlackTreeIterator( + this.self, + this.stack.slice(), + this.direction === Direction.Forward ? Direction.Backward : Direction.Forward + ) + } + + /** + * Iterator next + */ + next(): IteratorResult<[K, V], number> { + const entry = this.entry + this.count++ + if (this.direction === Direction.Forward) { + this.moveNext() + } else { + this.movePrev() + } + switch (entry._tag) { + case "None": { + return { done: true, value: this.count } + } + case "Some": { + return { done: false, value: entry.value } + } + } + } + + /** + * Returns the key + */ + get key(): Option.Option { + if (this.stack.length > 0) { + return Option.some(this.stack[this.stack.length - 1]!.key) + } + return Option.none() + } + + /** + * Returns the value + */ + get value(): Option.Option { + if (this.stack.length > 0) { + return Option.some(this.stack[this.stack.length - 1]!.value) + } + return Option.none() + } + + /** + * Returns the key + */ + get entry(): Option.Option<[K, V]> { + return Option.map(Arr.last(this.stack), (node) => [node.key, node.value]) + } + + /** + * Returns the position of this iterator in the sorted list + */ + get index(): number { + let idx = 0 + const stack = this.stack + if (stack.length === 0) { + const r = (this.self as RedBlackTreeImpl)._root + if (r != null) { + return r.count + } + return 0 + } else if (stack[stack.length - 1]!.left != null) { + idx = stack[stack.length - 1]!.left!.count + } + for (let s = stack.length - 2; s >= 0; --s) { + if (stack[s + 1] === stack[s]!.right) { + ++idx + if (stack[s]!.left != null) { + idx += stack[s]!.left!.count + } + } + } + return idx + } + + /** + * Advances iterator to next element in list + */ + moveNext() { + const stack = this.stack + if (stack.length === 0) { + return + } + let n: Node.Node | undefined = stack[stack.length - 1]! + if (n.right != null) { + n = n.right + while (n != null) { + stack.push(n) + n = n.left + } + } else { + stack.pop() + while (stack.length > 0 && stack[stack.length - 1]!.right === n) { + n = stack[stack.length - 1] + stack.pop() + } + } + } + + /** + * Checks if there is a next element + */ + get hasNext() { + const stack = this.stack + if (stack.length === 0) { + return false + } + if (stack[stack.length - 1]!.right != null) { + return true + } + for (let s = stack.length - 1; s > 0; --s) { + if (stack[s - 1]!.left === stack[s]) { + return true + } + } + return false + } + + /** + * Advances iterator to previous element in list + */ + movePrev() { + const stack = this.stack + if (stack.length === 0) { + return + } + let n: Node.Node | undefined = stack[stack.length - 1] + if (n != null && n.left != null) { + n = n.left + while (n != null) { + stack.push(n) + n = n.right + } + } else { + stack.pop() + while (stack.length > 0 && stack[stack.length - 1]!.left === n) { + n = stack[stack.length - 1] + stack.pop() + } + } + } + + /** + * Checks if there is a previous element + */ + get hasPrev() { + const stack = this.stack + if (stack.length === 0) { + return false + } + if (stack[stack.length - 1]!.left != null) { + return true + } + for (let s = stack.length - 1; s > 0; --s) { + if (stack[s - 1]!.right === stack[s]) { + return true + } + } + return false + } +} diff --git a/backend/node_modules/effect/src/internal/redBlackTree/node.ts b/backend/node_modules/effect/src/internal/redBlackTree/node.ts new file mode 100644 index 0000000000000000000000000000000000000000..904a6cfc46759c703f743ab3bf9bda4182ae445b --- /dev/null +++ b/backend/node_modules/effect/src/internal/redBlackTree/node.ts @@ -0,0 +1,68 @@ +/** @internal */ +export const Color = { + Red: 0 as Node.Color, + Black: 1 << 0 as Node.Color +} as const + +export declare namespace Node { + export type Color = number & { + readonly Color: unique symbol + } +} + +export interface Node { + color: Node.Color + key: K + value: V + left: Node | undefined + right: Node | undefined + count: number +} + +/** @internal */ +export const clone = ({ + color, + count, + key, + left, + right, + value +}: Node) => ({ + color, + key, + value, + left, + right, + count +}) + +/** @internal */ +export function swap(n: Node, v: Node) { + n.key = v.key + n.value = v.value + n.left = v.left + n.right = v.right + n.color = v.color + n.count = v.count +} + +/** @internal */ +export const repaint = ({ + count, + key, + left, + right, + value +}: Node, color: Node.Color) => ({ + color, + key, + value, + left, + right, + count +}) + +/** @internal */ +export const recount = (node: Node) => { + node.count = 1 + (node.left?.count ?? 0) + (node.right?.count ?? 0) +} diff --git a/backend/node_modules/effect/src/internal/redacted.ts b/backend/node_modules/effect/src/internal/redacted.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab10cf0686e62c3b511a939cd804e8a915264019 --- /dev/null +++ b/backend/node_modules/effect/src/internal/redacted.ts @@ -0,0 +1,73 @@ +import * as Equal from "../Equal.js" +import { pipe } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import * as Hash from "../Hash.js" +import { NodeInspectSymbol } from "../Inspectable.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty } from "../Predicate.js" +import type * as Redacted from "../Redacted.js" + +/** @internal */ +const RedactedSymbolKey = "effect/Redacted" + +/** @internal */ +export const redactedRegistry = globalValue( + "effect/Redacted/redactedRegistry", + () => new WeakMap, any>() +) + +/** @internal */ +export const RedactedTypeId: Redacted.RedactedTypeId = Symbol.for( + RedactedSymbolKey +) as Redacted.RedactedTypeId + +/** @internal */ +export const proto = { + [RedactedTypeId]: { + _A: (_: never) => _ + }, + pipe() { + return pipeArguments(this, arguments) + }, + toString() { + return "" + }, + toJSON() { + return "" + }, + [NodeInspectSymbol]() { + return "" + }, + [Hash.symbol](this: Redacted.Redacted): number { + return pipe( + Hash.hash(RedactedSymbolKey), + Hash.combine(Hash.hash(redactedRegistry.get(this))), + Hash.cached(this) + ) + }, + [Equal.symbol](this: Redacted.Redacted, that: unknown): boolean { + return isRedacted(that) && Equal.equals(redactedRegistry.get(this), redactedRegistry.get(that)) + } +} + +/** @internal */ +export const isRedacted = (u: unknown): u is Redacted.Redacted => hasProperty(u, RedactedTypeId) + +/** @internal */ +export const make = (value: T): Redacted.Redacted => { + const redacted = Object.create(proto) + redactedRegistry.set(redacted, value) + return redacted +} + +/** @internal */ +export const value = (self: Redacted.Redacted): T => { + if (redactedRegistry.has(self)) { + return redactedRegistry.get(self) + } else { + throw new Error("Unable to get redacted value") + } +} + +/** @internal */ +export const unsafeWipe = (self: Redacted.Redacted): boolean => redactedRegistry.delete(self) diff --git a/backend/node_modules/effect/src/internal/ref.ts b/backend/node_modules/effect/src/internal/ref.ts new file mode 100644 index 0000000000000000000000000000000000000000..cc10f6b3e0132314c056453b3dec1f068ba5543a --- /dev/null +++ b/backend/node_modules/effect/src/internal/ref.ts @@ -0,0 +1,171 @@ +import type * as Effect from "../Effect.js" +import * as Effectable from "../Effectable.js" +import { dual } from "../Function.js" +import * as MutableRef from "../MutableRef.js" +import * as Option from "../Option.js" +import * as Readable from "../Readable.js" +import type * as Ref from "../Ref.js" +import * as core from "./core.js" + +/** @internal */ +export const RefTypeId: Ref.RefTypeId = Symbol.for("effect/Ref") as Ref.RefTypeId + +/** @internal */ +export const refVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +class RefImpl extends Effectable.Class implements Ref.Ref { + commit() { + return this.get + } + readonly [RefTypeId] = refVariance + readonly [Readable.TypeId]: Readable.TypeId = Readable.TypeId + constructor(readonly ref: MutableRef.MutableRef) { + super() + this.get = core.sync(() => MutableRef.get(this.ref)) + } + readonly get: Effect.Effect + modify(f: (a: A) => readonly [B, A]): Effect.Effect { + return core.sync(() => { + const current = MutableRef.get(this.ref) + const [b, a] = f(current) + if ((current as unknown) !== (a as unknown)) { + MutableRef.set(a)(this.ref) + } + return b + }) + } +} + +/** @internal */ +export const unsafeMake = (value: A): Ref.Ref => new RefImpl(MutableRef.make(value)) + +/** @internal */ +export const make = (value: A): Effect.Effect> => core.sync(() => unsafeMake(value)) + +/** @internal */ +export const get = (self: Ref.Ref) => self.get + +/** @internal */ +export const set = dual< + (value: A) => (self: Ref.Ref) => Effect.Effect, + (self: Ref.Ref, value: A) => Effect.Effect +>(2, (self: Ref.Ref, value: A) => self.modify((): [void, A] => [void 0, value])) + +/** @internal */ +export const getAndSet = dual< + (value: A) => (self: Ref.Ref) => Effect.Effect, + (self: Ref.Ref, value: A) => Effect.Effect +>(2, (self: Ref.Ref, value: A) => self.modify((a): [A, A] => [a, value])) + +/** @internal */ +export const getAndUpdate = dual< + (f: (a: A) => A) => (self: Ref.Ref) => Effect.Effect, + (self: Ref.Ref, f: (a: A) => A) => Effect.Effect +>(2, (self: Ref.Ref, f: (a: A) => A) => self.modify((a): [A, A] => [a, f(a)])) + +/** @internal */ +export const getAndUpdateSome = dual< + (pf: (a: A) => Option.Option) => (self: Ref.Ref) => Effect.Effect, + (self: Ref.Ref, pf: (a: A) => Option.Option) => Effect.Effect +>(2, (self: Ref.Ref, pf: (a: A) => Option.Option) => + self.modify((value): [A, A] => { + const option = pf(value) + switch (option._tag) { + case "None": { + return [value, value] + } + case "Some": { + return [value, option.value] + } + } + })) + +/** @internal */ +export const setAndGet = dual< + (value: A) => (self: Ref.Ref) => Effect.Effect, + (self: Ref.Ref, value: A) => Effect.Effect +>(2, (self: Ref.Ref, value: A) => self.modify((): [A, A] => [value, value])) + +/** @internal */ +export const modify = dual< + (f: (a: A) => readonly [B, A]) => (self: Ref.Ref) => Effect.Effect, + (self: Ref.Ref, f: (a: A) => readonly [B, A]) => Effect.Effect +>(2, (self, f) => self.modify(f)) + +/** @internal */ +export const modifySome = dual< + ( + fallback: B, + pf: (a: A) => Option.Option + ) => (self: Ref.Ref) => Effect.Effect, + ( + self: Ref.Ref, + fallback: B, + pf: (a: A) => Option.Option + ) => Effect.Effect +>(3, (self, fallback, pf) => + self.modify((value) => { + const option = pf(value) + switch (option._tag) { + case "None": { + return [fallback, value] + } + case "Some": { + return option.value + } + } + })) + +/** @internal */ +export const update = dual< + (f: (a: A) => A) => (self: Ref.Ref) => Effect.Effect, + (self: Ref.Ref, f: (a: A) => A) => Effect.Effect +>(2, (self: Ref.Ref, f: (a: A) => A) => self.modify((a): [void, A] => [void 0, f(a)])) + +/** @internal */ +export const updateAndGet = dual< + (f: (a: A) => A) => (self: Ref.Ref) => Effect.Effect, + (self: Ref.Ref, f: (a: A) => A) => Effect.Effect +>(2, (self: Ref.Ref, f: (a: A) => A) => + self.modify((a): [A, A] => { + const result = f(a) + return [result, result] + })) + +/** @internal */ +export const updateSome = dual< + (f: (a: A) => Option.Option) => (self: Ref.Ref) => Effect.Effect, + (self: Ref.Ref, f: (a: A) => Option.Option) => Effect.Effect +>(2, (self: Ref.Ref, f: (a: A) => Option.Option) => + self.modify( + (a): [void, A] => [ + void 0, + Option.match(f(a), { + onNone: () => a, + onSome: (b) => b + }) + ] + )) + +/** @internal */ +export const updateSomeAndGet = dual< + (pf: (a: A) => Option.Option) => (self: Ref.Ref) => Effect.Effect, + (self: Ref.Ref, pf: (a: A) => Option.Option) => Effect.Effect +>(2, (self: Ref.Ref, pf: (a: A) => Option.Option) => + self.modify((value): [A, A] => { + const option = pf(value) + switch (option._tag) { + case "None": { + return [value, value] + } + case "Some": { + return [option.value, option.value] + } + } + })) + +/** @internal */ +export const unsafeGet = (self: Ref.Ref): A => MutableRef.get((self as RefImpl).ref) diff --git a/backend/node_modules/effect/src/internal/reloadable.ts b/backend/node_modules/effect/src/internal/reloadable.ts new file mode 100644 index 0000000000000000000000000000000000000000..f871ff6d61d0ed0d69159057440bd38a98d002bb --- /dev/null +++ b/backend/node_modules/effect/src/internal/reloadable.ts @@ -0,0 +1,140 @@ +import * as Context from "../Context.js" +import type * as Effect from "../Effect.js" +import { pipe } from "../Function.js" +import type * as Layer from "../Layer.js" +import type * as Reloadable from "../Reloadable.js" +import type * as Schedule from "../Schedule.js" +import * as effect from "./core-effect.js" +import * as core from "./core.js" +import * as fiberRuntime from "./fiberRuntime.js" +import * as layer_ from "./layer.js" +import * as schedule_ from "./schedule.js" +import * as scopedRef from "./scopedRef.js" + +/** @internal */ +const ReloadableSymbolKey = "effect/Reloadable" + +/** @internal */ +export const ReloadableTypeId: Reloadable.ReloadableTypeId = Symbol.for( + ReloadableSymbolKey +) as Reloadable.ReloadableTypeId + +const reloadableVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +export const auto = ( + tag: Context.Tag, + options: { + readonly layer: Layer.Layer + readonly schedule: Schedule.Schedule + } +): Layer.Layer, E, R | In> => + layer_.scoped( + reloadableTag(tag), + pipe( + layer_.build(manual(tag, { layer: options.layer })), + core.map(Context.unsafeGet(reloadableTag(tag))), + core.tap((reloadable) => + fiberRuntime.acquireRelease( + pipe( + reloadable.reload, + effect.ignoreLogged, + schedule_.schedule_Effect(options.schedule), + fiberRuntime.forkDaemon + ), + core.interruptFiber + ) + ) + ) + ) + +/** @internal */ +export const autoFromConfig = ( + tag: Context.Tag, + options: { + readonly layer: Layer.Layer + readonly scheduleFromConfig: (context: Context.Context) => Schedule.Schedule + } +): Layer.Layer, E, R | In> => + layer_.scoped( + reloadableTag(tag), + pipe( + core.context(), + core.flatMap((env) => + pipe( + layer_.build(auto(tag, { + layer: options.layer, + schedule: options.scheduleFromConfig(env) + })), + core.map(Context.unsafeGet(reloadableTag(tag))) + ) + ) + ) + ) + +/** @internal */ +export const get = ( + tag: Context.Tag +): Effect.Effect> => + core.flatMap( + reloadableTag(tag), + (reloadable) => scopedRef.get(reloadable.scopedRef) + ) + +/** @internal */ +export const manual = ( + tag: Context.Tag, + options: { + readonly layer: Layer.Layer + } +): Layer.Layer, E, In> => + layer_.scoped( + reloadableTag(tag), + pipe( + core.context(), + core.flatMap((env) => + pipe( + scopedRef.fromAcquire(pipe(layer_.build(options.layer), core.map(Context.unsafeGet(tag)))), + core.map((ref) => ({ + [ReloadableTypeId]: reloadableVariance, + scopedRef: ref, + reload: pipe( + scopedRef.set(ref, pipe(layer_.build(options.layer), core.map(Context.unsafeGet(tag)))), + core.provideContext(env) + ) + })) + ) + ) + ) + ) + +/** @internal */ +export const reloadableTag = ( + tag: Context.Tag +): Context.Tag, Reloadable.Reloadable> => { + return Context.GenericTag, Reloadable.Reloadable>(`effect/Reloadable<${tag.key}>`) +} + +/** @internal */ +export const reload = ( + tag: Context.Tag +): Effect.Effect> => + core.flatMap( + reloadableTag(tag), + (reloadable) => reloadable.reload + ) + +/** @internal */ +export const reloadFork = ( + tag: Context.Tag +): Effect.Effect> => + core.flatMap(reloadableTag(tag), (reloadable) => + pipe( + reloadable.reload, + effect.ignoreLogged, + fiberRuntime.forkDaemon, + core.asVoid + )) diff --git a/backend/node_modules/effect/src/internal/request.ts b/backend/node_modules/effect/src/internal/request.ts new file mode 100644 index 0000000000000000000000000000000000000000..1c5d44e987907a1af789e321931d412bd33a2fcc --- /dev/null +++ b/backend/node_modules/effect/src/internal/request.ts @@ -0,0 +1,177 @@ +import type * as Cause from "../Cause.js" +import type * as Effect from "../Effect.js" +import { dual } from "../Function.js" +import { hasProperty } from "../Predicate.js" +import type * as Request from "../Request.js" +import type * as Types from "../Types.js" +import * as completedRequestMap from "./completedRequestMap.js" +import * as core from "./core.js" +import { StructuralPrototype } from "./effectable.js" + +/** @internal */ +const RequestSymbolKey = "effect/Request" + +/** @internal */ +export const RequestTypeId: Request.RequestTypeId = Symbol.for( + RequestSymbolKey +) as Request.RequestTypeId + +const requestVariance = { + /* c8 ignore next */ + _E: (_: never) => _, + /* c8 ignore next */ + _A: (_: never) => _ +} + +const RequestPrototype = { + ...StructuralPrototype, + [RequestTypeId]: requestVariance +} + +/** @internal */ +export const isRequest = (u: unknown): u is Request.Request => hasProperty(u, RequestTypeId) + +/** @internal */ +export const of = >(): Request.Request.Constructor => (args) => + Object.assign(Object.create(RequestPrototype), args) + +/** @internal */ +export const tagged = & { _tag: string }>( + tag: R["_tag"] +): Request.Request.Constructor => +(args) => { + const request = Object.assign(Object.create(RequestPrototype), args) + request._tag = tag + return request +} + +/** @internal */ +export const Class: new>( + args: Types.Equals>, {}> extends true ? void + : { readonly [P in keyof A as P extends keyof Request.Request ? never : P]: A[P] } +) => Request.Request & Readonly = (function() { + function Class(this: any, args: any) { + if (args) { + Object.assign(this, args) + } + } + Class.prototype = RequestPrototype + return Class as any +})() + +/** @internal */ +export const TaggedClass = ( + tag: Tag +): new>( + args: Types.Equals>, {}> extends true ? void + : { readonly [P in keyof A as P extends "_tag" | keyof Request.Request ? never : P]: A[P] } +) => Request.Request & Readonly & { readonly _tag: Tag } => { + return class TaggedClass extends Class { + readonly _tag = tag + } as any +} + +/** @internal */ +export const complete = dual< + >( + result: Request.Request.Result + ) => (self: A) => Effect.Effect, + >( + self: A, + result: Request.Request.Result + ) => Effect.Effect +>(2, (self, result) => + core.fiberRefGetWith( + completedRequestMap.currentRequestMap, + (map) => + core.sync(() => { + if (map.has(self)) { + const entry = map.get(self)! + if (!entry.state.completed) { + entry.state.completed = true + core.deferredUnsafeDone(entry.result, result) + } + } + }) + )) + +/** @internal */ +export const completeEffect = dual< + , R>( + effect: Effect.Effect, Request.Request.Error, R> + ) => (self: A) => Effect.Effect, + , R>( + self: A, + effect: Effect.Effect, Request.Request.Error, R> + ) => Effect.Effect +>(2, (self, effect) => + core.matchEffect(effect, { + onFailure: (error) => complete(self, core.exitFail(error) as any), + onSuccess: (value) => complete(self, core.exitSucceed(value) as any) + })) + +/** @internal */ +export const fail = dual< + >( + error: Request.Request.Error + ) => (self: A) => Effect.Effect, + >( + self: A, + error: Request.Request.Error + ) => Effect.Effect +>(2, (self, error) => complete(self, core.exitFail(error) as any)) + +/** @internal */ +export const failCause = dual< + >( + cause: Cause.Cause> + ) => (self: A) => Effect.Effect, + >( + self: A, + cause: Cause.Cause> + ) => Effect.Effect +>(2, (self, cause) => complete(self, core.exitFailCause(cause) as any)) + +/** @internal */ +export const succeed = dual< + >( + value: Request.Request.Success + ) => (self: A) => Effect.Effect, + >( + self: A, + value: Request.Request.Success + ) => Effect.Effect +>(2, (self, value) => complete(self, core.exitSucceed(value) as any)) + +/** @internal */ +export class Listeners { + count = 0 + observers: Set<(count: number) => void> = new Set() + interrupted = false + addObserver(f: (count: number) => void): void { + this.observers.add(f) + } + removeObserver(f: (count: number) => void): void { + this.observers.delete(f) + } + increment() { + this.count++ + this.observers.forEach((f) => f(this.count)) + } + decrement() { + this.count-- + this.observers.forEach((f) => f(this.count)) + } +} + +/** + * @internal + */ +export const filterOutCompleted = >(requests: Array) => + core.fiberRefGetWith( + completedRequestMap.currentRequestMap, + (map) => + core.succeed( + requests.filter((request) => !(map.get(request)?.state.completed === true)) + ) + ) diff --git a/backend/node_modules/effect/src/internal/resource.ts b/backend/node_modules/effect/src/internal/resource.ts new file mode 100644 index 0000000000000000000000000000000000000000..bb57c5c97f51ee0e81a6c5eb32af0f3779094f91 --- /dev/null +++ b/backend/node_modules/effect/src/internal/resource.ts @@ -0,0 +1,76 @@ +import type * as Effect from "../Effect.js" +import { identity, pipe } from "../Function.js" +import type * as Resource from "../Resource.js" +import type * as Schedule from "../Schedule.js" +import type * as Scope from "../Scope.js" +import * as core from "./core.js" +import * as effectable from "./effectable.js" +import * as fiberRuntime from "./fiberRuntime.js" +import * as schedule_ from "./schedule.js" +import * as scopedRef from "./scopedRef.js" + +/** @internal */ +const ResourceSymbolKey = "effect/Resource" + +/** @internal */ +export const ResourceTypeId: Resource.ResourceTypeId = Symbol.for( + ResourceSymbolKey +) as Resource.ResourceTypeId + +const resourceVariance = { + /* c8 ignore next */ + _E: (_: any) => _, + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +const proto: ThisType> = { + ...effectable.CommitPrototype, + commit() { + return get(this) + }, + [ResourceTypeId]: resourceVariance +} + +/** @internal */ +export const auto = ( + acquire: Effect.Effect, + policy: Schedule.Schedule +): Effect.Effect, never, R | R2 | Scope.Scope> => + core.tap(manual(acquire), (manual) => + fiberRuntime.acquireRelease( + pipe( + refresh(manual), + schedule_.schedule_Effect(policy), + core.interruptible, + fiberRuntime.forkDaemon + ), + core.interruptFiber + )) + +/** @internal */ +export const manual = ( + acquire: Effect.Effect +): Effect.Effect, never, R | Scope.Scope> => + core.flatMap(core.context(), (env) => + pipe( + scopedRef.fromAcquire(core.exit(acquire)), + core.map((ref) => { + const resource = Object.create(proto) + resource.scopedRef = ref + resource.acquire = core.provideContext(acquire, env) + return resource + }) + )) + +/** @internal */ +export const get = (self: Resource.Resource): Effect.Effect => + core.flatMap(scopedRef.get(self.scopedRef), identity) + +/** @internal */ +export const refresh = (self: Resource.Resource): Effect.Effect => + scopedRef.set( + self.scopedRef, + core.map(self.acquire, core.exitSucceed) + ) diff --git a/backend/node_modules/effect/src/internal/ringBuffer.ts b/backend/node_modules/effect/src/internal/ringBuffer.ts new file mode 100644 index 0000000000000000000000000000000000000000..93a127421d9176d1e9c62a9eeb0ebd361b072c70 --- /dev/null +++ b/backend/node_modules/effect/src/internal/ringBuffer.ts @@ -0,0 +1,68 @@ +import * as Chunk from "../Chunk.js" +import { constUndefined } from "../Function.js" +import * as Option from "../Option.js" + +/** @internal */ +export class RingBuffer { + private array: Array + private size = 0 + private current = 0 + + constructor(public readonly capacity: number) { + this.array = Array.from({ length: capacity }, constUndefined) + } + + head(): Option.Option { + return Option.fromNullable(this.array[this.current]) + } + + lastOrNull(): A | undefined { + if (this.size === 0) { + return undefined + } + + const index = this.current === 0 ? this.array.length - 1 : this.current - 1 + + return this.array[index] ?? undefined + } + + put(value: A): void { + this.array[this.current] = value + this.increment() + } + + dropLast(): void { + if (this.size > 0) { + this.decrement() + this.array[this.current] = undefined + } + } + + toChunk(): Chunk.Chunk { + const begin = this.current - this.size + const newArray = begin < 0 + ? [ + ...this.array.slice(this.capacity + begin, this.capacity), + ...this.array.slice(0, this.current) + ] + : this.array.slice(begin, this.current) + + return Chunk.fromIterable(newArray) as Chunk.Chunk + } + + private increment(): void { + if (this.size < this.capacity) { + this.size += 1 + } + this.current = (this.current + 1) % this.capacity + } + + private decrement(): void { + this.size -= 1 + if (this.current > 0) { + this.current -= 1 + } else { + this.current = this.capacity - 1 + } + } +} diff --git a/backend/node_modules/effect/src/internal/runtime.ts b/backend/node_modules/effect/src/internal/runtime.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd4b69bf2d07d681f197118bbced7bd82f4c8d0e --- /dev/null +++ b/backend/node_modules/effect/src/internal/runtime.ts @@ -0,0 +1,558 @@ +import type * as ReadonlyArray from "../Array.js" +import type * as Cause from "../Cause.js" +import * as Context from "../Context.js" +import type * as Effect from "../Effect.js" +import { equals } from "../Equal.js" +import * as Exit from "../Exit.js" +import * as Fiber from "../Fiber.js" +import * as FiberId from "../FiberId.js" +import type * as FiberRef from "../FiberRef.js" +import * as FiberRefs from "../FiberRefs.js" +import { dual, pipe } from "../Function.js" +import * as Inspectable from "../Inspectable.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import * as Predicate from "../Predicate.js" +import type * as Runtime from "../Runtime.js" +import type * as RuntimeFlags from "../RuntimeFlags.js" +import * as scheduler_ from "../Scheduler.js" +import * as scope_ from "../Scope.js" +import * as InternalCause from "./cause.js" +import * as core from "./core.js" +import * as executionStrategy from "./executionStrategy.js" +import * as FiberRuntime from "./fiberRuntime.js" +import * as fiberScope from "./fiberScope.js" +import * as OpCodes from "./opCodes/effect.js" +import * as runtimeFlags from "./runtimeFlags.js" +import * as supervisor_ from "./supervisor.js" + +const makeDual = , Return>( + f: (runtime: Runtime.Runtime, effect: Effect.Effect, ...args: Args) => Return +): { + (runtime: Runtime.Runtime): (effect: Effect.Effect, ...args: Args) => Return + (runtime: Runtime.Runtime, effect: Effect.Effect, ...args: Args): Return +} => + function(this: any) { + if (arguments.length === 1) { + const runtime = arguments[0] + return (effect: any, ...args: Args) => f(runtime, effect, ...args) + } + return f.apply(this, arguments as any) + } as any + +/** @internal */ +export const unsafeFork: { + (runtime: Runtime.Runtime): ( + effect: Effect.Effect, + options?: Runtime.RunForkOptions | undefined + ) => Fiber.RuntimeFiber + ( + runtime: Runtime.Runtime, + effect: Effect.Effect, + options?: Runtime.RunForkOptions | undefined + ): Fiber.RuntimeFiber +} = makeDual(( + runtime: Runtime.Runtime, + self: Effect.Effect, + options?: Runtime.RunForkOptions +): Fiber.RuntimeFiber => { + const fiberId = FiberId.unsafeMake() + const fiberRefUpdates: ReadonlyArray.NonEmptyArray< + readonly [FiberRef.FiberRef, ReadonlyArray.NonEmptyReadonlyArray] + > = [[core.currentContext, [[fiberId, runtime.context]]]] + + if (options?.scheduler) { + fiberRefUpdates.push([scheduler_.currentScheduler, [[fiberId, options.scheduler]]]) + } + + let fiberRefs = FiberRefs.updateManyAs(runtime.fiberRefs, { + entries: fiberRefUpdates, + forkAs: fiberId + }) + + if (options?.updateRefs) { + fiberRefs = options.updateRefs(fiberRefs, fiberId) + } + + const fiberRuntime: FiberRuntime.FiberRuntime = new FiberRuntime.FiberRuntime( + fiberId, + fiberRefs, + runtime.runtimeFlags + ) + + let effect: Effect.Effect = self + + if (options?.scope) { + effect = core.flatMap( + scope_.fork(options.scope, executionStrategy.sequential), + (closeableScope) => + core.zipRight( + core.scopeAddFinalizer( + closeableScope, + core.fiberIdWith((id) => + equals(id, fiberRuntime.id()) ? core.void : core.interruptAsFiber(fiberRuntime, id) + ) + ), + core.onExit(self, (exit) => scope_.close(closeableScope, exit)) + ) + ) + } + + const supervisor = fiberRuntime.currentSupervisor + + // we can compare by reference here as _supervisor.none is wrapped with globalValue + if (supervisor !== supervisor_.none) { + supervisor.onStart(runtime.context, effect, Option.none(), fiberRuntime) + + fiberRuntime.addObserver((exit) => supervisor.onEnd(exit, fiberRuntime)) + } + + fiberScope.globalScope.add(runtime.runtimeFlags, fiberRuntime) + + // Only an explicit false will prevent immediate execution + if (options?.immediate === false) { + fiberRuntime.resume(effect) + } else { + fiberRuntime.start(effect) + } + + return fiberRuntime +}) + +/** @internal */ +export const unsafeRunCallback: { + (runtime: Runtime.Runtime): ( + effect: Effect.Effect, + options?: Runtime.RunCallbackOptions | undefined + ) => (fiberId?: FiberId.FiberId, options?: Runtime.RunCallbackOptions | undefined) => void + ( + runtime: Runtime.Runtime, + effect: Effect.Effect, + options?: Runtime.RunCallbackOptions | undefined + ): (fiberId?: FiberId.FiberId, options?: Runtime.RunCallbackOptions | undefined) => void +} = makeDual(( + runtime, + effect, + options: Runtime.RunCallbackOptions = {} +): (fiberId?: FiberId.FiberId, options?: Runtime.RunCallbackOptions | undefined) => void => { + const fiberRuntime = unsafeFork(runtime, effect, options) + + if (options.onExit) { + fiberRuntime.addObserver((exit) => { + options.onExit!(exit) + }) + } + + return (id, cancelOptions) => + unsafeRunCallback(runtime)( + pipe(fiberRuntime, Fiber.interruptAs(id ?? FiberId.none)), + { + ...cancelOptions, + onExit: cancelOptions?.onExit + ? (exit) => cancelOptions.onExit!(Exit.flatten(exit)) + : undefined + } + ) +}) + +/** @internal */ +export const unsafeRunSync: { + (runtime: Runtime.Runtime, effect: Effect.Effect): A + (runtime: Runtime.Runtime): (effect: Effect.Effect) => A +} = makeDual((runtime, effect) => { + const result = unsafeRunSyncExit(runtime)(effect) + if (result._tag === "Failure") { + throw fiberFailure(result.effect_instruction_i0) + } + return result.effect_instruction_i0 +}) + +class AsyncFiberExceptionImpl extends Error implements Runtime.AsyncFiberException { + readonly _tag = "AsyncFiberException" + constructor(readonly fiber: Fiber.RuntimeFiber) { + super( + `Fiber #${fiber.id().id} cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work` + ) + this.name = this._tag + this.stack = this.message + } +} + +const asyncFiberException = (fiber: Fiber.RuntimeFiber): Runtime.AsyncFiberException => { + const limit = Error.stackTraceLimit + Error.stackTraceLimit = 0 + const error = new AsyncFiberExceptionImpl(fiber) + Error.stackTraceLimit = limit + return error +} + +/** @internal */ +export const isAsyncFiberException = (u: unknown): u is Runtime.AsyncFiberException => + Predicate.isTagged(u, "AsyncFiberException") && "fiber" in u + +/** @internal */ +export const FiberFailureId: Runtime.FiberFailureId = Symbol.for("effect/Runtime/FiberFailure") as any +/** @internal */ +export const FiberFailureCauseId: Runtime.FiberFailureCauseId = Symbol.for( + "effect/Runtime/FiberFailure/Cause" +) as any + +class FiberFailureImpl extends Error implements Runtime.FiberFailure { + readonly [FiberFailureId]: Runtime.FiberFailureId + readonly [FiberFailureCauseId]: Cause.Cause + constructor(cause: Cause.Cause) { + const head = InternalCause.prettyErrors(cause)[0] + + super(head?.message || "An error has occurred") + this[FiberFailureId] = FiberFailureId + this[FiberFailureCauseId] = cause + + this.name = head ? `(FiberFailure) ${head.name}` : "FiberFailure" + if (head?.stack) { + this.stack = head.stack + } + } + + toJSON(): unknown { + return { + _id: "FiberFailure", + cause: this[FiberFailureCauseId].toJSON() + } + } + + toString(): string { + return "(FiberFailure) " + InternalCause.pretty(this[FiberFailureCauseId], { renderErrorCause: true }) + } + [Inspectable.NodeInspectSymbol](): unknown { + return this.toString() + } +} + +/** @internal */ +export const fiberFailure = (cause: Cause.Cause): Runtime.FiberFailure => { + const limit = Error.stackTraceLimit + Error.stackTraceLimit = 0 + const error = new FiberFailureImpl(cause) + Error.stackTraceLimit = limit + return error +} + +/** @internal */ +export const isFiberFailure = (u: unknown): u is Runtime.FiberFailure => Predicate.hasProperty(u, FiberFailureId) + +const fastPath = (effect: Effect.Effect): Exit.Exit | undefined => { + const op = effect as core.Primitive + switch (op._op) { + case "Failure": + case "Success": { + // @ts-expect-error + return op + } + case "Left": { + return core.exitFail(op.left) + } + case "Right": { + return core.exitSucceed(op.right) + } + case "Some": { + return core.exitSucceed(op.value) + } + case "None": { + // @ts-expect-error + return core.exitFail(new core.NoSuchElementException()) + } + } +} + +/** @internal */ +export const unsafeRunSyncExit: { + (runtime: Runtime.Runtime, effect: Effect.Effect): Exit.Exit + (runtime: Runtime.Runtime): (effect: Effect.Effect) => Exit.Exit +} = makeDual((runtime, effect) => { + const op = fastPath(effect) + if (op) { + return op + } + const scheduler = new scheduler_.SyncScheduler() + const fiberRuntime = unsafeFork(runtime)(effect, { scheduler }) + scheduler.flush() + const result = fiberRuntime.unsafePoll() + if (result) { + return result + } + return core.exitDie(core.capture(asyncFiberException(fiberRuntime), core.currentSpanFromFiber(fiberRuntime))) +}) + +/** @internal */ +export const unsafeRunPromise: { + (runtime: Runtime.Runtime): ( + effect: Effect.Effect, + options?: { + readonly signal?: AbortSignal | undefined + } | undefined + ) => Promise + ( + runtime: Runtime.Runtime, + effect: Effect.Effect, + options?: { + readonly signal?: AbortSignal | undefined + } | undefined + ): Promise +} = makeDual(( + runtime, + effect, + options?: { + readonly signal?: AbortSignal | undefined + } | undefined +) => + unsafeRunPromiseExit(runtime, effect, options).then((result) => { + switch (result._tag) { + case OpCodes.OP_SUCCESS: { + return result.effect_instruction_i0 + } + case OpCodes.OP_FAILURE: { + throw fiberFailure(result.effect_instruction_i0) + } + } + }) +) + +/** @internal */ +export const unsafeRunPromiseExit: { + ( + runtime: Runtime.Runtime + ): ( + effect: Effect.Effect, + options?: { readonly signal?: AbortSignal | undefined } | undefined + ) => Promise> + ( + runtime: Runtime.Runtime, + effect: Effect.Effect, + options?: { readonly signal?: AbortSignal | undefined } | undefined + ): Promise> +} = makeDual(( + runtime, + effect, + options?: { + readonly signal?: AbortSignal | undefined + } | undefined +) => + new Promise>((resolve) => { + const op = fastPath(effect) + if (op) { + resolve(op) + } + const fiber = unsafeFork(runtime)(effect) + fiber.addObserver((exit) => { + resolve(exit) + }) + if (options?.signal !== undefined) { + if (options.signal.aborted) { + fiber.unsafeInterruptAsFork(fiber.id()) + } else { + options.signal.addEventListener("abort", () => { + fiber.unsafeInterruptAsFork(fiber.id()) + }, { once: true }) + } + } + }) +) + +/** @internal */ +export class RuntimeImpl implements Runtime.Runtime { + constructor( + readonly context: Context.Context, + readonly runtimeFlags: RuntimeFlags.RuntimeFlags, + readonly fiberRefs: FiberRefs.FiberRefs + ) {} + + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const make = ( + options: { + readonly context: Context.Context + readonly runtimeFlags: RuntimeFlags.RuntimeFlags + readonly fiberRefs: FiberRefs.FiberRefs + } +): Runtime.Runtime => new RuntimeImpl(options.context, options.runtimeFlags, options.fiberRefs) + +/** @internal */ +export const runtime = (): Effect.Effect, never, R> => + core.withFiberRuntime((state, status) => + core.succeed( + new RuntimeImpl( + state.getFiberRef(core.currentContext as unknown as FiberRef.FiberRef>), + status.runtimeFlags, + state.getFiberRefs() + ) + ) + ) + +/** @internal */ +export const defaultRuntimeFlags: RuntimeFlags.RuntimeFlags = runtimeFlags.make( + runtimeFlags.Interruption, + runtimeFlags.CooperativeYielding, + runtimeFlags.RuntimeMetrics +) + +/** @internal */ +export const defaultRuntime = make({ + context: Context.empty(), + runtimeFlags: defaultRuntimeFlags, + fiberRefs: FiberRefs.empty() +}) + +/** @internal */ +export const updateRuntimeFlags: { + ( + f: (flags: RuntimeFlags.RuntimeFlags) => RuntimeFlags.RuntimeFlags + ): (self: Runtime.Runtime) => Runtime.Runtime + (self: Runtime.Runtime, f: (flags: RuntimeFlags.RuntimeFlags) => RuntimeFlags.RuntimeFlags): Runtime.Runtime +} = dual( + 2, + (self: Runtime.Runtime, f: (flags: RuntimeFlags.RuntimeFlags) => RuntimeFlags.RuntimeFlags) => + make({ + context: self.context, + runtimeFlags: f(self.runtimeFlags), + fiberRefs: self.fiberRefs + }) +) + +/** @internal */ +export const disableRuntimeFlag: { + (flag: RuntimeFlags.RuntimeFlag): (self: Runtime.Runtime) => Runtime.Runtime + (self: Runtime.Runtime, flag: RuntimeFlags.RuntimeFlag): Runtime.Runtime +} = dual( + 2, + (self: Runtime.Runtime, flag: RuntimeFlags.RuntimeFlag) => updateRuntimeFlags(self, runtimeFlags.disable(flag)) +) + +/** @internal */ +export const enableRuntimeFlag: { + (flag: RuntimeFlags.RuntimeFlag): (self: Runtime.Runtime) => Runtime.Runtime + (self: Runtime.Runtime, flag: RuntimeFlags.RuntimeFlag): Runtime.Runtime +} = dual( + 2, + (self: Runtime.Runtime, flag: RuntimeFlags.RuntimeFlag) => updateRuntimeFlags(self, runtimeFlags.enable(flag)) +) + +/** @internal */ +export const updateContext: { + (f: (context: Context.Context) => Context.Context): (self: Runtime.Runtime) => Runtime.Runtime + (self: Runtime.Runtime, f: (context: Context.Context) => Context.Context): Runtime.Runtime +} = dual( + 2, + (self: Runtime.Runtime, f: (context: Context.Context) => Context.Context) => + make({ + context: f(self.context), + runtimeFlags: self.runtimeFlags, + fiberRefs: self.fiberRefs + }) +) + +/** @internal */ +export const provideService: { + (tag: Context.Tag, service: S): (self: Runtime.Runtime) => Runtime.Runtime + (self: Runtime.Runtime, tag: Context.Tag, service: S): Runtime.Runtime +} = dual( + 3, + (self: Runtime.Runtime, tag: Context.Tag, service: S) => + updateContext(self, Context.add(tag, service)) +) + +/** @internal */ +export const updateFiberRefs: { + (f: (fiberRefs: FiberRefs.FiberRefs) => FiberRefs.FiberRefs): (self: Runtime.Runtime) => Runtime.Runtime + (self: Runtime.Runtime, f: (fiberRefs: FiberRefs.FiberRefs) => FiberRefs.FiberRefs): Runtime.Runtime +} = dual( + 2, + (self: Runtime.Runtime, f: (fiberRefs: FiberRefs.FiberRefs) => FiberRefs.FiberRefs): Runtime.Runtime => + make({ + context: self.context, + runtimeFlags: self.runtimeFlags, + fiberRefs: f(self.fiberRefs) + }) +) + +/** @internal */ +export const setFiberRef: { + (fiberRef: FiberRef.FiberRef, value: A): (self: Runtime.Runtime) => Runtime.Runtime + (self: Runtime.Runtime, fiberRef: FiberRef.FiberRef, value: A): Runtime.Runtime +} = dual( + 3, + (self: Runtime.Runtime, fiberRef: FiberRef.FiberRef, value: A): Runtime.Runtime => + updateFiberRefs( + self, + FiberRefs.updateAs({ + fiberId: FiberId.none, + fiberRef, + value + }) + ) +) + +/** @internal */ +export const deleteFiberRef: { + (fiberRef: FiberRef.FiberRef): (self: Runtime.Runtime) => Runtime.Runtime + (self: Runtime.Runtime, fiberRef: FiberRef.FiberRef): Runtime.Runtime +} = dual( + 2, + (self: Runtime.Runtime, fiberRef: FiberRef.FiberRef): Runtime.Runtime => + updateFiberRefs(self, FiberRefs.delete(fiberRef)) +) + +/** @internal */ +export const unsafeRunEffect = unsafeRunCallback(defaultRuntime) + +/** @internal */ +export const unsafeForkEffect = unsafeFork(defaultRuntime) + +/** @internal */ +export const unsafeRunPromiseEffect = unsafeRunPromise(defaultRuntime) + +/** @internal */ +export const unsafeRunPromiseExitEffect = unsafeRunPromiseExit(defaultRuntime) + +/** @internal */ +export const unsafeRunSyncEffect = unsafeRunSync(defaultRuntime) + +/** @internal */ +export const unsafeRunSyncExitEffect = unsafeRunSyncExit(defaultRuntime) + +// circular with Effect + +/** @internal */ +export const asyncEffect = ( + register: ( + callback: (_: Effect.Effect) => void + ) => Effect.Effect | void, E2, R2> +): Effect.Effect => + core.suspend(() => { + let cleanup: Effect.Effect | void = undefined + return core.flatMap( + core.deferredMake(), + (deferred) => + core.flatMap(runtime(), (runtime) => + core.uninterruptibleMask((restore) => + core.zipRight( + FiberRuntime.fork(restore( + core.matchCauseEffect( + register((cb) => unsafeRunCallback(runtime)(core.intoDeferred(cb, deferred))), + { + onFailure: (cause) => core.deferredFailCause(deferred, cause), + onSuccess: (cleanup_) => { + cleanup = cleanup_ + return core.void + } + } + ) + )), + restore(core.onInterrupt(core.deferredAwait(deferred), () => cleanup ?? core.void)) + ) + )) + ) + }) diff --git a/backend/node_modules/effect/src/internal/runtimeFlags.ts b/backend/node_modules/effect/src/internal/runtimeFlags.ts new file mode 100644 index 0000000000000000000000000000000000000000..c76ee36c5c84ce05915707bcea2971a9424c6777 --- /dev/null +++ b/backend/node_modules/effect/src/internal/runtimeFlags.ts @@ -0,0 +1,178 @@ +import type * as Differ from "../Differ.js" +import { dual } from "../Function.js" +import type * as RuntimeFlags from "../RuntimeFlags.js" +import type * as RuntimeFlagsPatch from "../RuntimeFlagsPatch.js" +import * as internalDiffer from "./differ.js" +import * as runtimeFlagsPatch from "./runtimeFlagsPatch.js" + +/** @internal */ +export const None: RuntimeFlags.RuntimeFlag = 0 as RuntimeFlags.RuntimeFlag + +/** @internal */ +export const Interruption: RuntimeFlags.RuntimeFlag = 1 << 0 as RuntimeFlags.RuntimeFlag + +/** @internal */ +export const OpSupervision: RuntimeFlags.RuntimeFlag = 1 << 1 as RuntimeFlags.RuntimeFlag + +/** @internal */ +export const RuntimeMetrics: RuntimeFlags.RuntimeFlag = 1 << 2 as RuntimeFlags.RuntimeFlag + +/** @internal */ +export const WindDown: RuntimeFlags.RuntimeFlag = 1 << 4 as RuntimeFlags.RuntimeFlag + +/** @internal */ +export const CooperativeYielding: RuntimeFlags.RuntimeFlag = 1 << 5 as RuntimeFlags.RuntimeFlag + +/** @internal */ +export const allFlags: ReadonlyArray = [ + None, + Interruption, + OpSupervision, + RuntimeMetrics, + WindDown, + CooperativeYielding +] + +const print = (flag: RuntimeFlags.RuntimeFlag) => { + switch (flag) { + case CooperativeYielding: { + return "CooperativeYielding" + } + case WindDown: { + return "WindDown" + } + case RuntimeMetrics: { + return "RuntimeMetrics" + } + case OpSupervision: { + return "OpSupervision" + } + case Interruption: { + return "Interruption" + } + case None: { + return "None" + } + } +} + +/** @internal */ +export const cooperativeYielding = (self: RuntimeFlags.RuntimeFlags): boolean => isEnabled(self, CooperativeYielding) + +/** @internal */ +export const disable = dual< + (flag: RuntimeFlags.RuntimeFlag) => (self: RuntimeFlags.RuntimeFlags) => RuntimeFlags.RuntimeFlags, + (self: RuntimeFlags.RuntimeFlags, flag: RuntimeFlags.RuntimeFlag) => RuntimeFlags.RuntimeFlags +>(2, (self, flag) => (self & ~flag) as RuntimeFlags.RuntimeFlags) + +/** @internal */ +export const disableAll = dual< + (flags: RuntimeFlags.RuntimeFlags) => (self: RuntimeFlags.RuntimeFlags) => RuntimeFlags.RuntimeFlags, + (self: RuntimeFlags.RuntimeFlags, flags: RuntimeFlags.RuntimeFlags) => RuntimeFlags.RuntimeFlags +>(2, (self, flags) => (self & ~flags) as RuntimeFlags.RuntimeFlags) + +/** @internal */ +export const enable = dual< + (flag: RuntimeFlags.RuntimeFlag) => (self: RuntimeFlags.RuntimeFlags) => RuntimeFlags.RuntimeFlags, + (self: RuntimeFlags.RuntimeFlags, flag: RuntimeFlags.RuntimeFlag) => RuntimeFlags.RuntimeFlags +>(2, (self, flag) => (self | flag) as RuntimeFlags.RuntimeFlags) + +/** @internal */ +export const enableAll = dual< + (flags: RuntimeFlags.RuntimeFlags) => (self: RuntimeFlags.RuntimeFlags) => RuntimeFlags.RuntimeFlags, + (self: RuntimeFlags.RuntimeFlags, flags: RuntimeFlags.RuntimeFlags) => RuntimeFlags.RuntimeFlags +>(2, (self, flags) => (self | flags) as RuntimeFlags.RuntimeFlags) + +/** @internal */ +export const interruptible = (self: RuntimeFlags.RuntimeFlags): boolean => interruption(self) && !windDown(self) + +/** @internal */ +export const interruption = (self: RuntimeFlags.RuntimeFlags): boolean => isEnabled(self, Interruption) + +/** @internal */ +export const isDisabled = dual< + (flag: RuntimeFlags.RuntimeFlag) => (self: RuntimeFlags.RuntimeFlags) => boolean, + (self: RuntimeFlags.RuntimeFlags, flag: RuntimeFlags.RuntimeFlag) => boolean +>(2, (self, flag) => !isEnabled(self, flag)) + +/** @internal */ +export const isEnabled = dual< + (flag: RuntimeFlags.RuntimeFlag) => (self: RuntimeFlags.RuntimeFlags) => boolean, + (self: RuntimeFlags.RuntimeFlags, flag: RuntimeFlags.RuntimeFlag) => boolean +>(2, (self, flag) => (self & flag) !== 0) + +/** @internal */ +export const make = (...flags: ReadonlyArray): RuntimeFlags.RuntimeFlags => + flags.reduce((a, b) => a | b, 0) as RuntimeFlags.RuntimeFlags + +/** @internal */ +export const none: RuntimeFlags.RuntimeFlags = make(None) + +/** @internal */ +export const opSupervision = (self: RuntimeFlags.RuntimeFlags): boolean => isEnabled(self, OpSupervision) + +/** @internal */ +export const render = (self: RuntimeFlags.RuntimeFlags): string => { + const active: Array = [] + allFlags.forEach((flag) => { + if (isEnabled(self, flag)) { + active.push(`${print(flag)}`) + } + }) + return `RuntimeFlags(${active.join(", ")})` +} + +/** @internal */ +export const runtimeMetrics = (self: RuntimeFlags.RuntimeFlags): boolean => isEnabled(self, RuntimeMetrics) + +/** @internal */ +export const toSet = (self: RuntimeFlags.RuntimeFlags): ReadonlySet => + new Set(allFlags.filter((flag) => isEnabled(self, flag))) + +export const windDown = (self: RuntimeFlags.RuntimeFlags): boolean => isEnabled(self, WindDown) + +// circular with RuntimeFlagsPatch + +/** @internal */ +export const enabledSet = (self: RuntimeFlagsPatch.RuntimeFlagsPatch): ReadonlySet => + toSet((runtimeFlagsPatch.active(self) & runtimeFlagsPatch.enabled(self)) as RuntimeFlags.RuntimeFlags) + +/** @internal */ +export const disabledSet = (self: RuntimeFlagsPatch.RuntimeFlagsPatch): ReadonlySet => + toSet((runtimeFlagsPatch.active(self) & ~runtimeFlagsPatch.enabled(self)) as RuntimeFlags.RuntimeFlags) + +/** @internal */ +export const diff = dual< + (that: RuntimeFlags.RuntimeFlags) => (self: RuntimeFlags.RuntimeFlags) => RuntimeFlagsPatch.RuntimeFlagsPatch, + (self: RuntimeFlags.RuntimeFlags, that: RuntimeFlags.RuntimeFlags) => RuntimeFlagsPatch.RuntimeFlagsPatch +>(2, (self, that) => runtimeFlagsPatch.make(self ^ that, that)) + +/** @internal */ +export const patch = dual< + (patch: RuntimeFlagsPatch.RuntimeFlagsPatch) => (self: RuntimeFlags.RuntimeFlags) => RuntimeFlags.RuntimeFlags, + (self: RuntimeFlags.RuntimeFlags, patch: RuntimeFlagsPatch.RuntimeFlagsPatch) => RuntimeFlags.RuntimeFlags +>(2, (self, patch) => + ( + (self & (runtimeFlagsPatch.invert(runtimeFlagsPatch.active(patch)) | runtimeFlagsPatch.enabled(patch))) | + (runtimeFlagsPatch.active(patch) & runtimeFlagsPatch.enabled(patch)) + ) as RuntimeFlags.RuntimeFlags) + +/** @internal */ +export const renderPatch = (self: RuntimeFlagsPatch.RuntimeFlagsPatch): string => { + const enabled = Array.from(enabledSet(self)) + .map((flag) => print(flag)) + .join(", ") + const disabled = Array.from(disabledSet(self)) + .map((flag) => print(flag)) + .join(", ") + return `RuntimeFlagsPatch(enabled = (${enabled}), disabled = (${disabled}))` +} + +/** @internal */ +export const differ: Differ.Differ = internalDiffer + .make({ + empty: runtimeFlagsPatch.empty, + diff: (oldValue, newValue) => diff(oldValue, newValue), + combine: (first, second) => runtimeFlagsPatch.andThen(second)(first), + patch: (_patch, oldValue) => patch(oldValue, _patch) + }) diff --git a/backend/node_modules/effect/src/internal/runtimeFlagsPatch.ts b/backend/node_modules/effect/src/internal/runtimeFlagsPatch.ts new file mode 100644 index 0000000000000000000000000000000000000000..ed28cc56ed60a9113080bb07462e86176b527865 --- /dev/null +++ b/backend/node_modules/effect/src/internal/runtimeFlagsPatch.ts @@ -0,0 +1,103 @@ +import { dual } from "../Function.js" +import type * as RuntimeFlags from "../RuntimeFlags.js" +import type * as RuntimeFlagsPatch from "../RuntimeFlagsPatch.js" + +/** @internal */ +const BIT_MASK = 0xff + +/** @internal */ +const BIT_SHIFT = 0x08 + +/** @internal */ +export const active = (patch: RuntimeFlagsPatch.RuntimeFlagsPatch): number => patch & BIT_MASK + +/** @internal */ +export const enabled = (patch: RuntimeFlagsPatch.RuntimeFlagsPatch): number => (patch >> BIT_SHIFT) & BIT_MASK + +/** @internal */ +export const make = (active: number, enabled: number): RuntimeFlagsPatch.RuntimeFlagsPatch => + ((active & BIT_MASK) + (((enabled & active) & BIT_MASK) << BIT_SHIFT)) as RuntimeFlagsPatch.RuntimeFlagsPatch + +/** @internal */ +export const empty = make(0, 0) + +/** @internal */ +export const enable = (flag: RuntimeFlags.RuntimeFlag): RuntimeFlagsPatch.RuntimeFlagsPatch => make(flag, flag) + +/** @internal */ +export const disable = (flag: RuntimeFlags.RuntimeFlag): RuntimeFlagsPatch.RuntimeFlagsPatch => make(flag, 0) + +/** @internal */ +export const isEmpty = (patch: RuntimeFlagsPatch.RuntimeFlagsPatch): boolean => patch === 0 + +/** @internal */ +export const isActive = dual< + (flag: RuntimeFlagsPatch.RuntimeFlagsPatch) => (self: RuntimeFlagsPatch.RuntimeFlagsPatch) => boolean, + (self: RuntimeFlagsPatch.RuntimeFlagsPatch, flag: RuntimeFlagsPatch.RuntimeFlagsPatch) => boolean +>(2, (self, flag) => (active(self) & flag) !== 0) + +/** @internal */ +export const isEnabled = dual< + (flag: RuntimeFlags.RuntimeFlag) => (self: RuntimeFlagsPatch.RuntimeFlagsPatch) => boolean, + (self: RuntimeFlagsPatch.RuntimeFlagsPatch, flag: RuntimeFlags.RuntimeFlag) => boolean +>(2, (self, flag) => (enabled(self) & flag) !== 0) + +/** @internal */ +export const isDisabled = dual< + (flag: RuntimeFlags.RuntimeFlag) => (self: RuntimeFlagsPatch.RuntimeFlagsPatch) => boolean, + (self: RuntimeFlagsPatch.RuntimeFlagsPatch, flag: RuntimeFlags.RuntimeFlag) => boolean +>(2, (self, flag) => ((active(self) & flag) !== 0) && ((enabled(self) & flag) === 0)) + +/** @internal */ +export const exclude = dual< + ( + flag: RuntimeFlags.RuntimeFlag + ) => (self: RuntimeFlagsPatch.RuntimeFlagsPatch) => RuntimeFlagsPatch.RuntimeFlagsPatch, + (self: RuntimeFlagsPatch.RuntimeFlagsPatch, flag: RuntimeFlags.RuntimeFlag) => RuntimeFlagsPatch.RuntimeFlagsPatch +>(2, (self, flag) => make(active(self) & ~flag, enabled(self))) + +/** @internal */ +export const both = dual< + ( + that: RuntimeFlagsPatch.RuntimeFlagsPatch + ) => ( + self: RuntimeFlagsPatch.RuntimeFlagsPatch + ) => RuntimeFlagsPatch.RuntimeFlagsPatch, + ( + self: RuntimeFlagsPatch.RuntimeFlagsPatch, + that: RuntimeFlagsPatch.RuntimeFlagsPatch + ) => RuntimeFlagsPatch.RuntimeFlagsPatch +>(2, (self, that) => make(active(self) | active(that), enabled(self) & enabled(that))) + +/** @internal */ +export const either = dual< + ( + that: RuntimeFlagsPatch.RuntimeFlagsPatch + ) => ( + self: RuntimeFlagsPatch.RuntimeFlagsPatch + ) => RuntimeFlagsPatch.RuntimeFlagsPatch, + ( + self: RuntimeFlagsPatch.RuntimeFlagsPatch, + that: RuntimeFlagsPatch.RuntimeFlagsPatch + ) => RuntimeFlagsPatch.RuntimeFlagsPatch +>(2, (self, that) => make(active(self) | active(that), enabled(self) | enabled(that))) + +/** @internal */ +export const andThen = dual< + ( + that: RuntimeFlagsPatch.RuntimeFlagsPatch + ) => ( + self: RuntimeFlagsPatch.RuntimeFlagsPatch + ) => RuntimeFlagsPatch.RuntimeFlagsPatch, + ( + self: RuntimeFlagsPatch.RuntimeFlagsPatch, + that: RuntimeFlagsPatch.RuntimeFlagsPatch + ) => RuntimeFlagsPatch.RuntimeFlagsPatch +>(2, (self, that) => (self | that) as RuntimeFlagsPatch.RuntimeFlagsPatch) + +/** @internal */ +export const inverse = (patch: RuntimeFlagsPatch.RuntimeFlagsPatch): RuntimeFlagsPatch.RuntimeFlagsPatch => + make(enabled(patch), invert(active(patch))) + +/** @internal */ +export const invert = (n: number): number => (~n >>> 0) & BIT_MASK diff --git a/backend/node_modules/effect/src/internal/schedule.ts b/backend/node_modules/effect/src/internal/schedule.ts new file mode 100644 index 0000000000000000000000000000000000000000..4c54b1d3d1e372a9dc4db6bcac095c9619c9aad1 --- /dev/null +++ b/backend/node_modules/effect/src/internal/schedule.ts @@ -0,0 +1,2199 @@ +import type * as Cause from "../Cause.js" +import * as Chunk from "../Chunk.js" +import * as Clock from "../Clock.js" +import * as Context from "../Context.js" +import * as Cron from "../Cron.js" +import type * as DateTime from "../DateTime.js" +import * as Duration from "../Duration.js" +import type * as Effect from "../Effect.js" +import * as Either from "../Either.js" +import * as Equal from "../Equal.js" +import type * as Fiber from "../Fiber.js" +import type { LazyArg } from "../Function.js" +import { constVoid, dual, pipe } from "../Function.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty, type Predicate } from "../Predicate.js" +import * as Random from "../Random.js" +import type * as Ref from "../Ref.js" +import type * as Schedule from "../Schedule.js" +import * as ScheduleDecision from "../ScheduleDecision.js" +import * as Interval from "../ScheduleInterval.js" +import * as Intervals from "../ScheduleIntervals.js" +import type { Scope } from "../Scope.js" +import type * as Types from "../Types.js" +import * as internalCause from "./cause.js" +import * as effect from "./core-effect.js" +import * as core from "./core.js" +import { forkScoped } from "./effect/circular.js" +import * as ref from "./ref.js" + +/** @internal */ +const ScheduleSymbolKey = "effect/Schedule" + +/** @internal */ +export const ScheduleTypeId: Schedule.ScheduleTypeId = Symbol.for( + ScheduleSymbolKey +) as Schedule.ScheduleTypeId + +/** @internal */ +export const isSchedule = (u: unknown): u is Schedule.Schedule => + hasProperty(u, ScheduleTypeId) + +/** @internal */ +const ScheduleDriverSymbolKey = "effect/ScheduleDriver" + +/** @internal */ +export const ScheduleDriverTypeId: Schedule.ScheduleDriverTypeId = Symbol.for( + ScheduleDriverSymbolKey +) as Schedule.ScheduleDriverTypeId + +/** @internal */ +const defaultIterationMetadata: Schedule.IterationMetadata = { + start: 0, + now: 0, + input: undefined, + output: undefined, + elapsed: Duration.zero, + elapsedSincePrevious: Duration.zero, + recurrence: 0 +} + +/** @internal */ +export const CurrentIterationMetadata = Context.Reference()( + "effect/Schedule/CurrentIterationMetadata", + { defaultValue: () => defaultIterationMetadata } +) + +const scheduleVariance = { + /* c8 ignore next */ + _Out: (_: never) => _, + /* c8 ignore next */ + _In: (_: unknown) => _, + /* c8 ignore next */ + _R: (_: never) => _ +} + +const scheduleDriverVariance = { + /* c8 ignore next */ + _Out: (_: never) => _, + /* c8 ignore next */ + _In: (_: unknown) => _, + /* c8 ignore next */ + _R: (_: never) => _ +} + +/** @internal */ +class ScheduleImpl implements Schedule.Schedule { + [ScheduleTypeId] = scheduleVariance + constructor( + readonly initial: S, + readonly step: ( + now: number, + input: In, + state: S + ) => Effect.Effect + ) { + } + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +const updateInfo = ( + iterationMetaRef: Ref.Ref, + now: number, + input: unknown, + output: unknown +) => + ref.update(iterationMetaRef, (prev) => + (prev.recurrence === 0) ? + { + now, + input, + output, + recurrence: prev.recurrence + 1, + elapsed: Duration.zero, + elapsedSincePrevious: Duration.zero, + start: now + } : + { + now, + input, + output, + recurrence: prev.recurrence + 1, + elapsed: Duration.millis(now - prev.start), + elapsedSincePrevious: Duration.millis(now - prev.now), + start: prev.start + }) + +/** @internal */ +class ScheduleDriverImpl implements Schedule.ScheduleDriver { + [ScheduleDriverTypeId] = scheduleDriverVariance + + constructor( + readonly schedule: Schedule.Schedule, + readonly ref: Ref.Ref, any]> + ) {} + + get state(): Effect.Effect { + return core.map(ref.get(this.ref), (tuple) => tuple[1]) + } + + get last(): Effect.Effect { + return core.flatMap(ref.get(this.ref), ([element, _]) => { + switch (element._tag) { + case "None": { + return core.failSync(() => new core.NoSuchElementException()) + } + case "Some": { + return core.succeed(element.value) + } + } + }) + } + + iterationMeta = ref.unsafeMake(defaultIterationMetadata) + + get reset(): Effect.Effect { + return ref.set(this.ref, [Option.none(), this.schedule.initial]).pipe( + core.zipLeft(ref.set(this.iterationMeta, defaultIterationMetadata)) + ) + } + + next(input: In): Effect.Effect, R> { + return pipe( + core.map(ref.get(this.ref), (tuple) => tuple[1]), + core.flatMap((state) => + pipe( + Clock.currentTimeMillis, + core.flatMap((now) => + pipe( + core.suspend(() => this.schedule.step(now, input, state)), + core.flatMap(([state, out, decision]) => { + const setState = ref.set(this.ref, [Option.some(out), state] as const) + if (ScheduleDecision.isDone(decision)) { + return setState.pipe( + core.zipRight(core.fail(Option.none())) + ) + } + const millis = Intervals.start(decision.intervals) - now + if (millis <= 0) { + return setState.pipe( + core.zipRight(updateInfo(this.iterationMeta, now, input, out)), + core.as(out) + ) + } + const duration = Duration.millis(millis) + return pipe( + setState, + core.zipRight(updateInfo(this.iterationMeta, now, input, out)), + core.zipRight(effect.sleep(duration)), + core.as(out) + ) + }) + ) + ) + ) + ) + ) + } +} + +/** @internal */ +export const makeWithState = ( + initial: S, + step: ( + now: number, + input: In, + state: S + ) => Effect.Effect +): Schedule.Schedule => new ScheduleImpl(initial, step) + +/** @internal */ +export const addDelay = dual< + ( + f: (out: Out) => Duration.DurationInput + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (out: Out) => Duration.DurationInput + ) => Schedule.Schedule +>(2, (self, f) => addDelayEffect(self, (out) => core.sync(() => f(out)))) + +/** @internal */ +export const addDelayEffect = dual< + ( + f: (out: Out) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (out: Out) => Effect.Effect + ) => Schedule.Schedule +>(2, (self, f) => + modifyDelayEffect(self, (out, duration) => + core.map( + f(out), + (delay) => Duration.sum(duration, Duration.decode(delay)) + ))) + +/** @internal */ +export const andThen = dual< + ( + that: Schedule.Schedule + ) => (self: Schedule.Schedule) => Schedule.Schedule< + Out | Out2, + In & In2, + R | R2 + >, + ( + self: Schedule.Schedule, + that: Schedule.Schedule + ) => Schedule.Schedule< + Out | Out2, + In & In2, + R | R2 + > +>(2, (self, that) => map(andThenEither(self, that), Either.merge)) + +/** @internal */ +export const andThenEither = dual< + ( + that: Schedule.Schedule + ) => ( + self: Schedule.Schedule + ) => Schedule.Schedule, In & In2, R | R2>, + ( + self: Schedule.Schedule, + that: Schedule.Schedule + ) => Schedule.Schedule, In & In2, R | R2> +>(2, ( + self: Schedule.Schedule, + that: Schedule.Schedule +): Schedule.Schedule, In & In2, R | R2> => + makeWithState( + [self.initial, that.initial, true as boolean] as const, + (now, input, state) => + state[2] ? + core.flatMap(self.step(now, input, state[0]), ([lState, out, decision]) => { + if (ScheduleDecision.isDone(decision)) { + return core.map(that.step(now, input, state[1]), ([rState, out, decision]) => + [ + [lState, rState, false as boolean] as const, + Either.right(out) as Either.Either, + decision as ScheduleDecision.ScheduleDecision + ] as const) + } + return core.succeed( + [ + [lState, state[1], true as boolean] as const, + Either.left(out), + decision + ] as const + ) + }) : + core.map(that.step(now, input, state[1]), ([rState, out, decision]) => + [ + [state[0], rState, false as boolean] as const, + Either.right(out) as Either.Either, + decision + ] as const) + )) + +/** @internal */ +export const as = dual< + (out: Out2) => (self: Schedule.Schedule) => Schedule.Schedule, + (self: Schedule.Schedule, out: Out2) => Schedule.Schedule +>(2, (self, out) => map(self, () => out)) + +/** @internal */ +export const asVoid = ( + self: Schedule.Schedule +): Schedule.Schedule => map(self, constVoid) + +/** @internal */ +export const bothInOut = dual< + ( + that: Schedule.Schedule + ) => ( + self: Schedule.Schedule + ) => Schedule.Schedule<[Out, Out2], readonly [In, In2], R | R2>, + ( + self: Schedule.Schedule, + that: Schedule.Schedule + ) => Schedule.Schedule<[Out, Out2], readonly [In, In2], R | R2> +>(2, (self, that) => + makeWithState([self.initial, that.initial], (now, [in1, in2], state) => + core.zipWith( + self.step(now, in1, state[0]), + that.step(now, in2, state[1]), + ([lState, out, lDecision], [rState, out2, rDecision]) => { + if (ScheduleDecision.isContinue(lDecision) && ScheduleDecision.isContinue(rDecision)) { + const interval = pipe(lDecision.intervals, Intervals.union(rDecision.intervals)) + return [ + [lState, rState], + [out, out2], + ScheduleDecision.continue(interval) + ] + } + return [[lState, rState], [out, out2], ScheduleDecision.done] + } + ))) + +/** @internal */ +export const check = dual< + ( + test: (input: In, output: Out) => boolean + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + test: (input: In, output: Out) => boolean + ) => Schedule.Schedule +>(2, (self, test) => checkEffect(self, (input, out) => core.sync(() => test(input, out)))) + +/** @internal */ +export const checkEffect = dual< + ( + test: (input: In, output: Out) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + test: (input: In, output: Out) => Effect.Effect + ) => Schedule.Schedule +>(2, (self, test) => + makeWithState( + self.initial, + (now, input, state) => + core.flatMap(self.step(now, input, state), ([state, out, decision]) => { + if (ScheduleDecision.isDone(decision)) { + return core.succeed([state, out, ScheduleDecision.done] as const) + } + return core.map(test(input, out), (cont) => + cont ? + [state, out, decision] as const : + [state, out, ScheduleDecision.done] as const) + }) + )) +/** @internal */ +export const collectAllInputs = (): Schedule.Schedule, A> => collectAllOutputs(identity()) + +/** @internal */ +export const collectAllOutputs = ( + self: Schedule.Schedule +): Schedule.Schedule, In, R> => + reduce(self, Chunk.empty(), (outs, out) => pipe(outs, Chunk.append(out))) + +/** @internal */ +export const collectUntil = (f: Predicate): Schedule.Schedule, A> => + collectAllOutputs(recurUntil(f)) + +/** @internal */ +export const collectUntilEffect = ( + f: (a: A) => Effect.Effect +): Schedule.Schedule, A, R> => collectAllOutputs(recurUntilEffect(f)) + +/** @internal */ +export const collectWhile = (f: Predicate): Schedule.Schedule, A> => + collectAllOutputs(recurWhile(f)) + +/** @internal */ +export const collectWhileEffect = ( + f: (a: A) => Effect.Effect +): Schedule.Schedule, A, R> => collectAllOutputs(recurWhileEffect(f)) + +/** @internal */ +export const compose = dual< + ( + that: Schedule.Schedule + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + that: Schedule.Schedule + ) => Schedule.Schedule +>(2, (self, that) => + makeWithState( + [self.initial, that.initial] as const, + (now, input, state) => + core.flatMap( + self.step(now, input, state[0]), + ([lState, out, lDecision]) => + core.map(that.step(now, out, state[1]), ([rState, out2, rDecision]) => + ScheduleDecision.isDone(lDecision) + ? [[lState, rState] as const, out2, ScheduleDecision.done] as const + : ScheduleDecision.isDone(rDecision) + ? [[lState, rState] as const, out2, ScheduleDecision.done] as const + : [ + [lState, rState] as const, + out2, + ScheduleDecision.continue(pipe(lDecision.intervals, Intervals.max(rDecision.intervals))) + ] as const) + ) + )) + +/** @internal */ +export const mapInput = dual< + ( + f: (in2: In2) => In + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (in2: In2) => In + ) => Schedule.Schedule +>(2, (self, f) => mapInputEffect(self, (input2) => core.sync(() => f(input2)))) + +/** @internal */ +export const mapInputContext = dual< + ( + f: (env0: Context.Context) => Context.Context + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (env0: Context.Context) => Context.Context + ) => Schedule.Schedule +>(2, (self, f) => + makeWithState( + self.initial, + (now, input, state) => core.mapInputContext(self.step(now, input, state), f) + )) + +/** @internal */ +export const mapInputEffect = dual< + ( + f: (in2: In2) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (in2: In2) => Effect.Effect + ) => Schedule.Schedule +>(2, (self, f) => + makeWithState(self.initial, (now, input2, state) => + core.flatMap( + f(input2), + (input) => self.step(now, input, state) + ))) + +/** @internal */ +export const cron: { + (expression: Cron.Cron): Schedule.Schedule<[number, number]> + (expression: string, tz?: DateTime.TimeZone | string): Schedule.Schedule<[number, number]> +} = (expression: string | Cron.Cron, tz?: DateTime.TimeZone | string): Schedule.Schedule<[number, number]> => { + const parsed = Cron.isCron(expression) ? Either.right(expression) : Cron.parse(expression, tz) + return makeWithState<[boolean, [number, number, number]], unknown, [number, number]>( + [true, [Number.MIN_SAFE_INTEGER, 0, 0]], + (now, _, [initial, previous]) => { + if (now < previous[0]) { + return core.succeed([ + [false, previous], + [previous[1], previous[2]], + ScheduleDecision.continueWith(Interval.make(previous[1], previous[2])) + ]) + } + + if (Either.isLeft(parsed)) { + return core.die(parsed.left) + } + + const cron = parsed.right + const date = new Date(now) + + let next: number + if (initial && Cron.match(cron, date)) { + next = now + } + + next = Cron.next(cron, date).getTime() + const start = beginningOfSecond(next) + const end = endOfSecond(next) + return core.succeed([ + [false, [next, start, end]], + [start, end], + ScheduleDecision.continueWith(Interval.make(start, end)) + ]) + } + ) +} + +/** @internal */ +export const dayOfMonth = (day: number): Schedule.Schedule => { + return makeWithState<[number, number], unknown, number>( + [Number.NEGATIVE_INFINITY, 0], + (now, _, state) => { + if (!Number.isInteger(day) || day < 1 || 31 < day) { + return core.dieSync(() => + new core.IllegalArgumentException( + `Invalid argument in: dayOfMonth(${day}). Must be in range 1...31` + ) + ) + } + const n = state[1] + const initial = n === 0 + const day0 = nextDayOfMonth(now, day, initial) + const start = beginningOfDay(day0) + const end = endOfDay(day0) + const interval = Interval.make(start, end) + return core.succeed( + [ + [end, n + 1], + n, + ScheduleDecision.continueWith(interval) + ] + ) + } + ) +} + +/** @internal */ +export const dayOfWeek = (day: number): Schedule.Schedule => { + return makeWithState<[number, number], unknown, number>( + [Number.MIN_SAFE_INTEGER, 0], + (now, _, state) => { + if (!Number.isInteger(day) || day < 1 || 7 < day) { + return core.dieSync(() => + new core.IllegalArgumentException( + `Invalid argument in: dayOfWeek(${day}). Must be in range 1 (Monday)...7 (Sunday)` + ) + ) + } + const n = state[1] + const initial = n === 0 + const day0 = nextDay(now, day, initial) + const start = beginningOfDay(day0) + const end = endOfDay(day0) + const interval = Interval.make(start, end) + return core.succeed( + [ + [end, n + 1], + n, + ScheduleDecision.continueWith(interval) + ] + ) + } + ) +} + +/** @internal */ +export const delayed = dual< + ( + f: (duration: Duration.Duration) => Duration.DurationInput + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (duration: Duration.Duration) => Duration.DurationInput + ) => Schedule.Schedule +>(2, (self, f) => delayedEffect(self, (duration) => core.sync(() => f(duration)))) + +/** @internal */ +export const delayedEffect = dual< + ( + f: (duration: Duration.Duration) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (duration: Duration.Duration) => Effect.Effect + ) => Schedule.Schedule +>(2, (self, f) => modifyDelayEffect(self, (_, delay) => f(delay))) + +/** @internal */ +export const delayedSchedule = ( + schedule: Schedule.Schedule +): Schedule.Schedule => addDelay(schedule, (x) => x) + +/** @internal */ +export const delays = ( + self: Schedule.Schedule +): Schedule.Schedule => + makeWithState(self.initial, (now, input, state) => + pipe( + self.step(now, input, state), + core.flatMap(( + [state, _, decision] + ): Effect.Effect<[any, Duration.Duration, ScheduleDecision.ScheduleDecision]> => { + if (ScheduleDecision.isDone(decision)) { + return core.succeed([state, Duration.zero, decision]) + } + return core.succeed( + [ + state, + Duration.millis(Intervals.start(decision.intervals) - now), + decision + ] + ) + }) + )) + +/** @internal */ +export const mapBoth = dual< + ( + options: { + readonly onInput: (in2: In2) => In + readonly onOutput: (out: Out) => Out2 + } + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + options: { + readonly onInput: (in2: In2) => In + readonly onOutput: (out: Out) => Out2 + } + ) => Schedule.Schedule +>(2, (self, { onInput, onOutput }) => map(mapInput(self, onInput), onOutput)) + +/** @internal */ +export const mapBothEffect = dual< + ( + options: { + readonly onInput: (input: In2) => Effect.Effect + readonly onOutput: (out: Out) => Effect.Effect + } + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + options: { + readonly onInput: (input: In2) => Effect.Effect + readonly onOutput: (out: Out) => Effect.Effect + } + ) => Schedule.Schedule +>(2, (self, { onInput, onOutput }) => mapEffect(mapInputEffect(self, onInput), onOutput)) + +/** @internal */ +export const driver = ( + self: Schedule.Schedule +): Effect.Effect> => + pipe( + ref.make, any]>([Option.none(), self.initial]), + core.map((ref) => new ScheduleDriverImpl(self, ref)) + ) + +/** @internal */ +export const duration = ( + durationInput: Duration.DurationInput +): Schedule.Schedule => { + const duration = Duration.decode(durationInput) + const durationMillis = Duration.toMillis(duration) + return makeWithState(true as boolean, (now, _, state) => + core.succeed( + state + ? [ + false, + duration, + ScheduleDecision.continueWith(Interval.after(now + durationMillis)) + ] as const + : [false, Duration.zero, ScheduleDecision.done] as const + )) +} + +/** @internal */ +export const either = dual< + ( + that: Schedule.Schedule + ) => (self: Schedule.Schedule) => Schedule.Schedule<[Out, Out2], In & In2, R | R2>, + ( + self: Schedule.Schedule, + that: Schedule.Schedule + ) => Schedule.Schedule<[Out, Out2], In & In2, R | R2> +>(2, (self, that) => union(self, that)) + +/** @internal */ +export const eitherWith = dual< + ( + that: Schedule.Schedule, + f: (x: Intervals.Intervals, y: Intervals.Intervals) => Intervals.Intervals + ) => (self: Schedule.Schedule) => Schedule.Schedule<[Out, Out2], In & In2, R | R2>, + ( + self: Schedule.Schedule, + that: Schedule.Schedule, + f: (x: Intervals.Intervals, y: Intervals.Intervals) => Intervals.Intervals + ) => Schedule.Schedule<[Out, Out2], In & In2, R | R2> +>(3, (self, that, f) => unionWith(self, that, f)) + +/** @internal */ +export const ensuring = dual< + ( + finalizer: Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + finalizer: Effect.Effect + ) => Schedule.Schedule +>(2, (self, finalizer) => + makeWithState( + self.initial, + (now, input, state) => + core.flatMap(self.step(now, input, state), ([state, out, decision]) => + ScheduleDecision.isDone(decision) + ? core.as(finalizer, [state, out, decision as ScheduleDecision.ScheduleDecision] as const) + : core.succeed([state, out, decision] as const)) + )) + +/** @internal */ +export const exponential = ( + baseInput: Duration.DurationInput, + factor = 2.0 +): Schedule.Schedule => { + const base = Duration.decode(baseInput) + return delayedSchedule( + map(forever, (i) => Duration.times(base, Math.pow(factor, i))) + ) +} + +/** @internal */ +export const fibonacci = (oneInput: Duration.DurationInput): Schedule.Schedule => { + const one = Duration.decode(oneInput) + return delayedSchedule( + pipe( + unfold( + [one, one] as const, + ([a, b]) => [b, Duration.sum(a, b)] as const + ), + map((out) => out[0]) + ) + ) +} + +/** @internal */ +export const fixed = (intervalInput: Duration.DurationInput): Schedule.Schedule => { + const interval = Duration.decode(intervalInput) + const intervalMillis = Duration.toMillis(interval) + return makeWithState<[Option.Option<[number, number]>, number], unknown, number>( + [Option.none(), 0], + (now, _, [option, n]) => + core.sync(() => { + switch (option._tag) { + case "None": { + return [ + [Option.some([now, now + intervalMillis]), n + 1], + n, + ScheduleDecision.continueWith(Interval.after(now + intervalMillis)) + ] + } + case "Some": { + const [startMillis, lastRun] = option.value + const runningBehind = now > (lastRun + intervalMillis) + const boundary = Equal.equals(interval, Duration.zero) + ? interval + : Duration.millis(intervalMillis - ((now - startMillis) % intervalMillis)) + const sleepTime = Equal.equals(boundary, Duration.zero) ? interval : boundary + const nextRun = runningBehind ? now : now + Duration.toMillis(sleepTime) + return [ + [Option.some([startMillis, nextRun]), n + 1], + n, + ScheduleDecision.continueWith(Interval.after(nextRun)) + ] + } + } + }) + ) +} + +/** @internal */ +export const fromDelay = (delay: Duration.DurationInput): Schedule.Schedule => duration(delay) + +/** @internal */ +export const fromDelays = ( + delay: Duration.DurationInput, + ...delays: Array +): Schedule.Schedule => + makeWithState( + [[delay, ...delays].map((_) => Duration.decode(_)) as Array, true as boolean] as const, + (now, _, [durations, cont]) => + core.sync(() => { + if (cont) { + const x = durations[0]! + const interval = Interval.after(now + Duration.toMillis(x)) + if (durations.length >= 2) { + return [ + [durations.slice(1), true] as const, + x, + ScheduleDecision.continueWith(interval) + ] as const + } + const y = durations.slice(1) + return [ + [[x, ...y] as Array, false] as const, + x, + ScheduleDecision.continueWith(interval) + ] as const + } + return [[durations, false] as const, Duration.zero, ScheduleDecision.done] as const + }) + ) + +/** @internal */ +export const fromFunction = (f: (a: A) => B): Schedule.Schedule => map(identity(), f) + +/** @internal */ +export const hourOfDay = (hour: number): Schedule.Schedule => + makeWithState<[number, number], unknown, number>( + [Number.NEGATIVE_INFINITY, 0], + (now, _, state) => { + if (!Number.isInteger(hour) || hour < 0 || 23 < hour) { + return core.dieSync(() => + new core.IllegalArgumentException( + `Invalid argument in: hourOfDay(${hour}). Must be in range 0...23` + ) + ) + } + const n = state[1] + const initial = n === 0 + const hour0 = nextHour(now, hour, initial) + const start = beginningOfHour(hour0) + const end = endOfHour(hour0) + const interval = Interval.make(start, end) + return core.succeed( + [ + [end, n + 1], + n, + ScheduleDecision.continueWith(interval) + ] + ) + } + ) + +/** @internal */ +export const identity = (): Schedule.Schedule => + makeWithState(void 0, (now, input, state) => + core.succeed( + [ + state, + input, + ScheduleDecision.continueWith(Interval.after(now)) + ] as const + )) + +/** @internal */ +export const intersect = dual< + ( + that: Schedule.Schedule + ) => (self: Schedule.Schedule) => Schedule.Schedule<[Out, Out2], In & In2, R | R2>, + ( + self: Schedule.Schedule, + that: Schedule.Schedule + ) => Schedule.Schedule<[Out, Out2], In & In2, R | R2> +>(2, (self, that) => intersectWith(self, that, Intervals.intersect)) + +/** @internal */ +export const intersectWith = dual< + ( + that: Schedule.Schedule, + f: (x: Intervals.Intervals, y: Intervals.Intervals) => Intervals.Intervals + ) => (self: Schedule.Schedule) => Schedule.Schedule<[Out, Out2], In & In2, R | R2>, + ( + self: Schedule.Schedule, + that: Schedule.Schedule, + f: (x: Intervals.Intervals, y: Intervals.Intervals) => Intervals.Intervals + ) => Schedule.Schedule<[Out, Out2], In & In2, R | R2> +>(3, ( + self: Schedule.Schedule, + that: Schedule.Schedule, + f: (x: Intervals.Intervals, y: Intervals.Intervals) => Intervals.Intervals +): Schedule.Schedule<[Out, Out2], In & In2, Env | Env2> => + makeWithState<[any, any], In & In2, [Out, Out2], Env | Env2>( + [self.initial, that.initial], + (now, input: In & In2, state) => + pipe( + core.zipWith( + self.step(now, input, state[0]), + that.step(now, input, state[1]), + (a, b) => [a, b] as const + ), + core.flatMap(([ + [lState, out, lDecision], + [rState, out2, rDecision] + ]) => { + if (ScheduleDecision.isContinue(lDecision) && ScheduleDecision.isContinue(rDecision)) { + return intersectWithLoop( + self, + that, + input, + lState, + out, + lDecision.intervals, + rState, + out2, + rDecision.intervals, + f + ) + } + return core.succeed( + [ + [lState, rState], + [out, out2], + ScheduleDecision.done + ] + ) + }) + ) + )) + +/** @internal */ +const intersectWithLoop = ( + self: Schedule.Schedule, + that: Schedule.Schedule, + input: In & In1, + lState: State, + out: Out, + lInterval: Intervals.Intervals, + rState: State1, + out2: Out2, + rInterval: Intervals.Intervals, + f: (x: Intervals.Intervals, y: Intervals.Intervals) => Intervals.Intervals +): Effect.Effect< + [[State, State1], [Out, Out2], ScheduleDecision.ScheduleDecision], + never, + Env | Env1 +> => { + const combined = f(lInterval, rInterval) + if (Intervals.isNonEmpty(combined)) { + return core.succeed([ + [lState, rState], + [out, out2], + ScheduleDecision.continue(combined) + ]) + } + + if (pipe(lInterval, Intervals.lessThan(rInterval))) { + return core.flatMap(self.step(Intervals.end(lInterval), input, lState), ([lState, out, decision]) => { + if (ScheduleDecision.isDone(decision)) { + return core.succeed([ + [lState, rState], + [out, out2], + ScheduleDecision.done + ]) + } + return intersectWithLoop( + self, + that, + input, + lState, + out, + decision.intervals, + rState, + out2, + rInterval, + f + ) + }) + } + return core.flatMap(that.step(Intervals.end(rInterval), input, rState), ([rState, out2, decision]) => { + if (ScheduleDecision.isDone(decision)) { + return core.succeed([ + [lState, rState], + [out, out2], + ScheduleDecision.done + ]) + } + return intersectWithLoop( + self, + that, + input, + lState, + out, + lInterval, + rState, + out2, + decision.intervals, + f + ) + }) +} + +/** @internal */ +export const jittered = (self: Schedule.Schedule): Schedule.Schedule => + jitteredWith(self, { min: 0.8, max: 1.2 }) + +/** @internal */ +export const jitteredWith = dual< + (options: { min?: number | undefined; max?: number | undefined }) => ( + self: Schedule.Schedule + ) => Schedule.Schedule, + ( + self: Schedule.Schedule, + options: { min?: number | undefined; max?: number | undefined } + ) => Schedule.Schedule +>(2, (self, options) => { + const { max, min } = Object.assign({ min: 0.8, max: 1.2 }, options) + return delayedEffect(self, (duration) => + core.map(Random.next, (random) => { + const d = Duration.toMillis(duration) + const jittered = d * min * (1 - random) + d * max * random + return Duration.millis(jittered) + })) +}) + +/** @internal */ +export const linear = (baseInput: Duration.DurationInput): Schedule.Schedule => { + const base = Duration.decode(baseInput) + return delayedSchedule(map(forever, (i) => Duration.times(base, i + 1))) +} + +/** @internal */ +export const map = dual< + ( + f: (out: Out) => Out2 + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (out: Out) => Out2 + ) => Schedule.Schedule +>(2, (self, f) => mapEffect(self, (out) => core.sync(() => f(out)))) + +/** @internal */ +export const mapEffect = dual< + ( + f: (out: Out) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (out: Out) => Effect.Effect + ) => Schedule.Schedule +>(2, (self, f) => + makeWithState( + self.initial, + (now, input, state) => + core.flatMap(self.step(now, input, state), ([state, out, decision]) => + core.map( + f(out), + (out2) => [state, out2, decision] as const + )) + )) + +/** @internal */ +export const minuteOfHour = (minute: number): Schedule.Schedule => + makeWithState<[number, number], unknown, number>( + [Number.MIN_SAFE_INTEGER, 0], + (now, _, state) => { + if (!Number.isInteger(minute) || minute < 0 || 59 < minute) { + return core.dieSync(() => + new core.IllegalArgumentException( + `Invalid argument in: minuteOfHour(${minute}). Must be in range 0...59` + ) + ) + } + const n = state[1] + const initial = n === 0 + const minute0 = nextMinute(now, minute, initial) + const start = beginningOfMinute(minute0) + const end = endOfMinute(minute0) + const interval = Interval.make(start, end) + return core.succeed( + [ + [end, n + 1], + n, + ScheduleDecision.continueWith(interval) + ] + ) + } + ) + +/** @internal */ +export const modifyDelay = dual< + ( + f: (out: Out, duration: Duration.Duration) => Duration.DurationInput + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (out: Out, duration: Duration.Duration) => Duration.DurationInput + ) => Schedule.Schedule +>(2, (self, f) => modifyDelayEffect(self, (out, duration) => core.sync(() => f(out, duration)))) + +/** @internal */ +export const modifyDelayEffect = dual< + ( + f: (out: Out, duration: Duration.Duration) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (out: Out, duration: Duration.Duration) => Effect.Effect + ) => Schedule.Schedule +>(2, (self, f) => + makeWithState( + self.initial, + (now, input, state) => + core.flatMap(self.step(now, input, state), ([state, out, decision]) => { + if (ScheduleDecision.isDone(decision)) { + return core.succeed([state, out, decision] as const) + } + const intervals = decision.intervals + const delay = Interval.size(Interval.make(now, Intervals.start(intervals))) + return core.map(f(out, delay), (durationInput) => { + const duration = Duration.decode(durationInput) + const oldStart = Intervals.start(intervals) + const newStart = now + Duration.toMillis(duration) + const delta = newStart - oldStart + const newEnd = Math.max(0, Intervals.end(intervals) + delta) + const newInterval = Interval.make(newStart, newEnd) + return [state, out, ScheduleDecision.continueWith(newInterval)] as const + }) + }) + )) + +/** @internal */ +export const onDecision = dual< + ( + f: (out: Out, decision: ScheduleDecision.ScheduleDecision) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (out: Out, decision: ScheduleDecision.ScheduleDecision) => Effect.Effect + ) => Schedule.Schedule +>(2, (self, f) => + makeWithState( + self.initial, + (now, input, state) => + core.flatMap( + self.step(now, input, state), + ([state, out, decision]) => core.as(f(out, decision), [state, out, decision] as const) + ) + )) + +/** @internal */ +export const passthrough = ( + self: Schedule.Schedule +): Schedule.Schedule => + makeWithState(self.initial, (now, input, state) => + pipe( + self.step(now, input, state), + core.map(([state, _, decision]) => [state, input, decision] as const) + )) + +/** @internal */ +export const provideContext = dual< + ( + context: Context.Context + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + context: Context.Context + ) => Schedule.Schedule +>(2, (self, context) => + makeWithState(self.initial, (now, input, state) => + core.provideContext( + self.step(now, input, state), + context + ))) + +/** @internal */ +export const provideService = dual< + ( + tag: Context.Tag, + service: Types.NoInfer + ) => ( + self: Schedule.Schedule + ) => Schedule.Schedule>, + ( + self: Schedule.Schedule, + tag: Context.Tag, + service: Types.NoInfer + ) => Schedule.Schedule> +>(3, ( + self: Schedule.Schedule, + tag: Context.Tag, + service: Types.NoInfer +): Schedule.Schedule> => + makeWithState(self.initial, (now, input, state) => + core.contextWithEffect((env) => + core.provideContext( + // @ts-expect-error + self.step(now, input, state), + Context.add(env, tag, service) + ) + ))) + +/** @internal */ +export const recurUntil = (f: Predicate): Schedule.Schedule => untilInput(identity(), f) + +/** @internal */ +export const recurUntilEffect = ( + f: (a: A) => Effect.Effect +): Schedule.Schedule => untilInputEffect(identity(), f) + +/** @internal */ +export const recurUntilOption = (pf: (a: A) => Option.Option): Schedule.Schedule, A> => + untilOutput(map(identity(), pf), Option.isSome) + +/** @internal */ +export const recurUpTo = ( + durationInput: Duration.DurationInput +): Schedule.Schedule => { + const duration = Duration.decode(durationInput) + return whileOutput(elapsed, (elapsed) => Duration.lessThan(elapsed, duration)) +} + +/** @internal */ +export const recurWhile = (f: Predicate): Schedule.Schedule => whileInput(identity(), f) + +/** @internal */ +export const recurWhileEffect = ( + f: (a: A) => Effect.Effect +): Schedule.Schedule => whileInputEffect(identity(), f) + +/** @internal */ +export const recurs = (n: number): Schedule.Schedule => whileOutput(forever, (out) => out < n) + +/** @internal */ +export const reduce = dual< + ( + zero: Z, + f: (z: Z, out: Out) => Z + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + zero: Z, + f: (z: Z, out: Out) => Z + ) => Schedule.Schedule +>(3, (self, zero, f) => reduceEffect(self, zero, (z, out) => core.sync(() => f(z, out)))) + +/** @internal */ +export const reduceEffect = dual< + ( + zero: Z, + f: (z: Z, out: Out) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + zero: Z, + f: (z: Z, out: Out) => Effect.Effect + ) => Schedule.Schedule +>(3, (self, zero, f) => + makeWithState( + [self.initial, zero] as const, + (now, input, [s, z]) => + core.flatMap(self.step(now, input, s), ([s, out, decision]) => + ScheduleDecision.isDone(decision) + ? core.succeed([[s, z], z, decision as ScheduleDecision.ScheduleDecision] as const) + : core.map(f(z, out), (z2) => [[s, z2], z, decision] as const)) + )) + +/** @internal */ +export const repeatForever = (self: Schedule.Schedule): Schedule.Schedule => + makeWithState(self.initial, (now, input, state) => { + const step = ( + now: number, + input: In, + state: any + ): Effect.Effect<[any, Out, ScheduleDecision.ScheduleDecision], never, Env> => + core.flatMap( + self.step(now, input, state), + ([state, out, decision]) => + ScheduleDecision.isDone(decision) + ? step(now, input, self.initial) + : core.succeed([state, out, decision]) + ) + return step(now, input, state) + }) + +/** @internal */ +export const repetitions = (self: Schedule.Schedule): Schedule.Schedule => + reduce(self, 0, (n, _) => n + 1) + +/** @internal */ +export const resetAfter = dual< + ( + duration: Duration.DurationInput + ) => ( + self: Schedule.Schedule + ) => Schedule.Schedule, + ( + self: Schedule.Schedule, + duration: Duration.DurationInput + ) => Schedule.Schedule +>(2, (self, durationInput) => { + const duration = Duration.decode(durationInput) + return pipe( + self, + intersect(elapsed), + resetWhen(([, time]) => Duration.greaterThanOrEqualTo(time, duration)), + map((out) => out[0]) + ) +}) + +/** @internal */ +export const resetWhen = dual< + (f: Predicate) => (self: Schedule.Schedule) => Schedule.Schedule, + (self: Schedule.Schedule, f: Predicate) => Schedule.Schedule +>(2, (self, f) => + makeWithState( + self.initial, + (now, input, state) => + core.flatMap(self.step(now, input, state), ([state, out, decision]) => + f(out) + ? self.step(now, input, self.initial) + : core.succeed([state, out, decision] as const)) + )) + +/** @internal */ +export const run = dual< + ( + now: number, + input: Iterable + ) => (self: Schedule.Schedule) => Effect.Effect, never, R>, + ( + self: Schedule.Schedule, + now: number, + input: Iterable + ) => Effect.Effect, never, R> +>(3, (self, now, input) => + pipe( + runLoop(self, now, Chunk.fromIterable(input), self.initial, Chunk.empty()), + core.map((list) => Chunk.reverse(list)) + )) + +/** @internal */ +const runLoop = ( + self: Schedule.Schedule, + now: number, + inputs: Chunk.Chunk, + state: any, + acc: Chunk.Chunk +): Effect.Effect, never, Env> => { + if (!Chunk.isNonEmpty(inputs)) { + return core.succeed(acc) + } + const input = Chunk.headNonEmpty(inputs) + const nextInputs = Chunk.tailNonEmpty(inputs) + return core.flatMap(self.step(now, input, state), ([state, out, decision]) => { + if (ScheduleDecision.isDone(decision)) { + return core.sync(() => pipe(acc, Chunk.prepend(out))) + } + return runLoop( + self, + Intervals.start(decision.intervals), + nextInputs, + state, + Chunk.prepend(acc, out) + ) + }) +} + +/** @internal */ +export const secondOfMinute = (second: number): Schedule.Schedule => + makeWithState<[number, number], unknown, number>( + [Number.NEGATIVE_INFINITY, 0], + (now, _, state) => { + if (!Number.isInteger(second) || second < 0 || 59 < second) { + return core.dieSync(() => + new core.IllegalArgumentException( + `Invalid argument in: secondOfMinute(${second}). Must be in range 0...59` + ) + ) + } + const n = state[1] + const initial = n === 0 + const second0 = nextSecond(now, second, initial) + const start = beginningOfSecond(second0) + const end = endOfSecond(second0) + const interval = Interval.make(start, end) + return core.succeed( + [ + [end, n + 1], + n, + ScheduleDecision.continueWith(interval) + ] + ) + } + ) + +/** @internal */ +export const spaced = (duration: Duration.DurationInput): Schedule.Schedule => addDelay(forever, () => duration) + +/** @internal */ +export const succeed = (value: A): Schedule.Schedule => map(forever, () => value) + +/** @internal */ +export const sync = (evaluate: LazyArg): Schedule.Schedule => map(forever, evaluate) + +/** @internal */ +export const tapInput = dual< + ( + f: (input: In2) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (input: In2) => Effect.Effect + ) => Schedule.Schedule +>(2, (self, f) => + makeWithState(self.initial, (now, input, state) => + core.zipRight( + f(input), + self.step(now, input, state) + ))) + +/** @internal */ +export const tapOutput = dual< + ( + f: (out: Types.NoInfer) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (out: Out) => Effect.Effect + ) => Schedule.Schedule +>( + 2, + ( + self: Schedule.Schedule, + f: (out: Out) => Effect.Effect + ): Schedule.Schedule => + makeWithState(self.initial, (now, input, state) => + core.tap( + self.step(now, input, state), + ([, out]) => f(out) + )) +) + +/** @internal */ +export const unfold = (initial: A, f: (a: A) => A): Schedule.Schedule => + makeWithState(initial, (now, _, state) => + core.sync(() => + [ + f(state), + state, + ScheduleDecision.continueWith(Interval.after(now)) + ] as const + )) + +/** @internal */ +export const union = dual< + ( + that: Schedule.Schedule + ) => (self: Schedule.Schedule) => Schedule.Schedule<[Out, Out2], In & In2, R | R2>, + ( + self: Schedule.Schedule, + that: Schedule.Schedule + ) => Schedule.Schedule<[Out, Out2], In & In2, R | R2> +>(2, (self, that) => unionWith(self, that, Intervals.union)) + +/** @internal */ +export const unionWith = dual< + ( + that: Schedule.Schedule, + f: (x: Intervals.Intervals, y: Intervals.Intervals) => Intervals.Intervals + ) => (self: Schedule.Schedule) => Schedule.Schedule<[Out, Out2], In & In2, R | R2>, + ( + self: Schedule.Schedule, + that: Schedule.Schedule, + f: (x: Intervals.Intervals, y: Intervals.Intervals) => Intervals.Intervals + ) => Schedule.Schedule<[Out, Out2], In & In2, R | R2> +>(3, (self, that, f) => + makeWithState([self.initial, that.initial], (now, input, state) => + core.zipWith( + self.step(now, input, state[0]), + that.step(now, input, state[1]), + ([lState, l, lDecision], [rState, r, rDecision]) => { + if (ScheduleDecision.isDone(lDecision) && ScheduleDecision.isDone(rDecision)) { + return [[lState, rState], [l, r], ScheduleDecision.done] + } + if (ScheduleDecision.isDone(lDecision) && ScheduleDecision.isContinue(rDecision)) { + return [ + [lState, rState], + [l, r], + ScheduleDecision.continue(rDecision.intervals) + ] + } + if (ScheduleDecision.isContinue(lDecision) && ScheduleDecision.isDone(rDecision)) { + return [ + [lState, rState], + [l, r], + ScheduleDecision.continue(lDecision.intervals) + ] + } + if (ScheduleDecision.isContinue(lDecision) && ScheduleDecision.isContinue(rDecision)) { + const combined = f(lDecision.intervals, rDecision.intervals) + return [ + [lState, rState], + [l, r], + ScheduleDecision.continue(combined) + ] + } + throw new Error( + "BUG: Schedule.unionWith - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + } + ))) + +/** @internal */ +export const untilInput = dual< + (f: Predicate) => (self: Schedule.Schedule) => Schedule.Schedule, + (self: Schedule.Schedule, f: Predicate) => Schedule.Schedule +>(2, (self, f) => check(self, (input, _) => !f(input))) + +/** @internal */ +export const untilInputEffect = dual< + ( + f: (input: In) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (input: In) => Effect.Effect + ) => Schedule.Schedule +>(2, (self, f) => checkEffect(self, (input, _) => effect.negate(f(input)))) + +/** @internal */ +export const untilOutput = dual< + (f: Predicate) => (self: Schedule.Schedule) => Schedule.Schedule, + (self: Schedule.Schedule, f: Predicate) => Schedule.Schedule +>(2, (self, f) => check(self, (_, out) => !f(out))) + +/** @internal */ +export const untilOutputEffect = dual< + ( + f: (out: Out) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (out: Out) => Effect.Effect + ) => Schedule.Schedule +>(2, (self, f) => checkEffect(self, (_, out) => effect.negate(f(out)))) + +/** @internal */ +export const upTo = dual< + (duration: Duration.DurationInput) => ( + self: Schedule.Schedule + ) => Schedule.Schedule, + ( + self: Schedule.Schedule, + duration: Duration.DurationInput + ) => Schedule.Schedule +>(2, (self, duration) => zipLeft(self, recurUpTo(duration))) + +/** @internal */ +export const whileInput = dual< + (f: Predicate) => (self: Schedule.Schedule) => Schedule.Schedule, + (self: Schedule.Schedule, f: Predicate) => Schedule.Schedule +>(2, (self, f) => check(self, (input, _) => f(input))) + +/** @internal */ +export const whileInputEffect = dual< + ( + f: (input: In) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (input: In) => Effect.Effect + ) => Schedule.Schedule +>(2, (self, f) => checkEffect(self, (input, _) => f(input))) + +/** @internal */ +export const whileOutput = dual< + (f: Predicate) => (self: Schedule.Schedule) => Schedule.Schedule, + (self: Schedule.Schedule, f: Predicate) => Schedule.Schedule +>(2, (self, f) => check(self, (_, out) => f(out))) + +/** @internal */ +export const whileOutputEffect = dual< + ( + f: (out: Out) => Effect.Effect + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + f: (out: Out) => Effect.Effect + ) => Schedule.Schedule +>(2, (self, f) => checkEffect(self, (_, out) => f(out))) + +/** @internal */ +export const windowed = (intervalInput: Duration.DurationInput): Schedule.Schedule => { + const interval = Duration.decode(intervalInput) + const millis = Duration.toMillis(interval) + return makeWithState<[Option.Option, number], unknown, number>( + [Option.none(), 0], + (now, _, [option, n]) => { + switch (option._tag) { + case "None": { + return core.succeed( + [ + [Option.some(now), n + 1], + n, + ScheduleDecision.continueWith(Interval.after(now + millis)) + ] + ) + } + case "Some": { + return core.succeed( + [ + [Option.some(option.value), n + 1], + n, + ScheduleDecision.continueWith( + Interval.after(now + (millis - ((now - option.value) % millis))) + ) + ] + ) + } + } + } + ) +} + +/** @internal */ +export const zipLeft = dual< + ( + that: Schedule.Schedule + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + that: Schedule.Schedule + ) => Schedule.Schedule +>(2, (self, that) => map(intersect(self, that), (out) => out[0])) + +/** @internal */ +export const zipRight = dual< + ( + that: Schedule.Schedule + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + that: Schedule.Schedule + ) => Schedule.Schedule +>(2, (self, that) => map(intersect(self, that), (out) => out[1])) + +/** @internal */ +export const zipWith = dual< + ( + that: Schedule.Schedule, + f: (out: Out, out2: Out2) => Out3 + ) => (self: Schedule.Schedule) => Schedule.Schedule, + ( + self: Schedule.Schedule, + that: Schedule.Schedule, + f: (out: Out, out2: Out2) => Out3 + ) => Schedule.Schedule +>(3, (self, that, f) => map(intersect(self, that), ([out, out2]) => f(out, out2))) + +// ----------------------------------------------------------------------------- +// Seconds +// ----------------------------------------------------------------------------- + +/** @internal */ +export const beginningOfSecond = (now: number): number => { + const date = new Date(now) + return new Date( + date.getFullYear(), + date.getMonth(), + date.getDate(), + date.getHours(), + date.getMinutes(), + date.getSeconds(), + 0 + ).getTime() +} + +/** @internal */ +export const endOfSecond = (now: number): number => { + const date = new Date(beginningOfSecond(now)) + return date.setSeconds(date.getSeconds() + 1) +} + +/** @internal */ +export const nextSecond = (now: number, second: number, initial: boolean): number => { + const date = new Date(now) + if (date.getSeconds() === second && initial) { + return now + } + if (date.getSeconds() < second) { + return date.setSeconds(second) + } + // Set seconds to the provided value and add one minute + const newDate = new Date(date.setSeconds(second)) + return newDate.setTime(newDate.getTime() + 1000 * 60) +} + +// ----------------------------------------------------------------------------- +// Minutes +// ----------------------------------------------------------------------------- + +/** @internal */ +export const beginningOfMinute = (now: number): number => { + const date = new Date(now) + return new Date( + date.getFullYear(), + date.getMonth(), + date.getDate(), + date.getHours(), + date.getMinutes(), + 0, + 0 + ).getTime() +} + +/** @internal */ +export const endOfMinute = (now: number): number => { + const date = new Date(beginningOfMinute(now)) + return date.setMinutes(date.getMinutes() + 1) +} + +/** @internal */ +export const nextMinute = (now: number, minute: number, initial: boolean): number => { + const date = new Date(now) + if (date.getMinutes() === minute && initial) { + return now + } + if (date.getMinutes() < minute) { + return date.setMinutes(minute) + } + // Set minutes to the provided value and add one hour + const newDate = new Date(date.setMinutes(minute)) + return newDate.setTime(newDate.getTime() + 1000 * 60 * 60) +} + +// ----------------------------------------------------------------------------- +// Hours +// ----------------------------------------------------------------------------- + +/** @internal */ +export const beginningOfHour = (now: number): number => { + const date = new Date(now) + return new Date( + date.getFullYear(), + date.getMonth(), + date.getDate(), + date.getHours(), + 0, + 0, + 0 + ).getTime() +} + +/** @internal */ +export const endOfHour = (now: number): number => { + const date = new Date(beginningOfHour(now)) + return date.setHours(date.getHours() + 1) +} + +/** @internal */ +export const nextHour = (now: number, hour: number, initial: boolean): number => { + const date = new Date(now) + if (date.getHours() === hour && initial) { + return now + } + if (date.getHours() < hour) { + return date.setHours(hour) + } + // Set hours to the provided value and add one day + const newDate = new Date(date.setHours(hour)) + return newDate.setTime(newDate.getTime() + 1000 * 60 * 60 * 24) +} + +// ----------------------------------------------------------------------------- +// Days +// ----------------------------------------------------------------------------- + +/** @internal */ +export const beginningOfDay = (now: number): number => { + const date = new Date(now) + return new Date( + date.getFullYear(), + date.getMonth(), + date.getDate(), + 0, + 0, + 0, + 0 + ).getTime() +} + +/** @internal */ +export const endOfDay = (now: number): number => { + const date = new Date(beginningOfDay(now)) + return date.setDate(date.getDate() + 1) +} + +/** @internal */ +export const nextDay = (now: number, dayOfWeek: number, initial: boolean): number => { + const date = new Date(now) + if (date.getDay() === dayOfWeek && initial) { + return now + } + const nextDayOfWeek = (7 + dayOfWeek - date.getDay()) % 7 + return date.setDate(date.getDate() + (nextDayOfWeek === 0 ? 7 : nextDayOfWeek)) +} + +/** @internal */ +export const nextDayOfMonth = (now: number, day: number, initial: boolean): number => { + const date = new Date(now) + if (date.getDate() === day && initial) { + return now + } + if (date.getDate() < day) { + return date.setDate(day) + } + return findNextMonth(now, day, 1) +} + +/** @internal */ +export const findNextMonth = (now: number, day: number, months: number): number => { + const d = new Date(now) + const tmp1 = new Date(d.setDate(day)) + const tmp2 = new Date(tmp1.setMonth(tmp1.getMonth() + months)) + if (tmp2.getDate() === day) { + const d2 = new Date(now) + const tmp3 = new Date(d2.setDate(day)) + return tmp3.setMonth(tmp3.getMonth() + months) + } + return findNextMonth(now, day, months + 1) +} + +// circular with Effect + +const ScheduleDefectTypeId = Symbol.for("effect/Schedule/ScheduleDefect") +class ScheduleDefect { + readonly [ScheduleDefectTypeId]: typeof ScheduleDefectTypeId + constructor(readonly error: E) { + this[ScheduleDefectTypeId] = ScheduleDefectTypeId + } +} +const isScheduleDefect = (u: unknown): u is ScheduleDefect => hasProperty(u, ScheduleDefectTypeId) +const scheduleDefectWrap = (self: Effect.Effect) => + core.catchAll(self, (e) => core.die(new ScheduleDefect(e))) + +/** @internal */ +export const scheduleDefectRefailCause = (cause: Cause.Cause) => + Option.match( + internalCause.find( + cause, + (_) => internalCause.isDieType(_) && isScheduleDefect(_.defect) ? Option.some(_.defect) : Option.none() + ), + { + onNone: () => cause, + onSome: (error) => internalCause.fail(error.error) + } + ) + +/** @internal */ +export const scheduleDefectRefail = (effect: Effect.Effect) => + core.catchAllCause(effect, (cause) => core.failCause(scheduleDefectRefailCause(cause))) + +/** @internal */ +export const repeat_Effect = dual< + ( + schedule: Schedule.Schedule + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + schedule: Schedule.Schedule + ) => Effect.Effect +>(2, (self, schedule) => repeatOrElse_Effect(self, schedule, (e, _) => core.fail(e))) + +/** @internal */ +export const repeat_combined = dual<{ + , O>, A>( + options: O + ): (self: Effect.Effect) => Effect.Repeat.Return + ( + schedule: Schedule.Schedule + ): (self: Effect.Effect) => Effect.Effect +}, { + , O>>( + self: Effect.Effect, + options: O + ): Effect.Repeat.Return + ( + self: Effect.Effect, + schedule: Schedule.Schedule + ): Effect.Effect +}>( + 2, + (self: Effect.Effect, options: Effect.Repeat.Options | Schedule.Schedule) => { + if (isSchedule(options)) { + return repeat_Effect(self, options) + } + + const base = options.schedule ?? passthrough(forever) + const withWhile = options.while ? + whileInputEffect(base, (a) => { + const applied = options.while!(a) + if (typeof applied === "boolean") { + return core.succeed(applied) + } + return scheduleDefectWrap(applied) + }) : + base + const withUntil = options.until ? + untilInputEffect(withWhile, (a) => { + const applied = options.until!(a) + if (typeof applied === "boolean") { + return core.succeed(applied) + } + return scheduleDefectWrap(applied) + }) : + withWhile + const withTimes = options.times ? + intersect(withUntil, recurs(options.times)).pipe(map((intersectionPair) => intersectionPair[0])) : + withUntil + return scheduleDefectRefail(repeat_Effect(self, withTimes)) + } +) + +/** @internal */ +export const repeatOrElse_Effect = dual< + ( + schedule: Schedule.Schedule, + orElse: (error: E, option: Option.Option) => Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + schedule: Schedule.Schedule, + orElse: (error: E, option: Option.Option) => Effect.Effect + ) => Effect.Effect +>(3, (self, schedule, orElse) => + core.flatMap(driver(schedule), (driver) => + core.matchEffect(self, { + onFailure: (error) => orElse(error, Option.none()), + onSuccess: (value) => + repeatOrElseEffectLoop( + effect.provideServiceEffect( + self, + CurrentIterationMetadata, + ref.get(driver.iterationMeta) + ), + driver, + (error, option) => + effect.provideServiceEffect( + orElse(error, option), + CurrentIterationMetadata, + ref.get(driver.iterationMeta) + ), + value + ) + }))) + +/** @internal */ +const repeatOrElseEffectLoop = ( + self: Effect.Effect, + driver: Schedule.ScheduleDriver, + orElse: (error: E, option: Option.Option) => Effect.Effect, + value: A +): Effect.Effect => + core.matchEffect(driver.next(value), { + onFailure: () => core.orDie(driver.last), + onSuccess: (b) => + core.matchEffect(self, { + onFailure: (error) => orElse(error, Option.some(b)), + onSuccess: (value) => repeatOrElseEffectLoop(self, driver, orElse, value) + }) + }) + +/** @internal */ +export const retry_Effect = dual< + ( + policy: Schedule.Schedule + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + policy: Schedule.Schedule + ) => Effect.Effect +>(2, (self, policy) => retryOrElse_Effect(self, policy, (e, _) => core.fail(e))) + +/** @internal */ +export const retry_combined: { + , O>>( + options: O + ): ( + self: Effect.Effect + ) => Effect.Retry.Return + ( + policy: Schedule.Schedule, R1> + ): (self: Effect.Effect) => Effect.Effect + , O>>( + self: Effect.Effect, + options: O + ): Effect.Retry.Return + ( + self: Effect.Effect, + policy: Schedule.Schedule, R1> + ): Effect.Effect +} = dual( + 2, + ( + self: Effect.Effect, + options: Effect.Retry.Options | Schedule.Schedule + ) => { + if (isSchedule(options)) { + return retry_Effect(self, options) + } + return scheduleDefectRefail(retry_Effect(self, fromRetryOptions(options))) + } +) + +/** @internal */ +export const fromRetryOptions = (options: Effect.Retry.Options): Schedule.Schedule => { + const base = options.schedule ?? forever + const withWhile = options.while ? + whileInputEffect(base, (e) => { + const applied = options.while!(e) + if (typeof applied === "boolean") { + return core.succeed(applied) + } + return scheduleDefectWrap(applied) + }) : + base + const withUntil = options.until ? + untilInputEffect(withWhile, (e) => { + const applied = options.until!(e) + if (typeof applied === "boolean") { + return core.succeed(applied) + } + return scheduleDefectWrap(applied) + }) : + withWhile + return options.times !== undefined ? + intersect(withUntil, recurs(options.times)) : + withUntil +} + +/** @internal */ +export const retryOrElse_Effect = dual< + ( + policy: Schedule.Schedule, R1>, + orElse: (e: Types.NoInfer, out: A1) => Effect.Effect + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + policy: Schedule.Schedule, R1>, + orElse: (e: Types.NoInfer, out: A1) => Effect.Effect + ) => Effect.Effect +>(3, (self, policy, orElse) => + core.flatMap( + driver(policy), + (driver) => + retryOrElse_EffectLoop( + effect.provideServiceEffect( + self, + CurrentIterationMetadata, + ref.get(driver.iterationMeta) + ), + driver, + (e, out) => + effect.provideServiceEffect( + orElse(e, out), + CurrentIterationMetadata, + ref.get(driver.iterationMeta) + ) + ) + )) + +/** @internal */ +const retryOrElse_EffectLoop = ( + self: Effect.Effect, + driver: Schedule.ScheduleDriver, + orElse: (e: E, out: A1) => Effect.Effect +): Effect.Effect => { + return core.catchAll( + self, + (e) => + core.matchEffect(driver.next(e), { + onFailure: () => + pipe( + driver.last, + core.orDie, + core.flatMap((out) => orElse(e, out)) + ), + onSuccess: () => retryOrElse_EffectLoop(self, driver, orElse) + }) + ) +} + +/** @internal */ +export const schedule_Effect = dual< + ( + schedule: Schedule.Schedule | undefined, R2> + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + schedule: Schedule.Schedule + ) => Effect.Effect +>(2, ( + self: Effect.Effect, + schedule: Schedule.Schedule +) => scheduleFrom_Effect(self, void 0, schedule)) + +/** @internal */ +export const scheduleFrom_Effect = dual< + ( + initial: In, + schedule: Schedule.Schedule + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + initial: In, + schedule: Schedule.Schedule + ) => Effect.Effect +>(3, (self, initial, schedule) => + core.flatMap( + driver(schedule), + (driver) => + scheduleFrom_EffectLoop( + effect.provideServiceEffect( + self, + CurrentIterationMetadata, + ref.get(driver.iterationMeta) + ), + initial, + driver + ) + )) + +/** @internal */ +const scheduleFrom_EffectLoop = ( + self: Effect.Effect, + initial: In, + driver: Schedule.ScheduleDriver +): Effect.Effect => + core.matchEffect(driver.next(initial), { + onFailure: () => core.orDie(driver.last), + onSuccess: () => + core.flatMap( + self, + (a) => scheduleFrom_EffectLoop(self, a, driver) + ) + }) + +/** @internal */ +export const count: Schedule.Schedule = unfold(0, (n) => n + 1) + +/** @internal */ +export const elapsed: Schedule.Schedule = makeWithState( + Option.none() as Option.Option, + (now, _, state) => { + switch (state._tag) { + case "None": { + return core.succeed( + [ + Option.some(now), + Duration.zero, + ScheduleDecision.continueWith(Interval.after(now)) + ] as const + ) + } + case "Some": { + return core.succeed( + [ + Option.some(state.value), + Duration.millis(now - state.value), + ScheduleDecision.continueWith(Interval.after(now)) + ] as const + ) + } + } + } +) + +/** @internal */ +export const forever: Schedule.Schedule = unfold(0, (n) => n + 1) + +/** @internal */ +export const once: Schedule.Schedule = asVoid(recurs(1)) + +/** @internal */ +export const stop: Schedule.Schedule = asVoid(recurs(0)) + +/** @internal */ +export const scheduleForked = dual< + ( + schedule: Schedule.Schedule + ) => ( + self: Effect.Effect + ) => Effect.Effect, never, R | R2 | Scope>, + ( + self: Effect.Effect, + schedule: Schedule.Schedule + ) => Effect.Effect, never, R | R2 | Scope> +>(2, (self, schedule) => forkScoped(schedule_Effect(self, schedule))) diff --git a/backend/node_modules/effect/src/internal/schedule/decision.ts b/backend/node_modules/effect/src/internal/schedule/decision.ts new file mode 100644 index 0000000000000000000000000000000000000000..b981bd396a78e0a214695ee127bc0bd98c47b72f --- /dev/null +++ b/backend/node_modules/effect/src/internal/schedule/decision.ts @@ -0,0 +1,47 @@ +import * as Chunk from "../../Chunk.js" +import type * as ScheduleDecision from "../../ScheduleDecision.js" +import type * as Interval from "../../ScheduleInterval.js" +import * as Intervals from "../../ScheduleIntervals.js" + +/** @internal */ +export const OP_CONTINUE = "Continue" as const + +/** @internal */ +export type OP_CONTINUE = typeof OP_CONTINUE + +/** @internal */ +export const OP_DONE = "Done" as const + +/** @internal */ +export type OP_DONE = typeof OP_DONE + +/** @internal */ +export const _continue = (intervals: Intervals.Intervals): ScheduleDecision.ScheduleDecision => { + return { + _tag: OP_CONTINUE, + intervals + } +} + +/** @internal */ +export const continueWith = (interval: Interval.Interval): ScheduleDecision.ScheduleDecision => { + return { + _tag: OP_CONTINUE, + intervals: Intervals.make(Chunk.of(interval)) + } +} + +/** @internal */ +export const done: ScheduleDecision.ScheduleDecision = { + _tag: OP_DONE +} + +/** @internal */ +export const isContinue = (self: ScheduleDecision.ScheduleDecision): self is ScheduleDecision.Continue => { + return self._tag === OP_CONTINUE +} + +/** @internal */ +export const isDone = (self: ScheduleDecision.ScheduleDecision): self is ScheduleDecision.Done => { + return self._tag === OP_DONE +} diff --git a/backend/node_modules/effect/src/internal/schedule/interval.ts b/backend/node_modules/effect/src/internal/schedule/interval.ts new file mode 100644 index 0000000000000000000000000000000000000000..c4590012bda26dcf2801e0e9fd85cf6a2b8283e0 --- /dev/null +++ b/backend/node_modules/effect/src/internal/schedule/interval.ts @@ -0,0 +1,101 @@ +import * as Duration from "../../Duration.js" +import { dual } from "../../Function.js" +import * as Option from "../../Option.js" +import type * as Interval from "../../ScheduleInterval.js" + +/** @internal */ +const IntervalSymbolKey = "effect/ScheduleInterval" + +/** @internal */ +export const IntervalTypeId: Interval.IntervalTypeId = Symbol.for( + IntervalSymbolKey +) as Interval.IntervalTypeId + +/** @internal */ +export const empty: Interval.Interval = { + [IntervalTypeId]: IntervalTypeId, + startMillis: 0, + endMillis: 0 +} + +/** @internal */ +export const make = (startMillis: number, endMillis: number): Interval.Interval => { + if (startMillis > endMillis) { + return empty + } + return { + [IntervalTypeId]: IntervalTypeId, + startMillis, + endMillis + } +} + +/** @internal */ +export const lessThan = dual< + (that: Interval.Interval) => (self: Interval.Interval) => boolean, + (self: Interval.Interval, that: Interval.Interval) => boolean +>(2, (self, that) => min(self, that) === self) + +/** @internal */ +export const min = dual< + (that: Interval.Interval) => (self: Interval.Interval) => Interval.Interval, + (self: Interval.Interval, that: Interval.Interval) => Interval.Interval +>(2, (self, that) => { + if (self.endMillis <= that.startMillis) return self + if (that.endMillis <= self.startMillis) return that + if (self.startMillis < that.startMillis) return self + if (that.startMillis < self.startMillis) return that + if (self.endMillis <= that.endMillis) return self + return that +}) + +/** @internal */ +export const max = dual< + (that: Interval.Interval) => (self: Interval.Interval) => Interval.Interval, + (self: Interval.Interval, that: Interval.Interval) => Interval.Interval +>(2, (self, that) => min(self, that) === self ? that : self) + +/** @internal */ +export const isEmpty = (self: Interval.Interval): boolean => { + return self.startMillis >= self.endMillis +} + +/** @internal */ +export const isNonEmpty = (self: Interval.Interval): boolean => { + return !isEmpty(self) +} + +/** @internal */ +export const intersect = dual< + (that: Interval.Interval) => (self: Interval.Interval) => Interval.Interval, + (self: Interval.Interval, that: Interval.Interval) => Interval.Interval +>(2, (self, that) => { + const start = Math.max(self.startMillis, that.startMillis) + const end = Math.min(self.endMillis, that.endMillis) + return make(start, end) +}) + +/** @internal */ +export const size = (self: Interval.Interval): Duration.Duration => { + return Duration.millis(self.endMillis - self.startMillis) +} + +/** @internal */ +export const union = dual< + (that: Interval.Interval) => (self: Interval.Interval) => Option.Option, + (self: Interval.Interval, that: Interval.Interval) => Option.Option +>(2, (self, that) => { + const start = Math.max(self.startMillis, that.startMillis) + const end = Math.min(self.endMillis, that.endMillis) + return start < end ? Option.none() : Option.some(make(start, end)) +}) + +/** @internal */ +export const after = (startMilliseconds: number): Interval.Interval => { + return make(startMilliseconds, Number.POSITIVE_INFINITY) +} + +/** @internal */ +export const before = (endMilliseconds: number): Interval.Interval => { + return make(Number.NEGATIVE_INFINITY, endMilliseconds) +} diff --git a/backend/node_modules/effect/src/internal/schedule/intervals.ts b/backend/node_modules/effect/src/internal/schedule/intervals.ts new file mode 100644 index 0000000000000000000000000000000000000000..f3b8de5ea2257d6717e32abbef4447c2cdec3503 --- /dev/null +++ b/backend/node_modules/effect/src/internal/schedule/intervals.ts @@ -0,0 +1,180 @@ +import * as Chunk from "../../Chunk.js" +import { dual, pipe } from "../../Function.js" +import * as Option from "../../Option.js" +import * as Interval from "../../ScheduleInterval.js" +import type * as Intervals from "../../ScheduleIntervals.js" +import { getBugErrorMessage } from "../errors.js" + +/** @internal */ +const IntervalsSymbolKey = "effect/ScheduleIntervals" + +/** @internal */ +export const IntervalsTypeId: Intervals.IntervalsTypeId = Symbol.for( + IntervalsSymbolKey +) as Intervals.IntervalsTypeId + +/** @internal */ +export const make = (intervals: Chunk.Chunk): Intervals.Intervals => { + return { + [IntervalsTypeId]: IntervalsTypeId, + intervals + } +} +/** @internal */ +export const empty: Intervals.Intervals = make(Chunk.empty()) + +/** @internal */ +export const fromIterable = (intervals: Iterable): Intervals.Intervals => + Array.from(intervals).reduce( + (intervals, interval) => pipe(intervals, union(make(Chunk.of(interval)))), + empty + ) + +/** @internal */ +export const union = dual< + (that: Intervals.Intervals) => (self: Intervals.Intervals) => Intervals.Intervals, + (self: Intervals.Intervals, that: Intervals.Intervals) => Intervals.Intervals +>(2, (self, that) => { + if (!Chunk.isNonEmpty(that.intervals)) { + return self + } + if (!Chunk.isNonEmpty(self.intervals)) { + return that + } + if (Chunk.headNonEmpty(self.intervals).startMillis < Chunk.headNonEmpty(that.intervals).startMillis) { + return unionLoop( + Chunk.tailNonEmpty(self.intervals), + that.intervals, + Chunk.headNonEmpty(self.intervals), + Chunk.empty() + ) + } + return unionLoop( + self.intervals, + Chunk.tailNonEmpty(that.intervals), + Chunk.headNonEmpty(that.intervals), + Chunk.empty() + ) +}) + +/** @internal */ +const unionLoop = ( + _self: Chunk.Chunk, + _that: Chunk.Chunk, + _interval: Interval.Interval, + _acc: Chunk.Chunk +): Intervals.Intervals => { + let self = _self + let that = _that + let interval = _interval + let acc = _acc + while (Chunk.isNonEmpty(self) || Chunk.isNonEmpty(that)) { + if (!Chunk.isNonEmpty(self) && Chunk.isNonEmpty(that)) { + if (interval.endMillis < Chunk.headNonEmpty(that).startMillis) { + acc = pipe(acc, Chunk.prepend(interval)) + interval = Chunk.headNonEmpty(that) + that = Chunk.tailNonEmpty(that) + self = Chunk.empty() + } else { + interval = Interval.make(interval.startMillis, Chunk.headNonEmpty(that).endMillis) + that = Chunk.tailNonEmpty(that) + self = Chunk.empty() + } + } else if (Chunk.isNonEmpty(self) && Chunk.isEmpty(that)) { + if (interval.endMillis < Chunk.headNonEmpty(self).startMillis) { + acc = pipe(acc, Chunk.prepend(interval)) + interval = Chunk.headNonEmpty(self) + that = Chunk.empty() + self = Chunk.tailNonEmpty(self) + } else { + interval = Interval.make(interval.startMillis, Chunk.headNonEmpty(self).endMillis) + that = Chunk.empty() + self = Chunk.tailNonEmpty(self) + } + } else if (Chunk.isNonEmpty(self) && Chunk.isNonEmpty(that)) { + if (Chunk.headNonEmpty(self).startMillis < Chunk.headNonEmpty(that).startMillis) { + if (interval.endMillis < Chunk.headNonEmpty(self).startMillis) { + acc = pipe(acc, Chunk.prepend(interval)) + interval = Chunk.headNonEmpty(self) + self = Chunk.tailNonEmpty(self) + } else { + interval = Interval.make(interval.startMillis, Chunk.headNonEmpty(self).endMillis) + self = Chunk.tailNonEmpty(self) + } + } else if (interval.endMillis < Chunk.headNonEmpty(that).startMillis) { + acc = pipe(acc, Chunk.prepend(interval)) + interval = Chunk.headNonEmpty(that) + that = Chunk.tailNonEmpty(that) + } else { + interval = Interval.make(interval.startMillis, Chunk.headNonEmpty(that).endMillis) + that = Chunk.tailNonEmpty(that) + } + } else { + throw new Error(getBugErrorMessage("Intervals.unionLoop")) + } + } + return make(pipe(acc, Chunk.prepend(interval), Chunk.reverse)) +} + +/** @internal */ +export const intersect = dual< + (that: Intervals.Intervals) => (self: Intervals.Intervals) => Intervals.Intervals, + (self: Intervals.Intervals, that: Intervals.Intervals) => Intervals.Intervals +>(2, (self, that) => intersectLoop(self.intervals, that.intervals, Chunk.empty())) + +/** @internal */ +const intersectLoop = ( + _left: Chunk.Chunk, + _right: Chunk.Chunk, + _acc: Chunk.Chunk +): Intervals.Intervals => { + let left = _left + let right = _right + let acc = _acc + while (Chunk.isNonEmpty(left) && Chunk.isNonEmpty(right)) { + const interval = pipe(Chunk.headNonEmpty(left), Interval.intersect(Chunk.headNonEmpty(right))) + const intervals = Interval.isEmpty(interval) ? acc : pipe(acc, Chunk.prepend(interval)) + if (pipe(Chunk.headNonEmpty(left), Interval.lessThan(Chunk.headNonEmpty(right)))) { + left = Chunk.tailNonEmpty(left) + } else { + right = Chunk.tailNonEmpty(right) + } + acc = intervals + } + return make(Chunk.reverse(acc)) +} + +/** @internal */ +export const start = (self: Intervals.Intervals): number => { + return pipe( + self.intervals, + Chunk.head, + Option.getOrElse(() => Interval.empty) + ).startMillis +} + +/** @internal */ +export const end = (self: Intervals.Intervals): number => { + return pipe( + self.intervals, + Chunk.head, + Option.getOrElse(() => Interval.empty) + ).endMillis +} + +/** @internal */ +export const lessThan = dual< + (that: Intervals.Intervals) => (self: Intervals.Intervals) => boolean, + (self: Intervals.Intervals, that: Intervals.Intervals) => boolean +>(2, (self, that) => start(self) < start(that)) + +/** @internal */ +export const isNonEmpty = (self: Intervals.Intervals): boolean => { + return Chunk.isNonEmpty(self.intervals) +} + +/** @internal */ +export const max = dual< + (that: Intervals.Intervals) => (self: Intervals.Intervals) => Intervals.Intervals, + (self: Intervals.Intervals, that: Intervals.Intervals) => Intervals.Intervals +>(2, (self, that) => lessThan(self, that) ? that : self) diff --git a/backend/node_modules/effect/src/internal/schema/errors.ts b/backend/node_modules/effect/src/internal/schema/errors.ts new file mode 100644 index 0000000000000000000000000000000000000000..80ea2f5b7f0023ac5bb0b837cd48a6b189ed3cf8 --- /dev/null +++ b/backend/node_modules/effect/src/internal/schema/errors.ts @@ -0,0 +1,191 @@ +import * as array_ from "../../Array.js" +import * as Inspectable from "../../Inspectable.js" +import type * as AST from "../../SchemaAST.js" +import * as util_ from "./util.js" + +const getErrorMessage = ( + reason: string, + details?: string, + path?: ReadonlyArray, + ast?: AST.AST +): string => { + let out = reason + + if (path && array_.isNonEmptyReadonlyArray(path)) { + out += `\nat path: ${util_.formatPath(path)}` + } + + if (details !== undefined) { + out += `\ndetails: ${details}` + } + + if (ast) { + out += `\nschema (${ast._tag}): ${ast}` + } + + return out +} + +// --------------------------------------------- +// generic +// --------------------------------------------- + +/** @internal */ +export const getInvalidArgumentErrorMessage = (details: string) => getErrorMessage("Invalid Argument", details) + +const getUnsupportedSchemaErrorMessage = (details?: string, path?: ReadonlyArray, ast?: AST.AST): string => + getErrorMessage("Unsupported schema", details, path, ast) + +const getMissingAnnotationErrorMessage = (details?: string, path?: ReadonlyArray, ast?: AST.AST): string => + getErrorMessage("Missing annotation", details, path, ast) + +// --------------------------------------------- +// Arbitrary +// --------------------------------------------- + +/** @internal */ +export const getArbitraryUnsupportedErrorMessage = (path: ReadonlyArray, ast: AST.AST) => + getUnsupportedSchemaErrorMessage("Cannot build an Arbitrary for this schema", path, ast) + +/** @internal */ +export const getArbitraryMissingAnnotationErrorMessage = ( + path: ReadonlyArray, + ast: AST.AST +) => + getMissingAnnotationErrorMessage( + `Generating an Arbitrary for this schema requires an "arbitrary" annotation`, + path, + ast + ) + +/** @internal */ +export const getArbitraryEmptyEnumErrorMessage = (path: ReadonlyArray) => + getErrorMessage("Empty Enums schema", "Generating an Arbitrary for this schema requires at least one enum", path) + +// --------------------------------------------- +// Equivalence +// --------------------------------------------- + +/** @internal */ +export const getEquivalenceUnsupportedErrorMessage = (ast: AST.AST, path: ReadonlyArray) => + getUnsupportedSchemaErrorMessage("Cannot build an Equivalence", path, ast) + +// --------------------------------------------- +// JSON Schema +// --------------------------------------------- + +/** @internal */ +export const getJSONSchemaMissingAnnotationErrorMessage = ( + path: ReadonlyArray, + ast: AST.AST +) => + getMissingAnnotationErrorMessage( + `Generating a JSON Schema for this schema requires a "jsonSchema" annotation`, + path, + ast + ) + +/** @internal */ +export const getJSONSchemaMissingIdentifierAnnotationErrorMessage = ( + path: ReadonlyArray, + ast: AST.AST +) => + getMissingAnnotationErrorMessage( + `Generating a JSON Schema for this schema requires an "identifier" annotation`, + path, + ast + ) + +/** @internal */ +export const getJSONSchemaUnsupportedPostRestElementsErrorMessage = (path: ReadonlyArray): string => + getErrorMessage( + "Generating a JSON Schema for post-rest elements is not currently supported. You're welcome to contribute by submitting a Pull Request", + undefined, + path + ) + +/** @internal */ +export const getJSONSchemaUnsupportedKeyErrorMessage = (key: PropertyKey, path: ReadonlyArray): string => + getErrorMessage("Unsupported key", `Cannot encode ${Inspectable.formatPropertyKey(key)} key to JSON Schema`, path) + +// --------------------------------------------- +// Pretty +// --------------------------------------------- + +/** @internal */ +export const getPrettyMissingAnnotationErrorMessage = ( + path: ReadonlyArray, + ast: AST.AST +) => getMissingAnnotationErrorMessage(`Generating a Pretty for this schema requires a "pretty" annotation`, path, ast) + +/** @internal */ +export const getPrettyNeverErrorMessage = "Cannot pretty print a `never` value" + +/** @internal */ +export const getPrettyNoMatchingSchemaErrorMessage = ( + actual: unknown, + path: ReadonlyArray, + ast: AST.AST +) => + getErrorMessage( + "Unexpected Error", + `Cannot find a matching schema for ${Inspectable.formatUnknown(actual)}`, + path, + ast + ) + +// --------------------------------------------- +// Schema +// --------------------------------------------- + +/** @internal */ +export const getSchemaExtendErrorMessage = (x: AST.AST, y: AST.AST, path: ReadonlyArray) => + getErrorMessage("Unsupported schema or overlapping types", `cannot extend ${x} with ${y}`, path) + +/** @internal */ +export const getSchemaUnsupportedLiteralSpanErrorMessage = (ast: AST.AST) => + getErrorMessage("Unsupported template literal span", undefined, undefined, ast) + +// --------------------------------------------- +// AST +// --------------------------------------------- + +/** @internal */ +export const getASTUnsupportedSchemaErrorMessage = (ast: AST.AST) => + getUnsupportedSchemaErrorMessage(undefined, undefined, ast) + +/** @internal */ +export const getASTUnsupportedKeySchemaErrorMessage = (ast: AST.AST) => + getErrorMessage("Unsupported key schema", undefined, undefined, ast) + +/** @internal */ +export const getASTUnsupportedLiteralErrorMessage = (literal: AST.LiteralValue) => + getErrorMessage("Unsupported literal", `literal value: ${Inspectable.formatUnknown(literal)}`) + +/** @internal */ +export const getASTDuplicateIndexSignatureErrorMessage = (type: "string" | "symbol"): string => + getErrorMessage("Duplicate index signature", `${type} index signature`) + +/** @internal */ +export const getASTIndexSignatureParameterErrorMessage = getErrorMessage( + "Unsupported index signature parameter", + "An index signature parameter type must be `string`, `symbol`, a template literal type or a refinement of the previous types" +) + +/** @internal */ +export const getASTRequiredElementFollowinAnOptionalElementErrorMessage = getErrorMessage( + "Invalid element", + "A required element cannot follow an optional element. ts(1257)" +) + +/** @internal */ +export const getASTDuplicatePropertySignatureTransformationErrorMessage = (key: PropertyKey): string => + getErrorMessage("Duplicate property signature transformation", `Duplicate key ${Inspectable.formatUnknown(key)}`) + +/** @internal */ +export const getASTUnsupportedRenameSchemaErrorMessage = (ast: AST.AST): string => + getUnsupportedSchemaErrorMessage(undefined, undefined, ast) + +/** @internal */ +export const getASTDuplicatePropertySignatureErrorMessage = (key: PropertyKey): string => + getErrorMessage("Duplicate property signature", `Duplicate key ${Inspectable.formatUnknown(key)}`) diff --git a/backend/node_modules/effect/src/internal/schema/schemaId.ts b/backend/node_modules/effect/src/internal/schema/schemaId.ts new file mode 100644 index 0000000000000000000000000000000000000000..21164b9e3c2572a91175073fe7ef9eff4d95130e --- /dev/null +++ b/backend/node_modules/effect/src/internal/schema/schemaId.ts @@ -0,0 +1,106 @@ +import type * as Schema from "../../Schema.js" + +/** @internal */ +export const DateFromSelfSchemaId: Schema.DateFromSelfSchemaId = Symbol.for( + "effect/SchemaId/DateFromSelf" +) as Schema.DateFromSelfSchemaId + +/** @internal */ +export const GreaterThanSchemaId: Schema.GreaterThanSchemaId = Symbol.for( + "effect/SchemaId/GreaterThan" +) as Schema.GreaterThanSchemaId + +/** @internal */ +export const GreaterThanOrEqualToSchemaId: Schema.GreaterThanOrEqualToSchemaId = Symbol.for( + "effect/SchemaId/GreaterThanOrEqualTo" +) as Schema.GreaterThanOrEqualToSchemaId + +/** @internal */ +export const LessThanSchemaId: Schema.LessThanSchemaId = Symbol.for( + "effect/SchemaId/LessThan" +) as Schema.LessThanSchemaId + +/** @internal */ +export const LessThanOrEqualToSchemaId: Schema.LessThanOrEqualToSchemaId = Symbol.for( + "effect/SchemaId/LessThanOrEqualTo" +) as Schema.LessThanOrEqualToSchemaId + +/** @internal */ +export const IntSchemaId: Schema.IntSchemaId = Symbol.for( + "effect/SchemaId/Int" +) as Schema.IntSchemaId + +/** @internal */ +export const NonNaNSchemaId: Schema.NonNaNSchemaId = Symbol.for( + "effect/SchemaId/NonNaN" +) as Schema.NonNaNSchemaId + +/** @internal */ +export const FiniteSchemaId: Schema.FiniteSchemaId = Symbol.for( + "effect/SchemaId/Finite" +) as Schema.FiniteSchemaId + +/** @internal */ +export const JsonNumberSchemaId: Schema.JsonNumberSchemaId = Symbol.for( + "effect/SchemaId/JsonNumber" +) as Schema.JsonNumberSchemaId + +/** @internal */ +export const BetweenSchemaId: Schema.BetweenSchemaId = Symbol.for( + "effect/SchemaId/Between" +) as Schema.BetweenSchemaId + +/** @internal */ +export const GreaterThanBigintSchemaId: Schema.GreaterThanBigIntSchemaId = Symbol.for( + "effect/SchemaId/GreaterThanBigint" +) as Schema.GreaterThanBigIntSchemaId + +/** @internal */ +export const GreaterThanOrEqualToBigIntSchemaId: Schema.GreaterThanOrEqualToBigIntSchemaId = Symbol.for( + "effect/SchemaId/GreaterThanOrEqualToBigint" +) as Schema.GreaterThanOrEqualToBigIntSchemaId + +/** @internal */ +export const LessThanBigIntSchemaId: Schema.LessThanBigIntSchemaId = Symbol.for( + "effect/SchemaId/LessThanBigint" +) as Schema.LessThanBigIntSchemaId + +/** @internal */ +export const LessThanOrEqualToBigIntSchemaId: Schema.LessThanOrEqualToBigIntSchemaId = Symbol.for( + "effect/SchemaId/LessThanOrEqualToBigint" +) as Schema.LessThanOrEqualToBigIntSchemaId + +/** @internal */ +export const BetweenBigintSchemaId: Schema.BetweenBigIntSchemaId = Symbol.for( + "effect/SchemaId/BetweenBigint" +) as Schema.BetweenBigIntSchemaId + +/** @internal */ +export const MinLengthSchemaId: Schema.MinLengthSchemaId = Symbol.for( + "effect/SchemaId/MinLength" +) as Schema.MinLengthSchemaId + +/** @internal */ +export const MaxLengthSchemaId: Schema.MaxLengthSchemaId = Symbol.for( + "effect/SchemaId/MaxLength" +) as Schema.MaxLengthSchemaId + +/** @internal */ +export const LengthSchemaId: Schema.LengthSchemaId = Symbol.for( + "effect/SchemaId/Length" +) as Schema.LengthSchemaId + +/** @internal */ +export const MinItemsSchemaId: Schema.MinItemsSchemaId = Symbol.for( + "effect/SchemaId/MinItems" +) as Schema.MinItemsSchemaId + +/** @internal */ +export const MaxItemsSchemaId: Schema.MaxItemsSchemaId = Symbol.for( + "effect/SchemaId/MaxItems" +) as Schema.MaxItemsSchemaId + +/** @internal */ +export const ItemsCountSchemaId: Schema.ItemsCountSchemaId = Symbol.for( + "effect/SchemaId/ItemsCount" +) as Schema.ItemsCountSchemaId diff --git a/backend/node_modules/effect/src/internal/scopedCache.ts b/backend/node_modules/effect/src/internal/scopedCache.ts new file mode 100644 index 0000000000000000000000000000000000000000..6eddd6cc8a77155373dd5e270fe809490cb17d9f --- /dev/null +++ b/backend/node_modules/effect/src/internal/scopedCache.ts @@ -0,0 +1,644 @@ +import type * as Cache from "../Cache.js" +import type * as Clock from "../Clock.js" +import * as Context from "../Context.js" +import * as Data from "../Data.js" +import * as Duration from "../Duration.js" +import type * as Effect from "../Effect.js" +import * as Equal from "../Equal.js" +import * as Exit from "../Exit.js" +import { pipe } from "../Function.js" +import * as HashSet from "../HashSet.js" +import * as MutableHashMap from "../MutableHashMap.js" +import * as MutableQueue from "../MutableQueue.js" +import * as MutableRef from "../MutableRef.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import * as Scope from "../Scope.js" +import type * as ScopedCache from "../ScopedCache.js" +import * as cache_ from "./cache.js" +import * as effect from "./core-effect.js" +import * as core from "./core.js" +import * as fiberRuntime from "./fiberRuntime.js" + +/** + * The `CacheState` represents the mutable state underlying the cache. + * + * @internal + */ +export interface CacheState { + map: MutableHashMap.MutableHashMap> // mutable by design + keys: cache_.KeySet // mutable by design + accesses: MutableQueue.MutableQueue> // mutable by design + updating: MutableRef.MutableRef // mutable by design + hits: number // mutable by design + misses: number // mutable by design +} + +/** @internal */ +export const makeCacheState = ( + map: MutableHashMap.MutableHashMap>, + keys: cache_.KeySet, + accesses: MutableQueue.MutableQueue>, + updating: MutableRef.MutableRef, + hits: number, + misses: number +): CacheState => ({ + map, + keys, + accesses, + updating, + hits, + misses +}) + +/** + * Constructs an initial cache state. + * + * @internal + */ +export const initialCacheState = (): CacheState => + makeCacheState( + MutableHashMap.empty(), + cache_.makeKeySet(), + MutableQueue.unbounded(), + MutableRef.make(false), + 0, + 0 + ) + +/** + * A `MapValue` represents a value in the cache. A value may either be + * `Pending` with a `Promise` that will contain the result of computing the + * lookup function, when it is available, or `Complete` with an `Exit` value + * that contains the result of computing the lookup function. + * + * @internal + */ +export type MapValue = + | Complete + | Pending + | Refreshing + +/** @internal */ +export interface Complete { + readonly _tag: "Complete" + readonly key: cache_.MapKey + readonly exit: Exit.Exit + readonly ownerCount: MutableRef.MutableRef + readonly entryStats: Cache.EntryStats + readonly timeToLive: number +} + +/** @internal */ +export interface Pending { + readonly _tag: "Pending" + readonly key: cache_.MapKey + readonly scoped: Effect.Effect> +} + +/** @internal */ +export interface Refreshing { + readonly _tag: "Refreshing" + readonly scoped: Effect.Effect> + readonly complete: Complete +} + +/** @internal */ +export const complete = ( + key: cache_.MapKey, + exit: Exit.Exit, + ownerCount: MutableRef.MutableRef, + entryStats: Cache.EntryStats, + timeToLive: number +): Complete => + Data.struct({ + _tag: "Complete", + key, + exit, + ownerCount, + entryStats, + timeToLive + }) + +/** @internal */ +export const pending = ( + key: cache_.MapKey, + scoped: Effect.Effect> +): Pending => + Data.struct({ + _tag: "Pending", + key, + scoped + }) + +/** @internal */ +export const refreshing = ( + scoped: Effect.Effect>, + complete: Complete +): Refreshing => + Data.struct({ + _tag: "Refreshing", + scoped, + complete + }) + +/** @internal */ +export const toScoped = ( + self: Complete +): Effect.Effect => + Exit.matchEffect(self.exit, { + onFailure: (cause) => core.failCause(cause), + onSuccess: ([value]) => + fiberRuntime.acquireRelease( + core.as(core.sync(() => MutableRef.incrementAndGet(self.ownerCount)), value), + () => releaseOwner(self) + ) + }) + +/** @internal */ +export const releaseOwner = ( + self: Complete +): Effect.Effect => + Exit.matchEffect(self.exit, { + onFailure: () => core.void, + onSuccess: ([, finalizer]) => + core.flatMap( + core.sync(() => MutableRef.decrementAndGet(self.ownerCount)), + (numOwner) => effect.when(finalizer(Exit.void), () => numOwner === 0) + ) + }) + +/** @internal */ +const ScopedCacheSymbolKey = "effect/ScopedCache" + +/** @internal */ +export const ScopedCacheTypeId: ScopedCache.ScopedCacheTypeId = Symbol.for( + ScopedCacheSymbolKey +) as ScopedCache.ScopedCacheTypeId + +const scopedCacheVariance = { + /* c8 ignore next */ + _Key: (_: unknown) => _, + /* c8 ignore next */ + _Error: (_: never) => _, + /* c8 ignore next */ + _Value: (_: never) => _ +} + +class ScopedCacheImpl + implements ScopedCache.ScopedCache +{ + readonly [ScopedCacheTypeId] = scopedCacheVariance + readonly cacheState: CacheState + constructor( + readonly capacity: number, + readonly scopedLookup: ScopedCache.Lookup, + readonly clock: Clock.Clock, + readonly timeToLive: (exit: Exit.Exit) => Duration.Duration, + readonly context: Context.Context + ) { + this.cacheState = initialCacheState() + } + + pipe() { + return pipeArguments(this, arguments) + } + + get cacheStats(): Effect.Effect { + return core.sync(() => + cache_.makeCacheStats({ + hits: this.cacheState.hits, + misses: this.cacheState.misses, + size: MutableHashMap.size(this.cacheState.map) + }) + ) + } + + getOption(key: Key): Effect.Effect, Error, Scope.Scope> { + return core.suspend(() => + Option.match(MutableHashMap.get(this.cacheState.map, key), { + onNone: () => effect.succeedNone, + onSome: (value) => core.flatten(this.resolveMapValue(value)) + }) + ) + } + + getOptionComplete(key: Key): Effect.Effect, never, Scope.Scope> { + return core.suspend(() => + Option.match(MutableHashMap.get(this.cacheState.map, key), { + onNone: () => effect.succeedNone, + onSome: (value) => + core.flatten(this.resolveMapValue(value, true)) as Effect.Effect, never, Scope.Scope> + }) + ) + } + + contains(key: Key): Effect.Effect { + return core.sync(() => MutableHashMap.has(this.cacheState.map, key)) + } + + entryStats(key: Key): Effect.Effect> { + return core.sync(() => { + const value = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, key)) + if (value === undefined) { + return Option.none() + } + switch (value._tag) { + case "Complete": { + return Option.some(cache_.makeEntryStats(value.entryStats.loadedMillis)) + } + case "Pending": { + return Option.none() + } + case "Refreshing": { + return Option.some(cache_.makeEntryStats(value.complete.entryStats.loadedMillis)) + } + } + }) + } + + get(key: Key): Effect.Effect { + return pipe( + this.lookupValueOf(key), + effect.memoize, + core.flatMap((lookupValue) => + core.suspend(() => { + let k: cache_.MapKey | undefined = undefined + let value = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, key)) + if (value === undefined) { + k = cache_.makeMapKey(key) + if (MutableHashMap.has(this.cacheState.map, key)) { + value = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, key)) + } else { + MutableHashMap.set(this.cacheState.map, key, pending(k, lookupValue)) + } + } + if (value === undefined) { + this.trackMiss() + return core.zipRight( + this.ensureMapSizeNotExceeded(k!), + lookupValue + ) + } + + return core.map( + this.resolveMapValue(value), + core.flatMap(Option.match({ + onNone: () => { + const val = value as Complete + const current = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, key)) + if (Equal.equals(current, value)) { + MutableHashMap.remove(this.cacheState.map, key) + } + return pipe( + this.ensureMapSizeNotExceeded(val.key), + core.zipRight(releaseOwner(val)), + core.zipRight(this.get(key)) + ) + }, + onSome: core.succeed + })) + ) + }) + ), + core.flatten + ) + } + + invalidate(key: Key): Effect.Effect { + return core.suspend(() => { + if (MutableHashMap.has(this.cacheState.map, key)) { + const mapValue = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, key))! + MutableHashMap.remove(this.cacheState.map, key) + switch (mapValue._tag) { + case "Complete": { + return releaseOwner(mapValue) + } + case "Pending": { + return core.void + } + case "Refreshing": { + return releaseOwner(mapValue.complete) + } + } + } + return core.void + }) + } + + get invalidateAll(): Effect.Effect { + return fiberRuntime.forEachConcurrentDiscard( + HashSet.fromIterable(Array.from(this.cacheState.map).map(([key]) => key)), + (key) => this.invalidate(key), + false, + false + ) + } + + refresh(key: Key): Effect.Effect { + return pipe( + this.lookupValueOf(key), + effect.memoize, + core.flatMap((scoped) => { + let value = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, key)) + let newKey: cache_.MapKey | undefined = undefined + if (value === undefined) { + newKey = cache_.makeMapKey(key) + if (MutableHashMap.has(this.cacheState.map, key)) { + value = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, key)) + } else { + MutableHashMap.set(this.cacheState.map, key, pending(newKey, scoped)) + } + } + let finalScoped: Effect.Effect> + if (value === undefined) { + finalScoped = core.zipRight( + this.ensureMapSizeNotExceeded(newKey!), + scoped + ) + } else { + switch (value._tag) { + case "Complete": { + if (this.hasExpired(value.timeToLive)) { + finalScoped = core.succeed(this.get(key)) + } else { + const current = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, key)) + if (Equal.equals(current, value)) { + const mapValue = refreshing(scoped, value) + MutableHashMap.set(this.cacheState.map, key, mapValue) + finalScoped = scoped + } else { + finalScoped = core.succeed(this.get(key)) + } + } + break + } + case "Pending": { + finalScoped = value.scoped + break + } + case "Refreshing": { + finalScoped = value.scoped + break + } + } + } + return core.flatMap(finalScoped, (s) => fiberRuntime.scopedEffect(core.asVoid(s))) + }) + ) + } + + get size(): Effect.Effect { + return core.sync(() => MutableHashMap.size(this.cacheState.map)) + } + + resolveMapValue( + value: MapValue, + ignorePending = false + ): Effect.Effect, Error, Scope.Scope>> { + switch (value._tag) { + case "Complete": { + this.trackHit() + if (this.hasExpired(value.timeToLive)) { + return core.succeed(effect.succeedNone) + } + return core.as( + this.ensureMapSizeNotExceeded(value.key), + effect.asSome(toScoped(value)) + ) + } + case "Pending": { + this.trackHit() + + if (ignorePending) { + return core.succeed(effect.succeedNone) + } + + return core.zipRight( + this.ensureMapSizeNotExceeded(value.key), + core.map(value.scoped, effect.asSome) + ) + } + case "Refreshing": { + this.trackHit() + if (this.hasExpired(value.complete.timeToLive)) { + if (ignorePending) { + return core.succeed(effect.succeedNone) + } + return core.zipRight( + this.ensureMapSizeNotExceeded(value.complete.key), + core.map(value.scoped, effect.asSome) + ) + } + return core.as( + this.ensureMapSizeNotExceeded(value.complete.key), + effect.asSome(toScoped(value.complete)) + ) + } + } + } + + lookupValueOf(key: Key): Effect.Effect> { + return pipe( + core.onInterrupt( + core.flatMap(Scope.make(), (scope) => + pipe( + this.scopedLookup(key), + core.provideContext(pipe(this.context, Context.add(Scope.Scope, scope))), + core.exit, + core.map((exit) => [exit, ((exit) => Scope.close(scope, exit)) as Scope.Scope.Finalizer] as const) + )), + () => core.sync(() => MutableHashMap.remove(this.cacheState.map, key)) + ), + core.flatMap(([exit, release]) => { + const now = this.clock.unsafeCurrentTimeMillis() + const expiredAt = now + Duration.toMillis(this.timeToLive(exit)) + switch (exit._tag) { + case "Success": { + const exitWithFinalizer: Exit.Exit<[Value, Scope.Scope.Finalizer]> = Exit.succeed([ + exit.value, + release + ]) + const completedResult = complete( + cache_.makeMapKey(key), + exitWithFinalizer, + MutableRef.make(1), + cache_.makeEntryStats(now), + expiredAt + ) + let previousValue: MapValue | undefined = undefined + if (MutableHashMap.has(this.cacheState.map, key)) { + previousValue = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, key)) + } + MutableHashMap.set(this.cacheState.map, key, completedResult) + return core.sync(() => + core.flatten( + core.as( + this.cleanMapValue(previousValue), + toScoped(completedResult) + ) + ) + ) + } + case "Failure": { + const completedResult = complete( + cache_.makeMapKey(key), + exit as Exit.Exit, + MutableRef.make(0), + cache_.makeEntryStats(now), + expiredAt + ) + let previousValue: MapValue | undefined = undefined + if (MutableHashMap.has(this.cacheState.map, key)) { + previousValue = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, key)) + } + MutableHashMap.set(this.cacheState.map, key, completedResult) + return core.zipRight( + release(exit), + core.sync(() => + core.flatten( + core.as( + this.cleanMapValue(previousValue), + toScoped(completedResult) + ) + ) + ) + ) + } + } + }), + effect.memoize, + core.flatten + ) + } + + hasExpired(timeToLive: number): boolean { + return this.clock.unsafeCurrentTimeMillis() > timeToLive + } + + trackHit(): void { + this.cacheState.hits = this.cacheState.hits + 1 + } + + trackMiss(): void { + this.cacheState.misses = this.cacheState.misses + 1 + } + + trackAccess(key: cache_.MapKey): Array> { + const cleanedKeys: Array> = [] + MutableQueue.offer(this.cacheState.accesses, key) + if (MutableRef.compareAndSet(this.cacheState.updating, false, true)) { + let loop = true + while (loop) { + const key = MutableQueue.poll(this.cacheState.accesses, MutableQueue.EmptyMutableQueue) + if (key === MutableQueue.EmptyMutableQueue) { + loop = false + } else { + this.cacheState.keys.add(key) + } + } + let size = MutableHashMap.size(this.cacheState.map) + loop = size > this.capacity + while (loop) { + const key = this.cacheState.keys.remove() + if (key === undefined) { + loop = false + } else { + if (MutableHashMap.has(this.cacheState.map, key.current)) { + const removed = Option.getOrUndefined(MutableHashMap.get(this.cacheState.map, key.current))! + MutableHashMap.remove(this.cacheState.map, key.current) + size = size - 1 + cleanedKeys.push(removed) + loop = size > this.capacity + } + } + } + MutableRef.set(this.cacheState.updating, false) + } + return cleanedKeys + } + + cleanMapValue(mapValue: MapValue | undefined): Effect.Effect { + if (mapValue === undefined) { + return core.void + } + switch (mapValue._tag) { + case "Complete": { + return releaseOwner(mapValue) + } + case "Pending": { + return core.void + } + case "Refreshing": { + return releaseOwner(mapValue.complete) + } + } + } + + ensureMapSizeNotExceeded(key: cache_.MapKey): Effect.Effect { + return fiberRuntime.forEachConcurrentDiscard( + this.trackAccess(key), + (cleanedMapValue) => this.cleanMapValue(cleanedMapValue), + false, + false + ) + } +} + +/** @internal */ +export const make = ( + options: { + readonly lookup: ScopedCache.Lookup + readonly capacity: number + readonly timeToLive: Duration.DurationInput + } +): Effect.Effect, never, Environment | Scope.Scope> => { + const timeToLive = Duration.decode(options.timeToLive) + return makeWith({ + capacity: options.capacity, + lookup: options.lookup, + timeToLive: () => timeToLive + }) +} + +/** @internal */ +export const makeWith = ( + options: { + readonly capacity: number + readonly lookup: ScopedCache.Lookup + readonly timeToLive: (exit: Exit.Exit) => Duration.DurationInput + } +): Effect.Effect, never, Environment | Scope.Scope> => + core.flatMap( + effect.clock, + (clock) => + buildWith( + options.capacity, + options.lookup, + clock, + (exit) => Duration.decode(options.timeToLive(exit)) + ) + ) + +const buildWith = ( + capacity: number, + scopedLookup: ScopedCache.Lookup, + clock: Clock.Clock, + timeToLive: (exit: Exit.Exit) => Duration.Duration +): Effect.Effect, never, Environment | Scope.Scope> => + fiberRuntime.acquireRelease( + core.flatMap( + core.context(), + (context) => + core.sync(() => + new ScopedCacheImpl( + capacity, + scopedLookup, + clock, + timeToLive, + context + ) + ) + ), + (cache) => cache.invalidateAll + ) diff --git a/backend/node_modules/effect/src/internal/scopedRef.ts b/backend/node_modules/effect/src/internal/scopedRef.ts new file mode 100644 index 0000000000000000000000000000000000000000..dca8d7d4810a8d63a544912ba545c5459f36d5c9 --- /dev/null +++ b/backend/node_modules/effect/src/internal/scopedRef.ts @@ -0,0 +1,118 @@ +import * as Context from "../Context.js" +import type * as Effect from "../Effect.js" +import type { LazyArg } from "../Function.js" +import { dual, pipe } from "../Function.js" +import type * as Scope from "../Scope.js" +import type * as ScopedRef from "../ScopedRef.js" +import * as core from "./core.js" +import * as circular from "./effect/circular.js" +import * as effectable from "./effectable.js" +import * as fiberRuntime from "./fiberRuntime.js" +import * as ref from "./ref.js" +import * as synchronized from "./synchronizedRef.js" + +/** @internal */ +const ScopedRefSymbolKey = "effect/ScopedRef" + +/** @internal */ +export const ScopedRefTypeId: ScopedRef.ScopedRefTypeId = Symbol.for( + ScopedRefSymbolKey +) as ScopedRef.ScopedRefTypeId + +/** @internal */ +const scopedRefVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +const proto: ThisType> = { + ...effectable.CommitPrototype, + commit() { + return get(this) + }, + [ScopedRefTypeId]: scopedRefVariance +} + +/** @internal */ +const close = (self: ScopedRef.ScopedRef): Effect.Effect => + core.flatMap(ref.get(self.ref), (tuple) => tuple[0].close(core.exitVoid)) + +/** @internal */ +export const fromAcquire = ( + acquire: Effect.Effect +): Effect.Effect, E, R | Scope.Scope> => + core.uninterruptible( + fiberRuntime.scopeMake().pipe(core.flatMap((newScope) => + acquire.pipe( + core.mapInputContext(Context.add(fiberRuntime.scopeTag, newScope)), + core.onError((cause) => newScope.close(core.exitFail(cause))), + core.flatMap((value) => + circular.makeSynchronized([newScope, value] as const).pipe( + core.flatMap((ref) => { + const scopedRef = Object.create(proto) + scopedRef.ref = ref + return pipe( + fiberRuntime.addFinalizer(() => close(scopedRef)), + core.as(scopedRef) + ) + }) + ) + ) + ) + )) + ) + +/** @internal */ +export const get = (self: ScopedRef.ScopedRef): Effect.Effect => + core.map(ref.get(self.ref), (tuple) => tuple[1]) + +/** @internal */ +export const make = (evaluate: LazyArg): Effect.Effect, never, Scope.Scope> => + fromAcquire(core.sync(evaluate)) + +/** @internal */ +export const set = dual< + ( + acquire: Effect.Effect + ) => (self: ScopedRef.ScopedRef) => Effect.Effect>, + ( + self: ScopedRef.ScopedRef, + acquire: Effect.Effect + ) => Effect.Effect> +>(2, ( + self: ScopedRef.ScopedRef, + acquire: Effect.Effect +) => + core.flatten( + synchronized.modifyEffect(self.ref, ([oldScope, value]) => + core.uninterruptible( + core.scopeClose(oldScope, core.exitVoid).pipe( + core.zipRight(fiberRuntime.scopeMake()), + core.flatMap((newScope) => + core.exit(fiberRuntime.scopeExtend(acquire, newScope)).pipe( + core.flatMap((exit) => + core.exitMatch(exit, { + onFailure: (cause) => + core.scopeClose(newScope, core.exitVoid).pipe( + core.as( + [ + core.failCause(cause) as Effect.Effect, + [oldScope, value] as const + ] as const + ) + ), + onSuccess: (value) => + core.succeed( + [ + core.void as Effect.Effect, + [newScope, value] as const + ] as const + ) + }) + ) + ) + ) + ) + )) + )) diff --git a/backend/node_modules/effect/src/internal/secret.ts b/backend/node_modules/effect/src/internal/secret.ts new file mode 100644 index 0000000000000000000000000000000000000000..2d12b35f6cf219f9cba92f6c33b5fbc255b21a96 --- /dev/null +++ b/backend/node_modules/effect/src/internal/secret.ts @@ -0,0 +1,89 @@ +import * as Arr from "../Array.js" +import { hasProperty } from "../Predicate.js" +import type * as Secret from "../Secret.js" +import * as redacted_ from "./redacted.js" + +/** + * @internal + * @deprecated + */ +const SecretSymbolKey = "effect/Secret" + +/** + * @internal + * @deprecated + */ +export const SecretTypeId: Secret.SecretTypeId = Symbol.for( + SecretSymbolKey +) as Secret.SecretTypeId + +/** + * @internal + * @deprecated + */ +export const isSecret = (u: unknown): u is Secret.Secret => hasProperty(u, SecretTypeId) + +const SecretProto = { + ...redacted_.proto, + [SecretTypeId]: SecretTypeId +} + +/** + * @internal + * @deprecated + */ +export const make = (bytes: Array): Secret.Secret => { + const secret = Object.create(SecretProto) + Object.defineProperty(secret, "toString", { + enumerable: false, + value() { + return "Secret()" + } + }) + Object.defineProperty(secret, "toJSON", { + enumerable: false, + value() { + return "" + } + }) + Object.defineProperty(secret, "raw", { + enumerable: false, + value: bytes + }) + redacted_.redactedRegistry.set(secret, bytes.map((byte) => String.fromCharCode(byte)).join("")) + return secret +} + +/** + * @internal + * @deprecated + */ +export const fromIterable = (iterable: Iterable): Secret.Secret => + make(Arr.fromIterable(iterable).map((char) => char.charCodeAt(0))) + +/** + * @internal + * @deprecated + */ +export const fromString = (text: string): Secret.Secret => { + return make(text.split("").map((char) => char.charCodeAt(0))) +} + +/** + * @internal + * @deprecated + */ +export const value = (self: Secret.Secret): string => { + return self.raw.map((byte) => String.fromCharCode(byte)).join("") +} + +/** + * @internal + * @deprecated + */ +export const unsafeWipe = (self: Secret.Secret): void => { + for (let i = 0; i < self.raw.length; i++) { + self.raw[i] = 0 + } + redacted_.redactedRegistry.delete(self) +} diff --git a/backend/node_modules/effect/src/internal/singleShotGen.ts b/backend/node_modules/effect/src/internal/singleShotGen.ts new file mode 100644 index 0000000000000000000000000000000000000000..1b933449ea43f67d00f20b29cb3ccd325fc0e29d --- /dev/null +++ b/backend/node_modules/effect/src/internal/singleShotGen.ts @@ -0,0 +1,35 @@ +/** @internal */ +export class SingleShotGen implements Generator { + called = false + + constructor(readonly self: T) { + } + + next(a: A): IteratorResult { + return this.called ? + ({ + value: a, + done: true + }) : + (this.called = true, + ({ + value: this.self, + done: false + })) + } + + return(a: A): IteratorResult { + return ({ + value: a, + done: true + }) + } + + throw(e: unknown): IteratorResult { + throw e + } + + [Symbol.iterator](): Generator { + return new SingleShotGen(this.self) + } +} diff --git a/backend/node_modules/effect/src/internal/sink.ts b/backend/node_modules/effect/src/internal/sink.ts new file mode 100644 index 0000000000000000000000000000000000000000..bb24b36944d49b367014363eef1c37e006258625 --- /dev/null +++ b/backend/node_modules/effect/src/internal/sink.ts @@ -0,0 +1,2120 @@ +import * as Arr from "../Array.js" +import * as Cause from "../Cause.js" +import type * as Channel from "../Channel.js" +import * as Chunk from "../Chunk.js" +import * as Clock from "../Clock.js" +import type * as Context from "../Context.js" +import * as Duration from "../Duration.js" +import * as Effect from "../Effect.js" +import * as Either from "../Either.js" +import * as Exit from "../Exit.js" +import { constTrue, dual, identity, pipe } from "../Function.js" +import type { LazyArg } from "../Function.js" +import * as HashMap from "../HashMap.js" +import * as HashSet from "../HashSet.js" +import type * as MergeDecision from "../MergeDecision.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty, type Predicate, type Refinement } from "../Predicate.js" +import * as PubSub from "../PubSub.js" +import * as Queue from "../Queue.js" +import * as Ref from "../Ref.js" +import * as Scope from "../Scope.js" +import type * as Sink from "../Sink.js" +import type * as Types from "../Types.js" +import * as channel from "./channel.js" +import * as mergeDecision from "./channel/mergeDecision.js" +import * as core from "./core-stream.js" + +/** @internal */ +export const SinkTypeId: Sink.SinkTypeId = Symbol.for("effect/Sink") as Sink.SinkTypeId + +const sinkVariance = { + /* c8 ignore next */ + _A: (_: never) => _, + /* c8 ignore next */ + _In: (_: unknown) => _, + /* c8 ignore next */ + _L: (_: never) => _, + /* c8 ignore next */ + _E: (_: never) => _, + /* c8 ignore next */ + _R: (_: never) => _ +} + +/** @internal */ +export class SinkImpl + implements Sink.Sink +{ + readonly [SinkTypeId] = sinkVariance + constructor( + readonly channel: Channel.Channel, Chunk.Chunk, E, never, A, unknown, R> + ) { + } + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const isSink = (u: unknown): u is Sink.Sink => + hasProperty(u, SinkTypeId) + +/** @internal */ +export const suspend = (evaluate: LazyArg>): Sink.Sink => + new SinkImpl(core.suspend(() => toChannel(evaluate()))) + +/** @internal */ +export const as = dual< + (a: A2) => (self: Sink.Sink) => Sink.Sink, + (self: Sink.Sink, a: A2) => Sink.Sink +>( + 2, + (self, a) => pipe(self, map(() => a)) +) + +/** @internal */ +export const collectAll = (): Sink.Sink, In> => new SinkImpl(collectAllLoop(Chunk.empty())) + +/** @internal */ +const collectAllLoop = ( + acc: Chunk.Chunk +): Channel.Channel, never, never, Chunk.Chunk, unknown> => + core.readWithCause({ + onInput: (chunk: Chunk.Chunk) => collectAllLoop(pipe(acc, Chunk.appendAll(chunk))), + onFailure: core.failCause, + onDone: () => core.succeed(acc) + }) + +/** @internal */ +export const collectAllN = (n: number): Sink.Sink, In, In> => + suspend(() => fromChannel(collectAllNLoop(n, Chunk.empty()))) + +/** @internal */ +const collectAllNLoop = ( + n: number, + acc: Chunk.Chunk +): Channel.Channel, Chunk.Chunk, never, never, Chunk.Chunk, unknown> => + core.readWithCause({ + onInput: (chunk: Chunk.Chunk) => { + const [collected, leftovers] = Chunk.splitAt(chunk, n) + if (collected.length < n) { + return collectAllNLoop(n - collected.length, Chunk.appendAll(acc, collected)) + } + if (Chunk.isEmpty(leftovers)) { + return core.succeed(Chunk.appendAll(acc, collected)) + } + return core.flatMap(core.write(leftovers), () => core.succeed(Chunk.appendAll(acc, collected))) + }, + onFailure: core.failCause, + onDone: () => core.succeed(acc) + }) + +/** @internal */ +export const collectAllFrom = ( + self: Sink.Sink +): Sink.Sink, In, L, E, R> => + collectAllWhileWith(self, { + initial: Chunk.empty(), + while: constTrue, + body: (chunk, a) => pipe(chunk, Chunk.append(a)) + }) + +/** @internal */ +export const collectAllToMap = ( + key: (input: In) => K, + merge: (x: In, y: In) => In +): Sink.Sink, In> => { + return foldLeftChunks(HashMap.empty(), (map, chunk) => + pipe( + chunk, + Chunk.reduce(map, (map, input) => { + const k: K = key(input) + const v: In = pipe(map, HashMap.has(k)) ? + merge(pipe(map, HashMap.unsafeGet(k)), input) : + input + return pipe(map, HashMap.set(k, v)) + }) + )) +} + +/** @internal */ +export const collectAllToMapN = ( + n: number, + key: (input: In) => K, + merge: (x: In, y: In) => In +): Sink.Sink, In, In> => { + return foldWeighted, In>({ + initial: HashMap.empty(), + maxCost: n, + cost: (acc, input) => pipe(acc, HashMap.has(key(input))) ? 0 : 1, + body: (acc, input) => { + const k: K = key(input) + const v: In = pipe(acc, HashMap.has(k)) ? + merge(pipe(acc, HashMap.unsafeGet(k)), input) : + input + return pipe(acc, HashMap.set(k, v)) + } + }) +} + +/** @internal */ +export const collectAllToSet = (): Sink.Sink, In> => + foldLeftChunks, In>( + HashSet.empty(), + (acc, chunk) => pipe(chunk, Chunk.reduce(acc, (acc, input) => pipe(acc, HashSet.add(input)))) + ) + +/** @internal */ +export const collectAllToSetN = (n: number): Sink.Sink, In, In> => + foldWeighted, In>({ + initial: HashSet.empty(), + maxCost: n, + cost: (acc, input) => HashSet.has(acc, input) ? 0 : 1, + body: (acc, input) => HashSet.add(acc, input) + }) + +/** @internal */ +export const collectAllUntil = (p: Predicate): Sink.Sink, In, In> => { + return pipe( + fold<[Chunk.Chunk, boolean], In>( + [Chunk.empty(), true], + (tuple) => tuple[1], + ([chunk, _], input) => [pipe(chunk, Chunk.append(input)), !p(input)] + ), + map((tuple) => tuple[0]) + ) +} + +/** @internal */ +export const collectAllUntilEffect = (p: (input: In) => Effect.Effect) => { + return pipe( + foldEffect<[Chunk.Chunk, boolean], In, E, R>( + [Chunk.empty(), true], + (tuple) => tuple[1], + ([chunk, _], input) => pipe(p(input), Effect.map((bool) => [pipe(chunk, Chunk.append(input)), !bool])) + ), + map((tuple) => tuple[0]) + ) +} + +/** @internal */ +export const collectAllWhile: { + (refinement: Refinement): Sink.Sink, In, In> + (predicate: Predicate): Sink.Sink, In, In> +} = (predicate: Predicate): Sink.Sink, In, In> => + fromChannel(collectAllWhileReader(predicate, Chunk.empty())) + +/** @internal */ +const collectAllWhileReader = ( + predicate: Predicate, + done: Chunk.Chunk +): Channel.Channel, Chunk.Chunk, never, never, Chunk.Chunk, unknown> => + core.readWith({ + onInput: (input: Chunk.Chunk) => { + const [collected, leftovers] = pipe(Chunk.toReadonlyArray(input), Arr.span(predicate)) + if (leftovers.length === 0) { + return collectAllWhileReader( + predicate, + pipe(done, Chunk.appendAll(Chunk.unsafeFromArray(collected))) + ) + } + return pipe( + core.write(Chunk.unsafeFromArray(leftovers)), + channel.zipRight(core.succeed(pipe(done, Chunk.appendAll(Chunk.unsafeFromArray(collected))))) + ) + }, + onFailure: core.fail, + onDone: () => core.succeed(done) + }) + +/** @internal */ +export const collectAllWhileEffect = ( + predicate: (input: In) => Effect.Effect +): Sink.Sink, In, In, E, R> => fromChannel(collectAllWhileEffectReader(predicate, Chunk.empty())) + +/** @internal */ +const collectAllWhileEffectReader = ( + predicate: (input: In) => Effect.Effect, + done: Chunk.Chunk +): Channel.Channel, Chunk.Chunk, E, never, Chunk.Chunk, unknown, R> => + core.readWith({ + onInput: (input: Chunk.Chunk) => + pipe( + core.fromEffect(pipe(input, Effect.takeWhile(predicate), Effect.map(Chunk.unsafeFromArray))), + core.flatMap((collected) => { + const leftovers = pipe(input, Chunk.drop(collected.length)) + if (Chunk.isEmpty(leftovers)) { + return collectAllWhileEffectReader(predicate, pipe(done, Chunk.appendAll(collected))) + } + return pipe(core.write(leftovers), channel.zipRight(core.succeed(pipe(done, Chunk.appendAll(collected))))) + }) + ), + onFailure: core.fail, + onDone: () => core.succeed(done) + }) + +/** @internal */ +export const collectAllWhileWith: { + ( + options: { + readonly initial: S + readonly while: Predicate + readonly body: (s: S, a: A) => S + } + ): (self: Sink.Sink) => Sink.Sink + ( + self: Sink.Sink, + options: { + readonly initial: S + readonly while: Predicate + readonly body: (s: S, a: A) => S + } + ): Sink.Sink +} = dual( + 2, + ( + self: Sink.Sink, + options: { + readonly initial: S + readonly while: Predicate + readonly body: (s: S, a: A) => S + } + ): Sink.Sink => { + const refs = pipe( + Ref.make(Chunk.empty()), + Effect.zip(Ref.make(false)) + ) + const newChannel = pipe( + core.fromEffect(refs), + core.flatMap(([leftoversRef, upstreamDoneRef]) => { + const upstreamMarker: Channel.Channel, Chunk.Chunk, never, never, unknown, unknown> = core + .readWith({ + onInput: (input) => pipe(core.write(input), core.flatMap(() => upstreamMarker)), + onFailure: core.fail, + onDone: (done) => pipe(core.fromEffect(Ref.set(upstreamDoneRef, true)), channel.as(done)) + }) + return pipe( + upstreamMarker, + core.pipeTo(channel.bufferChunk(leftoversRef)), + core.pipeTo( + collectAllWhileWithLoop(self, leftoversRef, upstreamDoneRef, options.initial, options.while, options.body) + ) + ) + }) + ) + return new SinkImpl(newChannel) + } +) + +const collectAllWhileWithLoop = ( + self: Sink.Sink, + leftoversRef: Ref.Ref>, + upstreamDoneRef: Ref.Ref, + currentResult: S, + p: Predicate, + f: (s: S, z: Z) => S +): Channel.Channel, Chunk.Chunk, E, never, S, unknown, R> => { + return pipe( + toChannel(self), + channel.doneCollect, + channel.foldChannel({ + onFailure: core.fail, + onSuccess: ([leftovers, doneValue]) => + p(doneValue) + ? pipe( + core.fromEffect( + Ref.set(leftoversRef, Chunk.flatten(leftovers as Chunk.Chunk>)) + ), + core.flatMap(() => + pipe( + core.fromEffect(Ref.get(upstreamDoneRef)), + core.flatMap((upstreamDone) => { + const accumulatedResult = f(currentResult, doneValue) + return upstreamDone + ? pipe(core.write(Chunk.flatten(leftovers)), channel.as(accumulatedResult)) + : collectAllWhileWithLoop(self, leftoversRef, upstreamDoneRef, accumulatedResult, p, f) + }) + ) + ) + ) + : pipe(core.write(Chunk.flatten(leftovers)), channel.as(currentResult)) + }) + ) +} + +/** @internal */ +export const collectLeftover = ( + self: Sink.Sink +): Sink.Sink<[A, Chunk.Chunk], In, never, E, R> => + new SinkImpl(pipe(core.collectElements(toChannel(self)), channel.map(([chunks, z]) => [z, Chunk.flatten(chunks)]))) + +/** @internal */ +export const mapInput = dual< + (f: (input: In0) => In) => (self: Sink.Sink) => Sink.Sink, + (self: Sink.Sink, f: (input: In0) => In) => Sink.Sink +>( + 2, + (self: Sink.Sink, f: (input: In0) => In): Sink.Sink => + pipe(self, mapInputChunks(Chunk.map(f))) +) + +/** @internal */ +export const mapInputEffect = dual< + ( + f: (input: In0) => Effect.Effect + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + f: (input: In0) => Effect.Effect + ) => Sink.Sink +>( + 2, + ( + self: Sink.Sink, + f: (input: In0) => Effect.Effect + ): Sink.Sink => + mapInputChunksEffect( + self, + (chunk) => + Effect.map( + Effect.forEach(chunk, (v) => f(v)), + Chunk.unsafeFromArray + ) + ) +) + +/** @internal */ +export const mapInputChunks = dual< + ( + f: (chunk: Chunk.Chunk) => Chunk.Chunk + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + f: (chunk: Chunk.Chunk) => Chunk.Chunk + ) => Sink.Sink +>( + 2, + ( + self: Sink.Sink, + f: (chunk: Chunk.Chunk) => Chunk.Chunk + ): Sink.Sink => { + const loop: Channel.Channel, Chunk.Chunk, never, never, unknown, unknown, R> = core.readWith({ + onInput: (chunk) => pipe(core.write(f(chunk)), core.flatMap(() => loop)), + onFailure: core.fail, + onDone: core.succeed + }) + return new SinkImpl(pipe(loop, core.pipeTo(toChannel(self)))) + } +) + +/** @internal */ +export const mapInputChunksEffect = dual< + ( + f: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2> + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + f: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2> + ) => Sink.Sink +>( + 2, + ( + self: Sink.Sink, + f: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2> + ): Sink.Sink => { + const loop: Channel.Channel, Chunk.Chunk, E2, never, unknown, unknown, R | R2> = core + .readWith({ + onInput: (chunk) => pipe(core.fromEffect(f(chunk)), core.flatMap(core.write), core.flatMap(() => loop)), + onFailure: core.fail, + onDone: core.succeed + }) + return new SinkImpl(pipe(loop, channel.pipeToOrFail(toChannel(self)))) + } +) + +/** @internal */ +export const die = (defect: unknown): Sink.Sink => failCause(Cause.die(defect)) + +/** @internal */ +export const dieMessage = (message: string): Sink.Sink => + failCause(Cause.die(new Cause.RuntimeException(message))) + +/** @internal */ +export const dieSync = (evaluate: LazyArg): Sink.Sink => + failCauseSync(() => Cause.die(evaluate())) + +/** @internal */ +export const dimap = dual< + ( + options: { + readonly onInput: (input: In0) => In + readonly onDone: (a: A) => A2 + } + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + options: { + readonly onInput: (input: In0) => In + readonly onDone: (a: A) => A2 + } + ) => Sink.Sink +>( + 2, + ( + self: Sink.Sink, + options: { + readonly onInput: (input: In0) => In + readonly onDone: (a: A) => A2 + } + ): Sink.Sink => map(mapInput(self, options.onInput), options.onDone) +) + +/** @internal */ +export const dimapEffect = dual< + ( + options: { + readonly onInput: (input: In0) => Effect.Effect + readonly onDone: (a: A) => Effect.Effect + } + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + options: { + readonly onInput: (input: In0) => Effect.Effect + readonly onDone: (a: A) => Effect.Effect + } + ) => Sink.Sink +>( + 2, + (self, options) => + mapEffect( + mapInputEffect(self, options.onInput), + options.onDone + ) +) + +/** @internal */ +export const dimapChunks = dual< + ( + options: { + readonly onInput: (chunk: Chunk.Chunk) => Chunk.Chunk + readonly onDone: (a: A) => A2 + } + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + options: { + readonly onInput: (chunk: Chunk.Chunk) => Chunk.Chunk + readonly onDone: (a: A) => A2 + } + ) => Sink.Sink +>( + 2, + (self, options) => + map( + mapInputChunks(self, options.onInput), + options.onDone + ) +) + +/** @internal */ +export const dimapChunksEffect = dual< + ( + options: { + readonly onInput: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2> + readonly onDone: (a: A) => Effect.Effect + } + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + options: { + readonly onInput: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2> + readonly onDone: (a: A) => Effect.Effect + } + ) => Sink.Sink +>( + 2, + (self, options) => mapEffect(mapInputChunksEffect(self, options.onInput), options.onDone) +) + +/** @internal */ +export const drain: Sink.Sink = new SinkImpl( + channel.drain(channel.identityChannel()) +) + +/** @internal */ +export const drop = (n: number): Sink.Sink => suspend(() => new SinkImpl(dropLoop(n))) + +/** @internal */ +const dropLoop = ( + n: number +): Channel.Channel, Chunk.Chunk, never, never, unknown, unknown> => + core.readWith({ + onInput: (input: Chunk.Chunk) => { + const dropped = pipe(input, Chunk.drop(n)) + const leftover = Math.max(n - input.length, 0) + const more = Chunk.isEmpty(input) || leftover > 0 + if (more) { + return dropLoop(leftover) + } + return pipe( + core.write(dropped), + channel.zipRight(channel.identityChannel, never, unknown>()) + ) + }, + onFailure: core.fail, + onDone: () => core.void + }) + +/** @internal */ +export const dropUntil = (predicate: Predicate): Sink.Sink => + new SinkImpl( + pipe(toChannel(dropWhile((input: In) => !predicate(input))), channel.pipeToOrFail(toChannel(drop(1)))) + ) + +/** @internal */ +export const dropUntilEffect = ( + predicate: (input: In) => Effect.Effect +): Sink.Sink => suspend(() => new SinkImpl(dropUntilEffectReader(predicate))) + +/** @internal */ +const dropUntilEffectReader = ( + predicate: (input: In) => Effect.Effect +): Channel.Channel, Chunk.Chunk, E, E, unknown, unknown, R> => + core.readWith({ + onInput: (input: Chunk.Chunk) => + pipe( + input, + Effect.dropUntil(predicate), + Effect.map((leftover) => { + const more = leftover.length === 0 + return more ? + dropUntilEffectReader(predicate) : + pipe( + core.write(Chunk.unsafeFromArray(leftover)), + channel.zipRight(channel.identityChannel, E, unknown>()) + ) + }), + channel.unwrap + ), + onFailure: core.fail, + onDone: () => core.void + }) + +/** @internal */ +export const dropWhile = (predicate: Predicate): Sink.Sink => + new SinkImpl(dropWhileReader(predicate)) + +/** @internal */ +const dropWhileReader = ( + predicate: Predicate +): Channel.Channel, Chunk.Chunk, never, never, unknown, unknown> => + core.readWith({ + onInput: (input: Chunk.Chunk) => { + const out = pipe(input, Chunk.dropWhile(predicate)) + if (Chunk.isEmpty(out)) { + return dropWhileReader(predicate) + } + return pipe(core.write(out), channel.zipRight(channel.identityChannel, never, unknown>())) + }, + onFailure: core.fail, + onDone: core.succeedNow + }) + +/** @internal */ +export const dropWhileEffect = ( + predicate: (input: In) => Effect.Effect +): Sink.Sink => suspend(() => new SinkImpl(dropWhileEffectReader(predicate))) + +/** @internal */ +const dropWhileEffectReader = ( + predicate: (input: In) => Effect.Effect +): Channel.Channel, Chunk.Chunk, E, E, unknown, unknown, R> => + core.readWith({ + onInput: (input: Chunk.Chunk) => + pipe( + input, + Effect.dropWhile(predicate), + Effect.map((leftover) => { + const more = leftover.length === 0 + return more ? + dropWhileEffectReader(predicate) : + pipe( + core.write(Chunk.unsafeFromArray(leftover)), + channel.zipRight(channel.identityChannel, E, unknown>()) + ) + }), + channel.unwrap + ), + onFailure: core.fail, + onDone: () => core.void + }) + +/** @internal */ +export const ensuring = dual< + ( + finalizer: Effect.Effect + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + finalizer: Effect.Effect + ) => Sink.Sink +>( + 2, + (self, finalizer) => new SinkImpl(pipe(self, toChannel, channel.ensuring(finalizer))) +) + +/** @internal */ +export const ensuringWith = dual< + ( + finalizer: (exit: Exit.Exit) => Effect.Effect + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + finalizer: (exit: Exit.Exit) => Effect.Effect + ) => Sink.Sink +>( + 2, + (self, finalizer) => new SinkImpl(pipe(self, toChannel, core.ensuringWith(finalizer))) +) + +/** @internal */ +export const context = (): Sink.Sink, unknown, never, never, R> => fromEffect(Effect.context()) + +/** @internal */ +export const contextWith = ( + f: (context: Context.Context) => Z +): Sink.Sink => pipe(context(), map(f)) + +/** @internal */ +export const contextWithEffect = ( + f: (context: Context.Context) => Effect.Effect +): Sink.Sink => pipe(context(), mapEffect(f)) + +/** @internal */ +export const contextWithSink = ( + f: (context: Context.Context) => Sink.Sink +): Sink.Sink => + new SinkImpl(channel.unwrap(Effect.contextWith((context) => toChannel(f(context))))) + +/** @internal */ +export const every = (predicate: Predicate): Sink.Sink => + fold(true, identity, (acc, input) => acc && predicate(input)) + +/** @internal */ +export const fail = (e: E): Sink.Sink => new SinkImpl(core.fail(e)) + +/** @internal */ +export const failSync = (evaluate: LazyArg): Sink.Sink => + new SinkImpl(core.failSync(evaluate)) + +/** @internal */ +export const failCause = (cause: Cause.Cause): Sink.Sink => + new SinkImpl(core.failCause(cause)) + +/** @internal */ +export const failCauseSync = (evaluate: LazyArg>): Sink.Sink => + new SinkImpl(core.failCauseSync(evaluate)) + +/** @internal */ +export const filterInput: { + ( + f: Refinement + ): (self: Sink.Sink) => Sink.Sink + (f: Predicate): (self: Sink.Sink) => Sink.Sink +} = (f: Predicate) => { + return (self: Sink.Sink): Sink.Sink => + pipe(self, mapInputChunks(Chunk.filter(f))) +} + +/** @internal */ +export const filterInputEffect = dual< + ( + f: (input: In1) => Effect.Effect + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + f: (input: In1) => Effect.Effect + ) => Sink.Sink +>( + 2, + (self, f) => + mapInputChunksEffect( + self, + (chunk) => Effect.map(Effect.filter(chunk, f), Chunk.unsafeFromArray) + ) +) + +/** @internal */ +export const findEffect = dual< + ( + f: (a: A) => Effect.Effect + ) => (self: Sink.Sink) => Sink.Sink, In, L, E2 | E, R2 | R>, + ( + self: Sink.Sink, + f: (a: A) => Effect.Effect + ) => Sink.Sink, In, L, E2 | E, R2 | R> +>( + 2, + ( + self: Sink.Sink, + f: (a: A) => Effect.Effect + ): Sink.Sink, In, L, E2 | E, R2 | R> => { + const newChannel = pipe( + core.fromEffect(pipe( + Ref.make(Chunk.empty()), + Effect.zip(Ref.make(false)) + )), + core.flatMap(([leftoversRef, upstreamDoneRef]) => { + const upstreamMarker: Channel.Channel, Chunk.Chunk, never, never, unknown, unknown> = core + .readWith({ + onInput: (input) => pipe(core.write(input), core.flatMap(() => upstreamMarker)), + onFailure: core.fail, + onDone: (done) => pipe(core.fromEffect(Ref.set(upstreamDoneRef, true)), channel.as(done)) + }) + const loop: Channel.Channel, Chunk.Chunk, E | E2, never, Option.Option, unknown, R | R2> = + channel.foldChannel(core.collectElements(toChannel(self)), { + onFailure: core.fail, + onSuccess: ([leftovers, doneValue]) => + pipe( + core.fromEffect(f(doneValue)), + core.flatMap((satisfied) => + pipe( + core.fromEffect(Ref.set(leftoversRef, Chunk.flatten(leftovers))), + channel.zipRight( + pipe( + core.fromEffect(Ref.get(upstreamDoneRef)), + core.flatMap((upstreamDone) => { + if (satisfied) { + return pipe(core.write(Chunk.flatten(leftovers)), channel.as(Option.some(doneValue))) + } + if (upstreamDone) { + return pipe(core.write(Chunk.flatten(leftovers)), channel.as(Option.none())) + } + return loop + }) + ) + ) + ) + ) + ) + }) + return pipe(upstreamMarker, core.pipeTo(channel.bufferChunk(leftoversRef)), core.pipeTo(loop)) + }) + ) + return new SinkImpl(newChannel) + } +) + +/** @internal */ +export const fold = ( + s: S, + contFn: Predicate, + f: (s: S, input: In) => S +): Sink.Sink => suspend(() => new SinkImpl(foldReader(s, contFn, f))) + +/** @internal */ +const foldReader = ( + s: S, + contFn: Predicate, + f: (z: S, input: In) => S +): Channel.Channel, Chunk.Chunk, never, never, S, unknown> => { + if (!contFn(s)) { + return core.succeedNow(s) + } + return core.readWith({ + onInput: (input: Chunk.Chunk) => { + const [nextS, leftovers] = foldChunkSplit(s, input, contFn, f, 0, input.length) + if (Chunk.isNonEmpty(leftovers)) { + return pipe(core.write(leftovers), channel.as(nextS)) + } + return foldReader(nextS, contFn, f) + }, + onFailure: core.fail, + onDone: () => core.succeedNow(s) + }) +} + +/** @internal */ +const foldChunkSplit = ( + s: S, + chunk: Chunk.Chunk, + contFn: Predicate, + f: (z: S, input: In) => S, + index: number, + length: number +): [S, Chunk.Chunk] => { + if (index === length) { + return [s, Chunk.empty()] + } + const s1 = f(s, pipe(chunk, Chunk.unsafeGet(index))) + if (contFn(s1)) { + return foldChunkSplit(s1, chunk, contFn, f, index + 1, length) + } + return [s1, pipe(chunk, Chunk.drop(index + 1))] +} + +/** @internal */ +export const foldSink = dual< + ( + options: { + readonly onFailure: (err: E) => Sink.Sink + readonly onSuccess: (a: A) => Sink.Sink + } + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + options: { + readonly onFailure: (err: E) => Sink.Sink + readonly onSuccess: (a: A) => Sink.Sink + } + ) => Sink.Sink +>( + 2, + ( + self: Sink.Sink, + options: { + readonly onFailure: (err: E) => Sink.Sink + readonly onSuccess: (z: A) => Sink.Sink + } + ): Sink.Sink => { + const newChannel: Channel.Channel< + Chunk.Chunk, + Chunk.Chunk, + E1 | E2, + never, + A1 | A2, + unknown, + R | R1 | R2 + > = pipe( + toChannel(self), + core.collectElements, + channel.foldChannel({ + onFailure: (error) => toChannel(options.onFailure(error)), + onSuccess: ([leftovers, z]) => + core.suspend(() => { + const leftoversRef = { + ref: pipe(leftovers, Chunk.filter(Chunk.isNonEmpty)) as Chunk.Chunk> + } + const refReader = pipe( + core.sync(() => { + const ref = leftoversRef.ref + leftoversRef.ref = Chunk.empty() + return ref + }), + // This cast is safe because of the L1 >: L <: In1 bound. It follows that + // L <: In1 and therefore Chunk[L] can be safely cast to Chunk[In1]. + core.flatMap((chunk) => channel.writeChunk(chunk as Chunk.Chunk>)) + ) + const passthrough = channel.identityChannel, never, unknown>() + const continuationSink = pipe( + refReader, + channel.zipRight(passthrough), + core.pipeTo(toChannel(options.onSuccess(z))) + ) + return core.flatMap( + core.collectElements(continuationSink), + ([newLeftovers, z1]) => + pipe( + core.succeed(leftoversRef.ref), + core.flatMap(channel.writeChunk), + channel.zipRight(channel.writeChunk(newLeftovers)), + channel.as(z1) + ) + ) + }) + }) + ) + return new SinkImpl(newChannel) + } +) + +/** @internal */ +export const foldChunks = ( + s: S, + contFn: Predicate, + f: (s: S, chunk: Chunk.Chunk) => S +): Sink.Sink => suspend(() => new SinkImpl(foldChunksReader(s, contFn, f))) + +/** @internal */ +const foldChunksReader = ( + s: S, + contFn: Predicate, + f: (s: S, chunk: Chunk.Chunk) => S +): Channel.Channel, never, never, S, unknown> => { + if (!contFn(s)) { + return core.succeedNow(s) + } + return core.readWith({ + onInput: (input: Chunk.Chunk) => foldChunksReader(f(s, input), contFn, f), + onFailure: core.fail, + onDone: () => core.succeedNow(s) + }) +} + +/** @internal */ +export const foldChunksEffect = ( + s: S, + contFn: Predicate, + f: (s: S, chunk: Chunk.Chunk) => Effect.Effect +): Sink.Sink => suspend(() => new SinkImpl(foldChunksEffectReader(s, contFn, f))) + +/** @internal */ +const foldChunksEffectReader = ( + s: S, + contFn: Predicate, + f: (s: S, chunk: Chunk.Chunk) => Effect.Effect +): Channel.Channel, E, E, S, unknown, R> => { + if (!contFn(s)) { + return core.succeedNow(s) + } + return core.readWith({ + onInput: (input: Chunk.Chunk) => + pipe( + core.fromEffect(f(s, input)), + core.flatMap((s) => foldChunksEffectReader(s, contFn, f)) + ), + onFailure: core.fail, + onDone: () => core.succeedNow(s) + }) +} + +/** @internal */ +export const foldEffect = ( + s: S, + contFn: Predicate, + f: (s: S, input: In) => Effect.Effect +): Sink.Sink => suspend(() => new SinkImpl(foldEffectReader(s, contFn, f))) + +/** @internal */ +const foldEffectReader = ( + s: S, + contFn: Predicate, + f: (s: S, input: In) => Effect.Effect +): Channel.Channel, Chunk.Chunk, E, E, S, unknown, R> => { + if (!contFn(s)) { + return core.succeedNow(s) + } + return core.readWith({ + onInput: (input: Chunk.Chunk) => + pipe( + core.fromEffect(foldChunkSplitEffect(s, input, contFn, f)), + core.flatMap(([nextS, leftovers]) => + pipe( + leftovers, + Option.match({ + onNone: () => foldEffectReader(nextS, contFn, f), + onSome: (leftover) => pipe(core.write(leftover), channel.as(nextS)) + }) + ) + ) + ), + onFailure: core.fail, + onDone: () => core.succeedNow(s) + }) +} + +/** @internal */ +const foldChunkSplitEffect = ( + s: S, + chunk: Chunk.Chunk, + contFn: Predicate, + f: (s: S, input: In) => Effect.Effect +): Effect.Effect<[S, Option.Option>], E, R> => + foldChunkSplitEffectInternal(s, chunk, 0, chunk.length, contFn, f) + +/** @internal */ +const foldChunkSplitEffectInternal = ( + s: S, + chunk: Chunk.Chunk, + index: number, + length: number, + contFn: Predicate, + f: (s: S, input: In) => Effect.Effect +): Effect.Effect<[S, Option.Option>], E, R> => { + if (index === length) { + return Effect.succeed([s, Option.none()]) + } + return pipe( + f(s, pipe(chunk, Chunk.unsafeGet(index))), + Effect.flatMap((s1) => + contFn(s1) ? + foldChunkSplitEffectInternal(s1, chunk, index + 1, length, contFn, f) : + Effect.succeed([s1, Option.some(pipe(chunk, Chunk.drop(index + 1)))]) + ) + ) +} + +/** @internal */ +export const foldLeft = (s: S, f: (s: S, input: In) => S): Sink.Sink => + ignoreLeftover(fold(s, constTrue, f)) + +/** @internal */ +export const foldLeftChunks = ( + s: S, + f: (s: S, chunk: Chunk.Chunk) => S +): Sink.Sink => foldChunks(s, constTrue, f) + +/** @internal */ +export const foldLeftChunksEffect = ( + s: S, + f: (s: S, chunk: Chunk.Chunk) => Effect.Effect +): Sink.Sink => ignoreLeftover(foldChunksEffect(s, constTrue, f)) + +/** @internal */ +export const foldLeftEffect = ( + s: S, + f: (s: S, input: In) => Effect.Effect +): Sink.Sink => foldEffect(s, constTrue, f) + +/** @internal */ +export const foldUntil = (s: S, max: number, f: (s: S, input: In) => S): Sink.Sink => + pipe( + fold<[S, number], In>( + [s, 0], + (tuple) => tuple[1] < max, + ([output, count], input) => [f(output, input), count + 1] + ), + map((tuple) => tuple[0]) + ) + +/** @internal */ +export const foldUntilEffect = ( + s: S, + max: number, + f: (s: S, input: In) => Effect.Effect +): Sink.Sink => + pipe( + foldEffect( + [s, 0 as number] as const, + (tuple) => tuple[1] < max, + ([output, count], input: In) => pipe(f(output, input), Effect.map((s) => [s, count + 1] as const)) + ), + map((tuple) => tuple[0]) + ) + +/** @internal */ +export const foldWeighted = ( + options: { + readonly initial: S + readonly maxCost: number + readonly cost: (s: S, input: In) => number + readonly body: (s: S, input: In) => S + } +): Sink.Sink => + foldWeightedDecompose({ + ...options, + decompose: Chunk.of + }) + +/** @internal */ +export const foldWeightedDecompose = ( + options: { + readonly initial: S + readonly maxCost: number + readonly cost: (s: S, input: In) => number + readonly decompose: (input: In) => Chunk.Chunk + readonly body: (s: S, input: In) => S + } +): Sink.Sink => + suspend(() => + new SinkImpl( + foldWeightedDecomposeLoop( + options.initial, + 0, + false, + options.maxCost, + options.cost, + options.decompose, + options.body + ) + ) + ) + +/** @internal */ +const foldWeightedDecomposeLoop = ( + s: S, + cost: number, + dirty: boolean, + max: number, + costFn: (s: S, input: In) => number, + decompose: (input: In) => Chunk.Chunk, + f: (s: S, input: In) => S +): Channel.Channel, Chunk.Chunk, never, never, S, unknown> => + core.readWith({ + onInput: (input: Chunk.Chunk) => { + const [nextS, nextCost, nextDirty, leftovers] = foldWeightedDecomposeFold( + input, + s, + cost, + dirty, + max, + costFn, + decompose, + f + ) + if (Chunk.isNonEmpty(leftovers)) { + return pipe(core.write(leftovers), channel.zipRight(core.succeedNow(nextS))) + } + if (cost > max) { + return core.succeedNow(nextS) + } + return foldWeightedDecomposeLoop(nextS, nextCost, nextDirty, max, costFn, decompose, f) + }, + onFailure: core.fail, + onDone: () => core.succeedNow(s) + }) + +/** @internal */ +const foldWeightedDecomposeFold = ( + input: Chunk.Chunk, + s: S, + cost: number, + dirty: boolean, + max: number, + costFn: (s: S, input: In) => number, + decompose: (input: In) => Chunk.Chunk, + f: (s: S, input: In) => S +): [S, number, boolean, Chunk.Chunk] => { + for (let index = 0; index < input.length; index++) { + const elem = Chunk.unsafeGet(input, index) + const prevCost = cost + cost = cost + costFn(s, elem) + if (cost <= max) { + s = f(s, elem) + dirty = true + continue + } + const decomposed = decompose(elem) + if (decomposed.length <= 1 && !dirty) { + // If `elem` cannot be decomposed, we need to cross the `max` threshold. To + // minimize "injury", we only allow this when we haven't added anything else + // to the aggregate (dirty = false). + return [f(s, elem), cost, true, Chunk.drop(input, index + 1)] + } + if (decomposed.length <= 1 && dirty) { + // If the state is dirty and `elem` cannot be decomposed, we stop folding + // and include `elem` in the leftovers. + return [s, prevCost, dirty, Chunk.drop(input, index)] + } + // `elem` got decomposed, so we will recurse with the decomposed elements pushed + // into the chunk we're processing and see if we can aggregate further. + input = Chunk.appendAll(decomposed, Chunk.drop(input, index + 1)) + cost = prevCost + index = -1 + } + return [s, cost, dirty, Chunk.empty()] +} + +/** @internal */ +export const foldWeightedDecomposeEffect = ( + options: { + readonly initial: S + readonly maxCost: number + readonly cost: (s: S, input: In) => Effect.Effect + readonly decompose: (input: In) => Effect.Effect, E2, R2> + readonly body: (s: S, input: In) => Effect.Effect + } +): Sink.Sink => + suspend(() => + new SinkImpl( + foldWeightedDecomposeEffectLoop( + options.initial, + options.maxCost, + options.cost, + options.decompose, + options.body, + 0, + false + ) + ) + ) + +/** @internal */ +export const foldWeightedEffect = ( + options: { + readonly initial: S + readonly maxCost: number + readonly cost: (s: S, input: In) => Effect.Effect + readonly body: (s: S, input: In) => Effect.Effect + } +): Sink.Sink => + foldWeightedDecomposeEffect({ + ...options, + decompose: (input) => Effect.succeed(Chunk.of(input)) + }) + +const foldWeightedDecomposeEffectLoop = ( + s: S, + max: number, + costFn: (s: S, input: In) => Effect.Effect, + decompose: (input: In) => Effect.Effect, E2, R2>, + f: (s: S, input: In) => Effect.Effect, + cost: number, + dirty: boolean +): Channel.Channel, Chunk.Chunk, E | E2 | E3, E | E2 | E3, S, unknown, R | R2 | R3> => + core.readWith({ + onInput: (input: Chunk.Chunk) => + pipe( + core.fromEffect(foldWeightedDecomposeEffectFold(s, max, costFn, decompose, f, input, dirty, cost, 0)), + core.flatMap(([nextS, nextCost, nextDirty, leftovers]) => { + if (Chunk.isNonEmpty(leftovers)) { + return pipe(core.write(leftovers), channel.zipRight(core.succeedNow(nextS))) + } + if (cost > max) { + return core.succeedNow(nextS) + } + return foldWeightedDecomposeEffectLoop(nextS, max, costFn, decompose, f, nextCost, nextDirty) + }) + ), + onFailure: core.fail, + onDone: () => core.succeedNow(s) + }) + +/** @internal */ +const foldWeightedDecomposeEffectFold = ( + s: S, + max: number, + costFn: (s: S, input: In) => Effect.Effect, + decompose: (input: In) => Effect.Effect, E2, R2>, + f: (s: S, input: In) => Effect.Effect, + input: Chunk.Chunk, + dirty: boolean, + cost: number, + index: number +): Effect.Effect<[S, number, boolean, Chunk.Chunk], E | E2 | E3, R | R2 | R3> => { + if (index === input.length) { + return Effect.succeed([s, cost, dirty, Chunk.empty()]) + } + const elem = pipe(input, Chunk.unsafeGet(index)) + return pipe( + costFn(s, elem), + Effect.map((newCost) => cost + newCost), + Effect.flatMap((total) => { + if (total <= max) { + return pipe( + f(s, elem), + Effect.flatMap((s) => + foldWeightedDecomposeEffectFold(s, max, costFn, decompose, f, input, true, total, index + 1) + ) + ) + } + return pipe( + decompose(elem), + Effect.flatMap((decomposed) => { + if (decomposed.length <= 1 && !dirty) { + // If `elem` cannot be decomposed, we need to cross the `max` threshold. To + // minimize "injury", we only allow this when we haven't added anything else + // to the aggregate (dirty = false). + return pipe( + f(s, elem), + Effect.map((s) => [s, total, true, pipe(input, Chunk.drop(index + 1))]) + ) + } + if (decomposed.length <= 1 && dirty) { + // If the state is dirty and `elem` cannot be decomposed, we stop folding + // and include `elem` in th leftovers. + return Effect.succeed([s, cost, dirty, pipe(input, Chunk.drop(index))]) + } + // `elem` got decomposed, so we will recurse with the decomposed elements pushed + // into the chunk we're processing and see if we can aggregate further. + const next = pipe(decomposed, Chunk.appendAll(pipe(input, Chunk.drop(index + 1)))) + return foldWeightedDecomposeEffectFold(s, max, costFn, decompose, f, next, dirty, cost, 0) + }) + ) + }) + ) +} + +/** @internal */ +export const flatMap = dual< + ( + f: (a: A) => Sink.Sink + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + f: (a: A) => Sink.Sink + ) => Sink.Sink +>( + 2, + (self, f) => foldSink(self, { onFailure: fail, onSuccess: f }) +) + +/** @internal */ +export const forEach = (f: (input: In) => Effect.Effect): Sink.Sink => { + const process: Channel.Channel, E, E, void, unknown, R> = core.readWithCause({ + onInput: (input: Chunk.Chunk) => + pipe(core.fromEffect(Effect.forEach(input, (v) => f(v), { discard: true })), core.flatMap(() => process)), + onFailure: core.failCause, + onDone: () => core.void + }) + return new SinkImpl(process) +} + +/** @internal */ +export const forEachChunk = ( + f: (input: Chunk.Chunk) => Effect.Effect +): Sink.Sink => { + const process: Channel.Channel, E, E, void, unknown, R> = core.readWithCause({ + onInput: (input: Chunk.Chunk) => pipe(core.fromEffect(f(input)), core.flatMap(() => process)), + onFailure: core.failCause, + onDone: () => core.void + }) + return new SinkImpl(process) +} + +/** @internal */ +export const forEachWhile = ( + f: (input: In) => Effect.Effect +): Sink.Sink => { + const process: Channel.Channel, Chunk.Chunk, E, E, void, unknown, R> = core.readWithCause({ + onInput: (input: Chunk.Chunk) => forEachWhileReader(f, input, 0, input.length, process), + onFailure: core.failCause, + onDone: () => core.void + }) + return new SinkImpl(process) +} + +/** @internal */ +const forEachWhileReader = ( + f: (input: In) => Effect.Effect, + input: Chunk.Chunk, + index: number, + length: number, + cont: Channel.Channel, Chunk.Chunk, E, E, void, unknown, R> +): Channel.Channel, Chunk.Chunk, E, E, void, unknown, R> => { + if (index === length) { + return cont + } + return pipe( + core.fromEffect(f(pipe(input, Chunk.unsafeGet(index)))), + core.flatMap((bool) => + bool ? + forEachWhileReader(f, input, index + 1, length, cont) : + core.write(pipe(input, Chunk.drop(index))) + ), + channel.catchAll((error) => pipe(core.write(pipe(input, Chunk.drop(index))), channel.zipRight(core.fail(error)))) + ) +} + +/** @internal */ +export const forEachChunkWhile = ( + f: (input: Chunk.Chunk) => Effect.Effect +): Sink.Sink => { + const reader: Channel.Channel, E, E, void, unknown, R> = core.readWith({ + onInput: (input: Chunk.Chunk) => + pipe( + core.fromEffect(f(input)), + core.flatMap((cont) => cont ? reader : core.void) + ), + onFailure: core.fail, + onDone: () => core.void + }) + return new SinkImpl(reader) +} + +/** @internal */ +export const fromChannel = ( + channel: Channel.Channel, Chunk.Chunk, E, never, A, unknown, R> +): Sink.Sink => new SinkImpl(channel) + +/** @internal */ +export const fromEffect = (effect: Effect.Effect): Sink.Sink => + new SinkImpl(core.fromEffect(effect)) + +/** @internal */ +export const fromPubSub = ( + pubsub: PubSub.PubSub, + options?: { + readonly shutdown?: boolean | undefined + } +): Sink.Sink => fromQueue(pubsub, options) + +/** @internal */ +export const fromPush = ( + push: Effect.Effect< + (_: Option.Option>) => Effect.Effect, Chunk.Chunk], R>, + never, + R + > +): Sink.Sink> => + new SinkImpl(channel.unwrapScoped(pipe(push, Effect.map(fromPushPull)))) + +const fromPushPull = ( + push: ( + option: Option.Option> + ) => Effect.Effect, Chunk.Chunk], R> +): Channel.Channel, Chunk.Chunk, E, never, Z, unknown, R> => + core.readWith({ + onInput: (input: Chunk.Chunk) => + channel.foldChannel(core.fromEffect(push(Option.some(input))), { + onFailure: ([either, leftovers]) => + Either.match(either, { + onLeft: (error) => pipe(core.write(leftovers), channel.zipRight(core.fail(error))), + onRight: (z) => pipe(core.write(leftovers), channel.zipRight(core.succeedNow(z))) + }), + onSuccess: () => fromPushPull(push) + }), + onFailure: core.fail, + onDone: () => + channel.foldChannel(core.fromEffect(push(Option.none())), { + onFailure: ([either, leftovers]) => + Either.match(either, { + onLeft: (error) => pipe(core.write(leftovers), channel.zipRight(core.fail(error))), + onRight: (z) => pipe(core.write(leftovers), channel.zipRight(core.succeedNow(z))) + }), + onSuccess: () => + core.fromEffect( + Effect.dieMessage( + "BUG: Sink.fromPush - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + ) + }) + }) + +/** @internal */ +export const fromQueue = ( + queue: Queue.Enqueue, + options?: { + readonly shutdown?: boolean | undefined + } +): Sink.Sink => + options?.shutdown ? + unwrapScoped( + Effect.map( + Effect.acquireRelease(Effect.succeed(queue), Queue.shutdown), + fromQueue + ) + ) : + forEachChunk((input: Chunk.Chunk) => Queue.offerAll(queue, input)) + +/** @internal */ +export const head = (): Sink.Sink, In, In> => + fold( + Option.none() as Option.Option, + Option.isNone, + (option, input) => + Option.match(option, { + onNone: () => Option.some(input), + onSome: () => option + }) + ) + +/** @internal */ +export const ignoreLeftover = (self: Sink.Sink): Sink.Sink => + new SinkImpl(channel.drain(toChannel(self))) + +/** @internal */ +export const last = (): Sink.Sink, In, In> => + foldLeftChunks(Option.none(), (s, input) => Option.orElse(Chunk.last(input), () => s)) + +/** @internal */ +export const leftover = (chunk: Chunk.Chunk): Sink.Sink => + new SinkImpl(core.suspend(() => core.write(chunk))) + +/** @internal */ +export const map = dual< + (f: (a: A) => A2) => (self: Sink.Sink) => Sink.Sink, + (self: Sink.Sink, f: (a: A) => A2) => Sink.Sink +>(2, (self, f) => { + return new SinkImpl(pipe(toChannel(self), channel.map(f))) +}) + +/** @internal */ +export const mapEffect = dual< + ( + f: (a: A) => Effect.Effect + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + f: (a: A) => Effect.Effect + ) => Sink.Sink +>( + 2, + (self, f) => new SinkImpl(pipe(toChannel(self), channel.mapEffect(f))) +) + +/** @internal */ +export const mapError = dual< + (f: (error: E) => E2) => (self: Sink.Sink) => Sink.Sink, + (self: Sink.Sink, f: (error: E) => E2) => Sink.Sink +>( + 2, + (self, f) => new SinkImpl(pipe(toChannel(self), channel.mapError(f))) +) + +/** @internal */ +export const mapLeftover = dual< + (f: (leftover: L) => L2) => (self: Sink.Sink) => Sink.Sink, + (self: Sink.Sink, f: (leftover: L) => L2) => Sink.Sink +>( + 2, + (self, f) => new SinkImpl(pipe(toChannel(self), channel.mapOut(Chunk.map(f)))) +) + +/** @internal */ +export const never: Sink.Sink = fromEffect(Effect.never) + +/** @internal */ +export const orElse = dual< + ( + that: LazyArg> + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + that: LazyArg> + ) => Sink.Sink +>( + 2, + ( + self: Sink.Sink, + that: LazyArg> + ): Sink.Sink => + new SinkImpl( + pipe(toChannel(self), channel.orElse(() => toChannel(that()))) + ) +) + +/** @internal */ +export const provideContext = dual< + (context: Context.Context) => (self: Sink.Sink) => Sink.Sink, + (self: Sink.Sink, context: Context.Context) => Sink.Sink +>( + 2, + (self, context) => new SinkImpl(pipe(toChannel(self), core.provideContext(context))) +) + +/** @internal */ +export const race = dual< + ( + that: Sink.Sink + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + that: Sink.Sink + ) => Sink.Sink +>( + 2, + (self, that) => pipe(self, raceBoth(that), map(Either.merge)) +) + +/** @internal */ +export const raceBoth = dual< + ( + that: Sink.Sink, + options?: { + readonly capacity?: number | undefined + } + ) => ( + self: Sink.Sink + ) => Sink.Sink, In & In1, L1 | L, E1 | E, R1 | R>, + ( + self: Sink.Sink, + that: Sink.Sink, + options?: { + readonly capacity?: number | undefined + } + ) => Sink.Sink, In & In1, L1 | L, E1 | E, R1 | R> +>( + (args) => isSink(args[1]), + (self, that, options) => + raceWith(self, { + other: that, + onSelfDone: (selfDone) => mergeDecision.Done(Effect.map(selfDone, Either.left)), + onOtherDone: (thatDone) => mergeDecision.Done(Effect.map(thatDone, Either.right)), + capacity: options?.capacity ?? 16 + }) +) + +/** @internal */ +export const raceWith = dual< + ( + options: { + readonly other: Sink.Sink + readonly onSelfDone: (exit: Exit.Exit) => MergeDecision.MergeDecision + readonly onOtherDone: (exit: Exit.Exit) => MergeDecision.MergeDecision + readonly capacity?: number | undefined + } + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + options: { + readonly other: Sink.Sink + readonly onSelfDone: (exit: Exit.Exit) => MergeDecision.MergeDecision + readonly onOtherDone: (exit: Exit.Exit) => MergeDecision.MergeDecision + readonly capacity?: number | undefined + } + ) => Sink.Sink +>( + 2, + ( + self: Sink.Sink, + options: { + readonly other: Sink.Sink + readonly onSelfDone: (exit: Exit.Exit) => MergeDecision.MergeDecision + readonly onOtherDone: (exit: Exit.Exit) => MergeDecision.MergeDecision + readonly capacity?: number | undefined + } + ): Sink.Sink => { + function race(scope: Scope.Scope) { + return Effect.gen(function*() { + const pubsub = yield* PubSub.bounded< + Either.Either, Exit.Exit> + >(options?.capacity ?? 16) + const subscription1 = yield* Scope.extend(PubSub.subscribe(pubsub), scope) + const subscription2 = yield* Scope.extend(PubSub.subscribe(pubsub), scope) + const reader = channel.toPubSub(pubsub) + const writer = channel.fromQueue(subscription1).pipe( + core.pipeTo(toChannel(self)), + channel.zipLeft(core.fromEffect(Queue.shutdown(subscription1))), + channel.mergeWith({ + other: channel.fromQueue(subscription2).pipe( + core.pipeTo(toChannel(options.other)), + channel.zipLeft(core.fromEffect(Queue.shutdown(subscription2))) + ), + onSelfDone: options.onSelfDone, + onOtherDone: options.onOtherDone + }) + ) + const racedChannel = channel.mergeWith(reader, { + other: writer, + onSelfDone: () => mergeDecision.Await(identity), + onOtherDone: (exit) => mergeDecision.Done(exit) + }) as Channel.Channel< + Chunk.Chunk, + Chunk.Chunk, + E | E2, + never, + A3 | A4, + unknown, + R | R2 + > + return new SinkImpl(racedChannel) + }) + } + return unwrapScopedWith(race) + } +) + +/** @internal */ +export const refineOrDie = dual< + ( + pf: (error: E) => Option.Option + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + pf: (error: E) => Option.Option + ) => Sink.Sink +>( + 2, + (self, pf) => pipe(self, refineOrDieWith(pf, identity)) +) + +/** @internal */ +export const refineOrDieWith = dual< + ( + pf: (error: E) => Option.Option, + f: (error: E) => unknown + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + pf: (error: E) => Option.Option, + f: (error: E) => unknown + ) => Sink.Sink +>( + 3, + (self, pf, f) => { + const newChannel = pipe( + self, + toChannel, + channel.catchAll((error) => + Option.match(pf(error), { + onNone: () => core.failCauseSync(() => Cause.die(f(error))), + onSome: core.fail + }) + ) + ) + return new SinkImpl(newChannel) + } +) + +/** @internal */ +export const service = ( + tag: Context.Tag +): Sink.Sink => serviceWith(tag, identity) + +/** @internal */ +export const serviceWith = ( + tag: Context.Tag, + f: (service: Types.NoInfer) => Z +): Sink.Sink => fromEffect(Effect.map(tag, f)) + +/** @internal */ +export const serviceWithEffect = ( + tag: Context.Tag, + f: (service: Types.NoInfer) => Effect.Effect +): Sink.Sink => fromEffect(Effect.flatMap(tag, f)) + +/** @internal */ +export const serviceWithSink = ( + tag: Context.Tag, + f: (service: Types.NoInfer) => Sink.Sink +): Sink.Sink => + new SinkImpl(pipe(Effect.map(tag, (service) => toChannel(f(service))), channel.unwrap)) + +/** @internal */ +export const some = (predicate: Predicate): Sink.Sink => + fold(false, (bool) => !bool, (acc, input) => acc || predicate(input)) + +/** @internal */ +export const splitWhere = dual< + (f: Predicate) => (self: Sink.Sink) => Sink.Sink, + (self: Sink.Sink, f: Predicate) => Sink.Sink +>(2, (self: Sink.Sink, f: Predicate): Sink.Sink => { + const newChannel = pipe( + core.fromEffect(Ref.make(Chunk.empty())), + core.flatMap((ref) => + pipe( + splitWhereSplitter(false, ref, f), + channel.pipeToOrFail(toChannel(self)), + core.collectElements, + core.flatMap(([leftovers, z]) => + pipe( + core.fromEffect(Ref.get(ref)), + core.flatMap((leftover) => + pipe( + core.write>(pipe(leftover, Chunk.appendAll(Chunk.flatten(leftovers)))), + channel.zipRight(core.succeed(z)) + ) + ) + ) + ) + ) + ) + ) + return new SinkImpl(newChannel) +}) + +/** @internal */ +const splitWhereSplitter = ( + written: boolean, + leftovers: Ref.Ref>, + f: Predicate +): Channel.Channel, Chunk.Chunk, E, never, unknown, unknown> => + core.readWithCause({ + onInput: (input) => { + if (Chunk.isEmpty(input)) { + return splitWhereSplitter(written, leftovers, f) + } + if (written) { + const index = indexWhere(input, f) + if (index === -1) { + return channel.zipRight( + core.write(input), + splitWhereSplitter(true, leftovers, f) + ) + } + const [left, right] = Chunk.splitAt(input, index) + return channel.zipRight( + core.write(left), + core.fromEffect(Ref.set(leftovers, right)) + ) + } + const index = indexWhere(input, f, 1) + if (index === -1) { + return channel.zipRight( + core.write(input), + splitWhereSplitter(true, leftovers, f) + ) + } + const [left, right] = pipe(input, Chunk.splitAt(Math.max(index, 1))) + return channel.zipRight(core.write(left), core.fromEffect(Ref.set(leftovers, right))) + }, + onFailure: core.failCause, + onDone: core.succeed + }) + +/** @internal */ +const indexWhere = (self: Chunk.Chunk, predicate: Predicate, from = 0): number => { + const iterator = self[Symbol.iterator]() + let index = 0 + let result = -1 + let next: IteratorResult + while (result < 0 && (next = iterator.next()) && !next.done) { + const a = next.value + if (index >= from && predicate(a)) { + result = index + } + index = index + 1 + } + return result +} + +/** @internal */ +export const succeed = (a: A): Sink.Sink => new SinkImpl(core.succeed(a)) + +/** @internal */ +export const sum: Sink.Sink = foldLeftChunks( + 0, + (acc, chunk) => acc + Chunk.reduce(chunk, 0, (s, a) => s + a) +) + +/** @internal */ +export const summarized = dual< + ( + summary: Effect.Effect, + f: (start: A2, end: A2) => A3 + ) => (self: Sink.Sink) => Sink.Sink<[A, A3], In, L, E2 | E, R2 | R>, + ( + self: Sink.Sink, + summary: Effect.Effect, + f: (start: A2, end: A2) => A3 + ) => Sink.Sink<[A, A3], In, L, E2 | E, R2 | R> +>( + 3, + (self, summary, f) => { + const newChannel = pipe( + core.fromEffect(summary), + core.flatMap((start) => + pipe( + self, + toChannel, + core.flatMap((done) => + pipe( + core.fromEffect(summary), + channel.map((end) => [done, f(start, end)]) + ) + ) + ) + ) + ) + return new SinkImpl(newChannel) + } +) + +/** @internal */ +export const sync = (evaluate: LazyArg): Sink.Sink => new SinkImpl(core.sync(evaluate)) + +/** @internal */ +export const take = (n: number): Sink.Sink, In, In> => + pipe( + foldChunks, In>( + Chunk.empty(), + (chunk) => chunk.length < n, + (acc, chunk) => pipe(acc, Chunk.appendAll(chunk)) + ), + flatMap((acc) => { + const [taken, leftover] = pipe(acc, Chunk.splitAt(n)) + return new SinkImpl(pipe(core.write(leftover), channel.zipRight(core.succeedNow(taken)))) + }) + ) + +/** @internal */ +export const toChannel = ( + self: Sink.Sink +): Channel.Channel, Chunk.Chunk, E, never, A, unknown, R> => + Effect.isEffect(self) ? + toChannel(fromEffect(self as Effect.Effect)) : + (self as SinkImpl).channel + +/** @internal */ +export const unwrap = ( + effect: Effect.Effect, E, R> +): Sink.Sink => + new SinkImpl( + channel.unwrap(pipe(effect, Effect.map((sink) => toChannel(sink)))) + ) + +/** @internal */ +export const unwrapScoped = ( + effect: Effect.Effect, E, R> +): Sink.Sink> => + new SinkImpl( + channel.unwrapScoped(effect.pipe( + Effect.map((sink) => toChannel(sink)) + )) + ) + +/** @internal */ +export const unwrapScopedWith = ( + f: (scope: Scope.Scope) => Effect.Effect, E, R> +): Sink.Sink => + new SinkImpl( + channel.unwrapScopedWith((scope) => + f(scope).pipe( + Effect.map((sink) => toChannel(sink)) + ) + ) + ) + +/** @internal */ +export const withDuration = ( + self: Sink.Sink +): Sink.Sink<[A, Duration.Duration], In, L, E, R> => + pipe(self, summarized(Clock.currentTimeMillis, (start, end) => Duration.millis(end - start))) + +/** @internal */ +export const zip = dual< + ( + that: Sink.Sink, + options?: { + readonly concurrent?: boolean | undefined + } + ) => (self: Sink.Sink) => Sink.Sink<[A, A2], In & In2, L | L2, E2 | E, R2 | R>, + ( + self: Sink.Sink, + that: Sink.Sink, + options?: { + readonly concurrent?: boolean | undefined + } + ) => Sink.Sink<[A, A2], In & In2, L | L2, E2 | E, R2 | R> +>( + (args) => isSink(args[1]), + ( + self: Sink.Sink, + that: Sink.Sink, + options?: { + readonly concurrent?: boolean | undefined + } + ): Sink.Sink<[A, A2], In & In2, L | L2, E2 | E, R2 | R> => zipWith(self, that, (z, z2) => [z, z2], options) +) + +/** @internal */ +export const zipLeft = dual< + ( + that: Sink.Sink, + options?: { + readonly concurrent?: boolean | undefined + } + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + that: Sink.Sink, + options?: { + readonly concurrent?: boolean | undefined + } + ) => Sink.Sink +>( + (args) => isSink(args[1]), + ( + self: Sink.Sink, + that: Sink.Sink, + options?: { + readonly concurrent?: boolean | undefined + } + ): Sink.Sink => zipWith(self, that, (z, _) => z, options) +) + +/** @internal */ +export const zipRight = dual< + ( + that: Sink.Sink, + options?: { + readonly concurrent?: boolean | undefined + } + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + that: Sink.Sink, + options?: { + readonly concurrent?: boolean | undefined + } + ) => Sink.Sink +>( + (args) => isSink(args[1]), + ( + self: Sink.Sink, + that: Sink.Sink, + options?: { + readonly concurrent?: boolean | undefined + } + ): Sink.Sink => zipWith(self, that, (_, z2) => z2, options) +) + +/** @internal */ +export const zipWith = dual< + ( + that: Sink.Sink, + f: (a: A, a2: A2) => A3, + options?: { + readonly concurrent?: boolean | undefined + } + ) => (self: Sink.Sink) => Sink.Sink, + ( + self: Sink.Sink, + that: Sink.Sink, + f: (a: A, a2: A2) => A3, + options?: { + readonly concurrent?: boolean | undefined + } + ) => Sink.Sink +>( + (args) => isSink(args[1]), + ( + self: Sink.Sink, + that: Sink.Sink, + f: (a: A, a2: A2) => A3, + options?: { + readonly concurrent?: boolean | undefined + } + ): Sink.Sink => + options?.concurrent ? + raceWith(self, { + other: that, + onSelfDone: Exit.match({ + onFailure: (cause) => mergeDecision.Done(Effect.failCause(cause)), + onSuccess: (leftZ) => + mergeDecision.Await( + Exit.match({ + onFailure: Effect.failCause, + onSuccess: (rightZ) => Effect.succeed(f(leftZ, rightZ)) + }) + ) + }), + onOtherDone: Exit.match({ + onFailure: (cause) => mergeDecision.Done(Effect.failCause(cause)), + onSuccess: (rightZ) => + mergeDecision.Await( + Exit.match({ + onFailure: Effect.failCause, + onSuccess: (leftZ) => Effect.succeed(f(leftZ, rightZ)) + }) + ) + }) + }) : + flatMap(self, (z) => map(that, (z2) => f(z, z2))) +) + +// Circular with Channel + +/** @internal */ +export const channelToSink = ( + self: Channel.Channel, Chunk.Chunk, OutErr, InErr, OutDone, unknown, Env> +): Sink.Sink => new SinkImpl(self) + +// Constants + +/** @internal */ +export const count: Sink.Sink = foldLeftChunks( + 0, + (acc, chunk) => acc + chunk.length +) + +/** @internal */ +export const mkString: Sink.Sink = suspend(() => { + const strings: Array = [] + return pipe( + foldLeftChunks(void 0, (_, elems) => + Chunk.map(elems, (elem) => { + strings.push(String(elem)) + })), + map(() => strings.join("")) + ) +}) + +/** @internal */ +export const timed: Sink.Sink = pipe( + withDuration(drain), + map((tuple) => tuple[1]) +) diff --git a/backend/node_modules/effect/src/internal/stack.ts b/backend/node_modules/effect/src/internal/stack.ts new file mode 100644 index 0000000000000000000000000000000000000000..27a9ec97a584828dc9e5e8b4cf6d3db9d6074435 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stack.ts @@ -0,0 +1,10 @@ +/** @internal */ +export interface Stack { + readonly value: A + readonly previous: Stack | undefined +} + +export const make = (value: A, previous?: Stack): Stack => ({ + value, + previous +}) diff --git a/backend/node_modules/effect/src/internal/stm/core.ts b/backend/node_modules/effect/src/internal/stm/core.ts new file mode 100644 index 0000000000000000000000000000000000000000..d3a16b215acd1c0da2725ebe5d5f6e38f7dc7b4a --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/core.ts @@ -0,0 +1,817 @@ +import * as Cause from "../../Cause.js" +import * as Context from "../../Context.js" +import * as Effect from "../../Effect.js" +import * as Either from "../../Either.js" +import * as Equal from "../../Equal.js" +import * as Exit from "../../Exit.js" +import type * as FiberId from "../../FiberId.js" +import * as FiberRef from "../../FiberRef.js" +import type { LazyArg } from "../../Function.js" +import { constVoid, dual, pipe } from "../../Function.js" +import * as Hash from "../../Hash.js" +import type * as Option from "../../Option.js" +import { pipeArguments } from "../../Pipeable.js" +import { hasProperty } from "../../Predicate.js" +import type * as Scheduler from "../../Scheduler.js" +import type * as STM from "../../STM.js" +import { internalCall, YieldWrap } from "../../Utils.js" +import { ChannelTypeId } from "../core-stream.js" +import { withFiberRuntime } from "../core.js" +import { effectVariance, StreamTypeId } from "../effectable.js" +import { OP_COMMIT } from "../opCodes/effect.js" +import { SingleShotGen } from "../singleShotGen.js" +import { SinkTypeId } from "../sink.js" +import * as Journal from "./journal.js" +import * as OpCodes from "./opCodes/stm.js" +import * as TExitOpCodes from "./opCodes/tExit.js" +import * as TryCommitOpCodes from "./opCodes/tryCommit.js" +import * as STMState from "./stmState.js" +import * as TExit from "./tExit.js" +import * as TryCommit from "./tryCommit.js" +import * as TxnId from "./txnId.js" + +/** @internal */ +const STMSymbolKey = "effect/STM" + +/** @internal */ +export const STMTypeId: STM.STMTypeId = Symbol.for( + STMSymbolKey +) as STM.STMTypeId + +/** @internal */ +export type Primitive = + | STMEffect + | STMOnFailure + | STMOnRetry + | STMOnSuccess + | STMProvide + | STMSync + | STMSucceed + | STMRetry + | STMFail + | STMDie + | STMInterrupt + +/** @internal */ +type Op = STM.STM & Body & { + readonly _op: OP_COMMIT + readonly effect_instruction_i0: Tag +} + +/** @internal */ +interface STMEffect extends + Op + ) => STM.STM + }> +{} + +/** @internal */ +interface STMOnFailure extends + Op + readonly effect_instruction_i2: (error: unknown) => STM.STM + }> +{} + +/** @internal */ +interface STMOnRetry extends + Op + readonly effect_instruction_i2: () => STM.STM + }> +{} + +/** @internal */ +interface STMOnSuccess extends + Op + readonly effect_instruction_i2: (a: unknown) => STM.STM + }> +{} + +/** @internal */ +interface STMProvide extends + Op + readonly effect_instruction_i2: (context: Context.Context) => Context.Context + }> +{} + +/** @internal */ +interface STMSync extends + Op unknown + }> +{} + +/** @internal */ +interface STMSucceed extends + Op +{} + +/** @internal */ +interface STMRetry extends Op {} + +/** @internal */ +interface STMFail extends + Op + }> +{} + +/** @internal */ +interface STMDie extends + Op + }> +{} + +/** @internal */ +interface STMInterrupt extends + Op +{} + +const stmVariance = { + /* c8 ignore next */ + _R: (_: never) => _, + /* c8 ignore next */ + _E: (_: never) => _, + /* c8 ignore next */ + _A: (_: never) => _ +} + +/** @internal */ +class STMPrimitive implements STM.STM { + public _op = OP_COMMIT + public effect_instruction_i1: any = undefined + public effect_instruction_i2: any = undefined; + [Effect.EffectTypeId]: any; + [StreamTypeId]: any; + [SinkTypeId]: any; + [ChannelTypeId]: any + get [STMTypeId]() { + return stmVariance + } + constructor(readonly effect_instruction_i0: Primitive["effect_instruction_i0"]) { + this[Effect.EffectTypeId] = effectVariance + this[StreamTypeId] = stmVariance + this[SinkTypeId] = stmVariance + this[ChannelTypeId] = stmVariance + } + [Equal.symbol](this: {}, that: unknown) { + return this === that + } + [Hash.symbol](this: {}) { + return Hash.cached(this, Hash.random(this)) + } + [Symbol.iterator]() { + return new SingleShotGen(new YieldWrap(this)) as any + } + commit(this: STM.STM): Effect.Effect { + return unsafeAtomically(this, constVoid, constVoid) + } + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const isSTM = (u: unknown): u is STM.STM => hasProperty(u, STMTypeId) + +/** @internal */ +export const commit = (self: STM.STM): Effect.Effect => + unsafeAtomically(self, constVoid, constVoid) + +/** @internal */ +export const unsafeAtomically = ( + self: STM.STM, + onDone: (exit: Exit.Exit) => unknown, + onInterrupt: LazyArg +): Effect.Effect => + withFiberRuntime((state) => { + const fiberId = state.id() + const env = state.getFiberRef(FiberRef.currentContext) as Context.Context + const scheduler = state.getFiberRef(FiberRef.currentScheduler) + const priority = state.getFiberRef(FiberRef.currentSchedulingPriority) + const commitResult = tryCommitSync(fiberId, self, env, scheduler, priority) + switch (commitResult._tag) { + case TryCommitOpCodes.OP_DONE: { + onDone(commitResult.exit) + return commitResult.exit + } + case TryCommitOpCodes.OP_SUSPEND: { + const txnId = TxnId.make() + const state: { value: STMState.STMState } = { value: STMState.running } + const effect = Effect.async( + (k: (effect: Effect.Effect) => unknown): void => + tryCommitAsync(fiberId, self, txnId, state, env, scheduler, priority, k) + ) + return Effect.uninterruptibleMask((restore) => + pipe( + restore(effect), + Effect.catchAllCause((cause) => { + let currentState = state.value + if (STMState.isRunning(currentState)) { + state.value = STMState.interrupted + } + currentState = state.value + if (STMState.isDone(currentState)) { + onDone(currentState.exit) + return currentState.exit + } + onInterrupt() + return Effect.failCause(cause) + }) + ) + ) + } + } + }) + +/** @internal */ +const tryCommit = ( + fiberId: FiberId.FiberId, + stm: STM.STM, + state: { value: STMState.STMState }, + env: Context.Context, + scheduler: Scheduler.Scheduler, + priority: number +): TryCommit.TryCommit => { + const journal: Journal.Journal = new Map() + const tExit = new STMDriver(stm, journal, fiberId, env).run() + const analysis = Journal.analyzeJournal(journal) + + if (analysis === Journal.JournalAnalysisReadWrite) { + Journal.commitJournal(journal) + } else if (analysis === Journal.JournalAnalysisInvalid) { + throw new Error( + "BUG: STM.TryCommit.tryCommit - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + } + + switch (tExit._tag) { + case TExitOpCodes.OP_SUCCEED: { + state.value = STMState.fromTExit(tExit) + return completeTodos(Exit.succeed(tExit.value), journal, scheduler, priority) + } + case TExitOpCodes.OP_FAIL: { + state.value = STMState.fromTExit(tExit) + const cause = Cause.fail(tExit.error) + return completeTodos( + Exit.failCause(cause), + journal, + scheduler, + priority + ) + } + case TExitOpCodes.OP_DIE: { + state.value = STMState.fromTExit(tExit) + const cause = Cause.die(tExit.defect) + return completeTodos( + Exit.failCause(cause), + journal, + scheduler, + priority + ) + } + case TExitOpCodes.OP_INTERRUPT: { + state.value = STMState.fromTExit(tExit) + const cause = Cause.interrupt(fiberId) + return completeTodos( + Exit.failCause(cause), + journal, + scheduler, + priority + ) + } + case TExitOpCodes.OP_RETRY: { + return TryCommit.suspend(journal) + } + } +} + +/** @internal */ +const tryCommitSync = ( + fiberId: FiberId.FiberId, + stm: STM.STM, + env: Context.Context, + scheduler: Scheduler.Scheduler, + priority: number +): TryCommit.TryCommit => { + const journal: Journal.Journal = new Map() + const tExit = new STMDriver(stm, journal, fiberId, env).run() + const analysis = Journal.analyzeJournal(journal) + + if (analysis === Journal.JournalAnalysisReadWrite && TExit.isSuccess(tExit)) { + Journal.commitJournal(journal) + } else if (analysis === Journal.JournalAnalysisInvalid) { + throw new Error( + "BUG: STM.TryCommit.tryCommitSync - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + } + + switch (tExit._tag) { + case TExitOpCodes.OP_SUCCEED: { + return completeTodos(Exit.succeed(tExit.value), journal, scheduler, priority) + } + case TExitOpCodes.OP_FAIL: { + const cause = Cause.fail(tExit.error) + return completeTodos( + Exit.failCause(cause), + journal, + scheduler, + priority + ) + } + case TExitOpCodes.OP_DIE: { + const cause = Cause.die(tExit.defect) + return completeTodos( + Exit.failCause(cause), + journal, + scheduler, + priority + ) + } + case TExitOpCodes.OP_INTERRUPT: { + const cause = Cause.interrupt(fiberId) + return completeTodos( + Exit.failCause(cause), + journal, + scheduler, + priority + ) + } + case TExitOpCodes.OP_RETRY: { + return TryCommit.suspend(journal) + } + } +} + +/** @internal */ +const tryCommitAsync = ( + fiberId: FiberId.FiberId, + self: STM.STM, + txnId: TxnId.TxnId, + state: { value: STMState.STMState }, + context: Context.Context, + scheduler: Scheduler.Scheduler, + priority: number, + k: (effect: Effect.Effect) => unknown +) => { + if (STMState.isRunning(state.value)) { + const result = tryCommit(fiberId, self, state, context, scheduler, priority) + switch (result._tag) { + case TryCommitOpCodes.OP_DONE: { + completeTryCommit(result.exit, k) + break + } + case TryCommitOpCodes.OP_SUSPEND: { + Journal.addTodo( + txnId, + result.journal, + () => tryCommitAsync(fiberId, self, txnId, state, context, scheduler, priority, k) + ) + break + } + } + } +} + +/** @internal */ +const completeTodos = ( + exit: Exit.Exit, + journal: Journal.Journal, + scheduler: Scheduler.Scheduler, + priority: number +): TryCommit.TryCommit => { + const todos = Journal.collectTodos(journal) + if (todos.size > 0) { + scheduler.scheduleTask(() => Journal.execTodos(todos), priority) + } + return TryCommit.done(exit) +} + +/** @internal */ +const completeTryCommit = ( + exit: Exit.Exit, + k: (effect: Effect.Effect) => unknown +): void => { + k(exit) +} + +/** @internal */ +type Continuation = STMOnFailure | STMOnSuccess | STMOnRetry + +/** @internal */ +export const context = (): STM.STM, never, R> => + effect>((_, __, env) => env) + +/** @internal */ +export const contextWith = (f: (environment: Context.Context) => R): STM.STM => + map(context(), f) + +/** @internal */ +export const contextWithSTM = ( + f: (environment: Context.Context) => STM.STM +): STM.STM => flatMap(context(), f) + +/** @internal */ +export class STMDriver { + private contStack: Array = [] + private env: Context.Context + + constructor( + readonly self: STM.STM, + readonly journal: Journal.Journal, + readonly fiberId: FiberId.FiberId, + r0: Context.Context + ) { + this.env = r0 as Context.Context + } + + getEnv(): Context.Context { + return this.env + } + + pushStack(cont: Continuation) { + this.contStack.push(cont) + } + + popStack() { + return this.contStack.pop() + } + + nextSuccess() { + let current = this.popStack() + while (current !== undefined && current.effect_instruction_i0 !== OpCodes.OP_ON_SUCCESS) { + current = this.popStack() + } + return current + } + + nextFailure() { + let current = this.popStack() + while (current !== undefined && current.effect_instruction_i0 !== OpCodes.OP_ON_FAILURE) { + current = this.popStack() + } + return current + } + + nextRetry() { + let current = this.popStack() + while (current !== undefined && current.effect_instruction_i0 !== OpCodes.OP_ON_RETRY) { + current = this.popStack() + } + return current + } + + run(): TExit.TExit { + let curr = this.self as Primitive | Context.Tag | Either.Either | Option.Option | undefined + let exit: TExit.TExit | undefined = undefined + while (exit === undefined && curr !== undefined) { + try { + const current = curr + if (current) { + switch (current._op) { + case "Tag": { + curr = effect((_, __, env) => Context.unsafeGet(env, current)) as Primitive + break + } + case "Left": { + curr = fail(current.left) as Primitive + break + } + case "None": { + curr = fail(new Cause.NoSuchElementException()) as Primitive + break + } + case "Right": { + curr = succeed(current.right) as Primitive + break + } + case "Some": { + curr = succeed(current.value) as Primitive + break + } + case "Commit": { + switch (current.effect_instruction_i0) { + case OpCodes.OP_DIE: { + exit = TExit.die(internalCall(() => current.effect_instruction_i1())) + break + } + case OpCodes.OP_FAIL: { + const cont = this.nextFailure() + if (cont === undefined) { + exit = TExit.fail(internalCall(() => current.effect_instruction_i1())) + } else { + curr = internalCall(() => + cont.effect_instruction_i2( + internalCall(() => current.effect_instruction_i1()) + ) as Primitive + ) + } + break + } + case OpCodes.OP_RETRY: { + const cont = this.nextRetry() + if (cont === undefined) { + exit = TExit.retry + } else { + curr = internalCall(() => cont.effect_instruction_i2() as Primitive) + } + break + } + case OpCodes.OP_INTERRUPT: { + exit = TExit.interrupt(this.fiberId) + break + } + case OpCodes.OP_WITH_STM_RUNTIME: { + curr = internalCall(() => + current.effect_instruction_i1(this as STMDriver) as Primitive + ) + break + } + case OpCodes.OP_ON_SUCCESS: + case OpCodes.OP_ON_FAILURE: + case OpCodes.OP_ON_RETRY: { + this.pushStack(current) + curr = current.effect_instruction_i1 as Primitive + break + } + case OpCodes.OP_PROVIDE: { + const env = this.env + this.env = internalCall(() => current.effect_instruction_i2(env)) + curr = pipe( + current.effect_instruction_i1, + ensuring(sync(() => (this.env = env))) + ) as Primitive + break + } + case OpCodes.OP_SUCCEED: { + const value = current.effect_instruction_i1 + const cont = this.nextSuccess() + if (cont === undefined) { + exit = TExit.succeed(value) + } else { + curr = internalCall(() => cont.effect_instruction_i2(value) as Primitive) + } + break + } + case OpCodes.OP_SYNC: { + const value = internalCall(() => current.effect_instruction_i1()) + const cont = this.nextSuccess() + if (cont === undefined) { + exit = TExit.succeed(value) + } else { + curr = internalCall(() => cont.effect_instruction_i2(value) as Primitive) + } + break + } + } + break + } + } + } + } catch (e) { + curr = die(e) as Primitive + } + } + return exit as TExit.TExit + } +} + +/** @internal */ +export const catchAll = dual< + ( + f: (e: E) => STM.STM + ) => ( + self: STM.STM + ) => STM.STM, + ( + self: STM.STM, + f: (e: E) => STM.STM + ) => STM.STM +>(2, (self, f) => { + const stm = new STMPrimitive(OpCodes.OP_ON_FAILURE) + stm.effect_instruction_i1 = self + stm.effect_instruction_i2 = f + return stm +}) + +/** @internal */ +export const mapInputContext = dual< + ( + f: (context: Context.Context) => Context.Context + ) => ( + self: STM.STM + ) => STM.STM, + ( + self: STM.STM, + f: (context: Context.Context) => Context.Context + ) => STM.STM +>(2, (self, f) => { + const stm = new STMPrimitive(OpCodes.OP_PROVIDE) + stm.effect_instruction_i1 = self + stm.effect_instruction_i2 = f + return stm +}) + +/** @internal */ +export const die = (defect: unknown): STM.STM => dieSync(() => defect) + +/** @internal */ +export const dieMessage = (message: string): STM.STM => dieSync(() => new Cause.RuntimeException(message)) + +/** @internal */ +export const dieSync = (evaluate: LazyArg): STM.STM => { + const stm = new STMPrimitive(OpCodes.OP_DIE) + stm.effect_instruction_i1 = evaluate + return stm as any +} + +/** @internal */ +export const effect = ( + f: (journal: Journal.Journal, fiberId: FiberId.FiberId, environment: Context.Context) => A +): STM.STM => withSTMRuntime((_) => succeed(f(_.journal, _.fiberId, _.getEnv()))) + +/** @internal */ +export const ensuring = dual< + (finalizer: STM.STM) => (self: STM.STM) => STM.STM, + (self: STM.STM, finalizer: STM.STM) => STM.STM +>(2, (self, finalizer) => + matchSTM(self, { + onFailure: (e) => zipRight(finalizer, fail(e)), + onSuccess: (a) => zipRight(finalizer, succeed(a)) + })) + +/** @internal */ +export const fail = (error: E): STM.STM => failSync(() => error) + +/** @internal */ +export const failSync = (evaluate: LazyArg): STM.STM => { + const stm = new STMPrimitive(OpCodes.OP_FAIL) + stm.effect_instruction_i1 = evaluate + return stm as any +} + +/** @internal */ +export const flatMap = dual< + (f: (a: A) => STM.STM) => (self: STM.STM) => STM.STM, + (self: STM.STM, f: (a: A) => STM.STM) => STM.STM +>(2, (self, f) => { + const stm = new STMPrimitive(OpCodes.OP_ON_SUCCESS) + stm.effect_instruction_i1 = self + stm.effect_instruction_i2 = f + return stm +}) + +/** @internal */ +export const matchSTM = dual< + ( + options: { + readonly onFailure: (e: E) => STM.STM + readonly onSuccess: (a: A) => STM.STM + } + ) => (self: STM.STM) => STM.STM, + ( + self: STM.STM, + options: { + readonly onFailure: (e: E) => STM.STM + readonly onSuccess: (a: A) => STM.STM + } + ) => STM.STM +>(2, ( + self: STM.STM, + { onFailure, onSuccess }: { + readonly onFailure: (e: E) => STM.STM + readonly onSuccess: (a: A) => STM.STM + } +): STM.STM => + pipe( + self, + map(Either.right), + catchAll((e) => pipe(onFailure(e), map(Either.left))), + flatMap((either): STM.STM => { + switch (either._tag) { + case "Left": { + return succeed(either.left) + } + case "Right": { + return onSuccess(either.right) + } + } + }) + )) + +/** @internal */ +export const withSTMRuntime = ( + f: (runtime: STMDriver) => STM.STM +): STM.STM => { + const stm = new STMPrimitive(OpCodes.OP_WITH_STM_RUNTIME) + stm.effect_instruction_i1 = f + return stm +} + +/** @internal */ +export const interrupt: STM.STM = withSTMRuntime((_) => { + const stm = new STMPrimitive(OpCodes.OP_INTERRUPT) + stm.effect_instruction_i1 = _.fiberId + return stm as any +}) + +/** @internal */ +export const interruptAs = (fiberId: FiberId.FiberId): STM.STM => { + const stm = new STMPrimitive(OpCodes.OP_INTERRUPT) + stm.effect_instruction_i1 = fiberId + return stm as any +} + +/** @internal */ +export const map = dual< + (f: (a: A) => B) => (self: STM.STM) => STM.STM, + (self: STM.STM, f: (a: A) => B) => STM.STM +>(2, (self, f) => pipe(self, flatMap((a) => sync(() => f(a))))) + +/** @internal */ +export const orTry = dual< + ( + that: LazyArg> + ) => ( + self: STM.STM + ) => STM.STM, + ( + self: STM.STM, + that: LazyArg> + ) => STM.STM +>(2, (self, that) => { + const stm = new STMPrimitive(OpCodes.OP_ON_RETRY) + stm.effect_instruction_i1 = self + stm.effect_instruction_i2 = that + return stm +}) + +/** @internal */ +export const retry: STM.STM = new STMPrimitive(OpCodes.OP_RETRY) + +/** @internal */ +export const succeed = (value: A): STM.STM => { + const stm = new STMPrimitive(OpCodes.OP_SUCCEED) + stm.effect_instruction_i1 = value + return stm as any +} + +/** @internal */ +export const sync = (evaluate: () => A): STM.STM => { + const stm = new STMPrimitive(OpCodes.OP_SYNC) + stm.effect_instruction_i1 = evaluate + return stm as any +} + +/** @internal */ +export const zip = dual< + ( + that: STM.STM + ) => ( + self: STM.STM + ) => STM.STM<[A, A1], E1 | E, R1 | R>, + ( + self: STM.STM, + that: STM.STM + ) => STM.STM<[A, A1], E1 | E, R1 | R> +>(2, (self, that) => pipe(self, zipWith(that, (a, a1) => [a, a1]))) + +/** @internal */ +export const zipLeft = dual< + (that: STM.STM) => (self: STM.STM) => STM.STM, + (self: STM.STM, that: STM.STM) => STM.STM +>(2, (self, that) => pipe(self, flatMap((a) => pipe(that, map(() => a))))) + +/** @internal */ +export const zipRight = dual< + (that: STM.STM) => (self: STM.STM) => STM.STM, + (self: STM.STM, that: STM.STM) => STM.STM +>(2, (self, that) => pipe(self, flatMap(() => that))) + +/** @internal */ +export const zipWith = dual< + ( + that: STM.STM, + f: (a: A, b: A1) => A2 + ) => ( + self: STM.STM + ) => STM.STM, + ( + self: STM.STM, + that: STM.STM, + f: (a: A, b: A1) => A2 + ) => STM.STM +>( + 3, + (self, that, f) => pipe(self, flatMap((a) => pipe(that, map((b) => f(a, b))))) +) diff --git a/backend/node_modules/effect/src/internal/stm/entry.ts b/backend/node_modules/effect/src/internal/stm/entry.ts new file mode 100644 index 0000000000000000000000000000000000000000..82aab45db33bba0c75d7d621d22c7d1d7caf2f71 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/entry.ts @@ -0,0 +1,59 @@ +import type * as TRef from "../../TRef.js" +import * as Versioned from "./versioned.js" + +/** @internal */ +export interface Entry { + readonly ref: TRef.TRef + readonly expected: Versioned.Versioned + isChanged: boolean // mutable by design + readonly isNew: boolean + newValue: any // mutable by design +} + +/** @internal */ +export const make = (ref: TRef.TRef, isNew: boolean): Entry => ({ + ref, + isNew, + isChanged: false, + expected: ref.versioned, + newValue: ref.versioned.value +}) + +export const unsafeGet = (self: Entry): unknown => { + return self.newValue +} + +/** @internal */ +export const unsafeSet = (self: Entry, value: unknown): void => { + self.isChanged = true + self.newValue = value +} + +/** @internal */ +export const commit = (self: Entry): void => { + self.ref.versioned = new Versioned.Versioned(self.newValue) +} + +/** @internal */ +export const copy = (self: Entry): Entry => ({ + ref: self.ref, + isNew: self.isNew, + isChanged: self.isChanged, + expected: self.expected, + newValue: self.newValue +}) + +/** @internal */ +export const isValid = (self: Entry): boolean => { + return self.ref.versioned === self.expected +} + +/** @internal */ +export const isInvalid = (self: Entry): boolean => { + return self.ref.versioned !== self.expected +} + +/** @internal */ +export const isChanged = (self: Entry): boolean => { + return self.isChanged +} diff --git a/backend/node_modules/effect/src/internal/stm/journal.ts b/backend/node_modules/effect/src/internal/stm/journal.ts new file mode 100644 index 0000000000000000000000000000000000000000..6226d9a16878f9cae61251a81d7c3cf542596469 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/journal.ts @@ -0,0 +1,123 @@ +import type * as TRef from "../../TRef.js" +import * as Entry from "./entry.js" +import type * as TxnId from "./txnId.js" + +/** @internal */ +export type Journal = Map, Entry.Entry> + +/** @internal */ +export type Todo = () => unknown + +/** @internal */ +export type JournalAnalysis = JournalAnalysisInvalid | JournalAnalysisReadWrite | JournalAnalysisReadOnly + +/** @internal */ +export const JournalAnalysisInvalid = "Invalid" as const + +/** @internal */ +export type JournalAnalysisInvalid = typeof JournalAnalysisInvalid + +/** @internal */ +export const JournalAnalysisReadWrite = "ReadWrite" as const + +/** @internal */ +export type JournalAnalysisReadWrite = typeof JournalAnalysisReadWrite + +/** @internal */ +export const JournalAnalysisReadOnly = "ReadOnly" as const + +/** @internal */ +export type JournalAnalysisReadOnly = typeof JournalAnalysisReadOnly + +/** @internal */ +export const commitJournal = (journal: Journal) => { + for (const entry of journal) { + Entry.commit(entry[1]) + } +} + +/** + * Analyzes the journal, determining whether it is valid and whether it is + * read only in a single pass. Note that information on whether the + * journal is read only will only be accurate if the journal is valid, due + * to short-circuiting that occurs on an invalid journal. + * + * @internal + */ +export const analyzeJournal = (journal: Journal): JournalAnalysis => { + let val: JournalAnalysis = JournalAnalysisReadOnly + for (const [, entry] of journal) { + val = Entry.isInvalid(entry) ? JournalAnalysisInvalid : Entry.isChanged(entry) ? JournalAnalysisReadWrite : val + if (val === JournalAnalysisInvalid) { + return val + } + } + return val +} + +/** @internal */ +export const prepareResetJournal = (journal: Journal): () => void => { + const saved: Journal = new Map, Entry.Entry>() + for (const entry of journal) { + saved.set(entry[0], Entry.copy(entry[1])) + } + return () => { + journal.clear() + for (const entry of saved) { + journal.set(entry[0], entry[1]) + } + } +} + +/** @internal */ +export const collectTodos = (journal: Journal): Map => { + const allTodos: Map = new Map() + for (const [, entry] of journal) { + for (const todo of entry.ref.todos) { + allTodos.set(todo[0], todo[1]) + } + entry.ref.todos = new Map() + } + return allTodos +} + +/** @internal */ +export const execTodos = (todos: Map) => { + const todosSorted = Array.from(todos.entries()).sort((x, y) => x[0] - y[0]) + for (const [_, todo] of todosSorted) { + todo() + } +} + +/** @internal */ +export const addTodo = ( + txnId: TxnId.TxnId, + journal: Journal, + todoEffect: Todo +): boolean => { + let added = false + for (const [, entry] of journal) { + if (!entry.ref.todos.has(txnId)) { + entry.ref.todos.set(txnId, todoEffect) + added = true + } + } + return added +} + +/** @internal */ +export const isValid = (journal: Journal): boolean => { + let valid = true + for (const [, entry] of journal) { + valid = Entry.isValid(entry) + if (!valid) { + return valid + } + } + return valid +} + +/** @internal */ +export const isInvalid = (journal: Journal): boolean => { + return !isValid(journal) +} diff --git a/backend/node_modules/effect/src/internal/stm/stm.ts b/backend/node_modules/effect/src/internal/stm/stm.ts new file mode 100644 index 0000000000000000000000000000000000000000..42ac738bcdb6e9869a283e04f274faea9df77312 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/stm.ts @@ -0,0 +1,1453 @@ +import * as RA from "../../Array.js" +import * as Cause from "../../Cause.js" +import * as Chunk from "../../Chunk.js" +import * as Context from "../../Context.js" +import * as Effect from "../../Effect.js" +import * as Either from "../../Either.js" +import * as Exit from "../../Exit.js" +import type * as FiberId from "../../FiberId.js" +import type { LazyArg } from "../../Function.js" +import { constFalse, constTrue, constVoid, dual, identity, pipe } from "../../Function.js" +import * as Option from "../../Option.js" +import type { Predicate, Refinement } from "../../Predicate.js" +import * as predicate from "../../Predicate.js" +import type * as STM from "../../STM.js" +import type * as Types from "../../Types.js" +import { yieldWrapGet } from "../../Utils.js" +import * as effectCore from "../core.js" +import * as core from "./core.js" +import * as Journal from "./journal.js" +import * as STMState from "./stmState.js" + +/** @internal */ +export const acquireUseRelease = dual< + ( + use: (resource: A) => STM.STM, + release: (resource: A) => STM.STM + ) => ( + acquire: STM.STM + ) => Effect.Effect, + ( + acquire: STM.STM, + use: (resource: A) => STM.STM, + release: (resource: A) => STM.STM + ) => Effect.Effect +>(3, ( + acquire: STM.STM, + use: (resource: A) => STM.STM, + release: (resource: A) => STM.STM +): Effect.Effect => + Effect.uninterruptibleMask((restore) => { + let state: STMState.STMState = STMState.running + return pipe( + restore( + core.unsafeAtomically( + acquire, + (exit) => { + state = STMState.done(exit) + }, + () => { + state = STMState.interrupted + } + ) + ), + Effect.matchCauseEffect({ + onFailure: (cause) => { + if (STMState.isDone(state) && Exit.isSuccess(state.exit)) { + return pipe( + release(state.exit.value), + Effect.matchCauseEffect({ + onFailure: (cause2) => Effect.failCause(Cause.parallel(cause, cause2)), + onSuccess: () => Effect.failCause(cause) + }) + ) + } + return Effect.failCause(cause) + }, + onSuccess: (a) => + pipe( + restore(use(a)), + Effect.matchCauseEffect({ + onFailure: (cause) => + pipe( + release(a), + Effect.matchCauseEffect({ + onFailure: (cause2) => Effect.failCause(Cause.parallel(cause, cause2)), + onSuccess: () => Effect.failCause(cause) + }) + ), + onSuccess: (a2) => pipe(release(a), Effect.as(a2)) + }) + ) + }) + ) + })) + +/** @internal */ +export const as = dual< + (value: A2) => (self: STM.STM) => STM.STM, + (self: STM.STM, value: A2) => STM.STM +>(2, (self, value) => pipe(self, core.map(() => value))) + +/** @internal */ +export const asSome = (self: STM.STM): STM.STM, E, R> => + pipe(self, core.map(Option.some)) + +/** @internal */ +export const asSomeError = (self: STM.STM): STM.STM, R> => + pipe(self, mapError(Option.some)) + +/** @internal */ +export const asVoid = (self: STM.STM): STM.STM => pipe(self, core.map(constVoid)) + +/** @internal */ +export const attempt = (evaluate: LazyArg): STM.STM => + suspend(() => { + try { + return core.succeed(evaluate()) + } catch (defect) { + return core.fail(defect) + } + }) + +export const bind = dual< + ( + tag: Exclude, + f: (_: K) => STM.STM + ) => (self: STM.STM) => STM.STM, E | E2, R | R2>, + ( + self: STM.STM, + tag: Exclude, + f: (_: K) => STM.STM + ) => STM.STM, E | E2, R | R2> +>(3, ( + self: STM.STM, + tag: Exclude, + f: (_: K) => STM.STM +) => + core.flatMap(self, (k) => + core.map( + f(k), + (a): Types.MergeRecord => ({ ...k, [tag]: a } as any) + ))) + +/* @internal */ +export const bindTo = dual< + (tag: N) => (self: STM.STM) => STM.STM< + Record, + E, + R + >, + ( + self: STM.STM, + tag: N + ) => STM.STM< + Record, + E, + R + > +>( + 2, + (self: STM.STM, tag: N): STM.STM, E, R> => + core.map(self, (a) => ({ [tag]: a } as Record)) +) + +/* @internal */ +export const let_ = dual< + ( + tag: Exclude, + f: (_: K) => A + ) => (self: STM.STM) => STM.STM< + Types.MergeRecord, + E, + R + >, + ( + self: STM.STM, + tag: Exclude, + f: (_: K) => A + ) => STM.STM< + Types.MergeRecord, + E, + R + > +>(3, (self: STM.STM, tag: Exclude, f: (_: K) => A) => + core.map( + self, + (k): Types.MergeRecord => ({ ...k, [tag]: f(k) } as any) + )) + +/** @internal */ +export const catchSome = dual< + ( + pf: (error: E) => Option.Option> + ) => ( + self: STM.STM + ) => STM.STM, + ( + self: STM.STM, + pf: (error: E) => Option.Option> + ) => STM.STM +>(2, ( + self: STM.STM, + pf: (error: E) => Option.Option> +): STM.STM => + core.catchAll( + self, + (e): STM.STM => Option.getOrElse(pf(e), () => core.fail(e)) + )) + +/** @internal */ +export const catchTag = dual< + ( + k: K, + f: (e: Extract) => STM.STM + ) => (self: STM.STM) => STM.STM | E1, R | R1>, + ( + self: STM.STM, + k: K, + f: (e: Extract) => STM.STM + ) => STM.STM | E1, R | R1> +>(3, (self, k, f) => + core.catchAll(self, (e) => { + if ("_tag" in e && e["_tag"] === k) { + return f(e as any) + } + return core.fail(e as any) + })) + +/** @internal */ +export const catchTags: { + < + E extends { _tag: string }, + Cases extends { + [K in E["_tag"]]+?: (error: Extract) => STM.STM + } + >( + cases: Cases + ): (self: STM.STM) => STM.STM< + | A + | { + [K in keyof Cases]: Cases[K] extends ((...args: Array) => STM.STM) ? A : never + }[keyof Cases], + | Exclude + | { + [K in keyof Cases]: Cases[K] extends ((...args: Array) => STM.STM) ? E : never + }[keyof Cases], + | R + | { + [K in keyof Cases]: Cases[K] extends ((...args: Array) => STM.STM) ? R : never + }[keyof Cases] + > + < + R, + E extends { _tag: string }, + A, + Cases extends { + [K in E["_tag"]]+?: (error: Extract) => STM.STM + } + >( + self: STM.STM, + cases: Cases + ): STM.STM< + | A + | { + [K in keyof Cases]: Cases[K] extends ((...args: Array) => STM.STM) ? A : never + }[keyof Cases], + | Exclude + | { + [K in keyof Cases]: Cases[K] extends ((...args: Array) => STM.STM) ? E : never + }[keyof Cases], + | R + | { + [K in keyof Cases]: Cases[K] extends ((...args: Array) => STM.STM) ? R : never + }[keyof Cases] + > +} = dual(2, (self, cases) => + core.catchAll(self, (e: any) => { + const keys = Object.keys(cases) + if ("_tag" in e && keys.includes(e["_tag"])) { + return cases[e["_tag"]](e as any) + } + return core.fail(e as any) + })) + +/** @internal */ +export const check = (predicate: LazyArg): STM.STM => suspend(() => predicate() ? void_ : core.retry) + +/** @internal */ +export const collect = dual< + (pf: (a: A) => Option.Option) => (self: STM.STM) => STM.STM, + (self: STM.STM, pf: (a: A) => Option.Option) => STM.STM +>(2, (self, pf) => + collectSTM( + self, + (a) => Option.map(pf(a), core.succeed) + )) + +/** @internal */ +export const collectSTM = dual< + ( + pf: (a: A) => Option.Option> + ) => ( + self: STM.STM + ) => STM.STM, + ( + self: STM.STM, + pf: (a: A) => Option.Option> + ) => STM.STM +>(2, (self, pf) => + core.matchSTM(self, { + onFailure: core.fail, + onSuccess: (a) => { + const option = pf(a) + return Option.isSome(option) ? option.value : core.retry + } + })) + +/** @internal */ +export const commitEither = (self: STM.STM): Effect.Effect => + Effect.flatten(core.commit(either(self))) + +/** @internal */ +export const cond = ( + predicate: LazyArg, + error: LazyArg, + result: LazyArg +): STM.STM => { + return suspend( + () => predicate() ? core.sync(result) : core.failSync(error) + ) +} + +/** @internal */ +export const either = (self: STM.STM): STM.STM, never, R> => + match(self, { onFailure: Either.left, onSuccess: Either.right }) + +/** @internal */ +export const eventually = (self: STM.STM): STM.STM => + core.matchSTM(self, { onFailure: () => eventually(self), onSuccess: core.succeed }) + +/** @internal */ +export const every = dual< + ( + predicate: (a: Types.NoInfer) => STM.STM + ) => (iterable: Iterable) => STM.STM, + (iterable: Iterable, predicate: (a: A) => STM.STM) => STM.STM +>( + 2, + ( + iterable: Iterable, + predicate: (a: A) => STM.STM + ): STM.STM => + core.flatMap(core.sync(() => iterable[Symbol.iterator]()), (iterator) => { + const loop: STM.STM = suspend(() => { + const next = iterator.next() + if (next.done) { + return core.succeed(true) + } + return pipe( + predicate(next.value), + core.flatMap((bool) => bool ? loop : core.succeed(bool)) + ) + }) + return loop + }) +) + +/** @internal */ +export const exists = dual< + ( + predicate: (a: Types.NoInfer) => STM.STM + ) => (iterable: Iterable) => STM.STM, + (iterable: Iterable, predicate: (a: A) => STM.STM) => STM.STM +>( + 2, + (iterable: Iterable, predicate: (a: A) => STM.STM): STM.STM => + core.flatMap(core.sync(() => iterable[Symbol.iterator]()), (iterator) => { + const loop: STM.STM = suspend(() => { + const next = iterator.next() + if (next.done) { + return core.succeed(false) + } + return core.flatMap( + predicate(next.value), + (bool) => bool ? core.succeed(bool) : loop + ) + }) + return loop + }) +) + +/** @internal */ +export const fiberId: STM.STM = core.effect((_, fiberId) => fiberId) + +/** @internal */ +export const filter = dual< + ( + predicate: (a: Types.NoInfer) => STM.STM + ) => (iterable: Iterable) => STM.STM, E, R>, + (iterable: Iterable, predicate: (a: A) => STM.STM) => STM.STM, E, R> +>( + 2, + (iterable: Iterable, predicate: (a: A) => STM.STM): STM.STM, E, R> => + Array.from(iterable).reduce( + (acc, curr) => + pipe( + acc, + core.zipWith(predicate(curr), (as, p) => { + if (p) { + as.push(curr) + return as + } + return as + }) + ), + core.succeed([]) as STM.STM, E, R> + ) +) + +/** @internal */ +export const filterNot = dual< + ( + predicate: (a: Types.NoInfer) => STM.STM + ) => (iterable: Iterable) => STM.STM, E, R>, + (iterable: Iterable, predicate: (a: A) => STM.STM) => STM.STM, E, R> +>( + 2, + (iterable: Iterable, predicate: (a: A) => STM.STM): STM.STM, E, R> => + filter(iterable, (a) => negate(predicate(a))) +) + +/** @internal */ +export const filterOrDie: { + ( + refinement: Refinement, B>, + defect: LazyArg + ): (self: STM.STM) => STM.STM + ( + predicate: Predicate>, + defect: LazyArg + ): (self: STM.STM) => STM.STM + ( + self: STM.STM, + refinement: Refinement, + defect: LazyArg + ): STM.STM + (self: STM.STM, predicate: Predicate, defect: LazyArg): STM.STM +} = dual( + 3, + (self: STM.STM, predicate: Predicate, defect: LazyArg): STM.STM => + filterOrElse(self, predicate, () => core.dieSync(defect)) +) + +/** @internal */ +export const filterOrDieMessage: { + ( + refinement: Refinement, B>, + message: string + ): (self: STM.STM) => STM.STM + (predicate: Predicate>, message: string): (self: STM.STM) => STM.STM + (self: STM.STM, refinement: Refinement, message: string): STM.STM + (self: STM.STM, predicate: Predicate, message: string): STM.STM +} = dual( + 3, + (self: STM.STM, predicate: Predicate, message: string): STM.STM => + filterOrElse(self, predicate, () => core.dieMessage(message)) +) + +/** @internal */ +export const filterOrElse: { + ( + refinement: Refinement, B>, + orElse: (a: Types.NoInfer) => STM.STM + ): (self: STM.STM) => STM.STM + ( + predicate: Predicate>, + orElse: (a: Types.NoInfer) => STM.STM + ): (self: STM.STM) => STM.STM + ( + self: STM.STM, + refinement: Refinement, + orElse: (a: A) => STM.STM + ): STM.STM + ( + self: STM.STM, + predicate: Predicate, + orElse: (a: A) => STM.STM + ): STM.STM +} = dual( + 3, + ( + self: STM.STM, + predicate: Predicate, + orElse: (a: A) => STM.STM + ): STM.STM => + core.flatMap(self, (a): STM.STM => predicate(a) ? core.succeed(a) : orElse(a)) +) + +/** @internal */ +export const filterOrFail: { + ( + refinement: Refinement, B>, + orFailWith: (a: Types.NoInfer) => E2 + ): (self: STM.STM) => STM.STM + ( + predicate: Predicate>, + orFailWith: (a: Types.NoInfer) => E2 + ): (self: STM.STM) => STM.STM + ( + self: STM.STM, + refinement: Refinement, + orFailWith: (a: A) => E2 + ): STM.STM + (self: STM.STM, predicate: Predicate, orFailWith: (a: A) => E2): STM.STM +} = dual( + 3, + (self: STM.STM, predicate: Predicate, orFailWith: (a: A) => E2): STM.STM => + filterOrElse( + self, + predicate, + (a) => core.failSync(() => orFailWith(a)) + ) +) + +/** @internal */ +export const flatten = (self: STM.STM, E, R>): STM.STM => + core.flatMap(self, identity) + +/** @internal */ +export const flip = (self: STM.STM): STM.STM => + core.matchSTM(self, { onFailure: core.succeed, onSuccess: core.fail }) + +/** @internal */ +export const flipWith = dual< + ( + f: (stm: STM.STM) => STM.STM + ) => ( + self: STM.STM + ) => STM.STM, + ( + self: STM.STM, + f: (stm: STM.STM) => STM.STM + ) => STM.STM +>(2, (self, f) => flip(f(flip(self)))) + +/** @internal */ +export const match = dual< + (options: { + readonly onFailure: (error: E) => A2 + readonly onSuccess: (value: A) => A3 + }) => (self: STM.STM) => STM.STM, + (self: STM.STM, options: { + readonly onFailure: (error: E) => A2 + readonly onSuccess: (value: A) => A3 + }) => STM.STM +>(2, (self, { onFailure, onSuccess }) => + core.matchSTM(self, { + onFailure: (e) => core.succeed(onFailure(e)), + onSuccess: (a) => core.succeed(onSuccess(a)) + })) + +/** @internal */ +export const forEach = dual< + { + (f: (a: A) => STM.STM, options?: { + readonly discard?: false | undefined + }): (elements: Iterable) => STM.STM, E, R> + (f: (a: A) => STM.STM, options: { + readonly discard: true + }): (elements: Iterable) => STM.STM + }, + { + (elements: Iterable, f: (a: A) => STM.STM, options?: { + readonly discard?: false | undefined + }): STM.STM, E, R> + (elements: Iterable, f: (a: A) => STM.STM, options: { + readonly discard: true + }): STM.STM + } +>( + (args) => predicate.isIterable(args[0]), + (iterable: Iterable, f: (a: A) => STM.STM, options?: { + readonly discard?: boolean | undefined + }): STM.STM => { + if (options?.discard) { + return pipe( + core.sync(() => iterable[Symbol.iterator]()), + core.flatMap((iterator) => { + const loop: STM.STM = suspend(() => { + const next = iterator.next() + if (next.done) { + return void_ + } + return pipe(f(next.value), core.flatMap(() => loop)) + }) + return loop + }) + ) + } + + return suspend(() => + RA.fromIterable(iterable).reduce( + (acc, curr) => + core.zipWith(acc, f(curr), (array, elem) => { + array.push(elem) + return array + }), + core.succeed([]) as STM.STM, E, R> + ) + ) + } +) + +/** @internal */ +export const fromEither = (either: Either.Either): STM.STM => { + switch (either._tag) { + case "Left": { + return core.fail(either.left) + } + case "Right": { + return core.succeed(either.right) + } + } +} + +/** @internal */ +export const fromOption = (option: Option.Option): STM.STM> => + Option.match(option, { + onNone: () => core.fail(Option.none()), + onSome: core.succeed + }) + +/** + * Inspired by https://github.com/tusharmath/qio/pull/22 (revised) + * @internal + */ +export const gen: typeof STM.gen = (...args) => + suspend(() => { + const f = (args.length === 1) + ? args[0] + : args[1].bind(args[0]) + const iterator = f(pipe) + const state = iterator.next() + const run = ( + state: IteratorYieldResult | IteratorReturnResult + ): STM.STM => + state.done ? + core.succeed(state.value) : + core.flatMap(yieldWrapGet(state.value) as any, (val: any) => run(iterator.next(val as never))) + return run(state) + }) + +/** @internal */ +export const head = (self: STM.STM, E, R>): STM.STM, R> => + pipe( + self, + core.matchSTM({ + onFailure: (e) => core.fail(Option.some(e)), + onSuccess: (a) => { + const i = a[Symbol.iterator]() + const res = i.next() + if (res.done) { + return core.fail(Option.none()) + } else { + return core.succeed(res.value) + } + } + }) + ) + +/** @internal */ +export const if_ = dual< + ( + options: { + readonly onTrue: STM.STM + readonly onFalse: STM.STM + } + ) => ( + self: STM.STM | boolean + ) => STM.STM, + { + ( + self: boolean, + options: { + readonly onTrue: STM.STM + readonly onFalse: STM.STM + } + ): STM.STM + ( + self: STM.STM, + options: { + readonly onTrue: STM.STM + readonly onFalse: STM.STM + } + ): STM.STM + } +>( + (args) => typeof args[0] === "boolean" || core.isSTM(args[0]), + ( + self: STM.STM | boolean, + { onFalse, onTrue }: { + readonly onTrue: STM.STM + readonly onFalse: STM.STM + } + ) => { + if (typeof self === "boolean") { + return self ? onTrue : onFalse + } + + return core.flatMap(self, (bool): STM.STM => bool ? onTrue : onFalse) + } +) + +/** @internal */ +export const ignore = (self: STM.STM): STM.STM => + match(self, { onFailure: () => void_, onSuccess: () => void_ }) + +/** @internal */ +export const isFailure = (self: STM.STM): STM.STM => + match(self, { onFailure: constTrue, onSuccess: constFalse }) + +/** @internal */ +export const isSuccess = (self: STM.STM): STM.STM => + match(self, { onFailure: constFalse, onSuccess: constTrue }) + +/** @internal */ +export const iterate = ( + initial: Z, + options: { + readonly while: (z: Z) => boolean + readonly body: (z: Z) => STM.STM + } +): STM.STM => iterateLoop(initial, options.while, options.body) + +const iterateLoop = ( + initial: Z, + cont: (z: Z) => boolean, + body: (z: Z) => STM.STM +): STM.STM => { + if (cont(initial)) { + return pipe( + body(initial), + core.flatMap((z) => iterateLoop(z, cont, body)) + ) + } + return core.succeed(initial) +} + +/** @internal */ +export const loop: { + ( + initial: Z, + options: { + readonly while: (z: Z) => boolean + readonly step: (z: Z) => Z + readonly body: (z: Z) => STM.STM + readonly discard?: false | undefined + } + ): STM.STM, E, R> + ( + initial: Z, + options: { + readonly while: (z: Z) => boolean + readonly step: (z: Z) => Z + readonly body: (z: Z) => STM.STM + readonly discard: true + } + ): STM.STM +} = ( + initial: Z, + options: { + readonly while: (z: Z) => boolean + readonly step: (z: Z) => Z + readonly body: (z: Z) => STM.STM + readonly discard?: boolean | undefined + } +): STM.STM => + options.discard ? + loopDiscardLoop(initial, options.while, options.step, options.body) : + core.map(loopLoop(initial, options.while, options.step, options.body), (a) => Array.from(a)) + +const loopLoop = ( + initial: Z, + cont: (z: Z) => boolean, + inc: (z: Z) => Z, + body: (z: Z) => STM.STM +): STM.STM, E, R> => { + if (cont(initial)) { + return pipe( + body(initial), + core.flatMap((a) => pipe(loopLoop(inc(initial), cont, inc, body), core.map(Chunk.append(a)))) + ) + } + return core.succeed(Chunk.empty()) +} + +const loopDiscardLoop = ( + initial: Z, + cont: (z: Z) => boolean, + inc: (z: Z) => Z, + body: (z: Z) => STM.STM +): STM.STM => { + if (cont(initial)) { + return pipe( + body(initial), + core.flatMap(() => loopDiscardLoop(inc(initial), cont, inc, body)) + ) + } + return void_ +} + +/** @internal */ +export const mapAttempt = dual< + (f: (a: A) => B) => (self: STM.STM) => STM.STM, + (self: STM.STM, f: (a: A) => B) => STM.STM +>(2, (self: STM.STM, f: (a: A) => B): STM.STM => + core.matchSTM(self, { + onFailure: (e) => core.fail(e), + onSuccess: (a) => attempt(() => f(a)) + })) + +/** @internal */ +export const mapBoth = dual< + (options: { + readonly onFailure: (error: E) => E2 + readonly onSuccess: (value: A) => A2 + }) => (self: STM.STM) => STM.STM, + (self: STM.STM, options: { + readonly onFailure: (error: E) => E2 + readonly onSuccess: (value: A) => A2 + }) => STM.STM +>(2, (self, { onFailure, onSuccess }) => + core.matchSTM(self, { + onFailure: (e) => core.fail(onFailure(e)), + onSuccess: (a) => core.succeed(onSuccess(a)) + })) + +/** @internal */ +export const mapError = dual< + (f: (error: E) => E2) => (self: STM.STM) => STM.STM, + (self: STM.STM, f: (error: E) => E2) => STM.STM +>(2, (self, f) => + core.matchSTM(self, { + onFailure: (e) => core.fail(f(e)), + onSuccess: core.succeed + })) + +/** @internal */ +export const merge = (self: STM.STM): STM.STM => + core.matchSTM(self, { onFailure: (e) => core.succeed(e), onSuccess: core.succeed }) + +/** @internal */ +export const mergeAll = dual< + (zero: A2, f: (a2: A2, a: A) => A2) => (iterable: Iterable>) => STM.STM, + (iterable: Iterable>, zero: A2, f: (a2: A2, a: A) => A2) => STM.STM +>( + 3, + (iterable: Iterable>, zero: A2, f: (a2: A2, a: A) => A2): STM.STM => + suspend(() => + Array.from(iterable).reduce( + (acc, curr) => pipe(acc, core.zipWith(curr, f)), + core.succeed(zero) as STM.STM + ) + ) +) + +/** @internal */ +export const negate = (self: STM.STM): STM.STM => pipe(self, core.map((b) => !b)) + +/** @internal */ +export const none = (self: STM.STM, E, R>): STM.STM, R> => + core.matchSTM(self, { + onFailure: (e) => core.fail(Option.some(e)), + onSuccess: Option.match({ + onNone: () => void_, + onSome: () => core.fail(Option.none()) + }) + }) + +/** @internal */ +export const option = (self: STM.STM): STM.STM, never, R> => + match(self, { onFailure: () => Option.none(), onSuccess: Option.some }) + +/** @internal */ +export const orDie = (self: STM.STM): STM.STM => pipe(self, orDieWith(identity)) + +/** @internal */ +export const orDieWith = dual< + (f: (error: E) => unknown) => (self: STM.STM) => STM.STM, + (self: STM.STM, f: (error: E) => unknown) => STM.STM +>(2, (self, f) => pipe(self, mapError(f), core.catchAll(core.die))) + +/** @internal */ +export const orElse = dual< + (that: LazyArg>) => (self: STM.STM) => STM.STM, + (self: STM.STM, that: LazyArg>) => STM.STM +>( + 2, + (self: STM.STM, that: LazyArg>): STM.STM => + core.flatMap(core.effect>((journal) => Journal.prepareResetJournal(journal)), (reset) => + pipe( + core.orTry(self, () => core.flatMap(core.sync(reset), that)), + core.catchAll(() => core.flatMap(core.sync(reset), that)) + )) +) + +/** @internal */ +export const orElseEither = dual< + ( + that: LazyArg> + ) => ( + self: STM.STM + ) => STM.STM, E2, R2 | R>, + ( + self: STM.STM, + that: LazyArg> + ) => STM.STM, E2, R2 | R> +>( + 2, + ( + self: STM.STM, + that: LazyArg> + ): STM.STM, E2, R2 | R> => + orElse(core.map(self, Either.left), () => core.map(that(), Either.right)) +) + +/** @internal */ +export const orElseFail = dual< + (error: LazyArg) => (self: STM.STM) => STM.STM, + (self: STM.STM, error: LazyArg) => STM.STM +>( + 2, + (self: STM.STM, error: LazyArg): STM.STM => + orElse(self, () => core.failSync(error)) +) + +/** @internal */ +export const orElseOptional = dual< + ( + that: LazyArg, R2>> + ) => ( + self: STM.STM, R> + ) => STM.STM, R2 | R>, + ( + self: STM.STM, R>, + that: LazyArg, R2>> + ) => STM.STM, R2 | R> +>( + 2, + ( + self: STM.STM, R>, + that: LazyArg, R2>> + ): STM.STM, R2 | R> => + core.catchAll( + self, + Option.match({ + onNone: that, + onSome: (e) => core.fail(Option.some(e)) + }) + ) +) + +/** @internal */ +export const orElseSucceed = dual< + (value: LazyArg) => (self: STM.STM) => STM.STM, + (self: STM.STM, value: LazyArg) => STM.STM +>( + 2, + (self: STM.STM, value: LazyArg): STM.STM => + orElse(self, () => core.sync(value)) +) + +/** @internal */ +export const provideContext = dual< + (env: Context.Context) => (self: STM.STM) => STM.STM, + (self: STM.STM, env: Context.Context) => STM.STM +>(2, (self, env) => core.mapInputContext(self, (_: Context.Context) => env)) + +/** @internal */ +export const provideSomeContext = dual< + (context: Context.Context) => (self: STM.STM) => STM.STM>, + (self: STM.STM, context: Context.Context) => STM.STM> +>(2, ( + self: STM.STM, + context: Context.Context +): STM.STM> => + core.mapInputContext( + self, + (parent: Context.Context>): Context.Context => Context.merge(parent, context) as any + )) + +/** @internal */ +export const provideService = dual< + ( + tag: Context.Tag, + resource: Types.NoInfer + ) => ( + self: STM.STM + ) => STM.STM>, + ( + self: STM.STM, + tag: Context.Tag, + resource: Types.NoInfer + ) => STM.STM> +>(3, (self, tag, resource) => provideServiceSTM(self, tag, core.succeed(resource))) + +/** @internal */ +export const provideServiceSTM = dual< + ( + tag: Context.Tag, + stm: STM.STM, E1, R1> + ) => ( + self: STM.STM + ) => STM.STM>, + ( + self: STM.STM, + tag: Context.Tag, + stm: STM.STM, E1, R1> + ) => STM.STM> +>(3, ( + self: STM.STM, + tag: Context.Tag, + stm: STM.STM, E1, R1> +): STM.STM> => + core.contextWithSTM((env: Context.Context>) => + core.flatMap( + stm, + (service) => + provideContext( + self, + Context.add(env, tag, service) as Context.Context + ) + ) + )) + +/** @internal */ +export const reduce = dual< + (zero: S, f: (s: S, a: A) => STM.STM) => (iterable: Iterable) => STM.STM, + (iterable: Iterable, zero: S, f: (s: S, a: A) => STM.STM) => STM.STM +>( + 3, + (iterable: Iterable, zero: S, f: (s: S, a: A) => STM.STM): STM.STM => + suspend(() => + Array.from(iterable).reduce( + (acc, curr) => pipe(acc, core.flatMap((s) => f(s, curr))), + core.succeed(zero) as STM.STM + ) + ) +) + +/** @internal */ +export const reduceAll = dual< + ( + initial: STM.STM, + f: (x: A, y: A) => A + ) => ( + iterable: Iterable> + ) => STM.STM, + ( + iterable: Iterable>, + initial: STM.STM, + f: (x: A, y: A) => A + ) => STM.STM +>(3, ( + iterable: Iterable>, + initial: STM.STM, + f: (x: A, y: A) => A +): STM.STM => + suspend(() => + Array.from(iterable).reduce( + (acc, curr) => pipe(acc, core.zipWith(curr, f)), + initial as STM.STM + ) + )) + +/** @internal */ +export const reduceRight = dual< + (zero: S, f: (s: S, a: A) => STM.STM) => (iterable: Iterable) => STM.STM, + (iterable: Iterable, zero: S, f: (s: S, a: A) => STM.STM) => STM.STM +>( + 3, + (iterable: Iterable, zero: S, f: (s: S, a: A) => STM.STM): STM.STM => + suspend(() => + Array.from(iterable).reduceRight( + (acc, curr) => pipe(acc, core.flatMap((s) => f(s, curr))), + core.succeed(zero) as STM.STM + ) + ) +) + +/** @internal */ +export const refineOrDie = dual< + (pf: (error: E) => Option.Option) => (self: STM.STM) => STM.STM, + (self: STM.STM, pf: (error: E) => Option.Option) => STM.STM +>(2, (self, pf) => refineOrDieWith(self, pf, identity)) + +/** @internal */ +export const refineOrDieWith = dual< + ( + pf: (error: E) => Option.Option, + f: (error: E) => unknown + ) => ( + self: STM.STM + ) => STM.STM, + ( + self: STM.STM, + pf: (error: E) => Option.Option, + f: (error: E) => unknown + ) => STM.STM +>(3, (self, pf, f) => + core.catchAll( + self, + (e) => + Option.match(pf(e), { + onNone: () => core.die(f(e)), + onSome: core.fail + }) + )) + +/** @internal */ +export const reject = dual< + (pf: (a: A) => Option.Option) => (self: STM.STM) => STM.STM, + (self: STM.STM, pf: (a: A) => Option.Option) => STM.STM +>(2, (self, pf) => + rejectSTM( + self, + (a) => Option.map(pf(a), core.fail) + )) + +/** @internal */ +export const rejectSTM = dual< + ( + pf: (a: A) => Option.Option> + ) => ( + self: STM.STM + ) => STM.STM, + ( + self: STM.STM, + pf: (a: A) => Option.Option> + ) => STM.STM +>(2, (self, pf) => + core.flatMap(self, (a) => + Option.match(pf(a), { + onNone: () => core.succeed(a), + onSome: core.flatMap(core.fail) + }))) + +/** @internal */ +export const repeatUntil = dual< + (predicate: Predicate) => (self: STM.STM) => STM.STM, + (self: STM.STM, predicate: Predicate) => STM.STM +>(2, (self, predicate) => repeatUntilLoop(self, predicate)) + +const repeatUntilLoop = (self: STM.STM, predicate: Predicate): STM.STM => + core.flatMap(self, (a) => + predicate(a) ? + core.succeed(a) : + repeatUntilLoop(self, predicate)) + +/** @internal */ +export const repeatWhile = dual< + (predicate: Predicate) => (self: STM.STM) => STM.STM, + (self: STM.STM, predicate: Predicate) => STM.STM +>(2, (self, predicate) => repeatWhileLoop(self, predicate)) + +const repeatWhileLoop = (self: STM.STM, predicate: Predicate): STM.STM => + core.flatMap(self, (a) => + predicate(a) ? + repeatWhileLoop(self, predicate) : + core.succeed(a)) + +/** @internal */ +export const replicate = dual< + (n: number) => (self: STM.STM) => Array>, + (self: STM.STM, n: number) => Array> +>(2, (self, n) => Array.from({ length: n }, () => self)) + +/** @internal */ +export const replicateSTM = dual< + (n: number) => (self: STM.STM) => STM.STM, E, R>, + (self: STM.STM, n: number) => STM.STM, E, R> +>(2, (self, n) => all(replicate(self, n))) + +/** @internal */ +export const replicateSTMDiscard = dual< + (n: number) => (self: STM.STM) => STM.STM, + (self: STM.STM, n: number) => STM.STM +>(2, (self, n) => all(replicate(self, n), { discard: true })) + +/** @internal */ +export const retryUntil = dual< + { + (refinement: Refinement, B>): (self: STM.STM) => STM.STM + (predicate: Predicate): (self: STM.STM) => STM.STM + }, + { + (self: STM.STM, refinement: Refinement): STM.STM + (self: STM.STM, predicate: Predicate): STM.STM + } +>( + 2, + (self: STM.STM, predicate: Predicate) => + core.matchSTM(self, { onFailure: core.fail, onSuccess: (a) => predicate(a) ? core.succeed(a) : core.retry }) +) + +/** @internal */ +export const retryWhile = dual< + (predicate: Predicate) => (self: STM.STM) => STM.STM, + (self: STM.STM, predicate: Predicate) => STM.STM +>( + 2, + (self, predicate) => + core.matchSTM(self, { onFailure: core.fail, onSuccess: (a) => !predicate(a) ? core.succeed(a) : core.retry }) +) + +/** @internal */ +export const partition = dual< + ( + f: (a: A) => STM.STM + ) => ( + elements: Iterable + ) => STM.STM<[excluded: Array, satisfying: Array], never, R>, + ( + elements: Iterable, + f: (a: A) => STM.STM + ) => STM.STM<[excluded: Array, satisfying: Array], never, R> +>(2, (elements, f) => + pipe( + forEach(elements, (a) => either(f(a))), + core.map((as) => effectCore.partitionMap(as, identity)) + )) + +/** @internal */ +export const some = (self: STM.STM, E, R>): STM.STM, R> => + core.matchSTM(self, { + onFailure: (e) => core.fail(Option.some(e)), + onSuccess: Option.match({ + onNone: () => core.fail(Option.none()), + onSome: core.succeed + }) + }) + +/* @internal */ +export const all = (( + input: Iterable | Record, + options?: STM.All.Options +): STM.STM => { + if (Symbol.iterator in input) { + return forEach(input, identity, options as any) + } else if (options?.discard) { + return forEach(Object.values(input), identity, options as any) + } + + return core.map( + forEach( + Object.entries(input), + ([_, e]) => core.map(e, (a) => [_, a] as const) + ), + (values) => { + const res = {} + for (const [k, v] of values) { + ;(res as any)[k] = v + } + return res + } + ) +}) as STM.All.Signature + +/** @internal */ +export const succeedNone: STM.STM> = core.succeed(Option.none()) + +/** @internal */ +export const succeedSome = (value: A): STM.STM> => core.succeed(Option.some(value)) + +/** @internal */ +export const summarized = dual< + ( + summary: STM.STM, + f: (before: A2, after: A2) => A3 + ) => ( + self: STM.STM + ) => STM.STM<[A3, A], E2 | E, R2 | R>, + ( + self: STM.STM, + summary: STM.STM, + f: (before: A2, after: A2) => A3 + ) => STM.STM<[A3, A], E2 | E, R2 | R> +>(3, (self, summary, f) => + core.flatMap(summary, (start) => + core.flatMap(self, (value) => + core.map( + summary, + (end) => [f(start, end), value] + )))) + +/** @internal */ +export const suspend = (evaluate: LazyArg>): STM.STM => flatten(core.sync(evaluate)) + +/** @internal */ +export const tap: { + (f: (a: A) => STM.STM): (self: STM.STM) => STM.STM + (self: STM.STM, f: (a: A) => STM.STM): STM.STM +} = dual( + 2, + (self: STM.STM, f: (a: A) => STM.STM): STM.STM => + core.flatMap(self, (a) => as(f(a), a)) +) + +/** @internal */ +export const tapBoth = dual< + ( + options: { + readonly onFailure: (error: XE) => STM.STM + readonly onSuccess: (value: XA) => STM.STM + } + ) => ( + self: STM.STM + ) => STM.STM, + ( + self: STM.STM, + options: { + readonly onFailure: (error: XE) => STM.STM + readonly onSuccess: (value: XA) => STM.STM + } + ) => STM.STM +>(2, (self, { onFailure, onSuccess }) => + core.matchSTM(self, { + onFailure: (e) => pipe(onFailure(e as any), core.zipRight(core.fail(e))), + onSuccess: (a) => pipe(onSuccess(a as any), as(a)) + })) + +/** @internal */ +export const tapError: { + ( + f: (error: Types.NoInfer) => STM.STM + ): (self: STM.STM) => STM.STM + (self: STM.STM, f: (error: E) => STM.STM): STM.STM +} = dual( + 2, + (self: STM.STM, f: (error: E) => STM.STM): STM.STM => + core.matchSTM(self, { + onFailure: (e) => core.zipRight(f(e), core.fail(e)), + onSuccess: core.succeed + }) +) + +/** @internal */ +export const try_: { + (options: { + readonly try: LazyArg + readonly catch: (u: unknown) => E + }): STM.STM + (try_: LazyArg): STM.STM +} = ( + arg: LazyArg | { + readonly try: LazyArg + readonly catch: (u: unknown) => E + } +) => { + const evaluate = typeof arg === "function" ? arg : arg.try + return suspend(() => { + try { + return core.succeed(evaluate()) + } catch (error) { + return core.fail("catch" in arg ? arg.catch(error) : error) + } + }) +} + +/** @internal */ +const void_: STM.STM = core.succeed(void 0) +export { + /** @internal */ + void_ as void +} + +/** @internal */ +export const unless = dual< + (predicate: LazyArg) => (self: STM.STM) => STM.STM, E, R>, + (self: STM.STM, predicate: LazyArg) => STM.STM, E, R> +>(2, (self, predicate) => + suspend( + () => predicate() ? succeedNone : asSome(self) + )) + +/** @internal */ +export const unlessSTM = dual< + ( + predicate: STM.STM + ) => ( + self: STM.STM + ) => STM.STM, E2 | E, R2 | R>, + ( + self: STM.STM, + predicate: STM.STM + ) => STM.STM, E2 | E, R2 | R> +>(2, (self, predicate) => + core.flatMap( + predicate, + (bool) => bool ? succeedNone : asSome(self) + )) + +/** @internal */ +export const unsome = (self: STM.STM, R>): STM.STM, E, R> => + core.matchSTM(self, { + onFailure: Option.match({ + onNone: () => core.succeed(Option.none()), + onSome: core.fail + }), + onSuccess: (a) => core.succeed(Option.some(a)) + }) + +/** @internal */ +export const validateAll = dual< + ( + f: (a: A) => STM.STM + ) => ( + elements: Iterable + ) => STM.STM, RA.NonEmptyArray, R>, + ( + elements: Iterable, + f: (a: A) => STM.STM + ) => STM.STM, RA.NonEmptyArray, R> +>( + 2, + (elements, f) => + core.flatMap(partition(elements, f), ([errors, values]) => + RA.isNonEmptyArray(errors) ? + core.fail(errors) : + core.succeed(values)) +) + +/** @internal */ +export const validateFirst = dual< + (f: (a: A) => STM.STM) => (elements: Iterable) => STM.STM, R>, + (elements: Iterable, f: (a: A) => STM.STM) => STM.STM, R> +>(2, (elements, f) => flip(forEach(elements, (a) => flip(f(a))))) + +/** @internal */ +export const when = dual< + (predicate: LazyArg) => (self: STM.STM) => STM.STM, E, R>, + (self: STM.STM, predicate: LazyArg) => STM.STM, E, R> +>(2, (self, predicate) => + suspend( + () => predicate() ? asSome(self) : succeedNone + )) + +/** @internal */ +export const whenSTM = dual< + ( + predicate: STM.STM + ) => ( + self: STM.STM + ) => STM.STM, E2 | E, R2 | R>, + ( + self: STM.STM, + predicate: STM.STM + ) => STM.STM, E2 | E, R2 | R> +>(2, (self, predicate) => + core.flatMap( + predicate, + (bool) => bool ? asSome(self) : succeedNone + )) diff --git a/backend/node_modules/effect/src/internal/stm/stmState.ts b/backend/node_modules/effect/src/internal/stm/stmState.ts new file mode 100644 index 0000000000000000000000000000000000000000..ebe1b2d3bb1f29e3b339cb97a52e8cf7fc2ac388 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/stmState.ts @@ -0,0 +1,136 @@ +import * as Equal from "../../Equal.js" +import * as Exit from "../../Exit.js" +import { pipe } from "../../Function.js" +import * as Hash from "../../Hash.js" +import { hasProperty } from "../../Predicate.js" +import * as OpCodes from "./opCodes/stmState.js" +import * as TExitOpCodes from "./opCodes/tExit.js" +import type * as TExit from "./tExit.js" + +/** @internal */ +const STMStateSymbolKey = "effect/STM/State" + +/** @internal */ +export const STMStateTypeId = Symbol.for(STMStateSymbolKey) + +/** @internal */ +export type STMStateTypeId = typeof STMStateTypeId + +/** @internal */ +export type STMState = Done | Interrupted | Running + +/** @internal */ +export interface Done extends Equal.Equal { + readonly [STMStateTypeId]: STMStateTypeId + readonly _tag: OpCodes.OP_DONE + readonly exit: Exit.Exit +} + +/** @internal */ +export interface Interrupted extends Equal.Equal { + readonly [STMStateTypeId]: STMStateTypeId + readonly _tag: OpCodes.OP_INTERRUPTED +} + +/** @internal */ +export interface Running extends Equal.Equal { + readonly [STMStateTypeId]: STMStateTypeId + readonly _tag: OpCodes.OP_RUNNING +} + +/** @internal */ +export const isSTMState = (u: unknown): u is STMState => hasProperty(u, STMStateTypeId) + +/** @internal */ +export const isRunning = (self: STMState): self is Running => { + return self._tag === OpCodes.OP_RUNNING +} + +/** @internal */ +export const isDone = (self: STMState): self is Done => { + return self._tag === OpCodes.OP_DONE +} + +/** @internal */ +export const isInterrupted = (self: STMState): self is Interrupted => { + return self._tag === OpCodes.OP_INTERRUPTED +} + +/** @internal */ +export const done = (exit: Exit.Exit): STMState => { + return { + [STMStateTypeId]: STMStateTypeId, + _tag: OpCodes.OP_DONE, + exit, + [Hash.symbol](): number { + return pipe( + Hash.hash(STMStateSymbolKey), + Hash.combine(Hash.hash(OpCodes.OP_DONE)), + Hash.combine(Hash.hash(exit)), + Hash.cached(this) + ) + }, + [Equal.symbol](that: unknown): boolean { + return isSTMState(that) && that._tag === OpCodes.OP_DONE && Equal.equals(exit, that.exit) + } + } +} + +const interruptedHash = pipe( + Hash.hash(STMStateSymbolKey), + Hash.combine(Hash.hash(OpCodes.OP_INTERRUPTED)), + Hash.combine(Hash.hash("interrupted")) +) + +/** @internal */ +export const interrupted: STMState = { + [STMStateTypeId]: STMStateTypeId, + _tag: OpCodes.OP_INTERRUPTED, + [Hash.symbol](): number { + return interruptedHash + }, + [Equal.symbol](that: unknown): boolean { + return isSTMState(that) && that._tag === OpCodes.OP_INTERRUPTED + } +} + +const runningHash = pipe( + Hash.hash(STMStateSymbolKey), + Hash.combine(Hash.hash(OpCodes.OP_RUNNING)), + Hash.combine(Hash.hash("running")) +) + +/** @internal */ +export const running: STMState = { + [STMStateTypeId]: STMStateTypeId, + _tag: OpCodes.OP_RUNNING, + [Hash.symbol](): number { + return runningHash + }, + [Equal.symbol](that: unknown): boolean { + return isSTMState(that) && that._tag === OpCodes.OP_RUNNING + } +} + +/** @internal */ +export const fromTExit = (tExit: TExit.TExit): STMState => { + switch (tExit._tag) { + case TExitOpCodes.OP_FAIL: { + return done(Exit.fail(tExit.error)) + } + case TExitOpCodes.OP_DIE: { + return done(Exit.die(tExit.defect)) + } + case TExitOpCodes.OP_INTERRUPT: { + return done(Exit.interrupt(tExit.fiberId)) + } + case TExitOpCodes.OP_SUCCEED: { + return done(Exit.succeed(tExit.value)) + } + case TExitOpCodes.OP_RETRY: { + throw new Error( + "BUG: STM.STMState.fromTExit - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + } + } +} diff --git a/backend/node_modules/effect/src/internal/stm/tArray.ts b/backend/node_modules/effect/src/internal/stm/tArray.ts new file mode 100644 index 0000000000000000000000000000000000000000..b3d292e93dfe28b2423c4865f76751240de2e463 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tArray.ts @@ -0,0 +1,550 @@ +import * as Equal from "../../Equal.js" +import { dual, pipe } from "../../Function.js" +import * as Option from "../../Option.js" +import * as Order from "../../Order.js" +import type { Predicate } from "../../Predicate.js" +import type * as STM from "../../STM.js" +import type * as TArray from "../../TArray.js" +import type * as TRef from "../../TRef.js" +import * as core from "./core.js" +import * as stm from "./stm.js" +import * as tRef from "./tRef.js" + +/** @internal */ +const TArraySymbolKey = "effect/TArray" + +/** @internal */ +export const TArrayTypeId: TArray.TArrayTypeId = Symbol.for(TArraySymbolKey) as TArray.TArrayTypeId + +const tArrayVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +export class TArrayImpl implements TArray.TArray { + readonly [TArrayTypeId] = tArrayVariance + constructor(readonly chunk: Array>) {} +} + +/** @internal */ +export const collectFirst = dual< + (pf: (a: A) => Option.Option) => (self: TArray.TArray) => STM.STM>, + (self: TArray.TArray, pf: (a: A) => Option.Option) => STM.STM> +>(2, (self, pf) => + collectFirstSTM( + self, + (a) => pipe(pf(a), Option.map(core.succeed)) + )) + +/** @internal */ +export const collectFirstSTM = dual< + ( + pf: (a: A) => Option.Option> + ) => ( + self: TArray.TArray + ) => STM.STM, E, R>, + ( + self: TArray.TArray, + pf: (a: A) => Option.Option> + ) => STM.STM, E, R> +>( + 2, + (self: TArray.TArray, pf: (a: A) => Option.Option>) => + core.withSTMRuntime((runtime) => { + let index = 0 + let result: Option.Option> = Option.none() + while (Option.isNone(result) && index < self.chunk.length) { + const element = pipe(self.chunk[index], tRef.unsafeGet(runtime.journal)) + const option = pf(element) + if (Option.isSome(option)) { + result = option + } + index = index + 1 + } + return pipe( + result, + Option.match({ + onNone: () => stm.succeedNone, + onSome: core.map(Option.some) + }) + ) + }) +) + +/** @internal */ +export const contains = dual< + (value: A) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, value: A) => STM.STM +>(2, (self, value) => some(self, (a) => Equal.equals(a)(value))) + +/** @internal */ +export const count = dual< + (predicate: Predicate) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, predicate: Predicate) => STM.STM +>(2, (self, predicate) => + reduce( + self, + 0, + (n, a) => predicate(a) ? n + 1 : n + )) + +/** @internal */ +export const countSTM = dual< + (predicate: (value: A) => STM.STM) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, predicate: (value: A) => STM.STM) => STM.STM +>(2, (self, predicate) => + reduceSTM( + self, + 0, + (n, a) => core.map(predicate(a), (bool) => bool ? n + 1 : n) + )) + +/** @internal */ +export const empty = (): STM.STM> => fromIterable([]) + +/** @internal */ +export const every = dual< + (predicate: Predicate) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, predicate: Predicate) => STM.STM +>(2, (self, predicate) => stm.negate(some(self, (a) => !predicate(a)))) + +/** @internal */ +export const everySTM = dual< + (predicate: (value: A) => STM.STM) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, predicate: (value: A) => STM.STM) => STM.STM +>(2, (self, predicate) => + core.map( + countSTM(self, predicate), + (count) => count === self.chunk.length + )) + +/** @internal */ +export const findFirst = dual< + (predicate: Predicate) => (self: TArray.TArray) => STM.STM>, + (self: TArray.TArray, predicate: Predicate) => STM.STM> +>(2, (self, predicate) => + collectFirst(self, (a) => + predicate(a) + ? Option.some(a) + : Option.none())) + +/** @internal */ +export const findFirstIndex = dual< + (value: A) => (self: TArray.TArray) => STM.STM>, + (self: TArray.TArray, value: A) => STM.STM> +>(2, (self, value) => findFirstIndexFrom(self, value, 0)) + +/** @internal */ +export const findFirstIndexFrom = dual< + (value: A, from: number) => (self: TArray.TArray) => STM.STM>, + (self: TArray.TArray, value: A, from: number) => STM.STM> +>(3, (self, value, from) => + findFirstIndexWhereFrom( + self, + (a) => Equal.equals(a)(value), + from + )) + +/** @internal */ +export const findFirstIndexWhere = dual< + (predicate: Predicate) => (self: TArray.TArray) => STM.STM>, + (self: TArray.TArray, predicate: Predicate) => STM.STM> +>(2, (self, predicate) => findFirstIndexWhereFrom(self, predicate, 0)) + +/** @internal */ +export const findFirstIndexWhereFrom = dual< + ( + predicate: Predicate, + from: number + ) => (self: TArray.TArray) => STM.STM>, + (self: TArray.TArray, predicate: Predicate, from: number) => STM.STM> +>(3, (self, predicate, from) => { + if (from < 0) { + return stm.succeedNone + } + return core.effect>((journal) => { + let index: number = from + let found = false + while (!found && index < self.chunk.length) { + const element = tRef.unsafeGet(self.chunk[index], journal) + found = predicate(element) + index = index + 1 + } + if (found) { + return Option.some(index - 1) + } + return Option.none() + }) +}) + +/** @internal */ +export const findFirstIndexWhereSTM = dual< + ( + predicate: (value: A) => STM.STM + ) => (self: TArray.TArray) => STM.STM, E, R>, + ( + self: TArray.TArray, + predicate: (value: A) => STM.STM + ) => STM.STM, E, R> +>(2, (self, predicate) => findFirstIndexWhereFromSTM(self, predicate, 0)) + +/** @internal */ +export const findFirstIndexWhereFromSTM = dual< + ( + predicate: (value: A) => STM.STM, + from: number + ) => (self: TArray.TArray) => STM.STM, E, R>, + ( + self: TArray.TArray, + predicate: (value: A) => STM.STM, + from: number + ) => STM.STM, E, R> +>(3, ( + self: TArray.TArray, + predicate: (value: A) => STM.STM, + from: number +) => { + const forIndex = (index: number): STM.STM, E, R> => + index < self.chunk.length + ? pipe( + tRef.get(self.chunk[index]), + core.flatMap(predicate), + core.flatMap((bool) => + bool ? + core.succeed(Option.some(index)) : + forIndex(index + 1) + ) + ) + : stm.succeedNone + return from < 0 + ? stm.succeedNone + : forIndex(from) +}) + +/** @internal */ +export const findFirstSTM = dual< + ( + predicate: (value: A) => STM.STM + ) => ( + self: TArray.TArray + ) => STM.STM, E, R>, + ( + self: TArray.TArray, + predicate: (value: A) => STM.STM + ) => STM.STM, E, R> +>(2, (self: TArray.TArray, predicate: (value: A) => STM.STM) => { + const init = [Option.none() as Option.Option, 0 as number] as const + const cont = (state: readonly [Option.Option, number]) => + Option.isNone(state[0]) && state[1] < self.chunk.length - 1 + return core.map( + stm.iterate(init, { + while: cont, + body: (state) => { + const index = state[1] + return pipe( + tRef.get(self.chunk[index]), + core.flatMap((value) => + core.map( + predicate(value), + (bool) => [bool ? Option.some(value) : Option.none(), index + 1] as const + ) + ) + ) + } + }), + (state) => state[0] + ) +}) + +/** @internal */ +export const findLast = dual< + (predicate: Predicate) => (self: TArray.TArray) => STM.STM>, + (self: TArray.TArray, predicate: Predicate) => STM.STM> +>(2, (self: TArray.TArray, predicate: Predicate) => + core.effect>((journal) => { + let index = self.chunk.length - 1 + let result: Option.Option = Option.none() + while (Option.isNone(result) && index >= 0) { + const element = tRef.unsafeGet(self.chunk[index], journal) + if (predicate(element)) { + result = Option.some(element) + } + index = index - 1 + } + return result + })) + +/** @internal */ +export const findLastIndex = dual< + (value: A) => (self: TArray.TArray) => STM.STM>, + (self: TArray.TArray, value: A) => STM.STM> +>(2, (self, value) => findLastIndexFrom(self, value, self.chunk.length - 1)) + +/** @internal */ +export const findLastIndexFrom = dual< + (value: A, end: number) => (self: TArray.TArray) => STM.STM>, + (self: TArray.TArray, value: A, end: number) => STM.STM> +>(3, (self, value, end) => { + if (end >= self.chunk.length) { + return stm.succeedNone + } + return core.effect>((journal) => { + let index: number = end + let found = false + while (!found && index >= 0) { + const element = tRef.unsafeGet(self.chunk[index], journal) + found = Equal.equals(element)(value) + index = index - 1 + } + if (found) { + return Option.some(index + 1) + } + return Option.none() + }) +}) + +/** @internal */ +export const findLastSTM = dual< + ( + predicate: (value: A) => STM.STM + ) => (self: TArray.TArray) => STM.STM, E, R>, + ( + self: TArray.TArray, + predicate: (value: A) => STM.STM + ) => STM.STM, E, R> +>(2, (self: TArray.TArray, predicate: (value: A) => STM.STM) => { + const init = [Option.none() as Option.Option, self.chunk.length - 1] as const + const cont = (state: readonly [Option.Option, number]) => Option.isNone(state[0]) && state[1] >= 0 + return core.map( + stm.iterate(init, { + while: cont, + body: (state) => { + const index = state[1] + return pipe( + tRef.get(self.chunk[index]), + core.flatMap((value) => + core.map( + predicate(value), + (bool) => [bool ? Option.some(value) : Option.none(), index - 1] as const + ) + ) + ) + } + }), + (state) => state[0] + ) +}) + +/** @internal */ +export const forEach = dual< + (f: (value: A) => STM.STM) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, f: (value: A) => STM.STM) => STM.STM +>(2, (self, f) => reduceSTM(self, void 0 as void, (_, a) => f(a))) + +/** @internal */ +export const fromIterable = (iterable: Iterable): STM.STM> => + core.map( + stm.forEach(iterable, tRef.make), + (chunk) => new TArrayImpl(chunk) + ) + +/** @internal */ +export const get = dual< + (index: number) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, index: number) => STM.STM +>(2, (self, index) => { + if (index < 0 || index >= self.chunk.length) { + return core.dieMessage("Index out of bounds") + } + return tRef.get(self.chunk[index]) +}) + +/** @internal */ +export const headOption = (self: TArray.TArray): STM.STM> => + self.chunk.length === 0 ? + core.succeed(Option.none()) : + core.map(tRef.get(self.chunk[0]), Option.some) + +/** @internal */ +export const lastOption = (self: TArray.TArray): STM.STM> => + self.chunk.length === 0 ? + stm.succeedNone : + core.map(tRef.get(self.chunk[self.chunk.length - 1]), Option.some) + +/** @internal */ +export const make = ]>( + ...elements: Elements +): STM.STM> => fromIterable(elements) + +/** @internal */ +export const maxOption = dual< + (order: Order.Order) => (self: TArray.TArray) => STM.STM>, + (self: TArray.TArray, order: Order.Order) => STM.STM> +>(2, (self, order) => { + const greaterThan = Order.greaterThan(order) + return reduceOption(self, (acc, curr) => greaterThan(acc)(curr) ? curr : acc) +}) + +/** @internal */ +export const minOption = dual< + (order: Order.Order) => (self: TArray.TArray) => STM.STM>, + (self: TArray.TArray, order: Order.Order) => STM.STM> +>(2, (self, order) => { + const lessThan = Order.lessThan(order) + return reduceOption(self, (acc, curr) => lessThan(acc)(curr) ? curr : acc) +}) + +/** @internal */ +export const reduce = dual< + (zero: Z, f: (accumulator: Z, current: A) => Z) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, zero: Z, f: (accumulator: Z, current: A) => Z) => STM.STM +>( + 3, + (self: TArray.TArray, zero: Z, f: (accumulator: Z, current: A) => Z) => + core.effect((journal) => { + let index = 0 + let result = zero + while (index < self.chunk.length) { + const element = tRef.unsafeGet(self.chunk[index], journal) + result = f(result, element) + index = index + 1 + } + return result + }) +) + +/** @internal */ +export const reduceOption = dual< + (f: (x: A, y: A) => A) => (self: TArray.TArray) => STM.STM>, + (self: TArray.TArray, f: (x: A, y: A) => A) => STM.STM> +>( + 2, + (self: TArray.TArray, f: (x: A, y: A) => A) => + core.effect>((journal) => { + let index = 0 + let result: A | undefined = undefined + while (index < self.chunk.length) { + const element = tRef.unsafeGet(self.chunk[index], journal) + result = result === undefined ? element : f(result, element) + index = index + 1 + } + return Option.fromNullable(result) + }) +) + +/** @internal */ +export const reduceOptionSTM = dual< + (f: (x: A, y: A) => STM.STM) => (self: TArray.TArray) => STM.STM, E, R>, + (self: TArray.TArray, f: (x: A, y: A) => STM.STM) => STM.STM, E, R> +>( + 2, + (self: TArray.TArray, f: (x: A, y: A) => STM.STM) => + reduceSTM(self, Option.none(), (acc, curr) => + Option.isSome(acc) + ? core.map(f(acc.value, curr), Option.some) + : stm.succeedSome(curr)) +) + +/** @internal */ +export const reduceSTM = dual< + ( + zero: Z, + f: (accumulator: Z, current: A) => STM.STM + ) => (self: TArray.TArray) => STM.STM, + ( + self: TArray.TArray, + zero: Z, + f: (accumulator: Z, current: A) => STM.STM + ) => STM.STM +>(3, (self, zero, f) => + core.flatMap( + toArray(self), + stm.reduce(zero, f) + )) + +/** @internal */ +export const size = (self: TArray.TArray): number => self.chunk.length + +/** @internal */ +export const some = dual< + (predicate: Predicate) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, predicate: Predicate) => STM.STM +>(2, (self, predicate) => + core.map( + findFirst(self, predicate), + Option.isSome + )) + +/** @internal */ +export const someSTM = dual< + (predicate: (value: A) => STM.STM) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, predicate: (value: A) => STM.STM) => STM.STM +>(2, (self, predicate) => core.map(countSTM(self, predicate), (n) => n > 0)) + +/** @internal */ +export const toArray = (self: TArray.TArray): STM.STM> => stm.forEach(self.chunk, tRef.get) + +/** @internal */ +export const transform = dual< + (f: (value: A) => A) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, f: (value: A) => A) => STM.STM +>(2, (self, f) => + core.effect((journal) => { + let index = 0 + while (index < self.chunk.length) { + const ref = self.chunk[index] + tRef.unsafeSet(ref, f(tRef.unsafeGet(ref, journal)), journal) + index = index + 1 + } + return void 0 + })) + +/** @internal */ +export const transformSTM = dual< + (f: (value: A) => STM.STM) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, f: (value: A) => STM.STM) => STM.STM +>(2, (self: TArray.TArray, f: (value: A) => STM.STM) => + core.flatMap( + stm.forEach( + self.chunk, + (ref) => core.flatMap(tRef.get(ref), f) + ), + (chunk) => + core.effect((journal) => { + const iterator = chunk[Symbol.iterator]() + let index = 0 + let next: IteratorResult + while ((next = iterator.next()) && !next.done) { + tRef.unsafeSet(self.chunk[index], next.value, journal) + index = index + 1 + } + return void 0 + }) + )) + +/** @internal */ +export const update = dual< + (index: number, f: (value: A) => A) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, index: number, f: (value: A) => A) => STM.STM +>(3, (self, index, f) => { + if (index < 0 || index >= self.chunk.length) { + return core.dieMessage("Index out of bounds") + } + return tRef.update(self.chunk[index], f) +}) + +/** @internal */ +export const updateSTM = dual< + (index: number, f: (value: A) => STM.STM) => (self: TArray.TArray) => STM.STM, + (self: TArray.TArray, index: number, f: (value: A) => STM.STM) => STM.STM +>(3, (self, index, f) => { + if (index < 0 || index >= self.chunk.length) { + return core.dieMessage("Index out of bounds") + } + return pipe( + tRef.get(self.chunk[index]), + core.flatMap(f), + core.flatMap((updated) => tRef.set(self.chunk[index], updated)) + ) +}) diff --git a/backend/node_modules/effect/src/internal/stm/tDeferred.ts b/backend/node_modules/effect/src/internal/stm/tDeferred.ts new file mode 100644 index 0000000000000000000000000000000000000000..ebb819dd00d3cc7e7c93cff60aa03aeecb184139 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tDeferred.ts @@ -0,0 +1,81 @@ +import * as Either from "../../Either.js" +import { dual } from "../../Function.js" +import * as Option from "../../Option.js" +import type * as STM from "../../STM.js" +import type * as TDeferred from "../../TDeferred.js" +import type * as TRef from "../../TRef.js" +import * as core from "./core.js" +import * as stm from "./stm.js" +import * as tRef from "./tRef.js" + +/** @internal */ +const TDeferredSymbolKey = "effect/TDeferred" + +/** @internal */ +export const TDeferredTypeId: TDeferred.TDeferredTypeId = Symbol.for( + TDeferredSymbolKey +) as TDeferred.TDeferredTypeId + +/** @internal */ +const tDeferredVariance = { + /* c8 ignore next */ + _A: (_: any) => _, + /* c8 ignore next */ + _E: (_: any) => _ +} + +/** @internal */ +class TDeferredImpl implements TDeferred.TDeferred { + readonly [TDeferredTypeId] = tDeferredVariance + constructor(readonly ref: TRef.TRef>>) {} +} + +/** @internal */ +export const _await = (self: TDeferred.TDeferred): STM.STM => + stm.flatten( + stm.collect(tRef.get(self.ref), (option) => + Option.isSome(option) ? + Option.some(stm.fromEither(option.value)) : + Option.none()) + ) + +/** @internal */ +export const done = dual< + (either: Either.Either) => (self: TDeferred.TDeferred) => STM.STM, + (self: TDeferred.TDeferred, either: Either.Either) => STM.STM +>(2, (self, either) => + core.flatMap( + tRef.get(self.ref), + Option.match({ + onNone: () => + core.zipRight( + tRef.set(self.ref, Option.some(either)), + core.succeed(true) + ), + onSome: () => core.succeed(false) + }) + )) + +/** @internal */ +export const fail = dual< + (error: E) => (self: TDeferred.TDeferred) => STM.STM, + (self: TDeferred.TDeferred, error: E) => STM.STM +>(2, (self, error) => done(self, Either.left(error))) + +/** @internal */ +export const make = (): STM.STM> => + core.map( + tRef.make>>(Option.none()), + (ref) => new TDeferredImpl(ref) + ) + +/** @internal */ +export const poll = ( + self: TDeferred.TDeferred +): STM.STM>> => tRef.get(self.ref) + +/** @internal */ +export const succeed = dual< + (value: A) => (self: TDeferred.TDeferred) => STM.STM, + (self: TDeferred.TDeferred, value: A) => STM.STM +>(2, (self, value) => done(self, Either.right(value))) diff --git a/backend/node_modules/effect/src/internal/stm/tExit.ts b/backend/node_modules/effect/src/internal/stm/tExit.ts new file mode 100644 index 0000000000000000000000000000000000000000..478e42475c37ffcf2a37ce3be72e6a14eb2681d4 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tExit.ts @@ -0,0 +1,190 @@ +import * as Equal from "../../Equal.js" +import type * as FiberId from "../../FiberId.js" +import { pipe } from "../../Function.js" +import * as Hash from "../../Hash.js" +import { hasProperty } from "../../Predicate.js" +import type * as Types from "../../Types.js" +import * as OpCodes from "./opCodes/tExit.js" + +/** @internal */ +const TExitSymbolKey = "effect/TExit" + +/** @internal */ +export const TExitTypeId = Symbol.for(TExitSymbolKey) + +/** @internal */ +export type TExitTypeId = typeof TExitTypeId + +/** @internal */ +export type TExit = Fail | Die | Interrupt | Succeed | Retry + +/** @internal */ +export declare namespace TExit { + /** @internal */ + export interface Variance { + readonly [TExitTypeId]: { + readonly _A: Types.Covariant + readonly _E: Types.Covariant + } + } +} + +const variance = { + /* c8 ignore next */ + _A: (_: never) => _, + /* c8 ignore next */ + _E: (_: never) => _ +} + +/** @internal */ +export interface Fail extends TExit.Variance, Equal.Equal { + readonly _tag: OpCodes.OP_FAIL + readonly error: E +} + +/** @internal */ +export interface Die extends TExit.Variance, Equal.Equal { + readonly _tag: OpCodes.OP_DIE + readonly defect: unknown +} + +/** @internal */ +export interface Interrupt extends TExit.Variance, Equal.Equal { + readonly _tag: OpCodes.OP_INTERRUPT + readonly fiberId: FiberId.FiberId +} + +/** @internal */ +export interface Succeed extends TExit.Variance, Equal.Equal { + readonly _tag: OpCodes.OP_SUCCEED + readonly value: A +} + +/** @internal */ +export interface Retry extends TExit.Variance, Equal.Equal { + readonly _tag: OpCodes.OP_RETRY +} + +/** @internal */ +export const isExit = (u: unknown): u is TExit => hasProperty(u, TExitTypeId) + +/** @internal */ +export const isFail = (self: TExit): self is Fail => { + return self._tag === OpCodes.OP_FAIL +} + +/** @internal */ +export const isDie = (self: TExit): self is Die => { + return self._tag === OpCodes.OP_DIE +} + +/** @internal */ +export const isInterrupt = (self: TExit): self is Interrupt => { + return self._tag === OpCodes.OP_INTERRUPT +} + +/** @internal */ +export const isSuccess = (self: TExit): self is Succeed => { + return self._tag === OpCodes.OP_SUCCEED +} + +/** @internal */ +export const isRetry = (self: TExit): self is Retry => { + return self._tag === OpCodes.OP_RETRY +} + +/** @internal */ +export const fail = (error: E): TExit => ({ + [TExitTypeId]: variance, + _tag: OpCodes.OP_FAIL, + error, + [Hash.symbol](): number { + return pipe( + Hash.hash(TExitSymbolKey), + Hash.combine(Hash.hash(OpCodes.OP_FAIL)), + Hash.combine(Hash.hash(error)), + Hash.cached(this) + ) + }, + [Equal.symbol](that: unknown): boolean { + return isExit(that) && that._tag === OpCodes.OP_FAIL && Equal.equals(error, that.error) + } +}) + +/** @internal */ +export const die = (defect: unknown): TExit => ({ + [TExitTypeId]: variance, + _tag: OpCodes.OP_DIE, + defect, + [Hash.symbol](): number { + return pipe( + Hash.hash(TExitSymbolKey), + Hash.combine(Hash.hash(OpCodes.OP_DIE)), + Hash.combine(Hash.hash(defect)), + Hash.cached(this) + ) + }, + [Equal.symbol](that: unknown): boolean { + return isExit(that) && that._tag === OpCodes.OP_DIE && Equal.equals(defect, that.defect) + } +}) + +/** @internal */ +export const interrupt = (fiberId: FiberId.FiberId): TExit => ({ + [TExitTypeId]: variance, + _tag: OpCodes.OP_INTERRUPT, + fiberId, + [Hash.symbol](): number { + return pipe( + Hash.hash(TExitSymbolKey), + Hash.combine(Hash.hash(OpCodes.OP_INTERRUPT)), + Hash.combine(Hash.hash(fiberId)), + Hash.cached(this) + ) + }, + [Equal.symbol](that: unknown): boolean { + return isExit(that) && that._tag === OpCodes.OP_INTERRUPT && Equal.equals(fiberId, that.fiberId) + } +}) + +/** @internal */ +export const succeed = (value: A): TExit => ({ + [TExitTypeId]: variance, + _tag: OpCodes.OP_SUCCEED, + value, + [Hash.symbol](): number { + return pipe( + Hash.hash(TExitSymbolKey), + Hash.combine(Hash.hash(OpCodes.OP_SUCCEED)), + Hash.combine(Hash.hash(value)), + Hash.cached(this) + ) + }, + [Equal.symbol](that: unknown): boolean { + return isExit(that) && that._tag === OpCodes.OP_SUCCEED && Equal.equals(value, that.value) + } +}) + +const retryHash = pipe( + Hash.hash(TExitSymbolKey), + Hash.combine(Hash.hash(OpCodes.OP_RETRY)), + Hash.combine(Hash.hash("retry")) +) + +/** @internal */ +export const retry: TExit = { + [TExitTypeId]: variance, + _tag: OpCodes.OP_RETRY, + [Hash.symbol](): number { + return retryHash + }, + [Equal.symbol](that: unknown): boolean { + return isExit(that) && isRetry(that) + } +} + +const void_: TExit = succeed(undefined) +export { + /** @internal */ + void_ as void +} diff --git a/backend/node_modules/effect/src/internal/stm/tMap.ts b/backend/node_modules/effect/src/internal/stm/tMap.ts new file mode 100644 index 0000000000000000000000000000000000000000..b5545fc7f4d92bee0cf469bef70105ab717ba0c2 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tMap.ts @@ -0,0 +1,824 @@ +import * as RA from "../../Array.js" +import * as Chunk from "../../Chunk.js" +import * as Equal from "../../Equal.js" +import type { LazyArg } from "../../Function.js" +import { dual, pipe } from "../../Function.js" +import * as Hash from "../../Hash.js" +import * as HashMap from "../../HashMap.js" +import * as Option from "../../Option.js" +import { hasProperty } from "../../Predicate.js" +import * as STM from "../../STM.js" +import type * as TArray from "../../TArray.js" +import type * as TMap from "../../TMap.js" +import type * as TRef from "../../TRef.js" +import * as core from "./core.js" +import type * as Journal from "./journal.js" +import * as stm from "./stm.js" +import * as tArray from "./tArray.js" +import * as tRef from "./tRef.js" + +/** @internal */ +const TMapSymbolKey = "effect/TMap" + +/** @internal */ +export const TMapTypeId: TMap.TMapTypeId = Symbol.for( + TMapSymbolKey +) as TMap.TMapTypeId + +const tMapVariance = { + /* c8 ignore next */ + _K: (_: any) => _, + /* c8 ignore next */ + _V: (_: any) => _ +} + +/** @internal */ +class TMapImpl implements TMap.TMap { + readonly [TMapTypeId] = tMapVariance + constructor( + readonly tBuckets: TRef.TRef>>, + readonly tSize: TRef.TRef + ) {} +} + +const isTMap = (u: unknown) => hasProperty(u, TMapTypeId) + +/** @internal */ +const InitialCapacity = 16 +const LoadFactor = 0.75 + +/** @internal */ +const nextPowerOfTwo = (size: number): number => { + const n = -1 >>> Math.clz32(size - 1) + return n < 0 ? 1 : n + 1 +} + +/** @internal */ +const hash = (key: K): number => { + const h = Hash.hash(key) + return h ^ (h >>> 16) +} + +/** @internal */ +const indexOf = (k: K, capacity: number): number => hash(k) & (capacity - 1) + +/** @internal */ +const allocate = ( + capacity: number, + data: Chunk.Chunk +): STM.STM> => { + const buckets = Array.from({ length: capacity }, () => Chunk.empty()) + const distinct = new Map(data) + let size = 0 + for (const entry of distinct) { + const index = indexOf(entry[0], capacity) + buckets[index] = pipe(buckets[index], Chunk.prepend(entry)) + size = size + 1 + } + return pipe( + tArray.fromIterable(buckets), + core.flatMap((buckets) => + pipe( + tRef.make(buckets), + core.flatMap((tBuckets) => + pipe( + tRef.make(size), + core.map((tSize) => new TMapImpl(tBuckets, tSize)) + ) + ) + ) + ) + ) +} + +/** @internal */ +export const empty = (): STM.STM> => fromIterable([]) + +/** @internal */ +export const find = dual< + ( + pf: (key: K, value: V) => Option.Option + ) => (self: TMap.TMap) => STM.STM>, + ( + self: TMap.TMap, + pf: (key: K, value: V) => Option.Option + ) => STM.STM> +>(2, (self, pf) => + findSTM(self, (key, value) => { + const option = pf(key, value) + if (Option.isSome(option)) { + return core.succeed(option.value) + } + return core.fail(Option.none()) + })) + +/** @internal */ +export const findSTM = dual< + ( + f: (key: K, value: V) => STM.STM, R> + ) => (self: TMap.TMap) => STM.STM, E, R>, + ( + self: TMap.TMap, + f: (key: K, value: V) => STM.STM, R> + ) => STM.STM, E, R> +>(2, ( + self: TMap.TMap, + f: (key: K, value: V) => STM.STM, R> +) => + reduceSTM(self, Option.none(), (acc, value, key) => + Option.isNone(acc) ? + core.matchSTM(f(key, value), { + onFailure: Option.match({ + onNone: () => stm.succeedNone, + onSome: core.fail + }), + onSuccess: stm.succeedSome + }) : + STM.succeed(acc))) + +/** @internal */ +export const findAll = dual< + ( + pf: (key: K, value: V) => Option.Option + ) => (self: TMap.TMap) => STM.STM>, + ( + self: TMap.TMap, + pf: (key: K, value: V) => Option.Option + ) => STM.STM> +>(2, (self, pf) => + findAllSTM(self, (key, value) => { + const option = pf(key, value) + if (Option.isSome(option)) { + return core.succeed(option.value) + } + return core.fail(Option.none()) + })) + +/** @internal */ +export const findAllSTM = dual< + ( + pf: (key: K, value: V) => STM.STM, R> + ) => (self: TMap.TMap) => STM.STM, E, R>, + ( + self: TMap.TMap, + pf: (key: K, value: V) => STM.STM, R> + ) => STM.STM, E, R> +>(2, ( + self: TMap.TMap, + pf: (key: K, value: V) => STM.STM, R> +) => + core.map( + reduceSTM(self, Chunk.empty(), (acc, value, key) => + core.matchSTM(pf(key, value), { + onFailure: Option.match({ + onNone: () => core.succeed(acc), + onSome: core.fail + }), + onSuccess: (a) => core.succeed(Chunk.append(acc, a)) + })), + (a) => Array.from(a) + )) + +/** @internal */ +export const forEach = dual< + (f: (key: K, value: V) => STM.STM) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, f: (key: K, value: V) => STM.STM) => STM.STM +>(2, (self, f) => + reduceSTM( + self, + void 0 as void, + (_, value, key) => stm.asVoid(f(key, value)) + )) + +/** @internal */ +export const fromIterable = (iterable: Iterable): STM.STM> => + stm.suspend(() => { + const data = Chunk.fromIterable(iterable) + const capacity = data.length < InitialCapacity + ? InitialCapacity + : nextPowerOfTwo(data.length) + return allocate(capacity, data) + }) + +/** @internal */ +export const get = dual< + (key: K) => (self: TMap.TMap) => STM.STM>, + (self: TMap.TMap, key: K) => STM.STM> +>(2, (self: TMap.TMap, key: K) => + core.effect>((journal) => { + const buckets = tRef.unsafeGet(self.tBuckets, journal) + const index = indexOf(key, buckets.chunk.length) + const bucket = tRef.unsafeGet(buckets.chunk[index], journal) + return pipe( + Chunk.findFirst(bucket, (entry) => Equal.equals(entry[0])(key)), + Option.map((entry) => entry[1]) + ) + })) + +/** @internal */ +export const getOrElse = dual< + (key: K, fallback: LazyArg) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, key: K, fallback: LazyArg) => STM.STM +>(3, (self, key, fallback) => + core.map( + get(self, key), + Option.getOrElse(fallback) + )) + +/** @internal */ +export const has = dual< + (key: K) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, key: K) => STM.STM +>(2, (self, key) => core.map(get(self, key), Option.isSome)) + +/** @internal */ +export const isEmpty = (self: TMap.TMap): STM.STM => + core.map(tRef.get(self.tSize), (size) => size === 0) + +/** @internal */ +export const keys = (self: TMap.TMap): STM.STM> => + core.map(toReadonlyArray(self), RA.map((entry) => entry[0])) + +/** @internal */ +export const make = (...entries: Array): STM.STM> => fromIterable(entries) + +/** @internal */ +export const merge = dual< + (key: K, value: V, f: (x: V, y: V) => V) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, key: K, value: V, f: (x: V, y: V) => V) => STM.STM +>(4, (self, key, value, f) => + core.flatMap( + get(self, key), + Option.match({ + onNone: () => stm.as(set(self, key, value), value), + onSome: (v0) => { + const v1 = f(v0, value) + return stm.as(set(self, key, v1), v1) + } + }) + )) + +/** @internal */ +export const reduce = dual< + (zero: Z, f: (acc: Z, value: V, key: K) => Z) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, zero: Z, f: (acc: Z, value: V, key: K) => Z) => STM.STM +>( + 3, + (self: TMap.TMap, zero: Z, f: (acc: Z, value: V, key: K) => Z) => + core.effect((journal) => { + const buckets = tRef.unsafeGet(self.tBuckets, journal) + let result = zero + let index = 0 + while (index < buckets.chunk.length) { + const bucket = buckets.chunk[index] + const items = tRef.unsafeGet(bucket, journal) + result = Chunk.reduce(items, result, (acc, entry) => f(acc, entry[1], entry[0])) + index = index + 1 + } + return result + }) +) + +/** @internal */ +export const reduceSTM = dual< + ( + zero: Z, + f: (acc: Z, value: V, key: K) => STM.STM + ) => (self: TMap.TMap) => STM.STM, + ( + self: TMap.TMap, + zero: Z, + f: (acc: Z, value: V, key: K) => STM.STM + ) => STM.STM +>(3, (self, zero, f) => + core.flatMap( + toReadonlyArray(self), + stm.reduce(zero, (acc, entry) => f(acc, entry[1], entry[0])) + )) + +/** @internal */ +export const remove = dual< + (key: K) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, key: K) => STM.STM +>(2, (self, key) => + core.effect((journal) => { + const buckets = tRef.unsafeGet(self.tBuckets, journal) + const index = indexOf(key, buckets.chunk.length) + const bucket = tRef.unsafeGet(buckets.chunk[index], journal) + const [toRemove, toRetain] = Chunk.partition(bucket, (entry) => Equal.equals(entry[1], key)) + if (Chunk.isNonEmpty(toRemove)) { + const currentSize = tRef.unsafeGet(self.tSize, journal) + tRef.unsafeSet(buckets.chunk[index], toRetain, journal) + tRef.unsafeSet(self.tSize, currentSize - 1, journal) + } + })) + +/** @internal */ +export const removeAll = dual< + (keys: Iterable) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, keys: Iterable) => STM.STM +>(2, (self: TMap.TMap, keys: Iterable) => + core.effect((journal) => { + const iterator = keys[Symbol.iterator]() + let next: IteratorResult + while ((next = iterator.next()) && !next.done) { + const buckets = tRef.unsafeGet(self.tBuckets, journal) + const index = indexOf(next.value, buckets.chunk.length) + const bucket = tRef.unsafeGet(buckets.chunk[index], journal) + const [toRemove, toRetain] = Chunk.partition(bucket, (entry) => Equal.equals(next.value)(entry[0])) + if (Chunk.isNonEmpty(toRemove)) { + const currentSize = tRef.unsafeGet(self.tSize, journal) + tRef.unsafeSet(buckets.chunk[index], toRetain, journal) + tRef.unsafeSet(self.tSize, currentSize - 1, journal) + } + } + })) + +/** @internal */ +export const removeIf: { + ( + predicate: (key: K, value: V) => boolean, + options: { + readonly discard: true + } + ): (self: TMap.TMap) => STM.STM + ( + predicate: (key: K, value: V) => boolean, + options?: { + readonly discard: false + } + ): (self: TMap.TMap) => STM.STM> + ( + self: TMap.TMap, + predicate: (key: K, value: V) => boolean, + options: { + readonly discard: true + } + ): STM.STM + ( + self: TMap.TMap, + predicate: (key: K, value: V) => boolean, + options?: { + readonly discard: false + } + ): STM.STM> +} = dual((args) => isTMap(args[0]), ( + self: TMap.TMap, + predicate: (key: K, value: V) => boolean, + options?: { + readonly discard: boolean + } +) => + core.effect((journal) => { + const discard = options?.discard === true + const buckets = tRef.unsafeGet(self.tBuckets, journal) + const capacity = buckets.chunk.length + const removed: Array<[K, V]> = [] + let index = 0 + let newSize = 0 + while (index < capacity) { + const bucket = tRef.unsafeGet(buckets.chunk[index], journal) + const iterator = bucket[Symbol.iterator]() + let next: IteratorResult + let newBucket = Chunk.empty() + while ((next = iterator.next()) && !next.done) { + const [k, v] = next.value + if (!predicate(k, v)) { + newBucket = Chunk.prepend(newBucket, next.value) + newSize = newSize + 1 + } else { + if (!discard) { + removed.push([k, v]) + } + } + } + tRef.unsafeSet(buckets.chunk[index], newBucket, journal) + index = index + 1 + } + tRef.unsafeSet(self.tSize, newSize, journal) + if (!discard) { + return removed + } + })) + +/** @internal */ +export const retainIf: { + ( + predicate: (key: K, value: V) => boolean, + options: { + readonly discard: true + } + ): (self: TMap.TMap) => STM.STM + ( + predicate: (key: K, value: V) => boolean, + options?: { + readonly discard: false + } + ): (self: TMap.TMap) => STM.STM> + ( + self: TMap.TMap, + predicate: (key: K, value: V) => boolean, + options: { + readonly discard: true + } + ): STM.STM + ( + self: TMap.TMap, + predicate: (key: K, value: V) => boolean, + options?: { + readonly discard: false + } + ): STM.STM> +} = dual( + (args) => isTMap(args[0]), + (self, predicate, options) => removeIf(self, (key, value) => !predicate(key, value), options) +) + +/** @internal */ +export const set = dual< + (key: K, value: V) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, key: K, value: V) => STM.STM +>(3, (self: TMap.TMap, key: K, value: V) => { + const resize = (journal: Journal.Journal, buckets: TArray.TArray>): void => { + const capacity = buckets.chunk.length + const newCapacity = capacity << 1 + const newBuckets = Array.from({ length: newCapacity }, () => Chunk.empty()) + let index = 0 + while (index < capacity) { + const pairs = tRef.unsafeGet(buckets.chunk[index], journal) + const iterator = pairs[Symbol.iterator]() + let next: IteratorResult + while ((next = iterator.next()) && !next.done) { + const newIndex = indexOf(next.value[0], newCapacity) + newBuckets[newIndex] = Chunk.prepend(newBuckets[newIndex], next.value) + } + index = index + 1 + } + // insert new pair + const newIndex = indexOf(key, newCapacity) + newBuckets[newIndex] = Chunk.prepend(newBuckets[newIndex], [key, value] as const) + + const newArray: Array>> = [] + index = 0 + while (index < newCapacity) { + newArray[index] = new tRef.TRefImpl(newBuckets[index]) + index = index + 1 + } + const newTArray: TArray.TArray> = new tArray.TArrayImpl(newArray) + tRef.unsafeSet(self.tBuckets, newTArray, journal) + } + return core.effect((journal) => { + const buckets = tRef.unsafeGet(self.tBuckets, journal) + const capacity = buckets.chunk.length + const index = indexOf(key, capacity) + const bucket = tRef.unsafeGet(buckets.chunk[index], journal) + const shouldUpdate = Chunk.some(bucket, (entry) => Equal.equals(key)(entry[0])) + if (shouldUpdate) { + const newBucket = Chunk.map(bucket, (entry) => + Equal.equals(key)(entry[0]) ? + [key, value] as const : + entry) + tRef.unsafeSet(buckets.chunk[index], newBucket, journal) + } else { + const newSize = tRef.unsafeGet(self.tSize, journal) + 1 + tRef.unsafeSet(self.tSize, newSize, journal) + if (capacity * LoadFactor < newSize) { + resize(journal, buckets) + } else { + const newBucket = Chunk.prepend(bucket, [key, value] as const) + tRef.unsafeSet(buckets.chunk[index], newBucket, journal) + } + } + }) +}) + +/** @internal */ +export const setIfAbsent = dual< + (key: K, value: V) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, key: K, value: V) => STM.STM +>(3, (self, key, value) => + core.flatMap( + get(self, key), + Option.match({ + onNone: () => set(self, key, value), + onSome: () => stm.void + }) + )) + +/** @internal */ +export const size = (self: TMap.TMap): STM.STM => tRef.get(self.tSize) + +/** @internal */ +export const takeFirst = dual< + (pf: (key: K, value: V) => Option.Option) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, pf: (key: K, value: V) => Option.Option) => STM.STM +>(2, (self: TMap.TMap, pf: (key: K, value: V) => Option.Option) => + pipe( + core.effect>((journal) => { + const buckets = tRef.unsafeGet(self.tBuckets, journal) + const capacity = buckets.chunk.length + const size = tRef.unsafeGet(self.tSize, journal) + let result: Option.Option = Option.none() + let index = 0 + while (index < capacity && Option.isNone(result)) { + const bucket = tRef.unsafeGet(buckets.chunk[index], journal) + const recreate = Chunk.some(bucket, (entry) => Option.isSome(pf(entry[0], entry[1]))) + if (recreate) { + const iterator = bucket[Symbol.iterator]() + let newBucket = Chunk.empty() + let next: IteratorResult + while ((next = iterator.next()) && !next.done && Option.isNone(result)) { + const option = pf(next.value[0], next.value[1]) + if (Option.isSome(option) && Option.isNone(result)) { + result = option + } else { + newBucket = Chunk.prepend(newBucket, next.value) + } + } + tRef.unsafeSet(buckets.chunk[index], newBucket, journal) + } + index = index + 1 + } + if (Option.isSome(result)) { + tRef.unsafeSet(self.tSize, size - 1, journal) + } + return result + }), + stm.collect((option) => + Option.isSome(option) ? + Option.some(option.value) : + Option.none() + ) + )) + +/** @internal */ +export const takeFirstSTM = dual< + ( + pf: (key: K, value: V) => STM.STM, R> + ) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, pf: (key: K, value: V) => STM.STM, R>) => STM.STM +>(2, (self, pf) => + pipe( + findSTM(self, (key, value) => core.map(pf(key, value), (a) => [key, a] as const)), + stm.collect((option) => Option.isSome(option) ? Option.some(option.value) : Option.none()), + core.flatMap((entry) => stm.as(remove(self, entry[0]), entry[1])) + )) + +/** @internal */ +export const takeSome = dual< + ( + pf: (key: K, value: V) => Option.Option + ) => (self: TMap.TMap) => STM.STM>, + ( + self: TMap.TMap, + pf: (key: K, value: V) => Option.Option + ) => STM.STM> +>(2, (self: TMap.TMap, pf: (key: K, value: V) => Option.Option) => + pipe( + core.effect>>((journal) => { + const buckets = tRef.unsafeGet(self.tBuckets, journal) + const capacity = buckets.chunk.length + const builder: Array = [] + let newSize = 0 + let index = 0 + while (index < capacity) { + const bucket = tRef.unsafeGet(buckets.chunk[index], journal) + const recreate = Chunk.some(bucket, (entry) => Option.isSome(pf(entry[0], entry[1]))) + if (recreate) { + const iterator = bucket[Symbol.iterator]() + let newBucket = Chunk.empty() + let next: IteratorResult + while ((next = iterator.next()) && !next.done) { + const option = pf(next.value[0], next.value[1]) + if (Option.isSome(option)) { + builder.push(option.value) + } else { + newBucket = Chunk.prepend(newBucket, next.value) + newSize = newSize + 1 + } + } + tRef.unsafeSet(buckets.chunk[index], newBucket, journal) + } else { + newSize = newSize + bucket.length + } + index = index + 1 + } + tRef.unsafeSet(self.tSize, newSize, journal) + if (builder.length > 0) { + return Option.some(builder as RA.NonEmptyArray) + } + return Option.none() + }), + stm.collect((option) => + Option.isSome(option) ? + Option.some(option.value) : + Option.none>() + ) + )) + +/** @internal */ +export const takeSomeSTM = dual< + ( + pf: (key: K, value: V) => STM.STM, R> + ) => (self: TMap.TMap) => STM.STM, E, R>, + ( + self: TMap.TMap, + pf: (key: K, value: V) => STM.STM, R> + ) => STM.STM, E, R> +>(2, ( + self: TMap.TMap, + pf: (key: K, value: V) => STM.STM, R> +) => + pipe( + findAllSTM( + self, + (key, value) => core.map(pf(key, value), (a) => [key, a] as const) + ), + core.map((chunk) => + RA.isNonEmptyArray(chunk) ? + Option.some(chunk) : + Option.none() + ), + stm.collect((option) => + Option.isSome(option) ? + Option.some(option.value) : + Option.none() + ), + core.flatMap((entries) => + stm.as( + removeAll(self, entries.map((entry) => entry[0])), + RA.map(entries, (entry) => entry[1]) as RA.NonEmptyArray + ) + ) + )) + +const toReadonlyArray = (self: TMap.TMap): STM.STM> => + core.effect>((journal) => { + const buckets = tRef.unsafeGet(self.tBuckets, journal) + const capacity = buckets.chunk.length + const builder: Array = [] + let index = 0 + while (index < capacity) { + const bucket = buckets.chunk[index] + for (const entry of tRef.unsafeGet(bucket, journal)) { + builder.push(entry) + } + index = index + 1 + } + return builder + }) + +/** @internal */ +export const toChunk = (self: TMap.TMap): STM.STM> => + reduce( + self, + Chunk.empty<[K, V]>(), + (acc, value, key) => Chunk.append(acc, [key, value]) + ) + +/** @internal */ +export const toHashMap = (self: TMap.TMap): STM.STM> => + reduce( + self, + HashMap.empty(), + (acc, value, key) => pipe(acc, HashMap.set(key, value)) + ) + +/** @internal */ +export const toArray = (self: TMap.TMap): STM.STM> => + reduce( + self, + [] as Array<[K, V]>, + (acc, value, key) => { + acc.unshift([key, value]) + return acc + } + ) + +/** @internal */ +export const toMap = (self: TMap.TMap): STM.STM> => + reduce( + self, + new Map(), + (acc, value, key) => acc.set(key, value) + ) + +/** @internal */ +export const transform = dual< + (f: (key: K, value: V) => readonly [K, V]) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, f: (key: K, value: V) => readonly [K, V]) => STM.STM +>( + 2, + (self: TMap.TMap, f: (key: K, value: V) => readonly [K, V]) => + core.effect((journal) => { + const buckets = pipe(self.tBuckets, tRef.unsafeGet(journal)) + const capacity = buckets.chunk.length + const newBuckets = Array.from({ length: capacity }, () => Chunk.empty()) + let newSize = 0 + let index = 0 + while (index < capacity) { + const bucket = buckets.chunk[index] + const pairs = tRef.unsafeGet(bucket, journal) + const iterator = pairs[Symbol.iterator]() + let next: IteratorResult + while ((next = iterator.next()) && !next.done) { + const newPair = f(next.value[0], next.value[1]) + const index = indexOf(newPair[0], capacity) + const newBucket = newBuckets[index] + if (!Chunk.some(newBucket, (entry) => Equal.equals(entry[0], newPair[0]))) { + newBuckets[index] = Chunk.prepend(newBucket, newPair) + newSize = newSize + 1 + } + } + index = index + 1 + } + index = 0 + while (index < capacity) { + tRef.unsafeSet(buckets.chunk[index], newBuckets[index], journal) + index = index + 1 + } + tRef.unsafeSet(self.tSize, newSize, journal) + }) +) + +/** @internal */ +export const transformSTM = dual< + ( + f: (key: K, value: V) => STM.STM + ) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, f: (key: K, value: V) => STM.STM) => STM.STM +>( + 2, + (self: TMap.TMap, f: (key: K, value: V) => STM.STM) => + pipe( + core.flatMap( + toReadonlyArray(self), + stm.forEach((entry) => f(entry[0], entry[1])) + ), + core.flatMap((newData) => + core.effect((journal) => { + const buckets = tRef.unsafeGet(self.tBuckets, journal) + const capacity = buckets.chunk.length + const newBuckets = Array.from({ length: capacity }, () => Chunk.empty()) + const iterator = newData[Symbol.iterator]() + let newSize = 0 + let next: IteratorResult + while ((next = iterator.next()) && !next.done) { + const index = indexOf(next.value[0], capacity) + const newBucket = newBuckets[index] + if (!Chunk.some(newBucket, (entry) => Equal.equals(entry[0])(next.value[0]))) { + newBuckets[index] = Chunk.prepend(newBucket, next.value) + newSize = newSize + 1 + } + } + let index = 0 + while (index < capacity) { + tRef.unsafeSet(buckets.chunk[index], newBuckets[index], journal) + index = index + 1 + } + tRef.unsafeSet(self.tSize, newSize, journal) + }) + ) + ) +) + +/** @internal */ +export const transformValues = dual< + (f: (value: V) => V) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, f: (value: V) => V) => STM.STM +>(2, (self, f) => transform(self, (key, value) => [key, f(value)])) + +/** @internal */ +export const transformValuesSTM = dual< + (f: (value: V) => STM.STM) => (self: TMap.TMap) => STM.STM, + (self: TMap.TMap, f: (value: V) => STM.STM) => STM.STM +>(2, (self, f) => + transformSTM( + self, + (key, value) => core.map(f(value), (value) => [key, value]) + )) + +/** @internal */ +export const updateWith = dual< + ( + key: K, + f: (value: Option.Option) => Option.Option + ) => (self: TMap.TMap) => STM.STM>, + ( + self: TMap.TMap, + key: K, + f: (value: Option.Option) => Option.Option + ) => STM.STM> +>(3, (self, key, f) => + core.flatMap(get(self, key), (option) => + Option.match( + f(option), + { + onNone: () => stm.as(remove(self, key), Option.none()), + onSome: (value) => stm.as(set(self, key, value), Option.some(value)) + } + ))) + +/** @internal */ +export const values = (self: TMap.TMap): STM.STM> => + core.map(toReadonlyArray(self), RA.map((entry) => entry[1])) diff --git a/backend/node_modules/effect/src/internal/stm/tPriorityQueue.ts b/backend/node_modules/effect/src/internal/stm/tPriorityQueue.ts new file mode 100644 index 0000000000000000000000000000000000000000..dbc7801390b889461c8e47e691d4340b94bf27ed --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tPriorityQueue.ts @@ -0,0 +1,267 @@ +import * as Arr from "../../Array.js" +import * as Chunk from "../../Chunk.js" +import { dual, pipe } from "../../Function.js" +import * as Option from "../../Option.js" +import type * as Order from "../../Order.js" +import type { Predicate } from "../../Predicate.js" +import * as SortedMap from "../../SortedMap.js" +import type * as STM from "../../STM.js" +import type * as TPriorityQueue from "../../TPriorityQueue.js" +import type * as TRef from "../../TRef.js" +import * as core from "./core.js" +import * as tRef from "./tRef.js" + +/** @internal */ +const TPriorityQueueSymbolKey = "effect/TPriorityQueue" + +/** @internal */ +export const TPriorityQueueTypeId: TPriorityQueue.TPriorityQueueTypeId = Symbol.for( + TPriorityQueueSymbolKey +) as TPriorityQueue.TPriorityQueueTypeId + +const tPriorityQueueVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +export class TPriorityQueueImpl implements TPriorityQueue.TPriorityQueue { + readonly [TPriorityQueueTypeId] = tPriorityQueueVariance + constructor(readonly ref: TRef.TRef]>>) {} +} + +/** @internal */ +export const empty = (order: Order.Order): STM.STM> => + pipe( + tRef.make(SortedMap.empty]>(order)), + core.map((ref) => new TPriorityQueueImpl(ref)) + ) + +/** @internal */ +export const fromIterable = + (order: Order.Order) => (iterable: Iterable): STM.STM> => + pipe( + tRef.make( + Arr.fromIterable(iterable).reduce( + (map, value) => + pipe( + map, + SortedMap.set( + value, + pipe( + map, + SortedMap.get(value), + Option.match({ + onNone: () => Arr.of(value), + onSome: Arr.prepend(value) + }) + ) + ) + ), + SortedMap.empty]>(order) + ) + ), + core.map((ref) => new TPriorityQueueImpl(ref)) + ) + +/** @internal */ +export const isEmpty = (self: TPriorityQueue.TPriorityQueue): STM.STM => + core.map(tRef.get(self.ref), SortedMap.isEmpty) + +/** @internal */ +export const isNonEmpty = (self: TPriorityQueue.TPriorityQueue): STM.STM => + core.map(tRef.get(self.ref), SortedMap.isNonEmpty) + +/** @internal */ +export const make = (order: Order.Order) => (...elements: Array): STM.STM> => + fromIterable(order)(elements) + +/** @internal */ +export const offer = dual< + (value: A) => (self: TPriorityQueue.TPriorityQueue) => STM.STM, + (self: TPriorityQueue.TPriorityQueue, value: A) => STM.STM +>(2, (self, value) => + tRef.update(self.ref, (map) => + SortedMap.set( + map, + value, + Option.match(SortedMap.get(map, value), { + onNone: () => Arr.of(value), + onSome: Arr.prepend(value) + }) + ))) + +/** @internal */ +export const offerAll = dual< + (values: Iterable) => (self: TPriorityQueue.TPriorityQueue) => STM.STM, + (self: TPriorityQueue.TPriorityQueue, values: Iterable) => STM.STM +>(2, (self, values) => + tRef.update(self.ref, (map) => + Arr.fromIterable(values).reduce( + (map, value) => + SortedMap.set( + map, + value, + Option.match(SortedMap.get(map, value), { + onNone: () => Arr.of(value), + onSome: Arr.prepend(value) + }) + ), + map + ))) + +/** @internal */ +export const peek = (self: TPriorityQueue.TPriorityQueue): STM.STM => + core.withSTMRuntime((runtime) => { + const map = tRef.unsafeGet(self.ref, runtime.journal) + return Option.match( + SortedMap.headOption(map), + { + onNone: () => core.retry, + onSome: (elements) => core.succeed(elements[0]) + } + ) + }) + +/** @internal */ +export const peekOption = (self: TPriorityQueue.TPriorityQueue): STM.STM> => + tRef.modify(self.ref, (map) => [ + Option.map(SortedMap.headOption(map), (elements) => elements[0]), + map + ]) + +/** @internal */ +export const removeIf = dual< + (predicate: Predicate) => (self: TPriorityQueue.TPriorityQueue) => STM.STM, + (self: TPriorityQueue.TPriorityQueue, predicate: Predicate) => STM.STM +>(2, (self, predicate) => retainIf(self, (a) => !predicate(a))) + +/** @internal */ +export const retainIf = dual< + (predicate: Predicate) => (self: TPriorityQueue.TPriorityQueue) => STM.STM, + (self: TPriorityQueue.TPriorityQueue, predicate: Predicate) => STM.STM +>( + 2, + (self: TPriorityQueue.TPriorityQueue, predicate: Predicate) => + tRef.update( + self.ref, + (map) => + SortedMap.reduce(map, SortedMap.empty(SortedMap.getOrder(map)), (map, value, key) => { + const filtered: ReadonlyArray = Arr.filter(value, predicate) + return filtered.length > 0 ? + SortedMap.set(map, key, filtered as [A, ...Array]) : + SortedMap.remove(map, key) + }) + ) +) + +/** @internal */ +export const size = (self: TPriorityQueue.TPriorityQueue): STM.STM => + tRef.modify( + self.ref, + (map) => [SortedMap.reduce(map, 0, (n, as) => n + as.length), map] + ) + +/** @internal */ +export const take = (self: TPriorityQueue.TPriorityQueue): STM.STM => + core.withSTMRuntime((runtime) => { + const map = tRef.unsafeGet(self.ref, runtime.journal) + return Option.match(SortedMap.headOption(map), { + onNone: () => core.retry, + onSome: (values) => { + const head = values[1][0] + const tail = values[1].slice(1) + tRef.unsafeSet( + self.ref, + tail.length > 0 ? + SortedMap.set(map, head, tail as [A, ...Array]) : + SortedMap.remove(map, head), + runtime.journal + ) + return core.succeed(head) + } + }) + }) + +/** @internal */ +export const takeAll = (self: TPriorityQueue.TPriorityQueue): STM.STM> => + tRef.modify(self.ref, (map) => { + const builder: Array = [] + for (const entry of map) { + for (const value of entry[1]) { + builder.push(value) + } + } + return [builder, SortedMap.empty(SortedMap.getOrder(map))] + }) + +/** @internal */ +export const takeOption = (self: TPriorityQueue.TPriorityQueue): STM.STM> => + core.effect>((journal) => { + const map = pipe(self.ref, tRef.unsafeGet(journal)) + return Option.match(SortedMap.headOption(map), { + onNone: (): Option.Option => Option.none(), + onSome: ([key, value]) => { + const tail = value.slice(1) + tRef.unsafeSet( + self.ref, + tail.length > 0 ? + SortedMap.set(map, key, tail as [A, ...Array]) : + SortedMap.remove(map, key), + journal + ) + return Option.some(value[0]) + } + }) + }) + +/** @internal */ +export const takeUpTo = dual< + (n: number) => (self: TPriorityQueue.TPriorityQueue) => STM.STM>, + (self: TPriorityQueue.TPriorityQueue, n: number) => STM.STM> +>(2, (self: TPriorityQueue.TPriorityQueue, n: number) => + tRef.modify(self.ref, (map) => { + const builder: Array = [] + const iterator = map[Symbol.iterator]() + let updated = map + let index = 0 + let next: IteratorResult]], any> + while ((next = iterator.next()) && !next.done && index < n) { + const [key, value] = next.value + const [left, right] = pipe(value, Arr.splitAt(n - index)) + for (const value of left) { + builder.push(value) + } + if (right.length > 0) { + updated = SortedMap.set(updated, key, right as [A, ...Array]) + } else { + updated = SortedMap.remove(updated, key) + } + index = index + left.length + } + return [builder, updated] + })) + +/** @internal */ +export const toChunk = (self: TPriorityQueue.TPriorityQueue): STM.STM> => + tRef.modify(self.ref, (map) => { + const builder: Array = [] + for (const entry of map) { + for (const value of entry[1]) { + builder.push(value) + } + } + return [Chunk.unsafeFromArray(builder), map] + }) + +/** @internal */ +export const toArray = (self: TPriorityQueue.TPriorityQueue): STM.STM> => + tRef.modify(self.ref, (map) => { + const builder: Array = [] + for (const entry of map) { + for (const value of entry[1]) { + builder.push(value) + } + } + return [builder, map] + }) diff --git a/backend/node_modules/effect/src/internal/stm/tPubSub.ts b/backend/node_modules/effect/src/internal/stm/tPubSub.ts new file mode 100644 index 0000000000000000000000000000000000000000..b838ddb90a2eb58a8675762da863d46c0c6ca9fc --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tPubSub.ts @@ -0,0 +1,551 @@ +import * as RA from "../../Array.js" +import * as Effect from "../../Effect.js" +import { dual, identity, pipe } from "../../Function.js" +import * as HashSet from "../../HashSet.js" +import * as Option from "../../Option.js" +import type * as Scope from "../../Scope.js" +import type * as STM from "../../STM.js" +import type * as TPubSub from "../../TPubSub.js" +import type * as TQueue from "../../TQueue.js" +import type * as TRef from "../../TRef.js" +import * as core from "./core.js" +import * as OpCodes from "./opCodes/strategy.js" +import * as stm from "./stm.js" +import * as tQueue from "./tQueue.js" +import * as tRef from "./tRef.js" + +/** @internal */ +const TPubSubSymbolKey = "effect/TPubSub" + +/** @internal */ +export const TPubSubTypeId: TPubSub.TPubSubTypeId = Symbol.for(TPubSubSymbolKey) as TPubSub.TPubSubTypeId + +const AbsentValue = Symbol.for("effect/TPubSub/AbsentValue") +type AbsentValue = typeof AbsentValue + +/** @internal */ +export interface Node { + readonly head: A | AbsentValue + readonly subscribers: number + readonly tail: TRef.TRef | undefined> +} + +/** @internal */ +export const makeNode = ( + head: A | AbsentValue, + subscribers: number, + tail: TRef.TRef | undefined> +): Node => ({ + head, + subscribers, + tail +}) + +/** @internal */ +class TPubSubImpl implements TPubSub.TPubSub { + readonly [TPubSubTypeId] = { + _A: (_: any) => _ + } + readonly [tQueue.TEnqueueTypeId] = tQueue.tEnqueueVariance + constructor( + readonly pubsubSize: TRef.TRef, + readonly publisherHead: TRef.TRef | undefined>>, + readonly publisherTail: TRef.TRef | undefined> | undefined>, + readonly requestedCapacity: number, + readonly strategy: tQueue.TQueueStrategy, + readonly subscriberCount: TRef.TRef, + readonly subscribers: TRef.TRef> | undefined>>> + ) {} + + isShutdown: STM.STM = core.effect((journal) => { + const currentPublisherTail = tRef.unsafeGet(this.publisherTail, journal) + return currentPublisherTail === undefined + }) + + awaitShutdown: STM.STM = core.flatMap( + this.isShutdown, + (isShutdown) => isShutdown ? stm.void : core.retry + ) + + capacity(): number { + return this.requestedCapacity + } + + size: STM.STM = core.withSTMRuntime((runtime) => { + const currentPublisherTail = tRef.unsafeGet(this.publisherTail, runtime.journal) + if (currentPublisherTail === undefined) { + return core.interruptAs(runtime.fiberId) + } + return core.succeed(tRef.unsafeGet(this.pubsubSize, runtime.journal)) + }) + + isEmpty: STM.STM = core.map(this.size, (size) => size === 0) + + isFull: STM.STM = core.map(this.size, (size) => size === this.capacity()) + + offer(value: A): STM.STM { + return core.withSTMRuntime((runtime) => { + const currentPublisherTail = tRef.unsafeGet(this.publisherTail, runtime.journal) + if (currentPublisherTail === undefined) { + return core.interruptAs(runtime.fiberId) + } + const currentSubscriberCount = tRef.unsafeGet(this.subscriberCount, runtime.journal) + if (currentSubscriberCount === 0) { + return core.succeed(true) + } + const currentPubSubSize = tRef.unsafeGet(this.pubsubSize, runtime.journal) + if (currentPubSubSize < this.requestedCapacity) { + const updatedPublisherTail: TRef.TRef | undefined> = new tRef.TRefImpl | undefined>(void 0) + const updatedNode = makeNode(value, currentSubscriberCount, updatedPublisherTail) + tRef.unsafeSet | undefined>(currentPublisherTail, updatedNode, runtime.journal) + tRef.unsafeSet | undefined> | undefined>( + this.publisherTail, + updatedPublisherTail, + runtime.journal + ) + tRef.unsafeSet(this.pubsubSize, currentPubSubSize + 1, runtime.journal) + return core.succeed(true) + } + switch (this.strategy._tag) { + case OpCodes.OP_BACKPRESSURE_STRATEGY: { + return core.retry + } + case OpCodes.OP_DROPPING_STRATEGY: { + return core.succeed(false) + } + case OpCodes.OP_SLIDING_STRATEGY: { + if (this.requestedCapacity > 0) { + let currentPublisherHead: TRef.TRef | undefined> = tRef.unsafeGet( + this.publisherHead, + runtime.journal + ) + let loop = true + while (loop) { + const node = tRef.unsafeGet(currentPublisherHead, runtime.journal) + if (node === undefined) { + return core.retry + } + const head = node.head + const tail = node.tail + if (head !== AbsentValue) { + const updatedNode = makeNode(AbsentValue, node.subscribers, node.tail as any) + tRef.unsafeSet | undefined>( + currentPublisherHead as any, + updatedNode as any, + runtime.journal + ) + tRef.unsafeSet(this.publisherHead, tail, runtime.journal) + loop = false + } else { + currentPublisherHead = tail + } + } + } + const updatedPublisherTail: TRef.TRef | undefined> = new tRef.TRefImpl | undefined>(void 0) + const updatedNode = makeNode(value, currentSubscriberCount, updatedPublisherTail) + tRef.unsafeSet | undefined>(currentPublisherTail, updatedNode, runtime.journal) + tRef.unsafeSet | undefined> | undefined>( + this.publisherTail, + updatedPublisherTail, + runtime.journal + ) + return core.succeed(true) + } + } + }) + } + + offerAll(iterable: Iterable): STM.STM { + return core.map( + stm.forEach(iterable, (a) => this.offer(a)), + RA.every(identity) + ) + } + + shutdown: STM.STM = core.effect((journal) => { + const currentPublisherTail = tRef.unsafeGet(this.publisherTail, journal) + if (currentPublisherTail !== undefined) { + tRef.unsafeSet | undefined> | undefined>(this.publisherTail, void 0, journal) + const currentSubscribers = tRef.unsafeGet(this.subscribers, journal) + HashSet.forEach(currentSubscribers, (subscriber) => { + tRef.unsafeSet> | undefined>(subscriber, void 0, journal) + }) + tRef.unsafeSet(this.subscribers, HashSet.empty> | undefined>>(), journal) + } + }) +} + +/** @internal */ +class TPubSubSubscriptionImpl implements TQueue.TDequeue { + readonly [TPubSubTypeId]: TPubSub.TPubSubTypeId = TPubSubTypeId + readonly [tQueue.TDequeueTypeId] = tQueue.tDequeueVariance + constructor( + readonly pubsubSize: TRef.TRef, + readonly publisherHead: TRef.TRef | undefined>>, + readonly requestedCapacity: number, + readonly subscriberHead: TRef.TRef | undefined> | undefined>, + readonly subscriberCount: TRef.TRef, + readonly subscribers: TRef.TRef> | undefined>>> + ) {} + + isShutdown: STM.STM = core.effect((journal) => { + const currentSubscriberHead = tRef.unsafeGet(this.subscriberHead, journal) + return currentSubscriberHead === undefined + }) + + awaitShutdown: STM.STM = core.flatMap( + this.isShutdown, + (isShutdown) => isShutdown ? stm.void : core.retry + ) + + capacity(): number { + return this.requestedCapacity + } + + size: STM.STM = core.withSTMRuntime((runtime) => { + let currentSubscriberHead = tRef.unsafeGet(this.subscriberHead, runtime.journal) + if (currentSubscriberHead === undefined) { + return core.interruptAs(runtime.fiberId) + } + let loop = true + let size = 0 + while (loop) { + const node = tRef.unsafeGet(currentSubscriberHead, runtime.journal) + if (node === undefined) { + loop = false + } else { + const head = node.head + const tail: TRef.TRef | undefined> = node.tail + if (head !== AbsentValue) { + size = size + 1 + if (size >= Number.MAX_SAFE_INTEGER) { + loop = false + } + } + currentSubscriberHead = tail + } + } + return core.succeed(size) + }) + + isEmpty: STM.STM = core.map(this.size, (size) => size === 0) + + isFull: STM.STM = core.map(this.size, (size) => size === this.capacity()) + + peek: STM.STM = core.withSTMRuntime((runtime) => { + let currentSubscriberHead = tRef.unsafeGet(this.subscriberHead, runtime.journal) + if (currentSubscriberHead === undefined) { + return core.interruptAs(runtime.fiberId) + } + let value: A | AbsentValue = AbsentValue + let loop = true + while (loop) { + const node = tRef.unsafeGet(currentSubscriberHead, runtime.journal) + if (node === undefined) { + return core.retry + } + const head = node.head + const tail: TRef.TRef | undefined> = node.tail + if (head !== AbsentValue) { + value = head + loop = false + } else { + currentSubscriberHead = tail + } + } + return core.succeed(value as A) + }) + + peekOption: STM.STM> = core.withSTMRuntime((runtime) => { + let currentSubscriberHead = tRef.unsafeGet(this.subscriberHead, runtime.journal) + if (currentSubscriberHead === undefined) { + return core.interruptAs(runtime.fiberId) + } + let value: Option.Option = Option.none() + let loop = true + while (loop) { + const node = tRef.unsafeGet(currentSubscriberHead, runtime.journal) + if (node === undefined) { + value = Option.none() + loop = false + } else { + const head = node.head + const tail: TRef.TRef | undefined> = node.tail + if (head !== AbsentValue) { + value = Option.some(head) + loop = false + } else { + currentSubscriberHead = tail + } + } + } + return core.succeed(value) + }) + + shutdown: STM.STM = core.effect((journal) => { + let currentSubscriberHead = tRef.unsafeGet(this.subscriberHead, journal) + if (currentSubscriberHead !== undefined) { + tRef.unsafeSet | undefined> | undefined>(this.subscriberHead, void 0, journal) + let loop = true + while (loop) { + const node = tRef.unsafeGet(currentSubscriberHead, journal) + if (node === undefined) { + loop = false + } else { + const head = node.head + const tail: TRef.TRef | undefined> = node.tail + if (head !== AbsentValue) { + const subscribers = node.subscribers + if (subscribers === 1) { + const size = tRef.unsafeGet(this.pubsubSize, journal) + const updatedNode = makeNode(AbsentValue, 0, tail) + tRef.unsafeSet | undefined>(currentSubscriberHead, updatedNode, journal) + tRef.unsafeSet(this.publisherHead, tail as any, journal) + tRef.unsafeSet(this.pubsubSize, size - 1, journal) + } else { + const updatedNode = makeNode(head, subscribers - 1, tail) + tRef.unsafeSet | undefined>(currentSubscriberHead, updatedNode, journal) + } + } + currentSubscriberHead = tail + } + } + const currentSubscriberCount = tRef.unsafeGet(this.subscriberCount, journal) + tRef.unsafeSet(this.subscriberCount, currentSubscriberCount - 1, journal) + tRef.unsafeSet( + this.subscribers, + HashSet.remove( + tRef.unsafeGet(this.subscribers, journal), + this.subscriberHead as any + ), + journal + ) + } + }) + + take: STM.STM = core.withSTMRuntime((runtime) => { + let currentSubscriberHead = tRef.unsafeGet(this.subscriberHead, runtime.journal) + if (currentSubscriberHead === undefined) { + return core.interruptAs(runtime.fiberId) + } + let value: A | AbsentValue = AbsentValue + let loop = true + while (loop) { + const node = tRef.unsafeGet(currentSubscriberHead, runtime.journal) + if (node === undefined) { + return core.retry + } + const head = node.head + const tail: TRef.TRef | undefined> = node.tail + if (head !== AbsentValue) { + const subscribers = node.subscribers + if (subscribers === 1) { + const size = tRef.unsafeGet(this.pubsubSize, runtime.journal) + const updatedNode = makeNode(AbsentValue, 0, tail) + tRef.unsafeSet | undefined>(currentSubscriberHead, updatedNode, runtime.journal) + tRef.unsafeSet(this.publisherHead, tail as any, runtime.journal) + tRef.unsafeSet(this.pubsubSize, size - 1, runtime.journal) + } else { + const updatedNode = makeNode(head, subscribers - 1, tail) + tRef.unsafeSet | undefined>(currentSubscriberHead, updatedNode, runtime.journal) + } + tRef.unsafeSet | undefined> | undefined>( + this.subscriberHead, + tail, + runtime.journal + ) + value = head + loop = false + } else { + currentSubscriberHead = tail + } + } + return core.succeed(value as A) + }) + + takeAll: STM.STM> = this.takeUpTo(Number.POSITIVE_INFINITY) + + takeUpTo(max: number): STM.STM> { + return core.withSTMRuntime((runtime) => { + let currentSubscriberHead = tRef.unsafeGet(this.subscriberHead, runtime.journal) + if (currentSubscriberHead === undefined) { + return core.interruptAs(runtime.fiberId) + } + const builder: Array = [] + let n = 0 + while (n !== max) { + const node = tRef.unsafeGet(currentSubscriberHead, runtime.journal) + if (node === undefined) { + n = max + } else { + const head = node.head + const tail: TRef.TRef | undefined> = node.tail + if (head !== AbsentValue) { + const subscribers = node.subscribers + if (subscribers === 1) { + const size = tRef.unsafeGet(this.pubsubSize, runtime.journal) + const updatedNode = makeNode(AbsentValue, 0, tail) + tRef.unsafeSet | undefined>(currentSubscriberHead, updatedNode, runtime.journal) + tRef.unsafeSet(this.publisherHead, tail as any, runtime.journal) + tRef.unsafeSet(this.pubsubSize, size - 1, runtime.journal) + } else { + const updatedNode = makeNode(head, subscribers - 1, tail) + tRef.unsafeSet | undefined>(currentSubscriberHead, updatedNode, runtime.journal) + } + builder.push(head) + n = n + 1 + } + currentSubscriberHead = tail + } + } + tRef.unsafeSet | undefined> | undefined>( + this.subscriberHead, + currentSubscriberHead, + runtime.journal + ) + return core.succeed(builder) + }) + } +} + +/** @internal */ +const makeTPubSub = ( + requestedCapacity: number, + strategy: tQueue.TQueueStrategy +): STM.STM> => + pipe( + stm.all([ + tRef.make | undefined>(void 0), + tRef.make(0) + ]), + core.flatMap(([empty, pubsubSize]) => + pipe( + stm.all([ + tRef.make(empty), + tRef.make(empty), + tRef.make(0), + tRef.make(HashSet.empty()) + ]), + core.map(([publisherHead, publisherTail, subscriberCount, subscribers]) => + new TPubSubImpl( + pubsubSize, + publisherHead, + publisherTail as any, + requestedCapacity, + strategy, + subscriberCount, + subscribers as any + ) + ) + ) + ) + ) + +const makeSubscription = ( + pubsubSize: TRef.TRef, + publisherHead: TRef.TRef | undefined>>, + publisherTail: TRef.TRef | undefined> | undefined>, + requestedCapacity: number, + subscriberCount: TRef.TRef, + subscribers: TRef.TRef> | undefined>>> +): STM.STM> => + pipe( + tRef.get(publisherTail), + core.flatMap((currentPublisherTail) => + pipe( + stm.all([ + tRef.make(currentPublisherTail), + tRef.get(subscriberCount), + tRef.get(subscribers) + ]), + stm.tap(([_, currentSubscriberCount]) => + pipe( + subscriberCount, + tRef.set(currentSubscriberCount + 1) + ) + ), + stm.tap(([subscriberHead, _, currentSubscribers]) => + pipe( + subscribers as any, + tRef.set(pipe(currentSubscribers as any, HashSet.add(subscriberHead))) + ) + ), + core.map(([subscriberHead]) => + new TPubSubSubscriptionImpl( + pubsubSize, + publisherHead, + requestedCapacity, + subscriberHead as any, + subscriberCount, + subscribers + ) + ) + ) + ) + ) + +/** @internal */ +export const awaitShutdown = (self: TPubSub.TPubSub): STM.STM => self.awaitShutdown + +/** @internal */ +export const bounded = (requestedCapacity: number): STM.STM> => + makeTPubSub(requestedCapacity, tQueue.BackPressure) + +/** @internal */ +export const capacity = (self: TPubSub.TPubSub): number => self.capacity() + +/** @internal */ +export const dropping = (requestedCapacity: number): STM.STM> => + makeTPubSub(requestedCapacity, tQueue.Dropping) + +/** @internal */ +export const isEmpty = (self: TPubSub.TPubSub): STM.STM => self.isEmpty + +/** @internal */ +export const isFull = (self: TPubSub.TPubSub): STM.STM => self.isFull + +/** @internal */ +export const isShutdown = (self: TPubSub.TPubSub): STM.STM => self.isShutdown + +/** @internal */ +export const publish = dual< + (value: A) => (self: TPubSub.TPubSub) => STM.STM, + (self: TPubSub.TPubSub, value: A) => STM.STM +>(2, (self, value) => self.offer(value)) + +/** @internal */ +export const publishAll = dual< + (iterable: Iterable) => (self: TPubSub.TPubSub) => STM.STM, + (self: TPubSub.TPubSub, iterable: Iterable) => STM.STM +>(2, (self, iterable) => self.offerAll(iterable)) + +/** @internal */ +export const size = (self: TPubSub.TPubSub): STM.STM => self.size + +/** @internal */ +export const shutdown = (self: TPubSub.TPubSub): STM.STM => self.shutdown + +/** @internal */ +export const sliding = (requestedCapacity: number): STM.STM> => + makeTPubSub(requestedCapacity, tQueue.Sliding) + +/** @internal */ +export const subscribe = (self: TPubSub.TPubSub): STM.STM> => + makeSubscription( + self.pubsubSize, + self.publisherHead, + self.publisherTail, + self.requestedCapacity, + self.subscriberCount, + self.subscribers + ) + +/** @internal */ +export const subscribeScoped = (self: TPubSub.TPubSub): Effect.Effect, never, Scope.Scope> => + Effect.acquireRelease( + subscribe(self), + (dequeue) => tQueue.shutdown(dequeue) + ) + +/** @internal */ +export const unbounded = (): STM.STM> => makeTPubSub(Number.MAX_SAFE_INTEGER, tQueue.Dropping) diff --git a/backend/node_modules/effect/src/internal/stm/tQueue.ts b/backend/node_modules/effect/src/internal/stm/tQueue.ts new file mode 100644 index 0000000000000000000000000000000000000000..8ca27b9865c4ee07d97648f23ce8573f22d2751b --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tQueue.ts @@ -0,0 +1,393 @@ +import * as RA from "../../Array.js" +import * as Chunk from "../../Chunk.js" +import { dual, pipe } from "../../Function.js" +import * as Option from "../../Option.js" +import { hasProperty, type Predicate } from "../../Predicate.js" +import type * as STM from "../../STM.js" +import type * as TQueue from "../../TQueue.js" +import type * as TRef from "../../TRef.js" +import * as core from "./core.js" +import * as OpCodes from "./opCodes/strategy.js" +import * as stm from "./stm.js" +import * as tRef from "./tRef.js" + +const TEnqueueSymbolKey = "effect/TQueue/TEnqueue" + +/** @internal */ +export const TEnqueueTypeId: TQueue.TEnqueueTypeId = Symbol.for(TEnqueueSymbolKey) as TQueue.TEnqueueTypeId + +const TDequeueSymbolKey = "effect/TQueue/TDequeue" + +/** @internal */ +export const TDequeueTypeId: TQueue.TDequeueTypeId = Symbol.for(TDequeueSymbolKey) as TQueue.TDequeueTypeId + +/** + * A `Strategy` describes how the queue will handle values if the queue is at + * capacity. + * + * @internal + */ +export type TQueueStrategy = BackPressure | Dropping | Sliding + +/** + * A strategy that retries if the queue is at capacity. + * + * @internal + */ +export interface BackPressure { + readonly _tag: OpCodes.OP_BACKPRESSURE_STRATEGY +} + +/** + * A strategy that drops new values if the queue is at capacity. + * + * @internal + */ +export interface Dropping { + readonly _tag: OpCodes.OP_DROPPING_STRATEGY +} + +/** + * A strategy that drops old values if the queue is at capacity. + * + * @internal + */ +export interface Sliding { + readonly _tag: OpCodes.OP_SLIDING_STRATEGY +} + +/** @internal */ +export const BackPressure: TQueueStrategy = { + _tag: OpCodes.OP_BACKPRESSURE_STRATEGY +} + +/** @internal */ +export const Dropping: TQueueStrategy = { + _tag: OpCodes.OP_DROPPING_STRATEGY +} + +/** @internal */ +export const Sliding: TQueueStrategy = { + _tag: OpCodes.OP_SLIDING_STRATEGY +} + +/** @internal */ +export const tDequeueVariance = { + /* c8 ignore next */ + _Out: (_: never) => _ +} + +/** @internal */ +export const tEnqueueVariance = { + /* c8 ignore next */ + _In: (_: unknown) => _ +} + +class TQueueImpl implements TQueue.TQueue { + readonly [TDequeueTypeId] = tDequeueVariance + readonly [TEnqueueTypeId] = tEnqueueVariance + constructor( + readonly ref: TRef.TRef | undefined>, + readonly requestedCapacity: number, + readonly strategy: TQueueStrategy + ) {} + + capacity(): number { + return this.requestedCapacity + } + + size: STM.STM = core.withSTMRuntime((runtime) => { + const queue = tRef.unsafeGet(this.ref, runtime.journal) + if (queue === undefined) { + return core.interruptAs(runtime.fiberId) + } + return core.succeed(queue.length) + }) + + isFull: STM.STM = core.map(this.size, (size) => size === this.requestedCapacity) + + isEmpty: STM.STM = core.map(this.size, (size) => size === 0) + + shutdown: STM.STM = core.withSTMRuntime((runtime) => { + tRef.unsafeSet(this.ref, void 0, runtime.journal) + return stm.void + }) + + isShutdown: STM.STM = core.effect((journal) => { + const queue = tRef.unsafeGet(this.ref, journal) + return queue === undefined + }) + + awaitShutdown: STM.STM = core.flatMap( + this.isShutdown, + (isShutdown) => isShutdown ? stm.void : core.retry + ) + + offer(value: A): STM.STM { + return core.withSTMRuntime((runtime) => { + const queue = pipe(this.ref, tRef.unsafeGet(runtime.journal)) + if (queue === undefined) { + return core.interruptAs(runtime.fiberId) + } + if (queue.length < this.requestedCapacity) { + queue.push(value) + tRef.unsafeSet(this.ref, queue, runtime.journal) + return core.succeed(true) + } + switch (this.strategy._tag) { + case OpCodes.OP_BACKPRESSURE_STRATEGY: { + return core.retry + } + case OpCodes.OP_DROPPING_STRATEGY: { + return core.succeed(false) + } + case OpCodes.OP_SLIDING_STRATEGY: { + if (queue.length === 0) { + return core.succeed(true) + } + queue.shift() + queue.push(value) + tRef.unsafeSet(this.ref, queue, runtime.journal) + return core.succeed(true) + } + } + }) + } + + offerAll(iterable: Iterable): STM.STM { + return core.withSTMRuntime((runtime) => { + const as = Array.from(iterable) + const queue = tRef.unsafeGet(this.ref, runtime.journal) + if (queue === undefined) { + return core.interruptAs(runtime.fiberId) + } + if (queue.length + as.length <= this.requestedCapacity) { + tRef.unsafeSet(this.ref, [...queue, ...as], runtime.journal) + return core.succeed(true) + } + switch (this.strategy._tag) { + case OpCodes.OP_BACKPRESSURE_STRATEGY: { + return core.retry + } + case OpCodes.OP_DROPPING_STRATEGY: { + const forQueue = as.slice(0, this.requestedCapacity - queue.length) + tRef.unsafeSet(this.ref, [...queue, ...forQueue], runtime.journal) + return core.succeed(false) + } + case OpCodes.OP_SLIDING_STRATEGY: { + const forQueue = as.slice(0, this.requestedCapacity - queue.length) + const toDrop = queue.length + forQueue.length - this.requestedCapacity + const newQueue = queue.slice(toDrop) + tRef.unsafeSet(this.ref, [...newQueue, ...forQueue], runtime.journal) + return core.succeed(true) + } + } + }) + } + + peek: STM.STM = core.withSTMRuntime((runtime) => { + const queue = tRef.unsafeGet(this.ref, runtime.journal) + if (queue === undefined) { + return core.interruptAs(runtime.fiberId) + } + if (queue.length === 0) { + return core.retry + } + return core.succeed(queue[0]) + }) + + peekOption: STM.STM> = core.withSTMRuntime((runtime) => { + const queue = tRef.unsafeGet(this.ref, runtime.journal) + if (queue === undefined) { + return core.interruptAs(runtime.fiberId) + } + return core.succeed(Option.fromNullable(queue[0])) + }) + + take: STM.STM = core.withSTMRuntime((runtime) => { + const queue = tRef.unsafeGet(this.ref, runtime.journal) + if (queue === undefined) { + return core.interruptAs(runtime.fiberId) + } + if (queue.length === 0) { + return core.retry + } + const dequeued = queue.shift()! + tRef.unsafeSet(this.ref, queue, runtime.journal) + return core.succeed(dequeued) + }) + + takeAll: STM.STM> = core.withSTMRuntime((runtime) => { + const queue = tRef.unsafeGet(this.ref, runtime.journal) + if (queue === undefined) { + return core.interruptAs(runtime.fiberId) + } + tRef.unsafeSet(this.ref, [], runtime.journal) + return core.succeed(queue) + }) + + takeUpTo(max: number): STM.STM> { + return core.withSTMRuntime((runtime) => { + const queue = tRef.unsafeGet(this.ref, runtime.journal) + if (queue === undefined) { + return core.interruptAs(runtime.fiberId) + } + const [toTake, remaining] = Chunk.splitAt(Chunk.unsafeFromArray(queue), max) + tRef.unsafeSet | undefined>(this.ref, Array.from(remaining), runtime.journal) + return core.succeed(Array.from(toTake)) + }) + } +} + +/** @internal */ +export const isTQueue = (u: unknown): u is TQueue.TQueue => { + return isTEnqueue(u) && isTDequeue(u) +} + +/** @internal */ +export const isTEnqueue = (u: unknown): u is TQueue.TEnqueue => hasProperty(u, TEnqueueTypeId) + +/** @internal */ +export const isTDequeue = (u: unknown): u is TQueue.TDequeue => hasProperty(u, TDequeueTypeId) + +/** @internal */ +export const awaitShutdown = (self: TQueue.TDequeue | TQueue.TEnqueue): STM.STM => self.awaitShutdown + +/** @internal */ +export const bounded = (requestedCapacity: number): STM.STM> => + makeQueue(requestedCapacity, BackPressure) + +/** @internal */ +export const capacity = (self: TQueue.TDequeue | TQueue.TEnqueue): number => { + return self.capacity() +} + +/** @internal */ +export const dropping = (requestedCapacity: number): STM.STM> => + makeQueue(requestedCapacity, Dropping) + +/** @internal */ +export const isEmpty = (self: TQueue.TDequeue | TQueue.TEnqueue): STM.STM => self.isEmpty + +/** @internal */ +export const isFull = (self: TQueue.TDequeue | TQueue.TEnqueue): STM.STM => self.isFull + +/** @internal */ +export const isShutdown = (self: TQueue.TDequeue | TQueue.TEnqueue): STM.STM => self.isShutdown + +/** @internal */ +export const offer = dual< + (value: A) => (self: TQueue.TEnqueue) => STM.STM, + (self: TQueue.TEnqueue, value: A) => STM.STM +>(2, (self, value) => self.offer(value)) + +/** @internal */ +export const offerAll = dual< + (iterable: Iterable) => (self: TQueue.TEnqueue) => STM.STM, + (self: TQueue.TEnqueue, iterable: Iterable) => STM.STM +>(2, (self, iterable) => self.offerAll(iterable)) + +/** @internal */ +export const peek = (self: TQueue.TDequeue): STM.STM => self.peek + +/** @internal */ +export const peekOption = (self: TQueue.TDequeue): STM.STM> => self.peekOption + +/** @internal */ +export const poll = (self: TQueue.TDequeue): STM.STM> => + pipe(self.takeUpTo(1), core.map(RA.head)) + +/** @internal */ +export const seek = dual< + (predicate: Predicate) => (self: TQueue.TDequeue) => STM.STM, + (self: TQueue.TDequeue, predicate: Predicate) => STM.STM +>(2, (self, predicate) => seekLoop(self, predicate)) + +const seekLoop = (self: TQueue.TDequeue, predicate: Predicate): STM.STM => + core.flatMap( + self.take, + (a) => predicate(a) ? core.succeed(a) : seekLoop(self, predicate) + ) + +/** @internal */ +export const shutdown = (self: TQueue.TDequeue | TQueue.TEnqueue): STM.STM => self.shutdown + +/** @internal */ +export const size = (self: TQueue.TDequeue | TQueue.TEnqueue): STM.STM => self.size + +/** @internal */ +export const sliding = (requestedCapacity: number): STM.STM> => + makeQueue(requestedCapacity, Sliding) + +/** @internal */ +export const take = (self: TQueue.TDequeue): STM.STM => self.take + +/** @internal */ +export const takeAll = (self: TQueue.TDequeue): STM.STM> => self.takeAll + +/** @internal */ +export const takeBetween = dual< + (min: number, max: number) => (self: TQueue.TDequeue) => STM.STM>, + (self: TQueue.TDequeue, min: number, max: number) => STM.STM> +>( + 3, + (self: TQueue.TDequeue, min: number, max: number): STM.STM> => + stm.suspend(() => { + const takeRemainder = ( + min: number, + max: number, + acc: Chunk.Chunk + ): STM.STM> => { + if (max < min) { + return core.succeed(acc) + } + return pipe( + self.takeUpTo(max), + core.flatMap((taken) => { + const remaining = min - taken.length + if (remaining === 1) { + return pipe( + self.take, + core.map((a) => pipe(acc, Chunk.appendAll(Chunk.unsafeFromArray(taken)), Chunk.append(a))) + ) + } + if (remaining > 1) { + return pipe( + self.take, + core.flatMap((a) => + takeRemainder( + remaining - 1, + max - taken.length - 1, + pipe(acc, Chunk.appendAll(Chunk.unsafeFromArray(taken)), Chunk.append(a)) + ) + ) + ) + } + return core.succeed(pipe(acc, Chunk.appendAll(Chunk.unsafeFromArray(taken)))) + }) + ) + } + return core.map(takeRemainder(min, max, Chunk.empty()), (c) => Array.from(c)) + }) +) + +/** @internal */ +export const takeN = dual< + (n: number) => (self: TQueue.TDequeue) => STM.STM>, + (self: TQueue.TDequeue, n: number) => STM.STM> +>(2, (self, n) => pipe(self, takeBetween(n, n))) + +/** @internal */ +export const takeUpTo = dual< + (max: number) => (self: TQueue.TDequeue) => STM.STM>, + (self: TQueue.TDequeue, max: number) => STM.STM> +>(2, (self, max) => self.takeUpTo(max)) + +/** @internal */ +export const unbounded = (): STM.STM> => makeQueue(Number.MAX_SAFE_INTEGER, Dropping) + +const makeQueue = (requestedCapacity: number, strategy: TQueueStrategy): STM.STM> => + core.map( + tRef.make | undefined>([]), + (ref) => new TQueueImpl(ref, requestedCapacity, strategy) + ) diff --git a/backend/node_modules/effect/src/internal/stm/tRandom.ts b/backend/node_modules/effect/src/internal/stm/tRandom.ts new file mode 100644 index 0000000000000000000000000000000000000000..6e5c4b42c0b4530197a4cee8b3b769816d371991 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tRandom.ts @@ -0,0 +1,140 @@ +import * as Context from "../../Context.js" +import { pipe } from "../../Function.js" +import * as Layer from "../../Layer.js" +import type * as STM from "../../STM.js" +import type * as TArray from "../../TArray.js" +import type * as TRandom from "../../TRandom.js" +import type * as TRef from "../../TRef.js" +import * as Random from "../../Utils.js" +import * as core from "./core.js" +import * as stm from "./stm.js" +import * as tArray from "./tArray.js" +import * as tRef from "./tRef.js" + +const TRandomSymbolKey = "effect/TRandom" + +/** @internal */ +export const TRandomTypeId: TRandom.TRandomTypeId = Symbol.for( + TRandomSymbolKey +) as TRandom.TRandomTypeId + +const randomInteger = (state: Random.PCGRandomState): [number, Random.PCGRandomState] => { + const prng = new Random.PCGRandom() + prng.setState(state) + return [prng.integer(0), prng.getState()] +} + +const randomIntegerBetween = (low: number, high: number) => { + return (state: Random.PCGRandomState): [number, Random.PCGRandomState] => { + const prng = new Random.PCGRandom() + prng.setState(state) + return [prng.integer(high - low) + low, prng.getState()] + } +} + +const randomNumber = (state: Random.PCGRandomState): [number, Random.PCGRandomState] => { + const prng = new Random.PCGRandom() + prng.setState(state) + return [prng.number(), prng.getState()] +} + +const withState = ( + state: TRef.TRef, + f: (state: Random.PCGRandomState) => [A, Random.PCGRandomState] +): STM.STM => { + return pipe(state, tRef.modify(f)) +} + +const shuffleWith = ( + iterable: Iterable, + nextIntBounded: (n: number) => STM.STM +): STM.STM> => { + const swap = (buffer: TArray.TArray, index1: number, index2: number): STM.STM => + pipe( + buffer, + tArray.get(index1), + core.flatMap((tmp) => + pipe( + buffer, + tArray.updateSTM(index1, () => pipe(buffer, tArray.get(index2))), + core.zipRight( + pipe( + buffer, + tArray.update(index2, () => tmp) + ) + ) + ) + ) + ) + return pipe( + tArray.fromIterable(iterable), + core.flatMap((buffer) => { + const array: Array = [] + for (let i = array.length; i >= 2; i = i - 1) { + array.push(i) + } + return pipe( + array, + stm.forEach((n) => pipe(nextIntBounded(n), core.flatMap((k) => swap(buffer, n - 1, k))), { discard: true }), + core.zipRight(tArray.toArray(buffer)) + ) + }) + ) +} + +/** @internal */ +export const Tag = Context.GenericTag("effect/TRandom") + +class TRandomImpl implements TRandom.TRandom { + readonly [TRandomTypeId]: TRandom.TRandomTypeId = TRandomTypeId + constructor(readonly state: TRef.TRef) { + this.next = withState(this.state, randomNumber) + this.nextBoolean = core.flatMap(this.next, (n) => core.succeed(n > 0.5)) + this.nextInt = withState(this.state, randomInteger) + } + + next: STM.STM + nextBoolean: STM.STM + nextInt: STM.STM + + nextRange(min: number, max: number): STM.STM { + return core.flatMap(this.next, (n) => core.succeed((max - min) * n + min)) + } + nextIntBetween(low: number, high: number): STM.STM { + return withState(this.state, randomIntegerBetween(low, high)) + } + shuffle(elements: Iterable): STM.STM> { + return shuffleWith(elements, (n) => this.nextIntBetween(0, n)) + } +} + +/** @internal */ +export const live: Layer.Layer = Layer.effect( + Tag, + pipe( + tRef.make(new Random.PCGRandom((Math.random() * 4294967296) >>> 0).getState()), + core.map((seed) => new TRandomImpl(seed)), + core.commit + ) +) + +/** @internal */ +export const next: STM.STM = core.flatMap(Tag, (random) => random.next) + +/** @internal */ +export const nextBoolean: STM.STM = core.flatMap(Tag, (random) => random.nextBoolean) + +/** @internal */ +export const nextInt: STM.STM = core.flatMap(Tag, (random) => random.nextInt) + +/** @internal */ +export const nextIntBetween = (low: number, high: number): STM.STM => + core.flatMap(Tag, (random) => random.nextIntBetween(low, high)) + +/** @internal */ +export const nextRange = (min: number, max: number): STM.STM => + core.flatMap(Tag, (random) => random.nextRange(min, max)) + +/** @internal */ +export const shuffle = (elements: Iterable): STM.STM, never, TRandom.TRandom> => + core.flatMap(Tag, (random) => random.shuffle(elements)) diff --git a/backend/node_modules/effect/src/internal/stm/tReentrantLock.ts b/backend/node_modules/effect/src/internal/stm/tReentrantLock.ts new file mode 100644 index 0000000000000000000000000000000000000000..2949f7fa7553439f02122d27909108e0d3fb7d6a --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tReentrantLock.ts @@ -0,0 +1,352 @@ +import * as Effect from "../../Effect.js" +import * as Equal from "../../Equal.js" +import * as FiberId from "../../FiberId.js" +import { dual } from "../../Function.js" +import * as HashMap from "../../HashMap.js" +import * as Option from "../../Option.js" +import type * as Scope from "../../Scope.js" +import type * as STM from "../../STM.js" +import type * as TReentrantLock from "../../TReentrantLock.js" +import type * as TRef from "../../TRef.js" +import * as core from "./core.js" +import * as tRef from "./tRef.js" + +const TReentrantLockSymbolKey = "effect/TReentrantLock" + +/** @internal */ +export const TReentrantLockTypeId: TReentrantLock.TReentrantLockTypeId = Symbol.for( + TReentrantLockSymbolKey +) as TReentrantLock.TReentrantLockTypeId + +const WriteLockTypeId = Symbol.for("effect/TReentrantLock/WriteLock") + +type WriteLockTypeId = typeof WriteLockTypeId + +const ReadLockTypeId = Symbol.for("effect/TReentrantLock/ReadLock") + +type ReadLockTypeId = typeof ReadLockTypeId + +class TReentranLockImpl implements TReentrantLock.TReentrantLock { + readonly [TReentrantLockTypeId]: TReentrantLock.TReentrantLockTypeId = TReentrantLockTypeId + constructor(readonly state: TRef.TRef) {} +} + +/** @internal */ +export interface LockState { + /** + * Computes the total number of read locks acquired. + */ + readonly readLocks: number + /** + * Computes the total number of write locks acquired. + */ + readonly writeLocks: number + /** + * Computes the number of read locks held by the specified fiber id. + */ + readLocksHeld(fiberId: FiberId.FiberId): number + /** + * Computes the number of write locks held by the specified fiber id. + */ + writeLocksHeld(fiberId: FiberId.FiberId): number +} + +/** + * This data structure describes the state of the lock when multiple fibers + * have acquired read locks. The state is tracked as a map from fiber identity + * to number of read locks acquired by the fiber. This level of detail permits + * upgrading a read lock to a write lock. + * + * @internal + */ +export class ReadLock implements LockState { + readonly [ReadLockTypeId]: ReadLockTypeId = ReadLockTypeId + constructor(readonly readers: HashMap.HashMap) {} + get readLocks(): number { + return Array.from(this.readers).reduce((acc, curr) => acc + curr[1], 0) + } + get writeLocks(): number { + return 0 + } + readLocksHeld(fiberId: FiberId.FiberId): number { + return Option.getOrElse( + HashMap.get(this.readers, fiberId), + () => 0 + ) + } + writeLocksHeld(_fiberId: FiberId.FiberId): number { + return 0 + } +} + +/** + * This data structure describes the state of the lock when a single fiber has + * a write lock. The fiber has an identity, and may also have acquired a + * certain number of read locks. + * + * @internal + */ +export class WriteLock implements LockState { + readonly [WriteLockTypeId]: WriteLockTypeId = WriteLockTypeId + constructor( + readonly readLocks: number, + readonly writeLocks: number, + readonly fiberId: FiberId.FiberId + ) {} + readLocksHeld(fiberId: FiberId.FiberId): number { + return Equal.equals(fiberId)(this.fiberId) ? this.readLocks : 0 + } + writeLocksHeld(fiberId: FiberId.FiberId): number { + return Equal.equals(fiberId)(this.fiberId) ? this.writeLocks : 0 + } +} + +const isReadLock = (lock: LockState): lock is ReadLock => { + return ReadLockTypeId in lock +} + +const isWriteLock = (lock: LockState): lock is WriteLock => { + return WriteLockTypeId in lock +} + +/** + * An empty read lock state, in which no fiber holds any read locks. + */ +const emptyReadLock = new ReadLock(HashMap.empty()) + +/** + * Creates a new read lock where the specified fiber holds the specified + * number of read locks. + */ +const makeReadLock = (fiberId: FiberId.FiberId, count: number): ReadLock => { + if (count <= 0) { + return emptyReadLock + } + return new ReadLock(HashMap.make([fiberId, count])) +} + +/** + * Determines if there is no other holder of read locks aside from the + * specified fiber id. If there are no other holders of read locks aside + * from the specified fiber id, then it is safe to upgrade the read lock + * into a write lock. + */ +const noOtherHolder = (readLock: ReadLock, fiberId: FiberId.FiberId): boolean => { + return HashMap.isEmpty(readLock.readers) || + (HashMap.size(readLock.readers) === 1 && HashMap.has(readLock.readers, fiberId)) +} + +/** + * Adjusts the number of read locks held by the specified fiber id. + */ +const adjustReadLock = (readLock: ReadLock, fiberId: FiberId.FiberId, adjustment: number): ReadLock => { + const total = readLock.readLocksHeld(fiberId) + const newTotal = total + adjustment + if (newTotal < 0) { + throw new Error( + "BUG - TReentrantLock.ReadLock.adjust - please report an issue at https://github.com/Effect-TS/effect/issues" + ) + } + if (newTotal === 0) { + return new ReadLock(HashMap.remove(readLock.readers, fiberId)) + } + return new ReadLock(HashMap.set(readLock.readers, fiberId, newTotal)) +} + +const adjustRead = (self: TReentrantLock.TReentrantLock, delta: number): STM.STM => + core.withSTMRuntime((runtime) => { + const lock = tRef.unsafeGet(self.state, runtime.journal) + if (isReadLock(lock)) { + const result = adjustReadLock(lock, runtime.fiberId, delta) + tRef.unsafeSet(self.state, result, runtime.journal) + return core.succeed(result.readLocksHeld(runtime.fiberId)) + } + if (isWriteLock(lock) && Equal.equals(runtime.fiberId)(lock.fiberId)) { + const newTotal = lock.readLocks + delta + if (newTotal < 0) { + throw new Error( + `Defect: Fiber ${ + FiberId.threadName(runtime.fiberId) + } releasing read locks it does not hold, newTotal: ${newTotal}` + ) + } + tRef.unsafeSet( + self.state, + new WriteLock(newTotal, lock.writeLocks, runtime.fiberId), + runtime.journal + ) + return core.succeed(newTotal) + } + return core.retry + }) + +/** @internal */ +export const acquireRead = (self: TReentrantLock.TReentrantLock): STM.STM => adjustRead(self, 1) + +/** @internal */ +export const acquireWrite = (self: TReentrantLock.TReentrantLock): STM.STM => + core.withSTMRuntime((runtime) => { + const lock = tRef.unsafeGet(self.state, runtime.journal) + if (isReadLock(lock) && noOtherHolder(lock, runtime.fiberId)) { + tRef.unsafeSet( + self.state, + new WriteLock(lock.readLocksHeld(runtime.fiberId), 1, runtime.fiberId), + runtime.journal + ) + return core.succeed(1) + } + if (isWriteLock(lock) && Equal.equals(runtime.fiberId)(lock.fiberId)) { + tRef.unsafeSet( + self.state, + new WriteLock(lock.readLocks, lock.writeLocks + 1, runtime.fiberId), + runtime.journal + ) + return core.succeed(lock.writeLocks + 1) + } + return core.retry + }) + +/** @internal */ +export const fiberReadLocks = (self: TReentrantLock.TReentrantLock): STM.STM => + core.effect((journal, fiberId) => + tRef.unsafeGet( + self.state, + journal + ).readLocksHeld(fiberId) + ) + +/** @internal */ +export const fiberWriteLocks = (self: TReentrantLock.TReentrantLock): STM.STM => + core.effect((journal, fiberId) => + tRef.unsafeGet( + self.state, + journal + ).writeLocksHeld(fiberId) + ) + +/** @internal */ +export const lock = (self: TReentrantLock.TReentrantLock): Effect.Effect => writeLock(self) + +/** @internal */ +export const locked = (self: TReentrantLock.TReentrantLock): STM.STM => + core.zipWith( + readLocked(self), + writeLocked(self), + (x, y) => x || y + ) + +/** @internal */ +export const make: STM.STM = core.map( + tRef.make(emptyReadLock), + (readLock) => new TReentranLockImpl(readLock) +) + +/** @internal */ +export const readLock = (self: TReentrantLock.TReentrantLock): Effect.Effect => + Effect.acquireRelease( + core.commit(acquireRead(self)), + () => core.commit(releaseRead(self)) + ) + +/** @internal */ +export const readLocks = (self: TReentrantLock.TReentrantLock): STM.STM => + core.map( + tRef.get(self.state), + (state) => state.readLocks + ) + +/** @internal */ +export const readLocked = (self: TReentrantLock.TReentrantLock): STM.STM => + core.map( + tRef.get(self.state), + (state) => state.readLocks > 0 + ) + +/** @internal */ +export const releaseRead = (self: TReentrantLock.TReentrantLock): STM.STM => adjustRead(self, -1) + +/** @internal */ +export const releaseWrite = (self: TReentrantLock.TReentrantLock): STM.STM => + core.withSTMRuntime((runtime) => { + const lock = tRef.unsafeGet(self.state, runtime.journal) + if (isWriteLock(lock) && lock.writeLocks === 1 && Equal.equals(runtime.fiberId)(lock.fiberId)) { + const result = makeReadLock(lock.fiberId, lock.readLocks) + tRef.unsafeSet(self.state, result, runtime.journal) + return core.succeed(result.writeLocksHeld(runtime.fiberId)) + } + if (isWriteLock(lock) && Equal.equals(runtime.fiberId)(lock.fiberId)) { + const result = new WriteLock(lock.readLocks, lock.writeLocks - 1, runtime.fiberId) + tRef.unsafeSet(self.state, result, runtime.journal) + return core.succeed(result.writeLocksHeld(runtime.fiberId)) + } + throw new Error( + `Defect: Fiber ${FiberId.threadName(runtime.fiberId)} releasing write lock it does not hold` + ) + }) + +/** @internal */ +export const withLock = dual< + ( + self: TReentrantLock.TReentrantLock + ) => (effect: Effect.Effect) => Effect.Effect, + ( + effect: Effect.Effect, + self: TReentrantLock.TReentrantLock + ) => Effect.Effect +>(2, (effect, self) => withWriteLock(effect, self)) + +/** @internal */ +export const withReadLock = dual< + (self: TReentrantLock.TReentrantLock) => ( + effect: Effect.Effect + ) => Effect.Effect, + ( + effect: Effect.Effect, + self: TReentrantLock.TReentrantLock + ) => Effect.Effect +>(2, (effect, self) => + Effect.uninterruptibleMask((restore) => + Effect.zipRight( + restore(core.commit(acquireRead(self))), + Effect.ensuring( + effect, + core.commit(releaseRead(self)) + ) + ) + )) + +/** @internal */ +export const withWriteLock = dual< + (self: TReentrantLock.TReentrantLock) => (effect: Effect.Effect) => Effect.Effect, + (effect: Effect.Effect, self: TReentrantLock.TReentrantLock) => Effect.Effect +>(2, (effect, self) => + Effect.uninterruptibleMask((restore) => + Effect.zipRight( + restore(core.commit(acquireWrite(self))), + Effect.ensuring( + effect, + core.commit(releaseWrite(self)) + ) + ) + )) + +/** @internal */ +export const writeLock = (self: TReentrantLock.TReentrantLock): Effect.Effect => + Effect.acquireRelease( + core.commit(acquireWrite(self)), + () => core.commit(releaseWrite(self)) + ) + +/** @internal */ +export const writeLocked = (self: TReentrantLock.TReentrantLock): STM.STM => + core.map( + tRef.get(self.state), + (state) => state.writeLocks > 0 + ) + +/** @internal */ +export const writeLocks = (self: TReentrantLock.TReentrantLock): STM.STM => + core.map( + tRef.get(self.state), + (state) => state.writeLocks + ) diff --git a/backend/node_modules/effect/src/internal/stm/tRef.ts b/backend/node_modules/effect/src/internal/stm/tRef.ts new file mode 100644 index 0000000000000000000000000000000000000000..cb4f7ebb2f25d38f731ab06be71435053decfd8b --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tRef.ts @@ -0,0 +1,195 @@ +import { dual } from "../../Function.js" +import * as Option from "../../Option.js" +import type { Pipeable } from "../../Pipeable.js" +import { pipeArguments } from "../../Pipeable.js" +import type * as STM from "../../STM.js" +import type * as TRef from "../../TRef.js" +import * as core from "./core.js" +import * as Entry from "./entry.js" +import type * as Journal from "./journal.js" +import type * as TxnId from "./txnId.js" +import * as Versioned from "./versioned.js" + +/** @internal */ +const TRefSymbolKey = "effect/TRef" + +/** @internal */ +export const TRefTypeId: TRef.TRefTypeId = Symbol.for( + TRefSymbolKey +) as TRef.TRefTypeId + +export const tRefVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +export class TRefImpl implements TRef.TRef, Pipeable { + readonly [TRefTypeId] = tRefVariance + /** @internal */ + todos: Map + /** @internal */ + versioned: Versioned.Versioned + constructor(value: A) { + this.versioned = new Versioned.Versioned(value) + this.todos = new Map() + } + modify(f: (a: A) => readonly [B, A]): STM.STM { + return core.effect((journal) => { + const entry = getOrMakeEntry(this, journal) + const [retValue, newValue] = f(Entry.unsafeGet(entry) as A) + Entry.unsafeSet(entry, newValue) + return retValue + }) + } + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const make = (value: A): STM.STM> => + core.effect>((journal) => { + const ref = new TRefImpl(value) + journal.set(ref, Entry.make(ref, true)) + return ref + }) + +/** @internal */ +export const get = (self: TRef.TRef) => self.modify((a) => [a, a]) + +/** @internal */ +export const set = dual< + (value: A) => (self: TRef.TRef) => STM.STM, + (self: TRef.TRef, value: A) => STM.STM +>( + 2, + (self: TRef.TRef, value: A): STM.STM => self.modify((): [void, A] => [void 0, value]) +) + +/** @internal */ +export const getAndSet = dual< + (value: A) => (self: TRef.TRef) => STM.STM, + (self: TRef.TRef, value: A) => STM.STM +>(2, (self, value) => self.modify((a) => [a, value])) + +/** @internal */ +export const getAndUpdate = dual< + (f: (a: A) => A) => (self: TRef.TRef) => STM.STM, + (self: TRef.TRef, f: (a: A) => A) => STM.STM +>(2, (self, f) => self.modify((a) => [a, f(a)])) + +/** @internal */ +export const getAndUpdateSome = dual< + (f: (a: A) => Option.Option) => (self: TRef.TRef) => STM.STM, + (self: TRef.TRef, f: (a: A) => Option.Option) => STM.STM +>(2, (self, f) => + self.modify((a) => + Option.match(f(a), { + onNone: () => [a, a], + onSome: (b) => [a, b] + }) + )) + +/** @internal */ +export const setAndGet = dual< + (value: A) => (self: TRef.TRef) => STM.STM, + (self: TRef.TRef, value: A) => STM.STM +>(2, (self, value) => self.modify(() => [value, value])) + +/** @internal */ +export const modify = dual< + (f: (a: A) => readonly [B, A]) => (self: TRef.TRef) => STM.STM, + (self: TRef.TRef, f: (a: A) => readonly [B, A]) => STM.STM +>(2, (self, f) => self.modify(f)) + +/** @internal */ +export const modifySome = dual< + (fallback: B, f: (a: A) => Option.Option) => (self: TRef.TRef) => STM.STM, + (self: TRef.TRef, fallback: B, f: (a: A) => Option.Option) => STM.STM +>(3, (self, fallback, f) => + self.modify((a) => + Option.match(f(a), { + onNone: () => [fallback, a], + onSome: (b) => b + }) + )) + +/** @internal */ +export const update = dual< + (f: (a: A) => A) => (self: TRef.TRef) => STM.STM, + (self: TRef.TRef, f: (a: A) => A) => STM.STM +>(2, (self, f) => self.modify((a) => [void 0, f(a)])) + +/** @internal */ +export const updateAndGet = dual< + (f: (a: A) => A) => (self: TRef.TRef) => STM.STM, + (self: TRef.TRef, f: (a: A) => A) => STM.STM +>(2, (self, f) => + self.modify((a) => { + const b = f(a) + return [b, b] + })) + +/** @internal */ +export const updateSome = dual< + (f: (a: A) => Option.Option) => (self: TRef.TRef) => STM.STM, + (self: TRef.TRef, f: (a: A) => Option.Option) => STM.STM +>( + 2, + (self, f) => + self.modify((a) => [ + void 0, + Option.match(f(a), { + onNone: () => a, + onSome: (b) => b + }) + ]) +) + +/** @internal */ +export const updateSomeAndGet = dual< + (f: (a: A) => Option.Option) => (self: TRef.TRef) => STM.STM, + (self: TRef.TRef, f: (a: A) => Option.Option) => STM.STM +>( + 2, + (self, f) => + self.modify((a) => + Option.match(f(a), { + onNone: () => [a, a], + onSome: (b) => [b, b] + }) + ) +) + +/** @internal */ +const getOrMakeEntry = (self: TRef.TRef, journal: Journal.Journal): Entry.Entry => { + if (journal.has(self)) { + return journal.get(self)! + } + const entry = Entry.make(self, false) + journal.set(self, entry) + return entry +} + +/** @internal */ +export const unsafeGet: { + (journal: Journal.Journal): (self: TRef.TRef) => A + (self: TRef.TRef, journal: Journal.Journal): A +} = dual< + (journal: Journal.Journal) => (self: TRef.TRef) => A, + (self: TRef.TRef, journal: Journal.Journal) => A +>(2, (self: TRef.TRef, journal: Journal.Journal) => Entry.unsafeGet(getOrMakeEntry(self, journal)) as A) + +/** @internal */ +export const unsafeSet: { + (value: A, journal: Journal.Journal): (self: TRef.TRef) => void + (self: TRef.TRef, value: A, journal: Journal.Journal): void +} = dual< + (value: A, journal: Journal.Journal) => (self: TRef.TRef) => void, + (self: TRef.TRef, value: A, journal: Journal.Journal) => void +>(3, (self, value, journal) => { + const entry = getOrMakeEntry(self, journal) + Entry.unsafeSet(entry, value) + return undefined +}) diff --git a/backend/node_modules/effect/src/internal/stm/tSemaphore.ts b/backend/node_modules/effect/src/internal/stm/tSemaphore.ts new file mode 100644 index 0000000000000000000000000000000000000000..b766c8f8f395eea7f9fc77fe314babf9600f5dfe --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tSemaphore.ts @@ -0,0 +1,113 @@ +import * as Cause from "../../Cause.js" +import * as Effect from "../../Effect.js" +import { dual } from "../../Function.js" +import type * as Scope from "../../Scope.js" +import * as STM from "../../STM.js" +import type * as TRef from "../../TRef.js" +import type * as TSemaphore from "../../TSemaphore.js" +import * as core from "./core.js" +import * as tRef from "./tRef.js" + +/** @internal */ +const TSemaphoreSymbolKey = "effect/TSemaphore" + +/** @internal */ +export const TSemaphoreTypeId: TSemaphore.TSemaphoreTypeId = Symbol.for( + TSemaphoreSymbolKey +) as TSemaphore.TSemaphoreTypeId + +/** @internal */ +class TSemaphoreImpl implements TSemaphore.TSemaphore { + readonly [TSemaphoreTypeId]: TSemaphore.TSemaphoreTypeId = TSemaphoreTypeId + constructor(readonly permits: TRef.TRef) {} +} + +/** @internal */ +export const make = (permits: number): STM.STM => + STM.map(tRef.make(permits), (permits) => new TSemaphoreImpl(permits)) + +/** @internal */ +export const acquire = (self: TSemaphore.TSemaphore): STM.STM => acquireN(self, 1) + +/** @internal */ +export const acquireN = dual< + (n: number) => (self: TSemaphore.TSemaphore) => STM.STM, + (self: TSemaphore.TSemaphore, n: number) => STM.STM +>(2, (self, n) => + core.withSTMRuntime((driver) => { + if (n < 0) { + throw new Cause.IllegalArgumentException(`Unexpected negative value ${n} passed to Semaphore.acquireN`) + } + const value = tRef.unsafeGet(self.permits, driver.journal) + if (value < n) { + return STM.retry + } else { + return STM.succeed(tRef.unsafeSet(self.permits, value - n, driver.journal)) + } + })) + +/** @internal */ +export const available = (self: TSemaphore.TSemaphore) => tRef.get(self.permits) + +/** @internal */ +export const release = (self: TSemaphore.TSemaphore): STM.STM => releaseN(self, 1) + +/** @internal */ +export const releaseN = dual< + (n: number) => (self: TSemaphore.TSemaphore) => STM.STM, + (self: TSemaphore.TSemaphore, n: number) => STM.STM +>(2, (self, n) => + core.withSTMRuntime((driver) => { + if (n < 0) { + throw new Cause.IllegalArgumentException(`Unexpected negative value ${n} passed to Semaphore.releaseN`) + } + const current = tRef.unsafeGet(self.permits, driver.journal) + return STM.succeed(tRef.unsafeSet(self.permits, current + n, driver.journal)) + })) + +/** @internal */ +export const withPermit = dual< + (semaphore: TSemaphore.TSemaphore) => (self: Effect.Effect) => Effect.Effect, + (self: Effect.Effect, semaphore: TSemaphore.TSemaphore) => Effect.Effect +>(2, (self, semaphore) => withPermits(self, semaphore, 1)) + +/** @internal */ +export const withPermits = dual< + ( + semaphore: TSemaphore.TSemaphore, + permits: number + ) => (self: Effect.Effect) => Effect.Effect, + ( + self: Effect.Effect, + semaphore: TSemaphore.TSemaphore, + permits: number + ) => Effect.Effect +>(3, (self, semaphore, permits) => + Effect.uninterruptibleMask((restore) => + Effect.zipRight( + restore(core.commit(acquireN(permits)(semaphore))), + Effect.ensuring( + self, + core.commit(releaseN(permits)(semaphore)) + ) + ) + )) + +/** @internal */ +export const withPermitScoped = (self: TSemaphore.TSemaphore): Effect.Effect => + withPermitsScoped(self, 1) + +/** @internal */ +export const withPermitsScoped = dual< + (permits: number) => (self: TSemaphore.TSemaphore) => Effect.Effect, + (self: TSemaphore.TSemaphore, permits: number) => Effect.Effect +>(2, (self, permits) => + Effect.acquireReleaseInterruptible( + core.commit(acquireN(self, permits)), + () => core.commit(releaseN(self, permits)) + )) + +/** @internal */ +export const unsafeMakeSemaphore = (permits: number): TSemaphore.TSemaphore => { + return new TSemaphoreImpl(new tRef.TRefImpl(permits)) +} diff --git a/backend/node_modules/effect/src/internal/stm/tSet.ts b/backend/node_modules/effect/src/internal/stm/tSet.ts new file mode 100644 index 0000000000000000000000000000000000000000..902ebbd74ea66cbd4ab3e89e5c6df6ea8e40c7b5 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tSet.ts @@ -0,0 +1,259 @@ +import * as RA from "../../Array.js" +import * as Chunk from "../../Chunk.js" +import { dual, pipe } from "../../Function.js" +import * as HashSet from "../../HashSet.js" +import type * as Option from "../../Option.js" +import { hasProperty, type Predicate } from "../../Predicate.js" +import * as STM from "../../STM.js" +import type * as TMap from "../../TMap.js" +import type * as TSet from "../../TSet.js" +import * as core from "./core.js" +import * as tMap from "./tMap.js" + +/** @internal */ +const TSetSymbolKey = "effect/TSet" + +/** @internal */ +export const TSetTypeId: TSet.TSetTypeId = Symbol.for( + TSetSymbolKey +) as TSet.TSetTypeId + +const tSetVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +class TSetImpl implements TSet.TSet { + readonly [TSetTypeId] = tSetVariance + constructor(readonly tMap: TMap.TMap) {} +} + +const isTSet = (u: unknown) => hasProperty(u, TSetTypeId) + +/** @internal */ +export const add = dual< + (value: A) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, value: A) => STM.STM +>(2, (self, value) => tMap.set(self.tMap, value, void 0 as void)) + +/** @internal */ +export const difference = dual< + (other: TSet.TSet) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, other: TSet.TSet) => STM.STM +>(2, (self, other) => + core.flatMap( + toHashSet(other), + (values) => removeIf(self, (value) => HashSet.has(values, value), { discard: true }) + )) + +/** @internal */ +export const empty = (): STM.STM> => fromIterable([]) + +/** @internal */ +export const forEach = dual< + (f: (value: A) => STM.STM) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, f: (value: A) => STM.STM) => STM.STM +>(2, (self, f) => reduceSTM(self, void 0 as void, (_, value) => f(value))) + +/** @internal */ +export const fromIterable = (iterable: Iterable): STM.STM> => + core.map( + tMap.fromIterable(Array.from(iterable).map((a): [A, void] => [a, void 0])), + (tMap) => new TSetImpl(tMap) + ) + +/** @internal */ +export const has = dual< + (value: A) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, value: A) => STM.STM +>(2, (self, value) => tMap.has(self.tMap, value)) + +/** @internal */ +export const intersection = dual< + (other: TSet.TSet) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, other: TSet.TSet) => STM.STM +>(2, (self, other) => + core.flatMap( + toHashSet(other), + (values) => pipe(self, retainIf((value) => pipe(values, HashSet.has(value)), { discard: true })) + )) + +/** @internal */ +export const isEmpty = (self: TSet.TSet): STM.STM => tMap.isEmpty(self.tMap) + +/** @internal */ +export const make = >( + ...elements: Elements +): STM.STM> => fromIterable(elements) + +/** @internal */ +export const reduce = dual< + (zero: Z, f: (accumulator: Z, value: A) => Z) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, zero: Z, f: (accumulator: Z, value: A) => Z) => STM.STM +>(3, (self, zero, f) => + tMap.reduce( + self.tMap, + zero, + (acc, _, key) => f(acc, key) + )) + +/** @internal */ +export const reduceSTM = dual< + (zero: Z, f: (accumulator: Z, value: A) => STM.STM) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, zero: Z, f: (accumulator: Z, value: A) => STM.STM) => STM.STM +>(3, (self, zero, f) => + tMap.reduceSTM( + self.tMap, + zero, + (acc, _, key) => f(acc, key) + )) + +/** @internal */ +export const remove = dual< + (value: A) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, value: A) => STM.STM +>(2, (self, value) => tMap.remove(self.tMap, value)) + +/** @internal */ +export const removeAll = dual< + (iterable: Iterable) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, iterable: Iterable) => STM.STM +>(2, (self, iterable) => tMap.removeAll(self.tMap, iterable)) + +/** @internal */ +export const removeIf: { + (predicate: Predicate, options: { + readonly discard: true + }): (self: TSet.TSet) => STM.STM + ( + predicate: Predicate, + options?: { + readonly discard: false + } + ): (self: TSet.TSet) => STM.STM> + (self: TSet.TSet, predicate: Predicate, options: { + readonly discard: true + }): STM.STM + ( + self: TSet.TSet, + predicate: Predicate, + options?: { + readonly discard: false + } + ): STM.STM> +} = dual( + (args) => isTSet(args[0]), + (self, predicate, options) => + options?.discard === true ? tMap.removeIf(self.tMap, (key) => predicate(key), { discard: true }) : pipe( + tMap.removeIf(self.tMap, (key) => predicate(key)), + core.map(RA.map((entry) => entry[0])) + ) +) + +/** @internal */ +export const retainIf: { + (predicate: Predicate, options: { + readonly discard: true + }): (self: TSet.TSet) => STM.STM + ( + predicate: Predicate, + options?: { + readonly discard: false + } + ): (self: TSet.TSet) => STM.STM> + (self: TSet.TSet, predicate: Predicate, options: { + readonly discard: true + }): STM.STM + ( + self: TSet.TSet, + predicate: Predicate, + options?: { + readonly discard: false + } + ): STM.STM> +} = dual((args) => isTSet(args[0]), (self, predicate, options) => + options?.discard === true ? + tMap.retainIf(self.tMap, (key) => predicate(key), { discard: true }) : + pipe( + tMap.retainIf(self.tMap, (key) => predicate(key)), + core.map(RA.map((entry) => entry[0])) + )) + +/** @internal */ +export const size = (self: TSet.TSet): STM.STM => core.map(toChunk(self), (chunk) => chunk.length) + +/** @internal */ +export const takeFirst = dual< + (pf: (a: A) => Option.Option) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, pf: (a: A) => Option.Option) => STM.STM +>(2, (self, pf) => tMap.takeFirst(self.tMap, (key) => pf(key))) + +/** @internal */ +export const takeFirstSTM = dual< + (pf: (a: A) => STM.STM, R>) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, pf: (a: A) => STM.STM, R>) => STM.STM +>(2, (self, pf) => tMap.takeFirstSTM(self.tMap, (key) => pf(key))) + +/** @internal */ +export const takeSome = dual< + (pf: (a: A) => Option.Option) => (self: TSet.TSet) => STM.STM>, + (self: TSet.TSet, pf: (a: A) => Option.Option) => STM.STM> +>(2, (self, pf) => tMap.takeSome(self.tMap, (key) => pf(key))) + +/** @internal */ +export const takeSomeSTM = dual< + ( + pf: (a: A) => STM.STM, R> + ) => (self: TSet.TSet) => STM.STM, E, R>, + ( + self: TSet.TSet, + pf: (a: A) => STM.STM, R> + ) => STM.STM, E, R> +>(2, (self, pf) => tMap.takeSomeSTM(self.tMap, (key) => pf(key))) + +/** @internal */ +export const toChunk = (self: TSet.TSet): STM.STM> => + tMap.keys(self.tMap).pipe(STM.map(Chunk.unsafeFromArray)) + +/** @internal */ +export const toHashSet = (self: TSet.TSet): STM.STM> => + reduce( + self, + HashSet.empty(), + (acc, value) => pipe(acc, HashSet.add(value)) + ) + +/** @internal */ +export const toArray = (self: TSet.TSet): STM.STM> => + reduce, A>( + self, + [], + (acc, value) => [...acc, value] + ) + +/** @internal */ +export const toReadonlySet = (self: TSet.TSet): STM.STM> => + core.map(toArray(self), (values) => new Set(values)) + +/** @internal */ +export const transform = dual< + (f: (a: A) => A) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, f: (a: A) => A) => STM.STM +>(2, (self, f) => tMap.transform(self.tMap, (key, value) => [f(key), value])) + +/** @internal */ +export const transformSTM = dual< + (f: (a: A) => STM.STM) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, f: (a: A) => STM.STM) => STM.STM +>(2, (self, f) => + tMap.transformSTM( + self.tMap, + (key, value) => core.map(f(key), (a) => [a, value]) + )) + +/** @internal */ +export const union = dual< + (other: TSet.TSet) => (self: TSet.TSet) => STM.STM, + (self: TSet.TSet, other: TSet.TSet) => STM.STM +>(2, (self, other) => forEach(other, (value) => add(self, value))) diff --git a/backend/node_modules/effect/src/internal/stm/tSubscriptionRef.ts b/backend/node_modules/effect/src/internal/stm/tSubscriptionRef.ts new file mode 100644 index 0000000000000000000000000000000000000000..94a492405534fa441d9fdf28191529c7d729a2e0 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tSubscriptionRef.ts @@ -0,0 +1,286 @@ +import * as Effect from "../../Effect.js" +import { dual, pipe } from "../../Function.js" +import * as Option from "../../Option.js" +import { pipeArguments } from "../../Pipeable.js" +import * as STM from "../../STM.js" +import * as TPubSub from "../../TPubSub.js" +import * as TQueue from "../../TQueue.js" +import * as TRef from "../../TRef.js" +import type * as TSubscriptionRef from "../../TSubscriptionRef.js" +import * as stream from "../stream.js" +import { tDequeueVariance } from "./tQueue.js" +import { tRefVariance } from "./tRef.js" + +/** @internal */ +const TSubscriptionRefSymbolKey = "effect/TSubscriptionRef" + +/** @internal */ +export const TSubscriptionRefTypeId: TSubscriptionRef.TSubscriptionRefTypeId = Symbol.for( + TSubscriptionRefSymbolKey +) as TSubscriptionRef.TSubscriptionRefTypeId + +const TSubscriptionRefVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +class TDequeueMerge implements TQueue.TDequeue { + [TQueue.TDequeueTypeId] = tDequeueVariance + + constructor( + readonly first: TQueue.TDequeue, + readonly second: TQueue.TDequeue + ) {} + + peek: STM.STM = STM.gen(this, function*() { + const first = yield* this.peekOption + if (first._tag === "Some") { + return first.value + } + return yield* STM.retry + }) + + peekOption: STM.STM> = STM.gen(this, function*() { + const first = yield* this.first.peekOption + if (first._tag === "Some") { + return first + } + const second = yield* this.second.peekOption + if (second._tag === "Some") { + return second + } + return Option.none() + }) + + take: STM.STM = STM.gen(this, function*() { + if (!(yield* this.first.isEmpty)) { + return yield* this.first.take + } + if (!(yield* this.second.isEmpty)) { + return yield* this.second.take + } + return yield* STM.retry + }) + + takeAll: STM.STM> = STM.gen(this, function*() { + return [...yield* this.first.takeAll, ...yield* this.second.takeAll] + }) + + takeUpTo(max: number): STM.STM> { + return STM.gen(this, function*() { + const first = yield* this.first.takeUpTo(max) + if (first.length >= max) { + return first + } + return [...first, ...yield* this.second.takeUpTo(max - first.length)] + }) + } + + capacity(): number { + return this.first.capacity() + this.second.capacity() + } + + size: STM.STM = STM.gen(this, function*() { + return (yield* this.first.size) + (yield* this.second.size) + }) + + isFull: STM.STM = STM.gen(this, function*() { + return (yield* this.first.isFull) && (yield* this.second.isFull) + }) + + isEmpty: STM.STM = STM.gen(this, function*() { + return (yield* this.first.isEmpty) && (yield* this.second.isEmpty) + }) + + shutdown: STM.STM = STM.gen(this, function*() { + yield* this.first.shutdown + yield* this.second.shutdown + }) + + isShutdown: STM.STM = STM.gen(this, function*() { + return (yield* this.first.isShutdown) && (yield* this.second.isShutdown) + }) + + awaitShutdown: STM.STM = STM.gen(this, function*() { + yield* this.first.awaitShutdown + yield* this.second.awaitShutdown + }) +} + +/** @internal */ +class TSubscriptionRefImpl implements TSubscriptionRef.TSubscriptionRef { + readonly [TSubscriptionRefTypeId] = TSubscriptionRefVariance + readonly [TRef.TRefTypeId] = tRefVariance + + constructor( + readonly ref: TRef.TRef, + readonly pubsub: TPubSub.TPubSub + ) {} + + get todos() { + return this.ref.todos + } + + get versioned() { + return this.ref.versioned + } + + pipe() { + return pipeArguments(this, arguments) + } + + get changes(): STM.STM> { + return STM.gen(this, function*() { + const first = yield* TQueue.unbounded() + yield* TQueue.offer(first, yield* TRef.get(this.ref)) + return new TDequeueMerge(first, yield* TPubSub.subscribe(this.pubsub)) + }) + } + + modify(f: (a: A) => readonly [B, A]): STM.STM { + return pipe( + TRef.get(this.ref), + STM.map(f), + STM.flatMap(([b, a]) => + pipe( + TRef.set(this.ref, a), + STM.as(b), + STM.zipLeft(TPubSub.publish(this.pubsub, a)) + ) + ) + ) + } +} + +/** @internal */ +export const make = (value: A): STM.STM> => + pipe( + STM.all([ + TPubSub.unbounded(), + TRef.make(value) + ]), + STM.map(([pubsub, ref]) => new TSubscriptionRefImpl(ref, pubsub)) + ) + +/** @internal */ +export const get = (self: TSubscriptionRef.TSubscriptionRef) => TRef.get(self.ref) + +/** @internal */ +export const set = dual< + (value: A) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, value: A) => STM.STM +>( + 2, + (self: TSubscriptionRef.TSubscriptionRef, value: A): STM.STM => + self.modify((): [void, A] => [void 0, value]) +) + +/** @internal */ +export const getAndSet = dual< + (value: A) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, value: A) => STM.STM +>(2, (self, value) => self.modify((a) => [a, value])) + +/** @internal */ +export const getAndUpdate = dual< + (f: (a: A) => A) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => A) => STM.STM +>(2, (self, f) => self.modify((a) => [a, f(a)])) + +/** @internal */ +export const getAndUpdateSome = dual< + (f: (a: A) => Option.Option) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => Option.Option) => STM.STM +>(2, (self, f) => + self.modify((a) => + Option.match(f(a), { + onNone: () => [a, a], + onSome: (b) => [a, b] + }) + )) + +/** @internal */ +export const setAndGet = dual< + (value: A) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, value: A) => STM.STM +>(2, (self, value) => self.modify(() => [value, value])) + +/** @internal */ +export const modify = dual< + (f: (a: A) => readonly [B, A]) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => readonly [B, A]) => STM.STM +>(2, (self, f) => self.modify(f)) + +/** @internal */ +export const modifySome = dual< + ( + fallback: B, + f: (a: A) => Option.Option + ) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + ( + self: TSubscriptionRef.TSubscriptionRef, + fallback: B, + f: (a: A) => Option.Option + ) => STM.STM +>(3, (self, fallback, f) => + self.modify((a) => + Option.match(f(a), { + onNone: () => [fallback, a], + onSome: (b) => b + }) + )) + +/** @internal */ +export const update = dual< + (f: (a: A) => A) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => A) => STM.STM +>(2, (self, f) => self.modify((a) => [void 0, f(a)])) + +/** @internal */ +export const updateAndGet = dual< + (f: (a: A) => A) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => A) => STM.STM +>(2, (self, f) => + self.modify((a) => { + const b = f(a) + return [b, b] + })) + +/** @internal */ +export const updateSome = dual< + (f: (a: A) => Option.Option) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => Option.Option) => STM.STM +>( + 2, + (self, f) => + self.modify((a) => [ + void 0, + Option.match(f(a), { + onNone: () => a, + onSome: (b) => b + }) + ]) +) + +/** @internal */ +export const updateSomeAndGet = dual< + (f: (a: A) => Option.Option) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => Option.Option) => STM.STM +>( + 2, + (self, f) => + self.modify((a) => + Option.match(f(a), { + onNone: () => [a, a], + onSome: (b) => [b, b] + }) + ) +) + +/** @internal */ +export const changesScoped = (self: TSubscriptionRef.TSubscriptionRef) => + Effect.acquireRelease(self.changes, TQueue.shutdown) + +/** @internal */ +export const changesStream = (self: TSubscriptionRef.TSubscriptionRef) => + stream.unwrap(Effect.map(self.changes, stream.fromTQueue)) diff --git a/backend/node_modules/effect/src/internal/stm/tryCommit.ts b/backend/node_modules/effect/src/internal/stm/tryCommit.ts new file mode 100644 index 0000000000000000000000000000000000000000..d6203233009fdd35d05e4fd5e35a0c99c6a636ee --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/tryCommit.ts @@ -0,0 +1,34 @@ +import type * as Exit from "../../Exit.js" +import type * as Journal from "./journal.js" +import * as OpCodes from "./opCodes/tryCommit.js" + +/** @internal */ +export type TryCommit = Done | Suspend + +/** @internal */ +export interface Done { + readonly _tag: OpCodes.OP_DONE + readonly exit: Exit.Exit +} + +/** @internal */ +export interface Suspend { + readonly _tag: OpCodes.OP_SUSPEND + readonly journal: Journal.Journal +} + +/** @internal */ +export const done = (exit: Exit.Exit): TryCommit => { + return { + _tag: OpCodes.OP_DONE, + exit + } +} + +/** @internal */ +export const suspend = (journal: Journal.Journal): TryCommit => { + return { + _tag: OpCodes.OP_SUSPEND, + journal + } +} diff --git a/backend/node_modules/effect/src/internal/stm/txnId.ts b/backend/node_modules/effect/src/internal/stm/txnId.ts new file mode 100644 index 0000000000000000000000000000000000000000..895bd89cd64ccfbf8d6b767d69bf7293c6f92db3 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/txnId.ts @@ -0,0 +1,14 @@ +/** @internal */ +export type TxnId = number & { + readonly TransactioId: unique symbol +} + +/** @internal */ +const txnCounter = { ref: 0 } + +/** @internal */ +export const make = (): TxnId => { + const newId = txnCounter.ref + 1 + txnCounter.ref = newId + return newId as TxnId +} diff --git a/backend/node_modules/effect/src/internal/stm/versioned.ts b/backend/node_modules/effect/src/internal/stm/versioned.ts new file mode 100644 index 0000000000000000000000000000000000000000..4b1a6d48f11c2b79dc207474d34d3757136a9681 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stm/versioned.ts @@ -0,0 +1,4 @@ +/** @internal */ +export class Versioned { + constructor(readonly value: A) {} +} diff --git a/backend/node_modules/effect/src/internal/stream.ts b/backend/node_modules/effect/src/internal/stream.ts new file mode 100644 index 0000000000000000000000000000000000000000..8c2d46d3d8b30ad08489ae2374fb184cfeedb50e --- /dev/null +++ b/backend/node_modules/effect/src/internal/stream.ts @@ -0,0 +1,8802 @@ +import * as Cause from "../Cause.js" +import type * as Channel from "../Channel.js" +import * as Chunk from "../Chunk.js" +import * as Clock from "../Clock.js" +import * as Context from "../Context.js" +import * as Deferred from "../Deferred.js" +import * as Duration from "../Duration.js" +import * as Effect from "../Effect.js" +import * as Either from "../Either.js" +import * as Equal from "../Equal.js" +import type { ExecutionPlan } from "../ExecutionPlan.js" +import * as Exit from "../Exit.js" +import * as Fiber from "../Fiber.js" +import * as FiberRef from "../FiberRef.js" +import type { LazyArg } from "../Function.js" +import { constTrue, dual, identity, pipe } from "../Function.js" +import * as internalExecutionPlan from "../internal/executionPlan.js" +import * as Layer from "../Layer.js" +import * as MergeDecision from "../MergeDecision.js" +import * as Option from "../Option.js" +import type * as Order from "../Order.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty, type Predicate, type Refinement } from "../Predicate.js" +import * as PubSub from "../PubSub.js" +import * as Queue from "../Queue.js" +import * as RcRef from "../RcRef.js" +import * as Ref from "../Ref.js" +import * as Runtime from "../Runtime.js" +import * as Schedule from "../Schedule.js" +import type * as Scope from "../Scope.js" +import type * as Sink from "../Sink.js" +import type * as Stream from "../Stream.js" +import type * as Emit from "../StreamEmit.js" +import * as HaltStrategy from "../StreamHaltStrategy.js" +import type * as Take from "../Take.js" +import * as TPubSub from "../TPubSub.js" +import * as TQueue from "../TQueue.js" +import type * as Tracer from "../Tracer.js" +import * as Tuple from "../Tuple.js" +import type * as Types from "../Types.js" +import * as channel from "./channel.js" +import * as channelExecutor from "./channel/channelExecutor.js" +import * as MergeStrategy from "./channel/mergeStrategy.js" +import * as core from "./core-stream.js" +import * as doNotation from "./doNotation.js" +import { RingBuffer } from "./ringBuffer.js" +import * as InternalSchedule from "./schedule.js" +import * as sink_ from "./sink.js" +import * as DebounceState from "./stream/debounceState.js" +import * as emit from "./stream/emit.js" +import * as haltStrategy from "./stream/haltStrategy.js" +import * as Handoff from "./stream/handoff.js" +import * as HandoffSignal from "./stream/handoffSignal.js" +import * as pull from "./stream/pull.js" +import * as SinkEndReason from "./stream/sinkEndReason.js" +import * as ZipAllState from "./stream/zipAllState.js" +import * as ZipChunksState from "./stream/zipChunksState.js" +import * as InternalTake from "./take.js" +import * as InternalTracer from "./tracer.js" + +/** @internal */ +const StreamSymbolKey = "effect/Stream" + +/** @internal */ +export const StreamTypeId: Stream.StreamTypeId = Symbol.for( + StreamSymbolKey +) as Stream.StreamTypeId + +/** @internal */ +const streamVariance = { + _R: (_: never) => _, + _E: (_: never) => _, + _A: (_: never) => _ +} + +/** @internal */ +export class StreamImpl implements Stream.Stream { + readonly [StreamTypeId] = streamVariance + constructor( + readonly channel: Channel.Channel, unknown, E, unknown, unknown, unknown, R> + ) { + } + + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const isStream = (u: unknown): u is Stream.Stream => + hasProperty(u, StreamTypeId) || Effect.isEffect(u) + +/** @internal */ +export const DefaultChunkSize = 4096 + +/** @internal */ +export const accumulate = (self: Stream.Stream): Stream.Stream, E, R> => + chunks(accumulateChunks(self)) + +/** @internal */ +export const accumulateChunks = (self: Stream.Stream): Stream.Stream => { + const accumulator = ( + s: Chunk.Chunk + ): Channel.Channel, Chunk.Chunk, E, E, void, unknown> => + core.readWith({ + onInput: (input: Chunk.Chunk) => { + const next = Chunk.appendAll(s, input) + return core.flatMap( + core.write(next), + () => accumulator(next) + ) + }, + onFailure: core.fail, + onDone: () => core.void + }) + return new StreamImpl(core.pipeTo(toChannel(self), accumulator(Chunk.empty()))) +} + +/** @internal */ +export const acquireRelease = ( + acquire: Effect.Effect, + release: (resource: A, exit: Exit.Exit) => Effect.Effect +): Stream.Stream => scoped(Effect.acquireRelease(acquire, release)) + +/** @internal */ +export const aggregate = dual< + ( + sink: Sink.Sink + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + sink: Sink.Sink + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + sink: Sink.Sink + ): Stream.Stream => aggregateWithin(self, sink, Schedule.forever) +) + +/** @internal */ +export const aggregateWithin = dual< + ( + sink: Sink.Sink, + schedule: Schedule.Schedule, R3> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + sink: Sink.Sink, + schedule: Schedule.Schedule, R3> + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + sink: Sink.Sink, + schedule: Schedule.Schedule, R3> + ): Stream.Stream => + filterMap( + aggregateWithinEither(self, sink, schedule), + (_) => + Either.match(_, { + onLeft: Option.none, + onRight: Option.some + }) + ) +) + +/** @internal */ +export const aggregateWithinEither = dual< + ( + sink: Sink.Sink, + schedule: Schedule.Schedule, R3> + ) => (self: Stream.Stream) => Stream.Stream, E2 | E, R2 | R3 | R>, + ( + self: Stream.Stream, + sink: Sink.Sink, + schedule: Schedule.Schedule, R3> + ) => Stream.Stream, E2 | E, R2 | R3 | R> +>( + 3, + ( + self: Stream.Stream, + sink: Sink.Sink, + schedule: Schedule.Schedule, R3> + ): Stream.Stream, E2 | E, R2 | R3 | R> => { + const layer = Effect.all([ + Handoff.make>(), + Ref.make(SinkEndReason.ScheduleEnd), + Ref.make(Chunk.empty()), + Schedule.driver(schedule), + Ref.make(false), + Ref.make(false) + ]) + return fromEffect(layer).pipe( + flatMap(([handoff, sinkEndReason, sinkLeftovers, scheduleDriver, consumed, endAfterEmit]) => { + const handoffProducer: Channel.Channel, never, E | E2, unknown, unknown> = core + .readWithCause({ + onInput: (input: Chunk.Chunk) => + core.flatMap( + core.fromEffect(pipe( + handoff, + Handoff.offer>(HandoffSignal.emit(input)), + Effect.when(() => Chunk.isNonEmpty(input)) + )), + () => handoffProducer + ), + onFailure: (cause) => + core.fromEffect( + Handoff.offer>( + handoff, + HandoffSignal.halt(cause) + ) + ), + onDone: () => + core.fromEffect( + Handoff.offer>( + handoff, + HandoffSignal.end(SinkEndReason.UpstreamEnd) + ) + ) + }) + const handoffConsumer: Channel.Channel, unknown, E | E2, unknown, void, unknown> = pipe( + Ref.getAndSet(sinkLeftovers, Chunk.empty()), + Effect.flatMap((leftovers) => { + if (Chunk.isNonEmpty(leftovers)) { + return pipe( + Ref.set(consumed, true), + Effect.zipRight(Effect.succeed(pipe( + core.write(leftovers), + core.flatMap(() => handoffConsumer) + ))) + ) + } + return pipe( + Handoff.take(handoff), + Effect.map((signal) => { + switch (signal._tag) { + case HandoffSignal.OP_EMIT: { + return pipe( + core.fromEffect(Ref.set(consumed, true)), + channel.zipRight(core.write(signal.elements)), + channel.zipRight(core.fromEffect(Ref.get(endAfterEmit))), + core.flatMap((bool) => bool ? core.void : handoffConsumer) + ) + } + case HandoffSignal.OP_HALT: { + return core.failCause(signal.cause) + } + case HandoffSignal.OP_END: { + if (signal.reason._tag === SinkEndReason.OP_SCHEDULE_END) { + return pipe( + Ref.get(consumed), + Effect.map((bool) => + bool ? + core.fromEffect( + pipe( + Ref.set(sinkEndReason, SinkEndReason.ScheduleEnd), + Effect.zipRight(Ref.set(endAfterEmit, true)) + ) + ) : + pipe( + core.fromEffect( + pipe( + Ref.set(sinkEndReason, SinkEndReason.ScheduleEnd), + Effect.zipRight(Ref.set(endAfterEmit, true)) + ) + ), + core.flatMap(() => handoffConsumer) + ) + ), + channel.unwrap + ) + } + return pipe( + Ref.set(sinkEndReason, signal.reason), + Effect.zipRight(Ref.set(endAfterEmit, true)), + core.fromEffect + ) + } + } + }) + ) + }), + channel.unwrap + ) + const timeout = (lastB: Option.Option): Effect.Effect, R2 | R3> => + scheduleDriver.next(lastB) + const scheduledAggregator = ( + sinkFiber: Fiber.RuntimeFiber>, B], E | E2>, + scheduleFiber: Fiber.RuntimeFiber>, + scope: Scope.Scope + ): Channel.Channel>, unknown, E | E2, unknown, unknown, unknown, R2 | R3> => { + const forkSink = pipe( + Ref.set(consumed, false), + Effect.zipRight(Ref.set(endAfterEmit, false)), + Effect.zipRight( + pipe( + handoffConsumer, + channel.pipeToOrFail(sink_.toChannel(sink)), + core.collectElements, + channel.run, + Effect.forkIn(scope) + ) + ) + ) + const handleSide = ( + leftovers: Chunk.Chunk>, + b: B, + c: Option.Option + ): Channel.Channel>, unknown, E | E2, unknown, unknown, unknown, R2 | R3> => + pipe( + Ref.set(sinkLeftovers, Chunk.flatten(leftovers)), + Effect.zipRight( + Effect.map(Ref.get(sinkEndReason), (reason) => { + switch (reason._tag) { + case SinkEndReason.OP_SCHEDULE_END: { + return pipe( + Effect.all([ + Ref.get(consumed), + forkSink, + pipe(timeout(Option.some(b)), Effect.forkIn(scope)) + ]), + Effect.map(([wasConsumed, sinkFiber, scheduleFiber]) => { + const toWrite = pipe( + c, + Option.match({ + onNone: (): Chunk.Chunk> => Chunk.of(Either.right(b)), + onSome: (c): Chunk.Chunk> => + Chunk.make(Either.right(b), Either.left(c)) + }) + ) + if (wasConsumed) { + return pipe( + core.write(toWrite), + core.flatMap(() => scheduledAggregator(sinkFiber, scheduleFiber, scope)) + ) + } + return scheduledAggregator(sinkFiber, scheduleFiber, scope) + }), + channel.unwrap + ) + } + case SinkEndReason.OP_UPSTREAM_END: { + return pipe( + Ref.get(consumed), + Effect.map((wasConsumed) => + wasConsumed ? + core.write(Chunk.of>(Either.right(b))) : + core.void + ), + channel.unwrap + ) + } + } + }) + ), + channel.unwrap + ) + return channel.unwrap( + Effect.raceWith(Fiber.join(sinkFiber), Fiber.join(scheduleFiber), { + onSelfDone: (sinkExit, _) => + pipe( + Fiber.interrupt(scheduleFiber), + Effect.zipRight(pipe( + Effect.suspend(() => sinkExit), + Effect.map(([leftovers, b]) => handleSide(leftovers, b, Option.none())) + )) + ), + onOtherDone: (scheduleExit, _) => + Effect.matchCauseEffect(Effect.suspend(() => scheduleExit), { + onFailure: (cause) => + Either.match( + Cause.failureOrCause(cause), + { + onLeft: () => + pipe( + handoff, + Handoff.offer>( + HandoffSignal.end(SinkEndReason.ScheduleEnd) + ), + Effect.forkDaemon, + Effect.zipRight( + pipe( + Fiber.join(sinkFiber), + Effect.map(([leftovers, b]) => handleSide(leftovers, b, Option.none())) + ) + ) + ), + onRight: (cause) => + pipe( + handoff, + Handoff.offer>( + HandoffSignal.halt(cause) + ), + Effect.forkDaemon, + Effect.zipRight( + pipe( + Fiber.join(sinkFiber), + Effect.map(([leftovers, b]) => handleSide(leftovers, b, Option.none())) + ) + ) + ) + } + ), + onSuccess: (c) => + pipe( + handoff, + Handoff.offer>( + HandoffSignal.end(SinkEndReason.ScheduleEnd) + ), + Effect.forkDaemon, + Effect.zipRight( + pipe( + Fiber.join(sinkFiber), + Effect.map(([leftovers, b]) => handleSide(leftovers, b, Option.some(c))) + ) + ) + ) + }) + }) + ) + } + return unwrapScopedWith((scope) => + core.pipeTo(toChannel(self), handoffProducer).pipe( + channel.run, + Effect.forkIn(scope), + Effect.zipRight( + channel.pipeToOrFail(handoffConsumer, sink_.toChannel(sink)).pipe( + core.collectElements, + channel.run, + Effect.forkIn(scope), + Effect.flatMap((sinkFiber) => + timeout(Option.none()).pipe( + Effect.forkIn(scope), + Effect.map((scheduleFiber) => + new StreamImpl( + scheduledAggregator(sinkFiber, scheduleFiber, scope) + ) + ) + ) + ) + ) + ) + ) + ) + }) + ) + } +) + +/** @internal */ +export const as = dual< + (value: B) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, value: B) => Stream.Stream +>(2, (self: Stream.Stream, value: B): Stream.Stream => map(self, () => value)) + +const queueFromBufferOptions = ( + bufferSize?: number | "unbounded" | { + readonly bufferSize?: number | undefined + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + } | undefined +): Effect.Effect>> => { + if (bufferSize === "unbounded") { + return Queue.unbounded() + } else if (typeof bufferSize === "number" || bufferSize === undefined) { + return Queue.bounded(bufferSize ?? 16) + } + switch (bufferSize.strategy) { + case "dropping": + return Queue.dropping(bufferSize.bufferSize ?? 16) + case "sliding": + return Queue.sliding(bufferSize.bufferSize ?? 16) + default: + return Queue.bounded(bufferSize.bufferSize ?? 16) + } +} + +/** @internal */ +export const _async = ( + register: ( + emit: Emit.Emit + ) => Effect.Effect | void, + bufferSize?: number | "unbounded" | { + readonly bufferSize?: number | undefined + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + } | undefined +): Stream.Stream => + Effect.acquireRelease( + queueFromBufferOptions(bufferSize), + (queue) => Queue.shutdown(queue) + ).pipe( + Effect.flatMap((output) => + Effect.runtime().pipe( + Effect.flatMap((runtime) => + Effect.sync(() => { + const runPromiseExit = Runtime.runPromiseExit(runtime) + const canceler = register(emit.make((resume) => + InternalTake.fromPull(resume).pipe( + Effect.flatMap((take) => Queue.offer(output, take)), + Effect.asVoid, + runPromiseExit + ).then((exit) => { + if (Exit.isFailure(exit)) { + if (!Cause.isInterrupted(exit.cause)) { + throw Cause.squash(exit.cause) + } + } + }) + )) + return canceler + }) + ), + Effect.map((value) => { + const loop: Channel.Channel, unknown, E, unknown, void, unknown> = Queue.take(output).pipe( + Effect.flatMap((take) => InternalTake.done(take)), + Effect.match({ + onFailure: (maybeError) => + core.fromEffect(Queue.shutdown(output)).pipe( + channel.zipRight(Option.match(maybeError, { + onNone: () => core.void, + onSome: (error) => core.fail(error) + })) + ), + onSuccess: (chunk) => core.write(chunk).pipe(core.flatMap(() => loop)) + }), + channel.unwrap + ) + return fromChannel(loop).pipe(ensuring(value ?? Effect.void)) + }) + ) + ), + unwrapScoped + ) + +/** @internal */ +export const asyncEffect = ( + register: (emit: Emit.Emit) => Effect.Effect, + bufferSize?: number | "unbounded" | { + readonly bufferSize?: number | undefined + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + } | undefined +): Stream.Stream => + pipe( + Effect.acquireRelease( + queueFromBufferOptions(bufferSize), + (queue) => Queue.shutdown(queue) + ), + Effect.flatMap((output) => + pipe( + Effect.runtime(), + Effect.flatMap((runtime) => + pipe( + register( + emit.make((k) => + pipe( + InternalTake.fromPull(k), + Effect.flatMap((take) => Queue.offer(output, take)), + Effect.asVoid, + Runtime.runPromiseExit(runtime) + ).then((exit) => { + if (Exit.isFailure(exit)) { + if (!Cause.isInterrupted(exit.cause)) { + throw Cause.squash(exit.cause) + } + } + }) + ) + ), + Effect.map(() => { + const loop: Channel.Channel, unknown, E, unknown, void, unknown> = pipe( + Queue.take(output), + Effect.flatMap(InternalTake.done), + Effect.match({ + onFailure: (maybeError) => + pipe( + core.fromEffect(Queue.shutdown(output)), + channel.zipRight(Option.match(maybeError, { onNone: () => core.void, onSome: core.fail })) + ), + onSuccess: (chunk) => pipe(core.write(chunk), core.flatMap(() => loop)) + }), + channel.unwrap + ) + return loop + }) + ) + ) + ) + ), + channel.unwrapScoped, + fromChannel + ) + +const queueFromBufferOptionsPush = ( + options?: { readonly bufferSize: "unbounded" } | { + readonly bufferSize?: number | undefined + readonly strategy?: "dropping" | "sliding" | undefined + } | undefined +): Effect.Effect | Exit.Exit>> => { + if (options?.bufferSize === "unbounded" || (options?.bufferSize === undefined && options?.strategy === undefined)) { + return Queue.unbounded() + } + switch (options?.strategy) { + case "sliding": + return Queue.sliding(options.bufferSize ?? 16) + default: + return Queue.dropping(options?.bufferSize ?? 16) + } +} + +/** @internal */ +export const asyncPush = ( + register: (emit: Emit.EmitOpsPush) => Effect.Effect, + options?: { + readonly bufferSize: "unbounded" + } | { + readonly bufferSize?: number | undefined + readonly strategy?: "dropping" | "sliding" | undefined + } | undefined +): Stream.Stream> => + Effect.acquireRelease( + queueFromBufferOptionsPush(options), + Queue.shutdown + ).pipe( + Effect.tap((queue) => + FiberRef.getWith(FiberRef.currentScheduler, (scheduler) => register(emit.makePush(queue, scheduler))) + ), + Effect.map((queue) => { + const loop: Channel.Channel, unknown, E> = core.flatMap(Queue.take(queue), (item) => + Exit.isExit(item) + ? Exit.isSuccess(item) ? core.void : core.failCause(item.cause) + : channel.zipRight(core.write(Chunk.unsafeFromArray(item)), loop)) + return loop + }), + channel.unwrapScoped, + fromChannel + ) + +/** @internal */ +export const asyncScoped = ( + register: (emit: Emit.Emit) => Effect.Effect, + bufferSize?: number | "unbounded" | { + readonly bufferSize?: number | undefined + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + } | undefined +): Stream.Stream> => + pipe( + Effect.acquireRelease( + queueFromBufferOptions(bufferSize), + (queue) => Queue.shutdown(queue) + ), + Effect.flatMap((output) => + pipe( + Effect.runtime(), + Effect.flatMap((runtime) => + pipe( + register( + emit.make((k) => + pipe( + InternalTake.fromPull(k), + Effect.flatMap((take) => Queue.offer(output, take)), + Effect.asVoid, + Runtime.runPromiseExit(runtime) + ).then((exit) => { + if (Exit.isFailure(exit)) { + if (!Cause.isInterrupted(exit.cause)) { + throw Cause.squash(exit.cause) + } + } + }) + ) + ), + Effect.zipRight(Ref.make(false)), + Effect.flatMap((ref) => + pipe( + Ref.get(ref), + Effect.map((isDone) => + isDone ? + pull.end() : + pipe( + Queue.take(output), + Effect.flatMap(InternalTake.done), + Effect.onError(() => + pipe( + Ref.set(ref, true), + Effect.zipRight(Queue.shutdown(output)) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ), + scoped, + flatMap(repeatEffectChunkOption) + ) + +/** @internal */ +export const branchAfter = dual< + ( + n: number, + f: (input: Chunk.Chunk) => Stream.Stream + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + n: number, + f: (input: Chunk.Chunk) => Stream.Stream + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + n: number, + f: (input: Chunk.Chunk) => Stream.Stream + ) => + suspend(() => { + const buffering = ( + acc: Chunk.Chunk + ): Channel.Channel, Chunk.Chunk, E2, never, unknown, unknown, R | R2> => + core.readWith({ + onInput: (input) => { + const nextSize = acc.length + input.length + if (nextSize >= n) { + const [b1, b2] = pipe(input, Chunk.splitAt(n - acc.length)) + return running(pipe(acc, Chunk.appendAll(b1)), b2) + } + return buffering(pipe(acc, Chunk.appendAll(input))) + }, + onFailure: core.fail, + onDone: () => running(acc, Chunk.empty()) + }) + const running = ( + prefix: Chunk.Chunk, + leftover: Chunk.Chunk + ): Channel.Channel, Chunk.Chunk, E2, never, unknown, unknown, R | R2> => + core.pipeTo( + channel.zipRight( + core.write(leftover), + channel.identityChannel() + ), + toChannel(f(prefix)) + ) + return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(buffering(Chunk.empty())))) + }) +) + +/** @internal */ +export const broadcast = dual< + ( + n: N, + maximumLag: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ) => ( + self: Stream.Stream + ) => Effect.Effect>, never, Scope.Scope | R>, + ( + self: Stream.Stream, + n: N, + maximumLag: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ) => Effect.Effect>, never, Scope.Scope | R> +>(3, ( + self: Stream.Stream, + n: N, + maximumLag: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } +): Effect.Effect>, never, Scope.Scope | R> => + pipe( + self, + broadcastedQueues(n, maximumLag), + Effect.map((tuple) => + tuple.map((queue) => flattenTake(fromQueue(queue, { shutdown: true }))) as Types.TupleOf> + ) + )) + +/** @internal */ +export const broadcastDynamic = dual< + ( + maximumLag: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ) => (self: Stream.Stream) => Effect.Effect, never, Scope.Scope | R>, + ( + self: Stream.Stream, + maximumLag: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ) => Effect.Effect, never, Scope.Scope | R> +>(2, ( + self: Stream.Stream, + maximumLag: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } +): Effect.Effect, never, Scope.Scope | R> => + Effect.map(toPubSub(self, maximumLag), (pubsub) => flattenTake(fromPubSub(pubsub)))) + +export const share = dual< + ( + config: { + readonly capacity: "unbounded" + readonly replay?: number | undefined + readonly idleTimeToLive?: Duration.DurationInput | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + readonly idleTimeToLive?: Duration.DurationInput | undefined + } + ) => ( + self: Stream.Stream + ) => Effect.Effect, never, R | Scope.Scope>, + ( + self: Stream.Stream, + config: { + readonly capacity: "unbounded" + readonly replay?: number | undefined + readonly idleTimeToLive?: Duration.DurationInput | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + readonly idleTimeToLive?: Duration.DurationInput | undefined + } + ) => Effect.Effect, never, R | Scope.Scope> +>( + 2, + ( + self: Stream.Stream, + options: { + readonly capacity: "unbounded" + readonly replay?: number | undefined + readonly idleTimeToLive?: Duration.DurationInput | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + readonly idleTimeToLive?: Duration.DurationInput | undefined + } + ): Effect.Effect, never, R | Scope.Scope> => + Effect.map( + RcRef.make({ + acquire: broadcastDynamic(self, options), + idleTimeToLive: options.idleTimeToLive + }), + (rcRef) => unwrapScoped(RcRef.get(rcRef)) + ) +) + +/** @internal */ +export const broadcastedQueues = dual< + ( + n: N, + maximumLag: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ) => ( + self: Stream.Stream + ) => Effect.Effect>>, never, Scope.Scope | R>, + ( + self: Stream.Stream, + n: N, + maximumLag: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ) => Effect.Effect>>, never, Scope.Scope | R> +>(3, ( + self: Stream.Stream, + n: N, + maximumLag: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } +): Effect.Effect>>, never, Scope.Scope | R> => + Effect.flatMap(pubsubFromOptions(maximumLag), (pubsub) => + pipe( + Effect.all(Array.from({ length: n }, () => PubSub.subscribe(pubsub))) as Effect.Effect< + Types.TupleOf>>, + never, + R + >, + Effect.tap(() => Effect.forkScoped(runIntoPubSubScoped(self, pubsub))) + ))) + +/** @internal */ +export const broadcastedQueuesDynamic = dual< + ( + maximumLag: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ) => ( + self: Stream.Stream + ) => Effect.Effect>, never, Scope.Scope>, never, Scope.Scope | R>, + ( + self: Stream.Stream, + maximumLag: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ) => Effect.Effect>, never, Scope.Scope>, never, Scope.Scope | R> +>(2, ( + self: Stream.Stream, + maximumLag: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } +): Effect.Effect>, never, Scope.Scope>, never, Scope.Scope | R> => + Effect.map(toPubSub(self, maximumLag), PubSub.subscribe)) + +/** @internal */ +export const buffer = dual< + ( + options: { + readonly capacity: "unbounded" + } | { + readonly capacity: number + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + } + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + options: { + readonly capacity: "unbounded" + } | { + readonly capacity: number + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + } + ) => Stream.Stream +>(2, ( + self: Stream.Stream, + options: { + readonly capacity: "unbounded" + } | { + readonly capacity: number + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + } +): Stream.Stream => { + if (options.capacity === "unbounded") { + return bufferUnbounded(self) + } else if (options.strategy === "dropping") { + return bufferDropping(self, options.capacity) + } else if (options.strategy === "sliding") { + return bufferSliding(self, options.capacity) + } + const queue = toQueueOfElements(self, options) + return new StreamImpl( + channel.unwrapScoped( + Effect.map(queue, (queue) => { + const process: Channel.Channel, unknown, E, unknown, void, unknown> = pipe( + core.fromEffect(Queue.take(queue)), + core.flatMap(Exit.match({ + onFailure: (cause) => + pipe( + Cause.flipCauseOption(cause), + Option.match({ onNone: () => core.void, onSome: core.failCause }) + ), + onSuccess: (value) => core.flatMap(core.write(Chunk.of(value)), () => process) + })) + ) + return process + }) + ) + ) +}) + +/** @internal */ +export const bufferChunks = dual< + (options: { + readonly capacity: number + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + }) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, options: { + readonly capacity: number + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined + }) => Stream.Stream +>(2, (self: Stream.Stream, options: { + readonly capacity: number + readonly strategy?: "dropping" | "sliding" | "suspend" | undefined +}): Stream.Stream => { + if (options.strategy === "dropping") { + return bufferChunksDropping(self, options.capacity) + } else if (options.strategy === "sliding") { + return bufferChunksSliding(self, options.capacity) + } + const queue = toQueue(self, options) + return new StreamImpl( + channel.unwrapScoped( + Effect.map(queue, (queue) => { + const process: Channel.Channel, unknown, E, unknown, void, unknown> = pipe( + core.fromEffect(Queue.take(queue)), + core.flatMap(InternalTake.match({ + onEnd: () => core.void, + onFailure: core.failCause, + onSuccess: (value) => pipe(core.write(value), core.flatMap(() => process)) + })) + ) + return process + }) + ) + ) +}) + +const bufferChunksDropping = dual< + (capacity: number) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, capacity: number) => Stream.Stream +>(2, (self: Stream.Stream, capacity: number): Stream.Stream => { + const queue = Effect.acquireRelease( + Queue.dropping, Deferred.Deferred]>(capacity), + (queue) => Queue.shutdown(queue) + ) + return new StreamImpl(bufferSignal(queue, toChannel(self))) +}) + +const bufferChunksSliding = dual< + (capacity: number) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, capacity: number) => Stream.Stream +>(2, (self: Stream.Stream, capacity: number): Stream.Stream => { + const queue = Effect.acquireRelease( + Queue.sliding, Deferred.Deferred]>(capacity), + (queue) => Queue.shutdown(queue) + ) + return new StreamImpl(bufferSignal(queue, toChannel(self))) +}) + +const bufferDropping = dual< + (capacity: number) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, capacity: number) => Stream.Stream +>(2, (self: Stream.Stream, capacity: number): Stream.Stream => { + const queue = Effect.acquireRelease( + Queue.dropping, Deferred.Deferred]>(capacity), + (queue) => Queue.shutdown(queue) + ) + return new StreamImpl(bufferSignal(queue, toChannel(rechunk(1)(self)))) +}) + +const bufferSliding = dual< + (capacity: number) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, capacity: number) => Stream.Stream +>(2, (self: Stream.Stream, capacity: number): Stream.Stream => { + const queue = Effect.acquireRelease( + Queue.sliding, Deferred.Deferred]>(capacity), + (queue) => Queue.shutdown(queue) + ) + return new StreamImpl(bufferSignal(queue, toChannel(pipe(self, rechunk(1))))) +}) + +const bufferUnbounded = (self: Stream.Stream): Stream.Stream => { + const queue = toQueue(self, { strategy: "unbounded" }) + return new StreamImpl( + channel.unwrapScoped( + Effect.map(queue, (queue) => { + const process: Channel.Channel, unknown, E, unknown, void, unknown> = pipe( + core.fromEffect(Queue.take(queue)), + core.flatMap(InternalTake.match({ + onEnd: () => core.void, + onFailure: core.failCause, + onSuccess: (value) => core.flatMap(core.write(value), () => process) + })) + ) + return process + }) + ) + ) +} + +const bufferSignal = ( + scoped: Effect.Effect, Deferred.Deferred]>, never, Scope.Scope>, + bufferChannel: Channel.Channel, unknown, E, unknown, void, unknown, R> +): Channel.Channel, unknown, E, unknown, void, unknown, R> => { + const producer = ( + queue: Queue.Queue, Deferred.Deferred]>, + ref: Ref.Ref> + ): Channel.Channel, never, E, unknown, unknown, R> => { + const terminate = (take: Take.Take): Channel.Channel, never, E, unknown, unknown, R> => + pipe( + Ref.get(ref), + Effect.tap(Deferred.await), + Effect.zipRight(Deferred.make()), + Effect.flatMap((deferred) => + pipe( + Queue.offer(queue, [take, deferred] as const), + Effect.zipRight(Ref.set(ref, deferred)), + Effect.zipRight(Deferred.await(deferred)) + ) + ), + Effect.asVoid, + core.fromEffect + ) + return core.readWithCause({ + onInput: (input: Chunk.Chunk) => + pipe( + Deferred.make(), + Effect.flatMap( + (deferred) => + pipe( + Queue.offer(queue, [InternalTake.chunk(input), deferred] as const), + Effect.flatMap((added) => pipe(Ref.set(ref, deferred), Effect.when(() => added))) + ) + ), + Effect.asVoid, + core.fromEffect, + core.flatMap(() => producer(queue, ref)) + ), + onFailure: (error) => terminate(InternalTake.failCause(error)), + onDone: () => terminate(InternalTake.end) + }) + } + const consumer = ( + queue: Queue.Queue, Deferred.Deferred]> + ): Channel.Channel, unknown, E, unknown, void, unknown, R> => { + const process: Channel.Channel, unknown, E, unknown, void, unknown> = pipe( + core.fromEffect(Queue.take(queue)), + core.flatMap(([take, deferred]) => + channel.zipRight( + core.fromEffect(Deferred.succeed(deferred, void 0)), + InternalTake.match(take, { + onEnd: () => core.void, + onFailure: core.failCause, + onSuccess: (value) => pipe(core.write(value), core.flatMap(() => process)) + }) + ) + ) + ) + return process + } + return channel.unwrapScoped( + pipe( + scoped, + Effect.flatMap((queue) => + pipe( + Deferred.make(), + Effect.tap((start) => Deferred.succeed(start, void 0)), + Effect.flatMap((start) => + pipe( + Ref.make(start), + Effect.flatMap((ref) => + pipe( + bufferChannel, + core.pipeTo(producer(queue, ref)), + channel.runScoped, + Effect.forkScoped + ) + ), + Effect.as(consumer(queue)) + ) + ) + ) + ) + ) + ) +} + +/** @internal */ +export const catchAll = dual< + ( + f: (error: E) => Stream.Stream + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + f: (error: E) => Stream.Stream + ) => Stream.Stream +>(2, ( + self: Stream.Stream, + f: (error: E) => Stream.Stream +): Stream.Stream => + catchAllCause(self, (cause) => + Either.match(Cause.failureOrCause(cause), { + onLeft: f, + onRight: failCause + }))) + +/** @internal */ +export const catchAllCause = dual< + ( + f: (cause: Cause.Cause) => Stream.Stream + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + f: (cause: Cause.Cause) => Stream.Stream + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + f: (cause: Cause.Cause) => Stream.Stream + ): Stream.Stream => + new StreamImpl(pipe(toChannel(self), core.catchAllCause((cause) => toChannel(f(cause))))) +) + +/** @internal */ +export const catchSome = dual< + ( + pf: (error: E) => Option.Option> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + pf: (error: E) => Option.Option> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + pf: (error: E) => Option.Option> + ): Stream.Stream => + pipe(self, catchAll((error) => pipe(pf(error), Option.getOrElse(() => fail(error))))) +) + +/** @internal */ +export const catchSomeCause = dual< + ( + pf: (cause: Cause.Cause) => Option.Option> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + pf: (cause: Cause.Cause) => Option.Option> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + pf: (cause: Cause.Cause) => Option.Option> + ): Stream.Stream => + pipe(self, catchAllCause((cause) => pipe(pf(cause), Option.getOrElse(() => failCause(cause))))) +) + +/* @internal */ +export const catchTag = dual< + ( + k: K, + f: (e: Extract) => Stream.Stream + ) => (self: Stream.Stream) => Stream.Stream | E1, R | R1>, + ( + self: Stream.Stream, + k: K, + f: (e: Extract) => Stream.Stream + ) => Stream.Stream | E1, R | R1> +>(3, (self, k, f) => + catchAll(self, (e) => { + if ("_tag" in e && e["_tag"] === k) { + return f(e as any) + } + return fail(e as any) + })) + +/** @internal */ +export const catchTags: { + < + E extends { _tag: string }, + Cases extends { + [K in E["_tag"]]+?: (error: Extract) => Stream.Stream + } + >( + cases: Cases + ): (self: Stream.Stream) => Stream.Stream< + | A + | { + [K in keyof Cases]: Cases[K] extends + ((...args: Array) => Stream.Stream.Variance) ? A + : never + }[keyof Cases], + | Exclude + | { + [K in keyof Cases]: Cases[K] extends + ((...args: Array) => Stream.Stream.Variance) ? E + : never + }[keyof Cases], + | R + | { + [K in keyof Cases]: Cases[K] extends + ((...args: Array) => Stream.Stream.Variance) ? R + : never + }[keyof Cases] + > + < + A, + E extends { _tag: string }, + R, + Cases extends { + [K in E["_tag"]]+?: (error: Extract) => Stream.Stream + } + >( + self: Stream.Stream, + cases: Cases + ): Stream.Stream< + | A + | { + [K in keyof Cases]: Cases[K] extends + ((...args: Array) => Stream.Stream.Variance) ? A + : never + }[keyof Cases], + | Exclude + | { + [K in keyof Cases]: Cases[K] extends + ((...args: Array) => Stream.Stream.Variance) ? E + : never + }[keyof Cases], + | R + | { + [K in keyof Cases]: Cases[K] extends + ((...args: Array) => Stream.Stream.Variance) ? R + : never + }[keyof Cases] + > +} = dual(2, (self, cases) => + catchAll(self, (e: any) => { + const keys = Object.keys(cases) + if ("_tag" in e && keys.includes(e["_tag"])) { + return cases[e["_tag"]](e as any) + } + return fail(e as any) + })) + +/** @internal */ +export const changes = (self: Stream.Stream): Stream.Stream => + pipe(self, changesWith((x, y) => Equal.equals(y)(x))) + +/** @internal */ +export const changesWith = dual< + (f: (x: A, y: A) => boolean) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, f: (x: A, y: A) => boolean) => Stream.Stream +>(2, (self: Stream.Stream, f: (x: A, y: A) => boolean): Stream.Stream => { + const writer = ( + last: Option.Option + ): Channel.Channel, Chunk.Chunk, E, E, void, unknown> => + core.readWithCause({ + onInput: (input: Chunk.Chunk) => { + const [newLast, newChunk] = Chunk.reduce( + input, + [last, Chunk.empty()] as const, + ([option, outputs], output) => { + if (Option.isSome(option) && f(option.value, output)) { + return [Option.some(output), outputs] as const + } + return [Option.some(output), pipe(outputs, Chunk.append(output))] as const + } + ) + if (Chunk.isEmpty(newChunk)) return writer(newLast) + return core.flatMap( + core.write(newChunk), + () => writer(newLast) + ) + }, + onFailure: core.failCause, + onDone: () => core.void + }) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(writer(Option.none())))) +}) + +/** @internal */ +export const changesWithEffect = dual< + ( + f: (x: A, y: A) => Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + f: (x: A, y: A) => Effect.Effect + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + f: (x: A, y: A) => Effect.Effect + ): Stream.Stream => { + const writer = ( + last: Option.Option + ): Channel.Channel, Chunk.Chunk, E | E2, E | E2, void, unknown, R | R2> => + core.readWithCause({ + onInput: (input: Chunk.Chunk) => + pipe( + input, + Effect.reduce([last, Chunk.empty()] as const, ([option, outputs], output) => { + if (Option.isSome(option)) { + return pipe( + f(option.value, output), + Effect.map((bool) => + bool ? + [Option.some(output), outputs] as const : + [Option.some(output), pipe(outputs, Chunk.append(output))] as const + ) + ) + } + return Effect.succeed( + [ + Option.some(output), + pipe(outputs, Chunk.append(output)) + ] as const + ) + }), + core.fromEffect, + core.flatMap(([newLast, newChunk]) => + pipe( + core.write(newChunk), + core.flatMap(() => writer(newLast)) + ) + ) + ), + onFailure: core.failCause, + onDone: () => core.void + }) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(writer(Option.none())))) + } +) + +/** @internal */ +export const chunks = (self: Stream.Stream): Stream.Stream, E, R> => + pipe(self, mapChunks(Chunk.of)) + +/** @internal */ +export const chunksWith = dual< + ( + f: (stream: Stream.Stream, E, R>) => Stream.Stream, E2, R2> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + f: (stream: Stream.Stream, E, R>) => Stream.Stream, E2, R2> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + f: (stream: Stream.Stream, E, R>) => Stream.Stream, E2, R2> + ): Stream.Stream => flattenChunks(f(chunks(self))) +) + +const unsome = (effect: Effect.Effect, R>): Effect.Effect, E, R> => + Effect.catchAll( + Effect.asSome(effect), + (o) => o._tag === "None" ? Effect.succeedNone : Effect.fail(o.value) + ) + +/** @internal */ +export const combine = dual< + ( + that: Stream.Stream, + s: S, + f: ( + s: S, + pullLeft: Effect.Effect, R3>, + pullRight: Effect.Effect, R4> + ) => Effect.Effect>, never, R5> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + that: Stream.Stream, + s: S, + f: ( + s: S, + pullLeft: Effect.Effect, R3>, + pullRight: Effect.Effect, R4> + ) => Effect.Effect>, never, R5> + ) => Stream.Stream +>(4, ( + self: Stream.Stream, + that: Stream.Stream, + s: S, + f: ( + s: S, + pullLeft: Effect.Effect, R3>, + pullRight: Effect.Effect, R4> + ) => Effect.Effect>, never, R5> +): Stream.Stream => { + function producer( + handoff: Handoff.Handoff>>, + latch: Handoff.Handoff + ): Channel.Channel { + return core.fromEffect(Handoff.take(latch)).pipe( + channel.zipRight(core.readWithCause({ + onInput: (input) => + core.flatMap( + core.fromEffect( + Handoff.offer>>( + handoff, + Exit.succeed(input) + ) + ), + () => producer(handoff, latch) + ), + onFailure: (cause) => + core.fromEffect( + Handoff.offer>>( + handoff, + Exit.failCause(pipe(cause, Cause.map(Option.some))) + ) + ), + onDone: () => + core.flatMap( + core.fromEffect( + Handoff.offer>>( + handoff, + Exit.fail(Option.none()) + ) + ), + () => producer(handoff, latch) + ) + })) + ) + } + return new StreamImpl( + channel.unwrapScopedWith((scope) => + Effect.all([ + Handoff.make>>(), + Handoff.make>>(), + Handoff.make(), + Handoff.make() + ]).pipe( + Effect.tap(([left, _, latchL]) => + toChannel(self).pipe( + channel.concatMap(channel.writeChunk), + core.pipeTo(producer(left, latchL)), + channelExecutor.runIn(scope), + Effect.forkIn(scope) + ) + ), + Effect.tap(([, right, _, rightL]) => + toChannel(that).pipe( + channel.concatMap(channel.writeChunk), + core.pipeTo(producer(right, rightL)), + channelExecutor.runIn(scope), + Effect.forkIn(scope) + ) + ), + Effect.map(([left, right, latchL, latchR]) => { + const pullLeft = Handoff.offer(latchL, void 0).pipe( + Effect.zipRight(Handoff.take(left).pipe(Effect.flatMap(identity))) + ) + const pullRight = Handoff.offer(latchR, void 0).pipe( + Effect.zipRight(Handoff.take(right).pipe(Effect.flatMap(identity))) + ) + return toChannel(unfoldEffect(s, (s) => Effect.flatMap(f(s, pullLeft, pullRight), unsome))) + }) + ) + ) + ) +}) + +/** @internal */ +export const combineChunks = dual< + ( + that: Stream.Stream, + s: S, + f: ( + s: S, + pullLeft: Effect.Effect, Option.Option, R3>, + pullRight: Effect.Effect, Option.Option, R4> + ) => Effect.Effect, S], Option.Option>, never, R5> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + that: Stream.Stream, + s: S, + f: ( + s: S, + pullLeft: Effect.Effect, Option.Option, R3>, + pullRight: Effect.Effect, Option.Option, R4> + ) => Effect.Effect, S], Option.Option>, never, R5> + ) => Stream.Stream +>(4, ( + self: Stream.Stream, + that: Stream.Stream, + s: S, + f: ( + s: S, + pullLeft: Effect.Effect, Option.Option, R3>, + pullRight: Effect.Effect, Option.Option, R4> + ) => Effect.Effect, S], Option.Option>, never, R5> +): Stream.Stream => { + const producer = ( + handoff: Handoff.Handoff>, + latch: Handoff.Handoff + ): Channel.Channel, never, Err, unknown, unknown, R> => + channel.zipRight( + core.fromEffect(Handoff.take(latch)), + core.readWithCause({ + onInput: (input) => + core.flatMap( + core.fromEffect(pipe( + handoff, + Handoff.offer>(InternalTake.chunk(input)) + )), + () => producer(handoff, latch) + ), + onFailure: (cause) => + core.fromEffect( + Handoff.offer>( + handoff, + InternalTake.failCause(cause) + ) + ), + onDone: (): Channel.Channel, never, Err, unknown, unknown, R> => + core.fromEffect(Handoff.offer>(handoff, InternalTake.end)) + }) + ) + return new StreamImpl( + channel.unwrapScopedWith((scope) => + Effect.all([ + Handoff.make>(), + Handoff.make>(), + Handoff.make(), + Handoff.make() + ]).pipe( + Effect.tap(([left, _, latchL]) => + core.pipeTo(toChannel(self), producer(left, latchL)).pipe( + channelExecutor.runIn(scope), + Effect.forkIn(scope) + ) + ), + Effect.tap(([_, right, __, latchR]) => + core.pipeTo(toChannel(that), producer(right, latchR)).pipe( + channelExecutor.runIn(scope), + Effect.forkIn(scope) + ) + ), + Effect.map(([left, right, latchL, latchR]) => { + const pullLeft = Handoff.offer(latchL, void 0).pipe( + Effect.zipRight(Handoff.take(left).pipe(Effect.flatMap(InternalTake.done))) + ) + const pullRight = Handoff.offer(latchR, void 0).pipe( + Effect.zipRight(Handoff.take(right).pipe(Effect.flatMap(InternalTake.done))) + ) + return toChannel(unfoldChunkEffect(s, (s) => Effect.flatMap(f(s, pullLeft, pullRight), unsome))) + }) + ) + ) + ) +}) + +/** @internal */ +export const concat = dual< + ( + that: Stream.Stream + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + that: Stream.Stream + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + that: Stream.Stream + ): Stream.Stream => + new StreamImpl(pipe(toChannel(self), channel.zipRight(toChannel(that)))) +) + +/** @internal */ +export const concatAll = (streams: Chunk.Chunk>): Stream.Stream => + suspend(() => pipe(streams, Chunk.reduce(empty as Stream.Stream, (x, y) => concat(y)(x)))) + +/** @internal */ +export const cross: { + ( + right: Stream.Stream + ): (left: Stream.Stream) => Stream.Stream<[AL, AR], EL | ER, RL | RR> + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream<[AL, AR], EL | ER, RL | RR> +} = dual( + 2, + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream<[AL, AR], EL | ER, RL | RR> => pipe(left, crossWith(right, (a, a2) => [a, a2])) +) + +/** @internal */ +export const crossLeft: { + ( + right: Stream.Stream + ): (left: Stream.Stream) => Stream.Stream + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream +} = dual( + 2, + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream => pipe(left, crossWith(right, (a, _) => a)) +) + +/** @internal */ +export const crossRight: { + ( + right: Stream.Stream + ): (left: Stream.Stream) => Stream.Stream + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream +} = dual( + 2, + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream => flatMap(left, () => right) +) + +/** @internal */ +export const crossWith: { + ( + right: Stream.Stream, + f: (left: AL, right: AR) => A + ): (left: Stream.Stream) => Stream.Stream + ( + left: Stream.Stream, + right: Stream.Stream, + f: (left: AL, right: AR) => A + ): Stream.Stream +} = dual( + 3, + ( + left: Stream.Stream, + right: Stream.Stream, + f: (left: AL, right: AR) => A + ): Stream.Stream => pipe(left, flatMap((a) => pipe(right, map((b) => f(a, b))))) +) + +/** @internal */ +export const debounce = dual< + (duration: Duration.DurationInput) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, duration: Duration.DurationInput) => Stream.Stream +>( + 2, + (self: Stream.Stream, duration: Duration.DurationInput): Stream.Stream => + unwrapScopedWith((scope) => + Effect.gen(function*() { + const handoff = yield* Handoff.make>() + + function enqueue(last: Chunk.Chunk): Effect.Effect< + Channel.Channel, unknown, E, unknown, unknown, unknown> + > { + return Clock.sleep(duration).pipe( + Effect.as(last), + Effect.forkIn(scope), + Effect.map((fiber) => consumer(DebounceState.previous(fiber))) + ) + } + + const producer: Channel.Channel, E, E, unknown, unknown> = core.readWithCause({ + onInput: (input: Chunk.Chunk) => + Option.match(Chunk.last(input), { + onNone: () => producer, + onSome: (elem) => + core.fromEffect(Handoff.offer(handoff, HandoffSignal.emit(Chunk.of(elem)))).pipe( + core.flatMap(() => producer) + ) + }), + onFailure: (cause) => + core.fromEffect( + Handoff.offer>(handoff, HandoffSignal.halt(cause)) + ), + onDone: () => + core.fromEffect( + Handoff.offer>( + handoff, + HandoffSignal.end(SinkEndReason.UpstreamEnd) + ) + ) + }) + + function consumer( + state: DebounceState.DebounceState + ): Channel.Channel, unknown, E, unknown, unknown, unknown> { + switch (state._tag) { + case DebounceState.OP_NOT_STARTED: { + return channel.unwrap( + Handoff.take(handoff).pipe( + Effect.map((signal) => { + switch (signal._tag) { + case HandoffSignal.OP_EMIT: { + return channel.unwrap(enqueue(signal.elements)) + } + case HandoffSignal.OP_HALT: { + return core.failCause(signal.cause) + } + case HandoffSignal.OP_END: { + return core.void + } + } + }) + ) + ) + } + case DebounceState.OP_PREVIOUS: { + return channel.unwrap( + Handoff.take(handoff).pipe( + Effect.forkIn(scope), + Effect.flatMap((handoffFiber) => + Effect.raceWith(Fiber.join(state.fiber), Fiber.join(handoffFiber), { + onSelfDone: (leftExit, current) => + Exit.match(leftExit, { + onFailure: (cause) => + Fiber.interrupt(current).pipe( + Effect.as(core.failCause(cause)) + ), + onSuccess: (chunk) => + Fiber.interrupt(current).pipe( + Effect.zipRight(Effect.succeed( + core.write(chunk).pipe( + core.flatMap(() => consumer(DebounceState.current(handoffFiber))) + ) + )) + ) + }), + onOtherDone: (rightExit, previous) => + Exit.match(rightExit, { + onFailure: (cause) => + Fiber.interrupt(previous).pipe( + Effect.as(core.failCause(cause)) + ), + onSuccess: (signal) => { + switch (signal._tag) { + case HandoffSignal.OP_EMIT: { + return Fiber.interrupt(previous).pipe( + Effect.zipRight(enqueue(signal.elements)) + ) + } + case HandoffSignal.OP_HALT: { + return Fiber.interrupt(previous).pipe( + Effect.as(core.failCause(signal.cause)) + ) + } + case HandoffSignal.OP_END: { + return Fiber.join(previous).pipe( + Effect.map((chunk) => + core.write(chunk).pipe( + channel.zipRight(core.void) + ) + ) + ) + } + } + } + }) + }) + ) + ) + ) + } + case DebounceState.OP_CURRENT: { + return channel.unwrap( + Fiber.join(state.fiber).pipe( + Effect.map((signal) => { + switch (signal._tag) { + case HandoffSignal.OP_EMIT: { + return channel.unwrap(enqueue(signal.elements)) + } + case HandoffSignal.OP_HALT: { + return core.failCause(signal.cause) + } + case HandoffSignal.OP_END: { + return core.void + } + } + }) + ) + ) + } + } + } + + return scopedWith((scope) => + core.pipeTo(toChannel(self), producer).pipe( + channelExecutor.runIn(scope), + Effect.forkIn(scope) + ) + ).pipe(crossRight(new StreamImpl(consumer(DebounceState.notStarted)))) + }) + ) +) + +/** @internal */ +export const die = (defect: unknown): Stream.Stream => fromEffect(Effect.die(defect)) + +/** @internal */ +export const dieSync = (evaluate: LazyArg): Stream.Stream => fromEffect(Effect.dieSync(evaluate)) + +/** @internal */ +export const dieMessage = (message: string): Stream.Stream => fromEffect(Effect.dieMessage(message)) + +/** @internal */ +export const distributedWith = dual< + ( + options: { + readonly size: N + readonly maximumLag: number + readonly decide: (a: A) => Effect.Effect> + } + ) => ( + self: Stream.Stream + ) => Effect.Effect< + Types.TupleOf>>>, + never, + Scope.Scope | R + >, + ( + self: Stream.Stream, + options: { + readonly size: N + readonly maximumLag: number + readonly decide: (a: A) => Effect.Effect> + } + ) => Effect.Effect< + Types.TupleOf>>>, + never, + Scope.Scope | R + > +>( + 2, + ( + self: Stream.Stream, + options: { + readonly size: N + readonly maximumLag: number + readonly decide: (a: A) => Effect.Effect> + } + ): Effect.Effect< + Types.TupleOf>>>, + never, + Scope.Scope | R + > => + pipe( + Deferred.make<(a: A) => Effect.Effect>>(), + Effect.flatMap((deferred) => + pipe( + self, + distributedWithDynamic({ + maximumLag: options.maximumLag, + decide: (a) => Effect.flatMap(Deferred.await(deferred), (f) => f(a)) + }), + Effect.flatMap((next) => + pipe( + Effect.all( + Chunk.map( + Chunk.range(0, options.size - 1), + (id) => Effect.map(next, ([key, queue]) => [[key, id], queue] as const) + ) + ), + Effect.map(Chunk.unsafeFromArray), + Effect.flatMap((entries) => { + const [mappings, queues] = Chunk.reduceRight( + entries, + [ + new Map(), + Chunk.empty>>>() + ] as const, + ([mappings, queues], [mapping, queue]) => + [ + mappings.set(mapping[0], mapping[1]), + pipe(queues, Chunk.prepend(queue)) + ] as const + ) + return pipe( + Deferred.succeed(deferred, (a: A) => + Effect.map(options.decide(a), (f) => (key: number) => f(mappings.get(key)!))), + Effect.as( + Array.from(queues) as Types.TupleOf>>> + ) + ) + }) + ) + ) + ) + ) + ) +) + +/** @internal */ +const distributedWithDynamicId = { ref: 0 } + +const newDistributedWithDynamicId = () => { + const current = distributedWithDynamicId.ref + distributedWithDynamicId.ref = current + 1 + return current +} + +/** @internal */ +export const distributedWithDynamic = dual< + ( + options: { + readonly maximumLag: number + readonly decide: (a: A) => Effect.Effect> + } + ) => ( + self: Stream.Stream + ) => Effect.Effect< + Effect.Effect<[number, Queue.Dequeue>>]>, + never, + Scope.Scope | R + >, + ( + self: Stream.Stream, + options: { + readonly maximumLag: number + readonly decide: (a: A) => Effect.Effect> + } + ) => Effect.Effect< + Effect.Effect<[number, Queue.Dequeue>>]>, + never, + Scope.Scope | R + > +>(2, ( + self: Stream.Stream, + options: { + readonly maximumLag: number + readonly decide: (a: A) => Effect.Effect> + } +): Effect.Effect< + Effect.Effect<[number, Queue.Dequeue>>]>, + never, + Scope.Scope | R +> => distributedWithDynamicCallback(self, options.maximumLag, options.decide, () => Effect.void)) + +/** @internal */ +export const distributedWithDynamicCallback = dual< + ( + maximumLag: number, + decide: (a: A) => Effect.Effect>, + done: (exit: Exit.Exit>) => Effect.Effect + ) => ( + self: Stream.Stream + ) => Effect.Effect< + Effect.Effect<[number, Queue.Dequeue>>]>, + never, + Scope.Scope | R + >, + ( + self: Stream.Stream, + maximumLag: number, + decide: (a: A) => Effect.Effect>, + done: (exit: Exit.Exit>) => Effect.Effect + ) => Effect.Effect< + Effect.Effect<[number, Queue.Dequeue>>]>, + never, + Scope.Scope | R + > +>(4, ( + self: Stream.Stream, + maximumLag: number, + decide: (a: A) => Effect.Effect>, + done: (exit: Exit.Exit>) => Effect.Effect +): Effect.Effect< + Effect.Effect<[number, Queue.Dequeue>>]>, + never, + Scope.Scope | R +> => + pipe( + Effect.acquireRelease( + Ref.make>>>>(new Map()), + (ref, _) => pipe(Ref.get(ref), Effect.flatMap((queues) => pipe(queues.values(), Effect.forEach(Queue.shutdown)))) + ), + Effect.flatMap((queuesRef) => + Effect.gen(function*() { + const offer = (a: A): Effect.Effect => + pipe( + decide(a), + Effect.flatMap((shouldProcess) => + pipe( + Ref.get(queuesRef), + Effect.flatMap((queues) => + pipe( + queues.entries(), + Effect.reduce(Chunk.empty(), (acc, [id, queue]) => { + if (shouldProcess(id)) { + return pipe( + Queue.offer(queue, Exit.succeed(a)), + Effect.matchCauseEffect({ + onFailure: (cause) => + // Ignore all downstream queues that were shut + // down and remove them later + Cause.isInterrupted(cause) ? + Effect.succeed(pipe(acc, Chunk.prepend(id))) : + Effect.failCause(cause), + onSuccess: () => Effect.succeed(acc) + }) + ) + } + return Effect.succeed(acc) + }), + Effect.flatMap((ids) => { + if (Chunk.isNonEmpty(ids)) { + return Ref.update(queuesRef, (map) => { + for (const id of ids) { + map.delete(id) + } + return map + }) + } + return Effect.void + }) + ) + ) + ) + ), + Effect.asVoid + ) + const queuesLock = yield* Effect.makeSemaphore(1) + const newQueue = yield* Ref.make>>]>>( + pipe( + Queue.bounded>>(maximumLag), + Effect.flatMap((queue) => { + const id = newDistributedWithDynamicId() + return pipe( + Ref.update(queuesRef, (map) => map.set(id, queue)), + Effect.as([id, queue]) + ) + }) + ) + ) + const finalize = (endTake: Exit.Exit>): Effect.Effect => + // Make sure that no queues are currently being added + queuesLock.withPermits(1)( + pipe( + Ref.set( + newQueue, + pipe( + // All newly created queues should end immediately + Queue.bounded>>(1), + Effect.tap((queue) => Queue.offer(queue, endTake)), + Effect.flatMap((queue) => { + const id = newDistributedWithDynamicId() + return pipe( + Ref.update(queuesRef, (map) => map.set(id, queue)), + Effect.as(Tuple.make(id, queue)) + ) + }) + ) + ), + Effect.zipRight( + pipe( + Ref.get(queuesRef), + Effect.flatMap((map) => + pipe( + Chunk.fromIterable(map.values()), + Effect.forEach((queue) => + pipe( + Queue.offer(queue, endTake), + Effect.catchSomeCause((cause) => + Cause.isInterrupted(cause) ? Option.some(Effect.void) : Option.none() + ) + ) + ) + ) + ) + ) + ), + Effect.zipRight(done(endTake)), + Effect.asVoid + ) + ) + yield* pipe( + self, + runForEachScoped(offer), + Effect.matchCauseEffect({ + onFailure: (cause) => finalize(Exit.failCause(pipe(cause, Cause.map(Option.some)))), + onSuccess: () => finalize(Exit.fail(Option.none())) + }), + Effect.forkScoped + ) + return queuesLock.withPermits(1)( + Effect.flatten(Ref.get(newQueue)) + ) + }) + ) + )) + +/** @internal */ +export const drain = (self: Stream.Stream): Stream.Stream => + new StreamImpl(channel.drain(toChannel(self))) + +/** @internal */ +export const drainFork = dual< + ( + that: Stream.Stream + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + that: Stream.Stream + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + that: Stream.Stream + ): Stream.Stream => + fromEffect(Deferred.make()).pipe(flatMap((backgroundDied) => + scopedWith((scope) => + toChannel(that).pipe( + channel.drain, + channelExecutor.runIn(scope), + Effect.catchAllCause((cause) => Deferred.failCause(backgroundDied, cause)), + Effect.forkIn(scope) + ) + ).pipe(crossRight(interruptWhenDeferred(self, backgroundDied))) + )) +) + +/** @internal */ +export const drop = dual< + (n: number) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, n: number) => Stream.Stream +>(2, (self: Stream.Stream, n: number): Stream.Stream => { + const loop = (r: number): Channel.Channel, Chunk.Chunk, never, never, unknown, unknown> => + core.readWith({ + onInput: (input: Chunk.Chunk) => { + const dropped = pipe(input, Chunk.drop(r)) + const leftover = Math.max(0, r - input.length) + const more = Chunk.isEmpty(input) || leftover > 0 + if (more) { + return loop(leftover) + } + return pipe( + core.write(dropped), + channel.zipRight(channel.identityChannel, never, unknown>()) + ) + }, + onFailure: core.fail, + onDone: () => core.void + }) + return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(loop(n)))) +}) + +/** @internal */ +export const dropRight = dual< + (n: number) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, n: number) => Stream.Stream +>(2, (self: Stream.Stream, n: number): Stream.Stream => { + if (n <= 0) { + return identityStream() + } + return suspend(() => { + const queue = new RingBuffer(n) + const reader: Channel.Channel, Chunk.Chunk, E, E, void, unknown> = core.readWith({ + onInput: (input: Chunk.Chunk) => { + const outputs = pipe( + input, + Chunk.filterMap((elem) => { + const head = queue.head() + queue.put(elem) + return head + }) + ) + return pipe(core.write(outputs), core.flatMap(() => reader)) + }, + onFailure: core.fail, + onDone: () => core.void + }) + return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(reader))) + }) +}) + +/** @internal */ +export const dropUntil = dual< + (predicate: Predicate>) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, predicate: Predicate) => Stream.Stream +>( + 2, + (self: Stream.Stream, predicate: Predicate): Stream.Stream => + drop(dropWhile(self, (a) => !predicate(a)), 1) +) + +/** @internal */ +export const dropUntilEffect = dual< + ( + predicate: (a: Types.NoInfer) => Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + predicate: (a: Types.NoInfer) => Effect.Effect + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + predicate: (a: Types.NoInfer) => Effect.Effect + ): Stream.Stream => { + const loop: Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R2> = core.readWith({ + onInput: (input: Chunk.Chunk) => + pipe( + Effect.dropUntil(input, predicate), + Effect.map(Chunk.unsafeFromArray), + Effect.map((leftover) => { + const more = Chunk.isEmpty(leftover) + if (more) { + return core.suspend(() => loop) + } + return pipe( + core.write(leftover), + channel.zipRight(channel.identityChannel, E | E2, unknown>()) + ) + }), + channel.unwrap + ), + onFailure: core.fail, + onDone: () => core.void + }) + return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(loop))) + } +) + +/** @internal */ +export const dropWhile = dual< + (predicate: Predicate>) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, predicate: Predicate) => Stream.Stream +>(2, (self: Stream.Stream, predicate: Predicate): Stream.Stream => { + const loop: Channel.Channel, Chunk.Chunk, never, never, unknown, unknown> = core.readWith({ + onInput: (input: Chunk.Chunk) => { + const output = Chunk.dropWhile(input, predicate) + if (Chunk.isEmpty(output)) { + return core.suspend(() => loop) + } + return channel.zipRight( + core.write(output), + channel.identityChannel, never, unknown>() + ) + }, + onFailure: core.fail, + onDone: core.succeedNow + }) + return new StreamImpl(channel.pipeToOrFail(toChannel(self), loop)) +}) + +/** @internal */ +export const dropWhileEffect = dual< + ( + predicate: (a: Types.NoInfer) => Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + predicate: (a: A) => Effect.Effect + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + predicate: (a: A) => Effect.Effect + ): Stream.Stream => { + const loop: Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R2> = core.readWith({ + onInput: (input: Chunk.Chunk) => + pipe( + Effect.dropWhile(input, predicate), + Effect.map(Chunk.unsafeFromArray), + Effect.map((leftover) => { + const more = Chunk.isEmpty(leftover) + if (more) { + return core.suspend(() => loop) + } + return channel.zipRight( + core.write(leftover), + channel.identityChannel, E | E2, unknown>() + ) + }), + channel.unwrap + ), + onFailure: core.fail, + onDone: () => core.void + }) + return new StreamImpl(channel.pipeToOrFail( + toChannel(self), + loop + )) + } +) + +/** @internal */ +export const either = (self: Stream.Stream): Stream.Stream, never, R> => + pipe(self, map(Either.right), catchAll((error) => make(Either.left(error)))) + +/** @internal */ +export const empty: Stream.Stream = new StreamImpl(core.void) + +/** @internal */ +export const ensuring = dual< + ( + finalizer: Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, finalizer: Effect.Effect) => Stream.Stream +>( + 2, + (self: Stream.Stream, finalizer: Effect.Effect): Stream.Stream => + new StreamImpl(pipe(toChannel(self), channel.ensuring(finalizer))) +) + +/** @internal */ +export const ensuringWith = dual< + ( + finalizer: (exit: Exit.Exit) => Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + finalizer: (exit: Exit.Exit) => Effect.Effect + ) => Stream.Stream +>(2, (self, finalizer) => new StreamImpl(core.ensuringWith(toChannel(self), finalizer))) + +/** @internal */ +export const context = (): Stream.Stream, never, R> => fromEffect(Effect.context()) + +/** @internal */ +export const contextWith = (f: (env: Context.Context) => A): Stream.Stream => + pipe(context(), map(f)) + +/** @internal */ +export const contextWithEffect = ( + f: (env: Context.Context) => Effect.Effect +): Stream.Stream => pipe(context(), mapEffectSequential(f)) + +/** @internal */ +export const contextWithStream = ( + f: (env: Context.Context) => Stream.Stream +): Stream.Stream => pipe(context(), flatMap(f)) + +/** @internal */ +export const execute = (effect: Effect.Effect): Stream.Stream => + drain(fromEffect(effect)) + +/** @internal */ +export const fail = (error: E): Stream.Stream => fromEffectOption(Effect.fail(Option.some(error))) + +/** @internal */ +export const failSync = (evaluate: LazyArg): Stream.Stream => + fromEffectOption(Effect.failSync(() => Option.some(evaluate()))) + +/** @internal */ +export const failCause = (cause: Cause.Cause): Stream.Stream => fromEffect(Effect.failCause(cause)) + +/** @internal */ +export const failCauseSync = (evaluate: LazyArg>): Stream.Stream => + fromEffect(Effect.failCauseSync(evaluate)) + +/** @internal */ +export const filter: { + ( + refinement: Refinement, B> + ): (self: Stream.Stream) => Stream.Stream + (predicate: Predicate): (self: Stream.Stream) => Stream.Stream + (self: Stream.Stream, refinement: Refinement): Stream.Stream + (self: Stream.Stream, predicate: Predicate): Stream.Stream +} = dual( + 2, + (self: Stream.Stream, predicate: Predicate) => mapChunks(self, Chunk.filter(predicate)) +) + +/** @internal */ +export const filterEffect = dual< + ( + f: (a: Types.NoInfer) => Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect + ): Stream.Stream => { + const loop = ( + iterator: Iterator + ): Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R2> => { + const next = iterator.next() + if (next.done) { + return core.readWithCause({ + onInput: (input) => loop(input[Symbol.iterator]()), + onFailure: core.failCause, + onDone: core.succeed + }) + } else { + return pipe( + f(next.value), + Effect.map((bool) => + bool ? + pipe(core.write(Chunk.of(next.value)), core.flatMap(() => loop(iterator))) : + loop(iterator) + ), + channel.unwrap + ) + } + } + return new StreamImpl( + core.suspend(() => pipe(toChannel(self), core.pipeTo(loop(Chunk.empty()[Symbol.iterator]())))) + ) + } +) + +/** @internal */ +export const filterMap = dual< + (pf: (a: A) => Option.Option) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, pf: (a: A) => Option.Option) => Stream.Stream +>( + 2, + (self: Stream.Stream, pf: (a: A) => Option.Option): Stream.Stream => + mapChunks(self, Chunk.filterMap(pf)) +) + +/** @internal */ +export const filterMapEffect = dual< + ( + pf: (a: A) => Option.Option> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + pf: (a: A) => Option.Option> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + pf: (a: A) => Option.Option> + ): Stream.Stream => + suspend(() => { + const loop = ( + iterator: Iterator + ): Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R | R2> => { + const next = iterator.next() + if (next.done) { + return core.readWithCause({ + onInput: (input) => loop(input[Symbol.iterator]()), + onFailure: core.failCause, + onDone: core.succeed + }) + } else { + return pipe( + pf(next.value), + Option.match({ + onNone: () => Effect.sync(() => loop(iterator)), + onSome: Effect.map((a2) => core.flatMap(core.write(Chunk.of(a2)), () => loop(iterator))) + }), + channel.unwrap + ) + } + } + return new StreamImpl(pipe(toChannel(self), core.pipeTo(loop(Chunk.empty()[Symbol.iterator]())))) + }) +) + +/** @internal */ +export const filterMapWhile = dual< + ( + pf: (a: A) => Option.Option + ) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, pf: (a: A) => Option.Option) => Stream.Stream +>( + 2, + (self: Stream.Stream, pf: (a: A) => Option.Option) => { + const loop: Channel.Channel, Chunk.Chunk, E, E, unknown, unknown> = core.readWith({ + onInput: (input: Chunk.Chunk) => { + const mapped = Chunk.filterMapWhile(input, pf) + if (mapped.length === input.length) { + return pipe(core.write(mapped), core.flatMap(() => loop)) + } + return core.write(mapped) + }, + onFailure: core.fail, + onDone: core.succeed + }) + return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(loop))) + } +) + +/** @internal */ +export const filterMapWhileEffect = dual< + ( + pf: (a: A) => Option.Option> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + pf: (a: A) => Option.Option> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + pf: (a: A) => Option.Option> + ): Stream.Stream => + suspend(() => { + const loop = ( + iterator: Iterator + ): Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R | R2> => { + const next = iterator.next() + if (next.done) { + return core.readWithCause({ + onInput: (input) => loop(input[Symbol.iterator]()), + onFailure: core.failCause, + onDone: core.succeed + }) + } else { + return channel.unwrap( + Option.match(pf(next.value), { + onNone: () => Effect.succeed(core.void), + onSome: Effect.map( + (a2) => core.flatMap(core.write(Chunk.of(a2)), () => loop(iterator)) + ) + }) + ) + } + } + return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(loop(Chunk.empty()[Symbol.iterator]())))) + }) +) + +/** @internal */ +export const finalizer = (finalizer: Effect.Effect): Stream.Stream => + acquireRelease(Effect.void, () => finalizer) + +/** @internal */ +export const find: { + ( + refinement: Refinement, B> + ): (self: Stream.Stream) => Stream.Stream + (predicate: Predicate>): (self: Stream.Stream) => Stream.Stream + (self: Stream.Stream, refinement: Refinement): Stream.Stream + (self: Stream.Stream, predicate: Predicate): Stream.Stream +} = dual(2, (self: Stream.Stream, predicate: Predicate): Stream.Stream => { + const loop: Channel.Channel, Chunk.Chunk, E, E, unknown, unknown, R> = core.readWith({ + onInput: (input: Chunk.Chunk) => + Option.match(Chunk.findFirst(input, predicate), { + onNone: () => loop, + onSome: (n) => core.write(Chunk.of(n)) + }), + onFailure: core.fail, + onDone: () => core.void + }) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(loop))) +}) + +/** @internal */ +export const findEffect: { + ( + predicate: (a: Types.NoInfer) => Effect.Effect + ): (self: Stream.Stream) => Stream.Stream + ( + self: Stream.Stream, + predicate: (a: Types.NoInfer) => Effect.Effect + ): Stream.Stream +} = dual( + 2, + ( + self: Stream.Stream, + predicate: (a: Types.NoInfer) => Effect.Effect + ): Stream.Stream => { + const loop: Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R2> = core.readWith({ + onInput: (input: Chunk.Chunk) => + pipe( + Effect.findFirst(input, predicate), + Effect.map(Option.match({ + onNone: () => loop, + onSome: (n) => core.write(Chunk.of(n)) + })), + channel.unwrap + ), + onFailure: core.fail, + onDone: () => core.void + }) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(loop))) + } +) + +/** @internal */ +export const flatMap = dual< + ( + f: (a: A) => Stream.Stream, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly bufferSize?: number | undefined + readonly switch?: boolean | undefined + } + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + f: (a: A) => Stream.Stream, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly bufferSize?: number | undefined + readonly switch?: boolean | undefined + } + ) => Stream.Stream +>( + (args) => isStream(args[0]), + ( + self: Stream.Stream, + f: (a: A) => Stream.Stream, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly bufferSize?: number | undefined + readonly switch?: boolean | undefined + } + ): Stream.Stream => { + const bufferSize = options?.bufferSize ?? 16 + + if (options?.switch) { + return matchConcurrency( + options?.concurrency, + () => flatMapParSwitchBuffer(self, 1, bufferSize, f), + (n) => flatMapParSwitchBuffer(self, n, bufferSize, f) + ) + } + + return matchConcurrency( + options?.concurrency, + () => + new StreamImpl( + channel.concatMap( + toChannel(self), + (as) => + pipe( + as, + Chunk.map((a) => toChannel(f(a))), + Chunk.reduce( + core.void as Channel.Channel, unknown, E2, unknown, unknown, unknown, R2>, + (left, right) => pipe(left, channel.zipRight(right)) + ) + ) + ) + ), + (_) => + new StreamImpl( + pipe( + toChannel(self), + channel.concatMap(channel.writeChunk), + channel.mergeMap((out) => toChannel(f(out)), options as any) + ) + ) + ) + } +) + +/** @internal */ +export const matchConcurrency = ( + concurrency: number | "unbounded" | undefined, + sequential: () => A, + bounded: (n: number) => A +) => { + switch (concurrency) { + case undefined: + return sequential() + case "unbounded": + return bounded(Number.MAX_SAFE_INTEGER) + default: + return concurrency > 1 ? bounded(concurrency) : sequential() + } +} + +const flatMapParSwitchBuffer = dual< + ( + n: number, + bufferSize: number, + f: (a: A) => Stream.Stream + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + n: number, + bufferSize: number, + f: (a: A) => Stream.Stream + ) => Stream.Stream +>( + 4, + ( + self: Stream.Stream, + n: number, + bufferSize: number, + f: (a: A) => Stream.Stream + ): Stream.Stream => + new StreamImpl( + pipe( + toChannel(self), + channel.concatMap(channel.writeChunk), + channel.mergeMap((out) => toChannel(f(out)), { + concurrency: n, + mergeStrategy: MergeStrategy.BufferSliding(), + bufferSize + }) + ) + ) +) + +/** @internal */ +export const flatten = dual< + (options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly bufferSize?: number | undefined + }) => ( + self: Stream.Stream, E, R> + ) => Stream.Stream, + ( + self: Stream.Stream, E, R>, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly bufferSize?: number | undefined + } + ) => Stream.Stream +>((args) => isStream(args[0]), (self, options) => flatMap(self, identity, options)) + +/** @internal */ +export const flattenChunks = (self: Stream.Stream, E, R>): Stream.Stream => { + const flatten: Channel.Channel, Chunk.Chunk>, E, E, unknown, unknown> = core + .readWithCause({ + onInput: (chunks: Chunk.Chunk>) => + core.flatMap( + channel.writeChunk(chunks), + () => flatten + ), + onFailure: core.failCause, + onDone: () => core.void + }) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(flatten))) +} + +/** @internal */ +export const flattenEffect = dual< + ( + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly unordered?: boolean | undefined + } + ) => ( + self: Stream.Stream, E, R> + ) => Stream.Stream, + ( + self: Stream.Stream, E, R>, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly unordered?: boolean | undefined + } + ) => Stream.Stream +>( + (args) => isStream(args[0]), + (self, options) => + options?.unordered ? + flatMap(self, (a) => fromEffect(a), { concurrency: options.concurrency }) : + matchConcurrency( + options?.concurrency, + () => mapEffectSequential(self, identity), + (n) => + new StreamImpl( + pipe( + toChannel(self), + channel.concatMap(channel.writeChunk), + channel.mapOutEffectPar(identity, n), + channel.mapOut(Chunk.of) + ) + ) + ) +) + +/** @internal */ +export const flattenExitOption = ( + self: Stream.Stream>, E, R> +): Stream.Stream => { + const processChunk = ( + chunk: Chunk.Chunk>>, + cont: Channel.Channel, Chunk.Chunk>>, E | E2, E, unknown, unknown, R> + ) => { + const [toEmit, rest] = pipe(chunk, Chunk.splitWhere((exit) => !Exit.isSuccess(exit))) + const next = pipe( + Chunk.head(rest), + Option.match({ + onNone: () => cont, + onSome: Exit.match({ + onFailure: (cause) => + Option.match(Cause.flipCauseOption(cause), { + onNone: () => core.void, + onSome: core.failCause + }), + onSuccess: () => core.void + }) + }) + ) + return pipe( + core.write(pipe( + toEmit, + Chunk.filterMap((exit) => + Exit.isSuccess(exit) ? + Option.some(exit.value) : + Option.none() + ) + )), + core.flatMap(() => next) + ) + } + const process: Channel.Channel< + Chunk.Chunk, + Chunk.Chunk>>, + E | E2, + E, + unknown, + unknown, + R + > = core.readWithCause({ + onInput: (chunk: Chunk.Chunk>>) => processChunk(chunk, process), + onFailure: (cause) => core.failCause(cause), + onDone: () => core.void + }) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(process))) +} + +/** @internal */ +export const flattenIterables = (self: Stream.Stream, E, R>): Stream.Stream => + pipe(self, map(Chunk.fromIterable), flattenChunks) + +/** @internal */ +export const flattenTake = (self: Stream.Stream, E, R>): Stream.Stream => + flattenChunks(flattenExitOption(pipe(self, map((take) => take.exit)))) + +/** @internal */ +export const forever = (self: Stream.Stream): Stream.Stream => + new StreamImpl(channel.repeated(toChannel(self))) + +/** @internal */ +export const fromAsyncIterable = ( + iterable: AsyncIterable, + onError: (e: unknown) => E +) => + pipe( + Effect.acquireRelease( + Effect.sync(() => iterable[Symbol.asyncIterator]()), + (iterator) => iterator.return ? Effect.promise(async () => iterator.return!()) : Effect.void + ), + Effect.map((iterator) => + repeatEffectOption(pipe( + Effect.tryPromise({ + try: async () => iterator.next(), + catch: (reason) => Option.some(onError(reason)) + }), + Effect.flatMap((result) => result.done ? Effect.fail(Option.none()) : Effect.succeed(result.value)) + )) + ), + unwrapScoped + ) + +/** @internal */ +export const fromChannel = ( + channel: Channel.Channel, unknown, E, unknown, unknown, unknown, R> +): Stream.Stream => new StreamImpl(channel) + +/** @internal */ +export const toChannel = ( + stream: Stream.Stream +): Channel.Channel, unknown, E, unknown, unknown, unknown, R> => { + if ("channel" in stream) { + return (stream as StreamImpl).channel + } else if (Effect.isEffect(stream)) { + return toChannel(fromEffect(stream)) as any + } else { + throw new TypeError(`Expected a Stream.`) + } +} + +/** @internal */ +export const fromChunk = (chunk: Chunk.Chunk): Stream.Stream => + new StreamImpl(Chunk.isEmpty(chunk) ? core.void : core.write(chunk)) + +/** @internal */ +export const fromChunkPubSub: { + (pubsub: PubSub.PubSub>, options: { + readonly scoped: true + readonly shutdown?: boolean | undefined + }): Effect.Effect, never, Scope.Scope> + (pubsub: PubSub.PubSub>, options?: { + readonly scoped?: false | undefined + readonly shutdown?: boolean | undefined + }): Stream.Stream +} = (pubsub, options): any => { + if (options?.scoped) { + const effect = Effect.map(PubSub.subscribe(pubsub), fromChunkQueue) + return options.shutdown ? Effect.map(effect, ensuring(PubSub.shutdown(pubsub))) : effect + } + const stream = flatMap(scoped(PubSub.subscribe(pubsub)), fromChunkQueue) + return options?.shutdown ? ensuring(stream, PubSub.shutdown(pubsub)) : stream +} + +/** @internal */ +export const fromChunkQueue = (queue: Queue.Dequeue>, options?: { + readonly shutdown?: boolean | undefined +}): Stream.Stream => + pipe( + Queue.take(queue), + Effect.catchAllCause((cause) => + pipe( + Queue.isShutdown(queue), + Effect.flatMap((isShutdown) => + isShutdown && Cause.isInterrupted(cause) ? + pull.end() : + pull.failCause(cause) + ) + ) + ), + repeatEffectChunkOption, + options?.shutdown ? ensuring(Queue.shutdown(queue)) : identity + ) + +/** @internal */ +export const fromChunks = ( + ...chunks: Array> +): Stream.Stream => pipe(fromIterable(chunks), flatMap(fromChunk)) + +/** @internal */ +export const fromEffect = (effect: Effect.Effect): Stream.Stream => + pipe(effect, Effect.mapError(Option.some), fromEffectOption) + +/** @internal */ +export const fromEffectOption = (effect: Effect.Effect, R>): Stream.Stream => + new StreamImpl( + channel.unwrap( + Effect.match(effect, { + onFailure: Option.match({ + onNone: () => core.void, + onSome: core.fail + }), + onSuccess: (a) => core.write(Chunk.of(a)) + }) + ) + ) + +/** @internal */ +export const fromPubSub: { + (pubsub: PubSub.PubSub, options: { + readonly scoped: true + readonly maxChunkSize?: number | undefined + readonly shutdown?: boolean | undefined + }): Effect.Effect, never, Scope.Scope> + (pubsub: PubSub.PubSub, options?: { + readonly scoped?: false | undefined + readonly maxChunkSize?: number | undefined + readonly shutdown?: boolean | undefined + }): Stream.Stream +} = (pubsub, options): any => { + const maxChunkSize = options?.maxChunkSize ?? DefaultChunkSize + + if (options?.scoped) { + const effect = Effect.map( + PubSub.subscribe(pubsub), + (queue) => fromQueue(queue, { maxChunkSize, shutdown: true }) + ) + + return options.shutdown ? Effect.map(effect, ensuring(PubSub.shutdown(pubsub))) : effect + } + const stream = flatMap( + scoped(PubSub.subscribe(pubsub)), + (queue) => fromQueue(queue, { maxChunkSize }) + ) + return options?.shutdown ? ensuring(stream, PubSub.shutdown(pubsub)) : stream +} + +/** @internal */ +export const fromTPubSub = (pubsub: TPubSub.TPubSub): Stream.Stream => { + return unwrapScoped(Effect.map( + TPubSub.subscribeScoped(pubsub), + (queue) => fromTQueue(queue) + )) +} + +/** @internal */ +export const fromIterable = (iterable: Iterable): Stream.Stream => + suspend(() => + Chunk.isChunk(iterable) ? + fromChunk(iterable) : + fromIteratorSucceed(iterable[Symbol.iterator]()) + ) + +/** @internal */ +export const fromIterableEffect = ( + effect: Effect.Effect, E, R> +): Stream.Stream => pipe(effect, Effect.map(fromIterable), unwrap) + +/** @internal */ +export const fromIteratorSucceed = ( + iterator: Iterator, + maxChunkSize = DefaultChunkSize +): Stream.Stream => { + return pipe( + Effect.sync(() => { + let builder: Array = [] + const loop = ( + iterator: Iterator + ): Channel.Channel, unknown, never, unknown, unknown, unknown> => + pipe( + Effect.sync(() => { + let next: IteratorResult = iterator.next() + if (maxChunkSize === 1) { + if (next.done) { + return core.void + } + return pipe( + core.write(Chunk.of(next.value)), + core.flatMap(() => loop(iterator)) + ) + } + builder = [] + let count = 0 + while (next.done === false) { + builder.push(next.value) + count = count + 1 + if (count >= maxChunkSize) { + break + } + next = iterator.next() + } + if (count > 0) { + return pipe( + core.write(Chunk.unsafeFromArray(builder)), + core.flatMap(() => loop(iterator)) + ) + } + return core.void + }), + channel.unwrap + ) + return new StreamImpl(loop(iterator)) + }), + unwrap + ) +} + +/** @internal */ +export const fromPull = ( + effect: Effect.Effect, Option.Option, R2>, never, R | Scope.Scope> +): Stream.Stream | R2> => pipe(effect, Effect.map(repeatEffectChunkOption), unwrapScoped) + +/** @internal */ +export const fromQueue = ( + queue: Queue.Dequeue, + options?: { + readonly maxChunkSize?: number | undefined + readonly shutdown?: boolean | undefined + } +): Stream.Stream => + pipe( + Queue.takeBetween(queue, 1, options?.maxChunkSize ?? DefaultChunkSize), + Effect.catchAllCause((cause) => + pipe( + Queue.isShutdown(queue), + Effect.flatMap((isShutdown) => + isShutdown && Cause.isInterrupted(cause) ? + pull.end() : + pull.failCause(cause) + ) + ) + ), + repeatEffectChunkOption, + options?.shutdown ? ensuring(Queue.shutdown(queue)) : identity + ) + +/** @internal */ +export const fromTQueue = (queue: TQueue.TDequeue): Stream.Stream => + pipe( + TQueue.take(queue), + Effect.map(Chunk.of), + Effect.catchAllCause((cause) => + pipe( + TQueue.isShutdown(queue), + Effect.flatMap((isShutdown) => + isShutdown && Cause.isInterrupted(cause) ? + pull.end() : + pull.failCause(cause) + ) + ) + ), + repeatEffectChunkOption + ) + +/** @internal */ +export const fromSchedule = (schedule: Schedule.Schedule): Stream.Stream => + pipe( + Schedule.driver(schedule), + Effect.map((driver) => repeatEffectOption(driver.next(void 0))), + unwrap + ) + +/** @internal */ +export const fromReadableStream: { + ( + options: { + readonly evaluate: LazyArg> + readonly onError: (error: unknown) => E + readonly releaseLockOnEnd?: boolean | undefined + } + ): Stream.Stream + ( + evaluate: LazyArg>, + onError: (error: unknown) => E + ): Stream.Stream +} = ( + ...args: [options: { + readonly evaluate: LazyArg> + readonly onError: (error: unknown) => E + readonly releaseLockOnEnd?: boolean | undefined + }] | [ + evaluate: LazyArg>, + onError: (error: unknown) => E + ] +): Stream.Stream => { + const evaluate = args.length === 1 ? args[0].evaluate : args[0] + const onError = args.length === 1 ? args[0].onError : args[1] + const releaseLockOnEnd = args.length === 1 ? args[0].releaseLockOnEnd === true : false + return unwrapScoped(Effect.map( + Effect.acquireRelease( + Effect.sync(() => evaluate().getReader()), + (reader) => + releaseLockOnEnd + ? Effect.sync(() => reader.releaseLock()) + : Effect.promise(() => reader.cancel()) + ), + (reader) => + repeatEffectOption( + Effect.flatMap( + Effect.tryPromise({ + try: () => reader.read(), + catch: (reason) => Option.some(onError(reason)) + }), + ({ done, value }) => done ? Effect.fail(Option.none()) : Effect.succeed(value) + ) + ) + )) +} + +/** @internal */ +export const fromReadableStreamByob: { + ( + options: { + readonly evaluate: LazyArg> + readonly onError: (error: unknown) => E + readonly bufferSize?: number | undefined + readonly releaseLockOnEnd?: boolean | undefined + } + ): Stream.Stream + ( + evaluate: LazyArg>, + onError: (error: unknown) => E, + allocSize?: number + ): Stream.Stream +} = ( + ...args: [options: { + readonly evaluate: LazyArg> + readonly onError: (error: unknown) => E + readonly bufferSize?: number | undefined + readonly releaseLockOnEnd?: boolean | undefined + }] | [ + evaluate: LazyArg>, + onError: (error: unknown) => E, + allocSize?: number | undefined + ] +): Stream.Stream => { + const evaluate = args.length === 1 ? args[0].evaluate : args[0] + const onError = args.length === 1 ? args[0].onError : args[1] + const allocSize = (args.length === 1 ? args[0].bufferSize : args[2]) ?? 4096 + const releaseLockOnEnd = args.length === 1 ? args[0].releaseLockOnEnd === true : false + return unwrapScoped(Effect.map( + Effect.acquireRelease( + Effect.sync(() => evaluate().getReader({ mode: "byob" })), + (reader) => releaseLockOnEnd ? Effect.sync(() => reader.releaseLock()) : Effect.promise(() => reader.cancel()) + ), + (reader) => + catchAll( + forever(readChunkStreamByobReader(reader, onError, allocSize)), + (error) => error === EOF ? empty : fail(error) + ) + )) +} + +const EOF = Symbol.for("effect/Stream/EOF") + +const readChunkStreamByobReader = ( + reader: ReadableStreamBYOBReader, + onError: (error: unknown) => E, + size: number +): Stream.Stream => { + const buffer = new ArrayBuffer(size) + return paginateEffect(0, (offset) => + Effect.flatMap( + Effect.tryPromise({ + try: () => reader.read(new Uint8Array(buffer, offset, buffer.byteLength - offset)), + catch: (reason) => onError(reason) + }), + ({ done, value }) => { + if (done) { + return Effect.fail(EOF) + } + const newOffset = offset + value.byteLength + return Effect.succeed([ + value, + newOffset >= buffer.byteLength + ? Option.none() + : Option.some(newOffset) + ]) + } + )) +} + +/** @internal */ +export const groupAdjacentBy = dual< + ( + f: (a: A) => K + ) => (self: Stream.Stream) => Stream.Stream<[K, Chunk.NonEmptyChunk], E, R>, + ( + self: Stream.Stream, + f: (a: A) => K + ) => Stream.Stream<[K, Chunk.NonEmptyChunk], E, R> +>( + 2, + ( + self: Stream.Stream, + f: (a: A) => K + ): Stream.Stream<[K, Chunk.NonEmptyChunk], E, R> => { + type Output = [K, Chunk.NonEmptyChunk] + const groupAdjacentByChunk = ( + state: Option.Option, + chunk: Chunk.Chunk + ): [Option.Option, Chunk.Chunk] => { + if (Chunk.isEmpty(chunk)) { + return [state, Chunk.empty()] + } + const builder: Array = [] + let from = 0 + let until = 0 + let key: K | undefined = undefined + let previousChunk = Chunk.empty() + switch (state._tag) { + case "Some": { + const tuple = state.value + key = tuple[0] + let loop = true + while (loop && until < chunk.length) { + const input = Chunk.unsafeGet(chunk, until) + const updatedKey = f(input) + if (!Equal.equals(key, updatedKey)) { + const previousChunk = tuple[1] + const additionalChunk = Chunk.unsafeFromArray(Array.from(chunk).slice(from, until)) + const group = Chunk.appendAll(previousChunk, additionalChunk) + builder.push([key, group]) + key = updatedKey + from = until + loop = false + } + until = until + 1 + } + if (loop) { + previousChunk = tuple[1] + } + break + } + case "None": { + key = f(Chunk.unsafeGet(chunk, until)) + until = until + 1 + break + } + } + while (until < chunk.length) { + const input = Chunk.unsafeGet(chunk, until) + const updatedKey = f(input) + if (!Equal.equals(key, updatedKey)) { + builder.push([key, Chunk.unsafeFromArray(Array.from(chunk).slice(from, until)) as Chunk.NonEmptyChunk]) + key = updatedKey + from = until + } + until = until + 1 + } + const nonEmptyChunk = Chunk.appendAll(previousChunk, Chunk.unsafeFromArray(Array.from(chunk).slice(from, until))) + const output = Chunk.unsafeFromArray(builder) + return [Option.some([key, nonEmptyChunk as Chunk.NonEmptyChunk]), output] + } + + const groupAdjacent = ( + state: Option.Option + ): Channel.Channel, Chunk.Chunk, never, never, unknown, unknown> => + core.readWithCause({ + onInput: (input: Chunk.Chunk) => { + const [updatedState, output] = groupAdjacentByChunk(state, input) + return Chunk.isEmpty(output) + ? groupAdjacent(updatedState) + : core.flatMap(core.write(output), () => groupAdjacent(updatedState)) + }, + onFailure: (cause) => + Option.match(state, { + onNone: () => core.failCause(cause), + onSome: (output) => core.flatMap(core.write(Chunk.of(output)), () => core.failCause(cause)) + }), + onDone: (done) => + Option.match(state, { + onNone: () => core.succeedNow(done), + onSome: (output) => core.flatMap(core.write(Chunk.of(output)), () => core.succeedNow(done)) + }) + }) + return new StreamImpl(channel.pipeToOrFail(toChannel(self), groupAdjacent(Option.none()))) + } +) + +/** @internal */ +export const grouped = dual< + (chunkSize: number) => (self: Stream.Stream) => Stream.Stream, E, R>, + (self: Stream.Stream, chunkSize: number) => Stream.Stream, E, R> +>( + 2, + (self: Stream.Stream, chunkSize: number): Stream.Stream, E, R> => + pipe(self, rechunk(chunkSize), chunks) +) + +/** @internal */ +export const groupedWithin = dual< + ( + chunkSize: number, + duration: Duration.DurationInput + ) => (self: Stream.Stream) => Stream.Stream, E, R>, + ( + self: Stream.Stream, + chunkSize: number, + duration: Duration.DurationInput + ) => Stream.Stream, E, R> +>( + 3, + ( + self: Stream.Stream, + chunkSize: number, + duration: Duration.DurationInput + ): Stream.Stream, E, R> => + aggregateWithin(self, sink_.collectAllN(chunkSize), Schedule.spaced(duration)) +) + +/** @internal */ +export const haltWhen = dual< + ( + effect: Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + effect: Effect.Effect + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + effect: Effect.Effect + ): Stream.Stream => { + const writer = ( + fiber: Fiber.Fiber + ): Channel.Channel, Chunk.Chunk, E | E2, E | E2, void, unknown, R2> => + pipe( + Fiber.poll(fiber), + Effect.map(Option.match({ + onNone: () => + core.readWith({ + onInput: (input: Chunk.Chunk) => core.flatMap(core.write(input), () => writer(fiber)), + onFailure: core.fail, + onDone: () => core.void + }), + onSome: Exit.match({ + onFailure: core.failCause, + onSuccess: () => core.void + }) + })), + channel.unwrap + ) + return new StreamImpl( + channel.unwrapScopedWith((scope) => + effect.pipe( + Effect.forkIn(scope), + Effect.map((fiber) => toChannel(self).pipe(core.pipeTo(writer(fiber)))) + ) + ) + ) + } +) + +/** @internal */ +export const haltAfter = dual< + (duration: Duration.DurationInput) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, duration: Duration.DurationInput) => Stream.Stream +>( + 2, + (self: Stream.Stream, duration: Duration.DurationInput): Stream.Stream => + pipe(self, haltWhen(Clock.sleep(duration))) +) + +/** @internal */ +export const haltWhenDeferred = dual< + (deferred: Deferred.Deferred) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, deferred: Deferred.Deferred) => Stream.Stream +>( + 2, + (self: Stream.Stream, deferred: Deferred.Deferred): Stream.Stream => { + const writer: Channel.Channel, Chunk.Chunk, E | E2, E | E2, void, unknown, R> = pipe( + Deferred.poll(deferred), + Effect.map(Option.match({ + onNone: () => + core.readWith({ + onInput: (input: Chunk.Chunk) => pipe(core.write(input), core.flatMap(() => writer)), + onFailure: core.fail, + onDone: () => core.void + }), + onSome: (effect) => + channel.unwrap(Effect.match(effect, { + onFailure: core.fail, + onSuccess: () => core.void + })) + })), + channel.unwrap + ) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(writer))) + } +) + +/** @internal */ +export const identityStream = (): Stream.Stream => + new StreamImpl( + channel.identityChannel() as Channel.Channel, unknown, E, unknown, unknown, unknown> + ) + +/** @internal */ +export const interleave = dual< + ( + that: Stream.Stream + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + that: Stream.Stream + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + that: Stream.Stream + ): Stream.Stream => pipe(self, interleaveWith(that, forever(make(true, false)))) +) + +/** @internal */ +export const interleaveWith = dual< + ( + that: Stream.Stream, + decider: Stream.Stream + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + that: Stream.Stream, + decider: Stream.Stream + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + that: Stream.Stream, + decider: Stream.Stream + ): Stream.Stream => { + const producer = ( + handoff: Handoff.Handoff> + ): Channel.Channel => + core.readWithCause({ + onInput: (value: A | A2) => + core.flatMap( + core.fromEffect( + Handoff.offer>(handoff, InternalTake.of(value)) + ), + () => producer(handoff) + ), + onFailure: (cause) => + core.fromEffect( + Handoff.offer>( + handoff, + InternalTake.failCause(cause) + ) + ), + onDone: () => + core.fromEffect( + Handoff.offer>(handoff, InternalTake.end) + ) + }) + return new StreamImpl( + channel.unwrapScopedWith((scope) => + pipe( + Handoff.make>(), + Effect.zip(Handoff.make>()), + Effect.tap(([left]) => + toChannel(self).pipe( + channel.concatMap(channel.writeChunk), + core.pipeTo(producer(left)), + channelExecutor.runIn(scope), + Effect.forkIn(scope) + ) + ), + Effect.tap(([_, right]) => + toChannel(that).pipe( + channel.concatMap(channel.writeChunk), + core.pipeTo(producer(right)), + channelExecutor.runIn(scope), + Effect.forkIn(scope) + ) + ), + Effect.map(([left, right]) => { + const process = ( + leftDone: boolean, + rightDone: boolean + ): Channel.Channel, boolean, E | E2 | E3, E | E2 | E3, void, unknown, R> => + core.readWithCause({ + onInput: (bool: boolean) => { + if (bool && !leftDone) { + return pipe( + core.fromEffect(Handoff.take(left)), + core.flatMap(InternalTake.match({ + onEnd: () => rightDone ? core.void : process(true, rightDone), + onFailure: core.failCause, + onSuccess: (chunk) => pipe(core.write(chunk), core.flatMap(() => process(leftDone, rightDone))) + })) + ) + } + if (!bool && !rightDone) { + return pipe( + core.fromEffect(Handoff.take(right)), + core.flatMap(InternalTake.match({ + onEnd: () => leftDone ? core.void : process(leftDone, true), + onFailure: core.failCause, + onSuccess: (chunk) => pipe(core.write(chunk), core.flatMap(() => process(leftDone, rightDone))) + })) + ) + } + return process(leftDone, rightDone) + }, + onFailure: core.failCause, + onDone: () => core.void + }) + return pipe( + toChannel(decider), + channel.concatMap(channel.writeChunk), + core.pipeTo(process(false, false)) + ) + }) + ) + ) + ) + } +) + +/** @internal */ +export const intersperse = dual< + (element: A2) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, element: A2) => Stream.Stream +>(2, (self: Stream.Stream, element: A2): Stream.Stream => + new StreamImpl( + pipe( + toChannel(self), + channel.pipeToOrFail( + core.suspend(() => { + const writer = ( + isFirst: boolean + ): Channel.Channel, Chunk.Chunk, E, E, unknown, unknown> => + core.readWithCause({ + onInput: (chunk: Chunk.Chunk) => { + const builder: Array = [] + let flagResult = isFirst + for (const output of chunk) { + if (flagResult) { + flagResult = false + builder.push(output) + } else { + builder.push(element) + builder.push(output) + } + } + return pipe( + core.write(Chunk.unsafeFromArray(builder)), + core.flatMap(() => writer(flagResult)) + ) + }, + onFailure: core.failCause, + onDone: () => core.void + }) + return writer(true) + }) + ) + ) + )) + +/** @internal */ +export const intersperseAffixes = dual< + ( + options: { + readonly start: A2 + readonly middle: A3 + readonly end: A4 + } + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + options: { + readonly start: A2 + readonly middle: A3 + readonly end: A4 + } + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + { end, middle, start }: { + readonly start: A2 + readonly middle: A3 + readonly end: A4 + } + ): Stream.Stream => + pipe( + make(start), + concat(pipe(self, intersperse(middle))), + concat(make(end)) + ) +) + +/** @internal */ +export const interruptAfter = dual< + (duration: Duration.DurationInput) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, duration: Duration.DurationInput) => Stream.Stream +>( + 2, + (self: Stream.Stream, duration: Duration.DurationInput): Stream.Stream => + pipe(self, interruptWhen(Clock.sleep(duration))) +) + +/** @internal */ +export const interruptWhen = dual< + ( + effect: Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + effect: Effect.Effect + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + effect: Effect.Effect + ): Stream.Stream => new StreamImpl(pipe(toChannel(self), channel.interruptWhen(effect))) +) + +/** @internal */ +export const interruptWhenDeferred = dual< + (deferred: Deferred.Deferred) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, deferred: Deferred.Deferred) => Stream.Stream +>( + 2, + (self: Stream.Stream, deferred: Deferred.Deferred): Stream.Stream => + new StreamImpl(pipe(toChannel(self), channel.interruptWhenDeferred(deferred))) +) + +/** @internal */ +export const iterate = (value: A, next: (value: A) => A): Stream.Stream => + unfold(value, (a) => Option.some([a, next(a)] as const)) + +/** @internal */ +export const make = >(...as: As): Stream.Stream => fromIterable(as) + +/** @internal */ +export const map = dual< + (f: (a: A) => B) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, f: (a: A) => B) => Stream.Stream +>( + 2, + (self: Stream.Stream, f: (a: A) => B): Stream.Stream => + new StreamImpl(pipe(toChannel(self), channel.mapOut(Chunk.map(f)))) +) + +/** @internal */ +export const mapAccum = dual< + ( + s: S, + f: (s: S, a: A) => readonly [S, A2] + ) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, s: S, f: (s: S, a: A) => readonly [S, A2]) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + s: S, + f: (s: S, a: A) => readonly [S, A2] + ): Stream.Stream => { + const accumulator = (s: S): Channel.Channel, Chunk.Chunk, E, E, void, unknown> => + core.readWith({ + onInput: (input: Chunk.Chunk) => { + const [nextS, chunk] = Chunk.mapAccum(input, s, f) + return core.flatMap( + core.write(chunk), + () => accumulator(nextS) + ) + }, + onFailure: core.fail, + onDone: () => core.void + }) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(accumulator(s)))) + } +) + +/** @internal */ +export const mapAccumEffect = dual< + ( + s: S, + f: (s: S, a: A) => Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + s: S, + f: (s: S, a: A) => Effect.Effect + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + s: S, + f: (s: S, a: A) => Effect.Effect + ): Stream.Stream => + suspend(() => { + const accumulator = ( + s: S + ): Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R | R2> => + core.readWith({ + onInput: (input: Chunk.Chunk) => + pipe( + Effect.suspend(() => { + const outputs: Array = [] + const emit = (output: A2) => + Effect.sync(() => { + outputs.push(output) + }) + return pipe( + input, + Effect.reduce(s, (s, a) => + pipe( + f(s, a), + Effect.flatMap(([s, a]) => pipe(emit(a), Effect.as(s))) + )), + Effect.match({ + onFailure: (error) => { + if (outputs.length !== 0) { + return channel.zipRight(core.write(Chunk.unsafeFromArray(outputs)), core.fail(error)) + } + return core.fail(error) + }, + onSuccess: (s) => core.flatMap(core.write(Chunk.unsafeFromArray(outputs)), () => accumulator(s)) + }) + ) + }), + channel.unwrap + ), + onFailure: core.fail, + onDone: () => core.void + }) + return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(accumulator(s)))) + }) +) + +/** @internal */ +export const mapBoth = dual< + ( + options: { + readonly onFailure: (e: E) => E2 + readonly onSuccess: (a: A) => A2 + } + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + options: { + readonly onFailure: (e: E) => E2 + readonly onSuccess: (a: A) => A2 + } + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + options: { + readonly onFailure: (e: E) => E2 + readonly onSuccess: (a: A) => A2 + } + ): Stream.Stream => pipe(self, mapError(options.onFailure), map(options.onSuccess)) +) + +/** @internal */ +export const mapChunks = dual< + ( + f: (chunk: Chunk.Chunk) => Chunk.Chunk + ) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, f: (chunk: Chunk.Chunk) => Chunk.Chunk) => Stream.Stream +>( + 2, + (self: Stream.Stream, f: (chunk: Chunk.Chunk) => Chunk.Chunk): Stream.Stream => + new StreamImpl(pipe(toChannel(self), channel.mapOut(f))) +) + +/** @internal */ +export const mapChunksEffect = dual< + ( + f: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + f: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + f: (chunk: Chunk.Chunk) => Effect.Effect, E2, R2> + ): Stream.Stream => new StreamImpl(pipe(toChannel(self), channel.mapOutEffect(f))) +) + +/** @internal */ +export const mapConcat = dual< + (f: (a: A) => Iterable) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, f: (a: A) => Iterable) => Stream.Stream +>( + 2, + (self: Stream.Stream, f: (a: A) => Iterable): Stream.Stream => + pipe(self, mapConcatChunk((a) => Chunk.fromIterable(f(a)))) +) + +/** @internal */ +export const mapConcatChunk = dual< + (f: (a: A) => Chunk.Chunk) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, f: (a: A) => Chunk.Chunk) => Stream.Stream +>( + 2, + (self: Stream.Stream, f: (a: A) => Chunk.Chunk): Stream.Stream => + pipe(self, mapChunks(Chunk.flatMap(f))) +) + +/** @internal */ +export const mapConcatChunkEffect = dual< + ( + f: (a: A) => Effect.Effect, E2, R2> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect, E2, R2> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect, E2, R2> + ): Stream.Stream => pipe(self, mapEffectSequential(f), mapConcatChunk(identity)) +) + +/** @internal */ +export const mapConcatEffect = dual< + ( + f: (a: A) => Effect.Effect, E2, R2> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect, E2, R2> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect, E2, R2> + ): Stream.Stream => + pipe(self, mapEffectSequential((a) => pipe(f(a), Effect.map(Chunk.fromIterable))), mapConcatChunk(identity)) +) + +/** @internal */ +export const mapEffectSequential = dual< + ( + f: (a: A) => Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect + ): Stream.Stream => { + const loop = ( + iterator: Iterator + ): Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R2> => { + const next = iterator.next() + if (next.done) { + return core.readWithCause({ + onInput: (elem) => loop(elem[Symbol.iterator]()), + onFailure: core.failCause, + onDone: core.succeed + }) + } else { + const value = next.value + return channel.unwrap( + Effect.map(f(value), (a2) => + core.flatMap( + core.write(Chunk.of(a2)), + () => loop(iterator) + )) + ) + } + } + return new StreamImpl(pipe( + toChannel(self), + core.pipeTo(core.suspend(() => loop(Chunk.empty()[Symbol.iterator]()))) + )) + } +) + +/** @internal */ +export const mapEffectPar = dual< + ( + n: number, + f: (a: A) => Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + n: number, + f: (a: A) => Effect.Effect + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + n: number, + f: (a: A) => Effect.Effect + ): Stream.Stream => + new StreamImpl( + pipe( + toChannel(self), + channel.concatMap(channel.writeChunk), + channel.mapOutEffectPar(f, n), + channel.mapOut(Chunk.of) + ) + ) +) + +/** @internal */ +export const mapError = dual< + (f: (error: E) => E2) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, f: (error: E) => E2) => Stream.Stream +>( + 2, + (self: Stream.Stream, f: (error: E) => E2): Stream.Stream => + new StreamImpl(pipe(toChannel(self), channel.mapError(f))) +) + +/** @internal */ +export const mapErrorCause = dual< + ( + f: (cause: Cause.Cause) => Cause.Cause + ) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, f: (cause: Cause.Cause) => Cause.Cause) => Stream.Stream +>( + 2, + (self: Stream.Stream, f: (cause: Cause.Cause) => Cause.Cause): Stream.Stream => + new StreamImpl(pipe(toChannel(self), channel.mapErrorCause(f))) +) + +/** @internal */ +export const merge = dual< + ( + that: Stream.Stream, + options?: { + readonly haltStrategy?: HaltStrategy.HaltStrategyInput | undefined + } + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + that: Stream.Stream, + options?: { + readonly haltStrategy?: HaltStrategy.HaltStrategyInput | undefined + } + ) => Stream.Stream +>( + (args) => isStream(args[1]), + ( + self: Stream.Stream, + that: Stream.Stream, + options?: { + readonly haltStrategy?: HaltStrategy.HaltStrategyInput | undefined + } + ): Stream.Stream => + mergeWith(self, that, { + onSelf: identity, + onOther: identity, + haltStrategy: options?.haltStrategy + }) +) + +/** @internal */ +export const mergeAll = dual< + (options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + }) => (streams: Iterable>) => Stream.Stream, + (streams: Iterable>, options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + }) => Stream.Stream +>((args) => Symbol.iterator in args[0], (streams, options) => flatten(fromIterable(streams), options)) + +/** @internal */ +export const mergeWithTag: { + }>( + streams: S, + options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + } + ): Stream.Stream< + { [K in keyof S]: { _tag: K; value: Stream.Stream.Success } }[keyof S], + Stream.Stream.Error, + Stream.Stream.Context + > + (options: { + readonly concurrency: number | "unbounded" + readonly bufferSize?: number | undefined + }): }>(streams: S) => Stream.Stream< + { [K in keyof S]: { _tag: K; value: Stream.Stream.Success } }[keyof S], + Stream.Stream.Error, + Stream.Stream.Context + > +} = dual(2, (streams, options) => { + const keys = Object.keys(streams) + const values = keys.map((key) => streams[key].pipe(map((value) => ({ _tag: key, value })))) as any + return mergeAll(values, options) +}) + +/** @internal */ +export const mergeEither = dual< + ( + that: Stream.Stream + ) => (self: Stream.Stream) => Stream.Stream, E2 | E, R2 | R>, + ( + self: Stream.Stream, + that: Stream.Stream + ) => Stream.Stream, E2 | E, R2 | R> +>( + 2, + ( + self: Stream.Stream, + that: Stream.Stream + ): Stream.Stream, E2 | E, R2 | R> => + mergeWith(self, that, { onSelf: Either.left, onOther: Either.right }) +) + +/** @internal */ +export const mergeLeft = dual< + ( + right: Stream.Stream + ) => (left: Stream.Stream) => Stream.Stream, + ( + left: Stream.Stream, + right: Stream.Stream + ) => Stream.Stream +>( + 2, + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream => pipe(left, merge(drain(right))) +) + +/** @internal */ +export const mergeRight = dual< + ( + right: Stream.Stream + ) => (left: Stream.Stream) => Stream.Stream, + ( + left: Stream.Stream, + right: Stream.Stream + ) => Stream.Stream +>( + 2, + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream => pipe(drain(left), merge(right)) +) + +/** @internal */ +export const mergeWith = dual< + ( + other: Stream.Stream, + options: { + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A4 + readonly haltStrategy?: HaltStrategy.HaltStrategyInput | undefined + } + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + other: Stream.Stream, + options: { + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A4 + readonly haltStrategy?: HaltStrategy.HaltStrategyInput | undefined + } + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + other: Stream.Stream, + options: { + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A4 + readonly haltStrategy?: HaltStrategy.HaltStrategyInput | undefined + } + ): Stream.Stream => { + const strategy = options.haltStrategy ? haltStrategy.fromInput(options.haltStrategy) : HaltStrategy.Both + const handler = + (terminate: boolean) => + (exit: Exit.Exit): MergeDecision.MergeDecision => + terminate || !Exit.isSuccess(exit) ? + // TODO: remove + MergeDecision.Done(Effect.suspend(() => exit)) : + MergeDecision.Await((exit) => Effect.suspend(() => exit)) + + return new StreamImpl( + channel.mergeWith(toChannel(map(self, options.onSelf)), { + other: toChannel(map(other, options.onOther)), + onSelfDone: handler(strategy._tag === "Either" || strategy._tag === "Left"), + onOtherDone: handler(strategy._tag === "Either" || strategy._tag === "Right") + }) + ) + } +) + +/** @internal */ +export const mkString = (self: Stream.Stream): Effect.Effect => + run(self, sink_.mkString) + +/** @internal */ +export const never: Stream.Stream = fromEffect(Effect.never) + +/** @internal */ +export const onEnd: { + <_, E2, R2>( + effect: Effect.Effect<_, E2, R2> + ): (self: Stream.Stream) => Stream.Stream + ( + self: Stream.Stream, + effect: Effect.Effect<_, E2, R2> + ): Stream.Stream +} = dual( + 2, + ( + self: Stream.Stream, + effect: Effect.Effect<_, E2, R2> + ): Stream.Stream => concat(self, drain(fromEffect(effect))) +) + +/** @internal */ +export const onError = dual< + ( + cleanup: (cause: Cause.Cause) => Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + cleanup: (cause: Cause.Cause) => Effect.Effect + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + cleanup: (cause: Cause.Cause) => Effect.Effect + ): Stream.Stream => + pipe(self, catchAllCause((cause) => fromEffect(pipe(cleanup(cause), Effect.zipRight(Effect.failCause(cause)))))) +) + +/** @internal */ +export const onDone = dual< + ( + cleanup: () => Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + cleanup: () => Effect.Effect + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + cleanup: () => Effect.Effect + ): Stream.Stream => + new StreamImpl( + pipe(toChannel(self), core.ensuringWith((exit) => Exit.isSuccess(exit) ? cleanup() : Effect.void)) + ) +) + +/** @internal */ +export const onStart: { + <_, E2, R2>( + effect: Effect.Effect<_, E2, R2> + ): (self: Stream.Stream) => Stream.Stream + ( + self: Stream.Stream, + effect: Effect.Effect<_, E2, R2> + ): Stream.Stream +} = dual( + 2, + ( + self: Stream.Stream, + effect: Effect.Effect<_, E2, R2> + ): Stream.Stream => unwrap(Effect.as(effect, self)) +) + +/** @internal */ +export const orDie = (self: Stream.Stream): Stream.Stream => + pipe(self, orDieWith(identity)) + +/** @internal */ +export const orDieWith = dual< + (f: (e: E) => unknown) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, f: (e: E) => unknown) => Stream.Stream +>( + 2, + (self: Stream.Stream, f: (e: E) => unknown): Stream.Stream => + new StreamImpl(pipe(toChannel(self), channel.orDieWith(f))) +) + +/** @internal */ +export const orElse = dual< + ( + that: LazyArg> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + that: LazyArg> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + that: LazyArg> + ): Stream.Stream => + new StreamImpl(pipe(toChannel(self), channel.orElse(() => toChannel(that())))) +) + +/** @internal */ +export const orElseEither = dual< + ( + that: LazyArg> + ) => (self: Stream.Stream) => Stream.Stream, E2, R2 | R>, + ( + self: Stream.Stream, + that: LazyArg> + ) => Stream.Stream, E2, R2 | R> +>( + 2, + ( + self: Stream.Stream, + that: LazyArg> + ): Stream.Stream, E2, R2 | R> => + pipe(self, map(Either.left), orElse(() => pipe(that(), map(Either.right)))) +) + +/** @internal */ +export const orElseFail = dual< + (error: LazyArg) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, error: LazyArg) => Stream.Stream +>( + 2, + (self: Stream.Stream, error: LazyArg): Stream.Stream => + pipe(self, orElse(() => failSync(error))) +) + +/** @internal */ +export const orElseIfEmpty = dual< + (element: LazyArg) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, element: LazyArg) => Stream.Stream +>( + 2, + (self: Stream.Stream, element: LazyArg): Stream.Stream => + pipe(self, orElseIfEmptyChunk(() => Chunk.of(element()))) +) + +/** @internal */ +export const orElseIfEmptyChunk = dual< + (chunk: LazyArg>) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, chunk: LazyArg>) => Stream.Stream +>( + 2, + (self: Stream.Stream, chunk: LazyArg>): Stream.Stream => + pipe(self, orElseIfEmptyStream(() => new StreamImpl(core.write(chunk())))) +) + +/** @internal */ +export const orElseIfEmptyStream = dual< + ( + stream: LazyArg> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + stream: LazyArg> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + stream: LazyArg> + ): Stream.Stream => { + const writer: Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R2> = core.readWith( + { + onInput: (input: Chunk.Chunk) => { + if (Chunk.isEmpty(input)) { + return core.suspend(() => writer) + } + return pipe( + core.write(input), + channel.zipRight(channel.identityChannel, E, unknown>()) + ) + }, + onFailure: core.fail, + onDone: () => core.suspend(() => toChannel(stream())) + } + ) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(writer))) + } +) + +/** @internal */ +export const orElseSucceed = dual< + (value: LazyArg) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, value: LazyArg) => Stream.Stream +>( + 2, + (self: Stream.Stream, value: LazyArg): Stream.Stream => + pipe(self, orElse(() => sync(value))) +) + +/** @internal */ +export const paginate = (s: S, f: (s: S) => readonly [A, Option.Option]): Stream.Stream => + paginateChunk(s, (s) => { + const page = f(s) + return [Chunk.of(page[0]), page[1]] as const + }) + +/** @internal */ +export const paginateChunk = ( + s: S, + f: (s: S) => readonly [Chunk.Chunk, Option.Option] +): Stream.Stream => { + const loop = (s: S): Channel.Channel, unknown, never, unknown, unknown, unknown> => { + const page = f(s) + return Option.match(page[1], { + onNone: () => channel.zipRight(core.write(page[0]), core.void), + onSome: (s) => core.flatMap(core.write(page[0]), () => loop(s)) + }) + } + return new StreamImpl(core.suspend(() => loop(s))) +} + +/** @internal */ +export const paginateChunkEffect = ( + s: S, + f: (s: S) => Effect.Effect, Option.Option], E, R> +): Stream.Stream => { + const loop = (s: S): Channel.Channel, unknown, E, unknown, unknown, unknown, R> => + channel.unwrap( + Effect.map(f(s), ([chunk, option]) => + Option.match(option, { + onNone: () => channel.zipRight(core.write(chunk), core.void), + onSome: (s) => core.flatMap(core.write(chunk), () => loop(s)) + })) + ) + return new StreamImpl(core.suspend(() => loop(s))) +} + +/** @internal */ +export const paginateEffect = ( + s: S, + f: (s: S) => Effect.Effect], E, R> +): Stream.Stream => + paginateChunkEffect(s, (s) => pipe(f(s), Effect.map(([a, s]) => [Chunk.of(a), s] as const))) + +/** @internal */ +export const peel = dual< + ( + sink: Sink.Sink + ) => ( + self: Stream.Stream + ) => Effect.Effect<[A2, Stream.Stream], E2 | E, Scope.Scope | R2 | R>, + ( + self: Stream.Stream, + sink: Sink.Sink + ) => Effect.Effect<[A2, Stream.Stream], E2 | E, Scope.Scope | R2 | R> +>(2, ( + self: Stream.Stream, + sink: Sink.Sink +): Effect.Effect<[A2, Stream.Stream], E2 | E, Scope.Scope | R2 | R> => { + type Signal = Emit | Halt | End + const OP_EMIT = "Emit" as const + type OP_EMIT = typeof OP_EMIT + const OP_HALT = "Halt" as const + type OP_HALT = typeof OP_HALT + const OP_END = "End" as const + type OP_END = typeof OP_END + interface Emit { + readonly _tag: OP_EMIT + readonly elements: Chunk.Chunk + } + interface Halt { + readonly _tag: OP_HALT + readonly cause: Cause.Cause + } + interface End { + readonly _tag: OP_END + } + return pipe( + Deferred.make(), + Effect.flatMap((deferred) => + pipe( + Handoff.make(), + Effect.map((handoff) => { + const consumer = sink_.foldSink(sink_.collectLeftover(sink), { + onFailure: (error) => + sink_.zipRight( + sink_.fromEffect(Deferred.fail(deferred, error)), + sink_.fail(error) + ), + onSuccess: ([z, leftovers]) => { + const loop: Channel.Channel, Chunk.Chunk, E | E2, E, void, unknown> = core + .readWithCause({ + onInput: (elements) => + core.flatMap( + core.fromEffect( + Handoff.offer(handoff, { _tag: OP_EMIT, elements }) + ), + () => loop + ), + onFailure: (cause) => + channel.zipRight( + core.fromEffect(Handoff.offer(handoff, { _tag: OP_HALT, cause })), + core.failCause(cause) + ), + onDone: (_) => + channel.zipRight( + core.fromEffect(Handoff.offer(handoff, { _tag: OP_END })), + core.void + ) + }) + return sink_.fromChannel( + pipe( + core.fromEffect(Deferred.succeed(deferred, z)), + channel.zipRight(core.fromEffect( + pipe( + handoff, + Handoff.offer({ _tag: OP_EMIT, elements: leftovers }) + ) + )), + channel.zipRight(loop) + ) + ) + } + }) + + const producer: Channel.Channel, unknown, E, unknown, void, unknown> = pipe( + Handoff.take(handoff), + Effect.map((signal) => { + switch (signal._tag) { + case OP_EMIT: { + return pipe(core.write(signal.elements), core.flatMap(() => producer)) + } + case OP_HALT: { + return core.failCause(signal.cause) + } + case OP_END: { + return core.void + } + } + }), + channel.unwrap + ) + + return pipe( + self, + tapErrorCause((cause) => Deferred.failCause(deferred, cause)), + run(consumer), + Effect.forkScoped, + Effect.zipRight(Deferred.await(deferred)), + Effect.map((z) => [z, new StreamImpl(producer)] as [A2, StreamImpl]) + ) + }) + ) + ), + Effect.flatten + ) +}) + +/** @internal */ +export const partition: { + ( + refinement: Refinement, B>, + options?: { + bufferSize?: number | undefined + } + ): ( + self: Stream.Stream + ) => Effect.Effect< + [excluded: Stream.Stream, E>, satisfying: Stream.Stream], + E, + Scope.Scope | R + > + ( + predicate: Predicate, + options?: { + bufferSize?: number | undefined + } + ): ( + self: Stream.Stream + ) => Effect.Effect<[excluded: Stream.Stream, satisfying: Stream.Stream], E, Scope.Scope | R> + ( + self: Stream.Stream, + refinement: Refinement, + options?: { + bufferSize?: number | undefined + } + ): Effect.Effect< + [excluded: Stream.Stream, E>, satisfying: Stream.Stream], + E, + Scope.Scope | R + > + ( + self: Stream.Stream, + predicate: Predicate, + options?: { + bufferSize?: number | undefined + } + ): Effect.Effect<[excluded: Stream.Stream, satisfying: Stream.Stream], E, Scope.Scope | R> +} = dual( + (args) => typeof args[1] === "function", + ( + self: Stream.Stream, + predicate: Predicate, + options?: { + readonly bufferSize?: number | undefined + } + ): Effect.Effect< + [Stream.Stream, Stream.Stream], + E, + R | Scope.Scope + > => + partitionEither( + self, + (a) => Effect.succeed(predicate(a) ? Either.right(a) : Either.left(a)), + options + ) +) + +/** @internal */ +export const partitionEither = dual< + ( + predicate: (a: Types.NoInfer) => Effect.Effect, E2, R2>, + options?: { + readonly bufferSize?: number | undefined + } + ) => ( + self: Stream.Stream + ) => Effect.Effect< + [left: Stream.Stream, right: Stream.Stream], + E2 | E, + Scope.Scope | R2 | R + >, + ( + self: Stream.Stream, + predicate: (a: A) => Effect.Effect, E2, R2>, + options?: { + readonly bufferSize?: number | undefined + } + ) => Effect.Effect< + [left: Stream.Stream, right: Stream.Stream], + E2 | E, + Scope.Scope | R2 | R + > +>( + (args) => typeof args[1] === "function", + ( + self: Stream.Stream, + predicate: (a: A) => Effect.Effect, E2, R2>, + options?: { + readonly bufferSize?: number | undefined + } + ): Effect.Effect< + [left: Stream.Stream, right: Stream.Stream], + E | E2, + R | R2 | Scope.Scope + > => + pipe( + mapEffectSequential(self, predicate), + distributedWith({ + size: 2, + maximumLag: options?.bufferSize ?? 16, + decide: Either.match({ + onLeft: () => Effect.succeed((n) => n === 0), + onRight: () => Effect.succeed((n) => n === 1) + }) + }), + Effect.flatMap(([queue1, queue2]) => + Effect.succeed([ + filterMap( + flattenExitOption(fromQueue(queue1, { shutdown: true })), + (_) => + Either.match(_, { + onLeft: Option.some, + onRight: Option.none + }) + ), + filterMap( + flattenExitOption(fromQueue(queue2, { shutdown: true })), + (_) => + Either.match(_, { + onLeft: Option.none, + onRight: Option.some + }) + ) + ]) + ) + ) +) + +/** @internal */ +export const pipeThrough = dual< + ( + sink: Sink.Sink + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + sink: Sink.Sink + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + sink: Sink.Sink + ): Stream.Stream => + new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(sink_.toChannel(sink)))) +) + +/** @internal */ +export const pipeThroughChannel = dual< + ( + channel: Channel.Channel, Chunk.Chunk, E2, E, unknown, unknown, R2> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + channel: Channel.Channel, Chunk.Chunk, E2, E, unknown, unknown, R2> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + channel: Channel.Channel, Chunk.Chunk, E2, E, unknown, unknown, R2> + ): Stream.Stream => new StreamImpl(core.pipeTo(toChannel(self), channel)) +) + +/** @internal */ +export const pipeThroughChannelOrFail = dual< + ( + chan: Channel.Channel, Chunk.Chunk, E2, E, unknown, unknown, R2> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + chan: Channel.Channel, Chunk.Chunk, E2, E, unknown, unknown, R2> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + chan: Channel.Channel, Chunk.Chunk, E2, E, unknown, unknown, R2> + ): Stream.Stream => new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(chan))) +) + +/** @internal */ +export const prepend = dual< + (values: Chunk.Chunk) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, values: Chunk.Chunk) => Stream.Stream +>(2, (self, values) => + new StreamImpl( + channel.zipRight( + core.write(values as Chunk.Chunk), + toChannel(self) + ) + )) + +/** @internal */ +export const provideContext = dual< + (context: Context.Context) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, context: Context.Context) => Stream.Stream +>( + 2, + (self: Stream.Stream, context: Context.Context): Stream.Stream => + new StreamImpl(pipe(toChannel(self), core.provideContext(context))) +) + +/** @internal */ +export const provideSomeContext = dual< + (context: Context.Context) => (self: Stream.Stream) => Stream.Stream>, + (self: Stream.Stream, context: Context.Context) => Stream.Stream> +>( + 2, + (self: Stream.Stream, context: Context.Context): Stream.Stream> => + mapInputContext(self as any, Context.merge(context)) +) + +/** @internal */ +export const provideLayer = dual< + ( + layer: Layer.Layer + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + layer: Layer.Layer + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + layer: Layer.Layer + ): Stream.Stream => + new StreamImpl( + channel.unwrapScopedWith((scope) => + Layer.buildWithScope(layer, scope).pipe( + Effect.map((env) => pipe(toChannel(self), core.provideContext(env))) + ) + ) + ) +) + +/** @internal */ +export const provideService = dual< + ( + tag: Context.Tag, + resource: Types.NoInfer + ) => (self: Stream.Stream) => Stream.Stream>, + ( + self: Stream.Stream, + tag: Context.Tag, + resource: Types.NoInfer + ) => Stream.Stream> +>( + 3, + ( + self: Stream.Stream, + tag: Context.Tag, + resource: Types.NoInfer + ) => provideServiceEffect(self, tag, Effect.succeed(resource)) +) + +/** @internal */ +export const provideServiceEffect = dual< + ( + tag: Context.Tag, + effect: Effect.Effect, E2, R2> + ) => (self: Stream.Stream) => Stream.Stream>, + ( + self: Stream.Stream, + tag: Context.Tag, + effect: Effect.Effect, E2, R2> + ) => Stream.Stream> +>( + 3, + ( + self: Stream.Stream, + tag: Context.Tag, + effect: Effect.Effect, E2, R2> + ) => provideServiceStream(self, tag, fromEffect(effect)) +) + +/** @internal */ +export const provideServiceStream = dual< + ( + tag: Context.Tag, + stream: Stream.Stream, E2, R2> + ) => (self: Stream.Stream) => Stream.Stream>, + ( + self: Stream.Stream, + tag: Context.Tag, + stream: Stream.Stream, E2, R2> + ) => Stream.Stream> +>( + 3, + ( + self: Stream.Stream, + tag: Context.Tag, + stream: Stream.Stream, E2, R2> + ): Stream.Stream> => + contextWithStream((env: Context.Context>) => + flatMap( + stream, + (service) => pipe(self, provideContext(Context.add(env, tag, service) as Context.Context)) + ) + ) +) + +/** @internal */ +export const mapInputContext = dual< + ( + f: (env: Context.Context) => Context.Context + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + f: (env: Context.Context) => Context.Context + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + f: (env: Context.Context) => Context.Context + ): Stream.Stream => contextWithStream((env) => pipe(self, provideContext(f(env)))) +) + +/** @internal */ +export const provideSomeLayer = dual< + ( + layer: Layer.Layer + ) => (self: Stream.Stream) => Stream.Stream>, + ( + self: Stream.Stream, + layer: Layer.Layer + ) => Stream.Stream> +>( + 2, + ( + self: Stream.Stream, + layer: Layer.Layer + ): Stream.Stream> => + // @ts-expect-error + // @effect-diagnostics-next-line missingEffectContext:off + pipe( + self, + provideLayer(pipe(Layer.context(), Layer.merge(layer))) + ) +) + +/** @internal */ +export const range = (min: number, max: number, chunkSize = DefaultChunkSize): Stream.Stream => + suspend(() => { + if (min > max) { + return empty as Stream.Stream + } + const go = ( + min: number, + max: number, + chunkSize: number + ): Channel.Channel, unknown, never, unknown, unknown, unknown> => { + const remaining = max - min + 1 + if (remaining > chunkSize) { + return pipe( + core.write(Chunk.range(min, min + chunkSize - 1)), + core.flatMap(() => go(min + chunkSize, max, chunkSize)) + ) + } + return core.write(Chunk.range(min, min + remaining - 1)) + } + return new StreamImpl(go(min, max, chunkSize)) + }) + +/** @internal */ +export const race: { + ( + right: Stream.Stream + ): (left: Stream.Stream) => Stream.Stream + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream +} = dual( + 2, + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream => raceAll(left, right) +) + +/** @internal */ +export const raceAll = >>( + ...streams: S +): Stream.Stream< + Stream.Stream.Success, + Stream.Stream.Error, + Stream.Stream.Context +> => + Deferred.make().pipe( + Effect.map((halt) => { + let winner: number | null = null + return mergeAll( + streams.map((stream, index) => + stream.pipe( + takeWhile(() => { + if (winner === null) { + winner = index + Deferred.unsafeDone(halt, Exit.void) + return true + } + return winner === index + }), + interruptWhen( + Deferred.await(halt).pipe( + Effect.flatMap(() => winner === index ? Effect.never : Effect.void) + ) + ) + ) + ), + { concurrency: streams.length } + ) + }), + unwrap + ) + +/** @internal */ +export const rechunk = dual< + (n: number) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, n: number) => Stream.Stream +>(2, (self: Stream.Stream, n: number): Stream.Stream => + suspend(() => { + const target = Math.max(n, 1) + const process = rechunkProcess(new StreamRechunker(target), target) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(process))) + })) + +/** @internal */ +const rechunkProcess = ( + rechunker: StreamRechunker, + target: number +): Channel.Channel, Chunk.Chunk, E, E, unknown, unknown> => + core.readWithCause({ + onInput: (chunk: Chunk.Chunk) => { + if (chunk.length === target && rechunker.isEmpty()) { + return core.flatMap( + core.write(chunk), + () => rechunkProcess(rechunker, target) + ) + } + if (chunk.length > 0) { + const chunks: Array> = [] + let result: Chunk.Chunk | undefined = undefined + let index = 0 + while (index < chunk.length) { + while (index < chunk.length && result === undefined) { + result = rechunker.write(pipe(chunk, Chunk.unsafeGet(index))) + index = index + 1 + } + if (result !== undefined) { + chunks.push(result) + result = undefined + } + } + return core.flatMap( + channel.writeAll(...chunks), + () => rechunkProcess(rechunker, target) + ) + } + return core.suspend(() => rechunkProcess(rechunker, target)) + }, + onFailure: (cause) => channel.zipRight(rechunker.emitIfNotEmpty(), core.failCause(cause)), + onDone: () => rechunker.emitIfNotEmpty() + }) + +class StreamRechunker { + private builder: Array = [] + private pos = 0 + + constructor(readonly n: number) { + } + + isEmpty(): boolean { + return this.pos === 0 + } + + write(elem: A): Chunk.Chunk | undefined { + this.builder.push(elem) + this.pos += 1 + + if (this.pos === this.n) { + const result = Chunk.unsafeFromArray(this.builder) + this.builder = [] + this.pos = 0 + return result + } + + return undefined + } + + emitIfNotEmpty(): Channel.Channel, unknown, E, E, void, unknown> { + if (this.pos !== 0) { + return core.write(Chunk.unsafeFromArray(this.builder)) + } + return core.void + } +} + +/** @internal */ +export const refineOrDie = dual< + (pf: (error: E) => Option.Option) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, pf: (error: E) => Option.Option) => Stream.Stream +>( + 2, + (self: Stream.Stream, pf: (error: E) => Option.Option): Stream.Stream => + pipe(self, refineOrDieWith(pf, identity)) +) + +/** @internal */ +export const refineOrDieWith = dual< + ( + pf: (error: E) => Option.Option, + f: (error: E) => unknown + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + pf: (error: E) => Option.Option, + f: (error: E) => unknown + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + pf: (error: E) => Option.Option, + f: (error: E) => unknown + ): Stream.Stream => + new StreamImpl( + channel.catchAll(toChannel(self), (error) => + Option.match(pf(error), { + onNone: () => core.failCause(Cause.die(f(error))), + onSome: core.fail + })) + ) +) + +/** @internal */ +export const repeat = dual< + ( + schedule: Schedule.Schedule + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + schedule: Schedule.Schedule + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + schedule: Schedule.Schedule + ): Stream.Stream => + filterMap( + repeatEither(self, schedule), + (_) => + Either.match(_, { + onLeft: Option.none, + onRight: Option.some + }) + ) +) + +/** @internal */ +export const repeatEffect = (effect: Effect.Effect): Stream.Stream => + repeatEffectOption(pipe(effect, Effect.mapError(Option.some))) + +/** @internal */ +export const repeatEffectChunk = (effect: Effect.Effect, E, R>): Stream.Stream => + repeatEffectChunkOption(pipe(effect, Effect.mapError(Option.some))) + +/** @internal */ +export const repeatEffectChunkOption = ( + effect: Effect.Effect, Option.Option, R> +): Stream.Stream => + unfoldChunkEffect(effect, (effect) => + pipe( + Effect.map(effect, (chunk) => Option.some([chunk, effect] as const)), + Effect.catchAll(Option.match({ + onNone: () => Effect.succeed(Option.none()), + onSome: Effect.fail + })) + )) + +/** @internal */ +export const repeatEffectOption = (effect: Effect.Effect, R>): Stream.Stream => + repeatEffectChunkOption(pipe(effect, Effect.map(Chunk.of))) + +/** @internal */ +export const repeatEither = dual< + ( + schedule: Schedule.Schedule + ) => (self: Stream.Stream) => Stream.Stream, E, R2 | R>, + ( + self: Stream.Stream, + schedule: Schedule.Schedule + ) => Stream.Stream, E, R2 | R> +>( + 2, + ( + self: Stream.Stream, + schedule: Schedule.Schedule + ): Stream.Stream, E, R2 | R> => + repeatWith(self, schedule, { + onElement: (a): Either.Either => Either.right(a), + onSchedule: Either.left + }) +) + +/** @internal */ +export const repeatElements = dual< + ( + schedule: Schedule.Schedule + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + schedule: Schedule.Schedule + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + schedule: Schedule.Schedule + ): Stream.Stream => + filterMap( + repeatElementsWith(self, schedule, { onElement: (a) => Option.some(a), onSchedule: Option.none }), + identity + ) +) + +/** @internal */ +export const repeatElementsWith = dual< + ( + schedule: Schedule.Schedule, + options: { + readonly onElement: (a: A) => C + readonly onSchedule: (b: B) => C + } + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + schedule: Schedule.Schedule, + options: { + readonly onElement: (a: A) => C + readonly onSchedule: (b: B) => C + } + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + schedule: Schedule.Schedule, + options: { + readonly onElement: (a: A) => C + readonly onSchedule: (b: B) => C + } + ): Stream.Stream => { + const driver = pipe( + Schedule.driver(schedule), + Effect.map((driver) => { + const feed = ( + input: Chunk.Chunk + ): Channel.Channel, Chunk.Chunk, E, E, void, unknown, R2> => + Option.match(Chunk.head(input), { + onNone: () => loop, + onSome: (a) => + channel.zipRight( + core.write(Chunk.of(options.onElement(a))), + step(pipe(input, Chunk.drop(1)), a) + ) + }) + const step = ( + input: Chunk.Chunk, + a: A + ): Channel.Channel, Chunk.Chunk, E, E, void, unknown, R2> => { + const advance = pipe( + driver.next(a), + Effect.as(pipe(core.write(Chunk.of(options.onElement(a))), core.flatMap(() => step(input, a)))) + ) + const reset: Effect.Effect< + Channel.Channel, Chunk.Chunk, E, E, void, unknown, R2>, + never, + R2 + > = pipe( + driver.last, + Effect.orDie, + Effect.flatMap((b) => + pipe( + driver.reset, + Effect.map(() => + pipe( + core.write(Chunk.of(options.onSchedule(b))), + channel.zipRight(feed(input)) + ) + ) + ) + ) + ) + return pipe(advance, Effect.orElse(() => reset), channel.unwrap) + } + const loop: Channel.Channel, Chunk.Chunk, E, E, void, unknown, R2> = core.readWith({ + onInput: feed, + onFailure: core.fail, + onDone: () => core.void + }) + return loop + }), + channel.unwrap + ) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(driver))) + } +) + +/** @internal */ +export const repeatValue = (value: A): Stream.Stream => + new StreamImpl( + channel.repeated(core.write(Chunk.of(value))) + ) + +/** @internal */ +export const repeatWith = dual< + ( + schedule: Schedule.Schedule, + options: { + readonly onElement: (a: A) => C + readonly onSchedule: (b: B) => C + } + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + schedule: Schedule.Schedule, + options: { + readonly onElement: (a: A) => C + readonly onSchedule: (b: B) => C + } + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + schedule: Schedule.Schedule, + options: { + readonly onElement: (a: A) => C + readonly onSchedule: (b: B) => C + } + ): Stream.Stream => { + return pipe( + Schedule.driver(schedule), + Effect.map((driver) => { + const provideLastIterationInfo = provideServiceEffect( + Schedule.CurrentIterationMetadata, + Ref.get(driver.iterationMeta) + ) + + const process = pipe(self, provideLastIterationInfo, map(options.onElement), toChannel) + const loop: Channel.Channel, unknown, E, unknown, void, unknown, R | R2> = channel.unwrap( + Effect.match( + driver.next(void 0), + { + onFailure: () => core.void, + onSuccess: (output) => + core.flatMap( + process, + () => channel.zipRight(core.write(Chunk.of(options.onSchedule(output))), loop) + ) + } + ) + ) + return new StreamImpl(channel.zipRight(process, loop)) + }), + unwrap + ) + } +) + +const repeatWithSchedule = ( + value: A, + schedule: Schedule.Schedule +): Stream.Stream => repeatEffectWithSchedule(Effect.succeed(value), schedule) + +/** @internal */ +export const repeatEffectWithSchedule = ( + effect: Effect.Effect, + schedule: Schedule.Schedule +): Stream.Stream => + flatMap( + fromEffect(Effect.zip(effect, Schedule.driver(schedule))), + ([a, driver]) => { + const provideLastIterationInfo = Effect.provideServiceEffect( + Schedule.CurrentIterationMetadata, + Ref.get(driver.iterationMeta) + ) + return concat( + succeed(a), + unfoldEffect(a, (s) => + Effect.matchEffect(driver.next(s as A0), { + onFailure: Effect.succeed, + onSuccess: () => + Effect.map(provideLastIterationInfo(effect), (nextA) => Option.some([nextA, nextA] as const)) + })) + ) + } + ) + +/** @internal */ +export const retry = dual< + ( + policy: Schedule.Schedule, R2> + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + policy: Schedule.Schedule, R2> + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + policy: Schedule.Schedule, R2> + ): Stream.Stream => + Schedule.driver(policy).pipe( + Effect.map((driver) => { + const provideLastIterationInfo = provideServiceEffect( + Schedule.CurrentIterationMetadata, + Ref.get(driver.iterationMeta) + ) + + const loop: Channel.Channel< + Chunk.Chunk, + unknown, + E, + unknown, + unknown, + unknown, + R2 | R + > = toChannel(provideLastIterationInfo(self)).pipe( + channel.mapOutEffect((out) => Effect.as(driver.reset, out)), + channel.catchAll((error) => + driver.next(error).pipe( + Effect.match({ + onFailure: () => core.fail(error), + onSuccess: () => loop + }), + channel.unwrap + ) + ) + ) + return loop + }), + channel.unwrap, + fromChannel + ) +) + +/** @internal */ +export const withExecutionPlan: { + ( + policy: ExecutionPlan<{ + provides: Provides + input: Input + error: PolicyE + requirements: R2 + }>, + options?: { + readonly preventFallbackOnPartialStream?: boolean | undefined + } + ): ( + self: Stream.Stream + ) => Stream.Stream> + ( + self: Stream.Stream, + policy: ExecutionPlan<{ + provides: Provides + input: Input + error: PolicyE + requirements: R2 + }>, + options?: { + readonly preventFallbackOnPartialStream?: boolean | undefined + } + ): Stream.Stream> +} = dual((args) => isStream(args[0]), ( + self: Stream.Stream, + policy: ExecutionPlan<{ + provides: Provides + input: Input + error: PolicyE + requirements: R2 + }>, + options?: { + readonly preventFallbackOnPartialStream?: boolean | undefined + } +): Stream.Stream> => + suspend(() => { + const preventFallbackOnPartialStream = options?.preventFallbackOnPartialStream ?? false + let i = 0 + let lastError = Option.none() + const loop: Stream.Stream< + A, + E | PolicyE, + R2 | Exclude + > = suspend(() => { + const step = policy.steps[i++] + if (!step) { + return fail(Option.getOrThrow(lastError)) + } + + let nextStream: Stream.Stream> = Context.isContext(step.provide) + ? provideSomeContext(self, step.provide) + : provideSomeLayer(self, step.provide as Layer.Layer) + let receivedElements = false + + if (Option.isSome(lastError)) { + const error = lastError.value + let attempted = false + const wrapped = nextStream + // ensure the schedule is applied at least once + nextStream = suspend(() => { + if (attempted) return wrapped + attempted = true + return fail(error) + }) + nextStream = scheduleDefectRefail(retry(nextStream, internalExecutionPlan.scheduleFromStep(step, false)!)) + } else { + const schedule = internalExecutionPlan.scheduleFromStep(step, true) + nextStream = schedule ? scheduleDefectRefail(retry(nextStream, schedule)) : nextStream + } + + return catchAll( + preventFallbackOnPartialStream ? + mapChunks(nextStream, (chunk) => { + receivedElements = true + return chunk + }) : + nextStream, + (error) => { + if (preventFallbackOnPartialStream && receivedElements) { + return fail(error) + } + lastError = Option.some(error) + return loop + } + ) + }) + return loop + })) + +const scheduleDefectRefail = (self: Stream.Stream) => + catchAllCause(self, (cause) => failCause(InternalSchedule.scheduleDefectRefailCause(cause))) + +/** @internal */ +export const run = dual< + ( + sink: Sink.Sink + ) => ( + self: Stream.Stream + ) => Effect.Effect, + ( + self: Stream.Stream, + sink: Sink.Sink + ) => Effect.Effect +>(2, ( + self: Stream.Stream, + sink: Sink.Sink +): Effect.Effect => + toChannel(self).pipe( + channel.pipeToOrFail(sink_.toChannel(sink)), + channel.runDrain + )) + +/** @internal */ +export const runCollect = ( + self: Stream.Stream +): Effect.Effect, E, R> => run(self, sink_.collectAll()) + +/** @internal */ +export const runCount = (self: Stream.Stream): Effect.Effect => run(self, sink_.count) + +/** @internal */ +export const runDrain = (self: Stream.Stream): Effect.Effect => run(self, sink_.drain) + +/** @internal */ +export const runFold = dual< + ( + s: S, + f: (s: S, a: A) => S + ) => ( + self: Stream.Stream + ) => Effect.Effect, + ( + self: Stream.Stream, + s: S, + f: (s: S, a: A) => S + ) => Effect.Effect +>( + 3, + (self: Stream.Stream, s: S, f: (s: S, a: A) => S): Effect.Effect => + runFoldWhile(self, s, constTrue, f) +) + +/** @internal */ +export const runFoldEffect = dual< + ( + s: S, + f: (s: S, a: A) => Effect.Effect + ) => (self: Stream.Stream) => Effect.Effect, + ( + self: Stream.Stream, + s: S, + f: (s: S, a: A) => Effect.Effect + ) => Effect.Effect +>(3, ( + self: Stream.Stream, + s: S, + f: (s: S, a: A) => Effect.Effect +): Effect.Effect => runFoldWhileEffect(self, s, constTrue, f)) + +/** @internal */ +export const runFoldScoped = dual< + (s: S, f: (s: S, a: A) => S) => (self: Stream.Stream) => Effect.Effect, + (self: Stream.Stream, s: S, f: (s: S, a: A) => S) => Effect.Effect +>( + 3, + (self: Stream.Stream, s: S, f: (s: S, a: A) => S): Effect.Effect => + pipe(self, runFoldWhileScoped(s, constTrue, f)) +) + +/** @internal */ +export const runFoldScopedEffect = dual< + ( + s: S, + f: (s: S, a: A) => Effect.Effect + ) => (self: Stream.Stream) => Effect.Effect, + ( + self: Stream.Stream, + s: S, + f: (s: S, a: A) => Effect.Effect + ) => Effect.Effect +>(3, ( + self: Stream.Stream, + s: S, + f: (s: S, a: A) => Effect.Effect +): Effect.Effect => pipe(self, runFoldWhileScopedEffect(s, constTrue, f))) + +/** @internal */ +export const runFoldWhile = dual< + ( + s: S, + cont: Predicate, + f: (s: S, a: A) => S + ) => ( + self: Stream.Stream + ) => Effect.Effect, + ( + self: Stream.Stream, + s: S, + cont: Predicate, + f: (s: S, a: A) => S + ) => Effect.Effect +>(4, ( + self: Stream.Stream, + s: S, + cont: Predicate, + f: (s: S, a: A) => S +): Effect.Effect => run(self, sink_.fold(s, cont, f))) + +/** @internal */ +export const runFoldWhileEffect = dual< + ( + s: S, + cont: Predicate, + f: (s: S, a: A) => Effect.Effect + ) => ( + self: Stream.Stream + ) => Effect.Effect, + ( + self: Stream.Stream, + s: S, + cont: Predicate, + f: (s: S, a: A) => Effect.Effect + ) => Effect.Effect +>(4, ( + self: Stream.Stream, + s: S, + cont: Predicate, + f: (s: S, a: A) => Effect.Effect +): Effect.Effect => run(self, sink_.foldEffect(s, cont, f))) + +/** @internal */ +export const runFoldWhileScoped = dual< + ( + s: S, + cont: Predicate, + f: (s: S, a: A) => S + ) => (self: Stream.Stream) => Effect.Effect, + ( + self: Stream.Stream, + s: S, + cont: Predicate, + f: (s: S, a: A) => S + ) => Effect.Effect +>(4, ( + self: Stream.Stream, + s: S, + cont: Predicate, + f: (s: S, a: A) => S +): Effect.Effect => pipe(self, runScoped(sink_.fold(s, cont, f)))) + +/** @internal */ +export const runFoldWhileScopedEffect = dual< + ( + s: S, + cont: Predicate, + f: (s: S, a: A) => Effect.Effect + ) => (self: Stream.Stream) => Effect.Effect, + ( + self: Stream.Stream, + s: S, + cont: Predicate, + f: (s: S, a: A) => Effect.Effect + ) => Effect.Effect +>(4, ( + self: Stream.Stream, + s: S, + cont: Predicate, + f: (s: S, a: A) => Effect.Effect +): Effect.Effect => pipe(self, runScoped(sink_.foldEffect(s, cont, f)))) + +/** @internal */ +export const runForEach = dual< + ( + f: (a: A) => Effect.Effect + ) => ( + self: Stream.Stream + ) => Effect.Effect, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect + ) => Effect.Effect +>(2, ( + self: Stream.Stream, + f: (a: A) => Effect.Effect +): Effect.Effect => run(self, sink_.forEach(f))) + +/** @internal */ +export const runForEachChunk = dual< + ( + f: (a: Chunk.Chunk) => Effect.Effect + ) => ( + self: Stream.Stream + ) => Effect.Effect, + ( + self: Stream.Stream, + f: (a: Chunk.Chunk) => Effect.Effect + ) => Effect.Effect +>(2, ( + self: Stream.Stream, + f: (a: Chunk.Chunk) => Effect.Effect +): Effect.Effect => run(self, sink_.forEachChunk(f))) + +/** @internal */ +export const runForEachChunkScoped = dual< + ( + f: (a: Chunk.Chunk) => Effect.Effect + ) => (self: Stream.Stream) => Effect.Effect, + ( + self: Stream.Stream, + f: (a: Chunk.Chunk) => Effect.Effect + ) => Effect.Effect +>(2, ( + self: Stream.Stream, + f: (a: Chunk.Chunk) => Effect.Effect +): Effect.Effect => pipe(self, runScoped(sink_.forEachChunk(f)))) + +/** @internal */ +export const runForEachScoped = dual< + ( + f: (a: A) => Effect.Effect + ) => (self: Stream.Stream) => Effect.Effect, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect + ) => Effect.Effect +>(2, ( + self: Stream.Stream, + f: (a: A) => Effect.Effect +): Effect.Effect => pipe(self, runScoped(sink_.forEach(f)))) + +/** @internal */ +export const runForEachWhile = dual< + ( + f: (a: A) => Effect.Effect + ) => ( + self: Stream.Stream + ) => Effect.Effect, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect + ) => Effect.Effect +>(2, ( + self: Stream.Stream, + f: (a: A) => Effect.Effect +): Effect.Effect => run(self, sink_.forEachWhile(f))) + +/** @internal */ +export const runForEachWhileScoped = dual< + ( + f: (a: A) => Effect.Effect + ) => (self: Stream.Stream) => Effect.Effect, + ( + self: Stream.Stream, + f: (a: A) => Effect.Effect + ) => Effect.Effect +>(2, ( + self: Stream.Stream, + f: (a: A) => Effect.Effect +): Effect.Effect => pipe(self, runScoped(sink_.forEachWhile(f)))) + +/** @internal */ +export const runHead = ( + self: Stream.Stream +): Effect.Effect, E, R> => run(self, sink_.head()) + +/** @internal */ +export const runIntoPubSub = dual< + ( + pubsub: PubSub.PubSub> + ) => (self: Stream.Stream) => Effect.Effect>, + ( + self: Stream.Stream, + pubsub: PubSub.PubSub> + ) => Effect.Effect> +>( + 2, + ( + self: Stream.Stream, + pubsub: PubSub.PubSub> + ): Effect.Effect> => pipe(self, runIntoQueue(pubsub)) +) + +/** @internal */ +export const runIntoPubSubScoped = dual< + ( + pubsub: PubSub.PubSub> + ) => (self: Stream.Stream) => Effect.Effect, + ( + self: Stream.Stream, + pubsub: PubSub.PubSub> + ) => Effect.Effect +>(2, ( + self: Stream.Stream, + pubsub: PubSub.PubSub> +): Effect.Effect => pipe(self, runIntoQueueScoped(pubsub))) + +/** @internal */ +export const runIntoQueue = dual< + ( + queue: Queue.Enqueue> + ) => (self: Stream.Stream) => Effect.Effect>, + ( + self: Stream.Stream, + queue: Queue.Enqueue> + ) => Effect.Effect> +>( + 2, + ( + self: Stream.Stream, + queue: Queue.Enqueue> + ): Effect.Effect> => pipe(self, runIntoQueueScoped(queue), Effect.scoped) +) + +/** @internal */ +export const runIntoQueueElementsScoped = dual< + ( + queue: Queue.Enqueue>> + ) => (self: Stream.Stream) => Effect.Effect, + ( + self: Stream.Stream, + queue: Queue.Enqueue>> + ) => Effect.Effect +>(2, ( + self: Stream.Stream, + queue: Queue.Enqueue>> +): Effect.Effect => { + const writer: Channel.Channel>, Chunk.Chunk, never, E, unknown, unknown, R> = core + .readWithCause({ + onInput: (input: Chunk.Chunk) => + core.flatMap( + core.fromEffect(Queue.offerAll(queue, Chunk.map(input, Exit.succeed))), + () => writer + ), + onFailure: (cause) => core.fromEffect(Queue.offer(queue, Exit.failCause(Cause.map(cause, Option.some)))), + onDone: () => core.fromEffect(Queue.offer(queue, Exit.fail(Option.none()))) + }) + return pipe( + core.pipeTo(toChannel(self), writer), + channel.drain, + channel.runScoped, + Effect.asVoid + ) +}) + +/** @internal */ +export const runIntoQueueScoped = dual< + ( + queue: Queue.Enqueue> + ) => (self: Stream.Stream) => Effect.Effect, + ( + self: Stream.Stream, + queue: Queue.Enqueue> + ) => Effect.Effect +>(2, ( + self: Stream.Stream, + queue: Queue.Enqueue> +): Effect.Effect => { + const writer: Channel.Channel, Chunk.Chunk, never, E, unknown, unknown, R> = core + .readWithCause({ + onInput: (input: Chunk.Chunk) => core.flatMap(core.write(InternalTake.chunk(input)), () => writer), + onFailure: (cause) => core.write(InternalTake.failCause(cause)), + onDone: () => core.write(InternalTake.end) + }) + return pipe( + core.pipeTo(toChannel(self), writer), + channel.mapOutEffect((take) => Queue.offer(queue, take)), + channel.drain, + channel.runScoped, + Effect.asVoid + ) +}) + +/** @internal */ +export const runLast = ( + self: Stream.Stream +): Effect.Effect, E, R> => run(self, sink_.last()) + +/** @internal */ +export const runScoped = dual< + ( + sink: Sink.Sink + ) => (self: Stream.Stream) => Effect.Effect, + ( + self: Stream.Stream, + sink: Sink.Sink + ) => Effect.Effect +>(2, ( + self: Stream.Stream, + sink: Sink.Sink +): Effect.Effect => + pipe( + toChannel(self), + channel.pipeToOrFail(sink_.toChannel(sink)), + channel.drain, + channel.runScoped + )) + +/** @internal */ +export const runSum = (self: Stream.Stream): Effect.Effect => run(self, sink_.sum) + +/** @internal */ +export const scan = dual< + (s: S, f: (s: S, a: A) => S) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, s: S, f: (s: S, a: A) => S) => Stream.Stream +>( + 3, + (self: Stream.Stream, s: S, f: (s: S, a: A) => S): Stream.Stream => + pipe(self, scanEffect(s, (s, a) => Effect.succeed(f(s, a)))) +) + +/** @internal */ +export const scanReduce = dual< + (f: (a2: A2 | A, a: A) => A2) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, f: (a2: A2 | A, a: A) => A2) => Stream.Stream +>( + 2, + (self: Stream.Stream, f: (a2: A | A2, a: A) => A2): Stream.Stream => + pipe(self, scanReduceEffect((a2, a) => Effect.succeed(f(a2, a)))) +) + +/** @internal */ +export const scanReduceEffect = dual< + ( + f: (a2: A2 | A, a: A) => Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + f: (a2: A2 | A, a: A) => Effect.Effect + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + f: (a2: A | A2, a: A) => Effect.Effect + ): Stream.Stream => + pipe( + self, + mapAccumEffect, A, A | A2, E2, R2>(Option.none() as Option.Option, (option, a) => { + switch (option._tag) { + case "None": { + return Effect.succeed([Option.some(a), a] as const) + } + case "Some": { + return pipe( + f(option.value, a), + Effect.map((b) => [Option.some(b), b] as const) + ) + } + } + }) + ) +) + +/** @internal */ +export const schedule = dual< + ( + schedule: Schedule.Schedule + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + schedule: Schedule.Schedule + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + schedule: Schedule.Schedule + ): Stream.Stream => + filterMap( + scheduleWith(self, schedule, { onElement: Option.some, onSchedule: Option.none }), + identity + ) +) + +/** @internal */ +export const scheduleWith = dual< + ( + schedule: Schedule.Schedule, + options: { + readonly onElement: (a: A) => C + readonly onSchedule: (b: B) => C + } + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + schedule: Schedule.Schedule, + options: { + readonly onElement: (a: A) => C + readonly onSchedule: (b: B) => C + } + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + schedule: Schedule.Schedule, + options: { + readonly onElement: (a: A) => C + readonly onSchedule: (b: B) => C + } + ): Stream.Stream => { + const loop = ( + driver: Schedule.ScheduleDriver, + iterator: Iterator + ): Channel.Channel, Chunk.Chunk, E, E, unknown, unknown, R2> => { + const next = iterator.next() + if (next.done) { + return core.readWithCause({ + onInput: (chunk: Chunk.Chunk) => loop(driver, chunk[Symbol.iterator]()), + onFailure: core.failCause, + onDone: core.succeedNow + }) + } + return channel.unwrap( + Effect.matchEffect(driver.next(next.value as A0), { + onFailure: () => + pipe( + driver.last, + Effect.orDie, + Effect.map((b) => + pipe( + core.write(Chunk.make(options.onElement(next.value), options.onSchedule(b))), + core.flatMap(() => loop(driver, iterator)) + ) + ), + Effect.zipLeft(driver.reset) + ), + onSuccess: () => + Effect.succeed(pipe( + core.write(Chunk.of(options.onElement(next.value))), + core.flatMap(() => loop(driver, iterator)) + )) + }) + ) + } + return new StreamImpl( + pipe( + core.fromEffect(Schedule.driver(schedule)), + core.flatMap((driver) => + pipe( + toChannel(self), + core.pipeTo(loop(driver, Chunk.empty()[Symbol.iterator]())) + ) + ) + ) + ) + } +) + +/** @internal */ +export const scanEffect = dual< + ( + s: S, + f: (s: S, a: A) => Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + s: S, + f: (s: S, a: A) => Effect.Effect + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + s: S, + f: (s: S, a: A) => Effect.Effect + ): Stream.Stream => + new StreamImpl( + pipe( + core.write(Chunk.of(s)), + core.flatMap(() => + toChannel(pipe( + self, + mapAccumEffect(s, (s, a) => pipe(f(s, a), Effect.map((s) => [s, s]))) + )) + ) + ) + ) +) + +/** @internal */ +export const scoped = ( + effect: Effect.Effect +): Stream.Stream> => + new StreamImpl(channel.ensuring(channel.scoped(pipe(effect, Effect.map(Chunk.of))), Effect.void)) + +/** @internal */ +export const scopedWith = ( + f: (scope: Scope.Scope) => Effect.Effect +): Stream.Stream => + new StreamImpl(channel.scopedWith((scope) => + f(scope).pipe( + Effect.map(Chunk.of) + ) + )) + +/** @internal */ +export const some = (self: Stream.Stream, E, R>): Stream.Stream, R> => + pipe(self, mapError(Option.some), someOrFail(() => Option.none())) + +/** @internal */ +export const someOrElse = dual< + (fallback: LazyArg) => (self: Stream.Stream, E, R>) => Stream.Stream, + (self: Stream.Stream, E, R>, fallback: LazyArg) => Stream.Stream +>( + 2, + (self: Stream.Stream, E, R>, fallback: LazyArg): Stream.Stream => + pipe(self, map(Option.getOrElse(fallback))) +) + +/** @internal */ +export const someOrFail = dual< + (error: LazyArg) => (self: Stream.Stream, E, R>) => Stream.Stream, + (self: Stream.Stream, E, R>, error: LazyArg) => Stream.Stream +>( + 2, + (self: Stream.Stream, E, R>, error: LazyArg): Stream.Stream => + mapEffectSequential( + self, + Option.match({ + onNone: () => Effect.failSync(error), + onSome: Effect.succeed + }) + ) +) + +/** @internal */ +export const sliding = dual< + ( + chunkSize: number + ) => (self: Stream.Stream) => Stream.Stream, E, R>, + (self: Stream.Stream, chunkSize: number) => Stream.Stream, E, R> +>( + 2, + (self: Stream.Stream, chunkSize: number): Stream.Stream, E, R> => + slidingSize(self, chunkSize, 1) +) + +/** @internal */ +export const slidingSize = dual< + ( + chunkSize: number, + stepSize: number + ) => (self: Stream.Stream) => Stream.Stream, E, R>, + (self: Stream.Stream, chunkSize: number, stepSize: number) => Stream.Stream, E, R> +>( + 3, + (self: Stream.Stream, chunkSize: number, stepSize: number): Stream.Stream, E, R> => { + if (chunkSize <= 0 || stepSize <= 0) { + return die( + new Cause.IllegalArgumentException("Invalid bounds - `chunkSize` and `stepSize` must be greater than zero") + ) + } + return new StreamImpl(core.suspend(() => { + const queue = new RingBuffer(chunkSize) + const emitOnStreamEnd = ( + queueSize: number, + channelEnd: Channel.Channel>, Chunk.Chunk, E, E, unknown, unknown> + ) => { + if (queueSize < chunkSize) { + const items = queue.toChunk() + const result = Chunk.isEmpty(items) ? Chunk.empty>() : Chunk.of(items) + return pipe(core.write(result), core.flatMap(() => channelEnd)) + } + const lastEmitIndex = queueSize - (queueSize - chunkSize) % stepSize + if (lastEmitIndex === queueSize) { + return channelEnd + } + const leftovers = queueSize - (lastEmitIndex - chunkSize + stepSize) + const lastItems = pipe(queue.toChunk(), Chunk.takeRight(leftovers)) + const result = Chunk.isEmpty(lastItems) ? Chunk.empty>() : Chunk.of(lastItems) + return pipe(core.write(result), core.flatMap(() => channelEnd)) + } + const reader = ( + queueSize: number + ): Channel.Channel>, Chunk.Chunk, E, E, unknown, unknown> => + core.readWithCause({ + onInput: (input: Chunk.Chunk) => + core.flatMap( + core.write( + Chunk.filterMap(input, (element, index) => { + queue.put(element) + const currentIndex = queueSize + index + 1 + if (currentIndex < chunkSize || (currentIndex - chunkSize) % stepSize > 0) { + return Option.none() + } + return Option.some(queue.toChunk()) + }) + ), + () => reader(queueSize + input.length) + ), + onFailure: (cause) => emitOnStreamEnd(queueSize, core.failCause(cause)), + onDone: () => emitOnStreamEnd(queueSize, core.void) + }) + return pipe(toChannel(self), core.pipeTo(reader(0))) + })) + } +) + +/** @internal */ +export const split: { + ( + refinement: Refinement, B> + ): (self: Stream.Stream) => Stream.Stream>, E, R> + ( + predicate: Predicate> + ): (self: Stream.Stream) => Stream.Stream, E, R> + ( + self: Stream.Stream, + refinement: Refinement + ): Stream.Stream>, E, R> + (self: Stream.Stream, predicate: Predicate): Stream.Stream, E, R> +} = dual( + 2, + ( + self: Stream.Stream, + predicate: Predicate + ): Stream.Stream, E, R> => { + const split = ( + leftovers: Chunk.Chunk, + input: Chunk.Chunk + ): Channel.Channel>, Chunk.Chunk, E, E, unknown, unknown, R> => { + const [chunk, remaining] = pipe(leftovers, Chunk.appendAll(input), Chunk.splitWhere(predicate)) + if (Chunk.isEmpty(chunk) || Chunk.isEmpty(remaining)) { + return loop(pipe(chunk, Chunk.appendAll(pipe(remaining, Chunk.drop(1))))) + } + return pipe( + core.write(Chunk.of(chunk)), + core.flatMap(() => split(Chunk.empty(), pipe(remaining, Chunk.drop(1)))) + ) + } + const loop = ( + leftovers: Chunk.Chunk + ): Channel.Channel>, Chunk.Chunk, E, E, unknown, unknown, R> => + core.readWith({ + onInput: (input: Chunk.Chunk) => split(leftovers, input), + onFailure: core.fail, + onDone: () => { + if (Chunk.isEmpty(leftovers)) { + return core.void + } + if (Option.isNone(pipe(leftovers, Chunk.findFirst(predicate)))) { + return channel.zipRight(core.write(Chunk.of(leftovers)), core.void) + } + return channel.zipRight( + split(Chunk.empty(), leftovers), + core.void + ) + } + }) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(loop(Chunk.empty())))) + } +) + +/** @internal */ +export const splitOnChunk = dual< + (delimiter: Chunk.Chunk) => (self: Stream.Stream) => Stream.Stream, E, R>, + (self: Stream.Stream, delimiter: Chunk.Chunk) => Stream.Stream, E, R> +>(2, (self: Stream.Stream, delimiter: Chunk.Chunk): Stream.Stream, E, R> => { + const next = ( + leftover: Option.Option>, + delimiterIndex: number + ): Channel.Channel>, Chunk.Chunk, E, E, unknown, unknown, R> => + core.readWithCause({ + onInput: (inputChunk: Chunk.Chunk) => { + let buffer: Array> | undefined + const [carry, delimiterCursor] = pipe( + inputChunk, + Chunk.reduce( + [pipe(leftover, Option.getOrElse(() => Chunk.empty())), delimiterIndex] as const, + ([carry, delimiterCursor], a) => { + const concatenated = pipe(carry, Chunk.append(a)) + if ( + delimiterCursor < delimiter.length && + Equal.equals(a, pipe(delimiter, Chunk.unsafeGet(delimiterCursor))) + ) { + if (delimiterCursor + 1 === delimiter.length) { + if (buffer === undefined) { + buffer = [] + } + buffer.push(pipe(concatenated, Chunk.take(concatenated.length - delimiter.length))) + return [Chunk.empty(), 0] as const + } + return [concatenated, delimiterCursor + 1] as const + } + return [concatenated, Equal.equals(a, pipe(delimiter, Chunk.unsafeGet(0))) ? 1 : 0] as const + } + ) + ) + const output = buffer === undefined ? Chunk.empty>() : Chunk.unsafeFromArray(buffer) + return core.flatMap( + core.write(output), + () => next(Chunk.isNonEmpty(carry) ? Option.some(carry) : Option.none(), delimiterCursor) + ) + }, + onFailure: (cause) => + Option.match(leftover, { + onNone: () => core.failCause(cause), + onSome: (chunk) => + channel.zipRight( + core.write(Chunk.of(chunk)), + core.failCause(cause) + ) + }), + onDone: (done) => + Option.match(leftover, { + onNone: () => core.succeed(done), + onSome: (chunk) => channel.zipRight(core.write(Chunk.of(chunk)), core.succeed(done)) + }) + }) + return new StreamImpl(pipe(toChannel(self), core.pipeTo(next(Option.none(), 0)))) +}) + +/** @internal */ +export const splitLines = (self: Stream.Stream): Stream.Stream => + pipeThroughChannel(self, channel.splitLines()) + +/** @internal */ +export const succeed = (value: A): Stream.Stream => fromChunk(Chunk.of(value)) + +/** @internal */ +export const sync = (evaluate: LazyArg): Stream.Stream => suspend(() => fromChunk(Chunk.of(evaluate()))) + +/** @internal */ +export const suspend = (stream: LazyArg>): Stream.Stream => + new StreamImpl(core.suspend(() => toChannel(stream()))) + +/** @internal */ +export const take = dual< + (n: number) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, n: number) => Stream.Stream +>(2, (self: Stream.Stream, n: number): Stream.Stream => { + if (!Number.isInteger(n)) { + return die(new Cause.IllegalArgumentException(`${n} must be an integer`)) + } + const loop = (n: number): Channel.Channel, Chunk.Chunk, never, never, unknown, unknown> => + core.readWith({ + onInput: (input: Chunk.Chunk) => { + const taken = pipe(input, Chunk.take(Math.min(n, Number.POSITIVE_INFINITY))) + const leftover = Math.max(0, n - taken.length) + const more = leftover > 0 + if (more) { + return pipe(core.write(taken), core.flatMap(() => loop(leftover))) + } + return core.write(taken) + }, + onFailure: core.fail, + onDone: core.succeed + }) + return new StreamImpl( + pipe( + toChannel(self), + channel.pipeToOrFail(0 < n ? loop(n) : core.void) + ) + ) +}) + +/** @internal */ +export const takeRight = dual< + (n: number) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, n: number) => Stream.Stream +>(2, (self: Stream.Stream, n: number): Stream.Stream => { + if (n <= 0) { + return empty + } + return new StreamImpl( + pipe( + Effect.succeed(new RingBuffer(n)), + Effect.map((queue) => { + const reader: Channel.Channel, Chunk.Chunk, E, E, void, unknown> = core.readWith({ + onInput: (input: Chunk.Chunk) => { + for (const element of input) { + queue.put(element) + } + return reader + }, + onFailure: core.fail, + onDone: () => pipe(core.write(queue.toChunk()), channel.zipRight(core.void)) + }) + return pipe(toChannel(self), core.pipeTo(reader)) + }), + channel.unwrap + ) + ) +}) + +/** @internal */ +export const takeUntil: { + (predicate: Predicate>): (self: Stream.Stream) => Stream.Stream + (self: Stream.Stream, predicate: Predicate): Stream.Stream +} = dual(2, (self: Stream.Stream, predicate: Predicate): Stream.Stream => { + const loop: Channel.Channel, Chunk.Chunk, never, never, unknown, unknown> = core.readWith({ + onInput: (input: Chunk.Chunk) => { + const taken = pipe(input, Chunk.takeWhile((a) => !predicate(a))) + const last = pipe(input, Chunk.drop(taken.length), Chunk.take(1)) + if (Chunk.isEmpty(last)) { + return pipe(core.write(taken), core.flatMap(() => loop)) + } + return core.write(pipe(taken, Chunk.appendAll(last))) + }, + onFailure: core.fail, + onDone: core.succeed + }) + return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(loop))) +}) + +/** @internal */ +export const takeUntilEffect: { + ( + predicate: (a: Types.NoInfer) => Effect.Effect + ): (self: Stream.Stream) => Stream.Stream + ( + self: Stream.Stream, + predicate: (a: A) => Effect.Effect + ): Stream.Stream +} = dual( + 2, + ( + self: Stream.Stream, + predicate: (a: A) => Effect.Effect + ): Stream.Stream => { + const loop = ( + iterator: Iterator + ): Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R2> => { + const next = iterator.next() + if (next.done) { + return core.readWithCause({ + onInput: (elem) => loop(elem[Symbol.iterator]()), + onFailure: core.failCause, + onDone: core.succeed + }) + } + return pipe( + predicate(next.value), + Effect.map((bool) => + bool ? + core.write(Chunk.of(next.value)) : + pipe( + core.write(Chunk.of(next.value)), + core.flatMap(() => loop(iterator)) + ) + ), + channel.unwrap + ) + } + return new StreamImpl(pipe(toChannel(self), core.pipeTo(loop(Chunk.empty()[Symbol.iterator]())))) + } +) + +/** @internal */ +export const takeWhile: { + ( + refinement: Refinement, B> + ): (self: Stream.Stream) => Stream.Stream + (predicate: Predicate>): (self: Stream.Stream) => Stream.Stream + (self: Stream.Stream, refinement: Refinement): Stream.Stream + (self: Stream.Stream, predicate: Predicate): Stream.Stream +} = dual(2, (self: Stream.Stream, predicate: Predicate): Stream.Stream => { + const loop: Channel.Channel, Chunk.Chunk, never, never, unknown, unknown> = core.readWith({ + onInput: (input: Chunk.Chunk) => { + const taken = pipe(input, Chunk.takeWhile(predicate)) + const more = taken.length === input.length + if (more) { + return pipe(core.write(taken), core.flatMap(() => loop)) + } + return core.write(taken) + }, + onFailure: core.fail, + onDone: core.succeed + }) + return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(loop))) +}) + +/** @internal */ +export const tap: { + ( + f: (a: Types.NoInfer) => Effect.Effect + ): (self: Stream.Stream) => Stream.Stream + ( + self: Stream.Stream, + f: (a: Types.NoInfer) => Effect.Effect + ): Stream.Stream +} = dual( + 2, + ( + self: Stream.Stream, + f: (a: Types.NoInfer) => Effect.Effect + ): Stream.Stream => mapEffectSequential(self, (a) => Effect.as(f(a), a)) +) + +/** @internal */ +export const tapBoth: { + ( + options: { + readonly onFailure: (e: Types.NoInfer) => Effect.Effect + readonly onSuccess: (a: Types.NoInfer) => Effect.Effect + } + ): (self: Stream.Stream) => Stream.Stream + ( + self: Stream.Stream, + options: { + readonly onFailure: (e: Types.NoInfer) => Effect.Effect + readonly onSuccess: (a: Types.NoInfer) => Effect.Effect + } + ): Stream.Stream +} = dual( + 2, + ( + self: Stream.Stream, + options: { + readonly onFailure: (e: Types.NoInfer) => Effect.Effect + readonly onSuccess: (a: Types.NoInfer) => Effect.Effect + } + ): Stream.Stream => pipe(self, tapError(options.onFailure), tap(options.onSuccess)) +) + +/** @internal */ +export const tapError: { + ( + f: (error: Types.NoInfer) => Effect.Effect + ): (self: Stream.Stream) => Stream.Stream + ( + self: Stream.Stream, + f: (error: E) => Effect.Effect + ): Stream.Stream +} = dual( + 2, + ( + self: Stream.Stream, + f: (error: E) => Effect.Effect + ): Stream.Stream => + catchAll(self, (error) => fromEffect(Effect.zipRight(f(error), Effect.fail(error)))) +) + +/** @internal */ +export const tapErrorCause: { + ( + f: (cause: Cause.Cause>) => Effect.Effect + ): (self: Stream.Stream) => Stream.Stream + ( + self: Stream.Stream, + f: (cause: Cause.Cause) => Effect.Effect + ): Stream.Stream +} = dual( + 2, + ( + self: Stream.Stream, + f: (cause: Cause.Cause) => Effect.Effect + ): Stream.Stream => { + const loop: Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R | R2> = core + .readWithCause({ + onInput: (chunk) => core.flatMap(core.write(chunk), () => loop), + onFailure: (cause) => core.fromEffect(Effect.zipRight(f(cause), Effect.failCause(cause))), + onDone: core.succeedNow + }) + + return new StreamImpl(pipe(toChannel(self), core.pipeTo(loop))) + } +) + +/** @internal */ +export const tapSink = dual< + ( + sink: Sink.Sink + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + sink: Sink.Sink + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + sink: Sink.Sink + ): Stream.Stream => + pipe( + fromEffect(Effect.all([Queue.bounded>(1), Deferred.make()])), + flatMap(([queue, deferred]) => { + const right = flattenTake(fromQueue(queue, { maxChunkSize: 1 })) + const loop: Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R2> = core + .readWithCause({ + onInput: (chunk: Chunk.Chunk) => + pipe( + core.fromEffect(Queue.offer(queue, InternalTake.chunk(chunk))), + core.foldCauseChannel({ + onFailure: () => core.flatMap(core.write(chunk), () => channel.identityChannel()), + onSuccess: () => core.flatMap(core.write(chunk), () => loop) + }) + ) as Channel.Channel, Chunk.Chunk, E | E2, E, unknown, unknown, R2>, + onFailure: (cause: Cause.Cause) => + pipe( + core.fromEffect(Queue.offer(queue, InternalTake.failCause(cause))), + core.foldCauseChannel({ + onFailure: () => core.failCause(cause), + onSuccess: () => core.failCause(cause) + }) + ), + onDone: () => + pipe( + core.fromEffect(Queue.offer(queue, InternalTake.end)), + core.foldCauseChannel({ + onFailure: () => core.void, + onSuccess: () => core.void + }) + ) + }) + return pipe( + new StreamImpl(pipe( + core.pipeTo(toChannel(self), loop), + channel.ensuring(Effect.zipRight( + Effect.forkDaemon(Queue.offer(queue, InternalTake.end)), + Deferred.await(deferred) + )) + )), + merge( + execute(pipe( + run(right, sink), + Effect.ensuring(Effect.zipRight( + Queue.shutdown(queue), + Deferred.succeed(deferred, void 0) + )) + )) + ) + ) + }) + ) +) + +/** @internal */ +export const throttle = dual< + ( + options: { + readonly cost: (chunk: Chunk.Chunk) => number + readonly units: number + readonly duration: Duration.DurationInput + readonly burst?: number | undefined + readonly strategy?: "enforce" | "shape" | undefined + } + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + options: { + readonly cost: (chunk: Chunk.Chunk) => number + readonly units: number + readonly duration: Duration.DurationInput + readonly burst?: number | undefined + readonly strategy?: "enforce" | "shape" | undefined + } + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + options: { + readonly cost: (chunk: Chunk.Chunk) => number + readonly units: number + readonly duration: Duration.DurationInput + readonly burst?: number | undefined + readonly strategy?: "enforce" | "shape" | undefined + } + ): Stream.Stream => + throttleEffect(self, { + ...options, + cost: (chunk) => Effect.succeed(options.cost(chunk)) + }) +) + +/** @internal */ +export const throttleEffect = dual< + ( + options: { + readonly cost: (chunk: Chunk.Chunk) => Effect.Effect + readonly units: number + readonly duration: Duration.DurationInput + readonly burst?: number | undefined + readonly strategy?: "enforce" | "shape" | undefined + } + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + options: { + readonly cost: (chunk: Chunk.Chunk) => Effect.Effect + readonly units: number + readonly duration: Duration.DurationInput + readonly burst?: number | undefined + readonly strategy?: "enforce" | "shape" | undefined + } + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + options: { + readonly cost: (chunk: Chunk.Chunk) => Effect.Effect + readonly units: number + readonly duration: Duration.DurationInput + readonly burst?: number | undefined + readonly strategy?: "enforce" | "shape" | undefined + } + ): Stream.Stream => { + if (options.strategy === "enforce") { + return throttleEnforceEffect(self, options.cost, options.units, options.duration, options.burst ?? 0) + } + return throttleShapeEffect(self, options.cost, options.units, options.duration, options.burst ?? 0) + } +) + +const throttleEnforceEffect = ( + self: Stream.Stream, + cost: (chunk: Chunk.Chunk) => Effect.Effect, + units: number, + duration: Duration.DurationInput, + burst: number +): Stream.Stream => { + const loop = ( + tokens: number, + timestampMillis: number + ): Channel.Channel, Chunk.Chunk, E | E2, E, void, unknown, R2> => + core.readWithCause({ + onInput: (input: Chunk.Chunk) => + pipe( + cost(input), + Effect.zip(Clock.currentTimeMillis), + Effect.map(([weight, currentTimeMillis]) => { + const elapsed = currentTimeMillis - timestampMillis + const cycles = elapsed / Duration.toMillis(duration) + const sum = tokens + (cycles * units) + const max = units + burst < 0 ? Number.POSITIVE_INFINITY : units + burst + const available = sum < 0 ? max : Math.min(sum, max) + if (weight <= available) { + return pipe( + core.write(input), + core.flatMap(() => loop(available - weight, currentTimeMillis)) + ) + } + return loop(tokens, timestampMillis) + }), + channel.unwrap + ), + onFailure: core.failCause, + onDone: () => core.void + }) + const throttled = pipe( + Clock.currentTimeMillis, + Effect.map((currentTimeMillis) => loop(units, currentTimeMillis)), + channel.unwrap + ) + return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(throttled))) +} + +const throttleShapeEffect = ( + self: Stream.Stream, + costFn: (chunk: Chunk.Chunk) => Effect.Effect, + units: number, + duration: Duration.DurationInput, + burst: number +): Stream.Stream => { + const loop = ( + tokens: number, + timestampMillis: number + ): Channel.Channel, Chunk.Chunk, E | E2, E, void, unknown, R2> => + core.readWithCause({ + onInput: (input: Chunk.Chunk) => + pipe( + costFn(input), + Effect.zip(Clock.currentTimeMillis), + Effect.map(([weight, currentTimeMillis]) => { + const elapsed = currentTimeMillis - timestampMillis + const cycles = elapsed / Duration.toMillis(duration) + const sum = tokens + (cycles * units) + const max = units + burst < 0 ? Number.POSITIVE_INFINITY : units + burst + const available = sum < 0 ? max : Math.min(sum, max) + const remaining = available - weight + const waitCycles = remaining >= 0 ? 0 : -remaining / units + const delay = Duration.millis(Math.max(0, waitCycles * Duration.toMillis(duration))) + if (Duration.greaterThan(delay, Duration.zero)) { + return pipe( + core.fromEffect(Clock.sleep(delay)), + channel.zipRight(core.write(input)), + core.flatMap(() => loop(remaining, currentTimeMillis)) + ) + } + return core.flatMap( + core.write(input), + () => loop(remaining, currentTimeMillis) + ) + }), + channel.unwrap + ), + onFailure: core.failCause, + onDone: () => core.void + }) + const throttled = pipe( + Clock.currentTimeMillis, + Effect.map((currentTimeMillis) => loop(units, currentTimeMillis)), + channel.unwrap + ) + return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(throttled))) +} + +/** @internal */ +export const tick = (interval: Duration.DurationInput): Stream.Stream => + repeatWithSchedule(void 0, Schedule.spaced(interval)) + +/** @internal */ +export const timeout = dual< + (duration: Duration.DurationInput) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, duration: Duration.DurationInput) => Stream.Stream +>(2, (self: Stream.Stream, duration: Duration.DurationInput): Stream.Stream => + pipe( + toPull(self), + Effect.map(Effect.timeoutFail>({ + onTimeout: () => Option.none(), + duration + })), + fromPull + )) + +/** @internal */ +export const timeoutFail = dual< + ( + error: LazyArg, + duration: Duration.DurationInput + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + error: LazyArg, + duration: Duration.DurationInput + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + error: LazyArg, + duration: Duration.DurationInput + ): Stream.Stream => pipe(self, timeoutTo(duration, failSync(error))) +) + +/** @internal */ +export const timeoutFailCause = dual< + ( + cause: LazyArg>, + duration: Duration.DurationInput + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + cause: LazyArg>, + duration: Duration.DurationInput + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + cause: LazyArg>, + duration: Duration.DurationInput + ): Stream.Stream => + pipe( + toPull(self), + Effect.map( + Effect.timeoutFailCause>({ + onTimeout: () => Cause.map(cause(), Option.some), + duration + }) + ), + fromPull + ) +) + +/** @internal */ +export const timeoutTo = dual< + ( + duration: Duration.DurationInput, + that: Stream.Stream + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + duration: Duration.DurationInput, + that: Stream.Stream + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + duration: Duration.DurationInput, + that: Stream.Stream + ): Stream.Stream => { + const StreamTimeout = new Cause.RuntimeException("Stream Timeout") + return pipe( + self, + timeoutFailCause(() => Cause.die(StreamTimeout), duration), + catchSomeCause((cause) => + Cause.isDieType(cause) && + Cause.isRuntimeException(cause.defect) && + cause.defect.message !== undefined && + cause.defect.message === "Stream Timeout" ? + Option.some(that) : + Option.none() + ) + ) + } +) + +const pubsubFromOptions = ( + options: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } +): Effect.Effect>> => { + if (typeof options === "number") { + return PubSub.bounded(options) + } else if (options.capacity === "unbounded") { + return PubSub.unbounded({ replay: options.replay }) + } + switch (options.strategy) { + case "dropping": + return PubSub.dropping(options) + case "sliding": + return PubSub.sliding(options) + default: + return PubSub.bounded(options) + } +} + +/** @internal */ +export const toPubSub = dual< + ( + capacity: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ) => (self: Stream.Stream) => Effect.Effect>, never, Scope.Scope | R>, + ( + self: Stream.Stream, + capacity: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } + ) => Effect.Effect>, never, Scope.Scope | R> +>(2, ( + self: Stream.Stream, + capacity: number | { + readonly capacity: "unbounded" + readonly replay?: number | undefined + } | { + readonly capacity: number + readonly strategy?: "sliding" | "dropping" | "suspend" | undefined + readonly replay?: number | undefined + } +): Effect.Effect>, never, Scope.Scope | R> => + pipe( + Effect.acquireRelease(pubsubFromOptions(capacity), (pubsub) => PubSub.shutdown(pubsub)), + Effect.tap((pubsub) => pipe(self, runIntoPubSubScoped(pubsub), Effect.forkScoped)) + )) + +/** @internal */ +export const toPull = ( + self: Stream.Stream +): Effect.Effect, Option.Option, R>, never, R | Scope.Scope> => + Effect.map(channel.toPull(toChannel(self)), (pull) => + pipe( + pull, + Effect.mapError(Option.some), + Effect.flatMap(Either.match({ + onLeft: () => Effect.fail(Option.none()), + onRight: Effect.succeed + })) + )) + +/** @internal */ +export const toQueue = dual< + ( + options?: { + readonly strategy?: "suspend" | "sliding" | "dropping" | undefined + readonly capacity?: number | undefined + } | { + readonly strategy: "unbounded" + } + ) => ( + self: Stream.Stream + ) => Effect.Effect>, never, R | Scope.Scope>, + ( + self: Stream.Stream, + options?: { + readonly strategy?: "suspend" | "sliding" | "dropping" | undefined + readonly capacity?: number | undefined + } | { + readonly strategy: "unbounded" + } + ) => Effect.Effect>, never, R | Scope.Scope> +>((args) => isStream(args[0]), ( + self: Stream.Stream, + options?: { + readonly strategy?: "suspend" | "sliding" | "dropping" | undefined + readonly capacity?: number | undefined + } | { + readonly strategy: "unbounded" + } +) => + Effect.tap( + Effect.acquireRelease( + options?.strategy === "unbounded" ? + Queue.unbounded>() : + options?.strategy === "dropping" ? + Queue.dropping>(options.capacity ?? 2) : + options?.strategy === "sliding" ? + Queue.sliding>(options.capacity ?? 2) : + Queue.bounded>(options?.capacity ?? 2), + (queue) => Queue.shutdown(queue) + ), + (queue) => Effect.forkScoped(runIntoQueueScoped(self, queue)) + )) + +/** @internal */ +export const toQueueOfElements = dual< + (options?: { + readonly capacity?: number | undefined + }) => ( + self: Stream.Stream + ) => Effect.Effect>>, never, R | Scope.Scope>, + ( + self: Stream.Stream, + options?: { + readonly capacity?: number | undefined + } + ) => Effect.Effect>>, never, R | Scope.Scope> +>((args) => isStream(args[0]), ( + self: Stream.Stream, + options?: { + readonly capacity?: number | undefined + } +) => + Effect.tap( + Effect.acquireRelease( + Queue.bounded>>(options?.capacity ?? 2), + (queue) => Queue.shutdown(queue) + ), + (queue) => Effect.forkScoped(runIntoQueueElementsScoped(self, queue)) + )) + +/** @internal */ +export const toReadableStream = dual< + ( + options?: { readonly strategy?: QueuingStrategy | undefined } + ) => (self: Stream.Stream) => ReadableStream, + ( + self: Stream.Stream, + options?: { readonly strategy?: QueuingStrategy | undefined } + ) => ReadableStream +>( + (args) => isStream(args[0]), + ( + self: Stream.Stream, + options?: { readonly strategy?: QueuingStrategy | undefined } + ) => toReadableStreamRuntime(self, Runtime.defaultRuntime, options) +) + +/** @internal */ +export const toReadableStreamEffect = dual< + ( + options?: { readonly strategy?: QueuingStrategy | undefined } + ) => (self: Stream.Stream) => Effect.Effect, never, R>, + ( + self: Stream.Stream, + options?: { readonly strategy?: QueuingStrategy | undefined } + ) => Effect.Effect, never, R> +>( + (args) => isStream(args[0]), + ( + self: Stream.Stream, + options?: { readonly strategy?: QueuingStrategy | undefined } + ) => Effect.map(Effect.runtime(), (runtime) => toReadableStreamRuntime(self, runtime, options)) +) + +/** @internal */ +export const toReadableStreamRuntime = dual< + ( + runtime: Runtime.Runtime, + options?: { readonly strategy?: QueuingStrategy | undefined } + ) => (self: Stream.Stream) => ReadableStream, + ( + self: Stream.Stream, + runtime: Runtime.Runtime, + options?: { readonly strategy?: QueuingStrategy | undefined } + ) => ReadableStream +>( + (args) => isStream(args[0]), + ( + self: Stream.Stream, + runtime: Runtime.Runtime, + options?: { readonly strategy?: QueuingStrategy | undefined } + ): ReadableStream => { + const runFork = Runtime.runFork(runtime) + let currentResolve: (() => void) | undefined = undefined + let fiber: Fiber.RuntimeFiber | undefined = undefined + const latch = Effect.unsafeMakeLatch(false) + + return new ReadableStream({ + start(controller) { + fiber = runFork(runForEachChunk(self, (chunk) => { + if (chunk.length === 0) return Effect.void + return latch.whenOpen(Effect.sync(() => { + latch.unsafeClose() + for (const item of chunk) { + controller.enqueue(item) + } + currentResolve!() + currentResolve = undefined + })) + })) + fiber.addObserver((exit) => { + try { + if (exit._tag === "Failure") { + controller.error(Cause.squash(exit.cause)) + } else { + controller.close() + } + } catch { + // ignore + } + }) + }, + pull() { + return new Promise((resolve) => { + currentResolve = resolve + Effect.runSync(latch.open) + }) + }, + cancel() { + if (!fiber) return + return Effect.runPromise(Effect.asVoid(Fiber.interrupt(fiber))) + } + }, options?.strategy) + } +) + +/** @internal */ +export const transduce = dual< + ( + sink: Sink.Sink + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + sink: Sink.Sink + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + sink: Sink.Sink + ): Stream.Stream => { + const newChannel = core.suspend(() => { + const leftovers = { ref: Chunk.empty>() } + const upstreamDone = { ref: false } + const buffer: Channel.Channel, Chunk.Chunk, E, E, unknown, unknown> = core.suspend( + () => { + const leftover = leftovers.ref + if (Chunk.isEmpty(leftover)) { + return core.readWith({ + onInput: (input) => pipe(core.write(input), core.flatMap(() => buffer)), + onFailure: core.fail, + onDone: core.succeedNow + }) + } + leftovers.ref = Chunk.empty>() + return pipe(channel.writeChunk(leftover), core.flatMap(() => buffer)) + } + ) + const concatAndGet = (chunk: Chunk.Chunk>): Chunk.Chunk> => { + const leftover = leftovers.ref + const concatenated = Chunk.appendAll(leftover, Chunk.filter(chunk, (chunk) => chunk.length !== 0)) + leftovers.ref = concatenated + return concatenated + } + const upstreamMarker: Channel.Channel, Chunk.Chunk, E, E, unknown, unknown> = core + .readWith({ + onInput: (input: Chunk.Chunk) => core.flatMap(core.write(input), () => upstreamMarker), + onFailure: core.fail, + onDone: (done) => + channel.zipRight( + core.sync(() => { + upstreamDone.ref = true + }), + core.succeedNow(done) + ) + }) + const transducer: Channel.Channel, Chunk.Chunk, E | E2, never, void, unknown, R | R2> = pipe( + sink, + sink_.toChannel, + core.collectElements, + core.flatMap(([leftover, z]) => + pipe( + core.succeed([upstreamDone.ref, concatAndGet(leftover)] as const), + core.flatMap(([done, newLeftovers]) => { + const nextChannel = done && Chunk.isEmpty(newLeftovers) ? + core.void : + transducer + return pipe(core.write(Chunk.of(z)), core.flatMap(() => nextChannel)) + }) + ) + ) + ) + return pipe( + toChannel(self), + core.pipeTo(upstreamMarker), + core.pipeTo(buffer), + channel.pipeToOrFail(transducer) + ) + }) + return new StreamImpl(newChannel) + } +) + +/** @internal */ +export const toAsyncIterableRuntime = dual< + ( + runtime: Runtime.Runtime + ) => (self: Stream.Stream) => AsyncIterable, + ( + self: Stream.Stream, + runtime: Runtime.Runtime + ) => AsyncIterable +>( + (args) => isStream(args[0]), + ( + self: Stream.Stream, + runtime: Runtime.Runtime + ): AsyncIterable => { + const runFork = Runtime.runFork(runtime) + return { + [Symbol.asyncIterator]() { + let currentResolve: ((value: IteratorResult) => void) | undefined = undefined + let currentReject: ((reason: any) => void) | undefined = undefined + let fiber: Fiber.RuntimeFiber | undefined = undefined + const latch = Effect.unsafeMakeLatch(false) + let returned = false + return { + next() { + if (!fiber) { + fiber = runFork(runForEach(self, (value) => + latch.whenOpen(Effect.sync(() => { + latch.unsafeClose() + currentResolve!({ done: false, value }) + currentResolve = currentReject = undefined + })))) + fiber.addObserver((exit) => { + if (returned) return + fiber = Effect.runFork(latch.whenOpen(Effect.sync(() => { + if (exit._tag === "Failure") { + currentReject!(Cause.squash(exit.cause)) + } else { + currentResolve!({ done: true, value: void 0 }) + } + currentResolve = currentReject = undefined + }))) + }) + } + return new Promise>((resolve, reject) => { + currentResolve = resolve + currentReject = reject + latch.unsafeOpen() + }) + }, + return() { + returned = true + if (!fiber) return Promise.resolve({ done: true, value: void 0 }) + return Effect.runPromise(Effect.as(Fiber.interrupt(fiber), { done: true, value: void 0 })) + } + } + } + } + } +) + +/** @internal */ +export const toAsyncIterable = (self: Stream.Stream): AsyncIterable => + toAsyncIterableRuntime(self, Runtime.defaultRuntime) + +/** @internal */ +export const toAsyncIterableEffect = ( + self: Stream.Stream +): Effect.Effect, never, R> => + Effect.map(Effect.runtime(), (runtime) => toAsyncIterableRuntime(self, runtime)) + +/** @internal */ +export const unfold = (s: S, f: (s: S) => Option.Option): Stream.Stream => + unfoldChunk(s, (s) => pipe(f(s), Option.map(([a, s]) => [Chunk.of(a), s]))) + +/** @internal */ +export const unfoldChunk = ( + s: S, + f: (s: S) => Option.Option, S]> +): Stream.Stream => { + const loop = (s: S): Channel.Channel, unknown, never, unknown, unknown, unknown> => + Option.match(f(s), { + onNone: () => core.void, + onSome: ([chunk, s]) => core.flatMap(core.write(chunk), () => loop(s)) + }) + return new StreamImpl(core.suspend(() => loop(s))) +} + +/** @internal */ +export const unfoldChunkEffect = ( + s: S, + f: (s: S) => Effect.Effect, S]>, E, R> +): Stream.Stream => + suspend(() => { + const loop = (s: S): Channel.Channel, unknown, E, unknown, unknown, unknown, R> => + channel.unwrap( + Effect.map( + f(s), + Option.match({ + onNone: () => core.void, + onSome: ([chunk, s]) => core.flatMap(core.write(chunk), () => loop(s)) + }) + ) + ) + return new StreamImpl(loop(s)) + }) + +/** @internal */ +export const unfoldEffect = ( + s: S, + f: (s: S) => Effect.Effect, E, R> +): Stream.Stream => + unfoldChunkEffect(s, (s) => pipe(f(s), Effect.map(Option.map(([a, s]) => [Chunk.of(a), s])))) + +const void_: Stream.Stream = succeed(void 0) +export { + /** @internal */ + void_ as void +} + +/** @internal */ +export const unwrap = ( + effect: Effect.Effect, E, R> +): Stream.Stream => flatten(fromEffect(effect)) + +/** @internal */ +export const unwrapScoped = ( + effect: Effect.Effect, E, R> +): Stream.Stream | R2> => flatten(scoped(effect)) + +/** @internal */ +export const unwrapScopedWith = ( + f: (scope: Scope.Scope) => Effect.Effect, E, R> +): Stream.Stream => flatten(scopedWith((scope) => f(scope))) + +/** @internal */ +export const updateService = dual< + ( + tag: Context.Tag, + f: (service: Types.NoInfer) => Types.NoInfer + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + tag: Context.Tag, + f: (service: Types.NoInfer) => Types.NoInfer + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + tag: Context.Tag, + f: (service: Types.NoInfer) => Types.NoInfer + ): Stream.Stream => + pipe( + self, + mapInputContext((context) => + pipe( + context, + Context.add(tag, f(pipe(context, Context.unsafeGet(tag)))) + ) + ) + ) +) + +/** @internal */ +export const when = dual< + (test: LazyArg) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, test: LazyArg) => Stream.Stream +>( + 2, + (self: Stream.Stream, test: LazyArg): Stream.Stream => + pipe(self, whenEffect(Effect.sync(test))) +) + +/** @internal */ +export const whenCase = ( + evaluate: LazyArg, + pf: (a: A) => Option.Option> +) => whenCaseEffect(pf)(Effect.sync(evaluate)) + +/** @internal */ +export const whenCaseEffect = dual< + ( + pf: (a: A) => Option.Option> + ) => (self: Effect.Effect) => Stream.Stream, + ( + self: Effect.Effect, + pf: (a: A) => Option.Option> + ) => Stream.Stream +>( + 2, + ( + self: Effect.Effect, + pf: (a: A) => Option.Option> + ): Stream.Stream => + pipe( + fromEffect(self), + flatMap((a) => pipe(pf(a), Option.getOrElse(() => empty))) + ) +) + +/** @internal */ +export const whenEffect = dual< + ( + effect: Effect.Effect + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + effect: Effect.Effect + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + effect: Effect.Effect + ): Stream.Stream => pipe(fromEffect(effect), flatMap((bool) => bool ? self : empty)) +) + +/** @internal */ +export const withSpan: { + ( + name: string, + options?: Tracer.SpanOptions + ): (self: Stream.Stream) => Stream.Stream> + ( + self: Stream.Stream, + name: string, + options?: Tracer.SpanOptions + ): Stream.Stream> +} = function() { + const dataFirst = typeof arguments[0] !== "string" + const name = dataFirst ? arguments[1] : arguments[0] + const options = InternalTracer.addSpanStackTrace(dataFirst ? arguments[2] : arguments[1]) + if (dataFirst) { + const self = arguments[0] + return new StreamImpl(channel.withSpan(toChannel(self), name, options)) + } + return (self: Stream.Stream) => new StreamImpl(channel.withSpan(toChannel(self), name, options)) +} as any + +/** @internal */ +export const zip = dual< + ( + that: Stream.Stream + ) => (self: Stream.Stream) => Stream.Stream<[A, A2], E2 | E, R2 | R>, + ( + self: Stream.Stream, + that: Stream.Stream + ) => Stream.Stream<[A, A2], E2 | E, R2 | R> +>( + 2, + ( + self: Stream.Stream, + that: Stream.Stream + ): Stream.Stream<[A, A2], E2 | E, R2 | R> => pipe(self, zipWith(that, (a, a2) => [a, a2])) +) + +/** @internal */ +export const zipFlatten = dual< + ( + that: Stream.Stream + ) => , E, R>( + self: Stream.Stream + ) => Stream.Stream<[...A, A2], E2 | E, R2 | R>, + , E, R, A2, E2, R2>( + self: Stream.Stream, + that: Stream.Stream + ) => Stream.Stream<[...A, A2], E2 | E, R2 | R> +>( + 2, + , E, R, A2, E2, R2>( + self: Stream.Stream, + that: Stream.Stream + ): Stream.Stream<[...A, A2], E2 | E, R2 | R> => pipe(self, zipWith(that, (a, a2) => [...a, a2])) +) + +/** @internal */ +export const zipAll = dual< + ( + options: { + readonly other: Stream.Stream + readonly defaultSelf: A + readonly defaultOther: A2 + } + ) => (self: Stream.Stream) => Stream.Stream<[A, A2], E2 | E, R2 | R>, + ( + self: Stream.Stream, + options: { + readonly other: Stream.Stream + readonly defaultSelf: A + readonly defaultOther: A2 + } + ) => Stream.Stream<[A, A2], E2 | E, R2 | R> +>( + 2, + ( + self: Stream.Stream, + options: { + readonly other: Stream.Stream + readonly defaultSelf: A + readonly defaultOther: A2 + } + ): Stream.Stream<[A, A2], E2 | E, R2 | R> => + zipAllWith(self, { + other: options.other, + onSelf: (a) => [a, options.defaultOther], + onOther: (a2) => [options.defaultSelf, a2], + onBoth: (a, a2) => [a, a2] + }) +) + +/** @internal */ +export const zipAllLeft = dual< + ( + that: Stream.Stream, + defaultLeft: A + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + that: Stream.Stream, + defaultLeft: A + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + other: Stream.Stream, + defaultSelf: A + ): Stream.Stream => + zipAllWith(self, { + other, + onSelf: identity, + onOther: () => defaultSelf, + onBoth: (a) => a + }) +) + +/** @internal */ +export const zipAllRight = dual< + ( + that: Stream.Stream, + defaultRight: A2 + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + that: Stream.Stream, + defaultRight: A2 + ) => Stream.Stream +>( + 3, + ( + self: Stream.Stream, + other: Stream.Stream, + defaultRight: A2 + ): Stream.Stream => + zipAllWith(self, { + other, + onSelf: () => defaultRight, + onOther: identity, + onBoth: (_, a2) => a2 + }) +) + +/** @internal */ +export const zipAllSortedByKey = dual< + ( + options: { + readonly other: Stream.Stream + readonly defaultSelf: A + readonly defaultOther: A2 + readonly order: Order.Order + } + ) => ( + self: Stream.Stream + ) => Stream.Stream<[K, [A, A2]], E2 | E, R2 | R>, + ( + self: Stream.Stream, + options: { + readonly other: Stream.Stream + readonly defaultSelf: A + readonly defaultOther: A2 + readonly order: Order.Order + } + ) => Stream.Stream<[K, [A, A2]], E2 | E, R2 | R> +>( + 2, + ( + self: Stream.Stream, + options: { + readonly other: Stream.Stream + readonly defaultSelf: A + readonly defaultOther: A2 + readonly order: Order.Order + } + ): Stream.Stream<[K, [A, A2]], E2 | E, R2 | R> => + zipAllSortedByKeyWith(self, { + other: options.other, + onSelf: (a) => [a, options.defaultOther], + onOther: (a2) => [options.defaultSelf, a2], + onBoth: (a, a2) => [a, a2], + order: options.order + }) +) + +/** @internal */ +export const zipAllSortedByKeyLeft = dual< + ( + options: { + readonly other: Stream.Stream + readonly defaultSelf: A + readonly order: Order.Order + } + ) => (self: Stream.Stream) => Stream.Stream<[K, A], E2 | E, R2 | R>, + ( + self: Stream.Stream, + options: { + readonly other: Stream.Stream + readonly defaultSelf: A + readonly order: Order.Order + } + ) => Stream.Stream<[K, A], E2 | E, R2 | R> +>( + 2, + ( + self: Stream.Stream, + options: { + readonly other: Stream.Stream + readonly defaultSelf: A + readonly order: Order.Order + } + ): Stream.Stream<[K, A], E2 | E, R2 | R> => + zipAllSortedByKeyWith(self, { + other: options.other, + onSelf: identity, + onOther: () => options.defaultSelf, + onBoth: (a) => a, + order: options.order + }) +) + +/** @internal */ +export const zipAllSortedByKeyRight = dual< + ( + options: { + readonly other: Stream.Stream + readonly defaultOther: A2 + readonly order: Order.Order + } + ) => (self: Stream.Stream) => Stream.Stream<[K, A2], E2 | E, R2 | R>, + ( + self: Stream.Stream, + options: { + readonly other: Stream.Stream + readonly defaultOther: A2 + readonly order: Order.Order + } + ) => Stream.Stream<[K, A2], E2 | E, R2 | R> +>( + 2, + ( + self: Stream.Stream, + options: { + readonly other: Stream.Stream + readonly defaultOther: A2 + readonly order: Order.Order + } + ): Stream.Stream<[K, A2], E2 | E, R2 | R> => + zipAllSortedByKeyWith(self, { + other: options.other, + onSelf: () => options.defaultOther, + onOther: identity, + onBoth: (_, a2) => a2, + order: options.order + }) +) + +/** @internal */ +export const zipAllSortedByKeyWith = dual< + ( + options: { + readonly other: Stream.Stream + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A3 + readonly onBoth: (a: A, a2: A2) => A3 + readonly order: Order.Order + } + ) => (self: Stream.Stream) => Stream.Stream<[K, A3], E2 | E, R2 | R>, + ( + self: Stream.Stream, + options: { + readonly other: Stream.Stream + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A3 + readonly onBoth: (a: A, a2: A2) => A3 + readonly order: Order.Order + } + ) => Stream.Stream<[K, A3], E2 | E, R2 | R> +>( + 2, + ( + self: Stream.Stream, + options: { + readonly other: Stream.Stream + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A3 + readonly onBoth: (a: A, a2: A2) => A3 + readonly order: Order.Order + } + ): Stream.Stream<[K, A3], E2 | E, R2 | R> => { + const pull = ( + state: ZipAllState.ZipAllState, + pullLeft: Effect.Effect, Option.Option, R>, + pullRight: Effect.Effect, Option.Option, R2> + ): Effect.Effect< + Exit.Exit< + readonly [ + Chunk.Chunk<[K, A3]>, + ZipAllState.ZipAllState + ], + Option.Option + >, + never, + R | R2 + > => { + switch (state._tag) { + case ZipAllState.OP_DRAIN_LEFT: { + return pipe( + pullLeft, + Effect.match({ + onFailure: Exit.fail, + onSuccess: (leftChunk) => + Exit.succeed( + [ + Chunk.map(leftChunk, ([k, a]) => [k, options.onSelf(a)]), + ZipAllState.DrainLeft + ] as const + ) + }) + ) + } + case ZipAllState.OP_DRAIN_RIGHT: { + return pipe( + pullRight, + Effect.match({ + onFailure: Exit.fail, + onSuccess: (rightChunk) => + Exit.succeed( + [ + Chunk.map(rightChunk, ([k, a2]) => [k, options.onOther(a2)]), + ZipAllState.DrainRight + ] as const + ) + }) + ) + } + case ZipAllState.OP_PULL_BOTH: { + return pipe( + unsome(pullLeft), + Effect.zip(unsome(pullRight), { concurrent: true }), + Effect.matchEffect({ + onFailure: (error) => Effect.succeed(Exit.fail(Option.some(error))), + onSuccess: ([leftOption, rightOption]) => { + if (Option.isSome(leftOption) && Option.isSome(rightOption)) { + if (Chunk.isEmpty(leftOption.value) && Chunk.isEmpty(rightOption.value)) { + return pull(ZipAllState.PullBoth, pullLeft, pullRight) + } + if (Chunk.isEmpty(leftOption.value)) { + return pull(ZipAllState.PullLeft(rightOption.value), pullLeft, pullRight) + } + if (Chunk.isEmpty(rightOption.value)) { + return pull(ZipAllState.PullRight(leftOption.value), pullLeft, pullRight) + } + return Effect.succeed(Exit.succeed(merge(leftOption.value, rightOption.value))) + } + if (Option.isSome(leftOption) && Option.isNone(rightOption)) { + if (Chunk.isEmpty(leftOption.value)) { + return pull(ZipAllState.DrainLeft, pullLeft, pullRight) + } + return Effect.succeed( + Exit.succeed( + [ + pipe(leftOption.value, Chunk.map(([k, a]) => [k, options.onSelf(a)])), + ZipAllState.DrainLeft + ] as const + ) + ) + } + if (Option.isNone(leftOption) && Option.isSome(rightOption)) { + if (Chunk.isEmpty(rightOption.value)) { + return pull(ZipAllState.DrainRight, pullLeft, pullRight) + } + return Effect.succeed( + Exit.succeed( + [ + pipe(rightOption.value, Chunk.map(([k, a2]) => [k, options.onOther(a2)])), + ZipAllState.DrainRight + ] as const + ) + ) + } + return Effect.succeed(Exit.fail>(Option.none())) + } + }) + ) + } + case ZipAllState.OP_PULL_LEFT: { + return Effect.matchEffect(pullLeft, { + onFailure: Option.match({ + onNone: () => + Effect.succeed( + Exit.succeed([ + pipe(state.rightChunk, Chunk.map(([k, a2]) => [k, options.onOther(a2)])), + ZipAllState.DrainRight + ]) + ), + onSome: (error) => + Effect.succeed< + Exit.Exit< + readonly [ + Chunk.Chunk<[K, A3]>, + ZipAllState.ZipAllState + ], + Option.Option + > + >(Exit.fail(Option.some(error))) + }), + onSuccess: (leftChunk) => + Chunk.isEmpty(leftChunk) ? + pull(ZipAllState.PullLeft(state.rightChunk), pullLeft, pullRight) : + Effect.succeed(Exit.succeed(merge(leftChunk, state.rightChunk))) + }) + } + case ZipAllState.OP_PULL_RIGHT: { + return Effect.matchEffect(pullRight, { + onFailure: Option.match({ + onNone: () => + Effect.succeed( + Exit.succeed( + [ + Chunk.map(state.leftChunk, ([k, a]) => [k, options.onSelf(a)]), + ZipAllState.DrainLeft + ] as const + ) + ), + onSome: (error) => + Effect.succeed< + Exit.Exit< + readonly [ + Chunk.Chunk<[K, A3]>, + ZipAllState.ZipAllState + ], + Option.Option + > + >(Exit.fail(Option.some(error))) + }), + onSuccess: (rightChunk) => + Chunk.isEmpty(rightChunk) ? + pull(ZipAllState.PullRight(state.leftChunk), pullLeft, pullRight) : + Effect.succeed(Exit.succeed(merge(state.leftChunk, rightChunk))) + }) + } + } + } + const merge = ( + leftChunk: Chunk.Chunk, + rightChunk: Chunk.Chunk + ): readonly [ + Chunk.Chunk<[K, A3]>, + ZipAllState.ZipAllState + ] => { + const hasNext = (chunk: Chunk.Chunk, index: number) => index < chunk.length - 1 + const builder: Array<[K, A3]> = [] + let state: + | ZipAllState.ZipAllState< + readonly [K, A], + readonly [K, A2] + > + | undefined = undefined + let leftIndex = 0 + let rightIndex = 0 + let leftTuple = pipe(leftChunk, Chunk.unsafeGet(leftIndex)) + let rightTuple = pipe(rightChunk, Chunk.unsafeGet(rightIndex)) + let k1 = leftTuple[0] + let a = leftTuple[1] + let k2 = rightTuple[0] + let a2 = rightTuple[1] + let loop = true + while (loop) { + const compare = options.order(k1, k2) + if (compare === 0) { + builder.push([k1, options.onBoth(a, a2)]) + if (hasNext(leftChunk, leftIndex) && hasNext(rightChunk, rightIndex)) { + leftIndex = leftIndex + 1 + rightIndex = rightIndex + 1 + leftTuple = pipe(leftChunk, Chunk.unsafeGet(leftIndex)) + rightTuple = pipe(rightChunk, Chunk.unsafeGet(rightIndex)) + k1 = leftTuple[0] + a = leftTuple[1] + k2 = rightTuple[0] + a2 = rightTuple[1] + } else if (hasNext(leftChunk, leftIndex)) { + state = ZipAllState.PullRight(pipe(leftChunk, Chunk.drop(leftIndex + 1))) + loop = false + } else if (hasNext(rightChunk, rightIndex)) { + state = ZipAllState.PullLeft(pipe(rightChunk, Chunk.drop(rightIndex + 1))) + loop = false + } else { + state = ZipAllState.PullBoth + loop = false + } + } else if (compare < 0) { + builder.push([k1, options.onSelf(a)]) + if (hasNext(leftChunk, leftIndex)) { + leftIndex = leftIndex + 1 + leftTuple = pipe(leftChunk, Chunk.unsafeGet(leftIndex)) + k1 = leftTuple[0] + a = leftTuple[1] + } else { + const rightBuilder: Array = [] + rightBuilder.push(rightTuple) + while (hasNext(rightChunk, rightIndex)) { + rightIndex = rightIndex + 1 + rightTuple = pipe(rightChunk, Chunk.unsafeGet(rightIndex)) + rightBuilder.push(rightTuple) + } + state = ZipAllState.PullLeft(Chunk.unsafeFromArray(rightBuilder)) + loop = false + } + } else { + builder.push([k2, options.onOther(a2)]) + if (hasNext(rightChunk, rightIndex)) { + rightIndex = rightIndex + 1 + rightTuple = pipe(rightChunk, Chunk.unsafeGet(rightIndex)) + k2 = rightTuple[0] + a2 = rightTuple[1] + } else { + const leftBuilder: Array = [] + leftBuilder.push(leftTuple) + while (hasNext(leftChunk, leftIndex)) { + leftIndex = leftIndex + 1 + leftTuple = pipe(leftChunk, Chunk.unsafeGet(leftIndex)) + leftBuilder.push(leftTuple) + } + state = ZipAllState.PullRight(Chunk.unsafeFromArray(leftBuilder)) + loop = false + } + } + } + return [Chunk.unsafeFromArray(builder), state!] + } + return combineChunks(self, options.other, ZipAllState.PullBoth, pull) + } +) + +/** @internal */ +export const zipAllWith = dual< + ( + options: { + readonly other: Stream.Stream + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A3 + readonly onBoth: (a: A, a2: A2) => A3 + } + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + options: { + readonly other: Stream.Stream + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A3 + readonly onBoth: (a: A, a2: A2) => A3 + } + ) => Stream.Stream +>( + 2, + ( + self: Stream.Stream, + options: { + readonly other: Stream.Stream + readonly onSelf: (a: A) => A3 + readonly onOther: (a2: A2) => A3 + readonly onBoth: (a: A, a2: A2) => A3 + } + ): Stream.Stream => { + const pull = ( + state: ZipAllState.ZipAllState, + pullLeft: Effect.Effect, Option.Option, R>, + pullRight: Effect.Effect, Option.Option, R2> + ): Effect.Effect< + Exit.Exit, ZipAllState.ZipAllState], Option.Option>, + never, + R | R2 + > => { + switch (state._tag) { + case ZipAllState.OP_DRAIN_LEFT: { + return Effect.matchEffect(pullLeft, { + onFailure: (error) => Effect.succeed(Exit.fail(error)), + onSuccess: (leftChunk) => + Effect.succeed(Exit.succeed( + [ + Chunk.map(leftChunk, options.onSelf), + ZipAllState.DrainLeft + ] as const + )) + }) + } + case ZipAllState.OP_DRAIN_RIGHT: { + return Effect.matchEffect(pullRight, { + onFailure: (error) => Effect.succeed(Exit.fail(error)), + onSuccess: (rightChunk) => + Effect.succeed(Exit.succeed( + [ + Chunk.map(rightChunk, options.onOther), + ZipAllState.DrainRight + ] as const + )) + }) + } + case ZipAllState.OP_PULL_BOTH: { + return pipe( + unsome(pullLeft), + Effect.zip(unsome(pullRight), { concurrent: true }), + Effect.matchEffect({ + onFailure: (error) => Effect.succeed(Exit.fail(Option.some(error))), + onSuccess: ([leftOption, rightOption]) => { + if (Option.isSome(leftOption) && Option.isSome(rightOption)) { + if (Chunk.isEmpty(leftOption.value) && Chunk.isEmpty(rightOption.value)) { + return pull(ZipAllState.PullBoth, pullLeft, pullRight) + } + if (Chunk.isEmpty(leftOption.value)) { + return pull(ZipAllState.PullLeft(rightOption.value), pullLeft, pullRight) + } + if (Chunk.isEmpty(rightOption.value)) { + return pull(ZipAllState.PullRight(leftOption.value), pullLeft, pullRight) + } + return Effect.succeed(Exit.succeed(zip(leftOption.value, rightOption.value, options.onBoth))) + } + if (Option.isSome(leftOption) && Option.isNone(rightOption)) { + return Effect.succeed(Exit.succeed( + [ + Chunk.map(leftOption.value, options.onSelf), + ZipAllState.DrainLeft + ] as const + )) + } + if (Option.isNone(leftOption) && Option.isSome(rightOption)) { + return Effect.succeed(Exit.succeed( + [ + Chunk.map(rightOption.value, options.onOther), + ZipAllState.DrainRight + ] as const + )) + } + return Effect.succeed(Exit.fail>(Option.none())) + } + }) + ) + } + case ZipAllState.OP_PULL_LEFT: { + return Effect.matchEffect(pullLeft, { + onFailure: Option.match({ + onNone: () => + Effect.succeed(Exit.succeed( + [ + Chunk.map(state.rightChunk, options.onOther), + ZipAllState.DrainRight + ] as const + )), + onSome: (error) => + Effect.succeed< + Exit.Exit, ZipAllState.ZipAllState], Option.Option> + >( + Exit.fail(Option.some(error)) + ) + }), + onSuccess: (leftChunk) => { + if (Chunk.isEmpty(leftChunk)) { + return pull(ZipAllState.PullLeft(state.rightChunk), pullLeft, pullRight) + } + if (Chunk.isEmpty(state.rightChunk)) { + return pull(ZipAllState.PullRight(leftChunk), pullLeft, pullRight) + } + return Effect.succeed(Exit.succeed(zip(leftChunk, state.rightChunk, options.onBoth))) + } + }) + } + case ZipAllState.OP_PULL_RIGHT: { + return Effect.matchEffect(pullRight, { + onFailure: Option.match({ + onNone: () => + Effect.succeed( + Exit.succeed( + [ + Chunk.map(state.leftChunk, options.onSelf), + ZipAllState.DrainLeft + ] as const + ) + ), + onSome: (error) => + Effect.succeed< + Exit.Exit, ZipAllState.ZipAllState], Option.Option> + >( + Exit.fail(Option.some(error)) + ) + }), + onSuccess: (rightChunk) => { + if (Chunk.isEmpty(rightChunk)) { + return pull( + ZipAllState.PullRight(state.leftChunk), + pullLeft, + pullRight + ) + } + if (Chunk.isEmpty(state.leftChunk)) { + return pull( + ZipAllState.PullLeft(rightChunk), + pullLeft, + pullRight + ) + } + return Effect.succeed(Exit.succeed(zip(state.leftChunk, rightChunk, options.onBoth))) + } + }) + } + } + } + const zip = ( + leftChunk: Chunk.Chunk, + rightChunk: Chunk.Chunk, + f: (a: A, a2: A2) => A3 + ): readonly [Chunk.Chunk, ZipAllState.ZipAllState] => { + const [output, either] = zipChunks(leftChunk, rightChunk, f) + switch (either._tag) { + case "Left": { + if (Chunk.isEmpty(either.left)) { + return [output, ZipAllState.PullBoth] as const + } + return [output, ZipAllState.PullRight(either.left)] as const + } + case "Right": { + if (Chunk.isEmpty(either.right)) { + return [output, ZipAllState.PullBoth] as const + } + return [output, ZipAllState.PullLeft(either.right)] as const + } + } + } + return combineChunks(self, options.other, ZipAllState.PullBoth, pull) + } +) + +/** @internal */ +export const zipLatest: { + ( + right: Stream.Stream + ): (left: Stream.Stream) => Stream.Stream<[AL, AR], EL | ER, RL | RR> + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream<[AL, AR], EL | ER, RL | RR> +} = dual( + 2, + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream<[AL, AR], EL | ER, RL | RR> => pipe(left, zipLatestWith(right, (a, a2) => [a, a2])) +) + +export const zipLatestAll = >>( + ...streams: T +): Stream.Stream< + [T[number]] extends [never] ? never + : { [K in keyof T]: T[K] extends Stream.Stream ? A : never }, + [T[number]] extends [never] ? never : T[number] extends Stream.Stream ? _E : never, + [T[number]] extends [never] ? never : T[number] extends Stream.Stream ? _R : never +> => { + if (streams.length === 0) { + return empty + } else if (streams.length === 1) { + return map(streams[0]!, (x) => [x]) as any + } + const [head, ...tail] = streams + return zipLatestWith( + head, + zipLatestAll(...tail), + (first, second) => [first, ...second] + ) as any +} + +/** @internal */ +export const zipLatestWith: { + ( + right: Stream.Stream, + f: (left: AL, right: AR) => A + ): (left: Stream.Stream) => Stream.Stream + ( + left: Stream.Stream, + right: Stream.Stream, + f: (left: AL, right: AR) => A + ): Stream.Stream +} = dual( + 3, + ( + left: Stream.Stream, + right: Stream.Stream, + f: (left: AL, right: AR) => A + ): Stream.Stream => { + const pullNonEmpty = <_R, _E, _A>( + pull: Effect.Effect, Option.Option<_E>, _R> + ): Effect.Effect, Option.Option<_E>, _R> => + pipe(pull, Effect.flatMap((chunk) => Chunk.isEmpty(chunk) ? pullNonEmpty(pull) : Effect.succeed(chunk))) + return pipe( + toPull(left), + Effect.map(pullNonEmpty), + Effect.zip(pipe(toPull(right), Effect.map(pullNonEmpty))), + Effect.flatMap(([left, right]) => + pipe( + fromEffectOption, Chunk.Chunk, boolean], EL | ER, RL | RR>( + Effect.raceWith(left, right, { + onSelfDone: (leftDone, rightFiber) => + pipe( + Effect.suspend(() => leftDone), + Effect.zipWith(Fiber.join(rightFiber), (l, r) => [l, r, true] as const) + ), + onOtherDone: (rightDone, leftFiber) => + pipe( + Effect.suspend(() => rightDone), + Effect.zipWith(Fiber.join(leftFiber), (l, r) => [r, l, false] as const) + ) + }) + ), + flatMap(([l, r, leftFirst]) => + pipe( + fromEffect( + Ref.make([Chunk.unsafeLast(l), Chunk.unsafeLast(r)] as const) + ), + flatMap((latest) => + pipe( + fromChunk( + leftFirst ? + pipe(r, Chunk.map((a2) => f(Chunk.unsafeLast(l), a2))) : + pipe(l, Chunk.map((a) => f(a, Chunk.unsafeLast(r)))) + ), + concat( + pipe( + repeatEffectOption(left), + mergeEither(repeatEffectOption(right)), + mapEffectSequential(Either.match({ + onLeft: (leftChunk) => + Ref.modify(latest, ([_, rightLatest]) => + [ + pipe(leftChunk, Chunk.map((a) => f(a, rightLatest))), + [Chunk.unsafeLast(leftChunk), rightLatest] as const + ] as const), + onRight: (rightChunk) => + Ref.modify(latest, ([leftLatest, _]) => + [ + pipe(rightChunk, Chunk.map((a2) => f(leftLatest, a2))), + [leftLatest, Chunk.unsafeLast(rightChunk)] as const + ] as const) + })), + flatMap(fromChunk) + ) + ) + ) + ) + ) + ), + toPull + ) + ), + fromPull + ) + } +) + +/** @internal */ +export const zipLeft: { + ( + right: Stream.Stream + ): (left: Stream.Stream) => Stream.Stream + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream +} = dual( + 2, + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream => + pipe( + left, + zipWithChunks(right, (left, right) => { + if (left.length > right.length) { + return [ + pipe(left, Chunk.take(right.length)), + Either.left(pipe(left, Chunk.take(right.length))) + ] as const + } + return [ + left, + Either.right(pipe(right, Chunk.drop(left.length))) + ] + }) + ) +) + +/** @internal */ +export const zipRight: { + ( + right: Stream.Stream + ): (left: Stream.Stream) => Stream.Stream + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream +} = dual( + 2, + ( + left: Stream.Stream, + right: Stream.Stream + ): Stream.Stream => + pipe( + left, + zipWithChunks(right, (left, right) => { + if (left.length > right.length) { + return [ + right, + Either.left(pipe(left, Chunk.take(right.length))) + ] as const + } + return [ + pipe(right, Chunk.take(left.length)), + Either.right(pipe(right, Chunk.drop(left.length))) + ] + }) + ) +) + +/** @internal */ +export const zipWith: { + ( + right: Stream.Stream, + f: (left: AL, right: AR) => A + ): (left: Stream.Stream) => Stream.Stream + ( + left: Stream.Stream, + right: Stream.Stream, + f: (left: AL, right: AR) => A + ): Stream.Stream +} = dual( + 3, + ( + left: Stream.Stream, + right: Stream.Stream, + f: (left: AL, right: AR) => A + ): Stream.Stream => + pipe(left, zipWithChunks(right, (leftChunk, rightChunk) => zipChunks(leftChunk, rightChunk, f))) +) + +/** @internal */ +export const zipWithChunks = dual< + ( + that: Stream.Stream, + f: ( + left: Chunk.Chunk, + right: Chunk.Chunk + ) => readonly [Chunk.Chunk, Either.Either, Chunk.Chunk>] + ) => (self: Stream.Stream) => Stream.Stream, + ( + self: Stream.Stream, + that: Stream.Stream, + f: ( + left: Chunk.Chunk, + right: Chunk.Chunk + ) => readonly [Chunk.Chunk, Either.Either, Chunk.Chunk>] + ) => Stream.Stream +>(3, ( + self: Stream.Stream, + that: Stream.Stream, + f: ( + left: Chunk.Chunk, + right: Chunk.Chunk + ) => readonly [Chunk.Chunk, Either.Either, Chunk.Chunk>] +): Stream.Stream => { + const pull = ( + state: ZipChunksState.ZipChunksState, + pullLeft: Effect.Effect, Option.Option, R>, + pullRight: Effect.Effect, Option.Option, R2> + ): Effect.Effect< + Exit.Exit, ZipChunksState.ZipChunksState], Option.Option>, + never, + R | R2 + > => { + switch (state._tag) { + case ZipChunksState.OP_PULL_BOTH: { + return pipe( + unsome(pullLeft), + Effect.zip(unsome(pullRight), { concurrent: true }), + Effect.matchEffect({ + onFailure: (error) => Effect.succeed(Exit.fail(Option.some(error))), + onSuccess: ([leftOption, rightOption]) => { + if (Option.isSome(leftOption) && Option.isSome(rightOption)) { + if (Chunk.isEmpty(leftOption.value) && Chunk.isEmpty(rightOption.value)) { + return pull(ZipChunksState.PullBoth, pullLeft, pullRight) + } + if (Chunk.isEmpty(leftOption.value)) { + return pull(ZipChunksState.PullLeft(rightOption.value), pullLeft, pullRight) + } + if (Chunk.isEmpty(rightOption.value)) { + return pull(ZipChunksState.PullRight(leftOption.value), pullLeft, pullRight) + } + return Effect.succeed(Exit.succeed(zip(leftOption.value, rightOption.value))) + } + return Effect.succeed(Exit.fail(Option.none())) + } + }) + ) + } + case ZipChunksState.OP_PULL_LEFT: { + return Effect.matchEffect(pullLeft, { + onFailure: (error) => Effect.succeed(Exit.fail(error)), + onSuccess: (leftChunk) => { + if (Chunk.isEmpty(leftChunk)) { + return pull(ZipChunksState.PullLeft(state.rightChunk), pullLeft, pullRight) + } + if (Chunk.isEmpty(state.rightChunk)) { + return pull(ZipChunksState.PullRight(leftChunk), pullLeft, pullRight) + } + return Effect.succeed(Exit.succeed(zip(leftChunk, state.rightChunk))) + } + }) + } + case ZipChunksState.OP_PULL_RIGHT: { + return Effect.matchEffect(pullRight, { + onFailure: (error) => Effect.succeed(Exit.fail(error)), + onSuccess: (rightChunk) => { + if (Chunk.isEmpty(rightChunk)) { + return pull(ZipChunksState.PullRight(state.leftChunk), pullLeft, pullRight) + } + if (Chunk.isEmpty(state.leftChunk)) { + return pull(ZipChunksState.PullLeft(rightChunk), pullLeft, pullRight) + } + return Effect.succeed(Exit.succeed(zip(state.leftChunk, rightChunk))) + } + }) + } + } + } + const zip = ( + leftChunk: Chunk.Chunk, + rightChunk: Chunk.Chunk + ): readonly [Chunk.Chunk, ZipChunksState.ZipChunksState] => { + const [output, either] = f(leftChunk, rightChunk) + switch (either._tag) { + case "Left": { + if (Chunk.isEmpty(either.left)) { + return [output, ZipChunksState.PullBoth] as const + } + return [output, ZipChunksState.PullRight(either.left)] as const + } + case "Right": { + if (Chunk.isEmpty(either.right)) { + return [output, ZipChunksState.PullBoth] as const + } + return [output, ZipChunksState.PullLeft(either.right)] as const + } + } + } + return pipe( + self, + combineChunks(that, ZipChunksState.PullBoth, pull) + ) +}) + +/** @internal */ +export const zipWithIndex = (self: Stream.Stream): Stream.Stream<[A, number], E, R> => + pipe(self, mapAccum(0, (index, a) => [index + 1, [a, index]])) + +/** @internal */ +export const zipWithNext = ( + self: Stream.Stream +): Stream.Stream<[A, Option.Option], E, R> => { + const process = ( + last: Option.Option + ): Channel.Channel]>, Chunk.Chunk, never, never, void, unknown> => + core.readWithCause({ + onInput: (input: Chunk.Chunk) => { + const [newLast, chunk] = Chunk.mapAccum( + input, + last, + (prev, curr) => [Option.some(curr), pipe(prev, Option.map((a) => [a, curr] as const))] as const + ) + const output = Chunk.filterMap( + chunk, + (option) => + Option.isSome(option) ? + Option.some([option.value[0], Option.some(option.value[1])] as const) : + Option.none() + ) + return core.flatMap( + core.write(output), + () => process(newLast) + ) + }, + onFailure: core.failCause, + onDone: () => + Option.match(last, { + onNone: () => core.void, + onSome: (value) => + channel.zipRight( + core.write(Chunk.of]>([value, Option.none()])), + core.void + ) + }) + }) + return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(process(Option.none())))) +} + +/** @internal */ +export const zipWithPrevious = ( + self: Stream.Stream +): Stream.Stream<[Option.Option, A], E, R> => + pipe( + self, + mapAccum, A, [Option.Option, A]>( + Option.none(), + (prev, curr) => [Option.some(curr), [prev, curr]] + ) + ) + +/** @internal */ +export const zipWithPreviousAndNext = ( + self: Stream.Stream +): Stream.Stream<[Option.Option, A, Option.Option], E, R> => + pipe( + zipWithNext(zipWithPrevious(self)), + map(([[prev, curr], next]) => [prev, curr, pipe(next, Option.map((tuple) => tuple[1]))]) + ) + +/** @internal */ +const zipChunks = ( + left: Chunk.Chunk, + right: Chunk.Chunk, + f: (a: A, b: B) => C +): [Chunk.Chunk, Either.Either, Chunk.Chunk>] => { + if (left.length > right.length) { + return [ + pipe(left, Chunk.take(right.length), Chunk.zipWith(right, f)), + Either.left(pipe(left, Chunk.drop(right.length))) + ] + } + return [ + pipe(left, Chunk.zipWith(pipe(right, Chunk.take(left.length)), f)), + Either.right(pipe(right, Chunk.drop(left.length))) + ] +} + +// Do notation + +/** @internal */ +export const Do: Stream.Stream<{}> = succeed({}) + +/** @internal */ +export const bind = dual< + ( + tag: Exclude, + f: (_: Types.NoInfer) => Stream.Stream, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly bufferSize?: number | undefined + } + ) => (self: Stream.Stream) => Stream.Stream< + { [K in keyof A | N]: K extends keyof A ? A[K] : B }, + E | E2, + R | R2 + >, + ( + self: Stream.Stream, + tag: Exclude, + f: (_: Types.NoInfer) => Stream.Stream, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly bufferSize?: number | undefined + } + ) => Stream.Stream< + { [K in keyof A | N]: K extends keyof A ? A[K] : B }, + E | E2, + R | R2 + > +>((args) => typeof args[0] !== "string", ( + self: Stream.Stream, + tag: Exclude, + f: (_: A) => Stream.Stream, + options?: { + readonly concurrency?: number | "unbounded" | undefined + readonly bufferSize?: number | undefined + } +) => + flatMap(self, (k) => + map( + f(k), + (a) => ({ ...k, [tag]: a } as { [K in keyof A | N]: K extends keyof A ? A[K] : B }) + ), options)) + +/* @internal */ +export const bindTo: { + (name: N): (self: Stream.Stream) => Stream.Stream<{ [K in N]: A }, E, R> + (self: Stream.Stream, name: N): Stream.Stream<{ [K in N]: A }, E, R> +} = doNotation.bindTo(map) + +/* @internal */ +export const let_: { + ( + name: Exclude, + f: (a: Types.NoInfer) => B + ): ( + self: Stream.Stream + ) => Stream.Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E, R> + ( + self: Stream.Stream, + name: Exclude, + f: (a: Types.NoInfer) => B + ): Stream.Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E, R> +} = doNotation.let_(map) + +// Circular with Channel + +/** @internal */ +export const channelToStream = ( + self: Channel.Channel, unknown, OutErr, unknown, OutDone, unknown, Env> +): Stream.Stream => { + return new StreamImpl(self) +} + +// ============================================================================= +// encoding +// ============================================================================= + +/** @internal */ +export const decodeText = dual< + (encoding?: string) => (self: Stream.Stream) => Stream.Stream, + (self: Stream.Stream, encoding?: string) => Stream.Stream +>((args) => isStream(args[0]), (self, encoding = "utf-8") => + suspend(() => { + const decoder = new TextDecoder(encoding) + return map(self, (s) => decoder.decode(s, { stream: true })) + })) + +/** @internal */ +export const encodeText = (self: Stream.Stream): Stream.Stream => + suspend(() => { + const encoder = new TextEncoder() + return map(self, (s) => encoder.encode(s)) + }) + +/** @internal */ +export const fromEventListener = ( + target: Stream.EventListener, + type: string, + options?: boolean | { + readonly capture?: boolean + readonly passive?: boolean + readonly once?: boolean + readonly bufferSize?: number | "unbounded" | undefined + } | undefined +): Stream.Stream => + asyncPush((emit) => + Effect.acquireRelease( + Effect.sync(() => target.addEventListener(type, emit.single as any, options)), + () => Effect.sync(() => target.removeEventListener(type, emit.single, options)) + ), { bufferSize: typeof options === "object" ? options.bufferSize : undefined }) diff --git a/backend/node_modules/effect/src/internal/stream/debounceState.ts b/backend/node_modules/effect/src/internal/stream/debounceState.ts new file mode 100644 index 0000000000000000000000000000000000000000..3df63a8bce2ec762dc447ffb6558317713d64e38 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stream/debounceState.ts @@ -0,0 +1,57 @@ +import type * as Chunk from "../../Chunk.js" +import type * as Fiber from "../../Fiber.js" +import type * as HandoffSignal from "./handoffSignal.js" + +/** @internal */ +export type DebounceState = NotStarted | Previous | Current + +/** @internal */ +export const OP_NOT_STARTED = "NotStarted" as const + +/** @internal */ +export type OP_NOT_STARTED = typeof OP_NOT_STARTED + +/** @internal */ +export const OP_PREVIOUS = "Previous" as const + +/** @internal */ +export type OP_PREVIOUS = typeof OP_PREVIOUS + +/** @internal */ +export const OP_CURRENT = "Current" as const + +/** @internal */ +export type OP_CURRENT = typeof OP_CURRENT + +export interface NotStarted { + readonly _tag: OP_NOT_STARTED +} + +/** @internal */ +export interface Previous { + readonly _tag: OP_PREVIOUS + readonly fiber: Fiber.Fiber> +} + +/** @internal */ +export interface Current { + readonly _tag: OP_CURRENT + readonly fiber: Fiber.Fiber, E> +} + +/** @internal */ +export const notStarted: DebounceState = { + _tag: OP_NOT_STARTED +} + +/** @internal */ +export const previous = (fiber: Fiber.Fiber>): DebounceState => ({ + _tag: OP_PREVIOUS, + fiber +}) + +/** @internal */ +export const current = (fiber: Fiber.Fiber, E>): DebounceState => ({ + _tag: OP_CURRENT, + fiber +}) diff --git a/backend/node_modules/effect/src/internal/stream/emit.ts b/backend/node_modules/effect/src/internal/stream/emit.ts new file mode 100644 index 0000000000000000000000000000000000000000..7dd1623d430dc1bdd58446c8c8a9576fc39e6712 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stream/emit.ts @@ -0,0 +1,123 @@ +import * as Cause from "../../Cause.js" +import * as Chunk from "../../Chunk.js" +import * as Effect from "../../Effect.js" +import * as Exit from "../../Exit.js" +import { pipe } from "../../Function.js" +import * as Option from "../../Option.js" +import type * as Queue from "../../Queue.js" +import type * as Scheduler from "../../Scheduler.js" +import type * as Emit from "../../StreamEmit.js" + +/** @internal */ +export const make = ( + emit: (f: Effect.Effect, Option.Option, R>) => Promise +): Emit.Emit => { + const ops: Emit.EmitOps = { + chunk(this: Emit.Emit, as: Chunk.Chunk) { + return this(Effect.succeed(as)) + }, + die(this: Emit.Emit, defect: Err) { + return this(Effect.die(defect)) + }, + dieMessage(this: Emit.Emit, message: string) { + return this(Effect.dieMessage(message)) + }, + done(this: Emit.Emit, exit: Exit.Exit) { + return this(Effect.suspend(() => Exit.mapBoth(exit, { onFailure: Option.some, onSuccess: Chunk.of }))) + }, + end(this: Emit.Emit) { + return this(Effect.fail(Option.none())) + }, + fail(this: Emit.Emit, e: E) { + return this(Effect.fail(Option.some(e))) + }, + fromEffect(this: Emit.Emit, effect: Effect.Effect) { + return this(Effect.mapBoth(effect, { onFailure: Option.some, onSuccess: Chunk.of })) + }, + fromEffectChunk(this: Emit.Emit, effect: Effect.Effect, E, R>) { + return this(pipe(effect, Effect.mapError(Option.some))) + }, + halt(this: Emit.Emit, cause: Cause.Cause) { + return this(Effect.failCause(pipe(cause, Cause.map(Option.some)))) + }, + single(this: Emit.Emit, value: A) { + return this(Effect.succeed(Chunk.of(value))) + } + } + return Object.assign(emit, ops) +} + +/** @internal */ +export const makePush = ( + queue: Queue.Queue | Exit.Exit>, + scheduler: Scheduler.Scheduler +): Emit.EmitOpsPush => { + let finished = false + let buffer: Array = [] + let running = false + function array(items: ReadonlyArray) { + if (finished) return false + if (items.length <= 50_000) { + buffer.push.apply(buffer, items as Array) + } else { + for (let i = 0; i < items.length; i++) { + buffer.push(items[0]) + } + } + if (!running) { + running = true + scheduler.scheduleTask(flush, 0) + } + return true + } + function flush() { + running = false + if (buffer.length > 0) { + queue.unsafeOffer(buffer) + buffer = [] + } + } + function done(exit: Exit.Exit) { + if (finished) return + finished = true + if (exit._tag === "Success") { + buffer.push(exit.value) + } + flush() + queue.unsafeOffer(exit._tag === "Success" ? Exit.void : exit) + } + return { + single(value: A) { + if (finished) return false + buffer.push(value) + if (!running) { + running = true + scheduler.scheduleTask(flush, 0) + } + return true + }, + array, + chunk(chunk) { + return array(Chunk.toReadonlyArray(chunk)) + }, + done, + end() { + if (finished) return + finished = true + flush() + queue.unsafeOffer(Exit.void) + }, + halt(cause: Cause.Cause) { + return done(Exit.failCause(cause)) + }, + fail(error: E) { + return done(Exit.fail(error)) + }, + die(defect: Err): void { + return done(Exit.die(defect)) + }, + dieMessage(message: string): void { + return done(Exit.die(new Error(message))) + } + } +} diff --git a/backend/node_modules/effect/src/internal/stream/haltStrategy.ts b/backend/node_modules/effect/src/internal/stream/haltStrategy.ts new file mode 100644 index 0000000000000000000000000000000000000000..0115554ef47bc701d9adb9ce2a75091311da52de --- /dev/null +++ b/backend/node_modules/effect/src/internal/stream/haltStrategy.ts @@ -0,0 +1,94 @@ +import { dual } from "../../Function.js" +import type * as HaltStrategy from "../../StreamHaltStrategy.js" +import * as OpCodes from "../opCodes/streamHaltStrategy.js" + +/** @internal */ +export const Left: HaltStrategy.HaltStrategy = { + _tag: OpCodes.OP_LEFT +} + +/** @internal */ +export const Right: HaltStrategy.HaltStrategy = { + _tag: OpCodes.OP_RIGHT +} + +/** @internal */ +export const Both: HaltStrategy.HaltStrategy = { + _tag: OpCodes.OP_BOTH +} + +/** @internal */ +export const Either: HaltStrategy.HaltStrategy = { + _tag: OpCodes.OP_EITHER +} + +/** @internal */ +export const fromInput = (input: HaltStrategy.HaltStrategyInput): HaltStrategy.HaltStrategy => { + switch (input) { + case "left": + return Left + case "right": + return Right + case "both": + return Both + case "either": + return Either + default: + return input + } +} + +/** @internal */ +export const isLeft = (self: HaltStrategy.HaltStrategy): self is HaltStrategy.Left => self._tag === OpCodes.OP_LEFT + +/** @internal */ +export const isRight = (self: HaltStrategy.HaltStrategy): self is HaltStrategy.Right => self._tag === OpCodes.OP_RIGHT + +/** @internal */ +export const isBoth = (self: HaltStrategy.HaltStrategy): self is HaltStrategy.Both => self._tag === OpCodes.OP_BOTH + +/** @internal */ +export const isEither = (self: HaltStrategy.HaltStrategy): self is HaltStrategy.Either => + self._tag === OpCodes.OP_EITHER + +/** @internal */ +export const match = dual< + (options: { + readonly onLeft: () => Z + readonly onRight: () => Z + readonly onBoth: () => Z + readonly onEither: () => Z + }) => (self: HaltStrategy.HaltStrategy) => Z, + ( + self: HaltStrategy.HaltStrategy, + options: { + readonly onLeft: () => Z + readonly onRight: () => Z + readonly onBoth: () => Z + readonly onEither: () => Z + } + ) => Z +>(2, ( + self: HaltStrategy.HaltStrategy, + options: { + readonly onLeft: () => Z + readonly onRight: () => Z + readonly onBoth: () => Z + readonly onEither: () => Z + } +): Z => { + switch (self._tag) { + case OpCodes.OP_LEFT: { + return options.onLeft() + } + case OpCodes.OP_RIGHT: { + return options.onRight() + } + case OpCodes.OP_BOTH: { + return options.onBoth() + } + case OpCodes.OP_EITHER: { + return options.onEither() + } + } +}) diff --git a/backend/node_modules/effect/src/internal/stream/handoff.ts b/backend/node_modules/effect/src/internal/stream/handoff.ts new file mode 100644 index 0000000000000000000000000000000000000000..175213b0d40e6ba3485c0a1a8e49168b7c22f988 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stream/handoff.ts @@ -0,0 +1,187 @@ +import * as Deferred from "../../Deferred.js" +import * as Effect from "../../Effect.js" +import { dual, pipe } from "../../Function.js" +import * as Option from "../../Option.js" +import * as Ref from "../../Ref.js" + +/** @internal */ +export const HandoffTypeId = Symbol.for("effect/Stream/Handoff") + +/** @internal */ +export type HandoffTypeId = typeof HandoffTypeId + +/** + * A synchronous queue-like abstraction that allows a producer to offer an + * element and wait for it to be taken, and allows a consumer to wait for an + * element to be available. + * + * @internal + */ +export interface Handoff extends Handoff.Variance { + readonly ref: Ref.Ref> +} + +/** @internal */ +export const OP_HANDOFF_STATE_EMPTY = "Empty" as const + +/** @internal */ +export type OP_HANDOFF_STATE_EMPTY = typeof OP_HANDOFF_STATE_EMPTY + +/** @internal */ +export const OP_HANDOFF_STATE_FULL = "Full" as const + +/** @internal */ +export type OP_HANDOFF_STATE_FULL = typeof OP_HANDOFF_STATE_FULL + +/** @internal */ +export declare namespace Handoff { + /** @internal */ + export interface Variance { + readonly [HandoffTypeId]: { + readonly _A: (_: A) => A + } + } + + /** @internal */ + export type State = Empty | Full + + /** @internal */ + export interface Empty { + readonly _tag: OP_HANDOFF_STATE_EMPTY + readonly notifyConsumer: Deferred.Deferred + } + + /** @internal */ + export interface Full { + readonly _tag: OP_HANDOFF_STATE_FULL + readonly value: A + readonly notifyProducer: Deferred.Deferred + } +} + +/** @internal */ +const handoffStateEmpty = (notifyConsumer: Deferred.Deferred): Handoff.State => ({ + _tag: OP_HANDOFF_STATE_EMPTY, + notifyConsumer +}) + +/** @internal */ +const handoffStateFull = (value: A, notifyProducer: Deferred.Deferred): Handoff.State => ({ + _tag: OP_HANDOFF_STATE_FULL, + value, + notifyProducer +}) + +/** @internal */ +const handoffStateMatch = ( + onEmpty: (notifyConsumer: Deferred.Deferred) => Z, + onFull: (value: A, notifyProducer: Deferred.Deferred) => Z +) => { + return (self: Handoff.State): Z => { + switch (self._tag) { + case OP_HANDOFF_STATE_EMPTY: { + return onEmpty(self.notifyConsumer) + } + case OP_HANDOFF_STATE_FULL: { + return onFull(self.value, self.notifyProducer) + } + } + } +} + +const handoffVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +export const make = (): Effect.Effect> => + pipe( + Deferred.make(), + Effect.flatMap((deferred) => Ref.make(handoffStateEmpty(deferred))), + Effect.map((ref): Handoff => ({ + [HandoffTypeId]: handoffVariance, + ref + })) + ) + +/** @internal */ +export const offer = dual< + (value: A) => (self: Handoff) => Effect.Effect, + (self: Handoff, value: A) => Effect.Effect +>(2, (self, value): Effect.Effect => { + return Effect.flatMap(Deferred.make(), (deferred) => + Effect.flatten( + Ref.modify(self.ref, (state) => + pipe( + state, + handoffStateMatch( + (notifyConsumer) => [ + Effect.zipRight( + Deferred.succeed(notifyConsumer, void 0), + Deferred.await(deferred) + ), + handoffStateFull(value, deferred) + ], + (_, notifyProducer) => [ + Effect.flatMap( + Deferred.await(notifyProducer), + () => pipe(self, offer(value)) + ), + state + ] + ) + )) + )) +}) + +/** @internal */ +export const take = (self: Handoff): Effect.Effect => + Effect.flatMap(Deferred.make(), (deferred) => + Effect.flatten( + Ref.modify(self.ref, (state) => + pipe( + state, + handoffStateMatch( + (notifyConsumer) => + [ + Effect.flatMap( + Deferred.await(notifyConsumer), + () => take(self) + ), + state + ] as const, + (value, notifyProducer) => [ + Effect.as( + Deferred.succeed(notifyProducer, void 0), + value + ), + handoffStateEmpty(deferred) + ] + ) + )) + )) + +/** @internal */ +export const poll = (self: Handoff): Effect.Effect> => + Effect.flatMap(Deferred.make(), (deferred) => + Effect.flatten( + Ref.modify(self.ref, (state) => + pipe( + state, + handoffStateMatch( + () => + [ + Effect.succeed(Option.none()), + state + ] as const, + (value, notifyProducer) => [ + Effect.as( + Deferred.succeed(notifyProducer, void 0), + Option.some(value) + ), + handoffStateEmpty(deferred) + ] + ) + )) + )) diff --git a/backend/node_modules/effect/src/internal/stream/handoffSignal.ts b/backend/node_modules/effect/src/internal/stream/handoffSignal.ts new file mode 100644 index 0000000000000000000000000000000000000000..f42a26db6f2a55890cad8635b79c4cdd52cd57fc --- /dev/null +++ b/backend/node_modules/effect/src/internal/stream/handoffSignal.ts @@ -0,0 +1,59 @@ +import type * as Cause from "../../Cause.js" +import type * as Chunk from "../../Chunk.js" +import type * as SinkEndReason from "./sinkEndReason.js" + +/** @internal */ +export type HandoffSignal = Emit | Halt | End + +/** @internal */ +export const OP_EMIT = "Emit" as const + +/** @internal */ +export type OP_EMIT = typeof OP_EMIT + +/** @internal */ +export const OP_HALT = "Halt" as const + +/** @internal */ +export type OP_HALT = typeof OP_HALT + +/** @internal */ +export const OP_END = "End" as const + +/** @internal */ +export type OP_END = typeof OP_END + +export interface Emit { + readonly _tag: OP_EMIT + readonly elements: Chunk.Chunk +} + +/** @internal */ +export interface Halt { + readonly _tag: OP_HALT + readonly cause: Cause.Cause +} + +/** @internal */ +export interface End { + readonly _tag: OP_END + readonly reason: SinkEndReason.SinkEndReason +} + +/** @internal */ +export const emit = (elements: Chunk.Chunk): HandoffSignal => ({ + _tag: OP_EMIT, + elements +}) + +/** @internal */ +export const halt = (cause: Cause.Cause): HandoffSignal => ({ + _tag: OP_HALT, + cause +}) + +/** @internal */ +export const end = (reason: SinkEndReason.SinkEndReason): HandoffSignal => ({ + _tag: OP_END, + reason +}) diff --git a/backend/node_modules/effect/src/internal/stream/pull.ts b/backend/node_modules/effect/src/internal/stream/pull.ts new file mode 100644 index 0000000000000000000000000000000000000000..2f1d8e3c42197943a530831d1c0d9bb1ef25eea0 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stream/pull.ts @@ -0,0 +1,34 @@ +import type * as Cause from "../../Cause.js" +import * as Chunk from "../../Chunk.js" +import * as Effect from "../../Effect.js" +import * as Option from "../../Option.js" +import * as Queue from "../../Queue.js" +import type * as Take from "../../Take.js" +import * as take from "../take.js" + +/** @internal */ +export interface Pull extends Effect.Effect, Option.Option, R> {} + +/** @internal */ +export const emit = (value: A): Effect.Effect> => Effect.succeed(Chunk.of(value)) + +/** @internal */ +export const emitChunk = (chunk: Chunk.Chunk): Effect.Effect> => Effect.succeed(chunk) + +/** @internal */ +export const empty = (): Effect.Effect> => Effect.succeed(Chunk.empty()) + +/** @internal */ +export const end = (): Effect.Effect> => Effect.fail(Option.none()) + +/** @internal */ +export const fail = (error: E): Effect.Effect> => Effect.fail(Option.some(error)) + +/** @internal */ +export const failCause = (cause: Cause.Cause): Effect.Effect> => + Effect.mapError(Effect.failCause(cause), Option.some) + +/** @internal */ +export const fromDequeue = ( + dequeue: Queue.Dequeue> +): Effect.Effect, Option.Option> => Effect.flatMap(Queue.take(dequeue), take.done) diff --git a/backend/node_modules/effect/src/internal/stream/sinkEndReason.ts b/backend/node_modules/effect/src/internal/stream/sinkEndReason.ts new file mode 100644 index 0000000000000000000000000000000000000000..5c0ab6ca6d97f1eee01e221d2ee215c23dc4c15e --- /dev/null +++ b/backend/node_modules/effect/src/internal/stream/sinkEndReason.ts @@ -0,0 +1,30 @@ +/** @internal */ +export type SinkEndReason = ScheduleEnd | UpstreamEnd + +/** @internal */ +export const OP_SCHEDULE_END = "ScheduleEnd" as const + +/** @internal */ +export type OP_SCHEDULE_END = typeof OP_SCHEDULE_END + +/** @internal */ +export const OP_UPSTREAM_END = "UpstreamEnd" as const + +/** @internal */ +export type OP_UPSTREAM_END = typeof OP_UPSTREAM_END + +/** @internal */ +export interface ScheduleEnd { + readonly _tag: OP_SCHEDULE_END +} + +/** @internal */ +export interface UpstreamEnd { + readonly _tag: OP_UPSTREAM_END +} + +/** @internal */ +export const ScheduleEnd: SinkEndReason = { _tag: OP_SCHEDULE_END } + +/** @internal */ +export const UpstreamEnd: SinkEndReason = { _tag: OP_UPSTREAM_END } diff --git a/backend/node_modules/effect/src/internal/stream/zipAllState.ts b/backend/node_modules/effect/src/internal/stream/zipAllState.ts new file mode 100644 index 0000000000000000000000000000000000000000..710278330eeaa58a260a6e01e7de336021442806 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stream/zipAllState.ts @@ -0,0 +1,88 @@ +import type * as Chunk from "../../Chunk.js" + +/** @internal */ +export type ZipAllState = DrainLeft | DrainRight | PullBoth | PullLeft | PullRight + +/** @internal */ +export const OP_DRAIN_LEFT = "DrainLeft" as const + +/** @internal */ +export type OP_DRAIN_LEFT = typeof OP_DRAIN_LEFT + +/** @internal */ +export const OP_DRAIN_RIGHT = "DrainRight" as const + +/** @internal */ +export type OP_DRAIN_RIGHT = typeof OP_DRAIN_RIGHT + +/** @internal */ +export const OP_PULL_BOTH = "PullBoth" as const + +/** @internal */ +export type OP_PULL_BOTH = typeof OP_PULL_BOTH + +/** @internal */ +export const OP_PULL_LEFT = "PullLeft" as const + +/** @internal */ +export type OP_PULL_LEFT = typeof OP_PULL_LEFT + +/** @internal */ +export const OP_PULL_RIGHT = "PullRight" as const + +/** @internal */ +export type OP_PULL_RIGHT = typeof OP_PULL_RIGHT + +/** @internal */ +export interface DrainLeft { + readonly _tag: OP_DRAIN_LEFT +} + +/** @internal */ +export interface DrainRight { + readonly _tag: OP_DRAIN_RIGHT +} + +/** @internal */ +export interface PullBoth { + readonly _tag: OP_PULL_BOTH +} + +/** @internal */ +export interface PullLeft { + readonly _tag: OP_PULL_LEFT + readonly rightChunk: Chunk.Chunk +} + +/** @internal */ +export interface PullRight { + readonly _tag: OP_PULL_RIGHT + readonly leftChunk: Chunk.Chunk +} + +/** @internal */ +export const DrainLeft: ZipAllState = { + _tag: OP_DRAIN_LEFT +} + +/** @internal */ +export const DrainRight: ZipAllState = { + _tag: OP_DRAIN_RIGHT +} + +/** @internal */ +export const PullBoth: ZipAllState = { + _tag: OP_PULL_BOTH +} + +/** @internal */ +export const PullLeft = (rightChunk: Chunk.Chunk): ZipAllState => ({ + _tag: OP_PULL_LEFT, + rightChunk +}) + +/** @internal */ +export const PullRight = (leftChunk: Chunk.Chunk): ZipAllState => ({ + _tag: OP_PULL_RIGHT, + leftChunk +}) diff --git a/backend/node_modules/effect/src/internal/stream/zipChunksState.ts b/backend/node_modules/effect/src/internal/stream/zipChunksState.ts new file mode 100644 index 0000000000000000000000000000000000000000..a873407da777241786de2024d46b5774799d2f39 --- /dev/null +++ b/backend/node_modules/effect/src/internal/stream/zipChunksState.ts @@ -0,0 +1,56 @@ +import type * as Chunk from "../../Chunk.js" + +/** @internal */ +export type ZipChunksState = PullBoth | PullLeft | PullRight + +/** @internal */ +export const OP_PULL_BOTH = "PullBoth" as const + +/** @internal */ +export type OP_PULL_BOTH = typeof OP_PULL_BOTH + +/** @internal */ +export const OP_PULL_LEFT = "PullLet" as const + +/** @internal */ +export type OP_PULL_LEFT = typeof OP_PULL_LEFT + +/** @internal */ +export const OP_PULL_RIGHT = "PullRight" as const + +/** @internal */ +export type OP_PULL_RIGHT = typeof OP_PULL_RIGHT + +/** @internal */ +export interface PullBoth { + readonly _tag: OP_PULL_BOTH +} + +/** @internal */ +export interface PullLeft { + readonly _tag: OP_PULL_LEFT + readonly rightChunk: Chunk.Chunk +} + +/** @internal */ +export interface PullRight { + readonly _tag: OP_PULL_RIGHT + readonly leftChunk: Chunk.Chunk +} + +/** @internal */ +export const PullBoth: ZipChunksState = { + _tag: OP_PULL_BOTH +} + +/** @internal */ +export const PullLeft = (rightChunk: Chunk.Chunk): ZipChunksState => ({ + _tag: OP_PULL_LEFT, + rightChunk +}) + +/** @internal */ +export const PullRight = (leftChunk: Chunk.Chunk): ZipChunksState => ({ + _tag: OP_PULL_RIGHT, + leftChunk +}) diff --git a/backend/node_modules/effect/src/internal/string-utils.ts b/backend/node_modules/effect/src/internal/string-utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..b59d6d031cb3e5a2285ec88e4c1ed99aaa747990 --- /dev/null +++ b/backend/node_modules/effect/src/internal/string-utils.ts @@ -0,0 +1,107 @@ +/** + * Adapted from the `change-case` library. + * + * Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com) + */ + +/** @internal */ +export const lowerCase = (str: string) => str.toLowerCase() + +/** @internal */ +export const upperCase = (str: string) => str.toUpperCase() + +interface Options { + splitRegexp?: RegExp | ReadonlyArray + stripRegexp?: RegExp | ReadonlyArray + delimiter?: string + transform?: (part: string, index: number, parts: ReadonlyArray) => string +} + +/** + * Replace `re` in the input string with the replacement value. + */ +const replace = (input: string, re: RegExp | ReadonlyArray, value: string): string => + re instanceof RegExp + ? input.replace(re, value) + : re.reduce((input, re) => input.replace(re, value), input) + +// Support camel case ("camelCase" -> "camel Case" and "CAMELCase" -> "CAMEL Case"). +const DEFAULT_SPLIT_REGEXP = [/([a-z0-9])([A-Z])/g, /([A-Z])([A-Z][a-z])/g] + +// Remove all non-word characters. +const DEFAULT_STRIP_REGEXP = /[^A-Z0-9]+/gi + +/** + * Normalize the string into something other libraries can manipulate easier. + */ +const noCase = (input: string, options: Options = {}): string => { + const { + delimiter = " ", + splitRegexp = DEFAULT_SPLIT_REGEXP, + stripRegexp = DEFAULT_STRIP_REGEXP, + transform = lowerCase + } = options + const result = replace(replace(input, splitRegexp, "$1\0$2"), stripRegexp, "\0") + let start = 0 + let end = result.length + // Trim the delimiter from around the output string. + while (result.charAt(start) === "\0") { + start++ + } + while (result.charAt(end - 1) === "\0") { + end-- + } + // Transform each token independently. + return result.slice(start, end).split("\0").map(transform).join(delimiter) +} + +const pascalCaseTransform = (input: string, index: number): string => { + const firstChar = input.charAt(0) + const lowerChars = input.substring(1).toLowerCase() + if (index > 0 && firstChar >= "0" && firstChar <= "9") { + return `_${firstChar}${lowerChars}` + } + return `${firstChar.toUpperCase()}${lowerChars}` +} + +/** @internal */ +export const pascalCase = (input: string, options?: Options): string => + noCase(input, { + delimiter: "", + transform: pascalCaseTransform, + ...options + }) + +const camelCaseTransform = (input: string, index: number): string => + index === 0 + ? input.toLowerCase() + : pascalCaseTransform(input, index) + +/** @internal */ +export const camelCase = (input: string, options?: Options): string => + pascalCase(input, { + transform: camelCaseTransform, + ...options + }) + +/** @internal */ +export const constantCase = (input: string, options?: Options): string => + noCase(input, { + delimiter: "_", + transform: upperCase, + ...options + }) + +/** @internal */ +export const kebabCase = (input: string, options?: Options) => + noCase(input, { + delimiter: "-", + ...options + }) + +/** @internal */ +export const snakeCase = (input: string, options?: Options) => + noCase(input, { + delimiter: "_", + ...options + }) diff --git a/backend/node_modules/effect/src/internal/subscriptionRef.ts b/backend/node_modules/effect/src/internal/subscriptionRef.ts new file mode 100644 index 0000000000000000000000000000000000000000..24bb69cb1363d69120069c351632a8ee27a05768 --- /dev/null +++ b/backend/node_modules/effect/src/internal/subscriptionRef.ts @@ -0,0 +1,138 @@ +import * as Effect from "../Effect.js" +import * as Effectable from "../Effectable.js" +import { dual, pipe } from "../Function.js" +import * as PubSub from "../PubSub.js" +import * as Readable from "../Readable.js" +import * as Ref from "../Ref.js" +import type { Stream } from "../Stream.js" +import * as Subscribable from "../Subscribable.js" +import type * as SubscriptionRef from "../SubscriptionRef.js" +import * as Synchronized from "../SynchronizedRef.js" +import * as circular_ from "./effect/circular.js" +import * as ref_ from "./ref.js" +import * as stream from "./stream.js" + +/** @internal */ +const SubscriptionRefSymbolKey = "effect/SubscriptionRef" + +/** @internal */ +export const SubscriptionRefTypeId: SubscriptionRef.SubscriptionRefTypeId = Symbol.for( + SubscriptionRefSymbolKey +) as SubscriptionRef.SubscriptionRefTypeId + +const subscriptionRefVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +/** @internal */ +class SubscriptionRefImpl extends Effectable.Class implements SubscriptionRef.SubscriptionRef { + readonly [Readable.TypeId]: Readable.TypeId = Readable.TypeId + readonly [Subscribable.TypeId]: Subscribable.TypeId = Subscribable.TypeId + readonly [Ref.RefTypeId] = ref_.refVariance + readonly [Synchronized.SynchronizedRefTypeId] = circular_.synchronizedVariance + readonly [SubscriptionRefTypeId] = subscriptionRefVariance + constructor( + readonly ref: Ref.Ref, + readonly pubsub: PubSub.PubSub, + readonly semaphore: Effect.Semaphore + ) { + super() + this.get = Ref.get(this.ref) + } + commit() { + return this.get + } + readonly get: Effect.Effect + get changes(): Stream { + return pipe( + Ref.get(this.ref), + Effect.flatMap((a) => + Effect.map( + stream.fromPubSub(this.pubsub, { scoped: true }), + (s) => + stream.concat( + stream.make(a), + s + ) + ) + ), + this.semaphore.withPermits(1), + stream.unwrapScoped + ) + } + modify(f: (a: A) => readonly [B, A]): Effect.Effect { + return this.modifyEffect((a) => Effect.succeed(f(a))) + } + modifyEffect(f: (a: A) => Effect.Effect): Effect.Effect { + return pipe( + Ref.get(this.ref), + Effect.flatMap(f), + Effect.flatMap(([b, a]) => + pipe( + Ref.set(this.ref, a), + Effect.as(b), + Effect.zipLeft(PubSub.publish(this.pubsub, a)) + ) + ), + this.semaphore.withPermits(1) + ) + } +} + +/** @internal */ +export const get = (self: SubscriptionRef.SubscriptionRef): Effect.Effect => Ref.get(self.ref) + +/** @internal */ +export const make = (value: A): Effect.Effect> => + pipe( + Effect.all([ + PubSub.unbounded(), + Ref.make(value), + Effect.makeSemaphore(1) + ]), + Effect.map(([pubsub, ref, semaphore]) => new SubscriptionRefImpl(ref, pubsub, semaphore)) + ) + +/** @internal */ +export const modify = dual< + (f: (a: A) => readonly [B, A]) => (self: SubscriptionRef.SubscriptionRef) => Effect.Effect, + ( + self: SubscriptionRef.SubscriptionRef, + f: (a: A) => readonly [B, A] + ) => Effect.Effect +>(2, ( + self: SubscriptionRef.SubscriptionRef, + f: (a: A) => readonly [B, A] +): Effect.Effect => self.modify(f)) + +/** @internal */ +export const modifyEffect = dual< + ( + f: (a: A) => Effect.Effect + ) => (self: SubscriptionRef.SubscriptionRef) => Effect.Effect, + ( + self: SubscriptionRef.SubscriptionRef, + f: (a: A) => Effect.Effect + ) => Effect.Effect +>(2, ( + self: SubscriptionRef.SubscriptionRef, + f: (a: A) => Effect.Effect +): Effect.Effect => self.modifyEffect(f)) + +/** @internal */ +export const set = dual< + (value: A) => (self: SubscriptionRef.SubscriptionRef) => Effect.Effect, + ( + self: SubscriptionRef.SubscriptionRef, + value: A + ) => Effect.Effect +>(2, ( + self: SubscriptionRef.SubscriptionRef, + value: A +): Effect.Effect => + pipe( + Ref.set(self.ref, value), + Effect.zipLeft(PubSub.publish(self.pubsub, value)), + self.semaphore.withPermits(1) + )) diff --git a/backend/node_modules/effect/src/internal/supervisor.ts b/backend/node_modules/effect/src/internal/supervisor.ts new file mode 100644 index 0000000000000000000000000000000000000000..285df2e6246e5604e12c07caa72cc0e2c70c7528 --- /dev/null +++ b/backend/node_modules/effect/src/internal/supervisor.ts @@ -0,0 +1,303 @@ +import type * as Context from "../Context.js" +import type * as Effect from "../Effect.js" +import type * as Exit from "../Exit.js" +import type * as Fiber from "../Fiber.js" +import { pipe } from "../Function.js" +import { globalValue } from "../GlobalValue.js" +import * as MutableRef from "../MutableRef.js" +import type * as Option from "../Option.js" +import { hasProperty, isTagged } from "../Predicate.js" +import * as SortedSet from "../SortedSet.js" +import type * as Supervisor from "../Supervisor.js" +import * as core from "./core.js" + +/** @internal */ +const SupervisorSymbolKey = "effect/Supervisor" + +/** @internal */ +export const SupervisorTypeId: Supervisor.SupervisorTypeId = Symbol.for( + SupervisorSymbolKey +) as Supervisor.SupervisorTypeId + +/** @internal */ +export const supervisorVariance = { + /* c8 ignore next */ + _T: (_: never) => _ +} + +/** @internal */ +export class ProxySupervisor implements Supervisor.Supervisor { + readonly [SupervisorTypeId] = supervisorVariance + + constructor( + readonly underlying: Supervisor.Supervisor, + readonly value0: Effect.Effect + ) { + } + + get value(): Effect.Effect { + return this.value0 + } + + onStart( + context: Context.Context, + effect: Effect.Effect, + parent: Option.Option>, + fiber: Fiber.RuntimeFiber + ): void { + this.underlying.onStart(context, effect, parent, fiber) + } + + onEnd(value: Exit.Exit, fiber: Fiber.RuntimeFiber): void { + this.underlying.onEnd(value, fiber) + } + + onEffect(fiber: Fiber.RuntimeFiber, effect: Effect.Effect): void { + this.underlying.onEffect(fiber, effect) + } + + onSuspend(fiber: Fiber.RuntimeFiber): void { + this.underlying.onSuspend(fiber) + } + + onResume(fiber: Fiber.RuntimeFiber): void { + this.underlying.onResume(fiber) + } + + map(f: (a: T) => B): Supervisor.Supervisor { + return new ProxySupervisor(this, pipe(this.value, core.map(f))) + } + + zip(right: Supervisor.Supervisor): Supervisor.Supervisor<[T, B]> { + return new Zip(this, right) + } +} + +/** @internal */ +export class Zip implements Supervisor.Supervisor { + readonly _tag = "Zip" + readonly [SupervisorTypeId] = supervisorVariance + + constructor( + readonly left: Supervisor.Supervisor, + readonly right: Supervisor.Supervisor + ) { + } + + get value(): Effect.Effect<[T0, T1]> { + return core.zip(this.left.value, this.right.value) + } + + onStart( + context: Context.Context, + effect: Effect.Effect, + parent: Option.Option>, + fiber: Fiber.RuntimeFiber + ): void { + this.left.onStart(context, effect, parent, fiber) + this.right.onStart(context, effect, parent, fiber) + } + + onEnd(value: Exit.Exit, fiber: Fiber.RuntimeFiber): void { + this.left.onEnd(value, fiber) + this.right.onEnd(value, fiber) + } + + onEffect(fiber: Fiber.RuntimeFiber, effect: Effect.Effect): void { + this.left.onEffect(fiber, effect) + this.right.onEffect(fiber, effect) + } + + onSuspend(fiber: Fiber.RuntimeFiber): void { + this.left.onSuspend(fiber) + this.right.onSuspend(fiber) + } + + onResume(fiber: Fiber.RuntimeFiber): void { + this.left.onResume(fiber) + this.right.onResume(fiber) + } + + map(f: (a: [T0, T1]) => B): Supervisor.Supervisor { + return new ProxySupervisor(this, pipe(this.value, core.map(f))) + } + + zip(right: Supervisor.Supervisor): Supervisor.Supervisor<[[T0, T1], A]> { + return new Zip(this, right) + } +} + +/** @internal */ +export const isZip = (self: unknown): self is Zip => + hasProperty(self, SupervisorTypeId) && isTagged(self, "Zip") + +/** @internal */ +export class Track implements Supervisor.Supervisor>> { + readonly [SupervisorTypeId] = supervisorVariance + + readonly fibers: Set> = new Set() + + get value(): Effect.Effect>> { + return core.sync(() => Array.from(this.fibers)) + } + + onStart( + _context: Context.Context, + _effect: Effect.Effect, + _parent: Option.Option>, + fiber: Fiber.RuntimeFiber + ): void { + this.fibers.add(fiber) + } + + onEnd(_value: Exit.Exit, fiber: Fiber.RuntimeFiber): void { + this.fibers.delete(fiber) + } + + onEffect(_fiber: Fiber.RuntimeFiber, _effect: Effect.Effect): void { + // + } + + onSuspend(_fiber: Fiber.RuntimeFiber): void { + // + } + + onResume(_fiber: Fiber.RuntimeFiber): void { + // + } + + map(f: (a: Array>) => B): Supervisor.Supervisor { + return new ProxySupervisor(this, pipe(this.value, core.map(f))) + } + + zip( + right: Supervisor.Supervisor + ): Supervisor.Supervisor<[Array>, A]> { + return new Zip(this, right) + } + + onRun(execution: () => X, _fiber: Fiber.RuntimeFiber): X { + return execution() + } +} + +/** @internal */ +export class Const implements Supervisor.Supervisor { + readonly [SupervisorTypeId] = supervisorVariance + + constructor(readonly effect: Effect.Effect) { + } + + get value(): Effect.Effect { + return this.effect + } + + onStart( + _context: Context.Context, + _effect: Effect.Effect, + _parent: Option.Option>, + _fiber: Fiber.RuntimeFiber + ): void { + // + } + + onEnd(_value: Exit.Exit, _fiber: Fiber.RuntimeFiber): void { + // + } + + onEffect(_fiber: Fiber.RuntimeFiber, _effect: Effect.Effect): void { + // + } + + onSuspend(_fiber: Fiber.RuntimeFiber): void { + // + } + + onResume(_fiber: Fiber.RuntimeFiber): void { + // + } + + map(f: (a: T) => B): Supervisor.Supervisor { + return new ProxySupervisor(this, pipe(this.value, core.map(f))) + } + + zip(right: Supervisor.Supervisor): Supervisor.Supervisor<[T, A]> { + return new Zip(this, right) + } + + onRun(execution: () => X, _fiber: Fiber.RuntimeFiber): X { + return execution() + } +} + +class FibersIn implements Supervisor.Supervisor>> { + readonly [SupervisorTypeId] = supervisorVariance + + constructor(readonly ref: MutableRef.MutableRef>>) { + } + + get value(): Effect.Effect>> { + return core.sync(() => MutableRef.get(this.ref)) + } + + onStart( + _context: Context.Context, + _effect: Effect.Effect, + _parent: Option.Option>, + fiber: Fiber.RuntimeFiber + ): void { + pipe(this.ref, MutableRef.set(pipe(MutableRef.get(this.ref), SortedSet.add(fiber)))) + } + + onEnd(_value: Exit.Exit, fiber: Fiber.RuntimeFiber): void { + pipe(this.ref, MutableRef.set(pipe(MutableRef.get(this.ref), SortedSet.remove(fiber)))) + } + + onEffect(_fiber: Fiber.RuntimeFiber, _effect: Effect.Effect): void { + // + } + + onSuspend(_fiber: Fiber.RuntimeFiber): void { + // + } + + onResume(_fiber: Fiber.RuntimeFiber): void { + // + } + + map(f: (a: SortedSet.SortedSet>) => B): Supervisor.Supervisor { + return new ProxySupervisor(this, pipe(this.value, core.map(f))) + } + + zip( + right: Supervisor.Supervisor + ): Supervisor.Supervisor<[SortedSet.SortedSet>, A]> { + return new Zip(this, right) + } + + onRun(execution: () => X, _fiber: Fiber.RuntimeFiber): X { + return execution() + } +} + +/** @internal */ +export const unsafeTrack = (): Supervisor.Supervisor>> => { + return new Track() +} + +/** @internal */ +export const track: Effect.Effect>>> = core.sync(unsafeTrack) + +/** @internal */ +export const fromEffect = (effect: Effect.Effect): Supervisor.Supervisor => { + return new Const(effect) +} + +/** @internal */ +export const none = globalValue("effect/Supervisor/none", () => fromEffect(core.void)) + +/** @internal */ +export const fibersIn = ( + ref: MutableRef.MutableRef>> +): Effect.Effect>>> => + core.sync(() => new FibersIn(ref)) diff --git a/backend/node_modules/effect/src/internal/synchronizedRef.ts b/backend/node_modules/effect/src/internal/synchronizedRef.ts new file mode 100644 index 0000000000000000000000000000000000000000..7726e3e3eb2278e782abad8dfd6b9f9384e09c18 --- /dev/null +++ b/backend/node_modules/effect/src/internal/synchronizedRef.ts @@ -0,0 +1,114 @@ +import type * as Effect from "../Effect.js" +import { dual, pipe } from "../Function.js" +import * as Option from "../Option.js" +import type * as Synchronized from "../SynchronizedRef.js" +import * as core from "./core.js" + +/** @internal */ +export const getAndUpdateEffect = dual< + (f: (a: A) => Effect.Effect) => (self: Synchronized.SynchronizedRef) => Effect.Effect, + (self: Synchronized.SynchronizedRef, f: (a: A) => Effect.Effect) => Effect.Effect +>(2, (self, f) => + self.modifyEffect( + (value) => core.map(f(value), (result) => [value, result] as const) + )) + +/** @internal */ +export const getAndUpdateSomeEffect = dual< + ( + pf: (a: A) => Option.Option> + ) => (self: Synchronized.SynchronizedRef) => Effect.Effect, + ( + self: Synchronized.SynchronizedRef, + pf: (a: A) => Option.Option> + ) => Effect.Effect +>(2, (self, pf) => + self.modifyEffect((value) => { + const result = pf(value) + switch (result._tag) { + case "None": { + return core.succeed([value, value] as const) + } + case "Some": { + return core.map(result.value, (newValue) => [value, newValue] as const) + } + } + })) + +/** @internal */ +export const modify = dual< + (f: (a: A) => readonly [B, A]) => (self: Synchronized.SynchronizedRef) => Effect.Effect, + (self: Synchronized.SynchronizedRef, f: (a: A) => readonly [B, A]) => Effect.Effect +>(2, (self, f) => self.modify(f)) + +/** @internal */ +export const modifyEffect = dual< + ( + f: (a: A) => Effect.Effect + ) => (self: Synchronized.SynchronizedRef) => Effect.Effect, + ( + self: Synchronized.SynchronizedRef, + f: (a: A) => Effect.Effect + ) => Effect.Effect +>(2, (self, f) => self.modifyEffect(f)) + +/** @internal */ +export const modifySomeEffect = dual< + ( + fallback: B, + pf: (a: A) => Option.Option> + ) => (self: Synchronized.SynchronizedRef) => Effect.Effect, + ( + self: Synchronized.SynchronizedRef, + fallback: B, + pf: (a: A) => Option.Option> + ) => Effect.Effect +>(3, (self, fallback, pf) => + self.modifyEffect( + (value) => pipe(pf(value), Option.getOrElse(() => core.succeed([fallback, value] as const))) + )) + +/** @internal */ +export const updateEffect = dual< + ( + f: (a: A) => Effect.Effect + ) => (self: Synchronized.SynchronizedRef) => Effect.Effect, + (self: Synchronized.SynchronizedRef, f: (a: A) => Effect.Effect) => Effect.Effect +>(2, (self, f) => + self.modifyEffect((value) => + core.map( + f(value), + (result) => [undefined as void, result] as const + ) + )) + +/** @internal */ +export const updateAndGetEffect = dual< + (f: (a: A) => Effect.Effect) => (self: Synchronized.SynchronizedRef) => Effect.Effect, + (self: Synchronized.SynchronizedRef, f: (a: A) => Effect.Effect) => Effect.Effect +>(2, (self, f) => + self.modifyEffect( + (value) => core.map(f(value), (result) => [result, result] as const) + )) + +/** @internal */ +export const updateSomeEffect = dual< + ( + pf: (a: A) => Option.Option> + ) => (self: Synchronized.SynchronizedRef) => Effect.Effect, + ( + self: Synchronized.SynchronizedRef, + pf: (a: A) => Option.Option> + ) => Effect.Effect +>(2, (self, pf) => + self.modifyEffect((value) => { + const result = pf(value) + switch (result._tag) { + case "None": { + return core.succeed([void 0, value] as const) + } + case "Some": { + return core.map(result.value, (a) => [void 0, a] as const) + } + } + })) diff --git a/backend/node_modules/effect/src/internal/take.ts b/backend/node_modules/effect/src/internal/take.ts new file mode 100644 index 0000000000000000000000000000000000000000..4509cc833947ec1491fc3458ca9f42e0566f4092 --- /dev/null +++ b/backend/node_modules/effect/src/internal/take.ts @@ -0,0 +1,199 @@ +import * as Cause from "../Cause.js" +import * as Chunk from "../Chunk.js" +import * as Effect from "../Effect.js" +import * as Exit from "../Exit.js" +import { constFalse, constTrue, dual, pipe } from "../Function.js" +import * as Option from "../Option.js" +import { pipeArguments } from "../Pipeable.js" +import type * as Take from "../Take.js" + +/** @internal */ +const TakeSymbolKey = "effect/Take" + +/** @internal */ +export const TakeTypeId: Take.TakeTypeId = Symbol.for( + TakeSymbolKey +) as Take.TakeTypeId + +const takeVariance = { + /* c8 ignore next */ + _A: (_: never) => _, + /* c8 ignore next */ + _E: (_: never) => _ +} + +/** @internal */ +export class TakeImpl implements Take.Take { + readonly [TakeTypeId] = takeVariance + constructor(readonly exit: Exit.Exit, Option.Option>) { + } + pipe() { + return pipeArguments(this, arguments) + } +} + +/** @internal */ +export const chunk = (chunk: Chunk.Chunk): Take.Take => new TakeImpl(Exit.succeed(chunk)) + +/** @internal */ +export const die = (defect: unknown): Take.Take => new TakeImpl(Exit.die(defect)) + +/** @internal */ +export const dieMessage = (message: string): Take.Take => + new TakeImpl(Exit.die(new Cause.RuntimeException(message))) + +/** @internal */ +export const done = (self: Take.Take): Effect.Effect, Option.Option> => + Effect.suspend(() => self.exit) + +/** @internal */ +export const end: Take.Take = new TakeImpl(Exit.fail(Option.none())) + +/** @internal */ +export const fail = (error: E): Take.Take => new TakeImpl(Exit.fail(Option.some(error))) + +/** @internal */ +export const failCause = (cause: Cause.Cause): Take.Take => + new TakeImpl(Exit.failCause(pipe(cause, Cause.map(Option.some)))) + +/** @internal */ +export const fromEffect = (effect: Effect.Effect): Effect.Effect, never, R> => + Effect.matchCause(effect, { onFailure: failCause, onSuccess: of }) + +/** @internal */ +export const fromExit = (exit: Exit.Exit): Take.Take => + new TakeImpl(pipe(exit, Exit.mapBoth({ onFailure: Option.some, onSuccess: Chunk.of }))) + +/** @internal */ +export const fromPull = ( + pull: Effect.Effect, Option.Option, R> +): Effect.Effect, never, R> => + Effect.matchCause(pull, { + onFailure: (cause) => + Option.match(Cause.flipCauseOption(cause), { + onNone: () => end, + onSome: failCause + }), + onSuccess: chunk + }) + +/** @internal */ +export const isDone = (self: Take.Take): boolean => + Exit.match(self.exit, { + onFailure: (cause) => Option.isNone(Cause.flipCauseOption(cause)), + onSuccess: constFalse + }) + +/** @internal */ +export const isFailure = (self: Take.Take): boolean => + Exit.match(self.exit, { + onFailure: (cause) => Option.isSome(Cause.flipCauseOption(cause)), + onSuccess: constFalse + }) + +/** @internal */ +export const isSuccess = (self: Take.Take): boolean => + Exit.match(self.exit, { + onFailure: constFalse, + onSuccess: constTrue + }) + +/** @internal */ +export const make = ( + exit: Exit.Exit, Option.Option> +): Take.Take => new TakeImpl(exit) + +/** @internal */ +export const match = dual< + ( + options: { + readonly onEnd: () => Z + readonly onFailure: (cause: Cause.Cause) => Z2 + readonly onSuccess: (chunk: Chunk.Chunk) => Z3 + } + ) => (self: Take.Take) => Z | Z2 | Z3, + ( + self: Take.Take, + options: { + readonly onEnd: () => Z + readonly onFailure: (cause: Cause.Cause) => Z2 + readonly onSuccess: (chunk: Chunk.Chunk) => Z3 + } + ) => Z | Z2 | Z3 +>(2, ( + self: Take.Take, + { onEnd, onFailure, onSuccess }: { + readonly onEnd: () => Z + readonly onFailure: (cause: Cause.Cause) => Z2 + readonly onSuccess: (chunk: Chunk.Chunk) => Z3 + } +): Z | Z2 | Z3 => + Exit.match(self.exit, { + onFailure: (cause) => + Option.match(Cause.flipCauseOption(cause), { + onNone: onEnd, + onSome: onFailure + }), + onSuccess + })) + +/** @internal */ +export const matchEffect = dual< + ( + options: { + readonly onEnd: Effect.Effect + readonly onFailure: (cause: Cause.Cause) => Effect.Effect + readonly onSuccess: (chunk: Chunk.Chunk) => Effect.Effect + } + ) => (self: Take.Take) => Effect.Effect, + ( + self: Take.Take, + options: { + readonly onEnd: Effect.Effect + readonly onFailure: (cause: Cause.Cause) => Effect.Effect + readonly onSuccess: (chunk: Chunk.Chunk) => Effect.Effect + } + ) => Effect.Effect +>(2, ( + self: Take.Take, + { onEnd, onFailure, onSuccess }: { + readonly onEnd: Effect.Effect + readonly onFailure: (cause: Cause.Cause) => Effect.Effect + readonly onSuccess: (chunk: Chunk.Chunk) => Effect.Effect + } +): Effect.Effect => + Exit.matchEffect(self.exit, { + onFailure: (cause) => + Option.match, Effect.Effect>(Cause.flipCauseOption(cause), { + onNone: () => onEnd, + onSome: onFailure + }), + onSuccess + })) + +/** @internal */ +export const map = dual< + (f: (a: A) => B) => (self: Take.Take) => Take.Take, + (self: Take.Take, f: (a: A) => B) => Take.Take +>( + 2, + (self: Take.Take, f: (a: A) => B): Take.Take => + new TakeImpl(pipe(self.exit, Exit.map(Chunk.map(f)))) +) + +/** @internal */ +export const of = (value: A): Take.Take => new TakeImpl(Exit.succeed(Chunk.of(value))) + +/** @internal */ +export const tap = dual< + ( + f: (chunk: Chunk.Chunk) => Effect.Effect + ) => (self: Take.Take) => Effect.Effect, + ( + self: Take.Take, + f: (chunk: Chunk.Chunk) => Effect.Effect + ) => Effect.Effect +>(2, ( + self: Take.Take, + f: (chunk: Chunk.Chunk) => Effect.Effect +): Effect.Effect => pipe(self.exit, Exit.forEachEffect(f), Effect.asVoid)) diff --git a/backend/node_modules/effect/src/internal/testing/sleep.ts b/backend/node_modules/effect/src/internal/testing/sleep.ts new file mode 100644 index 0000000000000000000000000000000000000000..f49e833afd6b1a06c466b5597b228a604cc8ce42 --- /dev/null +++ b/backend/node_modules/effect/src/internal/testing/sleep.ts @@ -0,0 +1,27 @@ +import type * as Deferred from "../../Deferred.js" +import type * as Duration from "../../Duration.js" +import type * as FiberId from "../../FiberId.js" + +/** + * `Sleep` represents the state of a scheduled effect, including the time the + * effect is scheduled to run, a promise that can be completed to resume + * execution of the effect, and the fiber executing the effect. + * + * @internal + */ +export interface Sleep { + readonly duration: Duration.Duration + readonly deferred: Deferred.Deferred + readonly fiberId: FiberId.FiberId +} + +/** @internal */ +export const make = ( + duration: Duration.Duration, + deferred: Deferred.Deferred, + fiberId: FiberId.FiberId +): Sleep => ({ + duration, + deferred, + fiberId +}) diff --git a/backend/node_modules/effect/src/internal/testing/suspendedWarningData.ts b/backend/node_modules/effect/src/internal/testing/suspendedWarningData.ts new file mode 100644 index 0000000000000000000000000000000000000000..5742f3062b3175730ca75b6ad8bc23e9af0deeee --- /dev/null +++ b/backend/node_modules/effect/src/internal/testing/suspendedWarningData.ts @@ -0,0 +1,85 @@ +import type * as Fiber from "../../Fiber.js" + +/** @internal */ +export type SuspendedWarningData = Start | Pending | Done + +/** @internal */ +export const OP_SUSPENDED_WARNING_DATA_START = "Start" as const + +/** @internal */ +export type OP_SUSPENDED_WARNING_DATA_START = typeof OP_SUSPENDED_WARNING_DATA_START + +/** @internal */ +export const OP_SUSPENDED_WARNING_DATA_PENDING = "Pending" as const + +/** @internal */ +export type OP_SUSPENDED_WARNING_DATA_PENDING = typeof OP_SUSPENDED_WARNING_DATA_PENDING + +/** @internal */ +export const OP_SUSPENDED_WARNING_DATA_DONE = "Done" as const + +/** @internal */ +export type OP_SUSPENDED_WARNING_DATA_DONE = typeof OP_SUSPENDED_WARNING_DATA_DONE + +/** @internal */ +export interface Start { + readonly _tag: OP_SUSPENDED_WARNING_DATA_START +} + +/** @internal */ +export interface Pending { + readonly _tag: OP_SUSPENDED_WARNING_DATA_PENDING + readonly fiber: Fiber.Fiber +} + +/** @internal */ +export interface Done { + readonly _tag: OP_SUSPENDED_WARNING_DATA_DONE +} + +/** + * State indicating that a test has not adjusted the clock. + * + * @internal + */ +export const start: SuspendedWarningData = { + _tag: OP_SUSPENDED_WARNING_DATA_START +} + +/** + * State indicating that a test has adjusted the clock but a fiber is still + * running with a reference to the fiber that will display the warning + * message. + * + * @internal + */ +export const pending = (fiber: Fiber.Fiber): SuspendedWarningData => { + return { + _tag: OP_SUSPENDED_WARNING_DATA_PENDING, + fiber + } +} + +/** + * State indicating that the warning message has already been displayed. + * + * @internal + */ +export const done: SuspendedWarningData = { + _tag: OP_SUSPENDED_WARNING_DATA_DONE +} + +/** @internal */ +export const isStart = (self: SuspendedWarningData): self is Start => { + return self._tag === OP_SUSPENDED_WARNING_DATA_START +} + +/** @internal */ +export const isPending = (self: SuspendedWarningData): self is Pending => { + return self._tag === OP_SUSPENDED_WARNING_DATA_PENDING +} + +/** @internal */ +export const isDone = (self: SuspendedWarningData): self is Done => { + return self._tag === OP_SUSPENDED_WARNING_DATA_DONE +} diff --git a/backend/node_modules/effect/src/internal/testing/warningData.ts b/backend/node_modules/effect/src/internal/testing/warningData.ts new file mode 100644 index 0000000000000000000000000000000000000000..5144dad953045bc50d65bf8095d65d5a43105308 --- /dev/null +++ b/backend/node_modules/effect/src/internal/testing/warningData.ts @@ -0,0 +1,94 @@ +import type * as Fiber from "../../Fiber.js" + +/** + * `WarningData` describes the state of the warning message that is displayed + * if a test is using time by is not advancing the `TestClock`. The possible + * states are `Start` if a test has not used time, `Pending` if a test has + * used time but has not adjusted the `TestClock`, and `Done` if a test has + * adjusted the `TestClock` or the warning message has already been displayed. + * + * @internal + */ +export type WarningData = Start | Pending | Done + +/** @internal */ +export const OP_WARNING_DATA_START = "Start" as const + +/** @internal */ +export type OP_WARNING_DATA_START = typeof OP_WARNING_DATA_START + +/** @internal */ +export const OP_WARNING_DATA_PENDING = "Pending" as const + +/** @internal */ +export type OP_WARNING_DATA_PENDING = typeof OP_WARNING_DATA_PENDING + +/** @internal */ +export const OP_WARNING_DATA_DONE = "Done" as const + +/** @internal */ +export type OP_WARNING_DATA_DONE = typeof OP_WARNING_DATA_DONE + +/** @internal */ +export interface Start { + readonly _tag: OP_WARNING_DATA_START +} + +/** @internal */ +export interface Pending { + readonly _tag: OP_WARNING_DATA_PENDING + readonly fiber: Fiber.Fiber +} + +/** @internal */ +export interface Done { + readonly _tag: OP_WARNING_DATA_DONE +} + +/** + * State indicating that a test has not used time. + * + * @internal + */ +export const start: WarningData = { + _tag: OP_WARNING_DATA_START +} + +/** + * State indicating that a test has used time but has not adjusted the + * `TestClock` with a reference to the fiber that will display the warning + * message. + * + * @internal + */ +export const pending = (fiber: Fiber.Fiber): WarningData => { + return { + _tag: OP_WARNING_DATA_PENDING, + fiber + } +} + +/** + * State indicating that a test has used time or the warning message has + * already been displayed. + * + * @internal + */ +export const done: WarningData = { + _tag: OP_WARNING_DATA_DONE +} + +/** @internal */ +export const isStart = (self: WarningData): self is Start => { + return self._tag === OP_WARNING_DATA_START +} + +/** @internal */ +export const isPending = (self: WarningData): self is Pending => { + return self._tag === OP_WARNING_DATA_PENDING +} + +/** @internal */ +export const isDone = (self: WarningData): self is Done => { + return self._tag === OP_WARNING_DATA_DONE +} diff --git a/backend/node_modules/effect/src/internal/tracer.ts b/backend/node_modules/effect/src/internal/tracer.ts new file mode 100644 index 0000000000000000000000000000000000000000..ae37dc23b37317fd988b7c232b382b14b6b9454d --- /dev/null +++ b/backend/node_modules/effect/src/internal/tracer.ts @@ -0,0 +1,150 @@ +/** + * @since 2.0.0 + */ +import * as Context from "../Context.js" +import type * as Exit from "../Exit.js" +import { constFalse } from "../Function.js" +import type * as Option from "../Option.js" +import type * as Tracer from "../Tracer.js" + +/** @internal */ +export const TracerTypeId: Tracer.TracerTypeId = Symbol.for("effect/Tracer") as Tracer.TracerTypeId + +/** @internal */ +export const make = (options: Omit): Tracer.Tracer => ({ + [TracerTypeId]: TracerTypeId, + ...options +}) + +/** @internal */ +export const tracerTag = Context.GenericTag("effect/Tracer") + +/** @internal */ +export const spanTag = Context.GenericTag("effect/ParentSpan") + +const randomHexString = (function() { + const characters = "abcdef0123456789" + const charactersLength = characters.length + return function(length: number) { + let result = "" + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)) + } + return result + } +})() + +/** @internal */ +export class NativeSpan implements Tracer.Span { + readonly _tag = "Span" + readonly spanId: string + readonly traceId: string = "native" + readonly sampled = true + + status: Tracer.SpanStatus + attributes: Map + events: Array<[name: string, startTime: bigint, attributes: Record]> = [] + links: Array + + constructor( + readonly name: string, + readonly parent: Option.Option, + readonly context: Context.Context, + links: Iterable, + readonly startTime: bigint, + readonly kind: Tracer.SpanKind + ) { + this.status = { + _tag: "Started", + startTime + } + this.attributes = new Map() + this.traceId = parent._tag === "Some" ? parent.value.traceId : randomHexString(32) + this.spanId = randomHexString(16) + this.links = Array.from(links) + } + + end(endTime: bigint, exit: Exit.Exit): void { + this.status = { + _tag: "Ended", + endTime, + exit, + startTime: this.status.startTime + } + } + + attribute(key: string, value: unknown): void { + this.attributes.set(key, value) + } + + event(name: string, startTime: bigint, attributes?: Record): void { + this.events.push([name, startTime, attributes ?? {}]) + } + + addLinks(links: ReadonlyArray): void { + // eslint-disable-next-line no-restricted-syntax + this.links.push(...links) + } +} + +/** @internal */ +export const nativeTracer: Tracer.Tracer = make({ + span: (name, parent, context, links, startTime, kind) => + new NativeSpan( + name, + parent, + context, + links, + startTime, + kind + ), + context: (f) => f() +}) + +/** @internal */ +export const externalSpan = (options: { + readonly spanId: string + readonly traceId: string + readonly sampled?: boolean | undefined + readonly context?: Context.Context | undefined +}): Tracer.ExternalSpan => ({ + _tag: "ExternalSpan", + spanId: options.spanId, + traceId: options.traceId, + sampled: options.sampled ?? true, + context: options.context ?? Context.empty() +}) + +/** @internal */ +export const addSpanStackTrace = (options: Tracer.SpanOptions | undefined): Tracer.SpanOptions => { + if (options?.captureStackTrace === false) { + return options + } else if (options?.captureStackTrace !== undefined && typeof options.captureStackTrace !== "boolean") { + return options + } + const limit = Error.stackTraceLimit + Error.stackTraceLimit = 3 + const traceError = new Error() + Error.stackTraceLimit = limit + let cache: false | string = false + return { + ...options, + captureStackTrace: () => { + if (cache !== false) { + return cache + } + if (traceError.stack !== undefined) { + const stack = traceError.stack.split("\n") + if (stack[3] !== undefined) { + cache = stack[3].trim() + return cache + } + } + } + } +} + +/** @internal */ +export const DisablePropagation = Context.Reference()("effect/Tracer/DisablePropagation", { + defaultValue: constFalse +}) diff --git a/backend/node_modules/effect/src/internal/trie.ts b/backend/node_modules/effect/src/internal/trie.ts new file mode 100644 index 0000000000000000000000000000000000000000..b6c3033ed677cfb3e98e73a0cbe2a0e29df7a659 --- /dev/null +++ b/backend/node_modules/effect/src/internal/trie.ts @@ -0,0 +1,722 @@ +import * as Equal from "../Equal.js" +import { dual, identity, pipe } from "../Function.js" +import * as Hash from "../Hash.js" +import { format, NodeInspectSymbol, toJSON } from "../Inspectable.js" +import * as Option from "../Option.js" +import type * as Ordering from "../Ordering.js" +import { pipeArguments } from "../Pipeable.js" +import { hasProperty } from "../Predicate.js" +import type * as TR from "../Trie.js" +import type { NoInfer } from "../Types.js" + +const TrieSymbolKey = "effect/Trie" + +/** @internal */ +export const TrieTypeId: TR.TypeId = Symbol.for(TrieSymbolKey) as TR.TypeId + +type TraversalMap = (k: K, v: V) => A + +type TraversalFilter = (k: K, v: V) => boolean + +/** @internal */ +export interface TrieImpl extends TR.Trie { + readonly _root: Node | undefined + readonly _count: number +} + +const trieVariance = { + /* c8 ignore next */ + _Value: (_: never) => _ +} + +const TrieProto: TR.Trie = { + [TrieTypeId]: trieVariance, + [Symbol.iterator](this: TrieImpl): Iterator<[string, V]> { + return new TrieIterator(this, (k, v) => [k, v], () => true) + }, + [Hash.symbol](this: TR.Trie): number { + let hash = Hash.hash(TrieSymbolKey) + for (const item of this) { + hash ^= pipe(Hash.hash(item[0]), Hash.combine(Hash.hash(item[1]))) + } + return Hash.cached(this, hash) + }, + [Equal.symbol](this: TrieImpl, that: unknown): boolean { + if (isTrie(that)) { + const entries = Array.from(that) + return Array.from(this).every((itemSelf, i) => { + const itemThat = entries[i] + return Equal.equals(itemSelf[0], itemThat[0]) && Equal.equals(itemSelf[1], itemThat[1]) + }) + } + return false + }, + toString() { + return format(this.toJSON()) + }, + toJSON() { + return { + _id: "Trie", + values: Array.from(this).map(toJSON) + } + }, + [NodeInspectSymbol]() { + return this.toJSON() + }, + pipe() { + return pipeArguments(this, arguments) + } +} + +const makeImpl = (root: Node | undefined): TrieImpl => { + const trie = Object.create(TrieProto) + trie._root = root + trie._count = root?.count ?? 0 + return trie +} + +class TrieIterator implements IterableIterator { + stack: Array<[Node, string, boolean]> = [] + + constructor( + readonly trie: TrieImpl, + readonly f: TraversalMap, + readonly filter: TraversalFilter + ) { + const root = trie._root !== undefined ? trie._root : undefined + if (root !== undefined) { + this.stack.push([root, "", false]) + } + } + + next(): IteratorResult { + while (this.stack.length > 0) { + const [node, keyString, isAdded] = this.stack.pop()! + + if (isAdded) { + const value = node.value + if (value !== undefined) { + const key = keyString + node.key + if (this.filter(key, value)) { + return { done: false, value: this.f(key, value) } + } + } + } else { + this.addToStack(node, keyString) + } + } + + return { done: true, value: undefined } + } + + addToStack(node: Node, keyString: string) { + if (node.right !== undefined) { + this.stack.push([node.right, keyString, false]) + } + if (node.mid !== undefined) { + this.stack.push([node.mid, keyString + node.key, false]) + } + this.stack.push([node, keyString, true]) + if (node.left !== undefined) { + this.stack.push([node.left, keyString, false]) + } + } + + [Symbol.iterator](): IterableIterator { + return new TrieIterator(this.trie, this.f, this.filter) + } +} + +/** @internal */ +export const isTrie: { + (u: Iterable): u is TR.Trie + (u: unknown): u is TR.Trie +} = (u: unknown): u is TR.Trie => hasProperty(u, TrieTypeId) + +/** @internal */ +export const empty = (): TR.Trie => makeImpl(undefined) + +/** @internal */ +export const fromIterable = (entries: Iterable) => { + let trie = empty() + for (const [key, value] of entries) { + trie = insert(trie, key, value) + } + return trie +} + +/** @internal */ +export const make = >(...entries: Entries): TR.Trie< + Entries[number] extends readonly [any, infer V] ? V : never +> => { + return fromIterable(entries) +} + +/** @internal */ +export const insert = dual< + (key: string, value: V) => (self: TR.Trie) => TR.Trie, + (self: TR.Trie, key: string, value: V) => TR.Trie +>(3, (self: TR.Trie, key: string, value: V) => { + if (key.length === 0) return self + + // -1:left | 0:mid | 1:right + const dStack: Array = [] + const nStack: Array> = [] + let n: Node = (self as TrieImpl)._root ?? { + key: key[0], + count: 0 + } + const count = n.count + 1 + let cIndex = 0 + + while (cIndex < key.length) { + const c = key[cIndex] + nStack.push(n) + if (c > n.key) { + dStack.push(1) + if (n.right === undefined) { + n = { key: c, count } + } else { + n = n.right + } + } else if (c < n.key) { + dStack.push(-1) + if (n.left === undefined) { + n = { key: c, count } + } else { + n = n.left + } + } else { + if (cIndex === key.length - 1) { + n.value = value + } else if (n.mid === undefined) { + dStack.push(0) + n = { key: key[cIndex + 1], count } + } else { + dStack.push(0) + n = n.mid + } + + cIndex += 1 + } + } + + // Rebuild path to leaf node (Path-copying immutability) + for (let s = nStack.length - 2; s >= 0; --s) { + const n2 = nStack[s] + const d = dStack[s] + if (d === -1) { + // left + nStack[s] = { + key: n2.key, + count, + value: n2.value, + left: nStack[s + 1], + mid: n2.mid, + right: n2.right + } + } else if (d === 1) { + // right + nStack[s] = { + key: n2.key, + count, + value: n2.value, + left: n2.left, + mid: n2.mid, + right: nStack[s + 1] + } + } else { + // mid + nStack[s] = { + key: n2.key, + count, + value: n2.value, + left: n2.left, + mid: nStack[s + 1], + right: n2.right + } + } + } + + nStack[0].count = count + return makeImpl(nStack[0]) +}) + +/** @internal */ +export const size = (self: TR.Trie): number => (self as TrieImpl)._root?.count ?? 0 + +/** @internal */ +export const isEmpty = (self: TR.Trie): boolean => size(self) === 0 + +/** @internal */ +export const keys = (self: TR.Trie): IterableIterator => + new TrieIterator(self as TrieImpl, (key) => key, () => true) + +/** @internal */ +export const values = (self: TR.Trie): IterableIterator => + new TrieIterator(self as TrieImpl, (_, value) => value, () => true) + +/** @internal */ +export const entries = (self: TR.Trie): IterableIterator<[string, V]> => + new TrieIterator(self as TrieImpl, (key, value) => [key, value], () => true) + +/** @internal */ +export const reduce = dual< + ( + zero: Z, + f: (accumulator: Z, value: V, key: string) => Z + ) => (self: TR.Trie) => Z, + (self: TR.Trie, zero: Z, f: (accumulator: Z, value: V, key: string) => Z) => Z +>(3, (self, zero, f) => { + let accumulator = zero + for (const entry of self) { + accumulator = f(accumulator, entry[1], entry[0]) + } + return accumulator +}) + +/** @internal */ +export const map = dual< + (f: (value: V, key: string) => A) => (self: TR.Trie) => TR.Trie, + (self: TR.Trie, f: (value: V, key: string) => A) => TR.Trie +>(2, (self, f) => + reduce( + self, + empty(), + (trie, value, key) => insert(trie, key, f(value, key)) + )) + +/** @internal */ +export const filter: { + (f: (a: NoInfer, k: string) => a is B): (self: TR.Trie) => TR.Trie + (f: (a: NoInfer, k: string) => boolean): (self: TR.Trie) => TR.Trie + (self: TR.Trie, f: (a: A, k: string) => a is B): TR.Trie + (self: TR.Trie, f: (a: A, k: string) => boolean): TR.Trie +} = dual( + 2, + (self: TR.Trie, f: (a: A, k: string) => boolean): TR.Trie => + reduce( + self, + empty(), + (trie, value, key) => f(value, key) ? insert(trie, key, value) : trie + ) +) + +/** @internal */ +export const filterMap = dual< + ( + f: (value: A, key: string) => Option.Option + ) => (self: TR.Trie) => TR.Trie, + (self: TR.Trie, f: (value: A, key: string) => Option.Option) => TR.Trie +>(2, (self, f) => + reduce( + self, + empty(), + (trie, value, key) => { + const option = f(value, key) + return Option.isSome(option) ? insert(trie, key, option.value) : trie + } + )) + +/** @internal */ +export const compact = (self: TR.Trie>) => filterMap(self, identity) + +/** @internal */ +export const forEach = dual< + (f: (value: V, key: string) => void) => (self: TR.Trie) => void, + (self: TR.Trie, f: (value: V, key: string) => void) => void +>(2, (self, f) => reduce(self, void 0 as void, (_, value, key) => f(value, key))) + +/** @internal */ +export const keysWithPrefix = dual< + (prefix: string) => (self: TR.Trie) => IterableIterator, + (self: TR.Trie, prefix: string) => IterableIterator +>( + 2, + (self: TR.Trie, prefix: string): IterableIterator => + new TrieIterator(self as TrieImpl, (key) => key, (key) => key.startsWith(prefix)) +) + +/** @internal */ +export const valuesWithPrefix = dual< + (prefix: string) => (self: TR.Trie) => IterableIterator, + (self: TR.Trie, prefix: string) => IterableIterator +>( + 2, + (self: TR.Trie, prefix: string): IterableIterator => + new TrieIterator(self as TrieImpl, (_, value) => value, (key) => key.startsWith(prefix)) +) + +/** @internal */ +export const entriesWithPrefix = dual< + (prefix: string) => (self: TR.Trie) => IterableIterator<[string, V]>, + (self: TR.Trie, prefix: string) => IterableIterator<[string, V]> +>( + 2, + (self: TR.Trie, prefix: string): IterableIterator<[string, V]> => + new TrieIterator(self as TrieImpl, (key, value) => [key, value], (key) => key.startsWith(prefix)) +) + +/** @internal */ +export const toEntriesWithPrefix = dual< + (prefix: string) => (self: TR.Trie) => Array<[string, V]>, + (self: TR.Trie, prefix: string) => Array<[string, V]> +>( + 2, + (self: TR.Trie, prefix: string): Array<[string, V]> => Array.from(entriesWithPrefix(self, prefix)) +) + +/** @internal */ +export const get = dual< + (key: string) => (self: TR.Trie) => Option.Option, + (self: TR.Trie, key: string) => Option.Option +>( + 2, + (self: TR.Trie, key: string) => { + let n: Node | undefined = (self as TrieImpl)._root + if (n === undefined || key.length === 0) return Option.none() + let cIndex = 0 + while (cIndex < key.length) { + const c = key[cIndex] + if (c > n.key) { + if (n.right === undefined) { + return Option.none() + } else { + n = n.right + } + } else if (c < n.key) { + if (n.left === undefined) { + return Option.none() + } else { + n = n.left + } + } else { + if (cIndex === key.length - 1) { + return Option.fromNullable(n.value) + } else { + if (n.mid === undefined) { + return Option.none() + } else { + n = n.mid + cIndex += 1 + } + } + } + } + return Option.none() + } +) + +/** @internal */ +export const has = dual< + (key: string) => (self: TR.Trie) => boolean, + (self: TR.Trie, key: string) => boolean +>(2, (self, key) => Option.isSome(get(self, key))) + +/** @internal */ +export const unsafeGet = dual< + (key: string) => (self: TR.Trie) => V, + (self: TR.Trie, key: string) => V +>(2, (self, key) => { + const element = get(self, key) + if (Option.isNone(element)) { + throw new Error("Expected trie to contain key") + } + return element.value +}) + +/** @internal */ +export const remove = dual< + (key: string) => (self: TR.Trie) => TR.Trie, + (self: TR.Trie, key: string) => TR.Trie +>( + 2, + (self: TR.Trie, key: string) => { + let n: Node | undefined = (self as TrieImpl)._root + if (n === undefined || key.length === 0) return self + + const count = n.count - 1 + // -1:left | 0:mid | 1:right + const dStack: Array = [] + const nStack: Array> = [] + + let cIndex = 0 + while (cIndex < key.length) { + const c = key[cIndex] + if (c > n.key) { + if (n.right === undefined) { + return self + } else { + nStack.push(n) + dStack.push(1) + n = n.right + } + } else if (c < n.key) { + if (n.left === undefined) { + return self + } else { + nStack.push(n) + dStack.push(-1) + n = n.left + } + } else { + if (cIndex === key.length - 1) { + if (n.value !== undefined) { + nStack.push(n) + dStack.push(0) + cIndex += 1 + } else { + return self + } + } else { + if (n.mid === undefined) { + return self + } else { + nStack.push(n) + dStack.push(0) + n = n.mid + cIndex += 1 + } + } + } + } + + const removeNode = nStack[nStack.length - 1] + nStack[nStack.length - 1] = { + key: removeNode.key, + count, + left: removeNode.left, + mid: removeNode.mid, + right: removeNode.right + } + + // Rebuild path to leaf node (Path-copying immutability) + for (let s = nStack.length - 2; s >= 0; --s) { + const n2 = nStack[s] + const d = dStack[s] + const child = nStack[s + 1] + const nc = child.left === undefined && child.mid === undefined && child.right === undefined ? undefined : child + if (d === -1) { + // left + nStack[s] = { + key: n2.key, + count, + value: n2.value, + left: nc, + mid: n2.mid, + right: n2.right + } + } else if (d === 1) { + // right + nStack[s] = { + key: n2.key, + count, + value: n2.value, + left: n2.left, + mid: n2.mid, + right: nc + } + } else { + // mid + nStack[s] = { + key: n2.key, + count, + value: n2.value, + left: n2.left, + mid: nc, + right: n2.right + } + } + } + + nStack[0].count = count + return makeImpl(nStack[0]) + } +) + +/** @internal */ +export const removeMany = dual< + (keys: Iterable) => (self: TR.Trie) => TR.Trie, + (self: TR.Trie, keys: Iterable) => TR.Trie +>(2, (self, keys) => { + let trie = self + for (const key of keys) { + trie = remove(key)(trie) + } + return trie +}) + +/** @internal */ +export const insertMany = dual< + (iter: Iterable<[string, V]>) => (self: TR.Trie) => TR.Trie, + (self: TR.Trie, iter: Iterable<[string, V]>) => TR.Trie +>(2, (self, iter) => { + let trie = self + for (const [key, value] of iter) { + trie = insert(key, value)(trie) + } + return trie +}) + +/** @internal */ +export const modify = dual< + (key: string, f: (v: V) => V) => (self: TR.Trie) => TR.Trie, + (self: TR.Trie, key: string, f: (v: V) => V) => TR.Trie +>( + 3, + (self: TR.Trie, key: string, f: (v: V) => V): TR.Trie => { + let n: Node | undefined = (self as TrieImpl)._root + if (n === undefined || key.length === 0) return self + + // -1:left | 0:mid | 1:right + const dStack: Array = [] + const nStack: Array> = [] + + let cIndex = 0 + while (cIndex < key.length) { + const c = key[cIndex] + if (c > n.key) { + if (n.right === undefined) { + return self + } else { + nStack.push(n) + dStack.push(1) + n = n.right + } + } else if (c < n.key) { + if (n.left === undefined) { + return self + } else { + nStack.push(n) + dStack.push(-1) + n = n.left + } + } else { + if (cIndex === key.length - 1) { + if (n.value !== undefined) { + nStack.push(n) + dStack.push(0) + cIndex += 1 + } else { + return self + } + } else { + if (n.mid === undefined) { + return self + } else { + nStack.push(n) + dStack.push(0) + n = n.mid + cIndex += 1 + } + } + } + } + + const updateNode = nStack[nStack.length - 1] + if (updateNode.value === undefined) { + return self + } + + nStack[nStack.length - 1] = { + key: updateNode.key, + count: updateNode.count, + value: f(updateNode.value), // Update + left: updateNode.left, + mid: updateNode.mid, + right: updateNode.right + } + + // Rebuild path to leaf node (Path-copying immutability) + for (let s = nStack.length - 2; s >= 0; --s) { + const n2 = nStack[s] + const d = dStack[s] + const child = nStack[s + 1] + if (d === -1) { + // left + nStack[s] = { + key: n2.key, + count: n2.count, + value: n2.value, + left: child, + mid: n2.mid, + right: n2.right + } + } else if (d === 1) { + // right + nStack[s] = { + key: n2.key, + count: n2.count, + value: n2.value, + left: n2.left, + mid: n2.mid, + right: child + } + } else { + // mid + nStack[s] = { + key: n2.key, + count: n2.count, + value: n2.value, + left: n2.left, + mid: child, + right: n2.right + } + } + } + + return makeImpl(nStack[0]) + } +) + +/** @internal */ +export const longestPrefixOf = dual< + (key: string) => (self: TR.Trie) => Option.Option<[string, V]>, + (self: TR.Trie, key: string) => Option.Option<[string, V]> +>( + 2, + (self: TR.Trie, key: string) => { + let n: Node | undefined = (self as TrieImpl)._root + if (n === undefined || key.length === 0) return Option.none() + let longestPrefixNode: [string, V] | undefined = undefined + let cIndex = 0 + while (cIndex < key.length) { + const c = key[cIndex] + if (n.value !== undefined) { + longestPrefixNode = [key.slice(0, cIndex + 1), n.value] + } + + if (c > n.key) { + if (n.right === undefined) { + break + } else { + n = n.right + } + } else if (c < n.key) { + if (n.left === undefined) { + break + } else { + n = n.left + } + } else { + if (n.mid === undefined) { + break + } else { + n = n.mid + cIndex += 1 + } + } + } + + return Option.fromNullable(longestPrefixNode) + } +) + +interface Node { + key: string + count: number + value?: V | undefined + left?: Node | undefined + mid?: Node | undefined + right?: Node | undefined +} diff --git a/backend/node_modules/effect/src/internal/version.ts b/backend/node_modules/effect/src/internal/version.ts new file mode 100644 index 0000000000000000000000000000000000000000..d09d60b2423dde5c1ed2c6e9a544e6522dcc2c37 --- /dev/null +++ b/backend/node_modules/effect/src/internal/version.ts @@ -0,0 +1,7 @@ +let moduleVersion = "3.21.0" + +export const getCurrentVersion = () => moduleVersion + +export const setCurrentVersion = (version: string) => { + moduleVersion = version +}