@sayoriqwq/effect-harness 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HARNESS.md +34 -0
- package/README.md +113 -0
- package/dist/bin/effect-harness.d.ts +2 -0
- package/dist/bin/effect-harness.js +39 -0
- package/dist/bin/effect-harness.js.map +1 -0
- package/dist/src/cli/Main.d.ts +6 -0
- package/dist/src/cli/Main.js +141 -0
- package/dist/src/cli/Main.js.map +1 -0
- package/dist/src/harness/CraftSkills.d.ts +14 -0
- package/dist/src/harness/CraftSkills.js +164 -0
- package/dist/src/harness/CraftSkills.js.map +1 -0
- package/dist/src/harness/Errors.d.ts +24 -0
- package/dist/src/harness/Errors.js +26 -0
- package/dist/src/harness/Errors.js.map +1 -0
- package/dist/src/harness/GuardrailRules.d.ts +6 -0
- package/dist/src/harness/GuardrailRules.js +303 -0
- package/dist/src/harness/GuardrailRules.js.map +1 -0
- package/dist/src/harness/Guardrails.d.ts +10 -0
- package/dist/src/harness/Guardrails.js +69 -0
- package/dist/src/harness/Guardrails.js.map +1 -0
- package/dist/src/harness/Init.d.ts +9 -0
- package/dist/src/harness/Init.js +234 -0
- package/dist/src/harness/Init.js.map +1 -0
- package/dist/src/harness/Model.d.ts +44 -0
- package/dist/src/harness/Model.js +169 -0
- package/dist/src/harness/Model.js.map +1 -0
- package/dist/src/harness/ModuleSources.d.ts +5 -0
- package/dist/src/harness/ModuleSources.js +47 -0
- package/dist/src/harness/ModuleSources.js.map +1 -0
- package/dist/src/harness/PnpmWorkspace.d.ts +2 -0
- package/dist/src/harness/PnpmWorkspace.js +23 -0
- package/dist/src/harness/PnpmWorkspace.js.map +1 -0
- package/dist/src/harness/Publish.d.ts +53 -0
- package/dist/src/harness/Publish.js +282 -0
- package/dist/src/harness/Publish.js.map +1 -0
- package/dist/src/harness/SelfVerify.d.ts +4 -0
- package/dist/src/harness/SelfVerify.js +56 -0
- package/dist/src/harness/SelfVerify.js.map +1 -0
- package/dist/src/harness/SourcePin.d.ts +12 -0
- package/dist/src/harness/SourcePin.js +320 -0
- package/dist/src/harness/SourcePin.js.map +1 -0
- package/dist/src/harness/Status.d.ts +12 -0
- package/dist/src/harness/Status.js +88 -0
- package/dist/src/harness/Status.js.map +1 -0
- package/dist/src/harness/TargetVerify.d.ts +8 -0
- package/dist/src/harness/TargetVerify.js +432 -0
- package/dist/src/harness/TargetVerify.js.map +1 -0
- package/dist/src/harness/TestContract.d.ts +6 -0
- package/dist/src/harness/TestContract.js +45 -0
- package/dist/src/harness/TestContract.js.map +1 -0
- package/dist/src/platform/Json.d.ts +8 -0
- package/dist/src/platform/Json.js +35 -0
- package/dist/src/platform/Json.js.map +1 -0
- package/dist/src/platform/ManagedFiles.d.ts +8 -0
- package/dist/src/platform/ManagedFiles.js +59 -0
- package/dist/src/platform/ManagedFiles.js.map +1 -0
- package/dist/src/platform/Process.d.ts +8 -0
- package/dist/src/platform/Process.js +80 -0
- package/dist/src/platform/Process.js.map +1 -0
- package/guide/default-capabilities.md +38 -0
- package/guide/index.md +49 -0
- package/guide/setup.md +64 -0
- package/harness/default-capabilities.md +32 -0
- package/harness/exposure.md +46 -0
- package/harness/feedback/index.md +60 -0
- package/harness/index.md +22 -0
- package/harness/official-inventory.md +85 -0
- package/harness/runtime/codex/AGENTS.fragment.md +28 -0
- package/harness/runtime/codex/agents/effect-worker.md +26 -0
- package/harness/runtime/codex/skills/effect-code/SKILL.md +72 -0
- package/harness/runtime/codex/skills/effect-code/agents/openai.yaml +4 -0
- package/harness/runtime/codex/skills/effect-feedback/SKILL.md +66 -0
- package/harness/runtime/codex/skills/effect-feedback/agents/openai.yaml +4 -0
- package/harness/source.md +52 -0
- package/harness/target-agent-contract.md +205 -0
- package/package.json +68 -0
- package/repos/craft-skills.manifest.json +34 -0
- package/repos/effect/.agents/skills/grill-me/SKILL.md +43 -0
- package/repos/effect/.agents/skills/jsdocs/SKILL.md +203 -0
- package/repos/effect/.agents/skills/scratchpad/SKILL.md +44 -0
- package/repos/effect/.agents/skills/scratchpad/agents/openai.yaml +7 -0
- package/repos/effect/.agents/skills/scratchpad/scripts/extract-example.mjs +260 -0
- package/repos/effect/.changeset/add-bigdecimal-sumall-multiplyall.md +5 -0
- package/repos/effect/.changeset/add-chunk-schema.md +5 -0
- package/repos/effect/.changeset/add-command-hidden.md +19 -0
- package/repos/effect/.changeset/add-config-nested.md +5 -0
- package/repos/effect/.changeset/add-flag-hidden.md +15 -0
- package/repos/effect/.changeset/add-from-string-schemas.md +5 -0
- package/repos/effect/.changeset/add-headers-remove-many.md +5 -0
- package/repos/effect/.changeset/add-indexeddb-kvs-layer.md +5 -0
- package/repos/effect/.changeset/add-make-msgpack.md +5 -0
- package/repos/effect/.changeset/add-make-option.md +5 -0
- package/repos/effect/.changeset/add-missing-tx-modules.md +11 -0
- package/repos/effect/.changeset/add-newtype-module.md +5 -0
- package/repos/effect/.changeset/add-scalar-show-operation-id.md +5 -0
- package/repos/effect/.changeset/add-schedule-tap.md +5 -0
- package/repos/effect/.changeset/add-schema-annotate-encoded.md +5 -0
- package/repos/effect/.changeset/add-schema-array-ensure.md +5 -0
- package/repos/effect/.changeset/add-schema-bigdecimal.md +5 -0
- package/repos/effect/.changeset/add-schema-datetime.md +5 -0
- package/repos/effect/.changeset/add-schema-option-from-optional-nullor.md +5 -0
- package/repos/effect/.changeset/add-schema-option-from-undefined-nullish.md +5 -0
- package/repos/effect/.changeset/add-schema-string-encoding.md +5 -0
- package/repos/effect/.changeset/add-sql-pglite.md +5 -0
- package/repos/effect/.changeset/add-standard-jsdoc-rule.md +5 -0
- package/repos/effect/.changeset/add-stream-broadcastn.md +5 -0
- package/repos/effect/.changeset/add-unstable-encoding-export.md +5 -0
- package/repos/effect/.changeset/afraid-cobras-like.md +5 -0
- package/repos/effect/.changeset/ai-openai-config-field-leak.md +6 -0
- package/repos/effect/.changeset/ai-openai-file-nullable-fields.md +5 -0
- package/repos/effect/.changeset/anthropic-4-6-structured-output.md +5 -0
- package/repos/effect/.changeset/asyncresult-exhaustive.md +5 -0
- package/repos/effect/.changeset/atom-stream-error-type.md +5 -0
- package/repos/effect/.changeset/beige-paths-sort.md +5 -0
- package/repos/effect/.changeset/better-apples-nail.md +5 -0
- package/repos/effect/.changeset/better-rocks-arrive.md +5 -0
- package/repos/effect/.changeset/big-pans-look.md +5 -0
- package/repos/effect/.changeset/blue-dingos-greet.md +5 -0
- package/repos/effect/.changeset/blue-ligers-cheat.md +5 -0
- package/repos/effect/.changeset/blue-onions-smile.md +5 -0
- package/repos/effect/.changeset/blue-ravens-type.md +5 -0
- package/repos/effect/.changeset/blue-trams-kiss.md +7 -0
- package/repos/effect/.changeset/bold-chairs-yawn.md +5 -0
- package/repos/effect/.changeset/bold-planets-shout.md +30 -0
- package/repos/effect/.changeset/breezy-meals-see.md +5 -0
- package/repos/effect/.changeset/bright-bugs-bow.md +5 -0
- package/repos/effect/.changeset/bright-canyons-clean.md +5 -0
- package/repos/effect/.changeset/bright-dogs-fail.md +5 -0
- package/repos/effect/.changeset/bright-flags-stand.md +5 -0
- package/repos/effect/.changeset/bright-laws-teach.md +5 -0
- package/repos/effect/.changeset/bright-lemons-dance.md +5 -0
- package/repos/effect/.changeset/bright-planes-smash.md +5 -0
- package/repos/effect/.changeset/bright-rats-attend.md +5 -0
- package/repos/effect/.changeset/bright-toes-rush.md +5 -0
- package/repos/effect/.changeset/bumpy-boxes-teach.md +5 -0
- package/repos/effect/.changeset/busy-lions-sneeze.md +5 -0
- package/repos/effect/.changeset/busy-maps-attend.md +7 -0
- package/repos/effect/.changeset/calm-buckets-own.md +5 -0
- package/repos/effect/.changeset/calm-carrots-march.md +7 -0
- package/repos/effect/.changeset/calm-cars-rest.md +5 -0
- package/repos/effect/.changeset/calm-panthers-nail.md +5 -0
- package/repos/effect/.changeset/calm-seas-smile.md +5 -0
- package/repos/effect/.changeset/calm-squids-hug.md +5 -0
- package/repos/effect/.changeset/chatty-poets-type.md +5 -0
- package/repos/effect/.changeset/chilled-mice-wash.md +5 -0
- package/repos/effect/.changeset/chilly-pumas-rule.md +5 -0
- package/repos/effect/.changeset/chubby-buckets-feel.md +5 -0
- package/repos/effect/.changeset/chubby-parents-flow.md +5 -0
- package/repos/effect/.changeset/chubby-planets-fall.md +5 -0
- package/repos/effect/.changeset/clean-balloons-tan.md +5 -0
- package/repos/effect/.changeset/clean-bulldogs-care.md +5 -0
- package/repos/effect/.changeset/clean-dryers-sneeze.md +5 -0
- package/repos/effect/.changeset/clean-geese-work.md +5 -0
- package/repos/effect/.changeset/clean-goats-wave.md +5 -0
- package/repos/effect/.changeset/clean-needles-shake.md +23 -0
- package/repos/effect/.changeset/clean-tires-guess.md +5 -0
- package/repos/effect/.changeset/clear-spies-boil.md +5 -0
- package/repos/effect/.changeset/cli-help-choices.md +5 -0
- package/repos/effect/.changeset/cold-knives-lie.md +35 -0
- package/repos/effect/.changeset/cold-rooms-show.md +5 -0
- package/repos/effect/.changeset/cold-sloths-wave.md +5 -0
- package/repos/effect/.changeset/compact-json-schema-enum.md +25 -0
- package/repos/effect/.changeset/config-withdefault-eager.md +7 -0
- package/repos/effect/.changeset/config.json +24 -0
- package/repos/effect/.changeset/consolidate-encoding.md +5 -0
- package/repos/effect/.changeset/consolidate-sql-error.md +16 -0
- package/repos/effect/.changeset/crisp-seas-warn.md +5 -0
- package/repos/effect/.changeset/cuddly-rooms-bet.md +5 -0
- package/repos/effect/.changeset/curly-poems-talk.md +5 -0
- package/repos/effect/.changeset/curly-spies-relax.md +5 -0
- package/repos/effect/.changeset/curvy-apples-float.md +5 -0
- package/repos/effect/.changeset/custom-http-security-openapi-generator.md +5 -0
- package/repos/effect/.changeset/cute-heads-thank.md +5 -0
- package/repos/effect/.changeset/cyan-loops-grow.md +5 -0
- package/repos/effect/.changeset/cyan-radios-switch.md +5 -0
- package/repos/effect/.changeset/deep-rivers-spend.md +5 -0
- package/repos/effect/.changeset/dirty-lamps-trade.md +5 -0
- package/repos/effect/.changeset/dirty-laws-wear.md +5 -0
- package/repos/effect/.changeset/duration-temporal-object-input.md +7 -0
- package/repos/effect/.changeset/eager-coats-cheat.md +5 -0
- package/repos/effect/.changeset/early-birds-dream.md +5 -0
- package/repos/effect/.changeset/early-donuts-argue.md +5 -0
- package/repos/effect/.changeset/early-peaches-check.md +6 -0
- package/repos/effect/.changeset/eff-691-default-logger-ordering.md +5 -0
- package/repos/effect/.changeset/eff-693-rpcgroup-handler-deps.md +5 -0
- package/repos/effect/.changeset/eff-694-cli-completions-module.md +5 -0
- package/repos/effect/.changeset/eff-695-layer-mock-dual-api.md +5 -0
- package/repos/effect/.changeset/eff-697-rpcserialization-json-array-decode.md +5 -0
- package/repos/effect/.changeset/eff-698-rpcserialization-unreachable-branch.md +5 -0
- package/repos/effect/.changeset/eff-700-httpapi-middleware-errors.md +12 -0
- package/repos/effect/.changeset/eff-701-httpapierror-respondable.md +5 -0
- package/repos/effect/.changeset/eff-704-stream-merge-predicate.md +5 -0
- package/repos/effect/.changeset/eff-705-layer-tap-apis.md +5 -0
- package/repos/effect/.changeset/eff-706-servicemap-mutate.md +6 -0
- package/repos/effect/.changeset/eff-716-response-id-tracker-map.md +5 -0
- package/repos/effect/.changeset/eff-717-openai-socket-cancel.md +5 -0
- package/repos/effect/.changeset/eff-718-embedding-model-surface.md +13 -0
- package/repos/effect/.changeset/eff-725-fix-catch-jsdoc.md +5 -0
- package/repos/effect/.changeset/eff-726-model-dimensions.md +7 -0
- package/repos/effect/.changeset/eff-727-cli-help-alignment.md +5 -0
- package/repos/effect/.changeset/eff-730-language-model-incremental-fallback.md +5 -0
- package/repos/effect/.changeset/eff-736-cached-with-ttl.md +5 -0
- package/repos/effect/.changeset/eff-738-cron-prev.md +5 -0
- package/repos/effect/.changeset/eff-739-openai-function-call-done.md +5 -0
- package/repos/effect/.changeset/eff-740-missing-summary-parts.md +5 -0
- package/repos/effect/.changeset/eff-742-http-client-request-web.md +5 -0
- package/repos/effect/.changeset/eff-744-sqlite-migrator-lock.md +5 -0
- package/repos/effect/.changeset/eff-746-fixed-iteration-catchup.md +5 -0
- package/repos/effect/.changeset/eff-747-unify-effect.md +5 -0
- package/repos/effect/.changeset/eff-754-url-builder-any.md +6 -0
- package/repos/effect/.changeset/eff-755-references-core.md +5 -0
- package/repos/effect/.changeset/eff-769-select-text-highlight.md +5 -0
- package/repos/effect/.changeset/eff-774-mutable-list-append-all-empty-array.md +5 -0
- package/repos/effect/.changeset/eff-777-schema-make-effect.md +5 -0
- package/repos/effect/.changeset/eff-778-http-middleware-path-logger.md +5 -0
- package/repos/effect/.changeset/eff-779-keyvaluestore-layer-sql.md +5 -0
- package/repos/effect/.changeset/eff-780-layer-unify.md +5 -0
- package/repos/effect/.changeset/eff-781-fix-stream-toqueue-types.md +5 -0
- package/repos/effect/.changeset/eff-782-httpapi-status-literals.md +5 -0
- package/repos/effect/.changeset/eff-783-atom-http-api-errors.md +5 -0
- package/repos/effect/.changeset/eff-819-cluster-workflow-shard-groups.md +5 -0
- package/repos/effect/.changeset/eight-turkeys-own.md +7 -0
- package/repos/effect/.changeset/eighty-lies-deny.md +5 -0
- package/repos/effect/.changeset/eighty-poets-draw.md +9 -0
- package/repos/effect/.changeset/eighty-swans-scream.md +5 -0
- package/repos/effect/.changeset/eighty-teeth-sniff.md +5 -0
- package/repos/effect/.changeset/eleven-apes-share.md +5 -0
- package/repos/effect/.changeset/eleven-numbers-bake.md +5 -0
- package/repos/effect/.changeset/empty-gifts-beg.md +5 -0
- package/repos/effect/.changeset/empty-http-rpc-client.md +5 -0
- package/repos/effect/.changeset/eventlog-unencrypted.md +5 -0
- package/repos/effect/.changeset/expand-schema-filter-output.md +24 -0
- package/repos/effect/.changeset/export-schema-encode-keys-interface.md +7 -0
- package/repos/effect/.changeset/extract-semaphore-latch.md +10 -0
- package/repos/effect/.changeset/fair-bees-relax.md +5 -0
- package/repos/effect/.changeset/fair-buttons-share.md +7 -0
- package/repos/effect/.changeset/fair-cooks-stop.md +5 -0
- package/repos/effect/.changeset/fair-cups-train.md +5 -0
- package/repos/effect/.changeset/fair-dryers-speak.md +5 -0
- package/repos/effect/.changeset/fair-forks-shake.md +5 -0
- package/repos/effect/.changeset/fair-pandas-prove.md +5 -0
- package/repos/effect/.changeset/fair-pants-float.md +5 -0
- package/repos/effect/.changeset/fair-poems-visit.md +5 -0
- package/repos/effect/.changeset/famous-wolves-lead.md +5 -0
- package/repos/effect/.changeset/fancy-glasses-grow.md +5 -0
- package/repos/effect/.changeset/fast-times-camp.md +5 -0
- package/repos/effect/.changeset/few-birds-matter.md +5 -0
- package/repos/effect/.changeset/few-cougars-dig.md +5 -0
- package/repos/effect/.changeset/few-foxes-grin.md +5 -0
- package/repos/effect/.changeset/few-mirrors-pull.md +14 -0
- package/repos/effect/.changeset/few-socks-poke.md +5 -0
- package/repos/effect/.changeset/fiber-runtime-start-metrics.md +5 -0
- package/repos/effect/.changeset/fiery-jokes-care.md +5 -0
- package/repos/effect/.changeset/fiery-mammals-call.md +5 -0
- package/repos/effect/.changeset/fine-walls-decide.md +5 -0
- package/repos/effect/.changeset/first-success-of.md +5 -0
- package/repos/effect/.changeset/five-parents-relax.md +5 -0
- package/repos/effect/.changeset/five-worms-rhyme.md +5 -0
- package/repos/effect/.changeset/fix-1332.md +5 -0
- package/repos/effect/.changeset/fix-1917.md +11 -0
- package/repos/effect/.changeset/fix-1927.md +5 -0
- package/repos/effect/.changeset/fix-1940.md +7 -0
- package/repos/effect/.changeset/fix-1947.md +7 -0
- package/repos/effect/.changeset/fix-2002.md +5 -0
- package/repos/effect/.changeset/fix-2012.md +5 -0
- package/repos/effect/.changeset/fix-2015.md +5 -0
- package/repos/effect/.changeset/fix-2260.md +5 -0
- package/repos/effect/.changeset/fix-2268.md +5 -0
- package/repos/effect/.changeset/fix-2271.md +5 -0
- package/repos/effect/.changeset/fix-2384.md +7 -0
- package/repos/effect/.changeset/fix-ai-empty-params-structured-output.md +5 -0
- package/repos/effect/.changeset/fix-ai-text-toolkit-typing.md +5 -0
- package/repos/effect/.changeset/fix-catch-orelse-error-erasure.md +5 -0
- package/repos/effect/.changeset/fix-class-constructor-defaults.md +5 -0
- package/repos/effect/.changeset/fix-cli-mixed-global-flag-context.md +7 -0
- package/repos/effect/.changeset/fix-config-withDefault.md +5 -0
- package/repos/effect/.changeset/fix-config-withdefault-filter.md +7 -0
- package/repos/effect/.changeset/fix-datetime-gmt.md +5 -0
- package/repos/effect/.changeset/fix-devtools-flush-on-teardown.md +10 -0
- package/repos/effect/.changeset/fix-duration-symmetric-rounding.md +5 -0
- package/repos/effect/.changeset/fix-entity-manager-defect-replay.md +5 -0
- package/repos/effect/.changeset/fix-entity-proxy-rpc-handler-context.md +5 -0
- package/repos/effect/.changeset/fix-entity-proxy-server-path-params.md +5 -0
- package/repos/effect/.changeset/fix-from-readable-stream-cancel-defect.md +5 -0
- package/repos/effect/.changeset/fix-graph-undirected-traversal.md +5 -0
- package/repos/effect/.changeset/fix-has-interrupts-only-empty.md +5 -0
- package/repos/effect/.changeset/fix-hashmap-bit31-ordering.md +5 -0
- package/repos/effect/.changeset/fix-headers-proto-enumerability.md +5 -0
- package/repos/effect/.changeset/fix-http-incoming-message-parse-options.md +5 -0
- package/repos/effect/.changeset/fix-httpapi-endpoint-error-inference.md +5 -0
- package/repos/effect/.changeset/fix-httpapi-schema-types.md +5 -0
- package/repos/effect/.changeset/fix-httpapi-security-middleware-cache.md +5 -0
- package/repos/effect/.changeset/fix-is-json-dag.md +7 -0
- package/repos/effect/.changeset/fix-json-schema-anyof-oneof-siblings.md +5 -0
- package/repos/effect/.changeset/fix-json-schema-import-json.md +5 -0
- package/repos/effect/.changeset/fix-keepalive-blocked-timers.md +5 -0
- package/repos/effect/.changeset/fix-mcp-param-name-resolution.md +5 -0
- package/repos/effect/.changeset/fix-mermaid-escape-special-chars.md +5 -0
- package/repos/effect/.changeset/fix-mutable-list-filter-length.md +5 -0
- package/repos/effect/.changeset/fix-object-keyword-json-schema.md +5 -0
- package/repos/effect/.changeset/fix-openai-mcp-tool-names.md +5 -0
- package/repos/effect/.changeset/fix-openapi-generator-form-urlencoded.md +5 -0
- package/repos/effect/.changeset/fix-openapi-generator-swagger2openapi.md +5 -0
- package/repos/effect/.changeset/fix-openapi-preserve-multiple-response-content-types.md +5 -0
- package/repos/effect/.changeset/fix-openrouter-sparse-array.md +5 -0
- package/repos/effect/.changeset/fix-otel-logger-clock-skew.md +7 -0
- package/repos/effect/.changeset/fix-otel-logger-severity-number.md +20 -0
- package/repos/effect/.changeset/fix-queue-collect-duplication.md +5 -0
- package/repos/effect/.changeset/fix-random-string-seeds.md +5 -0
- package/repos/effect/.changeset/fix-ratelimiter-tokenbucket-redis-ttl.md +5 -0
- package/repos/effect/.changeset/fix-remainder-scientific-notation.md +5 -0
- package/repos/effect/.changeset/fix-request-resolver-pending-batches-leak.md +5 -0
- package/repos/effect/.changeset/fix-retry-transient-autocomplete.md +5 -0
- package/repos/effect/.changeset/fix-rpc-http-requestids-finalizer.md +5 -0
- package/repos/effect/.changeset/fix-rpc-json-id-edges.md +5 -0
- package/repos/effect/.changeset/fix-schedule-fixed-double-exec.md +9 -0
- package/repos/effect/.changeset/fix-schedule-reduce-sync-state.md +5 -0
- package/repos/effect/.changeset/fix-schema-arbitrary-exclusive-bounds.md +5 -0
- package/repos/effect/.changeset/fix-schema-defect-message.md +5 -0
- package/repos/effect/.changeset/fix-schema-encode-keys-property-keys.md +5 -0
- package/repos/effect/.changeset/fix-schema-encodekeys-class.md +5 -0
- package/repos/effect/.changeset/fix-schema-encodekeys-struct.md +5 -0
- package/repos/effect/.changeset/fix-schema-identifier-expected-message.md +5 -0
- package/repos/effect/.changeset/fix-schema-is-uuid.md +5 -0
- package/repos/effect/.changeset/fix-schema-option-non-schema-failures.md +17 -0
- package/repos/effect/.changeset/fix-schema-parser-checks.md +9 -0
- package/repos/effect/.changeset/fix-searchparam-initial-decode.md +5 -0
- package/repos/effect/.changeset/fix-serializable-wire-transfer.md +7 -0
- package/repos/effect/.changeset/fix-stream-grouped-within-flush.md +5 -0
- package/repos/effect/.changeset/fix-stream-run-for-each-while.md +5 -0
- package/repos/effect/.changeset/fix-stream-scan-effect.md +5 -0
- package/repos/effect/.changeset/fix-stream-scoped-scope.md +5 -0
- package/repos/effect/.changeset/fix-strip-approval-artifacts-multi-round.md +13 -0
- package/repos/effect/.changeset/fix-struct-utility-types-simplify.md +5 -0
- package/repos/effect/.changeset/fix-structwithrest-index-signatures.md +6 -0
- package/repos/effect/.changeset/fix-tagged-union-class-sentinels.md +5 -0
- package/repos/effect/.changeset/fix-tagged-union-match-unify.md +6 -0
- package/repos/effect/.changeset/fix-to-tagged-union-isanyof-custom-tags.md +10 -0
- package/repos/effect/.changeset/fix-tuple-with-rest-post-rest-index-drift.md +5 -0
- package/repos/effect/.changeset/fix-tuple-with-rest-post-rest-validation.md +5 -0
- package/repos/effect/.changeset/fix-types-voidifempty.md +5 -0
- package/repos/effect/.changeset/fix-void-response-encoding.md +5 -0
- package/repos/effect/.changeset/fix-workflow-proxy-rpc-handler-context.md +5 -0
- package/repos/effect/.changeset/flat-chicken-remain.md +5 -0
- package/repos/effect/.changeset/floppy-cows-spend.md +5 -0
- package/repos/effect/.changeset/floppy-items-admire.md +5 -0
- package/repos/effect/.changeset/floppy-pigs-kiss.md +5 -0
- package/repos/effect/.changeset/fluffy-meals-matter.md +5 -0
- package/repos/effect/.changeset/fluffy-pumas-push.md +5 -0
- package/repos/effect/.changeset/forked-memo-maps.md +6 -0
- package/repos/effect/.changeset/forty-hounds-cheer.md +5 -0
- package/repos/effect/.changeset/forty-otters-cry.md +5 -0
- package/repos/effect/.changeset/forty-rings-film.md +7 -0
- package/repos/effect/.changeset/forty-signs-stay.md +5 -0
- package/repos/effect/.changeset/forty-swans-divide.md +5 -0
- package/repos/effect/.changeset/forty-trees-pay.md +5 -0
- package/repos/effect/.changeset/four-papayas-bow.md +5 -0
- package/repos/effect/.changeset/four-points-repeat.md +5 -0
- package/repos/effect/.changeset/fresh-cats-smash.md +5 -0
- package/repos/effect/.changeset/fresh-emus-cheat.md +5 -0
- package/repos/effect/.changeset/fresh-monkeys-smoke.md +9 -0
- package/repos/effect/.changeset/fruity-houses-learn.md +6 -0
- package/repos/effect/.changeset/full-adults-double.md +5 -0
- package/repos/effect/.changeset/funny-crabs-hang.md +6 -0
- package/repos/effect/.changeset/funny-forks-move.md +5 -0
- package/repos/effect/.changeset/fuzzy-camels-hunt.md +5 -0
- package/repos/effect/.changeset/fuzzy-dodos-help.md +5 -0
- package/repos/effect/.changeset/fuzzy-lions-perform.md +5 -0
- package/repos/effect/.changeset/fuzzy-planets-sneeze.md +5 -0
- package/repos/effect/.changeset/gold-meteors-move.md +5 -0
- package/repos/effect/.changeset/gold-readers-hug.md +5 -0
- package/repos/effect/.changeset/gold-rings-start.md +5 -0
- package/repos/effect/.changeset/good-tools-work.md +7 -0
- package/repos/effect/.changeset/graph-algorithm-fixes.md +5 -0
- package/repos/effect/.changeset/great-trains-mate.md +5 -0
- package/repos/effect/.changeset/great-trams-report.md +5 -0
- package/repos/effect/.changeset/green-beds-unref.md +7 -0
- package/repos/effect/.changeset/green-chips-wash.md +5 -0
- package/repos/effect/.changeset/green-moons-smile.md +5 -0
- package/repos/effect/.changeset/green-pugs-play.md +5 -0
- package/repos/effect/.changeset/green-rings-prove.md +5 -0
- package/repos/effect/.changeset/happy-mirrors-dream.md +5 -0
- package/repos/effect/.changeset/heavy-loops-cut.md +5 -0
- package/repos/effect/.changeset/heavy-trams-fix.md +5 -0
- package/repos/effect/.changeset/hip-socks-travel.md +5 -0
- package/repos/effect/.changeset/honest-pens-thank.md +5 -0
- package/repos/effect/.changeset/honest-rivers-notice.md +5 -0
- package/repos/effect/.changeset/hot-taxis-fry.md +5 -0
- package/repos/effect/.changeset/httpapi-endpoint-relax-constraints.md +5 -0
- package/repos/effect/.changeset/httpapi-schema-service-types.md +6 -0
- package/repos/effect/.changeset/huge-moons-rhyme.md +5 -0
- package/repos/effect/.changeset/humble-pigs-dig.md +5 -0
- package/repos/effect/.changeset/icy-flies-cross.md +5 -0
- package/repos/effect/.changeset/itchy-radios-poke.md +5 -0
- package/repos/effect/.changeset/itchy-results-bet.md +5 -0
- package/repos/effect/.changeset/itchy-shrimps-deny.md +5 -0
- package/repos/effect/.changeset/itchy-toes-promise.md +5 -0
- package/repos/effect/.changeset/k8s-last-transition-null.md +5 -0
- package/repos/effect/.changeset/khaki-cats-learn.md +5 -0
- package/repos/effect/.changeset/khaki-melons-appear.md +5 -0
- package/repos/effect/.changeset/kind-hounds-float.md +5 -0
- package/repos/effect/.changeset/kind-windows-fall.md +5 -0
- package/repos/effect/.changeset/late-hotels-rule.md +5 -0
- package/repos/effect/.changeset/late-lamps-care.md +5 -0
- package/repos/effect/.changeset/late-rivers-applaud.md +5 -0
- package/repos/effect/.changeset/layer-map-dynamic-idle-ttl.md +5 -0
- package/repos/effect/.changeset/lazy-queens-rush.md +5 -0
- package/repos/effect/.changeset/lazy-recursive-forward-refs.md +5 -0
- package/repos/effect/.changeset/lazy-timers-exist.md +5 -0
- package/repos/effect/.changeset/legal-pants-drop.md +5 -0
- package/repos/effect/.changeset/lemon-taxis-sin.md +5 -0
- package/repos/effect/.changeset/light-kids-sneeze.md +5 -0
- package/repos/effect/.changeset/little-dryers-allow.md +7 -0
- package/repos/effect/.changeset/long-cameras-think.md +5 -0
- package/repos/effect/.changeset/lovely-cobras-change.md +5 -0
- package/repos/effect/.changeset/lovely-frogs-rescue.md +5 -0
- package/repos/effect/.changeset/lucky-buttons-jump.md +5 -0
- package/repos/effect/.changeset/lucky-phones-listen.md +5 -0
- package/repos/effect/.changeset/lucky-worms-type.md +5 -0
- package/repos/effect/.changeset/mean-dingos-share.md +5 -0
- package/repos/effect/.changeset/mean-trains-smash.md +5 -0
- package/repos/effect/.changeset/metal-parts-yell.md +5 -0
- package/repos/effect/.changeset/mighty-games-matter.md +5 -0
- package/repos/effect/.changeset/modern-uuid-guid-filter.md +5 -0
- package/repos/effect/.changeset/nasty-geese-grow.md +5 -0
- package/repos/effect/.changeset/neat-goats-wave.md +5 -0
- package/repos/effect/.changeset/neat-kings-chew.md +5 -0
- package/repos/effect/.changeset/neat-lions-rest.md +5 -0
- package/repos/effect/.changeset/neat-snails-wash.md +5 -0
- package/repos/effect/.changeset/neat-taxis-notice.md +5 -0
- package/repos/effect/.changeset/new-dogs-swim.md +5 -0
- package/repos/effect/.changeset/new-toes-stop.md +5 -0
- package/repos/effect/.changeset/ninety-geese-exist.md +5 -0
- package/repos/effect/.changeset/odd-boats-think.md +5 -0
- package/repos/effect/.changeset/odd-bulldogs-sleep.md +5 -0
- package/repos/effect/.changeset/odd-fans-glow.md +5 -0
- package/repos/effect/.changeset/odd-forks-talk.md +5 -0
- package/repos/effect/.changeset/odd-laws-draw.md +5 -0
- package/repos/effect/.changeset/odd-owls-smoke.md +5 -0
- package/repos/effect/.changeset/odd-socks-boil.md +5 -0
- package/repos/effect/.changeset/odd-suns-dance.md +5 -0
- package/repos/effect/.changeset/old-brooms-cry.md +5 -0
- package/repos/effect/.changeset/old-facts-stand.md +5 -0
- package/repos/effect/.changeset/old-mirrors-float.md +5 -0
- package/repos/effect/.changeset/olive-poems-visit.md +5 -0
- package/repos/effect/.changeset/open-hotels-remain.md +5 -0
- package/repos/effect/.changeset/openai-compat-nullable-tool-name.md +7 -0
- package/repos/effect/.changeset/openai-compat-reasoning.md +5 -0
- package/repos/effect/.changeset/otel-resource-env-precedence.md +5 -0
- package/repos/effect/.changeset/perfect-buckets-tickle.md +5 -0
- package/repos/effect/.changeset/petite-months-allow.md +6 -0
- package/repos/effect/.changeset/platform-crypto-service.md +9 -0
- package/repos/effect/.changeset/platform-node-shared-barrel.md +5 -0
- package/repos/effect/.changeset/plenty-moons-pull.md +5 -0
- package/repos/effect/.changeset/polite-brooms-tickle.md +5 -0
- package/repos/effect/.changeset/polite-pigs-speak.md +5 -0
- package/repos/effect/.changeset/polite-tables-kneel.md +5 -0
- package/repos/effect/.changeset/port-react-hydration.md +8 -0
- package/repos/effect/.changeset/pre.json +588 -0
- package/repos/effect/.changeset/public-deer-ring.md +5 -0
- package/repos/effect/.changeset/public-jeans-stop.md +5 -0
- package/repos/effect/.changeset/pubsub-publish-false.md +5 -0
- package/repos/effect/.changeset/puny-pens-clap.md +6 -0
- package/repos/effect/.changeset/purple-bars-prove.md +5 -0
- package/repos/effect/.changeset/purple-schools-float.md +5 -0
- package/repos/effect/.changeset/purple-turtles-draw.md +5 -0
- package/repos/effect/.changeset/quick-dragons-fix.md +5 -0
- package/repos/effect/.changeset/quick-falcons-travel.md +5 -0
- package/repos/effect/.changeset/quick-geese-relax.md +5 -0
- package/repos/effect/.changeset/quick-lamps-dig.md +5 -0
- package/repos/effect/.changeset/quick-lizards-fall.md +5 -0
- package/repos/effect/.changeset/quick-trees-join.md +5 -0
- package/repos/effect/.changeset/quiet-carpets-grin.md +5 -0
- package/repos/effect/.changeset/quiet-files-hunt.md +5 -0
- package/repos/effect/.changeset/quiet-lamps-jam.md +5 -0
- package/repos/effect/.changeset/quiet-radios-wave.md +5 -0
- package/repos/effect/.changeset/quiet-redis-scripts.md +5 -0
- package/repos/effect/.changeset/ready-olives-divide.md +5 -0
- package/repos/effect/.changeset/real-trains-ring.md +5 -0
- package/repos/effect/.changeset/red-pigs-repair.md +5 -0
- package/repos/effect/.changeset/redacted-representation-options.md +7 -0
- package/repos/effect/.changeset/refactor-cli-global-flags.md +27 -0
- package/repos/effect/.changeset/refactor-config-provider.md +23 -0
- package/repos/effect/.changeset/refactor-representation-references.md +5 -0
- package/repos/effect/.changeset/remove-effect-transactionwith.md +5 -0
- package/repos/effect/.changeset/remove-http-span-counter.md +5 -0
- package/repos/effect/.changeset/remove-nullor.md +5 -0
- package/repos/effect/.changeset/remove-openapi-fromapi-options.md +7 -0
- package/repos/effect/.changeset/remove-types-mergerecord.md +5 -0
- package/repos/effect/.changeset/remove-unused-utils-apis.md +5 -0
- package/repos/effect/.changeset/rename-rebuild-out.md +5 -0
- package/repos/effect/.changeset/restore-schema-parse-options.md +5 -0
- package/repos/effect/.changeset/rich-dots-push.md +5 -0
- package/repos/effect/.changeset/rich-hoops-nail.md +5 -0
- package/repos/effect/.changeset/rich-sloths-draw.md +5 -0
- package/repos/effect/.changeset/ripe-lies-battle.md +5 -0
- package/repos/effect/.changeset/rpc-middleware-provides-fix.md +5 -0
- package/repos/effect/.changeset/scalar-custom-fetch.md +5 -0
- package/repos/effect/.changeset/schema-as-class.md +17 -0
- package/repos/effect/.changeset/schema-asserts-signature.md +5 -0
- package/repos/effect/.changeset/schema-clean-up-additionalProperties.md +7 -0
- package/repos/effect/.changeset/schema-datetime-utc-from-string.md +5 -0
- package/repos/effect/.changeset/schema-decoding-defaults-services.md +7 -0
- package/repos/effect/.changeset/schema-defaults-issue-channel.md +7 -0
- package/repos/effect/.changeset/schema-dollar-prefix.md +5 -0
- package/repos/effect/.changeset/schema-missing-self-generic.md +5 -0
- package/repos/effect/.changeset/schema-ordered-arbitrary-constraints.md +12 -0
- package/repos/effect/.changeset/schema-parser-adapter-errors.md +5 -0
- package/repos/effect/.changeset/schema-refactor-toCodecJson.md +5 -0
- package/repos/effect/.changeset/schema-remove-annotate-in.md +5 -0
- package/repos/effect/.changeset/schema-rename-makeUnsafe-to-make.md +7 -0
- package/repos/effect/.changeset/schema-rename-parser-makeUnsafe.md +5 -0
- package/repos/effect/.changeset/schema-result-combinators.md +5 -0
- package/repos/effect/.changeset/schema-struct-simplify.md +5 -0
- package/repos/effect/.changeset/seven-mugs-marry.md +5 -0
- package/repos/effect/.changeset/shaggy-birds-stay.md +5 -0
- package/repos/effect/.changeset/shaggy-cities-push.md +5 -0
- package/repos/effect/.changeset/shaggy-numbers-accept.md +5 -0
- package/repos/effect/.changeset/sharp-emus-applaud.md +5 -0
- package/repos/effect/.changeset/sharp-goats-wink.md +5 -0
- package/repos/effect/.changeset/sharp-pandas-care.md +5 -0
- package/repos/effect/.changeset/sharp-peas-march.md +5 -0
- package/repos/effect/.changeset/sharp-rules-draw.md +5 -0
- package/repos/effect/.changeset/sharp-singers-sort.md +5 -0
- package/repos/effect/.changeset/shiny-trains-hug.md +5 -0
- package/repos/effect/.changeset/short-cows-relate.md +5 -0
- package/repos/effect/.changeset/short-foxes-admire.md +5 -0
- package/repos/effect/.changeset/shy-cycles-flow.md +5 -0
- package/repos/effect/.changeset/shy-geckos-sniff.md +30 -0
- package/repos/effect/.changeset/silent-geckos-matter.md +5 -0
- package/repos/effect/.changeset/silent-needles-design.md +6 -0
- package/repos/effect/.changeset/silent-plants-matter.md +8 -0
- package/repos/effect/.changeset/silent-spoons-stare.md +5 -0
- package/repos/effect/.changeset/silly-loops-tickle.md +5 -0
- package/repos/effect/.changeset/silver-bulk-indexeddb.md +5 -0
- package/repos/effect/.changeset/silver-emus-smoke.md +5 -0
- package/repos/effect/.changeset/silver-kings-poke.md +5 -0
- package/repos/effect/.changeset/silver-snails-sqlite.md +17 -0
- package/repos/effect/.changeset/silver-wings-watch.md +5 -0
- package/repos/effect/.changeset/six-cups-taste.md +5 -0
- package/repos/effect/.changeset/sixty-mails-shout.md +5 -0
- package/repos/effect/.changeset/sixty-socks-yell.md +5 -0
- package/repos/effect/.changeset/slimy-planets-divide.md +5 -0
- package/repos/effect/.changeset/slimy-turtles-juggle.md +5 -0
- package/repos/effect/.changeset/slow-beans-battle.md +28 -0
- package/repos/effect/.changeset/slow-berries-enjoy.md +5 -0
- package/repos/effect/.changeset/small-bugs-hunt.md +5 -0
- package/repos/effect/.changeset/small-crabs-care.md +9 -0
- package/repos/effect/.changeset/small-pets-sit.md +6 -0
- package/repos/effect/.changeset/smart-ducks-jump.md +6 -0
- package/repos/effect/.changeset/smart-pillows-buy.md +6 -0
- package/repos/effect/.changeset/smart-timers-fly.md +5 -0
- package/repos/effect/.changeset/smart-tips-sort.md +5 -0
- package/repos/effect/.changeset/social-pumas-prove.md +5 -0
- package/repos/effect/.changeset/soft-comics-wink.md +5 -0
- package/repos/effect/.changeset/soft-delete-sqlmodel.md +5 -0
- package/repos/effect/.changeset/soft-seals-allow.md +5 -0
- package/repos/effect/.changeset/solid-cougars-attack.md +5 -0
- package/repos/effect/.changeset/solid-doors-ring.md +5 -0
- package/repos/effect/.changeset/solid-items-tease.md +5 -0
- package/repos/effect/.changeset/solid-towns-smoke.md +5 -0
- package/repos/effect/.changeset/sour-canyons-rescue.md +5 -0
- package/repos/effect/.changeset/sparkly-bears-act.md +5 -0
- package/repos/effect/.changeset/sparkly-coins-sit.md +5 -0
- package/repos/effect/.changeset/spotty-comics-fry.md +5 -0
- package/repos/effect/.changeset/sql-migrator-mjs-mts.md +5 -0
- package/repos/effect/.changeset/stale-dots-tell.md +5 -0
- package/repos/effect/.changeset/stale-snakes-know.md +5 -0
- package/repos/effect/.changeset/strict-areas-end.md +5 -0
- package/repos/effect/.changeset/strict-buckets-hug.md +8 -0
- package/repos/effect/.changeset/strip-resolved-approvals.md +5 -0
- package/repos/effect/.changeset/strong-balloons-tickle.md +5 -0
- package/repos/effect/.changeset/strong-bees-queue.md +5 -0
- package/repos/effect/.changeset/struct-record.md +5 -0
- package/repos/effect/.changeset/sunny-ads-hang.md +5 -0
- package/repos/effect/.changeset/sunny-bikes-sleep.md +5 -0
- package/repos/effect/.changeset/sunny-rooms-invent.md +5 -0
- package/repos/effect/.changeset/sweet-donuts-bet.md +5 -0
- package/repos/effect/.changeset/sweet-hotels-give.md +5 -0
- package/repos/effect/.changeset/sweet-views-learn.md +5 -0
- package/repos/effect/.changeset/swift-spiders-unpack.md +5 -0
- package/repos/effect/.changeset/swift-symbols-stand.md +5 -0
- package/repos/effect/.changeset/tagged-error-class-optional-empty-props.md +5 -0
- package/repos/effect/.changeset/tall-hairs-return.md +5 -0
- package/repos/effect/.changeset/tall-mails-listen.md +5 -0
- package/repos/effect/.changeset/tall-queens-cheer.md +5 -0
- package/repos/effect/.changeset/tall-wombats-wave.md +5 -0
- package/repos/effect/.changeset/tangy-colts-lose.md +5 -0
- package/repos/effect/.changeset/tasty-comics-send.md +5 -0
- package/repos/effect/.changeset/ten-kings-fry.md +5 -0
- package/repos/effect/.changeset/thick-pandas-wait.md +5 -0
- package/repos/effect/.changeset/thin-ducks-wonder.md +5 -0
- package/repos/effect/.changeset/thirty-ducks-go.md +5 -0
- package/repos/effect/.changeset/thirty-pans-love.md +5 -0
- package/repos/effect/.changeset/three-corners-sort.md +5 -0
- package/repos/effect/.changeset/three-ravens-jam.md +5 -0
- package/repos/effect/.changeset/three-tomatoes-wave.md +5 -0
- package/repos/effect/.changeset/tidy-foxes-own.md +5 -0
- package/repos/effect/.changeset/tidy-icons-glow.md +5 -0
- package/repos/effect/.changeset/tidy-stacks-encode.md +5 -0
- package/repos/effect/.changeset/tidy-stars-drive.md +5 -0
- package/repos/effect/.changeset/tiny-buckets-wave.md +5 -0
- package/repos/effect/.changeset/tiny-lilies-flash.md +5 -0
- package/repos/effect/.changeset/tiny-rabbits-smile.md +5 -0
- package/repos/effect/.changeset/tocodecjson-return-json-type.md +10 -0
- package/repos/effect/.changeset/tool-get-json-schema-tests.md +5 -0
- package/repos/effect/.changeset/true-actors-battle.md +5 -0
- package/repos/effect/.changeset/twenty-buttons-cheer.md +5 -0
- package/repos/effect/.changeset/two-roses-double.md +5 -0
- package/repos/effect/.changeset/unify-error-defect-stack-options.md +23 -0
- package/repos/effect/.changeset/update-schema-arbitrary-report.md +22 -0
- package/repos/effect/.changeset/upset-colts-stick.md +5 -0
- package/repos/effect/.changeset/vast-bananas-send.md +5 -0
- package/repos/effect/.changeset/vast-deserts-travel.md +5 -0
- package/repos/effect/.changeset/violet-peaches-feel.md +5 -0
- package/repos/effect/.changeset/vitest-layer-top-level-options.md +5 -0
- package/repos/effect/.changeset/wacky-grapes-poke.md +5 -0
- package/repos/effect/.changeset/wacky-rice-add.md +5 -0
- package/repos/effect/.changeset/warm-dolls-brake.md +5 -0
- package/repos/effect/.changeset/warm-friends-tie.md +5 -0
- package/repos/effect/.changeset/warm-snails-shop.md +7 -0
- package/repos/effect/.changeset/wet-news-invent.md +5 -0
- package/repos/effect/.changeset/wild-readers-clean.md +5 -0
- package/repos/effect/.changeset/wild-suns-bearer-space.md +5 -0
- package/repos/effect/.changeset/wise-ants-wave.md +6 -0
- package/repos/effect/.changeset/wise-flags-shift.md +22 -0
- package/repos/effect/.changeset/wise-oranges-stay.md +5 -0
- package/repos/effect/.changeset/witty-lobsters-share.md +5 -0
- package/repos/effect/.changeset/yellow-adults-study.md +5 -0
- package/repos/effect/.changeset/yellow-clocks-dance.md +5 -0
- package/repos/effect/.changeset/yellow-dingos-jump.md +5 -0
- package/repos/effect/.changeset/young-doors-change.md +5 -0
- package/repos/effect/.envrc +1 -0
- package/repos/effect/.github/actions/setup/action.yaml +33 -0
- package/repos/effect/.github/workflows/ai-codegen.yml +66 -0
- package/repos/effect/.github/workflows/bundle-comment.yml +72 -0
- package/repos/effect/.github/workflows/check.yml +195 -0
- package/repos/effect/.github/workflows/release.yml +47 -0
- package/repos/effect/.github/workflows/snapshot.yml +34 -0
- package/repos/effect/.oxlintrc.json +25 -0
- package/repos/effect/.patterns/effect.md +102 -0
- package/repos/effect/.patterns/jsdoc.md +53 -0
- package/repos/effect/.patterns/testing.md +44 -0
- package/repos/effect/.specs/README.md +2 -0
- package/repos/effect/.vscode/extensions.json +6 -0
- package/repos/effect/.vscode/settings.json +44 -0
- package/repos/effect/AGENTS.md +139 -0
- package/repos/effect/LICENSE +21 -0
- package/repos/effect/LLMS.md +343 -0
- package/repos/effect/MIGRATION.md +83 -0
- package/repos/effect/README.md +3 -0
- package/repos/effect/TODOS.md +28 -0
- package/repos/effect/ai-docs/README.md +44 -0
- package/repos/effect/ai-docs/package.json +36 -0
- package/repos/effect/ai-docs/src/01_effect/01_basics/01_effect-gen.ts +30 -0
- package/repos/effect/ai-docs/src/01_effect/01_basics/02_effect-fn.ts +39 -0
- package/repos/effect/ai-docs/src/01_effect/01_basics/10_creating-effects.ts +74 -0
- package/repos/effect/ai-docs/src/01_effect/01_basics/index.md +5 -0
- package/repos/effect/ai-docs/src/01_effect/02_services/01_service.ts +45 -0
- package/repos/effect/ai-docs/src/01_effect/02_services/10_reference.ts +10 -0
- package/repos/effect/ai-docs/src/01_effect/02_services/20_layer-composition.ts +70 -0
- package/repos/effect/ai-docs/src/01_effect/02_services/20_layer-unwrap.ts +66 -0
- package/repos/effect/ai-docs/src/01_effect/02_services/index.md +5 -0
- package/repos/effect/ai-docs/src/01_effect/03_errors/01_error-handling.ts +30 -0
- package/repos/effect/ai-docs/src/01_effect/03_errors/10_catch-tags.ts +24 -0
- package/repos/effect/ai-docs/src/01_effect/03_errors/20_reason-errors.ts +64 -0
- package/repos/effect/ai-docs/src/01_effect/03_errors/index.md +1 -0
- package/repos/effect/ai-docs/src/01_effect/04_resources/10_acquire-release.ts +105 -0
- package/repos/effect/ai-docs/src/01_effect/04_resources/20_layer-side-effects.ts +31 -0
- package/repos/effect/ai-docs/src/01_effect/04_resources/30_layer-map.ts +86 -0
- package/repos/effect/ai-docs/src/01_effect/04_resources/index.md +3 -0
- package/repos/effect/ai-docs/src/01_effect/05_running/10_run-main.ts +30 -0
- package/repos/effect/ai-docs/src/01_effect/05_running/20_layer-launch.ts +27 -0
- package/repos/effect/ai-docs/src/01_effect/05_running/index.md +1 -0
- package/repos/effect/ai-docs/src/01_effect/06_pubsub/10_pubsub.ts +56 -0
- package/repos/effect/ai-docs/src/01_effect/06_pubsub/index.md +3 -0
- package/repos/effect/ai-docs/src/02_stream/10_creating-streams.ts +103 -0
- package/repos/effect/ai-docs/src/02_stream/20_consuming-streams.ts +137 -0
- package/repos/effect/ai-docs/src/02_stream/30_encoding.ts +165 -0
- package/repos/effect/ai-docs/src/02_stream/index.md +4 -0
- package/repos/effect/ai-docs/src/03_integration/10_managed-runtime.ts +129 -0
- package/repos/effect/ai-docs/src/03_integration/index.md +5 -0
- package/repos/effect/ai-docs/src/05_batching/10_request-resolver.ts +89 -0
- package/repos/effect/ai-docs/src/05_batching/index.md +3 -0
- package/repos/effect/ai-docs/src/06_schedule/10_schedules.ts +102 -0
- package/repos/effect/ai-docs/src/06_schedule/index.md +3 -0
- package/repos/effect/ai-docs/src/07_datetime/10_creating-and-formatting.ts +30 -0
- package/repos/effect/ai-docs/src/07_datetime/20_time-zones.ts +44 -0
- package/repos/effect/ai-docs/src/07_datetime/index.md +5 -0
- package/repos/effect/ai-docs/src/08_observability/10_logging.ts +66 -0
- package/repos/effect/ai-docs/src/08_observability/20_otlp-tracing.ts +95 -0
- package/repos/effect/ai-docs/src/08_observability/index.md +7 -0
- package/repos/effect/ai-docs/src/09_testing/10_effect-tests.ts +55 -0
- package/repos/effect/ai-docs/src/09_testing/20_layer-tests.ts +138 -0
- package/repos/effect/ai-docs/src/09_testing/index.md +1 -0
- package/repos/effect/ai-docs/src/50_http-client/10_basics.ts +102 -0
- package/repos/effect/ai-docs/src/50_http-client/index.md +3 -0
- package/repos/effect/ai-docs/src/51_http-server/10_basics.ts +116 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/api/Api.ts +14 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/api/Authorization.ts +36 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/api/System.ts +10 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/api/Users.ts +91 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/domain/User.ts +12 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts +22 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/server/Authorization.ts +36 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users/http.ts +71 -0
- package/repos/effect/ai-docs/src/51_http-server/fixtures/server/Users.ts +62 -0
- package/repos/effect/ai-docs/src/51_http-server/index.md +3 -0
- package/repos/effect/ai-docs/src/60_child-process/10_working-with-child-processes.ts +117 -0
- package/repos/effect/ai-docs/src/60_child-process/index.md +3 -0
- package/repos/effect/ai-docs/src/70_cli/10_basics.ts +136 -0
- package/repos/effect/ai-docs/src/70_cli/index.md +5 -0
- package/repos/effect/ai-docs/src/71_ai/10_language-model.ts +156 -0
- package/repos/effect/ai-docs/src/71_ai/20_tools.ts +226 -0
- package/repos/effect/ai-docs/src/71_ai/30_chat.ts +158 -0
- package/repos/effect/ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts +9 -0
- package/repos/effect/ai-docs/src/71_ai/index.md +5 -0
- package/repos/effect/ai-docs/src/80_cluster/10_entities.ts +97 -0
- package/repos/effect/ai-docs/src/80_cluster/index.md +4 -0
- package/repos/effect/ai-docs/src/index.md +12 -0
- package/repos/effect/ai-docs/tsconfig.json +24 -0
- package/repos/effect/cookbooks/schedule.md +3507 -0
- package/repos/effect/deno.json +33 -0
- package/repos/effect/docker-compose.yaml +18 -0
- package/repos/effect/dprint.json +32 -0
- package/repos/effect/flake.lock +27 -0
- package/repos/effect/flake.nix +24 -0
- package/repos/effect/jsdocs.config.json +16 -0
- package/repos/effect/migration/cause.md +163 -0
- package/repos/effect/migration/equality.md +72 -0
- package/repos/effect/migration/error-handling.md +106 -0
- package/repos/effect/migration/fiber-keep-alive.md +74 -0
- package/repos/effect/migration/fiberref.md +109 -0
- package/repos/effect/migration/forking.md +94 -0
- package/repos/effect/migration/generators.md +32 -0
- package/repos/effect/migration/layer-memoization.md +98 -0
- package/repos/effect/migration/runtime.md +88 -0
- package/repos/effect/migration/schema.md +1032 -0
- package/repos/effect/migration/scope.md +50 -0
- package/repos/effect/migration/services.md +235 -0
- package/repos/effect/migration/v3-to-v4.md +421 -0
- package/repos/effect/migration/yieldable.md +173 -0
- package/repos/effect/package.json +104 -0
- package/repos/effect/packages/ai/anthropic/CHANGELOG.md +611 -0
- package/repos/effect/packages/ai/anthropic/codegen.yml +63 -0
- package/repos/effect/packages/ai/anthropic/docgen.json +26 -0
- package/repos/effect/packages/ai/anthropic/package.json +67 -0
- package/repos/effect/packages/ai/anthropic/src/AnthropicClient.ts +432 -0
- package/repos/effect/packages/ai/anthropic/src/AnthropicConfig.ts +84 -0
- package/repos/effect/packages/ai/anthropic/src/AnthropicError.ts +212 -0
- package/repos/effect/packages/ai/anthropic/src/AnthropicLanguageModel.ts +3098 -0
- package/repos/effect/packages/ai/anthropic/src/AnthropicTelemetry.ts +132 -0
- package/repos/effect/packages/ai/anthropic/src/AnthropicTool.ts +2512 -0
- package/repos/effect/packages/ai/anthropic/src/Generated.ts +12600 -0
- package/repos/effect/packages/ai/anthropic/src/index.ts +40 -0
- package/repos/effect/packages/ai/anthropic/src/internal/errors.ts +370 -0
- package/repos/effect/packages/ai/anthropic/src/internal/utilities.ts +26 -0
- package/repos/effect/packages/ai/anthropic/test/AnthropicLanguageModel.test.ts +300 -0
- package/repos/effect/packages/ai/anthropic/tsconfig.json +8 -0
- package/repos/effect/packages/ai/anthropic/vitest.config.ts +6 -0
- package/repos/effect/packages/ai/openai/CHANGELOG.md +668 -0
- package/repos/effect/packages/ai/openai/codegen.yaml +41 -0
- package/repos/effect/packages/ai/openai/docgen.json +24 -0
- package/repos/effect/packages/ai/openai/package.json +67 -0
- package/repos/effect/packages/ai/openai/src/Generated.ts +35774 -0
- package/repos/effect/packages/ai/openai/src/OpenAiClient.ts +730 -0
- package/repos/effect/packages/ai/openai/src/OpenAiClientGenerated.ts +202 -0
- package/repos/effect/packages/ai/openai/src/OpenAiConfig.ts +93 -0
- package/repos/effect/packages/ai/openai/src/OpenAiEmbeddingModel.ts +258 -0
- package/repos/effect/packages/ai/openai/src/OpenAiError.ts +242 -0
- package/repos/effect/packages/ai/openai/src/OpenAiLanguageModel.ts +3133 -0
- package/repos/effect/packages/ai/openai/src/OpenAiSchema.ts +1286 -0
- package/repos/effect/packages/ai/openai/src/OpenAiTelemetry.ts +162 -0
- package/repos/effect/packages/ai/openai/src/OpenAiTool.ts +342 -0
- package/repos/effect/packages/ai/openai/src/index.ts +55 -0
- package/repos/effect/packages/ai/openai/src/internal/errors.ts +349 -0
- package/repos/effect/packages/ai/openai/src/internal/utilities.ts +33 -0
- package/repos/effect/packages/ai/openai/test/OpenAiClient.test.ts +798 -0
- package/repos/effect/packages/ai/openai/test/OpenAiEmbeddingModel.test.ts +289 -0
- package/repos/effect/packages/ai/openai/test/OpenAiLanguageModel.test.ts +1528 -0
- package/repos/effect/packages/ai/openai/test/OpenAiSchema.test.ts +273 -0
- package/repos/effect/packages/ai/openai/tsconfig.json +8 -0
- package/repos/effect/packages/ai/openai/vitest.config.ts +6 -0
- package/repos/effect/packages/ai/openai-compat/CHANGELOG.md +626 -0
- package/repos/effect/packages/ai/openai-compat/docgen.json +24 -0
- package/repos/effect/packages/ai/openai-compat/package.json +69 -0
- package/repos/effect/packages/ai/openai-compat/src/OpenAiClient.ts +1221 -0
- package/repos/effect/packages/ai/openai-compat/src/OpenAiConfig.ts +87 -0
- package/repos/effect/packages/ai/openai-compat/src/OpenAiEmbeddingModel.ts +254 -0
- package/repos/effect/packages/ai/openai-compat/src/OpenAiError.ts +178 -0
- package/repos/effect/packages/ai/openai-compat/src/OpenAiLanguageModel.ts +1951 -0
- package/repos/effect/packages/ai/openai-compat/src/OpenAiTelemetry.ts +164 -0
- package/repos/effect/packages/ai/openai-compat/src/index.ts +35 -0
- package/repos/effect/packages/ai/openai-compat/src/internal/errors.ts +327 -0
- package/repos/effect/packages/ai/openai-compat/src/internal/utilities.ts +33 -0
- package/repos/effect/packages/ai/openai-compat/test/OpenAiClient.test.ts +474 -0
- package/repos/effect/packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts +216 -0
- package/repos/effect/packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts +1505 -0
- package/repos/effect/packages/ai/openai-compat/tsconfig.json +8 -0
- package/repos/effect/packages/ai/openai-compat/vitest.config.ts +6 -0
- package/repos/effect/packages/ai/openrouter/CHANGELOG.md +607 -0
- package/repos/effect/packages/ai/openrouter/codegen.yml +35 -0
- package/repos/effect/packages/ai/openrouter/docgen.json +24 -0
- package/repos/effect/packages/ai/openrouter/package.json +67 -0
- package/repos/effect/packages/ai/openrouter/src/Generated.ts +10028 -0
- package/repos/effect/packages/ai/openrouter/src/OpenRouterClient.ts +357 -0
- package/repos/effect/packages/ai/openrouter/src/OpenRouterConfig.ts +99 -0
- package/repos/effect/packages/ai/openrouter/src/OpenRouterError.ts +225 -0
- package/repos/effect/packages/ai/openrouter/src/OpenRouterLanguageModel.ts +1796 -0
- package/repos/effect/packages/ai/openrouter/src/index.ts +30 -0
- package/repos/effect/packages/ai/openrouter/src/internal/errors.ts +375 -0
- package/repos/effect/packages/ai/openrouter/src/internal/utilities.ts +92 -0
- package/repos/effect/packages/ai/openrouter/tsconfig.json +10 -0
- package/repos/effect/packages/ai/openrouter/vitest.config.ts +6 -0
- package/repos/effect/packages/atom/react/CHANGELOG.md +599 -0
- package/repos/effect/packages/atom/react/LICENSE +21 -0
- package/repos/effect/packages/atom/react/README.md +7 -0
- package/repos/effect/packages/atom/react/docgen.json +25 -0
- package/repos/effect/packages/atom/react/package.json +79 -0
- package/repos/effect/packages/atom/react/src/Hooks.ts +476 -0
- package/repos/effect/packages/atom/react/src/ReactHydration.ts +109 -0
- package/repos/effect/packages/atom/react/src/RegistryContext.ts +108 -0
- package/repos/effect/packages/atom/react/src/ScopedAtom.ts +151 -0
- package/repos/effect/packages/atom/react/src/index.ts +23 -0
- package/repos/effect/packages/atom/react/test/index.test.tsx +650 -0
- package/repos/effect/packages/atom/react/tsconfig.json +8 -0
- package/repos/effect/packages/atom/react/vitest.config.ts +9 -0
- package/repos/effect/packages/atom/react/vitest.setup.ts +8 -0
- package/repos/effect/packages/atom/solid/CHANGELOG.md +595 -0
- package/repos/effect/packages/atom/solid/LICENSE +21 -0
- package/repos/effect/packages/atom/solid/README.md +7 -0
- package/repos/effect/packages/atom/solid/docgen.json +26 -0
- package/repos/effect/packages/atom/solid/package.json +72 -0
- package/repos/effect/packages/atom/solid/src/Hooks.ts +359 -0
- package/repos/effect/packages/atom/solid/src/RegistryContext.ts +80 -0
- package/repos/effect/packages/atom/solid/src/index.ts +13 -0
- package/repos/effect/packages/atom/solid/test/index.test.tsx +296 -0
- package/repos/effect/packages/atom/solid/tsconfig.json +8 -0
- package/repos/effect/packages/atom/solid/vitest.config.ts +15 -0
- package/repos/effect/packages/atom/solid/vitest.setup.ts +8 -0
- package/repos/effect/packages/atom/vue/CHANGELOG.md +593 -0
- package/repos/effect/packages/atom/vue/LICENSE +21 -0
- package/repos/effect/packages/atom/vue/README.md +7 -0
- package/repos/effect/packages/atom/vue/docgen.json +24 -0
- package/repos/effect/packages/atom/vue/package.json +68 -0
- package/repos/effect/packages/atom/vue/src/index.ts +211 -0
- package/repos/effect/packages/atom/vue/test/index.test.ts +5 -0
- package/repos/effect/packages/atom/vue/tsconfig.json +14 -0
- package/repos/effect/packages/atom/vue/vitest.config.ts +8 -0
- package/repos/effect/packages/effect/CHANGELOG.md +1607 -0
- package/repos/effect/packages/effect/CONFIG.md +690 -0
- package/repos/effect/packages/effect/HTTPAPI.md +3213 -0
- package/repos/effect/packages/effect/LICENSE +21 -0
- package/repos/effect/packages/effect/MCP.md +369 -0
- package/repos/effect/packages/effect/OPTIC.md +620 -0
- package/repos/effect/packages/effect/README.md +53 -0
- package/repos/effect/packages/effect/SCHEMA.md +7284 -0
- package/repos/effect/packages/effect/benchmark/schema/Enums.ts +40 -0
- package/repos/effect/packages/effect/benchmark/schema/Optic.ts +79 -0
- package/repos/effect/packages/effect/benchmark/schema/array.ts +74 -0
- package/repos/effect/packages/effect/benchmark/schema/filter.ts +74 -0
- package/repos/effect/packages/effect/benchmark/schema/object.ts +82 -0
- package/repos/effect/packages/effect/benchmark/schema/tagged-union.ts +69 -0
- package/repos/effect/packages/effect/benchmark/schema/transformation.ts +91 -0
- package/repos/effect/packages/effect/benchmark/stream/splitLines.ts +46 -0
- package/repos/effect/packages/effect/docgen.json +38 -0
- package/repos/effect/packages/effect/package.json +127 -0
- package/repos/effect/packages/effect/src/Array.ts +4908 -0
- package/repos/effect/packages/effect/src/BigDecimal.ts +1943 -0
- package/repos/effect/packages/effect/src/BigInt.ts +1029 -0
- package/repos/effect/packages/effect/src/Boolean.ts +481 -0
- package/repos/effect/packages/effect/src/Brand.ts +318 -0
- package/repos/effect/packages/effect/src/Cache.ts +1295 -0
- package/repos/effect/packages/effect/src/Cause.ts +1939 -0
- package/repos/effect/packages/effect/src/Channel.ts +8615 -0
- package/repos/effect/packages/effect/src/ChannelSchema.ts +308 -0
- package/repos/effect/packages/effect/src/Chunk.ts +2976 -0
- package/repos/effect/packages/effect/src/Clock.ts +194 -0
- package/repos/effect/packages/effect/src/Combiner.ts +316 -0
- package/repos/effect/packages/effect/src/Config.ts +1416 -0
- package/repos/effect/packages/effect/src/ConfigProvider.ts +1159 -0
- package/repos/effect/packages/effect/src/Console.ts +653 -0
- package/repos/effect/packages/effect/src/Context.ts +1339 -0
- package/repos/effect/packages/effect/src/Cron.ts +1212 -0
- package/repos/effect/packages/effect/src/Crypto.ts +318 -0
- package/repos/effect/packages/effect/src/Data.ts +774 -0
- package/repos/effect/packages/effect/src/DateTime.ts +2922 -0
- package/repos/effect/packages/effect/src/Deferred.ts +917 -0
- package/repos/effect/packages/effect/src/Differ.ts +32 -0
- package/repos/effect/packages/effect/src/Duration.ts +1827 -0
- package/repos/effect/packages/effect/src/Effect.ts +15347 -0
- package/repos/effect/packages/effect/src/Effectable.ts +68 -0
- package/repos/effect/packages/effect/src/Encoding.ts +1046 -0
- package/repos/effect/packages/effect/src/Equal.ts +550 -0
- package/repos/effect/packages/effect/src/Equivalence.ts +870 -0
- package/repos/effect/packages/effect/src/ErrorReporter.ts +543 -0
- package/repos/effect/packages/effect/src/ExecutionPlan.ts +416 -0
- package/repos/effect/packages/effect/src/Exit.ts +1085 -0
- package/repos/effect/packages/effect/src/Fiber.ts +632 -0
- package/repos/effect/packages/effect/src/FiberHandle.ts +815 -0
- package/repos/effect/packages/effect/src/FiberMap.ts +1030 -0
- package/repos/effect/packages/effect/src/FiberSet.ts +713 -0
- package/repos/effect/packages/effect/src/FileSystem.ts +1394 -0
- package/repos/effect/packages/effect/src/Filter.ts +827 -0
- package/repos/effect/packages/effect/src/Formatter.ts +319 -0
- package/repos/effect/packages/effect/src/Function.ts +1415 -0
- package/repos/effect/packages/effect/src/Graph.ts +4892 -0
- package/repos/effect/packages/effect/src/HKT.ts +213 -0
- package/repos/effect/packages/effect/src/Hash.ts +539 -0
- package/repos/effect/packages/effect/src/HashMap.ts +1236 -0
- package/repos/effect/packages/effect/src/HashRing.ts +426 -0
- package/repos/effect/packages/effect/src/HashSet.ts +605 -0
- package/repos/effect/packages/effect/src/Inspectable.ts +329 -0
- package/repos/effect/packages/effect/src/Iterable.ts +2483 -0
- package/repos/effect/packages/effect/src/JsonPatch.ts +469 -0
- package/repos/effect/packages/effect/src/JsonPointer.ts +80 -0
- package/repos/effect/packages/effect/src/JsonSchema.ts +948 -0
- package/repos/effect/packages/effect/src/Latch.ts +373 -0
- package/repos/effect/packages/effect/src/Layer.ts +2773 -0
- package/repos/effect/packages/effect/src/LayerMap.ts +468 -0
- package/repos/effect/packages/effect/src/LogLevel.ts +412 -0
- package/repos/effect/packages/effect/src/Logger.ts +1284 -0
- package/repos/effect/packages/effect/src/ManagedRuntime.ts +381 -0
- package/repos/effect/packages/effect/src/Match.ts +2606 -0
- package/repos/effect/packages/effect/src/Metric.ts +4058 -0
- package/repos/effect/packages/effect/src/MutableHashMap.ts +845 -0
- package/repos/effect/packages/effect/src/MutableHashSet.ts +422 -0
- package/repos/effect/packages/effect/src/MutableList.ts +967 -0
- package/repos/effect/packages/effect/src/MutableRef.ts +822 -0
- package/repos/effect/packages/effect/src/Newtype.ts +319 -0
- package/repos/effect/packages/effect/src/NonEmptyIterable.ts +221 -0
- package/repos/effect/packages/effect/src/Number.ts +860 -0
- package/repos/effect/packages/effect/src/Optic.ts +1701 -0
- package/repos/effect/packages/effect/src/Option.ts +2664 -0
- package/repos/effect/packages/effect/src/Order.ts +982 -0
- package/repos/effect/packages/effect/src/Ordering.ts +182 -0
- package/repos/effect/packages/effect/src/PartitionedSemaphore.ts +561 -0
- package/repos/effect/packages/effect/src/Path.ts +870 -0
- package/repos/effect/packages/effect/src/Pipeable.ts +674 -0
- package/repos/effect/packages/effect/src/PlatformError.ts +221 -0
- package/repos/effect/packages/effect/src/Pool.ts +699 -0
- package/repos/effect/packages/effect/src/Predicate.ts +1872 -0
- package/repos/effect/packages/effect/src/PrimaryKey.ts +123 -0
- package/repos/effect/packages/effect/src/PubSub.ts +2797 -0
- package/repos/effect/packages/effect/src/Pull.ts +376 -0
- package/repos/effect/packages/effect/src/Queue.ts +1962 -0
- package/repos/effect/packages/effect/src/Random.ts +549 -0
- package/repos/effect/packages/effect/src/RcMap.ts +612 -0
- package/repos/effect/packages/effect/src/RcRef.ts +225 -0
- package/repos/effect/packages/effect/src/Record.ts +1563 -0
- package/repos/effect/packages/effect/src/Redactable.ts +168 -0
- package/repos/effect/packages/effect/src/Redacted.ts +314 -0
- package/repos/effect/packages/effect/src/Reducer.ts +178 -0
- package/repos/effect/packages/effect/src/Ref.ts +801 -0
- package/repos/effect/packages/effect/src/References.ts +786 -0
- package/repos/effect/packages/effect/src/RegExp.ts +82 -0
- package/repos/effect/packages/effect/src/Request.ts +598 -0
- package/repos/effect/packages/effect/src/RequestResolver.ts +1249 -0
- package/repos/effect/packages/effect/src/Resource.ts +184 -0
- package/repos/effect/packages/effect/src/Result.ts +1939 -0
- package/repos/effect/packages/effect/src/Runtime.ts +432 -0
- package/repos/effect/packages/effect/src/Schedule.ts +3564 -0
- package/repos/effect/packages/effect/src/Scheduler.ts +285 -0
- package/repos/effect/packages/effect/src/Schema.ts +14653 -0
- package/repos/effect/packages/effect/src/SchemaAST.ts +3903 -0
- package/repos/effect/packages/effect/src/SchemaGetter.ts +1870 -0
- package/repos/effect/packages/effect/src/SchemaIssue.ts +1176 -0
- package/repos/effect/packages/effect/src/SchemaParser.ts +1105 -0
- package/repos/effect/packages/effect/src/SchemaRepresentation.ts +3944 -0
- package/repos/effect/packages/effect/src/SchemaTransformation.ts +1818 -0
- package/repos/effect/packages/effect/src/SchemaUtils.ts +51 -0
- package/repos/effect/packages/effect/src/Scope.ts +572 -0
- package/repos/effect/packages/effect/src/ScopedCache.ts +820 -0
- package/repos/effect/packages/effect/src/ScopedRef.ts +192 -0
- package/repos/effect/packages/effect/src/Semaphore.ts +517 -0
- package/repos/effect/packages/effect/src/Sink.ts +2174 -0
- package/repos/effect/packages/effect/src/Stdio.ts +143 -0
- package/repos/effect/packages/effect/src/Stream.ts +11722 -0
- package/repos/effect/packages/effect/src/String.ts +1427 -0
- package/repos/effect/packages/effect/src/Struct.ts +979 -0
- package/repos/effect/packages/effect/src/SubscriptionRef.ts +1021 -0
- package/repos/effect/packages/effect/src/Symbol.ts +31 -0
- package/repos/effect/packages/effect/src/SynchronizedRef.ts +668 -0
- package/repos/effect/packages/effect/src/Take.ts +46 -0
- package/repos/effect/packages/effect/src/Terminal.ts +188 -0
- package/repos/effect/packages/effect/src/Tracer.ts +683 -0
- package/repos/effect/packages/effect/src/Trie.ts +953 -0
- package/repos/effect/packages/effect/src/Tuple.ts +737 -0
- package/repos/effect/packages/effect/src/TxChunk.ts +822 -0
- package/repos/effect/packages/effect/src/TxDeferred.ts +319 -0
- package/repos/effect/packages/effect/src/TxHashMap.ts +2165 -0
- package/repos/effect/packages/effect/src/TxHashSet.ts +908 -0
- package/repos/effect/packages/effect/src/TxPriorityQueue.ts +567 -0
- package/repos/effect/packages/effect/src/TxPubSub.ts +684 -0
- package/repos/effect/packages/effect/src/TxQueue.ts +1488 -0
- package/repos/effect/packages/effect/src/TxReentrantLock.ts +655 -0
- package/repos/effect/packages/effect/src/TxRef.ts +282 -0
- package/repos/effect/packages/effect/src/TxSemaphore.ts +707 -0
- package/repos/effect/packages/effect/src/TxSubscriptionRef.ts +518 -0
- package/repos/effect/packages/effect/src/Types.ts +1121 -0
- package/repos/effect/packages/effect/src/UndefinedOr.ts +239 -0
- package/repos/effect/packages/effect/src/Unify.ts +314 -0
- package/repos/effect/packages/effect/src/Utils.ts +221 -0
- package/repos/effect/packages/effect/src/index.ts +712 -0
- package/repos/effect/packages/effect/src/internal/array.ts +8 -0
- package/repos/effect/packages/effect/src/internal/concurrency.ts +52 -0
- package/repos/effect/packages/effect/src/internal/core.ts +666 -0
- package/repos/effect/packages/effect/src/internal/dateTime.ts +1264 -0
- package/repos/effect/packages/effect/src/internal/doNotation.ts +117 -0
- package/repos/effect/packages/effect/src/internal/effect.ts +6427 -0
- package/repos/effect/packages/effect/src/internal/equal.ts +28 -0
- package/repos/effect/packages/effect/src/internal/errors.ts +9 -0
- package/repos/effect/packages/effect/src/internal/executionPlan.ts +120 -0
- package/repos/effect/packages/effect/src/internal/hashMap.ts +1309 -0
- package/repos/effect/packages/effect/src/internal/hashSet.ts +190 -0
- package/repos/effect/packages/effect/src/internal/layer.ts +88 -0
- package/repos/effect/packages/effect/src/internal/matcher.ts +637 -0
- package/repos/effect/packages/effect/src/internal/metric.ts +2 -0
- package/repos/effect/packages/effect/src/internal/option.ts +98 -0
- package/repos/effect/packages/effect/src/internal/random.ts +20 -0
- package/repos/effect/packages/effect/src/internal/rcRef.ts +189 -0
- package/repos/effect/packages/effect/src/internal/record.ts +18 -0
- package/repos/effect/packages/effect/src/internal/redacted.ts +19 -0
- package/repos/effect/packages/effect/src/internal/references.ts +72 -0
- package/repos/effect/packages/effect/src/internal/request.ts +205 -0
- package/repos/effect/packages/effect/src/internal/result.ts +112 -0
- package/repos/effect/packages/effect/src/internal/schedule.ts +248 -0
- package/repos/effect/packages/effect/src/internal/schema/annotations.ts +37 -0
- package/repos/effect/packages/effect/src/internal/schema/arbitrary.ts +894 -0
- package/repos/effect/packages/effect/src/internal/schema/cause.ts +26 -0
- package/repos/effect/packages/effect/src/internal/schema/equivalence.ts +154 -0
- package/repos/effect/packages/effect/src/internal/schema/representation.ts +799 -0
- package/repos/effect/packages/effect/src/internal/schema/schema.ts +128 -0
- package/repos/effect/packages/effect/src/internal/stream.ts +28 -0
- package/repos/effect/packages/effect/src/internal/tracer.ts +41 -0
- package/repos/effect/packages/effect/src/internal/trie.ts +733 -0
- package/repos/effect/packages/effect/src/internal/version.ts +2 -0
- package/repos/effect/packages/effect/src/testing/FastCheck.ts +89 -0
- package/repos/effect/packages/effect/src/testing/TestClock.ts +547 -0
- package/repos/effect/packages/effect/src/testing/TestConsole.ts +369 -0
- package/repos/effect/packages/effect/src/testing/TestSchema.ts +567 -0
- package/repos/effect/packages/effect/src/testing/index.ts +25 -0
- package/repos/effect/packages/effect/src/unstable/ai/AiError.ts +1629 -0
- package/repos/effect/packages/effect/src/unstable/ai/AnthropicStructuredOutput.ts +407 -0
- package/repos/effect/packages/effect/src/unstable/ai/Chat.ts +931 -0
- package/repos/effect/packages/effect/src/unstable/ai/EmbeddingModel.ts +269 -0
- package/repos/effect/packages/effect/src/unstable/ai/IdGenerator.ts +300 -0
- package/repos/effect/packages/effect/src/unstable/ai/LanguageModel.ts +2221 -0
- package/repos/effect/packages/effect/src/unstable/ai/McpSchema.ts +2527 -0
- package/repos/effect/packages/effect/src/unstable/ai/McpServer.ts +1468 -0
- package/repos/effect/packages/effect/src/unstable/ai/Model.ts +166 -0
- package/repos/effect/packages/effect/src/unstable/ai/OpenAiStructuredOutput.ts +476 -0
- package/repos/effect/packages/effect/src/unstable/ai/Prompt.ts +2234 -0
- package/repos/effect/packages/effect/src/unstable/ai/Response.ts +2582 -0
- package/repos/effect/packages/effect/src/unstable/ai/ResponseIdTracker.ts +139 -0
- package/repos/effect/packages/effect/src/unstable/ai/Telemetry.ts +515 -0
- package/repos/effect/packages/effect/src/unstable/ai/Tokenizer.ts +176 -0
- package/repos/effect/packages/effect/src/unstable/ai/Tool.ts +2003 -0
- package/repos/effect/packages/effect/src/unstable/ai/Toolkit.ts +554 -0
- package/repos/effect/packages/effect/src/unstable/ai/index.ts +90 -0
- package/repos/effect/packages/effect/src/unstable/ai/internal/codec-transformer.ts +13 -0
- package/repos/effect/packages/effect/src/unstable/cli/Argument.ts +749 -0
- package/repos/effect/packages/effect/src/unstable/cli/CliError.ts +549 -0
- package/repos/effect/packages/effect/src/unstable/cli/CliOutput.ts +607 -0
- package/repos/effect/packages/effect/src/unstable/cli/Command.ts +1610 -0
- package/repos/effect/packages/effect/src/unstable/cli/Completions.ts +123 -0
- package/repos/effect/packages/effect/src/unstable/cli/Flag.ts +986 -0
- package/repos/effect/packages/effect/src/unstable/cli/GlobalFlag.ts +295 -0
- package/repos/effect/packages/effect/src/unstable/cli/HelpDoc.ts +323 -0
- package/repos/effect/packages/effect/src/unstable/cli/Param.ts +2193 -0
- package/repos/effect/packages/effect/src/unstable/cli/Primitive.ts +766 -0
- package/repos/effect/packages/effect/src/unstable/cli/Prompt.ts +3852 -0
- package/repos/effect/packages/effect/src/unstable/cli/SEMANTICS.md +157 -0
- package/repos/effect/packages/effect/src/unstable/cli/index.ts +60 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/ansi.ts +152 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/auto-suggest.ts +41 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/command.ts +340 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/completions/bash.ts +222 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/completions/descriptor.ts +133 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/completions/fish.ts +253 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/completions/zsh.ts +220 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/config.ts +272 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/help.ts +181 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/lexer.ts +70 -0
- package/repos/effect/packages/effect/src/unstable/cli/internal/parser.ts +932 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ClusterCron.ts +163 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ClusterError.ts +238 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ClusterMetrics.ts +112 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ClusterSchema.ts +170 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ClusterWorkflowEngine.ts +778 -0
- package/repos/effect/packages/effect/src/unstable/cluster/DeliverAt.ts +58 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Entity.ts +779 -0
- package/repos/effect/packages/effect/src/unstable/cluster/EntityAddress.ts +93 -0
- package/repos/effect/packages/effect/src/unstable/cluster/EntityId.ts +50 -0
- package/repos/effect/packages/effect/src/unstable/cluster/EntityProxy.ts +263 -0
- package/repos/effect/packages/effect/src/unstable/cluster/EntityProxyServer.ts +149 -0
- package/repos/effect/packages/effect/src/unstable/cluster/EntityResource.ts +184 -0
- package/repos/effect/packages/effect/src/unstable/cluster/EntityType.ts +52 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Envelope.ts +433 -0
- package/repos/effect/packages/effect/src/unstable/cluster/HttpRunner.ts +345 -0
- package/repos/effect/packages/effect/src/unstable/cluster/K8sHttpClient.ts +297 -0
- package/repos/effect/packages/effect/src/unstable/cluster/MachineId.ts +54 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Message.ts +314 -0
- package/repos/effect/packages/effect/src/unstable/cluster/MessageStorage.ts +1072 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Reply.ts +455 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Runner.ts +129 -0
- package/repos/effect/packages/effect/src/unstable/cluster/RunnerAddress.ts +112 -0
- package/repos/effect/packages/effect/src/unstable/cluster/RunnerHealth.ts +145 -0
- package/repos/effect/packages/effect/src/unstable/cluster/RunnerServer.ts +220 -0
- package/repos/effect/packages/effect/src/unstable/cluster/RunnerStorage.ts +243 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Runners.ts +690 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ShardId.ts +178 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Sharding.ts +1488 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ShardingConfig.ts +367 -0
- package/repos/effect/packages/effect/src/unstable/cluster/ShardingRegistrationEvent.ts +76 -0
- package/repos/effect/packages/effect/src/unstable/cluster/SingleRunner.ts +75 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Singleton.ts +56 -0
- package/repos/effect/packages/effect/src/unstable/cluster/SingletonAddress.ts +49 -0
- package/repos/effect/packages/effect/src/unstable/cluster/Snowflake.ts +282 -0
- package/repos/effect/packages/effect/src/unstable/cluster/SocketRunner.ts +96 -0
- package/repos/effect/packages/effect/src/unstable/cluster/SqlMessageStorage.ts +1045 -0
- package/repos/effect/packages/effect/src/unstable/cluster/SqlRunnerStorage.ts +700 -0
- package/repos/effect/packages/effect/src/unstable/cluster/TestRunner.ts +36 -0
- package/repos/effect/packages/effect/src/unstable/cluster/index.ts +195 -0
- package/repos/effect/packages/effect/src/unstable/cluster/internal/entityManager.ts +678 -0
- package/repos/effect/packages/effect/src/unstable/cluster/internal/entityReaper.ts +58 -0
- package/repos/effect/packages/effect/src/unstable/cluster/internal/hash.ts +23 -0
- package/repos/effect/packages/effect/src/unstable/cluster/internal/interruptors.ts +2 -0
- package/repos/effect/packages/effect/src/unstable/cluster/internal/resourceMap.ts +100 -0
- package/repos/effect/packages/effect/src/unstable/cluster/internal/resourceRef.ts +99 -0
- package/repos/effect/packages/effect/src/unstable/devtools/DevTools.ts +68 -0
- package/repos/effect/packages/effect/src/unstable/devtools/DevToolsClient.ts +236 -0
- package/repos/effect/packages/effect/src/unstable/devtools/DevToolsSchema.ts +558 -0
- package/repos/effect/packages/effect/src/unstable/devtools/DevToolsServer.ts +94 -0
- package/repos/effect/packages/effect/src/unstable/devtools/index.ts +25 -0
- package/repos/effect/packages/effect/src/unstable/encoding/Msgpack.ts +383 -0
- package/repos/effect/packages/effect/src/unstable/encoding/Ndjson.ts +602 -0
- package/repos/effect/packages/effect/src/unstable/encoding/Sse.ts +593 -0
- package/repos/effect/packages/effect/src/unstable/encoding/index.ts +20 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/Event.ts +447 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventGroup.ts +193 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventJournal.ts +795 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLog.ts +1030 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogEncryption.ts +169 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogMessage.ts +354 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogRemote.ts +377 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogServer.ts +242 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogServerEncrypted.ts +268 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogServerUnencrypted.ts +898 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/EventLogSessionAuth.ts +524 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/SqlEventJournal.ts +353 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/SqlEventLogServerEncrypted.ts +343 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/SqlEventLogServerUnencrypted.ts +527 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/index.ts +75 -0
- package/repos/effect/packages/effect/src/unstable/eventlog/internal/identityRootSecretDerivation.ts +153 -0
- package/repos/effect/packages/effect/src/unstable/http/Cookies.ts +952 -0
- package/repos/effect/packages/effect/src/unstable/http/Etag.ts +141 -0
- package/repos/effect/packages/effect/src/unstable/http/FetchHttpClient.ts +123 -0
- package/repos/effect/packages/effect/src/unstable/http/FindMyWay.ts +15 -0
- package/repos/effect/packages/effect/src/unstable/http/Headers.ts +438 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpBody.ts +545 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpClient.ts +1630 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpClientError.ts +328 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpClientRequest.ts +968 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpClientResponse.ts +408 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpEffect.ts +400 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpIncomingMessage.ts +153 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpMethod.ts +124 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpMiddleware.ts +394 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpPlatform.ts +165 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpRouter.ts +1341 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpServer.ts +329 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpServerError.ts +374 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpServerRequest.ts +1081 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpServerRespondable.ts +115 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpServerResponse.ts +1381 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpStaticServer.ts +464 -0
- package/repos/effect/packages/effect/src/unstable/http/HttpTraceContext.ts +160 -0
- package/repos/effect/packages/effect/src/unstable/http/Multipart.ts +830 -0
- package/repos/effect/packages/effect/src/unstable/http/Multipasta/HeadersParser.ts +14 -0
- package/repos/effect/packages/effect/src/unstable/http/Multipasta/Node.ts +14 -0
- package/repos/effect/packages/effect/src/unstable/http/Multipasta/Search.ts +14 -0
- package/repos/effect/packages/effect/src/unstable/http/Multipasta/Web.ts +14 -0
- package/repos/effect/packages/effect/src/unstable/http/Multipasta.ts +15 -0
- package/repos/effect/packages/effect/src/unstable/http/Template.ts +262 -0
- package/repos/effect/packages/effect/src/unstable/http/Url.ts +330 -0
- package/repos/effect/packages/effect/src/unstable/http/UrlParams.ts +678 -0
- package/repos/effect/packages/effect/src/unstable/http/index.ts +145 -0
- package/repos/effect/packages/effect/src/unstable/http/internal/preResponseHandler.ts +15 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApi.ts +316 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiBuilder.ts +1015 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiClient.ts +1005 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiEndpoint.ts +1545 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiError.ts +443 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiGroup.ts +381 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiMiddleware.ts +478 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiScalar.ts +267 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiSchema.ts +733 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiSecurity.ts +244 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiSwagger.ts +68 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/HttpApiTest.ts +112 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/OpenApi.ts +1098 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/index.ts +70 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/internal/html.ts +18 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/internal/httpApiScalar.ts +5 -0
- package/repos/effect/packages/effect/src/unstable/httpapi/internal/httpApiSwagger.ts +9 -0
- package/repos/effect/packages/effect/src/unstable/observability/Otlp.ts +172 -0
- package/repos/effect/packages/effect/src/unstable/observability/OtlpExporter.ts +149 -0
- package/repos/effect/packages/effect/src/unstable/observability/OtlpLogger.ts +340 -0
- package/repos/effect/packages/effect/src/unstable/observability/OtlpMetrics.ts +826 -0
- package/repos/effect/packages/effect/src/unstable/observability/OtlpResource.ts +288 -0
- package/repos/effect/packages/effect/src/unstable/observability/OtlpSerialization.ts +66 -0
- package/repos/effect/packages/effect/src/unstable/observability/OtlpTracer.ts +442 -0
- package/repos/effect/packages/effect/src/unstable/observability/PrometheusMetrics.ts +468 -0
- package/repos/effect/packages/effect/src/unstable/observability/index.ts +45 -0
- package/repos/effect/packages/effect/src/unstable/observability/internal/otlpEnv.ts +39 -0
- package/repos/effect/packages/effect/src/unstable/observability/internal/otlpProtobuf.ts +779 -0
- package/repos/effect/packages/effect/src/unstable/observability/internal/protobuf.ts +262 -0
- package/repos/effect/packages/effect/src/unstable/persistence/KeyValueStore.ts +864 -0
- package/repos/effect/packages/effect/src/unstable/persistence/Persistable.ts +252 -0
- package/repos/effect/packages/effect/src/unstable/persistence/PersistedCache.ts +111 -0
- package/repos/effect/packages/effect/src/unstable/persistence/PersistedQueue.ts +1196 -0
- package/repos/effect/packages/effect/src/unstable/persistence/Persistence.ts +1071 -0
- package/repos/effect/packages/effect/src/unstable/persistence/RateLimiter.ts +748 -0
- package/repos/effect/packages/effect/src/unstable/persistence/Redis.ts +188 -0
- package/repos/effect/packages/effect/src/unstable/persistence/index.ts +40 -0
- package/repos/effect/packages/effect/src/unstable/process/ChildProcess.ts +1036 -0
- package/repos/effect/packages/effect/src/unstable/process/ChildProcessSpawner.ts +285 -0
- package/repos/effect/packages/effect/src/unstable/process/index.ts +15 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/AsyncResult.ts +1081 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/Atom.ts +2520 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/AtomHttpApi.ts +352 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/AtomRef.ts +352 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/AtomRegistry.ts +1127 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/AtomRpc.ts +305 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/Hydration.ts +155 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/Reactivity.ts +348 -0
- package/repos/effect/packages/effect/src/unstable/reactivity/index.ts +45 -0
- package/repos/effect/packages/effect/src/unstable/rpc/Rpc.ts +1255 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcClient.ts +1370 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcClientError.ts +57 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcGroup.ts +409 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcMessage.ts +402 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcMiddleware.ts +344 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcSchema.ts +97 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcSerialization.ts +546 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcServer.ts +1492 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcTest.ts +56 -0
- package/repos/effect/packages/effect/src/unstable/rpc/RpcWorker.ts +118 -0
- package/repos/effect/packages/effect/src/unstable/rpc/Utils.ts +120 -0
- package/repos/effect/packages/effect/src/unstable/rpc/index.ts +65 -0
- package/repos/effect/packages/effect/src/unstable/schema/Model.ts +862 -0
- package/repos/effect/packages/effect/src/unstable/schema/VariantSchema.ts +604 -0
- package/repos/effect/packages/effect/src/unstable/schema/index.ts +15 -0
- package/repos/effect/packages/effect/src/unstable/socket/Socket.ts +911 -0
- package/repos/effect/packages/effect/src/unstable/socket/SocketServer.ts +147 -0
- package/repos/effect/packages/effect/src/unstable/socket/index.ts +15 -0
- package/repos/effect/packages/effect/src/unstable/sql/Migrator.ts +440 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlClient.ts +342 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlConnection.ts +83 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlError.ts +590 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlModel.ts +359 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlResolver.ts +380 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlSchema.ts +172 -0
- package/repos/effect/packages/effect/src/unstable/sql/SqlStream.ts +81 -0
- package/repos/effect/packages/effect/src/unstable/sql/Statement.ts +1500 -0
- package/repos/effect/packages/effect/src/unstable/sql/index.ts +50 -0
- package/repos/effect/packages/effect/src/unstable/workers/Transferable.ts +191 -0
- package/repos/effect/packages/effect/src/unstable/workers/Worker.ts +245 -0
- package/repos/effect/packages/effect/src/unstable/workers/WorkerError.ts +148 -0
- package/repos/effect/packages/effect/src/unstable/workers/WorkerRunner.ts +57 -0
- package/repos/effect/packages/effect/src/unstable/workers/index.ts +25 -0
- package/repos/effect/packages/effect/src/unstable/workflow/Activity.ts +322 -0
- package/repos/effect/packages/effect/src/unstable/workflow/DurableClock.ts +117 -0
- package/repos/effect/packages/effect/src/unstable/workflow/DurableDeferred.ts +653 -0
- package/repos/effect/packages/effect/src/unstable/workflow/DurableQueue.ts +360 -0
- package/repos/effect/packages/effect/src/unstable/workflow/Workflow.ts +898 -0
- package/repos/effect/packages/effect/src/unstable/workflow/WorkflowEngine.ts +759 -0
- package/repos/effect/packages/effect/src/unstable/workflow/WorkflowProxy.ts +212 -0
- package/repos/effect/packages/effect/src/unstable/workflow/WorkflowProxyServer.ts +151 -0
- package/repos/effect/packages/effect/src/unstable/workflow/index.ts +45 -0
- package/repos/effect/packages/effect/src/unstable/workflow/internal/crypto.ts +15 -0
- package/repos/effect/packages/effect/test/Array.test.ts +1579 -0
- package/repos/effect/packages/effect/test/AtomRef.test.ts +27 -0
- package/repos/effect/packages/effect/test/BigDecimal.test.ts +525 -0
- package/repos/effect/packages/effect/test/BigInt.test.ts +57 -0
- package/repos/effect/packages/effect/test/Boolean.test.ts +289 -0
- package/repos/effect/packages/effect/test/Brand.test.ts +160 -0
- package/repos/effect/packages/effect/test/Cache.test.ts +1423 -0
- package/repos/effect/packages/effect/test/Cause.test.ts +834 -0
- package/repos/effect/packages/effect/test/Channel.test.ts +556 -0
- package/repos/effect/packages/effect/test/Chunk.test.ts +874 -0
- package/repos/effect/packages/effect/test/Combiner.test.ts +42 -0
- package/repos/effect/packages/effect/test/Config.test.ts +1416 -0
- package/repos/effect/packages/effect/test/ConfigProvider.test.ts +726 -0
- package/repos/effect/packages/effect/test/Cron.test.ts +401 -0
- package/repos/effect/packages/effect/test/Crypto.test.ts +86 -0
- package/repos/effect/packages/effect/test/Data.test.ts +269 -0
- package/repos/effect/packages/effect/test/DateTime.test.ts +953 -0
- package/repos/effect/packages/effect/test/Deferred.test.ts +165 -0
- package/repos/effect/packages/effect/test/Duration.test.ts +679 -0
- package/repos/effect/packages/effect/test/Effect.test.ts +2658 -0
- package/repos/effect/packages/effect/test/EffectEager.test.ts +678 -0
- package/repos/effect/packages/effect/test/EffectKeepAlive.test.ts +71 -0
- package/repos/effect/packages/effect/test/Equal.test.ts +1278 -0
- package/repos/effect/packages/effect/test/Equivalence.test.ts +170 -0
- package/repos/effect/packages/effect/test/ExecutionPlan.test.ts +117 -0
- package/repos/effect/packages/effect/test/Exit.test.ts +12 -0
- package/repos/effect/packages/effect/test/Fiber.test.ts +9 -0
- package/repos/effect/packages/effect/test/FiberHandle.test.ts +146 -0
- package/repos/effect/packages/effect/test/FiberMap.test.ts +171 -0
- package/repos/effect/packages/effect/test/FiberSet.test.ts +119 -0
- package/repos/effect/packages/effect/test/Formatter.test.ts +247 -0
- package/repos/effect/packages/effect/test/Function.test.ts +333 -0
- package/repos/effect/packages/effect/test/Graph.test.ts +3505 -0
- package/repos/effect/packages/effect/test/HashMap.test.ts +618 -0
- package/repos/effect/packages/effect/test/HashSet.test.ts +298 -0
- package/repos/effect/packages/effect/test/HttpClient.test.ts +131 -0
- package/repos/effect/packages/effect/test/Iterable.test.ts +486 -0
- package/repos/effect/packages/effect/test/JsonPatch.test.ts +948 -0
- package/repos/effect/packages/effect/test/JsonPointer.test.ts +353 -0
- package/repos/effect/packages/effect/test/JsonSchema.test.ts +1253 -0
- package/repos/effect/packages/effect/test/Latch.test.ts +21 -0
- package/repos/effect/packages/effect/test/Layer.test.ts +648 -0
- package/repos/effect/packages/effect/test/LayerMap.test.ts +98 -0
- package/repos/effect/packages/effect/test/LogLevel.test.ts +28 -0
- package/repos/effect/packages/effect/test/Logger.test.ts +154 -0
- package/repos/effect/packages/effect/test/ManagedRuntime.test.ts +58 -0
- package/repos/effect/packages/effect/test/Match.test.ts +27 -0
- package/repos/effect/packages/effect/test/Metric.test.ts +682 -0
- package/repos/effect/packages/effect/test/Migrator.test.ts +69 -0
- package/repos/effect/packages/effect/test/MutableHashMap.test.ts +309 -0
- package/repos/effect/packages/effect/test/MutableHashSet.test.ts +10 -0
- package/repos/effect/packages/effect/test/MutableList.test.ts +58 -0
- package/repos/effect/packages/effect/test/Newtype.test.ts +90 -0
- package/repos/effect/packages/effect/test/Number.test.ts +422 -0
- package/repos/effect/packages/effect/test/Optic.test.ts +452 -0
- package/repos/effect/packages/effect/test/Option.test.ts +564 -0
- package/repos/effect/packages/effect/test/Order.test.ts +163 -0
- package/repos/effect/packages/effect/test/Ordering.test.ts +18 -0
- package/repos/effect/packages/effect/test/PartitionedSemaphore.test.ts +74 -0
- package/repos/effect/packages/effect/test/Pathfinding.test.ts +426 -0
- package/repos/effect/packages/effect/test/Pool.test.ts +422 -0
- package/repos/effect/packages/effect/test/Predicate.test.ts +555 -0
- package/repos/effect/packages/effect/test/PubSub.test.ts +538 -0
- package/repos/effect/packages/effect/test/Queue.test.ts +317 -0
- package/repos/effect/packages/effect/test/Random.test.ts +238 -0
- package/repos/effect/packages/effect/test/RcMap.test.ts +237 -0
- package/repos/effect/packages/effect/test/RcRef.test.ts +97 -0
- package/repos/effect/packages/effect/test/Record.test.ts +395 -0
- package/repos/effect/packages/effect/test/Redacted.test.ts +59 -0
- package/repos/effect/packages/effect/test/Reducer.test.ts +12 -0
- package/repos/effect/packages/effect/test/Ref.test.ts +187 -0
- package/repos/effect/packages/effect/test/Request.test.ts +305 -0
- package/repos/effect/packages/effect/test/Resource.test.ts +59 -0
- package/repos/effect/packages/effect/test/Result.test.ts +517 -0
- package/repos/effect/packages/effect/test/Schedule.test.ts +395 -0
- package/repos/effect/packages/effect/test/Scheduler.test.ts +70 -0
- package/repos/effect/packages/effect/test/Scope.test.ts +19 -0
- package/repos/effect/packages/effect/test/ScopedCache.test.ts +2316 -0
- package/repos/effect/packages/effect/test/ScopedRef.test.ts +80 -0
- package/repos/effect/packages/effect/test/Semaphore.test.ts +469 -0
- package/repos/effect/packages/effect/test/Sink.test.ts +230 -0
- package/repos/effect/packages/effect/test/Stream.test.ts +4712 -0
- package/repos/effect/packages/effect/test/String.test.ts +690 -0
- package/repos/effect/packages/effect/test/Struct.test.ts +216 -0
- package/repos/effect/packages/effect/test/SubscriptionRef.test.ts +90 -0
- package/repos/effect/packages/effect/test/Symbol.test.ts +50 -0
- package/repos/effect/packages/effect/test/SynchronizedRef.test.ts +105 -0
- package/repos/effect/packages/effect/test/TestClock.test.ts +71 -0
- package/repos/effect/packages/effect/test/Tracer.test.ts +410 -0
- package/repos/effect/packages/effect/test/Trie.test.ts +531 -0
- package/repos/effect/packages/effect/test/Tuple.test.ts +127 -0
- package/repos/effect/packages/effect/test/TxChunk.test.ts +342 -0
- package/repos/effect/packages/effect/test/TxDeferred.test.ts +197 -0
- package/repos/effect/packages/effect/test/TxHashMap.test.ts +963 -0
- package/repos/effect/packages/effect/test/TxHashSet.test.ts +442 -0
- package/repos/effect/packages/effect/test/TxPriorityQueue.test.ts +187 -0
- package/repos/effect/packages/effect/test/TxPubSub.test.ts +391 -0
- package/repos/effect/packages/effect/test/TxQueue.test.ts +1260 -0
- package/repos/effect/packages/effect/test/TxReentrantLock.test.ts +413 -0
- package/repos/effect/packages/effect/test/TxSemaphore.test.ts +283 -0
- package/repos/effect/packages/effect/test/TxSubscriptionRef.test.ts +197 -0
- package/repos/effect/packages/effect/test/UndefinedOr.test.ts +65 -0
- package/repos/effect/packages/effect/test/cluster/ClusterWorkflowEngine.test.ts +652 -0
- package/repos/effect/packages/effect/test/cluster/Entity.test.ts +81 -0
- package/repos/effect/packages/effect/test/cluster/K8sHttpClient.test.ts +34 -0
- package/repos/effect/packages/effect/test/cluster/MessageStorage.test.ts +196 -0
- package/repos/effect/packages/effect/test/cluster/Sharding.test.ts +578 -0
- package/repos/effect/packages/effect/test/cluster/TestEntity.ts +132 -0
- package/repos/effect/packages/effect/test/reactivity/AsyncResult.test.ts +41 -0
- package/repos/effect/packages/effect/test/reactivity/Atom.test.ts +2551 -0
- package/repos/effect/packages/effect/test/reactivity/AtomHttpApi.test.ts +86 -0
- package/repos/effect/packages/effect/test/reactivity/AtomRpc.test.ts +70 -0
- package/repos/effect/packages/effect/test/rpc/Rpc.test.ts +73 -0
- package/repos/effect/packages/effect/test/rpc/RpcClient.test.ts +58 -0
- package/repos/effect/packages/effect/test/rpc/RpcSerialization.test.ts +171 -0
- package/repos/effect/packages/effect/test/schema/HMR.test.ts +72 -0
- package/repos/effect/packages/effect/test/schema/Schema.test.ts +9133 -0
- package/repos/effect/packages/effect/test/schema/SchemaAST.test.ts +280 -0
- package/repos/effect/packages/effect/test/schema/SchemaGetter.test.ts +383 -0
- package/repos/effect/packages/effect/test/schema/SchemaIssue.test.ts +9 -0
- package/repos/effect/packages/effect/test/schema/SchemaParser.test.ts +323 -0
- package/repos/effect/packages/effect/test/schema/fixtures/HMR-Class.ts +5 -0
- package/repos/effect/packages/effect/test/schema/fixtures/HMR-sanity-check.ts +3 -0
- package/repos/effect/packages/effect/test/schema/representation/fromASTs.test.ts +820 -0
- package/repos/effect/packages/effect/test/schema/representation/fromJsonSchemaDocument.test.ts +2137 -0
- package/repos/effect/packages/effect/test/schema/representation/toCodeDocument.test.ts +2089 -0
- package/repos/effect/packages/effect/test/schema/representation/toJsonSchemaMultiDocument.test.ts +39 -0
- package/repos/effect/packages/effect/test/schema/representation/toSchema.test.ts +506 -0
- package/repos/effect/packages/effect/test/schema/toArbitrary.test.ts +1693 -0
- package/repos/effect/packages/effect/test/schema/toCodec.test.ts +3149 -0
- package/repos/effect/packages/effect/test/schema/toDifferJsonPatch.test.ts +239 -0
- package/repos/effect/packages/effect/test/schema/toEquivalence.test.ts +519 -0
- package/repos/effect/packages/effect/test/schema/toFormatter.test.ts +565 -0
- package/repos/effect/packages/effect/test/schema/toIso.test.ts +394 -0
- package/repos/effect/packages/effect/test/schema/toJsonSchemaDocument.test.ts +3630 -0
- package/repos/effect/packages/effect/test/schema/toStandardJSONSchemaV1.test.ts +126 -0
- package/repos/effect/packages/effect/test/schema/toStandardSchemaV1.test.ts +495 -0
- package/repos/effect/packages/effect/test/schema/v3-v4.test.ts +250 -0
- package/repos/effect/packages/effect/test/testing/TestSchema.test.ts +78 -0
- package/repos/effect/packages/effect/test/unstable/ai/AiError.test.ts +823 -0
- package/repos/effect/packages/effect/test/unstable/ai/AnthropicStructuredOutput.test.ts +593 -0
- package/repos/effect/packages/effect/test/unstable/ai/Chat.test.ts +147 -0
- package/repos/effect/packages/effect/test/unstable/ai/EmbeddingModel.test.ts +185 -0
- package/repos/effect/packages/effect/test/unstable/ai/LanguageModel.test.ts +1714 -0
- package/repos/effect/packages/effect/test/unstable/ai/LanguageModelTrackerLifecycle.test.ts +413 -0
- package/repos/effect/packages/effect/test/unstable/ai/McpServer.test.ts +82 -0
- package/repos/effect/packages/effect/test/unstable/ai/Model.test.ts +16 -0
- package/repos/effect/packages/effect/test/unstable/ai/OpenAiStructuredOutput.test.ts +723 -0
- package/repos/effect/packages/effect/test/unstable/ai/Prompt.test.ts +346 -0
- package/repos/effect/packages/effect/test/unstable/ai/ResponseIdTracker.test.ts +424 -0
- package/repos/effect/packages/effect/test/unstable/ai/Tool.test.ts +1264 -0
- package/repos/effect/packages/effect/test/unstable/ai/utils.ts +68 -0
- package/repos/effect/packages/effect/test/unstable/cli/Arguments.test.ts +390 -0
- package/repos/effect/packages/effect/test/unstable/cli/Command.test.ts +1801 -0
- package/repos/effect/packages/effect/test/unstable/cli/Errors.test.ts +150 -0
- package/repos/effect/packages/effect/test/unstable/cli/Help.test.ts +572 -0
- package/repos/effect/packages/effect/test/unstable/cli/LogLevel.test.ts +202 -0
- package/repos/effect/packages/effect/test/unstable/cli/Param.test.ts +391 -0
- package/repos/effect/packages/effect/test/unstable/cli/Primitive.test.ts +321 -0
- package/repos/effect/packages/effect/test/unstable/cli/Prompt.test.ts +597 -0
- package/repos/effect/packages/effect/test/unstable/cli/completions/completions.test.ts +656 -0
- package/repos/effect/packages/effect/test/unstable/cli/completions/descriptor.test.ts +245 -0
- package/repos/effect/packages/effect/test/unstable/cli/fixtures/ComprehensiveCli.ts +422 -0
- package/repos/effect/packages/effect/test/unstable/cli/services/MockTerminal.ts +135 -0
- package/repos/effect/packages/effect/test/unstable/cli/services/TestActions.ts +36 -0
- package/repos/effect/packages/effect/test/unstable/encoding/Ndjson.test.ts +18 -0
- package/repos/effect/packages/effect/test/unstable/encoding/Sse.test.ts +62 -0
- package/repos/effect/packages/effect/test/unstable/eventlog/EventJournal.test.ts +25 -0
- package/repos/effect/packages/effect/test/unstable/eventlog/EventLog.test.ts +104 -0
- package/repos/effect/packages/effect/test/unstable/eventlog/EventLogIdentityDerivation.test.ts +85 -0
- package/repos/effect/packages/effect/test/unstable/eventlog/EventLogRemote.test.ts +176 -0
- package/repos/effect/packages/effect/test/unstable/eventlog/EventLogSessionAuth.test.ts +130 -0
- package/repos/effect/packages/effect/test/unstable/eventlog/SqlEventLogServerUnencryptedStorageTest.ts +169 -0
- package/repos/effect/packages/effect/test/unstable/http/Cookies.test.ts +80 -0
- package/repos/effect/packages/effect/test/unstable/http/Headers.test.ts +73 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpClient.test.ts +306 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpClientRequest.test.ts +164 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpEffect.test.ts +254 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpMiddleware.test.ts +57 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpServerError.test.ts +121 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpServerRequest.test.ts +157 -0
- package/repos/effect/packages/effect/test/unstable/http/HttpServerResponse.test.ts +76 -0
- package/repos/effect/packages/effect/test/unstable/http/Multipart.test.ts +69 -0
- package/repos/effect/packages/effect/test/unstable/http/Url.test.ts +98 -0
- package/repos/effect/packages/effect/test/unstable/http/UrlParams.test.ts +30 -0
- package/repos/effect/packages/effect/test/unstable/httpapi/HttpApiBuilder.test.ts +186 -0
- package/repos/effect/packages/effect/test/unstable/httpapi/HttpApiClient.test.ts +446 -0
- package/repos/effect/packages/effect/test/unstable/httpapi/HttpApiEndpoint.test.ts +160 -0
- package/repos/effect/packages/effect/test/unstable/httpapi/HttpApiSchema.test.ts +108 -0
- package/repos/effect/packages/effect/test/unstable/httpapi/HttpApiSecurity.test.ts +41 -0
- package/repos/effect/packages/effect/test/unstable/httpapi/OpenApi.test.ts +39 -0
- package/repos/effect/packages/effect/test/unstable/observability/OtlpConfig.test.ts +151 -0
- package/repos/effect/packages/effect/test/unstable/observability/OtlpExporter.test.ts +108 -0
- package/repos/effect/packages/effect/test/unstable/observability/OtlpMetrics.test.ts +518 -0
- package/repos/effect/packages/effect/test/unstable/observability/OtlpResource.test.ts +84 -0
- package/repos/effect/packages/effect/test/unstable/observability/PrometheusMetrics.test.ts +417 -0
- package/repos/effect/packages/effect/test/unstable/persistence/KeyValueStore.test.ts +164 -0
- package/repos/effect/packages/effect/test/unstable/persistence/KeyValueStoreTest.ts +85 -0
- package/repos/effect/packages/effect/test/unstable/persistence/PersistedCache.test.ts +4 -0
- package/repos/effect/packages/effect/test/unstable/persistence/PersistedCacheTest.ts +111 -0
- package/repos/effect/packages/effect/test/unstable/persistence/PersistedQueue.test.ts +4 -0
- package/repos/effect/packages/effect/test/unstable/persistence/PersistedQueueTest.ts +106 -0
- package/repos/effect/packages/effect/test/unstable/persistence/RateLimiter.test.ts +145 -0
- package/repos/effect/packages/effect/test/unstable/persistence/Redis.test.ts +43 -0
- package/repos/effect/packages/effect/test/unstable/process/ChildProcess.test.ts +352 -0
- package/repos/effect/packages/effect/test/unstable/schema/VariantSchema.test.ts +72 -0
- package/repos/effect/packages/effect/test/unstable/sql/SqlError.test.ts +228 -0
- package/repos/effect/packages/effect/test/unstable/sql/SqlSchema.test.ts +168 -0
- package/repos/effect/packages/effect/test/unstable/workers/WorkerError.test.ts +90 -0
- package/repos/effect/packages/effect/test/unstable/workflow/DurableQueue.test.ts +105 -0
- package/repos/effect/packages/effect/test/unstable/workflow/WorkflowEngine.test.ts +60 -0
- package/repos/effect/packages/effect/test/utils/assert.ts +182 -0
- package/repos/effect/packages/effect/test/utils/chunkCoordination.ts +36 -0
- package/repos/effect/packages/effect/test/utils/counter.ts +54 -0
- package/repos/effect/packages/effect/tsconfig.json +8 -0
- package/repos/effect/packages/effect/typetest/AiError.tst.ts +49 -0
- package/repos/effect/packages/effect/typetest/Array.tst.ts +1289 -0
- package/repos/effect/packages/effect/typetest/Brand.tst.ts +51 -0
- package/repos/effect/packages/effect/typetest/Channel.tst.ts +112 -0
- package/repos/effect/packages/effect/typetest/Config.tst.ts +31 -0
- package/repos/effect/packages/effect/typetest/Data.tst.ts +121 -0
- package/repos/effect/packages/effect/typetest/Effect.tst.ts +976 -0
- package/repos/effect/packages/effect/typetest/Equivalence.tst.ts +19 -0
- package/repos/effect/packages/effect/typetest/Newtype.tst.ts +72 -0
- package/repos/effect/packages/effect/typetest/Optic.tst.ts +130 -0
- package/repos/effect/packages/effect/typetest/Option.tst.ts +72 -0
- package/repos/effect/packages/effect/typetest/Order.tst.ts +19 -0
- package/repos/effect/packages/effect/typetest/Predicate.tst.ts +251 -0
- package/repos/effect/packages/effect/typetest/Result.tst.ts +353 -0
- package/repos/effect/packages/effect/typetest/Schedule.tst.ts +21 -0
- package/repos/effect/packages/effect/typetest/Stream.tst.ts +197 -0
- package/repos/effect/packages/effect/typetest/Struct.tst.ts +422 -0
- package/repos/effect/packages/effect/typetest/Tuple.tst.ts +177 -0
- package/repos/effect/packages/effect/typetest/Types.tst.ts +364 -0
- package/repos/effect/packages/effect/typetest/VariantSchema.tst.ts +76 -0
- package/repos/effect/packages/effect/typetest/schema/Array.tst.ts +46 -0
- package/repos/effect/packages/effect/typetest/schema/Literals.tst.ts +58 -0
- package/repos/effect/packages/effect/typetest/schema/Record.tst.ts +145 -0
- package/repos/effect/packages/effect/typetest/schema/Schema.tst.ts +1854 -0
- package/repos/effect/packages/effect/typetest/schema/Struct.tst.ts +745 -0
- package/repos/effect/packages/effect/typetest/schema/Tuple.tst.ts +199 -0
- package/repos/effect/packages/effect/typetest/schema/Union.tst.ts +198 -0
- package/repos/effect/packages/effect/typetest/schema/api.tst.ts +276 -0
- package/repos/effect/packages/effect/typetest/schema/toArbitrary.tst.ts +65 -0
- package/repos/effect/packages/effect/typetest/schema/toIso.tst.ts +282 -0
- package/repos/effect/packages/effect/typetest/unstable/ai/LanguageModel.tst.ts +191 -0
- package/repos/effect/packages/effect/typetest/unstable/ai/Tool.tst.ts +33 -0
- package/repos/effect/packages/effect/typetest/unstable/cli/Command.tst.ts +124 -0
- package/repos/effect/packages/effect/typetest/unstable/cli/Param.tst.ts +20 -0
- package/repos/effect/packages/effect/typetest/unstable/cli/Prompt.tst.ts +97 -0
- package/repos/effect/packages/effect/typetest/unstable/encoding/Ndjson.tst.ts +24 -0
- package/repos/effect/packages/effect/typetest/unstable/http/HttpClient.tst.ts +143 -0
- package/repos/effect/packages/effect/typetest/unstable/httpapi/HttpApiBuilder.tst.ts +98 -0
- package/repos/effect/packages/effect/typetest/unstable/httpapi/HttpApiClient.tst.ts +677 -0
- package/repos/effect/packages/effect/typetest/unstable/httpapi/HttpApiEndpoint.tst.ts +466 -0
- package/repos/effect/packages/effect/typetest/unstable/httpapi/HttpApiMiddleware.tst.ts +90 -0
- package/repos/effect/packages/effect/typetest/unstable/httpapi/HttpApiSchema.tst.ts +135 -0
- package/repos/effect/packages/effect/typetest/unstable/reactivity/AsyncResult.tst.ts +65 -0
- package/repos/effect/packages/effect/typetest/unstable/reactivity/AtomHttpApi.tst.ts +63 -0
- package/repos/effect/packages/effect/typetest/unstable/rpc/Rpc.tst.ts +74 -0
- package/repos/effect/packages/effect/typetest/unstable/rpc/RpcGroup.tst.ts +61 -0
- package/repos/effect/packages/effect/typetest/unstable/rpc/RpcMiddleware.tst.ts +41 -0
- package/repos/effect/packages/effect/typetest/unstable/rpc/RpcServer.tst.ts +26 -0
- package/repos/effect/packages/effect/typetest/unstable/sql/SqlSchema.tst.ts +75 -0
- package/repos/effect/packages/effect/typetest/unstable/workflow/Workflow.tst.ts +16 -0
- package/repos/effect/packages/effect/vitest.config.ts +11 -0
- package/repos/effect/packages/opentelemetry/CHANGELOG.md +620 -0
- package/repos/effect/packages/opentelemetry/LICENSE +21 -0
- package/repos/effect/packages/opentelemetry/README.md +5 -0
- package/repos/effect/packages/opentelemetry/docgen.json +30 -0
- package/repos/effect/packages/opentelemetry/package.json +126 -0
- package/repos/effect/packages/opentelemetry/src/Logger.ts +193 -0
- package/repos/effect/packages/opentelemetry/src/Metrics.ts +138 -0
- package/repos/effect/packages/opentelemetry/src/NodeSdk.ts +162 -0
- package/repos/effect/packages/opentelemetry/src/Resource.ts +147 -0
- package/repos/effect/packages/opentelemetry/src/Tracer.ts +594 -0
- package/repos/effect/packages/opentelemetry/src/WebSdk.ts +140 -0
- package/repos/effect/packages/opentelemetry/src/index.ts +35 -0
- package/repos/effect/packages/opentelemetry/src/internal/attributes.ts +28 -0
- package/repos/effect/packages/opentelemetry/src/internal/metrics.ts +463 -0
- package/repos/effect/packages/opentelemetry/src/internal/utilities.ts +5 -0
- package/repos/effect/packages/opentelemetry/test/Logger.test.ts +122 -0
- package/repos/effect/packages/opentelemetry/test/Metrics.test.ts +339 -0
- package/repos/effect/packages/opentelemetry/test/Tracer.test.ts +167 -0
- package/repos/effect/packages/opentelemetry/tsconfig.json +8 -0
- package/repos/effect/packages/opentelemetry/vitest.config.ts +6 -0
- package/repos/effect/packages/platform-browser/CHANGELOG.md +637 -0
- package/repos/effect/packages/platform-browser/LICENSE +21 -0
- package/repos/effect/packages/platform-browser/README.md +1 -0
- package/repos/effect/packages/platform-browser/docgen.json +21 -0
- package/repos/effect/packages/platform-browser/package.json +74 -0
- package/repos/effect/packages/platform-browser/src/BrowserCrypto.ts +97 -0
- package/repos/effect/packages/platform-browser/src/BrowserHttpClient.ts +430 -0
- package/repos/effect/packages/platform-browser/src/BrowserKeyValueStore.ts +177 -0
- package/repos/effect/packages/platform-browser/src/BrowserPersistence.ts +334 -0
- package/repos/effect/packages/platform-browser/src/BrowserRuntime.ts +53 -0
- package/repos/effect/packages/platform-browser/src/BrowserSocket.ts +52 -0
- package/repos/effect/packages/platform-browser/src/BrowserStream.ts +56 -0
- package/repos/effect/packages/platform-browser/src/BrowserWorker.ts +98 -0
- package/repos/effect/packages/platform-browser/src/BrowserWorkerRunner.ts +199 -0
- package/repos/effect/packages/platform-browser/src/Clipboard.ts +143 -0
- package/repos/effect/packages/platform-browser/src/Geolocation.ts +233 -0
- package/repos/effect/packages/platform-browser/src/IndexedDb.ts +113 -0
- package/repos/effect/packages/platform-browser/src/IndexedDbDatabase.ts +648 -0
- package/repos/effect/packages/platform-browser/src/IndexedDbQueryBuilder.ts +2032 -0
- package/repos/effect/packages/platform-browser/src/IndexedDbTable.ts +260 -0
- package/repos/effect/packages/platform-browser/src/IndexedDbVersion.ts +138 -0
- package/repos/effect/packages/platform-browser/src/Permissions.ts +146 -0
- package/repos/effect/packages/platform-browser/src/index.ts +90 -0
- package/repos/effect/packages/platform-browser/test/BrowserCrypto.test.ts +74 -0
- package/repos/effect/packages/platform-browser/test/BrowserHttpClient.test.ts +91 -0
- package/repos/effect/packages/platform-browser/test/BrowserKeyValueStore.test.ts +23 -0
- package/repos/effect/packages/platform-browser/test/BrowserPersistence.test.ts +209 -0
- package/repos/effect/packages/platform-browser/test/BrowserPersistencePersistedCache.test.ts +29 -0
- package/repos/effect/packages/platform-browser/test/IndexedDbDatabase.test.ts +253 -0
- package/repos/effect/packages/platform-browser/test/IndexedDbQueryBuilder.test.ts +1459 -0
- package/repos/effect/packages/platform-browser/test/IndexedDbTable.test.ts +101 -0
- package/repos/effect/packages/platform-browser/test/IndexedDbVersion.test.ts +50 -0
- package/repos/effect/packages/platform-browser/test/Permissions.test.ts +12 -0
- package/repos/effect/packages/platform-browser/test/RpcWorker.test.ts +19 -0
- package/repos/effect/packages/platform-browser/test/fixtures/rpc-e2e.ts +141 -0
- package/repos/effect/packages/platform-browser/test/fixtures/rpc-schemas.ts +157 -0
- package/repos/effect/packages/platform-browser/test/fixtures/rpc-worker.ts +12 -0
- package/repos/effect/packages/platform-browser/tsconfig.json +8 -0
- package/repos/effect/packages/platform-browser/vitest.config.ts +17 -0
- package/repos/effect/packages/platform-browser/vitest.setup.ts +5 -0
- package/repos/effect/packages/platform-bun/CHANGELOG.md +691 -0
- package/repos/effect/packages/platform-bun/LICENSE +21 -0
- package/repos/effect/packages/platform-bun/README.md +7 -0
- package/repos/effect/packages/platform-bun/docgen.json +20 -0
- package/repos/effect/packages/platform-bun/package.json +73 -0
- package/repos/effect/packages/platform-bun/src/BunChildProcessSpawner.ts +6 -0
- package/repos/effect/packages/platform-bun/src/BunClusterHttp.ts +174 -0
- package/repos/effect/packages/platform-bun/src/BunClusterSocket.ts +153 -0
- package/repos/effect/packages/platform-bun/src/BunCrypto.ts +24 -0
- package/repos/effect/packages/platform-bun/src/BunFileSystem.ts +20 -0
- package/repos/effect/packages/platform-bun/src/BunHttpClient.ts +9 -0
- package/repos/effect/packages/platform-bun/src/BunHttpPlatform.ts +50 -0
- package/repos/effect/packages/platform-bun/src/BunHttpServer.ts +600 -0
- package/repos/effect/packages/platform-bun/src/BunHttpServerRequest.ts +19 -0
- package/repos/effect/packages/platform-bun/src/BunMultipart.ts +54 -0
- package/repos/effect/packages/platform-bun/src/BunPath.ts +37 -0
- package/repos/effect/packages/platform-bun/src/BunRedis.ts +88 -0
- package/repos/effect/packages/platform-bun/src/BunRuntime.ts +52 -0
- package/repos/effect/packages/platform-bun/src/BunServices.ts +49 -0
- package/repos/effect/packages/platform-bun/src/BunSink.ts +8 -0
- package/repos/effect/packages/platform-bun/src/BunSocket.ts +54 -0
- package/repos/effect/packages/platform-bun/src/BunSocketServer.ts +8 -0
- package/repos/effect/packages/platform-bun/src/BunStdio.ts +22 -0
- package/repos/effect/packages/platform-bun/src/BunStream.ts +68 -0
- package/repos/effect/packages/platform-bun/src/BunTerminal.ts +32 -0
- package/repos/effect/packages/platform-bun/src/BunWorker.ts +92 -0
- package/repos/effect/packages/platform-bun/src/BunWorkerRunner.ts +113 -0
- package/repos/effect/packages/platform-bun/src/index.ts +115 -0
- package/repos/effect/packages/platform-bun/tsconfig.json +12 -0
- package/repos/effect/packages/platform-node/CHANGELOG.md +695 -0
- package/repos/effect/packages/platform-node/LICENSE +21 -0
- package/repos/effect/packages/platform-node/README.md +7 -0
- package/repos/effect/packages/platform-node/docgen.json +24 -0
- package/repos/effect/packages/platform-node/package.json +82 -0
- package/repos/effect/packages/platform-node/src/Mime.ts +25 -0
- package/repos/effect/packages/platform-node/src/NodeChildProcessSpawner.ts +6 -0
- package/repos/effect/packages/platform-node/src/NodeClusterHttp.ts +155 -0
- package/repos/effect/packages/platform-node/src/NodeClusterSocket.ts +176 -0
- package/repos/effect/packages/platform-node/src/NodeCrypto.ts +24 -0
- package/repos/effect/packages/platform-node/src/NodeFileSystem.ts +21 -0
- package/repos/effect/packages/platform-node/src/NodeHttpClient.ts +664 -0
- package/repos/effect/packages/platform-node/src/NodeHttpIncomingMessage.ts +141 -0
- package/repos/effect/packages/platform-node/src/NodeHttpPlatform.ts +70 -0
- package/repos/effect/packages/platform-node/src/NodeHttpServer.ts +646 -0
- package/repos/effect/packages/platform-node/src/NodeHttpServerRequest.ts +33 -0
- package/repos/effect/packages/platform-node/src/NodeMultipart.ts +183 -0
- package/repos/effect/packages/platform-node/src/NodePath.ts +40 -0
- package/repos/effect/packages/platform-node/src/NodeRedis.ts +92 -0
- package/repos/effect/packages/platform-node/src/NodeRuntime.ts +53 -0
- package/repos/effect/packages/platform-node/src/NodeServices.ts +50 -0
- package/repos/effect/packages/platform-node/src/NodeSink.ts +8 -0
- package/repos/effect/packages/platform-node/src/NodeSocket.ts +72 -0
- package/repos/effect/packages/platform-node/src/NodeSocketServer.ts +8 -0
- package/repos/effect/packages/platform-node/src/NodeStdio.ts +22 -0
- package/repos/effect/packages/platform-node/src/NodeStream.ts +8 -0
- package/repos/effect/packages/platform-node/src/NodeTerminal.ts +32 -0
- package/repos/effect/packages/platform-node/src/NodeWorker.ts +121 -0
- package/repos/effect/packages/platform-node/src/NodeWorkerRunner.ts +110 -0
- package/repos/effect/packages/platform-node/src/Undici.ts +29 -0
- package/repos/effect/packages/platform-node/src/index.ts +130 -0
- package/repos/effect/packages/platform-node/test/HttpApi.test.ts +1831 -0
- package/repos/effect/packages/platform-node/test/HttpStaticServer.test.ts +253 -0
- package/repos/effect/packages/platform-node/test/HttpStaticServerConditional.test.ts +296 -0
- package/repos/effect/packages/platform-node/test/NodeCrypto.test.ts +59 -0
- package/repos/effect/packages/platform-node/test/NodeHttpClient.test.ts +157 -0
- package/repos/effect/packages/platform-node/test/NodeHttpPlatform.test.ts +51 -0
- package/repos/effect/packages/platform-node/test/NodeHttpServer.test.ts +598 -0
- package/repos/effect/packages/platform-node/test/NodeRedis.test.ts +31 -0
- package/repos/effect/packages/platform-node/test/NodeSocket.test.ts +153 -0
- package/repos/effect/packages/platform-node/test/OpenApi.test.ts +1573 -0
- package/repos/effect/packages/platform-node/test/RpcServer.test.ts +213 -0
- package/repos/effect/packages/platform-node/test/__snapshots__/HttpApi.test.ts.snap +715 -0
- package/repos/effect/packages/platform-node/test/cluster/MessageStorageTest.ts +196 -0
- package/repos/effect/packages/platform-node/test/cluster/SocketRunner.test.ts +120 -0
- package/repos/effect/packages/platform-node/test/cluster/SqlMessageStorage.test.ts +222 -0
- package/repos/effect/packages/platform-node/test/cluster/SqlRunnerStorage.test.ts +101 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/conditional.txt +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/custom/home.html +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/file.binx +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/guide/index.html +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/hello.txt +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/index.html +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server/range.txt +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/http-static-server-outside.txt +1 -0
- package/repos/effect/packages/platform-node/test/fixtures/mysql2-utils.ts +64 -0
- package/repos/effect/packages/platform-node/test/fixtures/pg-utils.ts +39 -0
- package/repos/effect/packages/platform-node/test/fixtures/rpc-e2e.ts +149 -0
- package/repos/effect/packages/platform-node/test/fixtures/rpc-schemas.ts +175 -0
- package/repos/effect/packages/platform-node/test/fixtures/text.txt +1 -0
- package/repos/effect/packages/platform-node/tsconfig.json +12 -0
- package/repos/effect/packages/platform-node/vitest.config.ts +6 -0
- package/repos/effect/packages/platform-node-shared/CHANGELOG.md +617 -0
- package/repos/effect/packages/platform-node-shared/LICENSE +21 -0
- package/repos/effect/packages/platform-node-shared/README.md +7 -0
- package/repos/effect/packages/platform-node-shared/docgen.json +38 -0
- package/repos/effect/packages/platform-node-shared/package.json +76 -0
- package/repos/effect/packages/platform-node-shared/src/NodeChildProcessSpawner.ts +713 -0
- package/repos/effect/packages/platform-node-shared/src/NodeClusterSocket.ts +70 -0
- package/repos/effect/packages/platform-node-shared/src/NodeCrypto.ts +60 -0
- package/repos/effect/packages/platform-node-shared/src/NodeFileSystem.ts +652 -0
- package/repos/effect/packages/platform-node-shared/src/NodePath.ts +80 -0
- package/repos/effect/packages/platform-node-shared/src/NodeRuntime.ts +59 -0
- package/repos/effect/packages/platform-node-shared/src/NodeSink.ts +116 -0
- package/repos/effect/packages/platform-node-shared/src/NodeSocket.ts +269 -0
- package/repos/effect/packages/platform-node-shared/src/NodeSocketServer.ts +301 -0
- package/repos/effect/packages/platform-node-shared/src/NodeStdio.ts +67 -0
- package/repos/effect/packages/platform-node-shared/src/NodeStream.ts +445 -0
- package/repos/effect/packages/platform-node-shared/src/NodeTerminal.ts +129 -0
- package/repos/effect/packages/platform-node-shared/src/index.ts +65 -0
- package/repos/effect/packages/platform-node-shared/src/internal/utils.ts +51 -0
- package/repos/effect/packages/platform-node-shared/test/NodeChildProcessSpawner.test.ts +1069 -0
- package/repos/effect/packages/platform-node-shared/test/NodeFileSystem.test.ts +214 -0
- package/repos/effect/packages/platform-node-shared/test/NodeSink.test.ts +153 -0
- package/repos/effect/packages/platform-node-shared/test/NodeStream.test.ts +173 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/bash/no-permissions.sh +3 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/bash/parent-exits-early.sh +31 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/bash/spawn-children.sh +32 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/config/SHOUTING +1 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/config/integer +1 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/config/nested/config +1 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/config/secret +1 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/helloworld.tar.gz +0 -0
- package/repos/effect/packages/platform-node-shared/test/fixtures/text.txt +1 -0
- package/repos/effect/packages/platform-node-shared/tsconfig.json +11 -0
- package/repos/effect/packages/platform-node-shared/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/clickhouse/CHANGELOG.md +681 -0
- package/repos/effect/packages/sql/clickhouse/LICENSE +21 -0
- package/repos/effect/packages/sql/clickhouse/README.md +7 -0
- package/repos/effect/packages/sql/clickhouse/docgen.json +24 -0
- package/repos/effect/packages/sql/clickhouse/package.json +72 -0
- package/repos/effect/packages/sql/clickhouse/src/ClickhouseClient.ts +546 -0
- package/repos/effect/packages/sql/clickhouse/src/ClickhouseMigrator.ts +51 -0
- package/repos/effect/packages/sql/clickhouse/src/index.ts +15 -0
- package/repos/effect/packages/sql/clickhouse/test/Client.test.ts +6 -0
- package/repos/effect/packages/sql/clickhouse/test/SqlErrorClassification.test.ts +75 -0
- package/repos/effect/packages/sql/clickhouse/tsconfig.json +12 -0
- package/repos/effect/packages/sql/clickhouse/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/d1/CHANGELOG.md +597 -0
- package/repos/effect/packages/sql/d1/LICENSE +21 -0
- package/repos/effect/packages/sql/d1/README.md +7 -0
- package/repos/effect/packages/sql/d1/docgen.json +24 -0
- package/repos/effect/packages/sql/d1/package.json +75 -0
- package/repos/effect/packages/sql/d1/src/D1Client.ts +249 -0
- package/repos/effect/packages/sql/d1/src/index.ts +10 -0
- package/repos/effect/packages/sql/d1/test/Client.test.ts +65 -0
- package/repos/effect/packages/sql/d1/test/Resolver.test.ts +176 -0
- package/repos/effect/packages/sql/d1/test/utils.ts +38 -0
- package/repos/effect/packages/sql/d1/tsconfig.json +8 -0
- package/repos/effect/packages/sql/d1/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/libsql/CHANGELOG.md +601 -0
- package/repos/effect/packages/sql/libsql/LICENSE +21 -0
- package/repos/effect/packages/sql/libsql/README.md +7 -0
- package/repos/effect/packages/sql/libsql/docgen.json +24 -0
- package/repos/effect/packages/sql/libsql/package.json +73 -0
- package/repos/effect/packages/sql/libsql/src/LibsqlClient.ts +381 -0
- package/repos/effect/packages/sql/libsql/src/LibsqlMigrator.ts +46 -0
- package/repos/effect/packages/sql/libsql/src/index.ts +15 -0
- package/repos/effect/packages/sql/libsql/test/Client.test.ts +106 -0
- package/repos/effect/packages/sql/libsql/test/Resolver.test.ts +177 -0
- package/repos/effect/packages/sql/libsql/test/util.ts +54 -0
- package/repos/effect/packages/sql/libsql/tsconfig.json +9 -0
- package/repos/effect/packages/sql/libsql/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/mssql/CHANGELOG.md +601 -0
- package/repos/effect/packages/sql/mssql/LICENSE +21 -0
- package/repos/effect/packages/sql/mssql/README.md +7 -0
- package/repos/effect/packages/sql/mssql/docgen.json +24 -0
- package/repos/effect/packages/sql/mssql/package.json +70 -0
- package/repos/effect/packages/sql/mssql/src/MssqlClient.ts +732 -0
- package/repos/effect/packages/sql/mssql/src/MssqlMigrator.ts +45 -0
- package/repos/effect/packages/sql/mssql/src/Parameter.ts +64 -0
- package/repos/effect/packages/sql/mssql/src/Procedure.ts +213 -0
- package/repos/effect/packages/sql/mssql/src/index.ts +32 -0
- package/repos/effect/packages/sql/mssql/test/Client.test.ts +91 -0
- package/repos/effect/packages/sql/mssql/test/SqlErrorClassification.test.ts +206 -0
- package/repos/effect/packages/sql/mssql/tsconfig.json +8 -0
- package/repos/effect/packages/sql/mssql/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/mysql2/CHANGELOG.md +603 -0
- package/repos/effect/packages/sql/mysql2/LICENSE +21 -0
- package/repos/effect/packages/sql/mysql2/README.md +7 -0
- package/repos/effect/packages/sql/mysql2/docgen.json +24 -0
- package/repos/effect/packages/sql/mysql2/package.json +71 -0
- package/repos/effect/packages/sql/mysql2/src/MysqlClient.ts +517 -0
- package/repos/effect/packages/sql/mysql2/src/MysqlMigrator.ts +102 -0
- package/repos/effect/packages/sql/mysql2/src/index.ts +15 -0
- package/repos/effect/packages/sql/mysql2/test/Client.test.ts +34 -0
- package/repos/effect/packages/sql/mysql2/test/KeyValueStore.test.ts +16 -0
- package/repos/effect/packages/sql/mysql2/test/Model.test.ts +227 -0
- package/repos/effect/packages/sql/mysql2/test/MysqlClient.test.ts +35 -0
- package/repos/effect/packages/sql/mysql2/test/Persistence.test.ts +22 -0
- package/repos/effect/packages/sql/mysql2/test/SqlErrorClassification.test.ts +133 -0
- package/repos/effect/packages/sql/mysql2/test/SqlEventLogServerUnencrypted.test.ts +7 -0
- package/repos/effect/packages/sql/mysql2/test/utils.ts +64 -0
- package/repos/effect/packages/sql/mysql2/tsconfig.json +9 -0
- package/repos/effect/packages/sql/mysql2/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/pg/CHANGELOG.md +3432 -0
- package/repos/effect/packages/sql/pg/LICENSE +21 -0
- package/repos/effect/packages/sql/pg/README.md +7 -0
- package/repos/effect/packages/sql/pg/docgen.json +24 -0
- package/repos/effect/packages/sql/pg/package.json +77 -0
- package/repos/effect/packages/sql/pg/src/PgClient.ts +947 -0
- package/repos/effect/packages/sql/pg/src/PgMigrator.ts +120 -0
- package/repos/effect/packages/sql/pg/src/index.ts +15 -0
- package/repos/effect/packages/sql/pg/test/Client.test.ts +373 -0
- package/repos/effect/packages/sql/pg/test/KeyValueStore.test.ts +16 -0
- package/repos/effect/packages/sql/pg/test/Persistence.test.ts +20 -0
- package/repos/effect/packages/sql/pg/test/SqlErrorClassification.test.ts +81 -0
- package/repos/effect/packages/sql/pg/test/SqlEventLogServerUnencrypted.test.ts +7 -0
- package/repos/effect/packages/sql/pg/test/TransactionAcquire.test.ts +52 -0
- package/repos/effect/packages/sql/pg/test/utils.ts +49 -0
- package/repos/effect/packages/sql/pg/tsconfig.json +9 -0
- package/repos/effect/packages/sql/pg/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/pglite/CHANGELOG.md +198 -0
- package/repos/effect/packages/sql/pglite/LICENSE +21 -0
- package/repos/effect/packages/sql/pglite/README.md +7 -0
- package/repos/effect/packages/sql/pglite/docgen.json +24 -0
- package/repos/effect/packages/sql/pglite/package.json +74 -0
- package/repos/effect/packages/sql/pglite/src/PgliteClient.ts +503 -0
- package/repos/effect/packages/sql/pglite/src/PgliteMigrator.ts +44 -0
- package/repos/effect/packages/sql/pglite/src/index.ts +15 -0
- package/repos/effect/packages/sql/pglite/test/Client.test.ts +149 -0
- package/repos/effect/packages/sql/pglite/test/Migrator.test.ts +45 -0
- package/repos/effect/packages/sql/pglite/test/SqlErrorClassification.test.ts +56 -0
- package/repos/effect/packages/sql/pglite/test/Transaction.test.ts +62 -0
- package/repos/effect/packages/sql/pglite/tsconfig.json +8 -0
- package/repos/effect/packages/sql/pglite/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/sqlite-bun/CHANGELOG.md +601 -0
- package/repos/effect/packages/sql/sqlite-bun/LICENSE +21 -0
- package/repos/effect/packages/sql/sqlite-bun/README.md +7 -0
- package/repos/effect/packages/sql/sqlite-bun/docgen.json +24 -0
- package/repos/effect/packages/sql/sqlite-bun/package.json +69 -0
- package/repos/effect/packages/sql/sqlite-bun/src/SqliteClient.ts +262 -0
- package/repos/effect/packages/sql/sqlite-bun/src/SqliteMigrator.ts +90 -0
- package/repos/effect/packages/sql/sqlite-bun/src/index.ts +15 -0
- package/repos/effect/packages/sql/sqlite-bun/test/Client.test.ts +6 -0
- package/repos/effect/packages/sql/sqlite-bun/tsconfig.json +11 -0
- package/repos/effect/packages/sql/sqlite-bun/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/sqlite-do/CHANGELOG.md +601 -0
- package/repos/effect/packages/sql/sqlite-do/LICENSE +21 -0
- package/repos/effect/packages/sql/sqlite-do/README.md +7 -0
- package/repos/effect/packages/sql/sqlite-do/docgen.json +24 -0
- package/repos/effect/packages/sql/sqlite-do/package.json +68 -0
- package/repos/effect/packages/sql/sqlite-do/src/SqliteClient.ts +249 -0
- package/repos/effect/packages/sql/sqlite-do/src/SqliteMigrator.ts +45 -0
- package/repos/effect/packages/sql/sqlite-do/src/index.ts +15 -0
- package/repos/effect/packages/sql/sqlite-do/test/Client.test.ts +24 -0
- package/repos/effect/packages/sql/sqlite-do/tsconfig.json +8 -0
- package/repos/effect/packages/sql/sqlite-do/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/sqlite-node/CHANGELOG.md +3415 -0
- package/repos/effect/packages/sql/sqlite-node/LICENSE +21 -0
- package/repos/effect/packages/sql/sqlite-node/README.md +7 -0
- package/repos/effect/packages/sql/sqlite-node/docgen.json +24 -0
- package/repos/effect/packages/sql/sqlite-node/package.json +72 -0
- package/repos/effect/packages/sql/sqlite-node/src/SqliteClient.ts +312 -0
- package/repos/effect/packages/sql/sqlite-node/src/SqliteMigrator.ts +90 -0
- package/repos/effect/packages/sql/sqlite-node/src/index.ts +15 -0
- package/repos/effect/packages/sql/sqlite-node/test/Client.test.ts +75 -0
- package/repos/effect/packages/sql/sqlite-node/test/KeyValueStore.test.ts +29 -0
- package/repos/effect/packages/sql/sqlite-node/test/Persistence.test.ts +109 -0
- package/repos/effect/packages/sql/sqlite-node/test/Resolver.test.ts +183 -0
- package/repos/effect/packages/sql/sqlite-node/test/SqlEventJournal.test.ts +84 -0
- package/repos/effect/packages/sql/sqlite-node/test/SqlEventLogServerEncrypted.test.ts +249 -0
- package/repos/effect/packages/sql/sqlite-node/test/SqlEventLogServerUnencrypted.test.ts +110 -0
- package/repos/effect/packages/sql/sqlite-node/test/SqliteMigrator.test.ts +48 -0
- package/repos/effect/packages/sql/sqlite-node/tsconfig.json +8 -0
- package/repos/effect/packages/sql/sqlite-node/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/sqlite-react-native/CHANGELOG.md +601 -0
- package/repos/effect/packages/sql/sqlite-react-native/LICENSE +21 -0
- package/repos/effect/packages/sql/sqlite-react-native/README.md +7 -0
- package/repos/effect/packages/sql/sqlite-react-native/docgen.json +24 -0
- package/repos/effect/packages/sql/sqlite-react-native/package.json +69 -0
- package/repos/effect/packages/sql/sqlite-react-native/src/SqliteClient.ts +354 -0
- package/repos/effect/packages/sql/sqlite-react-native/src/SqliteMigrator.ts +55 -0
- package/repos/effect/packages/sql/sqlite-react-native/src/index.ts +15 -0
- package/repos/effect/packages/sql/sqlite-react-native/test/Client.test.ts +6 -0
- package/repos/effect/packages/sql/sqlite-react-native/tsconfig.json +8 -0
- package/repos/effect/packages/sql/sqlite-react-native/vitest.config.ts +6 -0
- package/repos/effect/packages/sql/sqlite-wasm/CHANGELOG.md +601 -0
- package/repos/effect/packages/sql/sqlite-wasm/LICENSE +21 -0
- package/repos/effect/packages/sql/sqlite-wasm/README.md +7 -0
- package/repos/effect/packages/sql/sqlite-wasm/docgen.json +24 -0
- package/repos/effect/packages/sql/sqlite-wasm/package.json +69 -0
- package/repos/effect/packages/sql/sqlite-wasm/src/OpfsWorker.ts +118 -0
- package/repos/effect/packages/sql/sqlite-wasm/src/SqliteClient.ts +551 -0
- package/repos/effect/packages/sql/sqlite-wasm/src/SqliteMigrator.ts +55 -0
- package/repos/effect/packages/sql/sqlite-wasm/src/index.ts +14 -0
- package/repos/effect/packages/sql/sqlite-wasm/src/internal/opfsWorker.ts +7 -0
- package/repos/effect/packages/sql/sqlite-wasm/src/sqlite-wasm.d.ts +47 -0
- package/repos/effect/packages/sql/sqlite-wasm/test/Client.test.ts +6 -0
- package/repos/effect/packages/sql/sqlite-wasm/tsconfig.json +8 -0
- package/repos/effect/packages/sql/sqlite-wasm/vitest.config.ts +6 -0
- package/repos/effect/packages/tools/ai-codegen/codegen.schema.json +92 -0
- package/repos/effect/packages/tools/ai-codegen/docgen.json +4 -0
- package/repos/effect/packages/tools/ai-codegen/package.json +61 -0
- package/repos/effect/packages/tools/ai-codegen/src/Config.ts +292 -0
- package/repos/effect/packages/tools/ai-codegen/src/Discovery.ts +197 -0
- package/repos/effect/packages/tools/ai-codegen/src/Generator.ts +183 -0
- package/repos/effect/packages/tools/ai-codegen/src/Glob.ts +56 -0
- package/repos/effect/packages/tools/ai-codegen/src/PostProcess.ts +157 -0
- package/repos/effect/packages/tools/ai-codegen/src/SpecFetcher.ts +154 -0
- package/repos/effect/packages/tools/ai-codegen/src/bin.ts +10 -0
- package/repos/effect/packages/tools/ai-codegen/src/main.ts +237 -0
- package/repos/effect/packages/tools/ai-codegen/tsconfig.json +15 -0
- package/repos/effect/packages/tools/ai-docgen/docgen.json +4 -0
- package/repos/effect/packages/tools/ai-docgen/package.json +60 -0
- package/repos/effect/packages/tools/ai-docgen/src/Glob.ts +48 -0
- package/repos/effect/packages/tools/ai-docgen/src/main.ts +186 -0
- package/repos/effect/packages/tools/ai-docgen/tsconfig.json +14 -0
- package/repos/effect/packages/tools/bundle/docgen.json +4 -0
- package/repos/effect/packages/tools/bundle/fixtures/basic.ts +3 -0
- package/repos/effect/packages/tools/bundle/fixtures/batching.ts +25 -0
- package/repos/effect/packages/tools/bundle/fixtures/brand.ts +5 -0
- package/repos/effect/packages/tools/bundle/fixtures/cache.ts +10 -0
- package/repos/effect/packages/tools/bundle/fixtures/config.ts +13 -0
- package/repos/effect/packages/tools/bundle/fixtures/differ.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/http-client.ts +12 -0
- package/repos/effect/packages/tools/bundle/fixtures/logger.ts +5 -0
- package/repos/effect/packages/tools/bundle/fixtures/metric.ts +13 -0
- package/repos/effect/packages/tools/bundle/fixtures/optic.ts +6 -0
- package/repos/effect/packages/tools/bundle/fixtures/pubsub.ts +22 -0
- package/repos/effect/packages/tools/bundle/fixtures/queue.ts +18 -0
- package/repos/effect/packages/tools/bundle/fixtures/schedule.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-fromJsonSchemaDocument.ts +16 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-representation-roundtrip.ts +16 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-string-transformation.ts +20 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-string.ts +8 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-template-literal.ts +8 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toArbitraryLazy.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toCodeDocument.ts +12 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toCodecJson.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toEquivalence.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toFormatter.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toJsonSchemaDocument.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema-toRepresentation.ts +9 -0
- package/repos/effect/packages/tools/bundle/fixtures/schema.ts +12 -0
- package/repos/effect/packages/tools/bundle/fixtures/stm.ts +21 -0
- package/repos/effect/packages/tools/bundle/fixtures/stream.ts +7 -0
- package/repos/effect/packages/tools/bundle/package.json +52 -0
- package/repos/effect/packages/tools/bundle/src/Cli.ts +104 -0
- package/repos/effect/packages/tools/bundle/src/Fixtures.ts +51 -0
- package/repos/effect/packages/tools/bundle/src/Plugins.ts +131 -0
- package/repos/effect/packages/tools/bundle/src/Reporter.ts +200 -0
- package/repos/effect/packages/tools/bundle/src/Rollup.ts +179 -0
- package/repos/effect/packages/tools/bundle/src/bin.ts +23 -0
- package/repos/effect/packages/tools/bundle/tsconfig.fixtures.json +13 -0
- package/repos/effect/packages/tools/bundle/tsconfig.json +9 -0
- package/repos/effect/packages/tools/bundle/tsconfig.src.json +13 -0
- package/repos/effect/packages/tools/jsdocs/docgen.json +4 -0
- package/repos/effect/packages/tools/jsdocs/package.json +64 -0
- package/repos/effect/packages/tools/jsdocs/src/Jsdocs.ts +3303 -0
- package/repos/effect/packages/tools/jsdocs/src/bin.ts +23 -0
- package/repos/effect/packages/tools/jsdocs/test/jsdocs.test.ts +979 -0
- package/repos/effect/packages/tools/jsdocs/tsconfig.json +8 -0
- package/repos/effect/packages/tools/jsdocs/vitest.config.ts +6 -0
- package/repos/effect/packages/tools/openapi-generator/CHANGELOG.md +691 -0
- package/repos/effect/packages/tools/openapi-generator/LICENSE +21 -0
- package/repos/effect/packages/tools/openapi-generator/docgen.json +4 -0
- package/repos/effect/packages/tools/openapi-generator/package.json +78 -0
- package/repos/effect/packages/tools/openapi-generator/src/HttpApiTransformer.ts +552 -0
- package/repos/effect/packages/tools/openapi-generator/src/JsonSchemaGenerator.ts +319 -0
- package/repos/effect/packages/tools/openapi-generator/src/OpenApiGenerator.ts +1201 -0
- package/repos/effect/packages/tools/openapi-generator/src/OpenApiPatch.ts +539 -0
- package/repos/effect/packages/tools/openapi-generator/src/OpenApiTransformer.ts +1019 -0
- package/repos/effect/packages/tools/openapi-generator/src/ParsedOperation.ts +277 -0
- package/repos/effect/packages/tools/openapi-generator/src/Utils.ts +111 -0
- package/repos/effect/packages/tools/openapi-generator/src/bin.ts +10 -0
- package/repos/effect/packages/tools/openapi-generator/src/main.ts +116 -0
- package/repos/effect/packages/tools/openapi-generator/test/JsonSchemaGenerator.test.ts +173 -0
- package/repos/effect/packages/tools/openapi-generator/test/OpenApiGenerator.test.ts +2130 -0
- package/repos/effect/packages/tools/openapi-generator/test/OpenApiGeneratorCli.test.ts +147 -0
- package/repos/effect/packages/tools/openapi-generator/test/OpenApiPatch.test.ts +380 -0
- package/repos/effect/packages/tools/openapi-generator/test/Utils.test.ts +33 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/cli-basic-spec.json +35 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/cli-warning-spec.json +57 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/invalid-op.json +7 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/missing-path.json +6 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/missing-value.json +6 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/valid-add.json +8 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/valid-multiple.json +17 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/valid-patch.yaml +6 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/valid-remove.json +6 -0
- package/repos/effect/packages/tools/openapi-generator/test/fixtures/patches/valid-replace.json +7 -0
- package/repos/effect/packages/tools/openapi-generator/tsconfig.json +9 -0
- package/repos/effect/packages/tools/openapi-generator/vitest.config.ts +6 -0
- package/repos/effect/packages/tools/oxc/docgen.json +4 -0
- package/repos/effect/packages/tools/oxc/oxlintrc.json +83 -0
- package/repos/effect/packages/tools/oxc/package.json +72 -0
- package/repos/effect/packages/tools/oxc/src/oxlint/index.ts +18 -0
- package/repos/effect/packages/tools/oxc/src/oxlint/rules/jsdocs.ts +108 -0
- package/repos/effect/packages/tools/oxc/src/oxlint/rules/no-bigint-literals.ts +25 -0
- package/repos/effect/packages/tools/oxc/src/oxlint/rules/no-import-from-barrel-package.ts +144 -0
- package/repos/effect/packages/tools/oxc/src/oxlint/rules/no-js-extension-imports.ts +73 -0
- package/repos/effect/packages/tools/oxc/src/oxlint/rules/no-opaque-instance-fields.ts +92 -0
- package/repos/effect/packages/tools/oxc/test/jsdocs.test.ts +75 -0
- package/repos/effect/packages/tools/oxc/test/no-import-from-barrel-package.test.ts +182 -0
- package/repos/effect/packages/tools/oxc/test/no-js-extension-imports.test.ts +120 -0
- package/repos/effect/packages/tools/oxc/test/no-opaque-instance-fields.test.ts +276 -0
- package/repos/effect/packages/tools/oxc/test/utils.ts +61 -0
- package/repos/effect/packages/tools/oxc/tsconfig.json +8 -0
- package/repos/effect/packages/tools/oxc/vitest.config.ts +6 -0
- package/repos/effect/packages/tools/utils/docgen.json +4 -0
- package/repos/effect/packages/tools/utils/package.json +59 -0
- package/repos/effect/packages/tools/utils/src/Codegen.ts +214 -0
- package/repos/effect/packages/tools/utils/src/Glob.ts +61 -0
- package/repos/effect/packages/tools/utils/src/bin.ts +16 -0
- package/repos/effect/packages/tools/utils/src/commands/codegen.ts +44 -0
- package/repos/effect/packages/tools/utils/tsconfig.json +9 -0
- package/repos/effect/packages/vitest/CHANGELOG.md +601 -0
- package/repos/effect/packages/vitest/LICENSE +21 -0
- package/repos/effect/packages/vitest/README.md +320 -0
- package/repos/effect/packages/vitest/docgen.json +24 -0
- package/repos/effect/packages/vitest/package.json +60 -0
- package/repos/effect/packages/vitest/src/index.ts +259 -0
- package/repos/effect/packages/vitest/src/internal/internal.ts +374 -0
- package/repos/effect/packages/vitest/src/utils.ts +321 -0
- package/repos/effect/packages/vitest/test/index.test.ts +222 -0
- package/repos/effect/packages/vitest/test/isolation.test.ts +105 -0
- package/repos/effect/packages/vitest/test/nested-isolation.test.ts +212 -0
- package/repos/effect/packages/vitest/tsconfig.json +11 -0
- package/repos/effect/packages/vitest/typetest/index.tst.ts +56 -0
- package/repos/effect/packages/vitest/vitest.config.ts +6 -0
- package/repos/effect/patches/@changesets__assemble-release-plan.patch +26 -0
- package/repos/effect/patches/@changesets__get-github-info.patch +44 -0
- package/repos/effect/pnpm-lock.yaml +13483 -0
- package/repos/effect/pnpm-workspace.yaml +10 -0
- package/repos/effect/scripts/bundle-compare.sh +42 -0
- package/repos/effect/scripts/check-cookbook-schedule.ts +390 -0
- package/repos/effect/scripts/circular.mjs +26 -0
- package/repos/effect/scripts/clean.mjs +19 -0
- package/repos/effect/scripts/codemod.mjs +22 -0
- package/repos/effect/scripts/codemods/jsdoc.ts +79 -0
- package/repos/effect/scripts/docs.mjs +64 -0
- package/repos/effect/scripts/package-scalar.mjs +14 -0
- package/repos/effect/scripts/package-swagger.mjs +23 -0
- package/repos/effect/scripts/package.json +22 -0
- package/repos/effect/scripts/tsconfig.json +18 -0
- package/repos/effect/scripts/version.mjs +9 -0
- package/repos/effect/scripts/version.template.txt +2 -0
- package/repos/effect/scripts/worktree-setup.sh +27 -0
- package/repos/effect/stats.txt +30 -0
- package/repos/effect/tsconfig.base.json +46 -0
- package/repos/effect/tsconfig.json +128 -0
- package/repos/effect/tsconfig.packages.json +39 -0
- package/repos/effect/tstyche.json +7 -0
- package/repos/effect/vitest.config.ts +31 -0
- package/repos/effect/vitest.setup.ts +4 -0
- package/repos/effect/vitest.shared.ts +61 -0
- package/repos/effect.subtree.json +16 -0
|
@@ -0,0 +1,4892 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Models relationships between indexed nodes and edges.
|
|
3
|
+
*
|
|
4
|
+
* This module provides immutable and scoped-mutable graph data structures. A
|
|
5
|
+
* graph can be directed or undirected, and it can store user-defined data on
|
|
6
|
+
* both nodes and edges. The module includes traversal, analysis, path-finding,
|
|
7
|
+
* transformation, and diagram export utilities.
|
|
8
|
+
*
|
|
9
|
+
* @since 4.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as Data from "./Data.ts"
|
|
13
|
+
import * as Equal from "./Equal.ts"
|
|
14
|
+
import { dual } from "./Function.ts"
|
|
15
|
+
import * as Hash from "./Hash.ts"
|
|
16
|
+
import type { Inspectable } from "./Inspectable.ts"
|
|
17
|
+
import { NodeInspectSymbol } from "./Inspectable.ts"
|
|
18
|
+
import * as Option from "./Option.ts"
|
|
19
|
+
import type { Pipeable } from "./Pipeable.ts"
|
|
20
|
+
import { pipeArguments } from "./Pipeable.ts"
|
|
21
|
+
import { hasProperty } from "./Predicate.ts"
|
|
22
|
+
import type { Mutable } from "./Types.ts"
|
|
23
|
+
|
|
24
|
+
const TypeId = "~effect/collections/Graph"
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Node index for node identification using plain numbers.
|
|
28
|
+
*
|
|
29
|
+
* **When to use**
|
|
30
|
+
*
|
|
31
|
+
* Use when storing or passing the stable identifier of a graph node between
|
|
32
|
+
* `Graph` operations.
|
|
33
|
+
*
|
|
34
|
+
* **Details**
|
|
35
|
+
*
|
|
36
|
+
* `addNode` allocates node identifiers from the graph's next node index.
|
|
37
|
+
*
|
|
38
|
+
* **Gotchas**
|
|
39
|
+
*
|
|
40
|
+
* A `NodeIndex` is an identifier, not an array offset. Removed node identifiers
|
|
41
|
+
* are not reused.
|
|
42
|
+
*
|
|
43
|
+
* @see {@link EdgeIndex} for edge identifiers instead of node identifiers
|
|
44
|
+
* @see {@link addNode} for creating node identifiers
|
|
45
|
+
*
|
|
46
|
+
* @category models
|
|
47
|
+
* @since 3.18.0
|
|
48
|
+
*/
|
|
49
|
+
export type NodeIndex = number
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Edge index for edge identification using plain numbers.
|
|
53
|
+
*
|
|
54
|
+
* **When to use**
|
|
55
|
+
*
|
|
56
|
+
* Use when you need to keep the identifier for a graph edge so you can later
|
|
57
|
+
* read, update, remove, or compare that edge.
|
|
58
|
+
*
|
|
59
|
+
* **Gotchas**
|
|
60
|
+
*
|
|
61
|
+
* An `EdgeIndex` is an identifier, not an array offset. Removed edge
|
|
62
|
+
* identifiers are not reused.
|
|
63
|
+
*
|
|
64
|
+
* @see {@link NodeIndex} for node identifiers instead of edge identifiers
|
|
65
|
+
* @see {@link Edge} for the edge value addressed by this identifier
|
|
66
|
+
* @see {@link addEdge} for creating edge identifiers
|
|
67
|
+
* @see {@link getEdge} for reading edges by identifier
|
|
68
|
+
*
|
|
69
|
+
* @category models
|
|
70
|
+
* @since 3.18.0
|
|
71
|
+
*/
|
|
72
|
+
export type EdgeIndex = number
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Represents edge data containing source, target, and user data.
|
|
76
|
+
*
|
|
77
|
+
* **When to use**
|
|
78
|
+
*
|
|
79
|
+
* Use as the graph edge value that carries source node, target node, and stored
|
|
80
|
+
* edge data together.
|
|
81
|
+
*
|
|
82
|
+
* @see {@link getEdge} for reading a single edge by identifier
|
|
83
|
+
* @see {@link addEdge} for adding edges to a graph
|
|
84
|
+
* @see {@link edges} for iterating graph edges
|
|
85
|
+
*
|
|
86
|
+
* @category models
|
|
87
|
+
* @since 3.18.0
|
|
88
|
+
*/
|
|
89
|
+
export class Edge<E> extends Data.Class<{
|
|
90
|
+
readonly source: NodeIndex
|
|
91
|
+
readonly target: NodeIndex
|
|
92
|
+
readonly data: E
|
|
93
|
+
}> {}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Graph type for distinguishing directed and undirected graphs.
|
|
97
|
+
*
|
|
98
|
+
* **When to use**
|
|
99
|
+
*
|
|
100
|
+
* Use when writing graph-polymorphic types or helpers that need to preserve
|
|
101
|
+
* whether a graph is directed or undirected.
|
|
102
|
+
*
|
|
103
|
+
* @see {@link Graph} for immutable graphs parameterized by kind
|
|
104
|
+
* @see {@link MutableGraph} for mutable graphs parameterized by kind
|
|
105
|
+
*
|
|
106
|
+
* @category models
|
|
107
|
+
* @since 3.18.0
|
|
108
|
+
*/
|
|
109
|
+
export type Kind = "directed" | "undirected"
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Common structural interface shared by immutable and mutable graphs.
|
|
113
|
+
*
|
|
114
|
+
* **Details**
|
|
115
|
+
*
|
|
116
|
+
* Contains the node and edge maps, adjacency indexes, allocation counters, and
|
|
117
|
+
* shared protocols used by both `Graph` and `MutableGraph`.
|
|
118
|
+
*
|
|
119
|
+
* @category models
|
|
120
|
+
* @since 3.18.0
|
|
121
|
+
*/
|
|
122
|
+
export interface Proto<out N, out E> extends Iterable<readonly [NodeIndex, N]>, Equal.Equal, Pipeable, Inspectable {
|
|
123
|
+
readonly [TypeId]: typeof TypeId
|
|
124
|
+
readonly nodes: Map<NodeIndex, N>
|
|
125
|
+
readonly edges: Map<EdgeIndex, Edge<E>>
|
|
126
|
+
readonly adjacency: Map<NodeIndex, Array<EdgeIndex>>
|
|
127
|
+
readonly reverseAdjacency: Map<NodeIndex, Array<EdgeIndex>>
|
|
128
|
+
nextNodeIndex: NodeIndex
|
|
129
|
+
nextEdgeIndex: EdgeIndex
|
|
130
|
+
acyclic: Option.Option<boolean>
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Immutable graph interface.
|
|
135
|
+
*
|
|
136
|
+
* **When to use**
|
|
137
|
+
*
|
|
138
|
+
* Use as the immutable graph model for code that queries, traverses,
|
|
139
|
+
* transforms, or analyzes graph structure without mutating it.
|
|
140
|
+
*
|
|
141
|
+
* @see {@link MutableGraph} for the mutable counterpart used inside mutation scopes
|
|
142
|
+
* @see {@link DirectedGraph} for a `Graph` fixed to directed edges
|
|
143
|
+
* @see {@link UndirectedGraph} for a `Graph` fixed to undirected edges
|
|
144
|
+
*
|
|
145
|
+
* @category models
|
|
146
|
+
* @since 3.18.0
|
|
147
|
+
*/
|
|
148
|
+
export interface Graph<out N, out E, T extends Kind = "directed"> extends Proto<N, E> {
|
|
149
|
+
readonly type: T
|
|
150
|
+
readonly mutable: false
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Mutable graph interface.
|
|
155
|
+
*
|
|
156
|
+
* **When to use**
|
|
157
|
+
*
|
|
158
|
+
* Use when adding, removing, or updating nodes and edges inside a graph
|
|
159
|
+
* mutation scope.
|
|
160
|
+
*
|
|
161
|
+
* @see {@link Graph} for the immutable graph interface
|
|
162
|
+
* @see {@link mutate} for scoped mutation of an immutable graph
|
|
163
|
+
* @see {@link beginMutation} for opening a mutable graph manually
|
|
164
|
+
* @see {@link endMutation} for returning to an immutable graph
|
|
165
|
+
*
|
|
166
|
+
* @category models
|
|
167
|
+
* @since 3.18.0
|
|
168
|
+
*/
|
|
169
|
+
export interface MutableGraph<out N, out E, T extends Kind = "directed"> extends Proto<N, E> {
|
|
170
|
+
readonly type: T
|
|
171
|
+
readonly mutable: true
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Immutable graph type for source-to-target relationships.
|
|
176
|
+
*
|
|
177
|
+
* **When to use**
|
|
178
|
+
*
|
|
179
|
+
* Use as the immutable graph type when edge direction is part of the model and
|
|
180
|
+
* traversal or neighbor queries should follow source-to-target edges.
|
|
181
|
+
*
|
|
182
|
+
* **Details**
|
|
183
|
+
*
|
|
184
|
+
* `DirectedGraph<N, E>` is a `Graph<N, E, "directed">` with node data of type
|
|
185
|
+
* `N` and edge data of type `E`.
|
|
186
|
+
*
|
|
187
|
+
* @see {@link directed} for constructing directed graphs
|
|
188
|
+
* @see {@link Graph} for the generic immutable graph type
|
|
189
|
+
* @see {@link UndirectedGraph} for graphs whose edges connect both endpoints
|
|
190
|
+
* @see {@link MutableDirectedGraph} for the mutable directed graph type
|
|
191
|
+
*
|
|
192
|
+
* @category models
|
|
193
|
+
* @since 3.18.0
|
|
194
|
+
*/
|
|
195
|
+
export type DirectedGraph<N, E> = Graph<N, E, "directed">
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Immutable graph type for relationships without source-to-target direction.
|
|
199
|
+
*
|
|
200
|
+
* **When to use**
|
|
201
|
+
*
|
|
202
|
+
* Use when modeling relationships where each edge connects both endpoints
|
|
203
|
+
* without a source-to-target direction.
|
|
204
|
+
*
|
|
205
|
+
* **Details**
|
|
206
|
+
*
|
|
207
|
+
* `UndirectedGraph<N, E>` is a `Graph<N, E, "undirected">`.
|
|
208
|
+
*
|
|
209
|
+
* @see {@link undirected} for constructing undirected graphs
|
|
210
|
+
* @see {@link DirectedGraph} for graphs whose edges have source-to-target direction
|
|
211
|
+
* @see {@link MutableUndirectedGraph} for the mutable undirected graph type
|
|
212
|
+
*
|
|
213
|
+
* @category models
|
|
214
|
+
* @since 3.18.0
|
|
215
|
+
*/
|
|
216
|
+
export type UndirectedGraph<N, E> = Graph<N, E, "undirected">
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Mutable directed graph type alias.
|
|
220
|
+
*
|
|
221
|
+
* **When to use**
|
|
222
|
+
*
|
|
223
|
+
* Use when annotating a temporary graph value that can be changed in place and
|
|
224
|
+
* whose edges have source-to-target direction.
|
|
225
|
+
*
|
|
226
|
+
* @see {@link MutableGraph} for the generic mutable graph type
|
|
227
|
+
* @see {@link DirectedGraph} for the immutable directed graph type
|
|
228
|
+
* @see {@link MutableUndirectedGraph} for mutable graphs without edge direction
|
|
229
|
+
*
|
|
230
|
+
* @category models
|
|
231
|
+
* @since 3.18.0
|
|
232
|
+
*/
|
|
233
|
+
export type MutableDirectedGraph<N, E> = MutableGraph<N, E, "directed">
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Mutable undirected graph type alias.
|
|
237
|
+
*
|
|
238
|
+
* **When to use**
|
|
239
|
+
*
|
|
240
|
+
* Use when annotating a temporary graph value that can be changed in place and
|
|
241
|
+
* whose edges connect both endpoints without direction.
|
|
242
|
+
*
|
|
243
|
+
* @see {@link MutableDirectedGraph} for mutable graphs with directed edges
|
|
244
|
+
* @see {@link UndirectedGraph} for the immutable undirected graph type
|
|
245
|
+
* @see {@link MutableGraph} for the generic mutable graph type
|
|
246
|
+
*
|
|
247
|
+
* @category models
|
|
248
|
+
* @since 3.18.0
|
|
249
|
+
*/
|
|
250
|
+
export type MutableUndirectedGraph<N, E> = MutableGraph<N, E, "undirected">
|
|
251
|
+
|
|
252
|
+
// =============================================================================
|
|
253
|
+
// Proto Objects
|
|
254
|
+
// =============================================================================
|
|
255
|
+
|
|
256
|
+
/** @internal */
|
|
257
|
+
const ProtoGraph = {
|
|
258
|
+
[TypeId]: TypeId,
|
|
259
|
+
[Symbol.iterator](this: Graph<any, any>) {
|
|
260
|
+
return this.nodes[Symbol.iterator]()
|
|
261
|
+
},
|
|
262
|
+
[NodeInspectSymbol](this: Graph<any, any>) {
|
|
263
|
+
return this.toJSON()
|
|
264
|
+
},
|
|
265
|
+
[Equal.symbol](this: Graph<any, any>, that: Equal.Equal): boolean {
|
|
266
|
+
if (isGraph(that)) {
|
|
267
|
+
if (
|
|
268
|
+
this.nodes.size !== that.nodes.size ||
|
|
269
|
+
this.edges.size !== that.edges.size ||
|
|
270
|
+
this.type !== that.type
|
|
271
|
+
) {
|
|
272
|
+
return false
|
|
273
|
+
}
|
|
274
|
+
// Compare nodes
|
|
275
|
+
for (const [nodeIndex, nodeData] of this.nodes) {
|
|
276
|
+
if (!that.nodes.has(nodeIndex)) {
|
|
277
|
+
return false
|
|
278
|
+
}
|
|
279
|
+
const otherNodeData = that.nodes.get(nodeIndex)!
|
|
280
|
+
if (!Equal.equals(nodeData, otherNodeData)) {
|
|
281
|
+
return false
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Compare edges
|
|
285
|
+
for (const [edgeIndex, edgeData] of this.edges) {
|
|
286
|
+
if (!that.edges.has(edgeIndex)) {
|
|
287
|
+
return false
|
|
288
|
+
}
|
|
289
|
+
const otherEdge = that.edges.get(edgeIndex)!
|
|
290
|
+
if (!Equal.equals(edgeData, otherEdge)) {
|
|
291
|
+
return false
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return true
|
|
295
|
+
}
|
|
296
|
+
return false
|
|
297
|
+
},
|
|
298
|
+
[Hash.symbol](this: Graph<any, any>): number {
|
|
299
|
+
let hash = Hash.string("Graph")
|
|
300
|
+
hash = hash ^ Hash.string(this.type)
|
|
301
|
+
hash = hash ^ Hash.number(this.nodes.size)
|
|
302
|
+
hash = hash ^ Hash.number(this.edges.size)
|
|
303
|
+
for (const [nodeIndex, nodeData] of this.nodes) {
|
|
304
|
+
hash = hash ^ (Hash.hash(nodeIndex) + Hash.hash(nodeData))
|
|
305
|
+
}
|
|
306
|
+
for (const [edgeIndex, edgeData] of this.edges) {
|
|
307
|
+
hash = hash ^ (Hash.hash(edgeIndex) + Hash.hash(edgeData))
|
|
308
|
+
}
|
|
309
|
+
return hash
|
|
310
|
+
},
|
|
311
|
+
toJSON(this: Graph<any, any>) {
|
|
312
|
+
return {
|
|
313
|
+
_id: "Graph",
|
|
314
|
+
nodeCount: this.nodes.size,
|
|
315
|
+
edgeCount: this.edges.size,
|
|
316
|
+
type: this.type
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
toString(this: Graph<any, any>) {
|
|
320
|
+
return `Graph(${this.type}, ${this.nodes.size}, ${this.edges.size})`
|
|
321
|
+
},
|
|
322
|
+
pipe() {
|
|
323
|
+
return pipeArguments(this, arguments)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// =============================================================================
|
|
328
|
+
// Errors
|
|
329
|
+
// =============================================================================
|
|
330
|
+
|
|
331
|
+
// TODO: Do we need safe variants for these?
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Error thrown by graph operations when the requested graph structure is
|
|
335
|
+
* invalid, such as referencing a missing node or using unsupported edge
|
|
336
|
+
* weights.
|
|
337
|
+
*
|
|
338
|
+
* **When to use**
|
|
339
|
+
*
|
|
340
|
+
* Use when handling failures thrown by graph operations that reject invalid
|
|
341
|
+
* graph structure or unsupported algorithm inputs.
|
|
342
|
+
*
|
|
343
|
+
* @category errors
|
|
344
|
+
* @since 3.18.0
|
|
345
|
+
*/
|
|
346
|
+
export class GraphError extends Data.TaggedError("GraphError")<{
|
|
347
|
+
readonly message: string
|
|
348
|
+
}> {}
|
|
349
|
+
|
|
350
|
+
/** @internal */
|
|
351
|
+
const missingNode = (node: number) => new GraphError({ message: `Node ${node} does not exist` })
|
|
352
|
+
|
|
353
|
+
// =============================================================================
|
|
354
|
+
// Constructors
|
|
355
|
+
// =============================================================================
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Returns `true` if a value has the graph runtime type identifier, narrowing
|
|
359
|
+
* it to a `Graph`.
|
|
360
|
+
*
|
|
361
|
+
* **When to use**
|
|
362
|
+
*
|
|
363
|
+
* Use to narrow an unknown value before treating it as a graph value.
|
|
364
|
+
*
|
|
365
|
+
* **Gotchas**
|
|
366
|
+
*
|
|
367
|
+
* This guard checks the shared graph runtime type identifier and does not
|
|
368
|
+
* distinguish immutable graphs from mutable graphs.
|
|
369
|
+
*
|
|
370
|
+
* @category guards
|
|
371
|
+
* @since 4.0.0
|
|
372
|
+
*/
|
|
373
|
+
export const isGraph = (u: unknown): u is Graph<unknown, unknown> => hasProperty(u, TypeId)
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Creates a directed graph, optionally with initial mutations.
|
|
377
|
+
*
|
|
378
|
+
* **Example** (Creating a directed graph)
|
|
379
|
+
*
|
|
380
|
+
* ```ts
|
|
381
|
+
* import { Graph } from "effect"
|
|
382
|
+
*
|
|
383
|
+
* // Directed graph with initial nodes and edges
|
|
384
|
+
* const graph = Graph.directed<string, string>((mutable) => {
|
|
385
|
+
* const a = Graph.addNode(mutable, "A")
|
|
386
|
+
* const b = Graph.addNode(mutable, "B")
|
|
387
|
+
* const c = Graph.addNode(mutable, "C")
|
|
388
|
+
* Graph.addEdge(mutable, a, b, "A->B")
|
|
389
|
+
* Graph.addEdge(mutable, b, c, "B->C")
|
|
390
|
+
* })
|
|
391
|
+
* ```
|
|
392
|
+
*
|
|
393
|
+
* @category constructors
|
|
394
|
+
* @since 3.18.0
|
|
395
|
+
*/
|
|
396
|
+
export const directed = <N, E>(mutate?: (mutable: MutableDirectedGraph<N, E>) => void): DirectedGraph<N, E> => {
|
|
397
|
+
const graph: Mutable<DirectedGraph<N, E>> = Object.create(ProtoGraph)
|
|
398
|
+
graph.type = "directed"
|
|
399
|
+
graph.nodes = new Map()
|
|
400
|
+
graph.edges = new Map()
|
|
401
|
+
graph.adjacency = new Map()
|
|
402
|
+
graph.reverseAdjacency = new Map()
|
|
403
|
+
graph.nextNodeIndex = 0
|
|
404
|
+
graph.nextEdgeIndex = 0
|
|
405
|
+
graph.acyclic = Option.some(true)
|
|
406
|
+
graph.mutable = false
|
|
407
|
+
|
|
408
|
+
if (mutate) {
|
|
409
|
+
const mutable = beginMutation(graph as DirectedGraph<N, E>)
|
|
410
|
+
mutate(mutable as MutableDirectedGraph<N, E>)
|
|
411
|
+
return endMutation(mutable)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return graph
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Creates an undirected graph, optionally with initial mutations.
|
|
419
|
+
*
|
|
420
|
+
* **Example** (Creating an undirected graph)
|
|
421
|
+
*
|
|
422
|
+
* ```ts
|
|
423
|
+
* import { Graph } from "effect"
|
|
424
|
+
*
|
|
425
|
+
* // Undirected graph with initial nodes and edges
|
|
426
|
+
* const graph = Graph.undirected<string, string>((mutable) => {
|
|
427
|
+
* const a = Graph.addNode(mutable, "A")
|
|
428
|
+
* const b = Graph.addNode(mutable, "B")
|
|
429
|
+
* const c = Graph.addNode(mutable, "C")
|
|
430
|
+
* Graph.addEdge(mutable, a, b, "A-B")
|
|
431
|
+
* Graph.addEdge(mutable, b, c, "B-C")
|
|
432
|
+
* })
|
|
433
|
+
* ```
|
|
434
|
+
*
|
|
435
|
+
* @category constructors
|
|
436
|
+
* @since 3.18.0
|
|
437
|
+
*/
|
|
438
|
+
export const undirected = <N, E>(mutate?: (mutable: MutableUndirectedGraph<N, E>) => void): UndirectedGraph<N, E> => {
|
|
439
|
+
const graph: Mutable<UndirectedGraph<N, E>> = Object.create(ProtoGraph)
|
|
440
|
+
graph.type = "undirected"
|
|
441
|
+
graph.nodes = new Map()
|
|
442
|
+
graph.edges = new Map()
|
|
443
|
+
graph.adjacency = new Map()
|
|
444
|
+
graph.reverseAdjacency = new Map()
|
|
445
|
+
graph.nextNodeIndex = 0
|
|
446
|
+
graph.nextEdgeIndex = 0
|
|
447
|
+
graph.acyclic = Option.some(true)
|
|
448
|
+
graph.mutable = false
|
|
449
|
+
|
|
450
|
+
if (mutate) {
|
|
451
|
+
const mutable = beginMutation(graph)
|
|
452
|
+
mutate(mutable as MutableUndirectedGraph<N, E>)
|
|
453
|
+
return endMutation(mutable)
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return graph
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// =============================================================================
|
|
460
|
+
// Scoped Mutable API
|
|
461
|
+
// =============================================================================
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Creates a mutable scope for safe graph mutations by copying the data structure.
|
|
465
|
+
*
|
|
466
|
+
* **Example** (Beginning a mutation scope)
|
|
467
|
+
*
|
|
468
|
+
* ```ts
|
|
469
|
+
* import { Graph } from "effect"
|
|
470
|
+
*
|
|
471
|
+
* const graph = Graph.directed<string, number>()
|
|
472
|
+
* const mutable = Graph.beginMutation(graph)
|
|
473
|
+
* // Now mutable can be safely modified without affecting original graph
|
|
474
|
+
* ```
|
|
475
|
+
*
|
|
476
|
+
* @category mutations
|
|
477
|
+
* @since 3.18.0
|
|
478
|
+
*/
|
|
479
|
+
export const beginMutation = <N, E, T extends Kind = "directed">(
|
|
480
|
+
graph: Graph<N, E, T>
|
|
481
|
+
): MutableGraph<N, E, T> => {
|
|
482
|
+
// Copy adjacency maps with deep cloned arrays
|
|
483
|
+
const adjacency = new Map<NodeIndex, Array<EdgeIndex>>()
|
|
484
|
+
const reverseAdjacency = new Map<NodeIndex, Array<EdgeIndex>>()
|
|
485
|
+
|
|
486
|
+
for (const [nodeIndex, edges] of graph.adjacency) {
|
|
487
|
+
adjacency.set(nodeIndex, [...edges])
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
for (const [nodeIndex, edges] of graph.reverseAdjacency) {
|
|
491
|
+
reverseAdjacency.set(nodeIndex, [...edges])
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const mutable: Mutable<MutableGraph<N, E, T>> = Object.create(ProtoGraph)
|
|
495
|
+
mutable.type = graph.type
|
|
496
|
+
mutable.nodes = new Map(graph.nodes)
|
|
497
|
+
mutable.edges = new Map(graph.edges)
|
|
498
|
+
mutable.adjacency = adjacency
|
|
499
|
+
mutable.reverseAdjacency = reverseAdjacency
|
|
500
|
+
mutable.nextNodeIndex = graph.nextNodeIndex
|
|
501
|
+
mutable.nextEdgeIndex = graph.nextEdgeIndex
|
|
502
|
+
mutable.acyclic = graph.acyclic
|
|
503
|
+
mutable.mutable = true
|
|
504
|
+
|
|
505
|
+
return mutable
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Converts a mutable graph back to an immutable graph, ending the mutation scope.
|
|
510
|
+
*
|
|
511
|
+
* **Example** (Ending a mutation scope)
|
|
512
|
+
*
|
|
513
|
+
* ```ts
|
|
514
|
+
* import { Graph } from "effect"
|
|
515
|
+
*
|
|
516
|
+
* const graph = Graph.directed<string, number>()
|
|
517
|
+
* const mutable = Graph.beginMutation(graph)
|
|
518
|
+
* // ... perform mutations on mutable ...
|
|
519
|
+
* const newGraph = Graph.endMutation(mutable)
|
|
520
|
+
* ```
|
|
521
|
+
*
|
|
522
|
+
* @category mutations
|
|
523
|
+
* @since 3.18.0
|
|
524
|
+
*/
|
|
525
|
+
export const endMutation = <N, E, T extends Kind = "directed">(
|
|
526
|
+
mutable: MutableGraph<N, E, T>
|
|
527
|
+
): Graph<N, E, T> => {
|
|
528
|
+
const graph: Mutable<Graph<N, E, T>> = Object.create(ProtoGraph)
|
|
529
|
+
graph.type = mutable.type
|
|
530
|
+
graph.nodes = new Map(mutable.nodes)
|
|
531
|
+
graph.edges = new Map(mutable.edges)
|
|
532
|
+
graph.adjacency = mutable.adjacency
|
|
533
|
+
graph.reverseAdjacency = mutable.reverseAdjacency
|
|
534
|
+
graph.nextNodeIndex = mutable.nextNodeIndex
|
|
535
|
+
graph.nextEdgeIndex = mutable.nextEdgeIndex
|
|
536
|
+
graph.acyclic = mutable.acyclic
|
|
537
|
+
graph.mutable = false
|
|
538
|
+
|
|
539
|
+
return graph
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Performs scoped mutations on a graph, automatically managing the mutation lifecycle.
|
|
544
|
+
*
|
|
545
|
+
* **Example** (Applying scoped mutations)
|
|
546
|
+
*
|
|
547
|
+
* ```ts
|
|
548
|
+
* import { Graph } from "effect"
|
|
549
|
+
*
|
|
550
|
+
* const graph = Graph.directed<string, number>()
|
|
551
|
+
* const newGraph = Graph.mutate(graph, (mutable) => {
|
|
552
|
+
* const nodeA = Graph.addNode(mutable, "A")
|
|
553
|
+
* const nodeB = Graph.addNode(mutable, "B")
|
|
554
|
+
* Graph.addEdge(mutable, nodeA, nodeB, 1)
|
|
555
|
+
* })
|
|
556
|
+
*
|
|
557
|
+
* console.log(Graph.nodeCount(newGraph)) // 2
|
|
558
|
+
* console.log(Graph.edgeCount(newGraph)) // 1
|
|
559
|
+
* ```
|
|
560
|
+
*
|
|
561
|
+
* @category mutations
|
|
562
|
+
* @since 3.18.0
|
|
563
|
+
*/
|
|
564
|
+
export const mutate: {
|
|
565
|
+
<N, E, T extends Kind = "directed">(
|
|
566
|
+
f: (mutable: MutableGraph<N, E, T>) => void
|
|
567
|
+
): (graph: Graph<N, E, T>) => Graph<N, E, T>
|
|
568
|
+
<N, E, T extends Kind = "directed">(
|
|
569
|
+
graph: Graph<N, E, T>,
|
|
570
|
+
f: (mutable: MutableGraph<N, E, T>) => void
|
|
571
|
+
): Graph<N, E, T>
|
|
572
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
573
|
+
graph: Graph<N, E, T>,
|
|
574
|
+
f: (mutable: MutableGraph<N, E, T>) => void
|
|
575
|
+
): Graph<N, E, T> => {
|
|
576
|
+
const mutable = beginMutation(graph)
|
|
577
|
+
f(mutable)
|
|
578
|
+
return endMutation(mutable)
|
|
579
|
+
})
|
|
580
|
+
|
|
581
|
+
// =============================================================================
|
|
582
|
+
// Basic Node Operations
|
|
583
|
+
// =============================================================================
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Adds a new node to a mutable graph and returns its index.
|
|
587
|
+
*
|
|
588
|
+
* **When to use**
|
|
589
|
+
*
|
|
590
|
+
* Use to allocate a new node in a mutable graph before storing edges or
|
|
591
|
+
* querying it by index.
|
|
592
|
+
*
|
|
593
|
+
* **Details**
|
|
594
|
+
*
|
|
595
|
+
* The returned index is allocated from the graph's next node index. The mutable
|
|
596
|
+
* graph stores the node data and initializes empty incoming and outgoing edge
|
|
597
|
+
* indexes for the new node.
|
|
598
|
+
*
|
|
599
|
+
* **Gotchas**
|
|
600
|
+
*
|
|
601
|
+
* `NodeIndex` values are identifiers and are not reused after removals.
|
|
602
|
+
*
|
|
603
|
+
* **Example** (Adding nodes)
|
|
604
|
+
*
|
|
605
|
+
* ```ts
|
|
606
|
+
* import { Graph } from "effect"
|
|
607
|
+
*
|
|
608
|
+
* const result = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
609
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
610
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
611
|
+
* console.log(nodeA) // NodeIndex with value 0
|
|
612
|
+
* console.log(nodeB) // NodeIndex with value 1
|
|
613
|
+
* })
|
|
614
|
+
* ```
|
|
615
|
+
*
|
|
616
|
+
* @see {@link mutate} for obtaining a mutable graph from an immutable graph
|
|
617
|
+
* @see {@link addEdge} for connecting existing nodes
|
|
618
|
+
* @see {@link removeNode} for removing nodes from a mutable graph
|
|
619
|
+
*
|
|
620
|
+
* @category mutations
|
|
621
|
+
* @since 3.18.0
|
|
622
|
+
*/
|
|
623
|
+
export const addNode = <N, E, T extends Kind = "directed">(
|
|
624
|
+
mutable: MutableGraph<N, E, T>,
|
|
625
|
+
data: N
|
|
626
|
+
): NodeIndex => {
|
|
627
|
+
const nodeIndex = mutable.nextNodeIndex
|
|
628
|
+
|
|
629
|
+
// Add node data
|
|
630
|
+
mutable.nodes.set(nodeIndex, data)
|
|
631
|
+
|
|
632
|
+
// Initialize empty adjacency lists
|
|
633
|
+
mutable.adjacency.set(nodeIndex, [])
|
|
634
|
+
mutable.reverseAdjacency.set(nodeIndex, [])
|
|
635
|
+
|
|
636
|
+
// Update graph allocators
|
|
637
|
+
mutable.nextNodeIndex = mutable.nextNodeIndex + 1
|
|
638
|
+
|
|
639
|
+
return nodeIndex
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Gets the data associated with a node index safely, if it exists.
|
|
644
|
+
*
|
|
645
|
+
* **Example** (Getting node data)
|
|
646
|
+
*
|
|
647
|
+
* ```ts
|
|
648
|
+
* import { Graph, Option } from "effect"
|
|
649
|
+
*
|
|
650
|
+
* const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
651
|
+
* Graph.addNode(mutable, "Node A")
|
|
652
|
+
* })
|
|
653
|
+
*
|
|
654
|
+
* const nodeIndex = 0
|
|
655
|
+
* const nodeData = Graph.getNode(graph, nodeIndex)
|
|
656
|
+
*
|
|
657
|
+
* if (Option.isSome(nodeData)) {
|
|
658
|
+
* console.log(nodeData.value) // "Node A"
|
|
659
|
+
* }
|
|
660
|
+
* ```
|
|
661
|
+
*
|
|
662
|
+
* @category getters
|
|
663
|
+
* @since 3.18.0
|
|
664
|
+
*/
|
|
665
|
+
export const getNode: {
|
|
666
|
+
<N, E, T extends Kind = "directed">(
|
|
667
|
+
nodeIndex: NodeIndex
|
|
668
|
+
): (graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option.Option<N>
|
|
669
|
+
<N, E, T extends Kind = "directed">(
|
|
670
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
671
|
+
nodeIndex: NodeIndex
|
|
672
|
+
): Option.Option<N>
|
|
673
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
674
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
675
|
+
nodeIndex: NodeIndex
|
|
676
|
+
): Option.Option<N> => graph.nodes.has(nodeIndex) ? Option.some(graph.nodes.get(nodeIndex)!) : Option.none())
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Checks whether a node with the given index exists in the graph.
|
|
680
|
+
*
|
|
681
|
+
* **Example** (Checking node existence)
|
|
682
|
+
*
|
|
683
|
+
* ```ts
|
|
684
|
+
* import { Graph } from "effect"
|
|
685
|
+
*
|
|
686
|
+
* const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
687
|
+
* Graph.addNode(mutable, "Node A")
|
|
688
|
+
* })
|
|
689
|
+
*
|
|
690
|
+
* const nodeIndex = 0
|
|
691
|
+
* const exists = Graph.hasNode(graph, nodeIndex)
|
|
692
|
+
* console.log(exists) // true
|
|
693
|
+
*
|
|
694
|
+
* const nonExistentIndex = 999
|
|
695
|
+
* const notExists = Graph.hasNode(graph, nonExistentIndex)
|
|
696
|
+
* console.log(notExists) // false
|
|
697
|
+
* ```
|
|
698
|
+
*
|
|
699
|
+
* @category getters
|
|
700
|
+
* @since 3.18.0
|
|
701
|
+
*/
|
|
702
|
+
export const hasNode: {
|
|
703
|
+
(nodeIndex: NodeIndex): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => boolean
|
|
704
|
+
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, nodeIndex: NodeIndex): boolean
|
|
705
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
706
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
707
|
+
nodeIndex: NodeIndex
|
|
708
|
+
): boolean => graph.nodes.has(nodeIndex))
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Returns the number of nodes in the graph.
|
|
712
|
+
*
|
|
713
|
+
* **Example** (Counting nodes)
|
|
714
|
+
*
|
|
715
|
+
* ```ts
|
|
716
|
+
* import { Graph } from "effect"
|
|
717
|
+
*
|
|
718
|
+
* const emptyGraph = Graph.directed<string, number>()
|
|
719
|
+
* console.log(Graph.nodeCount(emptyGraph)) // 0
|
|
720
|
+
*
|
|
721
|
+
* const graphWithNodes = Graph.mutate(emptyGraph, (mutable) => {
|
|
722
|
+
* Graph.addNode(mutable, "Node A")
|
|
723
|
+
* Graph.addNode(mutable, "Node B")
|
|
724
|
+
* Graph.addNode(mutable, "Node C")
|
|
725
|
+
* })
|
|
726
|
+
*
|
|
727
|
+
* console.log(Graph.nodeCount(graphWithNodes)) // 3
|
|
728
|
+
* ```
|
|
729
|
+
*
|
|
730
|
+
* @category getters
|
|
731
|
+
* @since 3.18.0
|
|
732
|
+
*/
|
|
733
|
+
export const nodeCount = <N, E, T extends Kind = "directed">(
|
|
734
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>
|
|
735
|
+
): number => graph.nodes.size
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Finds the first node that matches the given predicate.
|
|
739
|
+
*
|
|
740
|
+
* **Example** (Finding the first matching node)
|
|
741
|
+
*
|
|
742
|
+
* ```ts
|
|
743
|
+
* import { Graph } from "effect"
|
|
744
|
+
*
|
|
745
|
+
* const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
746
|
+
* Graph.addNode(mutable, "Node A")
|
|
747
|
+
* Graph.addNode(mutable, "Node B")
|
|
748
|
+
* Graph.addNode(mutable, "Node C")
|
|
749
|
+
* })
|
|
750
|
+
*
|
|
751
|
+
* const result = Graph.findNode(graph, (data) => data.startsWith("Node B"))
|
|
752
|
+
* console.log(result) // Option.some(1)
|
|
753
|
+
*
|
|
754
|
+
* const notFound = Graph.findNode(graph, (data) => data === "Node D")
|
|
755
|
+
* console.log(notFound) // Option.none()
|
|
756
|
+
* ```
|
|
757
|
+
*
|
|
758
|
+
* @category getters
|
|
759
|
+
* @since 3.18.0
|
|
760
|
+
*/
|
|
761
|
+
export const findNode: {
|
|
762
|
+
<N>(
|
|
763
|
+
predicate: (data: N) => boolean
|
|
764
|
+
): <E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option.Option<NodeIndex>
|
|
765
|
+
<N, E, T extends Kind = "directed">(
|
|
766
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
767
|
+
predicate: (data: N) => boolean
|
|
768
|
+
): Option.Option<NodeIndex>
|
|
769
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
770
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
771
|
+
predicate: (data: N) => boolean
|
|
772
|
+
): Option.Option<NodeIndex> => {
|
|
773
|
+
for (const [index, data] of graph.nodes) {
|
|
774
|
+
if (predicate(data)) {
|
|
775
|
+
return Option.some(index)
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return Option.none()
|
|
779
|
+
})
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Finds all nodes that match the given predicate.
|
|
783
|
+
*
|
|
784
|
+
* **Example** (Finding matching nodes)
|
|
785
|
+
*
|
|
786
|
+
* ```ts
|
|
787
|
+
* import { Graph } from "effect"
|
|
788
|
+
*
|
|
789
|
+
* const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
790
|
+
* Graph.addNode(mutable, "Start A")
|
|
791
|
+
* Graph.addNode(mutable, "Node B")
|
|
792
|
+
* Graph.addNode(mutable, "Start C")
|
|
793
|
+
* })
|
|
794
|
+
*
|
|
795
|
+
* const result = Graph.findNodes(graph, (data) => data.startsWith("Start"))
|
|
796
|
+
* console.log(result) // [0, 2]
|
|
797
|
+
*
|
|
798
|
+
* const empty = Graph.findNodes(graph, (data) => data === "Not Found")
|
|
799
|
+
* console.log(empty) // []
|
|
800
|
+
* ```
|
|
801
|
+
*
|
|
802
|
+
* @category getters
|
|
803
|
+
* @since 3.18.0
|
|
804
|
+
*/
|
|
805
|
+
export const findNodes: {
|
|
806
|
+
<N>(
|
|
807
|
+
predicate: (data: N) => boolean
|
|
808
|
+
): <E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Array<NodeIndex>
|
|
809
|
+
<N, E, T extends Kind = "directed">(
|
|
810
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
811
|
+
predicate: (data: N) => boolean
|
|
812
|
+
): Array<NodeIndex>
|
|
813
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
814
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
815
|
+
predicate: (data: N) => boolean
|
|
816
|
+
): Array<NodeIndex> => {
|
|
817
|
+
const results: Array<NodeIndex> = []
|
|
818
|
+
for (const [index, data] of graph.nodes) {
|
|
819
|
+
if (predicate(data)) {
|
|
820
|
+
results.push(index)
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return results
|
|
824
|
+
})
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Finds the first edge that matches the given predicate.
|
|
828
|
+
*
|
|
829
|
+
* **Example** (Finding the first matching edge)
|
|
830
|
+
*
|
|
831
|
+
* ```ts
|
|
832
|
+
* import { Graph } from "effect"
|
|
833
|
+
*
|
|
834
|
+
* const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
835
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
836
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
837
|
+
* const nodeC = Graph.addNode(mutable, "Node C")
|
|
838
|
+
* Graph.addEdge(mutable, nodeA, nodeB, 10)
|
|
839
|
+
* Graph.addEdge(mutable, nodeB, nodeC, 20)
|
|
840
|
+
* })
|
|
841
|
+
*
|
|
842
|
+
* const result = Graph.findEdge(graph, (data) => data > 15)
|
|
843
|
+
* console.log(result) // Option.some(1)
|
|
844
|
+
*
|
|
845
|
+
* const notFound = Graph.findEdge(graph, (data) => data > 100)
|
|
846
|
+
* console.log(notFound) // Option.none()
|
|
847
|
+
* ```
|
|
848
|
+
*
|
|
849
|
+
* @category getters
|
|
850
|
+
* @since 3.18.0
|
|
851
|
+
*/
|
|
852
|
+
export const findEdge: {
|
|
853
|
+
<E>(
|
|
854
|
+
predicate: (data: E, source: NodeIndex, target: NodeIndex) => boolean
|
|
855
|
+
): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option.Option<EdgeIndex>
|
|
856
|
+
<N, E, T extends Kind = "directed">(
|
|
857
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
858
|
+
predicate: (data: E, source: NodeIndex, target: NodeIndex) => boolean
|
|
859
|
+
): Option.Option<EdgeIndex>
|
|
860
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
861
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
862
|
+
predicate: (data: E, source: NodeIndex, target: NodeIndex) => boolean
|
|
863
|
+
): Option.Option<EdgeIndex> => {
|
|
864
|
+
for (const [edgeIndex, edgeData] of graph.edges) {
|
|
865
|
+
if (predicate(edgeData.data, edgeData.source, edgeData.target)) {
|
|
866
|
+
return Option.some(edgeIndex)
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
return Option.none()
|
|
870
|
+
})
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Finds all edges that match the given predicate.
|
|
874
|
+
*
|
|
875
|
+
* **Example** (Finding matching edges)
|
|
876
|
+
*
|
|
877
|
+
* ```ts
|
|
878
|
+
* import { Graph } from "effect"
|
|
879
|
+
*
|
|
880
|
+
* const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
881
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
882
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
883
|
+
* const nodeC = Graph.addNode(mutable, "Node C")
|
|
884
|
+
* Graph.addEdge(mutable, nodeA, nodeB, 10)
|
|
885
|
+
* Graph.addEdge(mutable, nodeB, nodeC, 20)
|
|
886
|
+
* Graph.addEdge(mutable, nodeC, nodeA, 30)
|
|
887
|
+
* })
|
|
888
|
+
*
|
|
889
|
+
* const result = Graph.findEdges(graph, (data) => data >= 20)
|
|
890
|
+
* console.log(result) // [1, 2]
|
|
891
|
+
*
|
|
892
|
+
* const empty = Graph.findEdges(graph, (data) => data > 100)
|
|
893
|
+
* console.log(empty) // []
|
|
894
|
+
* ```
|
|
895
|
+
*
|
|
896
|
+
* @category getters
|
|
897
|
+
* @since 3.18.0
|
|
898
|
+
*/
|
|
899
|
+
export const findEdges: {
|
|
900
|
+
<E>(
|
|
901
|
+
predicate: (data: E, source: NodeIndex, target: NodeIndex) => boolean
|
|
902
|
+
): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Array<EdgeIndex>
|
|
903
|
+
<N, E, T extends Kind = "directed">(
|
|
904
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
905
|
+
predicate: (data: E, source: NodeIndex, target: NodeIndex) => boolean
|
|
906
|
+
): Array<EdgeIndex>
|
|
907
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
908
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
909
|
+
predicate: (data: E, source: NodeIndex, target: NodeIndex) => boolean
|
|
910
|
+
): Array<EdgeIndex> => {
|
|
911
|
+
const results: Array<EdgeIndex> = []
|
|
912
|
+
for (const [edgeIndex, edgeData] of graph.edges) {
|
|
913
|
+
if (predicate(edgeData.data, edgeData.source, edgeData.target)) {
|
|
914
|
+
results.push(edgeIndex)
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return results
|
|
918
|
+
})
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* Updates a single node's data by applying a transformation function.
|
|
922
|
+
*
|
|
923
|
+
* **Example** (Updating node data)
|
|
924
|
+
*
|
|
925
|
+
* ```ts
|
|
926
|
+
* import { Graph } from "effect"
|
|
927
|
+
*
|
|
928
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
929
|
+
* Graph.addNode(mutable, "Node A")
|
|
930
|
+
* Graph.addNode(mutable, "Node B")
|
|
931
|
+
* Graph.updateNode(mutable, 0, (data) => data.toUpperCase())
|
|
932
|
+
* })
|
|
933
|
+
*
|
|
934
|
+
* const nodeData = Graph.getNode(graph, 0)
|
|
935
|
+
* console.log(nodeData) // Option.some("NODE A")
|
|
936
|
+
* ```
|
|
937
|
+
*
|
|
938
|
+
* @category transforming
|
|
939
|
+
* @since 3.18.0
|
|
940
|
+
*/
|
|
941
|
+
export const updateNode = <N, E, T extends Kind = "directed">(
|
|
942
|
+
mutable: MutableGraph<N, E, T>,
|
|
943
|
+
index: NodeIndex,
|
|
944
|
+
f: (data: N) => N
|
|
945
|
+
): void => {
|
|
946
|
+
if (!mutable.nodes.has(index)) {
|
|
947
|
+
return
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
const currentData = mutable.nodes.get(index)!
|
|
951
|
+
const newData = f(currentData)
|
|
952
|
+
mutable.nodes.set(index, newData)
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* Updates a single edge's data by applying a transformation function.
|
|
957
|
+
*
|
|
958
|
+
* **Example** (Updating edge data)
|
|
959
|
+
*
|
|
960
|
+
* ```ts
|
|
961
|
+
* import { Graph } from "effect"
|
|
962
|
+
*
|
|
963
|
+
* const result = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
964
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
965
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
966
|
+
* const edgeIndex = Graph.addEdge(mutable, nodeA, nodeB, 10)
|
|
967
|
+
* Graph.updateEdge(mutable, edgeIndex, (data) => data * 2)
|
|
968
|
+
* })
|
|
969
|
+
*
|
|
970
|
+
* const edgeData = Graph.getEdge(result, 0)
|
|
971
|
+
* console.log(edgeData) // Option.some(new Graph.Edge({ source: 0, target: 1, data: 20 }))
|
|
972
|
+
* ```
|
|
973
|
+
*
|
|
974
|
+
* @category mutations
|
|
975
|
+
* @since 3.18.0
|
|
976
|
+
*/
|
|
977
|
+
export const updateEdge = <N, E, T extends Kind = "directed">(
|
|
978
|
+
mutable: MutableGraph<N, E, T>,
|
|
979
|
+
edgeIndex: EdgeIndex,
|
|
980
|
+
f: (data: E) => E
|
|
981
|
+
): void => {
|
|
982
|
+
if (!mutable.edges.has(edgeIndex)) {
|
|
983
|
+
return
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
const currentEdge = mutable.edges.get(edgeIndex)!
|
|
987
|
+
const newData = f(currentEdge.data)
|
|
988
|
+
mutable.edges.set(edgeIndex, new Edge({ ...currentEdge, data: newData }))
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* Transforms every node's data in a mutable graph in place using the provided
|
|
993
|
+
* mapping function.
|
|
994
|
+
*
|
|
995
|
+
* **Details**
|
|
996
|
+
*
|
|
997
|
+
* Node indices and edges are preserved; only the stored node data is replaced.
|
|
998
|
+
*
|
|
999
|
+
* **Example** (Mapping node data)
|
|
1000
|
+
*
|
|
1001
|
+
* ```ts
|
|
1002
|
+
* import { Graph } from "effect"
|
|
1003
|
+
*
|
|
1004
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
1005
|
+
* Graph.addNode(mutable, "node a")
|
|
1006
|
+
* Graph.addNode(mutable, "node b")
|
|
1007
|
+
* Graph.addNode(mutable, "node c")
|
|
1008
|
+
* Graph.mapNodes(mutable, (data) => data.toUpperCase())
|
|
1009
|
+
* })
|
|
1010
|
+
*
|
|
1011
|
+
* const nodeData = Graph.getNode(graph, 0)
|
|
1012
|
+
* console.log(nodeData) // Option.some("NODE A")
|
|
1013
|
+
* ```
|
|
1014
|
+
*
|
|
1015
|
+
* @category transforming
|
|
1016
|
+
* @since 3.18.0
|
|
1017
|
+
*/
|
|
1018
|
+
export const mapNodes = <N, E, T extends Kind = "directed">(
|
|
1019
|
+
mutable: MutableGraph<N, E, T>,
|
|
1020
|
+
f: (data: N) => N
|
|
1021
|
+
): void => {
|
|
1022
|
+
// Transform existing node data in place
|
|
1023
|
+
for (const [index, data] of mutable.nodes) {
|
|
1024
|
+
const newData = f(data)
|
|
1025
|
+
mutable.nodes.set(index, newData)
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Transforms all edge data in a mutable graph using the provided mapping function.
|
|
1031
|
+
*
|
|
1032
|
+
* **Example** (Mapping edge data)
|
|
1033
|
+
*
|
|
1034
|
+
* ```ts
|
|
1035
|
+
* import { Graph } from "effect"
|
|
1036
|
+
*
|
|
1037
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
1038
|
+
* const a = Graph.addNode(mutable, "A")
|
|
1039
|
+
* const b = Graph.addNode(mutable, "B")
|
|
1040
|
+
* const c = Graph.addNode(mutable, "C")
|
|
1041
|
+
* Graph.addEdge(mutable, a, b, 10)
|
|
1042
|
+
* Graph.addEdge(mutable, b, c, 20)
|
|
1043
|
+
* Graph.mapEdges(mutable, (data) => data * 2)
|
|
1044
|
+
* })
|
|
1045
|
+
*
|
|
1046
|
+
* const edgeData = Graph.getEdge(graph, 0)
|
|
1047
|
+
* console.log(edgeData) // Option.some(new Graph.Edge({ source: 0, target: 1, data: 20 }))
|
|
1048
|
+
* ```
|
|
1049
|
+
*
|
|
1050
|
+
* @category transforming
|
|
1051
|
+
* @since 3.18.0
|
|
1052
|
+
*/
|
|
1053
|
+
export const mapEdges = <N, E, T extends Kind = "directed">(
|
|
1054
|
+
mutable: MutableGraph<N, E, T>,
|
|
1055
|
+
f: (data: E) => E
|
|
1056
|
+
): void => {
|
|
1057
|
+
// Transform existing edge data in place
|
|
1058
|
+
for (const [index, edgeData] of mutable.edges) {
|
|
1059
|
+
const newData = f(edgeData.data)
|
|
1060
|
+
mutable.edges.set(index, {
|
|
1061
|
+
...edgeData,
|
|
1062
|
+
data: newData
|
|
1063
|
+
})
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* @internal
|
|
1069
|
+
*/
|
|
1070
|
+
const rebuildAdjacency = <N, E, T extends Kind = "directed">(
|
|
1071
|
+
mutable: MutableGraph<N, E, T>
|
|
1072
|
+
): void => {
|
|
1073
|
+
mutable.adjacency.clear()
|
|
1074
|
+
mutable.reverseAdjacency.clear()
|
|
1075
|
+
|
|
1076
|
+
for (const nodeIndex of mutable.nodes.keys()) {
|
|
1077
|
+
mutable.adjacency.set(nodeIndex, [])
|
|
1078
|
+
mutable.reverseAdjacency.set(nodeIndex, [])
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
for (const [edgeIndex, edgeData] of mutable.edges) {
|
|
1082
|
+
mutable.adjacency.get(edgeData.source)!.push(edgeIndex)
|
|
1083
|
+
mutable.reverseAdjacency.get(edgeData.target)!.push(edgeIndex)
|
|
1084
|
+
|
|
1085
|
+
if (mutable.type === "undirected") {
|
|
1086
|
+
mutable.adjacency.get(edgeData.target)!.push(edgeIndex)
|
|
1087
|
+
mutable.reverseAdjacency.get(edgeData.source)!.push(edgeIndex)
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* Swaps source and target nodes for every edge in a mutable graph.
|
|
1094
|
+
*
|
|
1095
|
+
* **Example** (Reversing edge directions)
|
|
1096
|
+
*
|
|
1097
|
+
* ```ts
|
|
1098
|
+
* import { Graph } from "effect"
|
|
1099
|
+
*
|
|
1100
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
1101
|
+
* const a = Graph.addNode(mutable, "A")
|
|
1102
|
+
* const b = Graph.addNode(mutable, "B")
|
|
1103
|
+
* const c = Graph.addNode(mutable, "C")
|
|
1104
|
+
* Graph.addEdge(mutable, a, b, 1) // A -> B
|
|
1105
|
+
* Graph.addEdge(mutable, b, c, 2) // B -> C
|
|
1106
|
+
* Graph.reverse(mutable) // Now B -> A, C -> B
|
|
1107
|
+
* })
|
|
1108
|
+
*
|
|
1109
|
+
* const edge0 = Graph.getEdge(graph, 0)
|
|
1110
|
+
* console.log(edge0) // Option.some(new Graph.Edge({ source: 1, target: 0, data: 1 }))
|
|
1111
|
+
* ```
|
|
1112
|
+
*
|
|
1113
|
+
* @category transforming
|
|
1114
|
+
* @since 3.18.0
|
|
1115
|
+
*/
|
|
1116
|
+
export const reverse = <N, E, T extends Kind = "directed">(
|
|
1117
|
+
mutable: MutableGraph<N, E, T>
|
|
1118
|
+
): void => {
|
|
1119
|
+
if (mutable.type === "undirected") {
|
|
1120
|
+
return
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
// Reverse all edges by swapping source and target
|
|
1124
|
+
for (const [index, edgeData] of mutable.edges) {
|
|
1125
|
+
mutable.edges.set(
|
|
1126
|
+
index,
|
|
1127
|
+
new Edge({
|
|
1128
|
+
source: edgeData.target,
|
|
1129
|
+
target: edgeData.source,
|
|
1130
|
+
data: edgeData.data
|
|
1131
|
+
})
|
|
1132
|
+
)
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
rebuildAdjacency(mutable)
|
|
1136
|
+
|
|
1137
|
+
// Invalidate cycle flag since edge directions changed
|
|
1138
|
+
mutable.acyclic = Option.none()
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/**
|
|
1142
|
+
* Filters and optionally transforms nodes in a mutable graph using a predicate function.
|
|
1143
|
+
* Nodes that return Option.none are removed along with all their connected edges.
|
|
1144
|
+
*
|
|
1145
|
+
* **Example** (Filtering and mapping nodes)
|
|
1146
|
+
*
|
|
1147
|
+
* ```ts
|
|
1148
|
+
* import { Graph, Option } from "effect"
|
|
1149
|
+
*
|
|
1150
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
1151
|
+
* const a = Graph.addNode(mutable, "active")
|
|
1152
|
+
* const b = Graph.addNode(mutable, "inactive")
|
|
1153
|
+
* const c = Graph.addNode(mutable, "active")
|
|
1154
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
1155
|
+
* Graph.addEdge(mutable, b, c, 2)
|
|
1156
|
+
*
|
|
1157
|
+
* // Keep only "active" nodes and transform to uppercase
|
|
1158
|
+
* Graph.filterMapNodes(
|
|
1159
|
+
* mutable,
|
|
1160
|
+
* (data) =>
|
|
1161
|
+
* data === "active" ? Option.some(data.toUpperCase()) : Option.none()
|
|
1162
|
+
* )
|
|
1163
|
+
* })
|
|
1164
|
+
*
|
|
1165
|
+
* console.log(Graph.nodeCount(graph)) // 2 (only "active" nodes remain)
|
|
1166
|
+
* ```
|
|
1167
|
+
*
|
|
1168
|
+
* @category transforming
|
|
1169
|
+
* @since 3.18.0
|
|
1170
|
+
*/
|
|
1171
|
+
export const filterMapNodes = <N, E, T extends Kind = "directed">(
|
|
1172
|
+
mutable: MutableGraph<N, E, T>,
|
|
1173
|
+
f: (data: N) => Option.Option<N>
|
|
1174
|
+
): void => {
|
|
1175
|
+
const nodesToRemove: Array<NodeIndex> = []
|
|
1176
|
+
|
|
1177
|
+
// First pass: identify nodes to remove and transform data for nodes to keep
|
|
1178
|
+
for (const [index, data] of mutable.nodes) {
|
|
1179
|
+
const result = f(data)
|
|
1180
|
+
if (Option.isSome(result)) {
|
|
1181
|
+
// Transform node data
|
|
1182
|
+
mutable.nodes.set(index, result.value)
|
|
1183
|
+
} else {
|
|
1184
|
+
// Mark for removal
|
|
1185
|
+
nodesToRemove.push(index)
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
// Second pass: remove filtered out nodes and their edges
|
|
1190
|
+
for (const nodeIndex of nodesToRemove) {
|
|
1191
|
+
removeNode(mutable, nodeIndex)
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
/**
|
|
1196
|
+
* Filters and optionally transforms edges in a mutable graph using a predicate function.
|
|
1197
|
+
* Edges that return Option.none are removed from the graph.
|
|
1198
|
+
*
|
|
1199
|
+
* **Example** (Filtering and mapping edges)
|
|
1200
|
+
*
|
|
1201
|
+
* ```ts
|
|
1202
|
+
* import { Graph, Option } from "effect"
|
|
1203
|
+
*
|
|
1204
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
1205
|
+
* const a = Graph.addNode(mutable, "A")
|
|
1206
|
+
* const b = Graph.addNode(mutable, "B")
|
|
1207
|
+
* const c = Graph.addNode(mutable, "C")
|
|
1208
|
+
* Graph.addEdge(mutable, a, b, 5)
|
|
1209
|
+
* Graph.addEdge(mutable, b, c, 15)
|
|
1210
|
+
* Graph.addEdge(mutable, c, a, 25)
|
|
1211
|
+
*
|
|
1212
|
+
* // Keep only edges with weight >= 10 and double their weight
|
|
1213
|
+
* Graph.filterMapEdges(
|
|
1214
|
+
* mutable,
|
|
1215
|
+
* (data) => data >= 10 ? Option.some(data * 2) : Option.none()
|
|
1216
|
+
* )
|
|
1217
|
+
* })
|
|
1218
|
+
*
|
|
1219
|
+
* console.log(Graph.edgeCount(graph)) // 2 (edges with weight 5 removed)
|
|
1220
|
+
* ```
|
|
1221
|
+
*
|
|
1222
|
+
* @category transforming
|
|
1223
|
+
* @since 3.18.0
|
|
1224
|
+
*/
|
|
1225
|
+
export const filterMapEdges = <N, E, T extends Kind = "directed">(
|
|
1226
|
+
mutable: MutableGraph<N, E, T>,
|
|
1227
|
+
f: (data: E) => Option.Option<E>
|
|
1228
|
+
): void => {
|
|
1229
|
+
const edgesToRemove: Array<EdgeIndex> = []
|
|
1230
|
+
|
|
1231
|
+
// First pass: identify edges to remove and transform data for edges to keep
|
|
1232
|
+
for (const [index, edgeData] of mutable.edges) {
|
|
1233
|
+
const result = f(edgeData.data)
|
|
1234
|
+
if (Option.isSome(result)) {
|
|
1235
|
+
// Transform edge data
|
|
1236
|
+
mutable.edges.set(index, {
|
|
1237
|
+
...edgeData,
|
|
1238
|
+
data: result.value
|
|
1239
|
+
})
|
|
1240
|
+
} else {
|
|
1241
|
+
// Mark for removal
|
|
1242
|
+
edgesToRemove.push(index)
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
// Second pass: remove filtered out edges
|
|
1247
|
+
for (const edgeIndex of edgesToRemove) {
|
|
1248
|
+
removeEdge(mutable, edgeIndex)
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
/**
|
|
1253
|
+
* Filters nodes by removing those that don't match the predicate.
|
|
1254
|
+
* This function modifies the mutable graph in place.
|
|
1255
|
+
*
|
|
1256
|
+
* **Example** (Filtering nodes)
|
|
1257
|
+
*
|
|
1258
|
+
* ```ts
|
|
1259
|
+
* import { Graph } from "effect"
|
|
1260
|
+
*
|
|
1261
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
1262
|
+
* Graph.addNode(mutable, "active")
|
|
1263
|
+
* Graph.addNode(mutable, "inactive")
|
|
1264
|
+
* Graph.addNode(mutable, "pending")
|
|
1265
|
+
* Graph.addNode(mutable, "active")
|
|
1266
|
+
*
|
|
1267
|
+
* // Keep only "active" nodes
|
|
1268
|
+
* Graph.filterNodes(mutable, (data) => data === "active")
|
|
1269
|
+
* })
|
|
1270
|
+
*
|
|
1271
|
+
* console.log(Graph.nodeCount(graph)) // 2 (only "active" nodes remain)
|
|
1272
|
+
* ```
|
|
1273
|
+
*
|
|
1274
|
+
* @category transforming
|
|
1275
|
+
* @since 3.18.0
|
|
1276
|
+
*/
|
|
1277
|
+
export const filterNodes = <N, E, T extends Kind = "directed">(
|
|
1278
|
+
mutable: MutableGraph<N, E, T>,
|
|
1279
|
+
predicate: (data: N) => boolean
|
|
1280
|
+
): void => {
|
|
1281
|
+
const nodesToRemove: Array<NodeIndex> = []
|
|
1282
|
+
|
|
1283
|
+
// Identify nodes to remove
|
|
1284
|
+
for (const [index, data] of mutable.nodes) {
|
|
1285
|
+
if (!predicate(data)) {
|
|
1286
|
+
nodesToRemove.push(index)
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
// Remove filtered out nodes (this also removes connected edges)
|
|
1291
|
+
for (const nodeIndex of nodesToRemove) {
|
|
1292
|
+
removeNode(mutable, nodeIndex)
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
/**
|
|
1297
|
+
* Filters edges by removing those that don't match the predicate.
|
|
1298
|
+
* This function modifies the mutable graph in place.
|
|
1299
|
+
*
|
|
1300
|
+
* **Example** (Filtering edges)
|
|
1301
|
+
*
|
|
1302
|
+
* ```ts
|
|
1303
|
+
* import { Graph } from "effect"
|
|
1304
|
+
*
|
|
1305
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
1306
|
+
* const a = Graph.addNode(mutable, "A")
|
|
1307
|
+
* const b = Graph.addNode(mutable, "B")
|
|
1308
|
+
* const c = Graph.addNode(mutable, "C")
|
|
1309
|
+
*
|
|
1310
|
+
* Graph.addEdge(mutable, a, b, 5)
|
|
1311
|
+
* Graph.addEdge(mutable, b, c, 15)
|
|
1312
|
+
* Graph.addEdge(mutable, c, a, 25)
|
|
1313
|
+
*
|
|
1314
|
+
* // Keep only edges with weight >= 10
|
|
1315
|
+
* Graph.filterEdges(mutable, (data) => data >= 10)
|
|
1316
|
+
* })
|
|
1317
|
+
*
|
|
1318
|
+
* console.log(Graph.edgeCount(graph)) // 2 (edge with weight 5 removed)
|
|
1319
|
+
* ```
|
|
1320
|
+
*
|
|
1321
|
+
* @category transforming
|
|
1322
|
+
* @since 3.18.0
|
|
1323
|
+
*/
|
|
1324
|
+
export const filterEdges = <N, E, T extends Kind = "directed">(
|
|
1325
|
+
mutable: MutableGraph<N, E, T>,
|
|
1326
|
+
predicate: (data: E) => boolean
|
|
1327
|
+
): void => {
|
|
1328
|
+
const edgesToRemove: Array<EdgeIndex> = []
|
|
1329
|
+
|
|
1330
|
+
// Identify edges to remove
|
|
1331
|
+
for (const [index, edgeData] of mutable.edges) {
|
|
1332
|
+
if (!predicate(edgeData.data)) {
|
|
1333
|
+
edgesToRemove.push(index)
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
// Remove filtered out edges
|
|
1338
|
+
for (const edgeIndex of edgesToRemove) {
|
|
1339
|
+
removeEdge(mutable, edgeIndex)
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
// =============================================================================
|
|
1344
|
+
// Cycle Flag Management (Internal)
|
|
1345
|
+
// =============================================================================
|
|
1346
|
+
|
|
1347
|
+
/** @internal */
|
|
1348
|
+
const invalidateCycleFlagOnRemoval = <N, E, T extends Kind = "directed">(
|
|
1349
|
+
mutable: MutableGraph<N, E, T>
|
|
1350
|
+
): void => {
|
|
1351
|
+
// Only invalidate if the graph had cycles (removing edges/nodes cannot introduce cycles in acyclic graphs).
|
|
1352
|
+
if (mutable.acyclic._tag === "Some" && mutable.acyclic.value === false) {
|
|
1353
|
+
mutable.acyclic = Option.none()
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
/** @internal */
|
|
1358
|
+
const invalidateCycleFlagOnAddition = <N, E, T extends Kind = "directed">(
|
|
1359
|
+
mutable: MutableGraph<N, E, T>
|
|
1360
|
+
): void => {
|
|
1361
|
+
// Only invalidate if the graph was acyclic (adding edges cannot remove cycles from cyclic graphs).
|
|
1362
|
+
if (mutable.acyclic._tag === "Some" && mutable.acyclic.value === true) {
|
|
1363
|
+
mutable.acyclic = Option.none()
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// =============================================================================
|
|
1368
|
+
// Edge Operations
|
|
1369
|
+
// =============================================================================
|
|
1370
|
+
|
|
1371
|
+
/**
|
|
1372
|
+
* Adds a new edge to a mutable graph and returns its index.
|
|
1373
|
+
*
|
|
1374
|
+
* **When to use**
|
|
1375
|
+
*
|
|
1376
|
+
* Use to connect two existing nodes in a mutable graph while storing edge data
|
|
1377
|
+
* and receiving the new edge identifier.
|
|
1378
|
+
*
|
|
1379
|
+
* **Details**
|
|
1380
|
+
*
|
|
1381
|
+
* Creates an `Edge` with the source, target, and data at the next edge index,
|
|
1382
|
+
* updates adjacency indexes, and increments the graph's next edge index.
|
|
1383
|
+
* Undirected graphs register the same edge for both endpoints.
|
|
1384
|
+
*
|
|
1385
|
+
* **Gotchas**
|
|
1386
|
+
*
|
|
1387
|
+
* The source and target nodes must already exist in the mutable graph; missing
|
|
1388
|
+
* endpoints throw a `GraphError`.
|
|
1389
|
+
*
|
|
1390
|
+
* **Example** (Adding edges)
|
|
1391
|
+
*
|
|
1392
|
+
* ```ts
|
|
1393
|
+
* import { Graph } from "effect"
|
|
1394
|
+
*
|
|
1395
|
+
* const result = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
1396
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
1397
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
1398
|
+
* const edge = Graph.addEdge(mutable, nodeA, nodeB, 42)
|
|
1399
|
+
* console.log(edge) // EdgeIndex with value 0
|
|
1400
|
+
* })
|
|
1401
|
+
* ```
|
|
1402
|
+
*
|
|
1403
|
+
* @see {@link mutate} for obtaining a mutable graph from an immutable graph
|
|
1404
|
+
* @see {@link addNode} for creating node indexes before connecting them
|
|
1405
|
+
* @see {@link getEdge} for reading the returned edge
|
|
1406
|
+
* @see {@link removeEdge} for removing an edge from a mutable graph
|
|
1407
|
+
*
|
|
1408
|
+
* @category mutations
|
|
1409
|
+
* @since 3.18.0
|
|
1410
|
+
*/
|
|
1411
|
+
export const addEdge = <N, E, T extends Kind = "directed">(
|
|
1412
|
+
mutable: MutableGraph<N, E, T>,
|
|
1413
|
+
source: NodeIndex,
|
|
1414
|
+
target: NodeIndex,
|
|
1415
|
+
data: E
|
|
1416
|
+
): EdgeIndex => {
|
|
1417
|
+
// Validate that both nodes exist
|
|
1418
|
+
if (!mutable.nodes.has(source)) {
|
|
1419
|
+
throw missingNode(source)
|
|
1420
|
+
}
|
|
1421
|
+
if (!mutable.nodes.has(target)) {
|
|
1422
|
+
throw missingNode(target)
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
const edgeIndex = mutable.nextEdgeIndex
|
|
1426
|
+
|
|
1427
|
+
// Create edge data
|
|
1428
|
+
const edgeData = new Edge({ source, target, data })
|
|
1429
|
+
mutable.edges.set(edgeIndex, edgeData)
|
|
1430
|
+
|
|
1431
|
+
// Update adjacency lists
|
|
1432
|
+
const sourceAdjacency = mutable.adjacency.get(source)
|
|
1433
|
+
if (sourceAdjacency !== undefined) {
|
|
1434
|
+
sourceAdjacency.push(edgeIndex)
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
const targetReverseAdjacency = mutable.reverseAdjacency.get(target)
|
|
1438
|
+
if (targetReverseAdjacency !== undefined) {
|
|
1439
|
+
targetReverseAdjacency.push(edgeIndex)
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// For undirected graphs, add reverse connections
|
|
1443
|
+
if (mutable.type === "undirected") {
|
|
1444
|
+
const targetAdjacency = mutable.adjacency.get(target)
|
|
1445
|
+
if (targetAdjacency !== undefined) {
|
|
1446
|
+
targetAdjacency.push(edgeIndex)
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
const sourceReverseAdjacency = mutable.reverseAdjacency.get(source)
|
|
1450
|
+
if (sourceReverseAdjacency !== undefined) {
|
|
1451
|
+
sourceReverseAdjacency.push(edgeIndex)
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// Update allocators
|
|
1456
|
+
mutable.nextEdgeIndex = mutable.nextEdgeIndex + 1
|
|
1457
|
+
|
|
1458
|
+
// Only invalidate cycle flag if the graph was acyclic
|
|
1459
|
+
// Adding edges cannot remove cycles from cyclic graphs
|
|
1460
|
+
invalidateCycleFlagOnAddition(mutable)
|
|
1461
|
+
|
|
1462
|
+
return edgeIndex
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/**
|
|
1466
|
+
* Removes a node and all its incident edges from a mutable graph.
|
|
1467
|
+
*
|
|
1468
|
+
* **Example** (Removing a node)
|
|
1469
|
+
*
|
|
1470
|
+
* ```ts
|
|
1471
|
+
* import { Graph } from "effect"
|
|
1472
|
+
*
|
|
1473
|
+
* const result = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
1474
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
1475
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
1476
|
+
* Graph.addEdge(mutable, nodeA, nodeB, 42)
|
|
1477
|
+
*
|
|
1478
|
+
* // Remove nodeA and all edges connected to it
|
|
1479
|
+
* Graph.removeNode(mutable, nodeA)
|
|
1480
|
+
* })
|
|
1481
|
+
* ```
|
|
1482
|
+
*
|
|
1483
|
+
* @category mutations
|
|
1484
|
+
* @since 3.18.0
|
|
1485
|
+
*/
|
|
1486
|
+
export const removeNode = <N, E, T extends Kind = "directed">(
|
|
1487
|
+
mutable: MutableGraph<N, E, T>,
|
|
1488
|
+
nodeIndex: NodeIndex
|
|
1489
|
+
): void => {
|
|
1490
|
+
// Check if node exists
|
|
1491
|
+
if (!mutable.nodes.has(nodeIndex)) {
|
|
1492
|
+
return // Node doesn't exist, nothing to remove
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
// Collect all incident edges for removal
|
|
1496
|
+
const edgesToRemove: Array<EdgeIndex> = []
|
|
1497
|
+
|
|
1498
|
+
// Get outgoing edges
|
|
1499
|
+
const outgoingEdges = mutable.adjacency.get(nodeIndex)
|
|
1500
|
+
if (outgoingEdges !== undefined) {
|
|
1501
|
+
for (const edge of outgoingEdges) {
|
|
1502
|
+
edgesToRemove.push(edge)
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
// Get incoming edges
|
|
1507
|
+
const incomingEdges = mutable.reverseAdjacency.get(nodeIndex)
|
|
1508
|
+
if (incomingEdges !== undefined) {
|
|
1509
|
+
for (const edge of incomingEdges) {
|
|
1510
|
+
edgesToRemove.push(edge)
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// Remove all incident edges
|
|
1515
|
+
for (const edgeIndex of edgesToRemove) {
|
|
1516
|
+
removeEdgeInternal(mutable, edgeIndex)
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
// Remove the node itself
|
|
1520
|
+
mutable.nodes.delete(nodeIndex)
|
|
1521
|
+
mutable.adjacency.delete(nodeIndex)
|
|
1522
|
+
mutable.reverseAdjacency.delete(nodeIndex)
|
|
1523
|
+
|
|
1524
|
+
// Only invalidate cycle flag if the graph wasn't already known to be acyclic
|
|
1525
|
+
// Removing nodes cannot introduce cycles in an acyclic graph
|
|
1526
|
+
invalidateCycleFlagOnRemoval(mutable)
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/**
|
|
1530
|
+
* Removes an edge from a mutable graph.
|
|
1531
|
+
*
|
|
1532
|
+
* **Example** (Removing an edge)
|
|
1533
|
+
*
|
|
1534
|
+
* ```ts
|
|
1535
|
+
* import { Graph } from "effect"
|
|
1536
|
+
*
|
|
1537
|
+
* const result = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
1538
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
1539
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
1540
|
+
* const edge = Graph.addEdge(mutable, nodeA, nodeB, 42)
|
|
1541
|
+
*
|
|
1542
|
+
* // Remove the edge
|
|
1543
|
+
* Graph.removeEdge(mutable, edge)
|
|
1544
|
+
* })
|
|
1545
|
+
* ```
|
|
1546
|
+
*
|
|
1547
|
+
* @category mutations
|
|
1548
|
+
* @since 3.18.0
|
|
1549
|
+
*/
|
|
1550
|
+
export const removeEdge = <N, E, T extends Kind = "directed">(
|
|
1551
|
+
mutable: MutableGraph<N, E, T>,
|
|
1552
|
+
edgeIndex: EdgeIndex
|
|
1553
|
+
): void => {
|
|
1554
|
+
const wasRemoved = removeEdgeInternal(mutable, edgeIndex)
|
|
1555
|
+
|
|
1556
|
+
// Only invalidate cycle flag if an edge was actually removed
|
|
1557
|
+
// and only if the graph wasn't already known to be acyclic
|
|
1558
|
+
if (wasRemoved) {
|
|
1559
|
+
invalidateCycleFlagOnRemoval(mutable)
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
/** @internal */
|
|
1564
|
+
const removeEdgeInternal = <N, E, T extends Kind = "directed">(
|
|
1565
|
+
mutable: MutableGraph<N, E, T>,
|
|
1566
|
+
edgeIndex: EdgeIndex
|
|
1567
|
+
): boolean => {
|
|
1568
|
+
// Get edge data
|
|
1569
|
+
const edge = mutable.edges.get(edgeIndex)
|
|
1570
|
+
if (edge === undefined) {
|
|
1571
|
+
return false // Edge doesn't exist, no mutation occurred
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
const { source, target } = edge
|
|
1575
|
+
|
|
1576
|
+
// Remove from adjacency lists
|
|
1577
|
+
const sourceAdjacency = mutable.adjacency.get(source)
|
|
1578
|
+
if (sourceAdjacency !== undefined) {
|
|
1579
|
+
const index = sourceAdjacency.indexOf(edgeIndex)
|
|
1580
|
+
if (index !== -1) {
|
|
1581
|
+
sourceAdjacency.splice(index, 1)
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
const targetReverseAdjacency = mutable.reverseAdjacency.get(target)
|
|
1586
|
+
if (targetReverseAdjacency !== undefined) {
|
|
1587
|
+
const index = targetReverseAdjacency.indexOf(edgeIndex)
|
|
1588
|
+
if (index !== -1) {
|
|
1589
|
+
targetReverseAdjacency.splice(index, 1)
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
// For undirected graphs, remove reverse connections
|
|
1594
|
+
if (mutable.type === "undirected") {
|
|
1595
|
+
const targetAdjacency = mutable.adjacency.get(target)
|
|
1596
|
+
if (targetAdjacency !== undefined) {
|
|
1597
|
+
const index = targetAdjacency.indexOf(edgeIndex)
|
|
1598
|
+
if (index !== -1) {
|
|
1599
|
+
targetAdjacency.splice(index, 1)
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
const sourceReverseAdjacency = mutable.reverseAdjacency.get(source)
|
|
1604
|
+
if (sourceReverseAdjacency !== undefined) {
|
|
1605
|
+
const index = sourceReverseAdjacency.indexOf(edgeIndex)
|
|
1606
|
+
if (index !== -1) {
|
|
1607
|
+
sourceReverseAdjacency.splice(index, 1)
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
// Remove edge data
|
|
1613
|
+
mutable.edges.delete(edgeIndex)
|
|
1614
|
+
|
|
1615
|
+
return true // Edge was successfully removed
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
// =============================================================================
|
|
1619
|
+
// Edge Query Operations
|
|
1620
|
+
// =============================================================================
|
|
1621
|
+
|
|
1622
|
+
/**
|
|
1623
|
+
* Gets the edge data associated with an edge index safely, if it exists.
|
|
1624
|
+
*
|
|
1625
|
+
* **Example** (Getting edge data)
|
|
1626
|
+
*
|
|
1627
|
+
* ```ts
|
|
1628
|
+
* import { Graph } from "effect"
|
|
1629
|
+
*
|
|
1630
|
+
* const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
1631
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
1632
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
1633
|
+
* Graph.addEdge(mutable, nodeA, nodeB, 42)
|
|
1634
|
+
* })
|
|
1635
|
+
*
|
|
1636
|
+
* const edgeIndex = 0
|
|
1637
|
+
* const edgeData = Graph.getEdge(graph, edgeIndex)
|
|
1638
|
+
*
|
|
1639
|
+
* if (edgeData._tag === "Some") {
|
|
1640
|
+
* console.log(edgeData.value.data) // 42
|
|
1641
|
+
* console.log(edgeData.value.source) // 0
|
|
1642
|
+
* console.log(edgeData.value.target) // 1
|
|
1643
|
+
* }
|
|
1644
|
+
* ```
|
|
1645
|
+
*
|
|
1646
|
+
* @category getters
|
|
1647
|
+
* @since 3.18.0
|
|
1648
|
+
*/
|
|
1649
|
+
export const getEdge: {
|
|
1650
|
+
<E>(
|
|
1651
|
+
edgeIndex: EdgeIndex
|
|
1652
|
+
): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option.Option<Edge<E>>
|
|
1653
|
+
<N, E, T extends Kind = "directed">(
|
|
1654
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
1655
|
+
edgeIndex: EdgeIndex
|
|
1656
|
+
): Option.Option<Edge<E>>
|
|
1657
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
1658
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
1659
|
+
edgeIndex: EdgeIndex
|
|
1660
|
+
): Option.Option<Edge<E>> => Option.fromUndefinedOr(graph.edges.get(edgeIndex)))
|
|
1661
|
+
|
|
1662
|
+
/**
|
|
1663
|
+
* Checks whether an edge exists between two nodes in the graph.
|
|
1664
|
+
*
|
|
1665
|
+
* **Example** (Checking edge existence)
|
|
1666
|
+
*
|
|
1667
|
+
* ```ts
|
|
1668
|
+
* import { Graph } from "effect"
|
|
1669
|
+
*
|
|
1670
|
+
* const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
1671
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
1672
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
1673
|
+
* const nodeC = Graph.addNode(mutable, "Node C")
|
|
1674
|
+
* Graph.addEdge(mutable, nodeA, nodeB, 42)
|
|
1675
|
+
* })
|
|
1676
|
+
*
|
|
1677
|
+
* const nodeA = 0
|
|
1678
|
+
* const nodeB = 1
|
|
1679
|
+
* const nodeC = 2
|
|
1680
|
+
*
|
|
1681
|
+
* const hasAB = Graph.hasEdge(graph, nodeA, nodeB)
|
|
1682
|
+
* console.log(hasAB) // true
|
|
1683
|
+
*
|
|
1684
|
+
* const hasAC = Graph.hasEdge(graph, nodeA, nodeC)
|
|
1685
|
+
* console.log(hasAC) // false
|
|
1686
|
+
* ```
|
|
1687
|
+
*
|
|
1688
|
+
* @category getters
|
|
1689
|
+
* @since 3.18.0
|
|
1690
|
+
*/
|
|
1691
|
+
export const hasEdge: {
|
|
1692
|
+
(
|
|
1693
|
+
source: NodeIndex,
|
|
1694
|
+
target: NodeIndex
|
|
1695
|
+
): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => boolean
|
|
1696
|
+
<N, E, T extends Kind = "directed">(
|
|
1697
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
1698
|
+
source: NodeIndex,
|
|
1699
|
+
target: NodeIndex
|
|
1700
|
+
): boolean
|
|
1701
|
+
} = dual(3, <N, E, T extends Kind = "directed">(
|
|
1702
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
1703
|
+
source: NodeIndex,
|
|
1704
|
+
target: NodeIndex
|
|
1705
|
+
): boolean => {
|
|
1706
|
+
const adjacencyList = graph.adjacency.get(source)
|
|
1707
|
+
if (adjacencyList === undefined) {
|
|
1708
|
+
return false
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
// Check if any edge in the adjacency list connects to the target
|
|
1712
|
+
for (const edgeIndex of adjacencyList) {
|
|
1713
|
+
const edge = graph.edges.get(edgeIndex)
|
|
1714
|
+
if (edge !== undefined) {
|
|
1715
|
+
const neighbor = graph.type === "undirected" && edge.target === source ? edge.source : edge.target
|
|
1716
|
+
if (neighbor === target) {
|
|
1717
|
+
return true
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
return false
|
|
1723
|
+
})
|
|
1724
|
+
|
|
1725
|
+
/**
|
|
1726
|
+
* Returns the number of edges in the graph.
|
|
1727
|
+
*
|
|
1728
|
+
* **Example** (Counting edges)
|
|
1729
|
+
*
|
|
1730
|
+
* ```ts
|
|
1731
|
+
* import { Graph } from "effect"
|
|
1732
|
+
*
|
|
1733
|
+
* const emptyGraph = Graph.directed<string, number>()
|
|
1734
|
+
* console.log(Graph.edgeCount(emptyGraph)) // 0
|
|
1735
|
+
*
|
|
1736
|
+
* const graphWithEdges = Graph.mutate(emptyGraph, (mutable) => {
|
|
1737
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
1738
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
1739
|
+
* const nodeC = Graph.addNode(mutable, "Node C")
|
|
1740
|
+
* Graph.addEdge(mutable, nodeA, nodeB, 1)
|
|
1741
|
+
* Graph.addEdge(mutable, nodeB, nodeC, 2)
|
|
1742
|
+
* Graph.addEdge(mutable, nodeC, nodeA, 3)
|
|
1743
|
+
* })
|
|
1744
|
+
*
|
|
1745
|
+
* console.log(Graph.edgeCount(graphWithEdges)) // 3
|
|
1746
|
+
* ```
|
|
1747
|
+
*
|
|
1748
|
+
* @category getters
|
|
1749
|
+
* @since 3.18.0
|
|
1750
|
+
*/
|
|
1751
|
+
export const edgeCount = <N, E, T extends Kind = "directed">(
|
|
1752
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>
|
|
1753
|
+
): number => graph.edges.size
|
|
1754
|
+
|
|
1755
|
+
const getDirectedNeighbors = <N, E>(
|
|
1756
|
+
graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">,
|
|
1757
|
+
nodeIndex: NodeIndex,
|
|
1758
|
+
direction: Direction
|
|
1759
|
+
): Array<NodeIndex> => {
|
|
1760
|
+
const adjacencyMap = direction === "incoming"
|
|
1761
|
+
? graph.reverseAdjacency
|
|
1762
|
+
: graph.adjacency
|
|
1763
|
+
|
|
1764
|
+
const adjacencyList = adjacencyMap.get(nodeIndex)
|
|
1765
|
+
if (adjacencyList === undefined) {
|
|
1766
|
+
return []
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
const result: Array<NodeIndex> = []
|
|
1770
|
+
for (const edgeIndex of adjacencyList) {
|
|
1771
|
+
const edge = graph.edges.get(edgeIndex)
|
|
1772
|
+
if (edge !== undefined) {
|
|
1773
|
+
result.push(direction === "incoming" ? edge.source : edge.target)
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
return result
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
/**
|
|
1781
|
+
* Returns the neighboring node indices for a node.
|
|
1782
|
+
*
|
|
1783
|
+
* **Details**
|
|
1784
|
+
*
|
|
1785
|
+
* For directed graphs, neighbors are the targets of outgoing edges. For
|
|
1786
|
+
* undirected graphs, neighbors are the other endpoints of incident edges.
|
|
1787
|
+
*
|
|
1788
|
+
* **Example** (Getting outgoing neighbors)
|
|
1789
|
+
*
|
|
1790
|
+
* ```ts
|
|
1791
|
+
* import { Graph } from "effect"
|
|
1792
|
+
*
|
|
1793
|
+
* const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
1794
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
1795
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
1796
|
+
* const nodeC = Graph.addNode(mutable, "Node C")
|
|
1797
|
+
* Graph.addEdge(mutable, nodeA, nodeB, 1)
|
|
1798
|
+
* Graph.addEdge(mutable, nodeA, nodeC, 2)
|
|
1799
|
+
* })
|
|
1800
|
+
*
|
|
1801
|
+
* const nodeA = 0
|
|
1802
|
+
* const nodeB = 1
|
|
1803
|
+
* const nodeC = 2
|
|
1804
|
+
*
|
|
1805
|
+
* const neighborsA = Graph.neighbors(graph, nodeA)
|
|
1806
|
+
* console.log(neighborsA) // [1, 2]
|
|
1807
|
+
*
|
|
1808
|
+
* const neighborsB = Graph.neighbors(graph, nodeB)
|
|
1809
|
+
* console.log(neighborsB) // []
|
|
1810
|
+
* ```
|
|
1811
|
+
*
|
|
1812
|
+
* @category getters
|
|
1813
|
+
* @since 3.18.0
|
|
1814
|
+
*/
|
|
1815
|
+
export const neighbors: {
|
|
1816
|
+
(
|
|
1817
|
+
nodeIndex: NodeIndex
|
|
1818
|
+
): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Array<NodeIndex>
|
|
1819
|
+
<N, E, T extends Kind = "directed">(
|
|
1820
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
1821
|
+
nodeIndex: NodeIndex
|
|
1822
|
+
): Array<NodeIndex>
|
|
1823
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
1824
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
1825
|
+
nodeIndex: NodeIndex
|
|
1826
|
+
): Array<NodeIndex> => {
|
|
1827
|
+
// For undirected graphs, use the specialized helper that returns the other endpoint
|
|
1828
|
+
if (graph.type === "undirected") {
|
|
1829
|
+
return getUndirectedNeighbors(graph as any, nodeIndex)
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
return getDirectedNeighbors(graph as Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, nodeIndex, "outgoing")
|
|
1833
|
+
})
|
|
1834
|
+
|
|
1835
|
+
/**
|
|
1836
|
+
* Returns the outgoing neighbor node indices for a node in a directed graph.
|
|
1837
|
+
*
|
|
1838
|
+
* **When to use**
|
|
1839
|
+
*
|
|
1840
|
+
* Use when you need the nodes reached by following outgoing edges from a node in
|
|
1841
|
+
* a directed graph.
|
|
1842
|
+
*
|
|
1843
|
+
* **Gotchas**
|
|
1844
|
+
*
|
|
1845
|
+
* Throws a `GraphError` when used with an undirected graph.
|
|
1846
|
+
*
|
|
1847
|
+
* @see {@link predecessors} for incoming neighbors in a directed graph
|
|
1848
|
+
* @see {@link neighbors} for generic neighbor lookup across graph kinds
|
|
1849
|
+
*
|
|
1850
|
+
* @category queries
|
|
1851
|
+
* @since 4.0.0
|
|
1852
|
+
*/
|
|
1853
|
+
export const successors: {
|
|
1854
|
+
(
|
|
1855
|
+
nodeIndex: NodeIndex
|
|
1856
|
+
): <N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => Array<NodeIndex>
|
|
1857
|
+
<N, E>(
|
|
1858
|
+
graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">,
|
|
1859
|
+
nodeIndex: NodeIndex
|
|
1860
|
+
): Array<NodeIndex>
|
|
1861
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
1862
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
1863
|
+
nodeIndex: NodeIndex
|
|
1864
|
+
): Array<NodeIndex> => {
|
|
1865
|
+
if (graph.type === "undirected") {
|
|
1866
|
+
throw new GraphError({ message: "Cannot get successors of undirected graph" })
|
|
1867
|
+
}
|
|
1868
|
+
return getDirectedNeighbors(graph as Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, nodeIndex, "outgoing")
|
|
1869
|
+
})
|
|
1870
|
+
|
|
1871
|
+
/**
|
|
1872
|
+
* Returns the incoming neighbor node indices for a node in a directed graph.
|
|
1873
|
+
*
|
|
1874
|
+
* **When to use**
|
|
1875
|
+
*
|
|
1876
|
+
* Use when you need the nodes that reach a node by following incoming edges in a
|
|
1877
|
+
* directed graph.
|
|
1878
|
+
*
|
|
1879
|
+
* **Gotchas**
|
|
1880
|
+
*
|
|
1881
|
+
* Throws a `GraphError` when used with an undirected graph.
|
|
1882
|
+
*
|
|
1883
|
+
* @see {@link successors} for outgoing neighbors in a directed graph
|
|
1884
|
+
* @see {@link neighbors} for generic neighbor lookup across graph kinds
|
|
1885
|
+
*
|
|
1886
|
+
* @category queries
|
|
1887
|
+
* @since 4.0.0
|
|
1888
|
+
*/
|
|
1889
|
+
export const predecessors: {
|
|
1890
|
+
(
|
|
1891
|
+
nodeIndex: NodeIndex
|
|
1892
|
+
): <N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => Array<NodeIndex>
|
|
1893
|
+
<N, E>(
|
|
1894
|
+
graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">,
|
|
1895
|
+
nodeIndex: NodeIndex
|
|
1896
|
+
): Array<NodeIndex>
|
|
1897
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
1898
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
1899
|
+
nodeIndex: NodeIndex
|
|
1900
|
+
): Array<NodeIndex> => {
|
|
1901
|
+
if (graph.type === "undirected") {
|
|
1902
|
+
throw new GraphError({ message: "Cannot get predecessors of undirected graph" })
|
|
1903
|
+
}
|
|
1904
|
+
return getDirectedNeighbors(graph as Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, nodeIndex, "incoming")
|
|
1905
|
+
})
|
|
1906
|
+
|
|
1907
|
+
/**
|
|
1908
|
+
* Gets directed neighbors of a node in a specific direction.
|
|
1909
|
+
*
|
|
1910
|
+
* **When to use**
|
|
1911
|
+
*
|
|
1912
|
+
* Use when maintaining existing code that already passes an explicit traversal
|
|
1913
|
+
* direction. New code should prefer `successors` or `predecessors`.
|
|
1914
|
+
*
|
|
1915
|
+
* **Gotchas**
|
|
1916
|
+
*
|
|
1917
|
+
* Throws a `GraphError` when used with an undirected graph.
|
|
1918
|
+
*
|
|
1919
|
+
* **Example** (Traversing directed neighbors)
|
|
1920
|
+
*
|
|
1921
|
+
* ```ts
|
|
1922
|
+
* import { Graph } from "effect"
|
|
1923
|
+
*
|
|
1924
|
+
* const graph = Graph.directed<string, string>((mutable) => {
|
|
1925
|
+
* const a = Graph.addNode(mutable, "A")
|
|
1926
|
+
* const b = Graph.addNode(mutable, "B")
|
|
1927
|
+
* Graph.addEdge(mutable, a, b, "A->B")
|
|
1928
|
+
* })
|
|
1929
|
+
*
|
|
1930
|
+
* const nodeA = 0
|
|
1931
|
+
* const nodeB = 1
|
|
1932
|
+
*
|
|
1933
|
+
* // Get outgoing neighbors (nodes that nodeA points to)
|
|
1934
|
+
* const outgoing = Graph.neighborsDirected(graph, nodeA, "outgoing")
|
|
1935
|
+
*
|
|
1936
|
+
* // Get incoming neighbors (nodes that point to nodeB)
|
|
1937
|
+
* const incoming = Graph.neighborsDirected(graph, nodeB, "incoming")
|
|
1938
|
+
* ```
|
|
1939
|
+
*
|
|
1940
|
+
* @deprecated Use {@link successors} for outgoing neighbors or {@link predecessors} for incoming neighbors.
|
|
1941
|
+
* @see {@link successors} for outgoing neighbors in a directed graph
|
|
1942
|
+
* @see {@link predecessors} for incoming neighbors in a directed graph
|
|
1943
|
+
* @category queries
|
|
1944
|
+
* @since 3.18.0
|
|
1945
|
+
*/
|
|
1946
|
+
export const neighborsDirected: {
|
|
1947
|
+
(
|
|
1948
|
+
nodeIndex: NodeIndex,
|
|
1949
|
+
direction: Direction
|
|
1950
|
+
): <N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => Array<NodeIndex>
|
|
1951
|
+
<N, E>(
|
|
1952
|
+
graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">,
|
|
1953
|
+
nodeIndex: NodeIndex,
|
|
1954
|
+
direction: Direction
|
|
1955
|
+
): Array<NodeIndex>
|
|
1956
|
+
} = dual(3, <N, E, T extends Kind = "directed">(
|
|
1957
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
1958
|
+
nodeIndex: NodeIndex,
|
|
1959
|
+
direction: Direction
|
|
1960
|
+
): Array<NodeIndex> => {
|
|
1961
|
+
if (graph.type === "undirected") {
|
|
1962
|
+
throw new GraphError({ message: "Cannot get directed neighbors of undirected graph" })
|
|
1963
|
+
}
|
|
1964
|
+
return getDirectedNeighbors(graph as Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, nodeIndex, direction)
|
|
1965
|
+
})
|
|
1966
|
+
|
|
1967
|
+
// =============================================================================
|
|
1968
|
+
// GraphViz Export
|
|
1969
|
+
// =============================================================================
|
|
1970
|
+
|
|
1971
|
+
/**
|
|
1972
|
+
* Configuration options for GraphViz DOT format generation from graphs.
|
|
1973
|
+
*
|
|
1974
|
+
* **Details**
|
|
1975
|
+
*
|
|
1976
|
+
* These options customize node labels, edge labels, and graph naming in DOT
|
|
1977
|
+
* format compatible with GraphViz tools.
|
|
1978
|
+
*
|
|
1979
|
+
* **Example** (Configuring GraphViz labels)
|
|
1980
|
+
*
|
|
1981
|
+
* ```ts
|
|
1982
|
+
* import type { Graph } from "effect"
|
|
1983
|
+
*
|
|
1984
|
+
* // Basic options with custom labels
|
|
1985
|
+
* const basicOptions: Graph.GraphVizOptions<string, number> = {
|
|
1986
|
+
* nodeLabel: (data) => `Node: ${data}`,
|
|
1987
|
+
* edgeLabel: (data) => `Weight: ${data}`
|
|
1988
|
+
* }
|
|
1989
|
+
*
|
|
1990
|
+
* // Complete options with graph naming
|
|
1991
|
+
* const namedOptions: Graph.GraphVizOptions<string, string> = {
|
|
1992
|
+
* nodeLabel: (data) => data.toUpperCase(),
|
|
1993
|
+
* edgeLabel: (data) => data,
|
|
1994
|
+
* graphName: "MyDependencyGraph"
|
|
1995
|
+
* }
|
|
1996
|
+
* ```
|
|
1997
|
+
*
|
|
1998
|
+
* @category options
|
|
1999
|
+
* @since 3.18.0
|
|
2000
|
+
*/
|
|
2001
|
+
export interface GraphVizOptions<N, E> {
|
|
2002
|
+
/**
|
|
2003
|
+
* Function to generate custom labels for nodes.
|
|
2004
|
+
* Defaults to String(data) if not provided.
|
|
2005
|
+
*/
|
|
2006
|
+
readonly nodeLabel?: (data: N) => string
|
|
2007
|
+
|
|
2008
|
+
/**
|
|
2009
|
+
* Function to generate custom labels for edges.
|
|
2010
|
+
* Defaults to String(data) if not provided.
|
|
2011
|
+
*/
|
|
2012
|
+
readonly edgeLabel?: (data: E) => string
|
|
2013
|
+
|
|
2014
|
+
/**
|
|
2015
|
+
* Name for the DOT graph.
|
|
2016
|
+
* Defaults to "G" if not provided.
|
|
2017
|
+
*/
|
|
2018
|
+
readonly graphName?: string
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
/**
|
|
2022
|
+
* Exports a graph to GraphViz DOT format for visualization.
|
|
2023
|
+
*
|
|
2024
|
+
* **Example** (Exporting GraphViz DOT)
|
|
2025
|
+
*
|
|
2026
|
+
* ```ts
|
|
2027
|
+
* import { Graph } from "effect"
|
|
2028
|
+
*
|
|
2029
|
+
* const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
|
|
2030
|
+
* const nodeA = Graph.addNode(mutable, "Node A")
|
|
2031
|
+
* const nodeB = Graph.addNode(mutable, "Node B")
|
|
2032
|
+
* const nodeC = Graph.addNode(mutable, "Node C")
|
|
2033
|
+
* Graph.addEdge(mutable, nodeA, nodeB, 1)
|
|
2034
|
+
* Graph.addEdge(mutable, nodeB, nodeC, 2)
|
|
2035
|
+
* Graph.addEdge(mutable, nodeC, nodeA, 3)
|
|
2036
|
+
* })
|
|
2037
|
+
*
|
|
2038
|
+
* const dot = Graph.toGraphViz(graph)
|
|
2039
|
+
* console.log(dot)
|
|
2040
|
+
* // digraph G {
|
|
2041
|
+
* // "0" [label="Node A"];
|
|
2042
|
+
* // "1" [label="Node B"];
|
|
2043
|
+
* // "2" [label="Node C"];
|
|
2044
|
+
* // "0" -> "1" [label="1"];
|
|
2045
|
+
* // "1" -> "2" [label="2"];
|
|
2046
|
+
* // "2" -> "0" [label="3"];
|
|
2047
|
+
* // }
|
|
2048
|
+
* ```
|
|
2049
|
+
*
|
|
2050
|
+
* @category converting
|
|
2051
|
+
* @since 3.18.0
|
|
2052
|
+
*/
|
|
2053
|
+
export const toGraphViz: {
|
|
2054
|
+
<N, E>(
|
|
2055
|
+
options?: GraphVizOptions<N, E>
|
|
2056
|
+
): <T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => string
|
|
2057
|
+
<N, E, T extends Kind = "directed">(
|
|
2058
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
2059
|
+
options?: GraphVizOptions<N, E>
|
|
2060
|
+
): string
|
|
2061
|
+
} = dual((args) => isGraph(args[0]), <N, E, T extends Kind = "directed">(
|
|
2062
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
2063
|
+
options?: GraphVizOptions<N, E>
|
|
2064
|
+
): string => {
|
|
2065
|
+
const {
|
|
2066
|
+
edgeLabel = (data: E) => String(data),
|
|
2067
|
+
graphName = "G",
|
|
2068
|
+
nodeLabel = (data: N) => String(data)
|
|
2069
|
+
} = options ?? {}
|
|
2070
|
+
|
|
2071
|
+
const isDirected = graph.type === "directed"
|
|
2072
|
+
const graphType = isDirected ? "digraph" : "graph"
|
|
2073
|
+
const edgeOperator = isDirected ? "->" : "--"
|
|
2074
|
+
|
|
2075
|
+
const lines: Array<string> = []
|
|
2076
|
+
lines.push(`${graphType} ${graphName} {`)
|
|
2077
|
+
|
|
2078
|
+
// Add nodes
|
|
2079
|
+
for (const [nodeIndex, nodeData] of graph.nodes) {
|
|
2080
|
+
const label = nodeLabel(nodeData).replace(/"/g, "\\\"")
|
|
2081
|
+
lines.push(` "${nodeIndex}" [label="${label}"];`)
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
// Add edges
|
|
2085
|
+
for (const [, edgeData] of graph.edges) {
|
|
2086
|
+
const label = edgeLabel(edgeData.data).replace(/"/g, "\\\"")
|
|
2087
|
+
lines.push(` "${edgeData.source}" ${edgeOperator} "${edgeData.target}" [label="${label}"];`)
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
lines.push("}")
|
|
2091
|
+
return lines.join("\n")
|
|
2092
|
+
})
|
|
2093
|
+
|
|
2094
|
+
// =============================================================================
|
|
2095
|
+
// Mermaid Export
|
|
2096
|
+
// =============================================================================
|
|
2097
|
+
|
|
2098
|
+
/**
|
|
2099
|
+
* Mermaid node shape types for diagram visualization.
|
|
2100
|
+
*
|
|
2101
|
+
* **Details**
|
|
2102
|
+
*
|
|
2103
|
+
* Each shape produces different visual representations in Mermaid diagrams:
|
|
2104
|
+
* - `rectangle`: Standard rectangular nodes `A["label"]`
|
|
2105
|
+
* - `rounded`: Rounded rectangular nodes `A("label")`
|
|
2106
|
+
* - `circle`: Circular nodes `A(("label"))`
|
|
2107
|
+
* - `diamond`: Diamond-shaped nodes `A{"label"}`
|
|
2108
|
+
* - `hexagon`: Hexagonal nodes `A{{"label"}}`
|
|
2109
|
+
* - `stadium`: Stadium-shaped nodes `A(["label"])`
|
|
2110
|
+
* - `subroutine`: Subroutine-style nodes `A[["label"]]`
|
|
2111
|
+
* - `cylindrical`: Cylindrical database-style nodes `A[("label")]`
|
|
2112
|
+
*
|
|
2113
|
+
* **Example** (Selecting Mermaid node shapes)
|
|
2114
|
+
*
|
|
2115
|
+
* ```ts
|
|
2116
|
+
* import type { Graph } from "effect"
|
|
2117
|
+
*
|
|
2118
|
+
* // Shape selector function for different node types
|
|
2119
|
+
* const shapeSelector = (nodeData: string): Graph.MermaidNodeShape => {
|
|
2120
|
+
* if (nodeData.includes("start") || nodeData.includes("end")) return "circle"
|
|
2121
|
+
* if (nodeData.includes("decision")) return "diamond"
|
|
2122
|
+
* if (nodeData.includes("process")) return "rectangle"
|
|
2123
|
+
* if (nodeData.includes("data")) return "cylindrical"
|
|
2124
|
+
* return "rounded"
|
|
2125
|
+
* }
|
|
2126
|
+
*
|
|
2127
|
+
* const options: Graph.MermaidOptions<string, string> = {
|
|
2128
|
+
* nodeShape: shapeSelector
|
|
2129
|
+
* }
|
|
2130
|
+
* ```
|
|
2131
|
+
*
|
|
2132
|
+
* @category models
|
|
2133
|
+
* @since 3.18.0
|
|
2134
|
+
*/
|
|
2135
|
+
export type MermaidNodeShape =
|
|
2136
|
+
| "rectangle" // A["label"]
|
|
2137
|
+
| "rounded" // A("label")
|
|
2138
|
+
| "circle" // A(("label"))
|
|
2139
|
+
| "diamond" // A{"label"}
|
|
2140
|
+
| "hexagon" // A{{"label"}}
|
|
2141
|
+
| "stadium" // A(["label"])
|
|
2142
|
+
| "subroutine" // A[["label"]]
|
|
2143
|
+
| "cylindrical" // A[("label")]
|
|
2144
|
+
|
|
2145
|
+
/**
|
|
2146
|
+
* Mermaid diagram direction types for controlling layout orientation.
|
|
2147
|
+
*
|
|
2148
|
+
* **Details**
|
|
2149
|
+
*
|
|
2150
|
+
* Determines the flow direction of nodes and edges in the diagram:
|
|
2151
|
+
* - `TB`/`TD`: Top to Bottom (vertical layout, default)
|
|
2152
|
+
* - `BT`: Bottom to Top (reverse vertical)
|
|
2153
|
+
* - `LR`: Left to Right (horizontal layout)
|
|
2154
|
+
* - `RL`: Right to Left (reverse horizontal)
|
|
2155
|
+
*
|
|
2156
|
+
* **Example** (Configuring Mermaid directions)
|
|
2157
|
+
*
|
|
2158
|
+
* ```ts
|
|
2159
|
+
* import type { Graph } from "effect"
|
|
2160
|
+
*
|
|
2161
|
+
* // Horizontal workflow diagram
|
|
2162
|
+
* const horizontalOptions: Graph.MermaidOptions<string, string> = {
|
|
2163
|
+
* direction: "LR"
|
|
2164
|
+
* }
|
|
2165
|
+
*
|
|
2166
|
+
* // Vertical hierarchy (default)
|
|
2167
|
+
* const verticalOptions: Graph.MermaidOptions<string, string> = {
|
|
2168
|
+
* direction: "TB"
|
|
2169
|
+
* }
|
|
2170
|
+
*
|
|
2171
|
+
* // Bottom-up flow
|
|
2172
|
+
* const bottomUpOptions: Graph.MermaidOptions<string, string> = {
|
|
2173
|
+
* direction: "BT"
|
|
2174
|
+
* }
|
|
2175
|
+
* ```
|
|
2176
|
+
*
|
|
2177
|
+
* @category models
|
|
2178
|
+
* @since 3.18.0
|
|
2179
|
+
*/
|
|
2180
|
+
export type MermaidDirection =
|
|
2181
|
+
| "TB" // Top to Bottom (default)
|
|
2182
|
+
| "TD" // Top Down (same as TB)
|
|
2183
|
+
| "BT" // Bottom to Top
|
|
2184
|
+
| "RL" // Right to Left
|
|
2185
|
+
| "LR" // Left to Right
|
|
2186
|
+
|
|
2187
|
+
/**
|
|
2188
|
+
* Mermaid diagram types for different visualization formats.
|
|
2189
|
+
*
|
|
2190
|
+
* **Details**
|
|
2191
|
+
*
|
|
2192
|
+
* Specifies the Mermaid diagram syntax to use:
|
|
2193
|
+
* - `flowchart`: For directed graphs with arrows (`A --> B`)
|
|
2194
|
+
* - `graph`: For undirected graphs with lines (`A --- B`)
|
|
2195
|
+
*
|
|
2196
|
+
* When not specified, automatically selects based on graph type:
|
|
2197
|
+
* directed graphs use "flowchart", undirected graphs use "graph".
|
|
2198
|
+
*
|
|
2199
|
+
* **Example** (Selecting Mermaid diagram types)
|
|
2200
|
+
*
|
|
2201
|
+
* ```ts
|
|
2202
|
+
* import type { Graph } from "effect"
|
|
2203
|
+
*
|
|
2204
|
+
* // Force flowchart format (even for undirected graphs)
|
|
2205
|
+
* const flowchartOptions: Graph.MermaidOptions<string, string> = {
|
|
2206
|
+
* diagramType: "flowchart"
|
|
2207
|
+
* }
|
|
2208
|
+
*
|
|
2209
|
+
* // Force graph format (shows undirected connections)
|
|
2210
|
+
* const graphOptions: Graph.MermaidOptions<string, string> = {
|
|
2211
|
+
* diagramType: "graph"
|
|
2212
|
+
* }
|
|
2213
|
+
*
|
|
2214
|
+
* // Auto-detection (recommended, default behavior)
|
|
2215
|
+
* const autoOptions: Graph.MermaidOptions<string, string> = {}
|
|
2216
|
+
* ```
|
|
2217
|
+
*
|
|
2218
|
+
* @category models
|
|
2219
|
+
* @since 3.18.0
|
|
2220
|
+
*/
|
|
2221
|
+
export type MermaidDiagramType =
|
|
2222
|
+
| "flowchart" // For directed graphs
|
|
2223
|
+
| "graph" // For undirected graphs
|
|
2224
|
+
|
|
2225
|
+
/**
|
|
2226
|
+
* Configuration options for Mermaid diagram generation, following GraphViz pattern.
|
|
2227
|
+
*
|
|
2228
|
+
* @category models
|
|
2229
|
+
* @since 4.0.0
|
|
2230
|
+
*/
|
|
2231
|
+
/**
|
|
2232
|
+
* Configuration options for Mermaid diagram generation from graphs.
|
|
2233
|
+
*
|
|
2234
|
+
* **Details**
|
|
2235
|
+
*
|
|
2236
|
+
* These options customize node labels, edge labels, diagram type, layout
|
|
2237
|
+
* direction, node shapes, and graph naming in Mermaid format.
|
|
2238
|
+
*
|
|
2239
|
+
* **Example** (Configuring Mermaid output)
|
|
2240
|
+
*
|
|
2241
|
+
* ```ts
|
|
2242
|
+
* import type { Graph } from "effect"
|
|
2243
|
+
*
|
|
2244
|
+
* // Basic options with custom labels
|
|
2245
|
+
* const basicOptions: Graph.MermaidOptions<string, number> = {
|
|
2246
|
+
* nodeLabel: (data) => `Node: ${data}`,
|
|
2247
|
+
* edgeLabel: (data) => `Weight: ${data}`
|
|
2248
|
+
* }
|
|
2249
|
+
*
|
|
2250
|
+
* // Advanced options with all features
|
|
2251
|
+
* const advancedOptions: Graph.MermaidOptions<string, string> = {
|
|
2252
|
+
* nodeLabel: (data) => data.toUpperCase(),
|
|
2253
|
+
* edgeLabel: (data) => data,
|
|
2254
|
+
* diagramType: "flowchart",
|
|
2255
|
+
* direction: "LR",
|
|
2256
|
+
* nodeShape: (data) => data.includes("start") ? "circle" : "rectangle"
|
|
2257
|
+
* }
|
|
2258
|
+
* ```
|
|
2259
|
+
*
|
|
2260
|
+
* @category options
|
|
2261
|
+
* @since 3.18.0
|
|
2262
|
+
*/
|
|
2263
|
+
export interface MermaidOptions<N, E> {
|
|
2264
|
+
/**
|
|
2265
|
+
* Function to generate custom labels for nodes.
|
|
2266
|
+
* Defaults to String(data) if not provided.
|
|
2267
|
+
*/
|
|
2268
|
+
readonly nodeLabel?: (data: N) => string
|
|
2269
|
+
|
|
2270
|
+
/**
|
|
2271
|
+
* Function to generate custom labels for edges.
|
|
2272
|
+
* Defaults to String(data) if not provided.
|
|
2273
|
+
*/
|
|
2274
|
+
readonly edgeLabel?: (data: E) => string
|
|
2275
|
+
|
|
2276
|
+
/**
|
|
2277
|
+
* Diagram type override. If not specified, automatically detects:
|
|
2278
|
+
* - "flowchart" for directed graphs
|
|
2279
|
+
* - "graph" for undirected graphs
|
|
2280
|
+
*/
|
|
2281
|
+
readonly diagramType?: MermaidDiagramType
|
|
2282
|
+
|
|
2283
|
+
/**
|
|
2284
|
+
* Direction for diagram layout.
|
|
2285
|
+
* Defaults to "TD" (Top Down) if not provided.
|
|
2286
|
+
*/
|
|
2287
|
+
readonly direction?: MermaidDirection
|
|
2288
|
+
|
|
2289
|
+
/**
|
|
2290
|
+
* Function to determine node shape for each node.
|
|
2291
|
+
* Defaults to "rectangle" for all nodes if not provided.
|
|
2292
|
+
*/
|
|
2293
|
+
readonly nodeShape?: (data: N) => MermaidNodeShape
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
/**
|
|
2297
|
+
* Escapes special characters in labels for Mermaid syntax compatibility.
|
|
2298
|
+
*/
|
|
2299
|
+
const escapeMermaidLabel = (label: string): string => {
|
|
2300
|
+
// Escape special characters for Mermaid using HTML entity codes
|
|
2301
|
+
// According to: https://mermaid.js.org/syntax/flowchart.html#special-characters-that-break-syntax
|
|
2302
|
+
return label
|
|
2303
|
+
.replace(/#/g, "#35;")
|
|
2304
|
+
.replace(/"/g, "#quot;")
|
|
2305
|
+
.replace(/</g, "#lt;")
|
|
2306
|
+
.replace(/>/g, "#gt;")
|
|
2307
|
+
.replace(/&/g, "#amp;")
|
|
2308
|
+
.replace(/\[/g, "#91;")
|
|
2309
|
+
.replace(/\]/g, "#93;")
|
|
2310
|
+
.replace(/\{/g, "#123;")
|
|
2311
|
+
.replace(/\}/g, "#125;")
|
|
2312
|
+
.replace(/\(/g, "#40;")
|
|
2313
|
+
.replace(/\)/g, "#41;")
|
|
2314
|
+
.replace(/\|/g, "#124;")
|
|
2315
|
+
.replace(/\\/g, "#92;")
|
|
2316
|
+
.replace(/\n/g, "<br/>")
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
/**
|
|
2320
|
+
* Formats a Mermaid node with the specified shape and label.
|
|
2321
|
+
*/
|
|
2322
|
+
const formatMermaidNode = (
|
|
2323
|
+
nodeId: string,
|
|
2324
|
+
label: string,
|
|
2325
|
+
shape: MermaidNodeShape
|
|
2326
|
+
): string => {
|
|
2327
|
+
switch (shape) {
|
|
2328
|
+
case "rectangle":
|
|
2329
|
+
return `${nodeId}["${label}"]`
|
|
2330
|
+
case "rounded":
|
|
2331
|
+
return `${nodeId}("${label}")`
|
|
2332
|
+
case "circle":
|
|
2333
|
+
return `${nodeId}(("${label}"))`
|
|
2334
|
+
case "diamond":
|
|
2335
|
+
return `${nodeId}{"${label}"}`
|
|
2336
|
+
case "hexagon":
|
|
2337
|
+
return `${nodeId}{{"${label}"}}`
|
|
2338
|
+
case "stadium":
|
|
2339
|
+
return `${nodeId}(["${label}"])`
|
|
2340
|
+
case "subroutine":
|
|
2341
|
+
return `${nodeId}[["${label}"]]`
|
|
2342
|
+
case "cylindrical":
|
|
2343
|
+
return `${nodeId}[("${label}")]`
|
|
2344
|
+
default:
|
|
2345
|
+
return `${nodeId}["${label}"]` // Default rectangle
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
/**
|
|
2350
|
+
* Exports a graph to Mermaid diagram format for visualization.
|
|
2351
|
+
*
|
|
2352
|
+
* **Details**
|
|
2353
|
+
*
|
|
2354
|
+
* Mermaid is a popular diagram-as-code tool that generates flowcharts and other
|
|
2355
|
+
* visualizations from text-based definitions. This function converts Effect Graph
|
|
2356
|
+
* structures to valid Mermaid syntax for use in documentation, web applications,
|
|
2357
|
+
* and visualization tools.
|
|
2358
|
+
*
|
|
2359
|
+
* **Example** (Exporting a directed Mermaid diagram)
|
|
2360
|
+
*
|
|
2361
|
+
* ```ts
|
|
2362
|
+
* import { Graph } from "effect"
|
|
2363
|
+
*
|
|
2364
|
+
* // Basic directed graph export
|
|
2365
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
2366
|
+
* const app = Graph.addNode(mutable, "App")
|
|
2367
|
+
* const db = Graph.addNode(mutable, "Database")
|
|
2368
|
+
* const cache = Graph.addNode(mutable, "Cache")
|
|
2369
|
+
* Graph.addEdge(mutable, app, db, 1)
|
|
2370
|
+
* Graph.addEdge(mutable, app, cache, 2)
|
|
2371
|
+
* })
|
|
2372
|
+
*
|
|
2373
|
+
* const mermaid = Graph.toMermaid(graph)
|
|
2374
|
+
* console.log(mermaid)
|
|
2375
|
+
* // flowchart TD
|
|
2376
|
+
* // 0["App"]
|
|
2377
|
+
* // 1["Database"]
|
|
2378
|
+
* // 2["Cache"]
|
|
2379
|
+
* // 0 -->|"1"| 1
|
|
2380
|
+
* // 0 -->|"2"| 2
|
|
2381
|
+
* ```
|
|
2382
|
+
*
|
|
2383
|
+
* **Example** (Exporting an undirected Mermaid diagram)
|
|
2384
|
+
*
|
|
2385
|
+
* ```ts
|
|
2386
|
+
* import { Graph } from "effect"
|
|
2387
|
+
*
|
|
2388
|
+
* // Undirected graph with custom labels and direction
|
|
2389
|
+
* const socialGraph = Graph.undirected<{ name: string }, string>((mutable) => {
|
|
2390
|
+
* const alice = Graph.addNode(mutable, { name: "Alice" })
|
|
2391
|
+
* const bob = Graph.addNode(mutable, { name: "Bob" })
|
|
2392
|
+
* const charlie = Graph.addNode(mutable, { name: "Charlie" })
|
|
2393
|
+
* Graph.addEdge(mutable, alice, bob, "friends")
|
|
2394
|
+
* Graph.addEdge(mutable, bob, charlie, "colleagues")
|
|
2395
|
+
* })
|
|
2396
|
+
*
|
|
2397
|
+
* const mermaid = Graph.toMermaid(socialGraph, {
|
|
2398
|
+
* nodeLabel: (person) => person.name,
|
|
2399
|
+
* edgeLabel: (relationship) => relationship,
|
|
2400
|
+
* direction: "LR"
|
|
2401
|
+
* })
|
|
2402
|
+
* console.log(mermaid)
|
|
2403
|
+
* // graph LR
|
|
2404
|
+
* // 0["Alice"]
|
|
2405
|
+
* // 1["Bob"]
|
|
2406
|
+
* // 2["Charlie"]
|
|
2407
|
+
* // 0 ---|"friends"| 1
|
|
2408
|
+
* // 1 ---|"colleagues"| 2
|
|
2409
|
+
* ```
|
|
2410
|
+
*
|
|
2411
|
+
* **Example** (Customizing Mermaid node shapes)
|
|
2412
|
+
*
|
|
2413
|
+
* ```ts
|
|
2414
|
+
* import { Graph } from "effect"
|
|
2415
|
+
*
|
|
2416
|
+
* // Advanced styling with node shapes for flowchart
|
|
2417
|
+
* const workflow = Graph.directed<{ type: string; name: string }, string>(
|
|
2418
|
+
* (mutable) => {
|
|
2419
|
+
* const start = Graph.addNode(mutable, { type: "start", name: "Begin" })
|
|
2420
|
+
* const process = Graph.addNode(mutable, {
|
|
2421
|
+
* type: "process",
|
|
2422
|
+
* name: "Process Data"
|
|
2423
|
+
* })
|
|
2424
|
+
* const decision = Graph.addNode(mutable, {
|
|
2425
|
+
* type: "decision",
|
|
2426
|
+
* name: "Valid?"
|
|
2427
|
+
* })
|
|
2428
|
+
* const end = Graph.addNode(mutable, { type: "end", name: "Complete" })
|
|
2429
|
+
* Graph.addEdge(mutable, start, process, "")
|
|
2430
|
+
* Graph.addEdge(mutable, process, decision, "")
|
|
2431
|
+
* Graph.addEdge(mutable, decision, end, "yes")
|
|
2432
|
+
* }
|
|
2433
|
+
* )
|
|
2434
|
+
*
|
|
2435
|
+
* const mermaid = Graph.toMermaid(workflow, {
|
|
2436
|
+
* nodeLabel: (node) => node.name,
|
|
2437
|
+
* nodeShape: (node) => {
|
|
2438
|
+
* switch (node.type) {
|
|
2439
|
+
* case "start":
|
|
2440
|
+
* return "stadium"
|
|
2441
|
+
* case "process":
|
|
2442
|
+
* return "rectangle"
|
|
2443
|
+
* case "decision":
|
|
2444
|
+
* return "diamond"
|
|
2445
|
+
* case "end":
|
|
2446
|
+
* return "stadium"
|
|
2447
|
+
* default:
|
|
2448
|
+
* return "rectangle"
|
|
2449
|
+
* }
|
|
2450
|
+
* }
|
|
2451
|
+
* })
|
|
2452
|
+
* console.log(mermaid)
|
|
2453
|
+
* // flowchart TD
|
|
2454
|
+
* // 0(["Begin"])
|
|
2455
|
+
* // 1["Process Data"]
|
|
2456
|
+
* // 2{"Valid?"}
|
|
2457
|
+
* // 3(["Complete"])
|
|
2458
|
+
* // 0 --> 1
|
|
2459
|
+
* // 1 --> 2
|
|
2460
|
+
* // 2 --> 3
|
|
2461
|
+
* ```
|
|
2462
|
+
*
|
|
2463
|
+
* **Example** (Visualizing dependency graphs)
|
|
2464
|
+
*
|
|
2465
|
+
* ```ts
|
|
2466
|
+
* import { Graph } from "effect"
|
|
2467
|
+
*
|
|
2468
|
+
* // Real-world example: Software dependency graph
|
|
2469
|
+
* interface Dependency {
|
|
2470
|
+
* name: string
|
|
2471
|
+
* version: string
|
|
2472
|
+
* type: "library" | "framework" | "tool"
|
|
2473
|
+
* }
|
|
2474
|
+
*
|
|
2475
|
+
* const dependencyGraph = Graph.directed<Dependency, string>((mutable) => {
|
|
2476
|
+
* const app = Graph.addNode(mutable, {
|
|
2477
|
+
* name: "MyApp",
|
|
2478
|
+
* version: "1.0.0",
|
|
2479
|
+
* type: "library"
|
|
2480
|
+
* })
|
|
2481
|
+
* const react = Graph.addNode(mutable, {
|
|
2482
|
+
* name: "React",
|
|
2483
|
+
* version: "18.0.0",
|
|
2484
|
+
* type: "framework"
|
|
2485
|
+
* })
|
|
2486
|
+
* const lodash = Graph.addNode(mutable, {
|
|
2487
|
+
* name: "Lodash",
|
|
2488
|
+
* version: "4.17.0",
|
|
2489
|
+
* type: "library"
|
|
2490
|
+
* })
|
|
2491
|
+
* const webpack = Graph.addNode(mutable, {
|
|
2492
|
+
* name: "Webpack",
|
|
2493
|
+
* version: "5.0.0",
|
|
2494
|
+
* type: "tool"
|
|
2495
|
+
* })
|
|
2496
|
+
*
|
|
2497
|
+
* Graph.addEdge(mutable, app, react, "depends on")
|
|
2498
|
+
* Graph.addEdge(mutable, app, lodash, "depends on")
|
|
2499
|
+
* Graph.addEdge(mutable, app, webpack, "builds with")
|
|
2500
|
+
* })
|
|
2501
|
+
*
|
|
2502
|
+
* const dependencyDiagram = Graph.toMermaid(dependencyGraph, {
|
|
2503
|
+
* nodeLabel: (dep) => `${dep.name}\\nv${dep.version}`,
|
|
2504
|
+
* edgeLabel: (edge) => edge,
|
|
2505
|
+
* nodeShape: (dep) =>
|
|
2506
|
+
* dep.type === "framework" ?
|
|
2507
|
+
* "hexagon" :
|
|
2508
|
+
* dep.type === "tool"
|
|
2509
|
+
* ? "diamond"
|
|
2510
|
+
* : "rectangle",
|
|
2511
|
+
* direction: "TB"
|
|
2512
|
+
* })
|
|
2513
|
+
*
|
|
2514
|
+
* console.log(dependencyDiagram)
|
|
2515
|
+
* // flowchart TB
|
|
2516
|
+
* // 0["MyApp\nv1.0.0"]
|
|
2517
|
+
* // 1{{"React\nv18.0.0"}}
|
|
2518
|
+
* // 2["Lodash\nv4.17.0"]
|
|
2519
|
+
* // 3{"Webpack\nv5.0.0"}
|
|
2520
|
+
* // 0 -->|"depends on"| 1
|
|
2521
|
+
* // 0 -->|"depends on"| 2
|
|
2522
|
+
* // 0 -->|"builds with"| 3
|
|
2523
|
+
* ```
|
|
2524
|
+
*
|
|
2525
|
+
* @category converting
|
|
2526
|
+
* @since 3.18.0
|
|
2527
|
+
*/
|
|
2528
|
+
export const toMermaid: {
|
|
2529
|
+
<N, E>(
|
|
2530
|
+
options?: MermaidOptions<N, E>
|
|
2531
|
+
): <T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => string
|
|
2532
|
+
<N, E, T extends Kind = "directed">(
|
|
2533
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
2534
|
+
options?: MermaidOptions<N, E>
|
|
2535
|
+
): string
|
|
2536
|
+
} = dual((args) => isGraph(args[0]), <N, E, T extends Kind = "directed">(
|
|
2537
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
2538
|
+
options?: MermaidOptions<N, E>
|
|
2539
|
+
): string => {
|
|
2540
|
+
// Extract and validate options with defaults
|
|
2541
|
+
const {
|
|
2542
|
+
diagramType,
|
|
2543
|
+
direction = "TD",
|
|
2544
|
+
edgeLabel = (data: E) => String(data),
|
|
2545
|
+
nodeLabel = (data: N) => String(data),
|
|
2546
|
+
nodeShape = () => "rectangle" as const
|
|
2547
|
+
} = options ?? {}
|
|
2548
|
+
|
|
2549
|
+
// Auto-detect diagram type if not specified
|
|
2550
|
+
const finalDiagramType = diagramType ??
|
|
2551
|
+
(graph.type === "directed" ? "flowchart" : "graph")
|
|
2552
|
+
|
|
2553
|
+
// Generate diagram header
|
|
2554
|
+
const lines: Array<string> = []
|
|
2555
|
+
lines.push(`${finalDiagramType} ${direction}`)
|
|
2556
|
+
|
|
2557
|
+
// Add nodes
|
|
2558
|
+
for (const [nodeIndex, nodeData] of graph.nodes) {
|
|
2559
|
+
const nodeId = String(nodeIndex)
|
|
2560
|
+
const label = escapeMermaidLabel(nodeLabel(nodeData))
|
|
2561
|
+
const shape = nodeShape(nodeData)
|
|
2562
|
+
const formattedNode = formatMermaidNode(nodeId, label, shape)
|
|
2563
|
+
lines.push(` ${formattedNode}`)
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
// Add edges
|
|
2567
|
+
const edgeOperator = finalDiagramType === "flowchart" ? "-->" : "---"
|
|
2568
|
+
for (const [, edgeData] of graph.edges) {
|
|
2569
|
+
const sourceId = String(edgeData.source)
|
|
2570
|
+
const targetId = String(edgeData.target)
|
|
2571
|
+
const label = escapeMermaidLabel(edgeLabel(edgeData.data))
|
|
2572
|
+
|
|
2573
|
+
if (label) {
|
|
2574
|
+
lines.push(` ${sourceId} ${edgeOperator}|"${label}"| ${targetId}`)
|
|
2575
|
+
} else {
|
|
2576
|
+
lines.push(` ${sourceId} ${edgeOperator} ${targetId}`)
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
return lines.join("\n")
|
|
2581
|
+
})
|
|
2582
|
+
|
|
2583
|
+
// =============================================================================
|
|
2584
|
+
// Direction Types for Bidirectional Traversal
|
|
2585
|
+
// =============================================================================
|
|
2586
|
+
|
|
2587
|
+
/**
|
|
2588
|
+
* Direction for graph traversal, indicating which edges to follow.
|
|
2589
|
+
*
|
|
2590
|
+
* **Example** (Traversing by direction)
|
|
2591
|
+
*
|
|
2592
|
+
* ```ts
|
|
2593
|
+
* import { Graph } from "effect"
|
|
2594
|
+
*
|
|
2595
|
+
* const graph = Graph.directed<string, string>((mutable) => {
|
|
2596
|
+
* const a = Graph.addNode(mutable, "A")
|
|
2597
|
+
* const b = Graph.addNode(mutable, "B")
|
|
2598
|
+
* Graph.addEdge(mutable, a, b, "A->B")
|
|
2599
|
+
* })
|
|
2600
|
+
*
|
|
2601
|
+
* // Follow outgoing edges (normal direction)
|
|
2602
|
+
* const outgoingNodes = Array.from(
|
|
2603
|
+
* Graph.indices(Graph.dfs(graph, { start: [0], direction: "outgoing" }))
|
|
2604
|
+
* )
|
|
2605
|
+
*
|
|
2606
|
+
* // Follow incoming edges (reverse direction)
|
|
2607
|
+
* const incomingNodes = Array.from(
|
|
2608
|
+
* Graph.indices(Graph.dfs(graph, { start: [1], direction: "incoming" }))
|
|
2609
|
+
* )
|
|
2610
|
+
* ```
|
|
2611
|
+
*
|
|
2612
|
+
* @category models
|
|
2613
|
+
* @since 3.18.0
|
|
2614
|
+
*/
|
|
2615
|
+
export type Direction = "outgoing" | "incoming"
|
|
2616
|
+
|
|
2617
|
+
// =============================================================================
|
|
2618
|
+
// Graph Structure Analysis Algorithms
|
|
2619
|
+
// =============================================================================
|
|
2620
|
+
|
|
2621
|
+
/**
|
|
2622
|
+
* Checks whether the graph is acyclic (contains no cycles).
|
|
2623
|
+
*
|
|
2624
|
+
* **Details**
|
|
2625
|
+
*
|
|
2626
|
+
* Uses depth-first search to detect back edges, which indicate cycles.
|
|
2627
|
+
* For directed graphs, any back edge creates a cycle. For undirected graphs,
|
|
2628
|
+
* a back edge that doesn't go to the immediate parent creates a cycle.
|
|
2629
|
+
*
|
|
2630
|
+
* **Example** (Checking cycles)
|
|
2631
|
+
*
|
|
2632
|
+
* ```ts
|
|
2633
|
+
* import { Graph } from "effect"
|
|
2634
|
+
*
|
|
2635
|
+
* // Acyclic directed graph (DAG)
|
|
2636
|
+
* const dag = Graph.directed<string, string>((mutable) => {
|
|
2637
|
+
* const a = Graph.addNode(mutable, "A")
|
|
2638
|
+
* const b = Graph.addNode(mutable, "B")
|
|
2639
|
+
* const c = Graph.addNode(mutable, "C")
|
|
2640
|
+
* Graph.addEdge(mutable, a, b, "A->B")
|
|
2641
|
+
* Graph.addEdge(mutable, b, c, "B->C")
|
|
2642
|
+
* })
|
|
2643
|
+
* console.log(Graph.isAcyclic(dag)) // true
|
|
2644
|
+
*
|
|
2645
|
+
* // Cyclic directed graph
|
|
2646
|
+
* const cyclic = Graph.directed<string, string>((mutable) => {
|
|
2647
|
+
* const a = Graph.addNode(mutable, "A")
|
|
2648
|
+
* const b = Graph.addNode(mutable, "B")
|
|
2649
|
+
* Graph.addEdge(mutable, a, b, "A->B")
|
|
2650
|
+
* Graph.addEdge(mutable, b, a, "B->A") // Creates cycle
|
|
2651
|
+
* })
|
|
2652
|
+
* console.log(Graph.isAcyclic(cyclic)) // false
|
|
2653
|
+
* ```
|
|
2654
|
+
*
|
|
2655
|
+
* @category algorithms
|
|
2656
|
+
* @since 3.18.0
|
|
2657
|
+
*/
|
|
2658
|
+
export const isAcyclic = <N, E, T extends Kind = "directed">(
|
|
2659
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>
|
|
2660
|
+
): boolean => {
|
|
2661
|
+
// Use existing cycle flag if available
|
|
2662
|
+
if (Option.isSome(graph.acyclic)) {
|
|
2663
|
+
return graph.acyclic.value
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
if (graph.type === "undirected") {
|
|
2667
|
+
const visited = new Set<NodeIndex>()
|
|
2668
|
+
|
|
2669
|
+
for (const startNode of graph.nodes.keys()) {
|
|
2670
|
+
if (visited.has(startNode)) {
|
|
2671
|
+
continue
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2674
|
+
visited.add(startNode)
|
|
2675
|
+
const stack: Array<{ node: NodeIndex; parent: NodeIndex | null }> = [{ node: startNode, parent: null }]
|
|
2676
|
+
|
|
2677
|
+
while (stack.length > 0) {
|
|
2678
|
+
const { node, parent } = stack.pop()!
|
|
2679
|
+
const nodeNeighbors = getUndirectedNeighbors(graph as any, node)
|
|
2680
|
+
|
|
2681
|
+
for (const neighbor of nodeNeighbors) {
|
|
2682
|
+
if (!visited.has(neighbor)) {
|
|
2683
|
+
visited.add(neighbor)
|
|
2684
|
+
stack.push({ node: neighbor, parent: node })
|
|
2685
|
+
} else if (neighbor !== parent) {
|
|
2686
|
+
graph.acyclic = Option.some(false)
|
|
2687
|
+
return false
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
graph.acyclic = Option.some(true)
|
|
2694
|
+
return true
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
// Stack-safe DFS cycle detection using iterative approach
|
|
2698
|
+
const visited = new Set<NodeIndex>()
|
|
2699
|
+
const recursionStack = new Set<NodeIndex>()
|
|
2700
|
+
|
|
2701
|
+
// Stack entry: [node, neighbors, neighborIndex, isFirstVisit]
|
|
2702
|
+
type DfsStackEntry = [NodeIndex, Array<NodeIndex>, number, boolean]
|
|
2703
|
+
|
|
2704
|
+
// Get all nodes to handle disconnected components
|
|
2705
|
+
for (const startNode of graph.nodes.keys()) {
|
|
2706
|
+
if (visited.has(startNode)) {
|
|
2707
|
+
continue // Already processed this component
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2710
|
+
// Iterative DFS with explicit stack
|
|
2711
|
+
const stack: Array<DfsStackEntry> = [[startNode, [], 0, true]]
|
|
2712
|
+
|
|
2713
|
+
while (stack.length > 0) {
|
|
2714
|
+
const [node, neighbors, neighborIndex, isFirstVisit] = stack[stack.length - 1]
|
|
2715
|
+
|
|
2716
|
+
// First visit to this node
|
|
2717
|
+
if (isFirstVisit) {
|
|
2718
|
+
if (recursionStack.has(node)) {
|
|
2719
|
+
// Back edge found - cycle detected
|
|
2720
|
+
graph.acyclic = Option.some(false)
|
|
2721
|
+
return false
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
if (visited.has(node)) {
|
|
2725
|
+
stack.pop()
|
|
2726
|
+
continue
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
visited.add(node)
|
|
2730
|
+
recursionStack.add(node)
|
|
2731
|
+
|
|
2732
|
+
// Get neighbors for this node
|
|
2733
|
+
const nodeNeighbors = getDirectedNeighbors(
|
|
2734
|
+
graph as Graph<N, E, "directed"> | MutableGraph<N, E, "directed">,
|
|
2735
|
+
node,
|
|
2736
|
+
"outgoing"
|
|
2737
|
+
)
|
|
2738
|
+
stack[stack.length - 1] = [node, nodeNeighbors, 0, false]
|
|
2739
|
+
continue
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
// Process next neighbor
|
|
2743
|
+
if (neighborIndex < neighbors.length) {
|
|
2744
|
+
const neighbor = neighbors[neighborIndex]
|
|
2745
|
+
stack[stack.length - 1] = [node, neighbors, neighborIndex + 1, false]
|
|
2746
|
+
|
|
2747
|
+
if (recursionStack.has(neighbor)) {
|
|
2748
|
+
// Back edge found - cycle detected
|
|
2749
|
+
graph.acyclic = Option.some(false)
|
|
2750
|
+
return false
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
if (!visited.has(neighbor)) {
|
|
2754
|
+
stack.push([neighbor, [], 0, true])
|
|
2755
|
+
}
|
|
2756
|
+
} else {
|
|
2757
|
+
// Done with this node - backtrack
|
|
2758
|
+
recursionStack.delete(node)
|
|
2759
|
+
stack.pop()
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
// Cache the result
|
|
2765
|
+
graph.acyclic = Option.some(true)
|
|
2766
|
+
return true
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
/**
|
|
2770
|
+
* Checks whether an undirected graph is bipartite.
|
|
2771
|
+
*
|
|
2772
|
+
* **Details**
|
|
2773
|
+
*
|
|
2774
|
+
* A bipartite graph is one whose vertices can be divided into two disjoint sets
|
|
2775
|
+
* such that no two vertices within the same set are adjacent. Uses BFS coloring
|
|
2776
|
+
* to determine bipartiteness.
|
|
2777
|
+
*
|
|
2778
|
+
* **Example** (Checking bipartite graphs)
|
|
2779
|
+
*
|
|
2780
|
+
* ```ts
|
|
2781
|
+
* import { Graph } from "effect"
|
|
2782
|
+
*
|
|
2783
|
+
* // Bipartite graph (alternating coloring possible)
|
|
2784
|
+
* const bipartite = Graph.undirected<string, string>((mutable) => {
|
|
2785
|
+
* const a = Graph.addNode(mutable, "A")
|
|
2786
|
+
* const b = Graph.addNode(mutable, "B")
|
|
2787
|
+
* const c = Graph.addNode(mutable, "C")
|
|
2788
|
+
* const d = Graph.addNode(mutable, "D")
|
|
2789
|
+
* Graph.addEdge(mutable, a, b, "edge") // Set 1: {A, C}, Set 2: {B, D}
|
|
2790
|
+
* Graph.addEdge(mutable, b, c, "edge")
|
|
2791
|
+
* Graph.addEdge(mutable, c, d, "edge")
|
|
2792
|
+
* })
|
|
2793
|
+
* console.log(Graph.isBipartite(bipartite)) // true
|
|
2794
|
+
*
|
|
2795
|
+
* // Non-bipartite graph (odd cycle)
|
|
2796
|
+
* const triangle = Graph.undirected<string, string>((mutable) => {
|
|
2797
|
+
* const a = Graph.addNode(mutable, "A")
|
|
2798
|
+
* const b = Graph.addNode(mutable, "B")
|
|
2799
|
+
* const c = Graph.addNode(mutable, "C")
|
|
2800
|
+
* Graph.addEdge(mutable, a, b, "edge")
|
|
2801
|
+
* Graph.addEdge(mutable, b, c, "edge")
|
|
2802
|
+
* Graph.addEdge(mutable, c, a, "edge") // Triangle (3-cycle)
|
|
2803
|
+
* })
|
|
2804
|
+
* console.log(Graph.isBipartite(triangle)) // false
|
|
2805
|
+
* ```
|
|
2806
|
+
*
|
|
2807
|
+
* @category algorithms
|
|
2808
|
+
* @since 3.18.0
|
|
2809
|
+
*/
|
|
2810
|
+
export const isBipartite = <N, E>(
|
|
2811
|
+
graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">
|
|
2812
|
+
): boolean => {
|
|
2813
|
+
const coloring = new Map<NodeIndex, 0 | 1>()
|
|
2814
|
+
const discovered = new Set<NodeIndex>()
|
|
2815
|
+
let isBipartiteGraph = true
|
|
2816
|
+
|
|
2817
|
+
// Get all nodes to handle disconnected components
|
|
2818
|
+
for (const startNode of graph.nodes.keys()) {
|
|
2819
|
+
if (!discovered.has(startNode)) {
|
|
2820
|
+
// Start BFS coloring from this component
|
|
2821
|
+
const queue: Array<NodeIndex> = [startNode]
|
|
2822
|
+
coloring.set(startNode, 0) // Color start node with 0
|
|
2823
|
+
discovered.add(startNode)
|
|
2824
|
+
|
|
2825
|
+
while (queue.length > 0 && isBipartiteGraph) {
|
|
2826
|
+
const current = queue.shift()!
|
|
2827
|
+
const currentColor = coloring.get(current)!
|
|
2828
|
+
const neighborColor: 0 | 1 = currentColor === 0 ? 1 : 0
|
|
2829
|
+
|
|
2830
|
+
// Get all neighbors for undirected graph
|
|
2831
|
+
const nodeNeighbors = getUndirectedNeighbors(graph, current)
|
|
2832
|
+
for (const neighbor of nodeNeighbors) {
|
|
2833
|
+
if (!discovered.has(neighbor)) {
|
|
2834
|
+
// Color unvisited neighbor with opposite color
|
|
2835
|
+
coloring.set(neighbor, neighborColor)
|
|
2836
|
+
discovered.add(neighbor)
|
|
2837
|
+
queue.push(neighbor)
|
|
2838
|
+
} else {
|
|
2839
|
+
// Check if neighbor has the same color (conflict)
|
|
2840
|
+
if (coloring.get(neighbor) === currentColor) {
|
|
2841
|
+
isBipartiteGraph = false
|
|
2842
|
+
break
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
// Early exit if not bipartite
|
|
2849
|
+
if (!isBipartiteGraph) {
|
|
2850
|
+
break
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
return isBipartiteGraph
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
/**
|
|
2859
|
+
* Get neighbors for undirected graphs by checking both adjacency and reverse adjacency.
|
|
2860
|
+
* For undirected graphs, we need to find the other endpoint of each edge incident to the node.
|
|
2861
|
+
*/
|
|
2862
|
+
const getUndirectedNeighbors = <N, E>(
|
|
2863
|
+
graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">,
|
|
2864
|
+
nodeIndex: NodeIndex
|
|
2865
|
+
): Array<NodeIndex> => {
|
|
2866
|
+
const neighbors = new Set<NodeIndex>()
|
|
2867
|
+
|
|
2868
|
+
// Check edges where this node is the source
|
|
2869
|
+
const adjacencyList = graph.adjacency.get(nodeIndex)
|
|
2870
|
+
if (adjacencyList !== undefined) {
|
|
2871
|
+
for (const edgeIndex of adjacencyList) {
|
|
2872
|
+
const edge = graph.edges.get(edgeIndex)
|
|
2873
|
+
if (edge !== undefined) {
|
|
2874
|
+
// For undirected graphs, the neighbor is the other endpoint
|
|
2875
|
+
const otherNode = edge.source === nodeIndex ? edge.target : edge.source
|
|
2876
|
+
neighbors.add(otherNode)
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2881
|
+
return Array.from(neighbors)
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2884
|
+
const getTraversalNeighbors = <N, E, T extends Kind>(
|
|
2885
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
2886
|
+
nodeIndex: NodeIndex,
|
|
2887
|
+
direction: Direction
|
|
2888
|
+
): Array<NodeIndex> =>
|
|
2889
|
+
graph.type === "undirected"
|
|
2890
|
+
? getUndirectedNeighbors(graph as any, nodeIndex)
|
|
2891
|
+
: getDirectedNeighbors(graph as Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, nodeIndex, direction)
|
|
2892
|
+
|
|
2893
|
+
const getTraversableNeighbor = <E, T extends Kind>(
|
|
2894
|
+
graph: Graph<unknown, E, T> | MutableGraph<unknown, E, T>,
|
|
2895
|
+
current: NodeIndex,
|
|
2896
|
+
edge: Edge<E>
|
|
2897
|
+
): NodeIndex => graph.type === "undirected" && edge.target === current ? edge.source : edge.target
|
|
2898
|
+
|
|
2899
|
+
/**
|
|
2900
|
+
* Finds connected components in an undirected graph.
|
|
2901
|
+
* Each component is represented as an array of node indices.
|
|
2902
|
+
*
|
|
2903
|
+
* **Example** (Finding connected components)
|
|
2904
|
+
*
|
|
2905
|
+
* ```ts
|
|
2906
|
+
* import { Graph } from "effect"
|
|
2907
|
+
*
|
|
2908
|
+
* const graph = Graph.undirected<string, string>((mutable) => {
|
|
2909
|
+
* const a = Graph.addNode(mutable, "A")
|
|
2910
|
+
* const b = Graph.addNode(mutable, "B")
|
|
2911
|
+
* const c = Graph.addNode(mutable, "C")
|
|
2912
|
+
* const d = Graph.addNode(mutable, "D")
|
|
2913
|
+
* Graph.addEdge(mutable, a, b, "edge") // Component 1: A-B
|
|
2914
|
+
* Graph.addEdge(mutable, c, d, "edge") // Component 2: C-D
|
|
2915
|
+
* })
|
|
2916
|
+
*
|
|
2917
|
+
* const components = Graph.connectedComponents(graph)
|
|
2918
|
+
* console.log(components) // [[0, 1], [2, 3]]
|
|
2919
|
+
* ```
|
|
2920
|
+
*
|
|
2921
|
+
* @category algorithms
|
|
2922
|
+
* @since 3.18.0
|
|
2923
|
+
*/
|
|
2924
|
+
export const connectedComponents = <N, E>(
|
|
2925
|
+
graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">
|
|
2926
|
+
): Array<Array<NodeIndex>> => {
|
|
2927
|
+
const visited = new Set<NodeIndex>()
|
|
2928
|
+
const components: Array<Array<NodeIndex>> = []
|
|
2929
|
+
for (const startNode of graph.nodes.keys()) {
|
|
2930
|
+
if (!visited.has(startNode)) {
|
|
2931
|
+
// DFS to find all nodes in this component
|
|
2932
|
+
const component: Array<NodeIndex> = []
|
|
2933
|
+
const stack: Array<NodeIndex> = [startNode]
|
|
2934
|
+
|
|
2935
|
+
while (stack.length > 0) {
|
|
2936
|
+
const current = stack.pop()!
|
|
2937
|
+
if (!visited.has(current)) {
|
|
2938
|
+
visited.add(current)
|
|
2939
|
+
component.push(current)
|
|
2940
|
+
|
|
2941
|
+
// Add all unvisited neighbors to stack
|
|
2942
|
+
const nodeNeighbors = getUndirectedNeighbors(graph, current)
|
|
2943
|
+
for (const neighbor of nodeNeighbors) {
|
|
2944
|
+
if (!visited.has(neighbor)) {
|
|
2945
|
+
stack.push(neighbor)
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2951
|
+
components.push(component)
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
return components
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
/**
|
|
2959
|
+
* Finds strongly connected components in a directed graph using Kosaraju's algorithm.
|
|
2960
|
+
* Each SCC is represented as an array of node indices.
|
|
2961
|
+
*
|
|
2962
|
+
* **Gotchas**
|
|
2963
|
+
*
|
|
2964
|
+
* Throws a `GraphError` when used with an undirected graph.
|
|
2965
|
+
*
|
|
2966
|
+
* **Example** (Finding strongly connected components)
|
|
2967
|
+
*
|
|
2968
|
+
* ```ts
|
|
2969
|
+
* import { Graph } from "effect"
|
|
2970
|
+
*
|
|
2971
|
+
* const graph = Graph.directed<string, string>((mutable) => {
|
|
2972
|
+
* const a = Graph.addNode(mutable, "A")
|
|
2973
|
+
* const b = Graph.addNode(mutable, "B")
|
|
2974
|
+
* const c = Graph.addNode(mutable, "C")
|
|
2975
|
+
* Graph.addEdge(mutable, a, b, "A->B")
|
|
2976
|
+
* Graph.addEdge(mutable, b, c, "B->C")
|
|
2977
|
+
* Graph.addEdge(mutable, c, a, "C->A") // Creates SCC: A-B-C
|
|
2978
|
+
* })
|
|
2979
|
+
*
|
|
2980
|
+
* const sccs = Graph.stronglyConnectedComponents(graph)
|
|
2981
|
+
* console.log(sccs) // [[0, 1, 2]]
|
|
2982
|
+
* ```
|
|
2983
|
+
*
|
|
2984
|
+
* @category algorithms
|
|
2985
|
+
* @since 3.18.0
|
|
2986
|
+
*/
|
|
2987
|
+
export const stronglyConnectedComponents = <N, E>(
|
|
2988
|
+
graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">
|
|
2989
|
+
): Array<Array<NodeIndex>> => {
|
|
2990
|
+
if ((graph as Graph<N, E, Kind> | MutableGraph<N, E, Kind>).type === "undirected") {
|
|
2991
|
+
throw new GraphError({ message: "Cannot find strongly connected components of undirected graph" })
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
const visited = new Set<NodeIndex>()
|
|
2995
|
+
const finishOrder: Array<NodeIndex> = []
|
|
2996
|
+
// Iterate directly over node keys
|
|
2997
|
+
|
|
2998
|
+
// Step 1: Stack-safe DFS on original graph to get finish times
|
|
2999
|
+
// Stack entry: [node, neighbors, neighborIndex, isFirstVisit]
|
|
3000
|
+
type DfsStackEntry = [NodeIndex, Array<NodeIndex>, number, boolean]
|
|
3001
|
+
|
|
3002
|
+
for (const startNode of graph.nodes.keys()) {
|
|
3003
|
+
if (visited.has(startNode)) {
|
|
3004
|
+
continue
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
const stack: Array<DfsStackEntry> = [[startNode, [], 0, true]]
|
|
3008
|
+
|
|
3009
|
+
while (stack.length > 0) {
|
|
3010
|
+
const [node, nodeNeighbors, neighborIndex, isFirstVisit] = stack[stack.length - 1]
|
|
3011
|
+
|
|
3012
|
+
if (isFirstVisit) {
|
|
3013
|
+
if (visited.has(node)) {
|
|
3014
|
+
stack.pop()
|
|
3015
|
+
continue
|
|
3016
|
+
}
|
|
3017
|
+
|
|
3018
|
+
visited.add(node)
|
|
3019
|
+
const nodeNeighborsList = getDirectedNeighbors(graph, node, "outgoing")
|
|
3020
|
+
stack[stack.length - 1] = [node, nodeNeighborsList, 0, false]
|
|
3021
|
+
continue
|
|
3022
|
+
}
|
|
3023
|
+
|
|
3024
|
+
// Process next neighbor
|
|
3025
|
+
if (neighborIndex < nodeNeighbors.length) {
|
|
3026
|
+
const neighbor = nodeNeighbors[neighborIndex]
|
|
3027
|
+
stack[stack.length - 1] = [node, nodeNeighbors, neighborIndex + 1, false]
|
|
3028
|
+
|
|
3029
|
+
if (!visited.has(neighbor)) {
|
|
3030
|
+
stack.push([neighbor, [], 0, true])
|
|
3031
|
+
}
|
|
3032
|
+
} else {
|
|
3033
|
+
// Done with this node - add to finish order (post-order)
|
|
3034
|
+
finishOrder.push(node)
|
|
3035
|
+
stack.pop()
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
|
|
3040
|
+
// Step 2: Stack-safe DFS on transpose graph in reverse finish order
|
|
3041
|
+
visited.clear()
|
|
3042
|
+
const sccs: Array<Array<NodeIndex>> = []
|
|
3043
|
+
|
|
3044
|
+
for (let i = finishOrder.length - 1; i >= 0; i--) {
|
|
3045
|
+
const startNode = finishOrder[i]
|
|
3046
|
+
if (visited.has(startNode)) {
|
|
3047
|
+
continue
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
const scc: Array<NodeIndex> = []
|
|
3051
|
+
const stack: Array<NodeIndex> = [startNode]
|
|
3052
|
+
|
|
3053
|
+
while (stack.length > 0) {
|
|
3054
|
+
const node = stack.pop()!
|
|
3055
|
+
|
|
3056
|
+
if (visited.has(node)) {
|
|
3057
|
+
continue
|
|
3058
|
+
}
|
|
3059
|
+
|
|
3060
|
+
visited.add(node)
|
|
3061
|
+
scc.push(node)
|
|
3062
|
+
|
|
3063
|
+
// Use reverse adjacency (transpose graph)
|
|
3064
|
+
const reverseAdjacency = graph.reverseAdjacency.get(node)
|
|
3065
|
+
if (reverseAdjacency !== undefined) {
|
|
3066
|
+
for (const edgeIndex of reverseAdjacency) {
|
|
3067
|
+
const edge = graph.edges.get(edgeIndex)
|
|
3068
|
+
if (edge !== undefined) {
|
|
3069
|
+
const predecessor = edge.source
|
|
3070
|
+
if (!visited.has(predecessor)) {
|
|
3071
|
+
stack.push(predecessor)
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
sccs.push(scc)
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
return sccs
|
|
3082
|
+
}
|
|
3083
|
+
|
|
3084
|
+
// =============================================================================
|
|
3085
|
+
// Path Finding Algorithms
|
|
3086
|
+
// =============================================================================
|
|
3087
|
+
|
|
3088
|
+
/**
|
|
3089
|
+
* Result of a shortest path computation.
|
|
3090
|
+
*
|
|
3091
|
+
* **When to use**
|
|
3092
|
+
*
|
|
3093
|
+
* Use to read the successful source-to-target shortest path returned by
|
|
3094
|
+
* path-finding algorithms, including the ordered node indices, total distance,
|
|
3095
|
+
* and traversed edge data.
|
|
3096
|
+
*
|
|
3097
|
+
* **Details**
|
|
3098
|
+
*
|
|
3099
|
+
* Contains the node-index path, the total numeric distance, and the edge data
|
|
3100
|
+
* encountered along the path.
|
|
3101
|
+
*
|
|
3102
|
+
* **Gotchas**
|
|
3103
|
+
*
|
|
3104
|
+
* `costs` contains original edge data, not the numeric output of the cost
|
|
3105
|
+
* function unless the edge data is numeric.
|
|
3106
|
+
*
|
|
3107
|
+
* @see {@link dijkstra} for shortest paths with non-negative edge costs
|
|
3108
|
+
* @see {@link astar} for heuristic shortest-path search
|
|
3109
|
+
* @see {@link bellmanFord} for shortest paths that may include negative edge weights
|
|
3110
|
+
* @see {@link AllPairsResult} for the all-pairs shortest-path result shape
|
|
3111
|
+
*
|
|
3112
|
+
* @category models
|
|
3113
|
+
* @since 3.18.0
|
|
3114
|
+
*/
|
|
3115
|
+
export interface PathResult<E> {
|
|
3116
|
+
readonly path: Array<NodeIndex>
|
|
3117
|
+
readonly distance: number
|
|
3118
|
+
readonly costs: Array<E>
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
/**
|
|
3122
|
+
* Configuration for finding a shortest path with Dijkstra's algorithm.
|
|
3123
|
+
*
|
|
3124
|
+
* **When to use**
|
|
3125
|
+
*
|
|
3126
|
+
* Use when configuring `dijkstra` to find a shortest path between two existing
|
|
3127
|
+
* node indices with non-negative edge costs.
|
|
3128
|
+
*
|
|
3129
|
+
* **Details**
|
|
3130
|
+
*
|
|
3131
|
+
* Specifies the source and target node indices, plus a cost function that maps
|
|
3132
|
+
* each edge's data to a non-negative numeric weight.
|
|
3133
|
+
*
|
|
3134
|
+
* **Gotchas**
|
|
3135
|
+
*
|
|
3136
|
+
* `dijkstra` throws a `GraphError` when either endpoint does not exist or when
|
|
3137
|
+
* the cost function returns a negative weight.
|
|
3138
|
+
*
|
|
3139
|
+
* @see {@link dijkstra} for the algorithm that consumes this configuration
|
|
3140
|
+
* @see {@link AstarConfig} for heuristic shortest-path search
|
|
3141
|
+
* @see {@link BellmanFordConfig} for shortest paths that may include negative edge weights
|
|
3142
|
+
*
|
|
3143
|
+
* @category models
|
|
3144
|
+
* @since 3.18.0
|
|
3145
|
+
*/
|
|
3146
|
+
export interface DijkstraConfig<E> {
|
|
3147
|
+
source: NodeIndex
|
|
3148
|
+
target: NodeIndex
|
|
3149
|
+
cost: (edgeData: E) => number
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
const validateNonNegativeEdgeWeights = <N, E, T extends Kind = "directed">(
|
|
3153
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
3154
|
+
cost: (edgeData: E) => number,
|
|
3155
|
+
algorithm: string
|
|
3156
|
+
): Map<EdgeIndex, number> => {
|
|
3157
|
+
const edgeWeights = new Map<EdgeIndex, number>()
|
|
3158
|
+
for (const [edgeIndex, edgeData] of graph.edges) {
|
|
3159
|
+
const weight = cost(edgeData.data)
|
|
3160
|
+
if (weight < 0 || Number.isNaN(weight)) {
|
|
3161
|
+
throw new GraphError({ message: `${algorithm} requires non-negative edge weights` })
|
|
3162
|
+
}
|
|
3163
|
+
edgeWeights.set(edgeIndex, weight)
|
|
3164
|
+
}
|
|
3165
|
+
return edgeWeights
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
/**
|
|
3169
|
+
* Finds the shortest path from the configured source node to the target node
|
|
3170
|
+
* using Dijkstra's algorithm.
|
|
3171
|
+
*
|
|
3172
|
+
* **Details**
|
|
3173
|
+
*
|
|
3174
|
+
* Edge costs must be non-negative. Returns `Option.none()` when the target is
|
|
3175
|
+
* not reachable, and throws a `GraphError` when either endpoint is missing or a
|
|
3176
|
+
* negative edge cost is encountered.
|
|
3177
|
+
*
|
|
3178
|
+
* **Example** (Finding shortest paths with Dijkstra)
|
|
3179
|
+
*
|
|
3180
|
+
* ```ts
|
|
3181
|
+
* import { Graph } from "effect"
|
|
3182
|
+
*
|
|
3183
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
3184
|
+
* const a = Graph.addNode(mutable, "A")
|
|
3185
|
+
* const b = Graph.addNode(mutable, "B")
|
|
3186
|
+
* const c = Graph.addNode(mutable, "C")
|
|
3187
|
+
* Graph.addEdge(mutable, a, b, 5)
|
|
3188
|
+
* Graph.addEdge(mutable, a, c, 10)
|
|
3189
|
+
* Graph.addEdge(mutable, b, c, 2)
|
|
3190
|
+
* })
|
|
3191
|
+
*
|
|
3192
|
+
* const result = Graph.dijkstra(graph, {
|
|
3193
|
+
* source: 0,
|
|
3194
|
+
* target: 2,
|
|
3195
|
+
* cost: (edgeData) => edgeData
|
|
3196
|
+
* })
|
|
3197
|
+
*
|
|
3198
|
+
* if (result._tag === "Some") {
|
|
3199
|
+
* console.log(result.value.path) // [0, 1, 2] - shortest path A->B->C
|
|
3200
|
+
* console.log(result.value.distance) // 7 - total distance
|
|
3201
|
+
* }
|
|
3202
|
+
* ```
|
|
3203
|
+
*
|
|
3204
|
+
* @category algorithms
|
|
3205
|
+
* @since 3.18.0
|
|
3206
|
+
*/
|
|
3207
|
+
export const dijkstra: {
|
|
3208
|
+
<E>(
|
|
3209
|
+
config: DijkstraConfig<E>
|
|
3210
|
+
): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option.Option<PathResult<E>>
|
|
3211
|
+
<N, E, T extends Kind = "directed">(
|
|
3212
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
3213
|
+
config: DijkstraConfig<E>
|
|
3214
|
+
): Option.Option<PathResult<E>>
|
|
3215
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
3216
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
3217
|
+
config: DijkstraConfig<E>
|
|
3218
|
+
): Option.Option<PathResult<E>> => {
|
|
3219
|
+
// Validate that source and target nodes exist
|
|
3220
|
+
if (!graph.nodes.has(config.source)) {
|
|
3221
|
+
throw missingNode(config.source)
|
|
3222
|
+
}
|
|
3223
|
+
if (!graph.nodes.has(config.target)) {
|
|
3224
|
+
throw missingNode(config.target)
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
const edgeWeights = validateNonNegativeEdgeWeights(graph, config.cost, "Dijkstra's algorithm")
|
|
3228
|
+
|
|
3229
|
+
// Early return if source equals target
|
|
3230
|
+
if (config.source === config.target) {
|
|
3231
|
+
return Option.some({
|
|
3232
|
+
path: [config.source],
|
|
3233
|
+
distance: 0,
|
|
3234
|
+
costs: []
|
|
3235
|
+
})
|
|
3236
|
+
}
|
|
3237
|
+
|
|
3238
|
+
// Distance tracking and priority queue simulation
|
|
3239
|
+
const distances = new Map<NodeIndex, number>()
|
|
3240
|
+
const previous = new Map<NodeIndex, { node: NodeIndex; edgeData: E } | null>()
|
|
3241
|
+
const visited = new Set<NodeIndex>()
|
|
3242
|
+
|
|
3243
|
+
// Initialize distances
|
|
3244
|
+
// Iterate directly over node keys
|
|
3245
|
+
for (const node of graph.nodes.keys()) {
|
|
3246
|
+
distances.set(node, node === config.source ? 0 : Infinity)
|
|
3247
|
+
previous.set(node, null)
|
|
3248
|
+
}
|
|
3249
|
+
|
|
3250
|
+
// Simple priority queue using array (can be optimized with proper heap)
|
|
3251
|
+
const priorityQueue: Array<{ node: NodeIndex; distance: number }> = [
|
|
3252
|
+
{ node: config.source, distance: 0 }
|
|
3253
|
+
]
|
|
3254
|
+
|
|
3255
|
+
while (priorityQueue.length > 0) {
|
|
3256
|
+
// Find minimum distance node (priority queue extract-min)
|
|
3257
|
+
let minIndex = 0
|
|
3258
|
+
for (let i = 1; i < priorityQueue.length; i++) {
|
|
3259
|
+
if (priorityQueue[i].distance < priorityQueue[minIndex].distance) {
|
|
3260
|
+
minIndex = i
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
|
|
3264
|
+
const current = priorityQueue.splice(minIndex, 1)[0]
|
|
3265
|
+
const currentNode = current.node
|
|
3266
|
+
|
|
3267
|
+
// Skip if already visited (can happen with duplicate entries)
|
|
3268
|
+
if (visited.has(currentNode)) {
|
|
3269
|
+
continue
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
visited.add(currentNode)
|
|
3273
|
+
|
|
3274
|
+
// Early termination if we reached the target
|
|
3275
|
+
if (currentNode === config.target) {
|
|
3276
|
+
break
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
// Get current distance
|
|
3280
|
+
const currentDistance = distances.get(currentNode)!
|
|
3281
|
+
|
|
3282
|
+
// Examine all outgoing edges
|
|
3283
|
+
const adjacencyList = graph.adjacency.get(currentNode)
|
|
3284
|
+
if (adjacencyList !== undefined) {
|
|
3285
|
+
for (const edgeIndex of adjacencyList) {
|
|
3286
|
+
const edge = graph.edges.get(edgeIndex)
|
|
3287
|
+
if (edge !== undefined) {
|
|
3288
|
+
const neighbor = getTraversableNeighbor(graph, currentNode, edge)
|
|
3289
|
+
const cost = edgeWeights.get(edgeIndex)!
|
|
3290
|
+
|
|
3291
|
+
const newDistance = currentDistance + cost
|
|
3292
|
+
const neighborDistance = distances.get(neighbor)!
|
|
3293
|
+
|
|
3294
|
+
// Relaxation step
|
|
3295
|
+
if (newDistance < neighborDistance) {
|
|
3296
|
+
distances.set(neighbor, newDistance)
|
|
3297
|
+
previous.set(neighbor, { node: currentNode, edgeData: edge.data })
|
|
3298
|
+
|
|
3299
|
+
// Add to priority queue if not visited
|
|
3300
|
+
if (!visited.has(neighbor)) {
|
|
3301
|
+
priorityQueue.push({ node: neighbor, distance: newDistance })
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3309
|
+
// Check if target is reachable
|
|
3310
|
+
const distance = distances.get(config.target)!
|
|
3311
|
+
if (distance === Infinity) {
|
|
3312
|
+
return Option.none() // No path exists
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3315
|
+
// Reconstruct path
|
|
3316
|
+
const path: Array<NodeIndex> = []
|
|
3317
|
+
const costs: Array<E> = []
|
|
3318
|
+
let currentNode: NodeIndex | null = config.target
|
|
3319
|
+
|
|
3320
|
+
while (currentNode !== null) {
|
|
3321
|
+
path.unshift(currentNode)
|
|
3322
|
+
const prev: { node: NodeIndex; edgeData: E } | null = previous.get(currentNode)!
|
|
3323
|
+
if (prev !== null) {
|
|
3324
|
+
costs.unshift(prev.edgeData)
|
|
3325
|
+
currentNode = prev.node
|
|
3326
|
+
} else {
|
|
3327
|
+
currentNode = null
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
return Option.some({
|
|
3332
|
+
path,
|
|
3333
|
+
distance,
|
|
3334
|
+
costs
|
|
3335
|
+
})
|
|
3336
|
+
})
|
|
3337
|
+
|
|
3338
|
+
/**
|
|
3339
|
+
* Result of an all-pairs shortest path computation.
|
|
3340
|
+
*
|
|
3341
|
+
* **When to use**
|
|
3342
|
+
*
|
|
3343
|
+
* Use when storing or passing around the complete output of `floydWarshall` so
|
|
3344
|
+
* callers can look up shortest distances, node paths, and edge data for any
|
|
3345
|
+
* source and target node pair.
|
|
3346
|
+
*
|
|
3347
|
+
* **Details**
|
|
3348
|
+
*
|
|
3349
|
+
* Contains distance, node-path, and edge-data maps keyed by source and target
|
|
3350
|
+
* node indices.
|
|
3351
|
+
*
|
|
3352
|
+
* @see {@link floydWarshall} for computing an all-pairs shortest path result
|
|
3353
|
+
* @see {@link PathResult} for the single source-to-target result shape used by path-finding algorithms
|
|
3354
|
+
*
|
|
3355
|
+
* @category models
|
|
3356
|
+
* @since 3.18.0
|
|
3357
|
+
*/
|
|
3358
|
+
export interface AllPairsResult<E> {
|
|
3359
|
+
readonly distances: Map<NodeIndex, Map<NodeIndex, number>>
|
|
3360
|
+
readonly paths: Map<NodeIndex, Map<NodeIndex, Array<NodeIndex> | null>>
|
|
3361
|
+
readonly costs: Map<NodeIndex, Map<NodeIndex, Array<E>>>
|
|
3362
|
+
}
|
|
3363
|
+
|
|
3364
|
+
/**
|
|
3365
|
+
* Finds shortest paths between all pairs of nodes using the Floyd-Warshall
|
|
3366
|
+
* algorithm.
|
|
3367
|
+
*
|
|
3368
|
+
* **Details**
|
|
3369
|
+
*
|
|
3370
|
+
* Computes distances, reconstructed node paths, and edge-data paths for every
|
|
3371
|
+
* source and target pair in O(V^3) time. Negative edge weights are allowed, but
|
|
3372
|
+
* a `GraphError` is thrown if any negative cycle is detected.
|
|
3373
|
+
*
|
|
3374
|
+
* **Example** (Finding all-pairs shortest paths)
|
|
3375
|
+
*
|
|
3376
|
+
* ```ts
|
|
3377
|
+
* import { Graph } from "effect"
|
|
3378
|
+
*
|
|
3379
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
3380
|
+
* const a = Graph.addNode(mutable, "A")
|
|
3381
|
+
* const b = Graph.addNode(mutable, "B")
|
|
3382
|
+
* const c = Graph.addNode(mutable, "C")
|
|
3383
|
+
* Graph.addEdge(mutable, a, b, 3)
|
|
3384
|
+
* Graph.addEdge(mutable, b, c, 2)
|
|
3385
|
+
* Graph.addEdge(mutable, a, c, 7)
|
|
3386
|
+
* })
|
|
3387
|
+
*
|
|
3388
|
+
* const result = Graph.floydWarshall(graph, (edgeData) => edgeData)
|
|
3389
|
+
* const distanceAToC = result.distances.get(0)?.get(2) // 5 (A->B->C)
|
|
3390
|
+
* const pathAToC = result.paths.get(0)?.get(2) // [0, 1, 2]
|
|
3391
|
+
* ```
|
|
3392
|
+
*
|
|
3393
|
+
* @category algorithms
|
|
3394
|
+
* @since 3.18.0
|
|
3395
|
+
*/
|
|
3396
|
+
export const floydWarshall: {
|
|
3397
|
+
<E>(
|
|
3398
|
+
cost: (edgeData: E) => number
|
|
3399
|
+
): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => AllPairsResult<E>
|
|
3400
|
+
<N, E, T extends Kind = "directed">(
|
|
3401
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
3402
|
+
cost: (edgeData: E) => number
|
|
3403
|
+
): AllPairsResult<E>
|
|
3404
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
3405
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
3406
|
+
cost: (edgeData: E) => number
|
|
3407
|
+
): AllPairsResult<E> => {
|
|
3408
|
+
// Get all nodes for Floyd-Warshall algorithm (needs array for nested iteration)
|
|
3409
|
+
const allNodes = Array.from(graph.nodes.keys())
|
|
3410
|
+
|
|
3411
|
+
// Initialize distance matrix
|
|
3412
|
+
const distances = new Map<NodeIndex, Map<NodeIndex, number>>()
|
|
3413
|
+
const next = new Map<NodeIndex, Map<NodeIndex, NodeIndex | null>>()
|
|
3414
|
+
const edgeMatrix = new Map<NodeIndex, Map<NodeIndex, E | null>>()
|
|
3415
|
+
|
|
3416
|
+
// Initialize with infinity for all pairs
|
|
3417
|
+
for (const i of allNodes) {
|
|
3418
|
+
distances.set(i, new Map())
|
|
3419
|
+
next.set(i, new Map())
|
|
3420
|
+
edgeMatrix.set(i, new Map())
|
|
3421
|
+
|
|
3422
|
+
for (const j of allNodes) {
|
|
3423
|
+
distances.get(i)!.set(j, i === j ? 0 : Infinity)
|
|
3424
|
+
next.get(i)!.set(j, null)
|
|
3425
|
+
edgeMatrix.get(i)!.set(j, null)
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
|
|
3429
|
+
// Set edge weights
|
|
3430
|
+
for (const [, edgeData] of graph.edges) {
|
|
3431
|
+
const weight = cost(edgeData.data)
|
|
3432
|
+
const i = edgeData.source
|
|
3433
|
+
const j = edgeData.target
|
|
3434
|
+
|
|
3435
|
+
// Use minimum weight if multiple edges exist
|
|
3436
|
+
const currentWeight = distances.get(i)!.get(j)!
|
|
3437
|
+
if (weight < currentWeight) {
|
|
3438
|
+
distances.get(i)!.set(j, weight)
|
|
3439
|
+
next.get(i)!.set(j, j)
|
|
3440
|
+
edgeMatrix.get(i)!.set(j, edgeData.data)
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
if (graph.type === "undirected") {
|
|
3444
|
+
const reverseWeight = distances.get(j)!.get(i)!
|
|
3445
|
+
if (weight < reverseWeight) {
|
|
3446
|
+
distances.get(j)!.set(i, weight)
|
|
3447
|
+
next.get(j)!.set(i, i)
|
|
3448
|
+
edgeMatrix.get(j)!.set(i, edgeData.data)
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3453
|
+
// Floyd-Warshall main loop
|
|
3454
|
+
for (const k of allNodes) {
|
|
3455
|
+
for (const i of allNodes) {
|
|
3456
|
+
for (const j of allNodes) {
|
|
3457
|
+
const distIK = distances.get(i)!.get(k)!
|
|
3458
|
+
const distKJ = distances.get(k)!.get(j)!
|
|
3459
|
+
const distIJ = distances.get(i)!.get(j)!
|
|
3460
|
+
|
|
3461
|
+
if (distIK !== Infinity && distKJ !== Infinity && distIK + distKJ < distIJ) {
|
|
3462
|
+
distances.get(i)!.set(j, distIK + distKJ)
|
|
3463
|
+
next.get(i)!.set(j, next.get(i)!.get(k)!)
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
|
|
3469
|
+
// Check for negative cycles
|
|
3470
|
+
for (const i of allNodes) {
|
|
3471
|
+
if (distances.get(i)!.get(i)! < 0) {
|
|
3472
|
+
throw new GraphError({ message: `Negative cycle detected involving node ${i}` })
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3476
|
+
// Build result paths and edge weights
|
|
3477
|
+
const paths = new Map<NodeIndex, Map<NodeIndex, Array<NodeIndex> | null>>()
|
|
3478
|
+
const costs = new Map<NodeIndex, Map<NodeIndex, Array<E>>>()
|
|
3479
|
+
|
|
3480
|
+
for (const i of allNodes) {
|
|
3481
|
+
paths.set(i, new Map())
|
|
3482
|
+
costs.set(i, new Map())
|
|
3483
|
+
|
|
3484
|
+
for (const j of allNodes) {
|
|
3485
|
+
if (i === j) {
|
|
3486
|
+
paths.get(i)!.set(j, [i])
|
|
3487
|
+
costs.get(i)!.set(j, [])
|
|
3488
|
+
} else if (distances.get(i)!.get(j)! === Infinity) {
|
|
3489
|
+
paths.get(i)!.set(j, null)
|
|
3490
|
+
costs.get(i)!.set(j, [])
|
|
3491
|
+
} else {
|
|
3492
|
+
// Reconstruct path iteratively
|
|
3493
|
+
const path: Array<NodeIndex> = []
|
|
3494
|
+
const weights: Array<E> = []
|
|
3495
|
+
let current = i
|
|
3496
|
+
|
|
3497
|
+
path.push(current)
|
|
3498
|
+
while (current !== j) {
|
|
3499
|
+
const nextNode = next.get(current)!.get(j)!
|
|
3500
|
+
if (nextNode === null) break
|
|
3501
|
+
|
|
3502
|
+
const edgeData = edgeMatrix.get(current)!.get(nextNode)!
|
|
3503
|
+
if (edgeData !== null) {
|
|
3504
|
+
weights.push(edgeData)
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
current = nextNode
|
|
3508
|
+
path.push(current)
|
|
3509
|
+
}
|
|
3510
|
+
|
|
3511
|
+
paths.get(i)!.set(j, path)
|
|
3512
|
+
costs.get(i)!.set(j, weights)
|
|
3513
|
+
}
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
return {
|
|
3518
|
+
distances,
|
|
3519
|
+
paths,
|
|
3520
|
+
costs
|
|
3521
|
+
}
|
|
3522
|
+
})
|
|
3523
|
+
|
|
3524
|
+
/**
|
|
3525
|
+
* Configuration for finding a shortest path with the A* algorithm.
|
|
3526
|
+
*
|
|
3527
|
+
* **When to use**
|
|
3528
|
+
*
|
|
3529
|
+
* Use when configuring `astar` for point-to-point shortest-path searches where
|
|
3530
|
+
* node data can provide a heuristic estimate toward the target.
|
|
3531
|
+
*
|
|
3532
|
+
* **Details**
|
|
3533
|
+
*
|
|
3534
|
+
* Specifies the source and target node indices, an edge-cost function, and a
|
|
3535
|
+
* heuristic that estimates the remaining cost from a node to the target.
|
|
3536
|
+
*
|
|
3537
|
+
* @see {@link astar} for the algorithm that consumes this configuration
|
|
3538
|
+
* @see {@link DijkstraConfig} for shortest paths without a heuristic
|
|
3539
|
+
* @see {@link BellmanFordConfig} for shortest paths that may include negative edge weights
|
|
3540
|
+
*
|
|
3541
|
+
* @category models
|
|
3542
|
+
* @since 3.18.0
|
|
3543
|
+
*/
|
|
3544
|
+
export interface AstarConfig<E, N> {
|
|
3545
|
+
source: NodeIndex
|
|
3546
|
+
target: NodeIndex
|
|
3547
|
+
cost: (edgeData: E) => number
|
|
3548
|
+
heuristic: (sourceNodeData: N, targetNodeData: N) => number
|
|
3549
|
+
}
|
|
3550
|
+
|
|
3551
|
+
/**
|
|
3552
|
+
* Finds the shortest path from the configured source node to the target node
|
|
3553
|
+
* using the A* pathfinding algorithm.
|
|
3554
|
+
*
|
|
3555
|
+
* **Details**
|
|
3556
|
+
*
|
|
3557
|
+
* The edge-cost function must return non-negative weights, and the heuristic
|
|
3558
|
+
* should be consistent to preserve shortest-path guarantees. Returns
|
|
3559
|
+
* `Option.none()` when the target is not reachable, and throws a `GraphError`
|
|
3560
|
+
* when either endpoint is missing or a negative edge cost is encountered.
|
|
3561
|
+
*
|
|
3562
|
+
* **Example** (Finding shortest paths with A-star)
|
|
3563
|
+
*
|
|
3564
|
+
* ```ts
|
|
3565
|
+
* import { Graph } from "effect"
|
|
3566
|
+
*
|
|
3567
|
+
* const graph = Graph.directed<{ x: number; y: number }, number>((mutable) => {
|
|
3568
|
+
* const a = Graph.addNode(mutable, { x: 0, y: 0 })
|
|
3569
|
+
* const b = Graph.addNode(mutable, { x: 1, y: 0 })
|
|
3570
|
+
* const c = Graph.addNode(mutable, { x: 2, y: 0 })
|
|
3571
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
3572
|
+
* Graph.addEdge(mutable, b, c, 1)
|
|
3573
|
+
* })
|
|
3574
|
+
*
|
|
3575
|
+
* // Manhattan distance heuristic
|
|
3576
|
+
* const heuristic = (
|
|
3577
|
+
* nodeData: { x: number; y: number },
|
|
3578
|
+
* targetData: { x: number; y: number }
|
|
3579
|
+
* ) => Math.abs(nodeData.x - targetData.x) + Math.abs(nodeData.y - targetData.y)
|
|
3580
|
+
*
|
|
3581
|
+
* const result = Graph.astar(graph, {
|
|
3582
|
+
* source: 0,
|
|
3583
|
+
* target: 2,
|
|
3584
|
+
* cost: (edgeData) => edgeData,
|
|
3585
|
+
* heuristic
|
|
3586
|
+
* })
|
|
3587
|
+
*
|
|
3588
|
+
* if (result._tag === "Some") {
|
|
3589
|
+
* console.log(result.value.path) // [0, 1, 2] - shortest path
|
|
3590
|
+
* console.log(result.value.distance) // 2 - total distance
|
|
3591
|
+
* }
|
|
3592
|
+
* ```
|
|
3593
|
+
*
|
|
3594
|
+
* @category algorithms
|
|
3595
|
+
* @since 3.18.0
|
|
3596
|
+
*/
|
|
3597
|
+
export const astar: {
|
|
3598
|
+
<E, N>(
|
|
3599
|
+
config: AstarConfig<E, N>
|
|
3600
|
+
): <T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option.Option<PathResult<E>>
|
|
3601
|
+
<N, E, T extends Kind = "directed">(
|
|
3602
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
3603
|
+
config: AstarConfig<E, N>
|
|
3604
|
+
): Option.Option<PathResult<E>>
|
|
3605
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
3606
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
3607
|
+
config: AstarConfig<E, N>
|
|
3608
|
+
): Option.Option<PathResult<E>> => {
|
|
3609
|
+
// Validate that source and target nodes exist
|
|
3610
|
+
if (!graph.nodes.has(config.source)) {
|
|
3611
|
+
throw missingNode(config.source)
|
|
3612
|
+
}
|
|
3613
|
+
if (!graph.nodes.has(config.target)) {
|
|
3614
|
+
throw missingNode(config.target)
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3617
|
+
const edgeWeights = validateNonNegativeEdgeWeights(graph, config.cost, "A* algorithm")
|
|
3618
|
+
|
|
3619
|
+
// Early return if source equals target
|
|
3620
|
+
if (config.source === config.target) {
|
|
3621
|
+
return Option.some({
|
|
3622
|
+
path: [config.source],
|
|
3623
|
+
distance: 0,
|
|
3624
|
+
costs: []
|
|
3625
|
+
})
|
|
3626
|
+
}
|
|
3627
|
+
|
|
3628
|
+
// Get target node data for heuristic calculations
|
|
3629
|
+
const targetNodeData = getNode(graph, config.target)
|
|
3630
|
+
if (Option.isNone(targetNodeData)) {
|
|
3631
|
+
throw new GraphError({ message: `Missing node data for target node ${config.target}` })
|
|
3632
|
+
}
|
|
3633
|
+
|
|
3634
|
+
// Distance tracking (g-score) and f-score (g + h)
|
|
3635
|
+
const gScore = new Map<NodeIndex, number>()
|
|
3636
|
+
const fScore = new Map<NodeIndex, number>()
|
|
3637
|
+
const previous = new Map<NodeIndex, { node: NodeIndex; edgeData: E } | null>()
|
|
3638
|
+
const visited = new Set<NodeIndex>()
|
|
3639
|
+
|
|
3640
|
+
// Initialize scores
|
|
3641
|
+
// Iterate directly over node keys
|
|
3642
|
+
for (const node of graph.nodes.keys()) {
|
|
3643
|
+
gScore.set(node, node === config.source ? 0 : Infinity)
|
|
3644
|
+
fScore.set(node, Infinity)
|
|
3645
|
+
previous.set(node, null)
|
|
3646
|
+
}
|
|
3647
|
+
|
|
3648
|
+
// Calculate initial f-score for source
|
|
3649
|
+
const sourceNodeData = getNode(graph, config.source)
|
|
3650
|
+
if (Option.isSome(sourceNodeData)) {
|
|
3651
|
+
const h = config.heuristic(sourceNodeData.value, targetNodeData.value)
|
|
3652
|
+
fScore.set(config.source, h)
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3655
|
+
// Priority queue using f-score (total estimated cost)
|
|
3656
|
+
const openSet: Array<{ node: NodeIndex; fScore: number }> = [
|
|
3657
|
+
{ node: config.source, fScore: fScore.get(config.source)! }
|
|
3658
|
+
]
|
|
3659
|
+
|
|
3660
|
+
while (openSet.length > 0) {
|
|
3661
|
+
// Find node with lowest f-score
|
|
3662
|
+
let minIndex = 0
|
|
3663
|
+
for (let i = 1; i < openSet.length; i++) {
|
|
3664
|
+
if (openSet[i].fScore < openSet[minIndex].fScore) {
|
|
3665
|
+
minIndex = i
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
|
|
3669
|
+
const current = openSet.splice(minIndex, 1)[0]
|
|
3670
|
+
const currentNode = current.node
|
|
3671
|
+
|
|
3672
|
+
// Skip if already visited
|
|
3673
|
+
if (visited.has(currentNode)) {
|
|
3674
|
+
continue
|
|
3675
|
+
}
|
|
3676
|
+
|
|
3677
|
+
visited.add(currentNode)
|
|
3678
|
+
|
|
3679
|
+
// Early termination if we reached the target
|
|
3680
|
+
if (currentNode === config.target) {
|
|
3681
|
+
break
|
|
3682
|
+
}
|
|
3683
|
+
|
|
3684
|
+
// Get current g-score
|
|
3685
|
+
const currentGScore = gScore.get(currentNode)!
|
|
3686
|
+
|
|
3687
|
+
// Examine all outgoing edges
|
|
3688
|
+
const adjacencyList = graph.adjacency.get(currentNode)
|
|
3689
|
+
if (adjacencyList !== undefined) {
|
|
3690
|
+
for (const edgeIndex of adjacencyList) {
|
|
3691
|
+
const edge = graph.edges.get(edgeIndex)
|
|
3692
|
+
if (edge !== undefined) {
|
|
3693
|
+
const neighbor = getTraversableNeighbor(graph, currentNode, edge)
|
|
3694
|
+
const weight = edgeWeights.get(edgeIndex)!
|
|
3695
|
+
|
|
3696
|
+
const tentativeGScore = currentGScore + weight
|
|
3697
|
+
const neighborGScore = gScore.get(neighbor)!
|
|
3698
|
+
|
|
3699
|
+
// If this path to neighbor is better than any previous one
|
|
3700
|
+
if (tentativeGScore < neighborGScore) {
|
|
3701
|
+
// Update g-score and previous
|
|
3702
|
+
gScore.set(neighbor, tentativeGScore)
|
|
3703
|
+
previous.set(neighbor, { node: currentNode, edgeData: edge.data })
|
|
3704
|
+
|
|
3705
|
+
// Calculate f-score using heuristic
|
|
3706
|
+
const neighborNodeData = getNode(graph, neighbor)
|
|
3707
|
+
if (Option.isSome(neighborNodeData)) {
|
|
3708
|
+
const h = config.heuristic(neighborNodeData.value, targetNodeData.value)
|
|
3709
|
+
const f = tentativeGScore + h
|
|
3710
|
+
fScore.set(neighbor, f)
|
|
3711
|
+
|
|
3712
|
+
// Add to open set if not visited
|
|
3713
|
+
if (!visited.has(neighbor)) {
|
|
3714
|
+
openSet.push({ node: neighbor, fScore: f })
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
|
|
3723
|
+
// Check if target is reachable
|
|
3724
|
+
const distance = gScore.get(config.target)!
|
|
3725
|
+
if (distance === Infinity) {
|
|
3726
|
+
return Option.none() // No path exists
|
|
3727
|
+
}
|
|
3728
|
+
|
|
3729
|
+
// Reconstruct path
|
|
3730
|
+
const path: Array<NodeIndex> = []
|
|
3731
|
+
const costs: Array<E> = []
|
|
3732
|
+
let currentNode: NodeIndex | null = config.target
|
|
3733
|
+
|
|
3734
|
+
while (currentNode !== null) {
|
|
3735
|
+
path.unshift(currentNode)
|
|
3736
|
+
const prev: { node: NodeIndex; edgeData: E } | null = previous.get(currentNode) ?? null
|
|
3737
|
+
if (prev !== null) {
|
|
3738
|
+
costs.unshift(prev.edgeData)
|
|
3739
|
+
currentNode = prev.node
|
|
3740
|
+
} else {
|
|
3741
|
+
currentNode = null
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3745
|
+
return Option.some({
|
|
3746
|
+
path,
|
|
3747
|
+
distance,
|
|
3748
|
+
costs
|
|
3749
|
+
})
|
|
3750
|
+
})
|
|
3751
|
+
|
|
3752
|
+
/**
|
|
3753
|
+
* Configuration for finding a shortest path with the Bellman-Ford algorithm.
|
|
3754
|
+
*
|
|
3755
|
+
* **When to use**
|
|
3756
|
+
*
|
|
3757
|
+
* Use when configuring `bellmanFord` to find a shortest path where edge
|
|
3758
|
+
* weights may be negative.
|
|
3759
|
+
*
|
|
3760
|
+
* **Details**
|
|
3761
|
+
*
|
|
3762
|
+
* Specifies the source and target node indices, plus a cost function that maps
|
|
3763
|
+
* each edge's data to a numeric weight.
|
|
3764
|
+
*
|
|
3765
|
+
* @see {@link bellmanFord} for the algorithm that consumes this configuration
|
|
3766
|
+
* @see {@link DijkstraConfig} for non-negative edge costs
|
|
3767
|
+
* @see {@link AstarConfig} for heuristic shortest-path search
|
|
3768
|
+
*
|
|
3769
|
+
* @category models
|
|
3770
|
+
* @since 3.18.0
|
|
3771
|
+
*/
|
|
3772
|
+
export interface BellmanFordConfig<E> {
|
|
3773
|
+
source: NodeIndex
|
|
3774
|
+
target: NodeIndex
|
|
3775
|
+
cost: (edgeData: E) => number
|
|
3776
|
+
}
|
|
3777
|
+
|
|
3778
|
+
/**
|
|
3779
|
+
* Finds the shortest path from the configured source node to the target node
|
|
3780
|
+
* using the Bellman-Ford algorithm.
|
|
3781
|
+
*
|
|
3782
|
+
* **Details**
|
|
3783
|
+
*
|
|
3784
|
+
* Negative edge weights are allowed. Returns `Option.none()` when the target is
|
|
3785
|
+
* unreachable or when a negative cycle affects the path to the target. Throws a
|
|
3786
|
+
* `GraphError` when either endpoint is missing.
|
|
3787
|
+
*
|
|
3788
|
+
* **Example** (Finding shortest paths with Bellman-Ford)
|
|
3789
|
+
*
|
|
3790
|
+
* ```ts
|
|
3791
|
+
* import { Graph } from "effect"
|
|
3792
|
+
*
|
|
3793
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
3794
|
+
* const a = Graph.addNode(mutable, "A")
|
|
3795
|
+
* const b = Graph.addNode(mutable, "B")
|
|
3796
|
+
* const c = Graph.addNode(mutable, "C")
|
|
3797
|
+
* Graph.addEdge(mutable, a, b, -1) // Negative weight allowed
|
|
3798
|
+
* Graph.addEdge(mutable, b, c, 3)
|
|
3799
|
+
* Graph.addEdge(mutable, a, c, 5)
|
|
3800
|
+
* })
|
|
3801
|
+
*
|
|
3802
|
+
* const result = Graph.bellmanFord(graph, {
|
|
3803
|
+
* source: 0,
|
|
3804
|
+
* target: 2,
|
|
3805
|
+
* cost: (edgeData) => edgeData
|
|
3806
|
+
* })
|
|
3807
|
+
*
|
|
3808
|
+
* if (result._tag === "Some") {
|
|
3809
|
+
* console.log(result.value.path) // [0, 1, 2] - shortest path A->B->C
|
|
3810
|
+
* console.log(result.value.distance) // 2 - total distance
|
|
3811
|
+
* }
|
|
3812
|
+
* ```
|
|
3813
|
+
*
|
|
3814
|
+
* @category algorithms
|
|
3815
|
+
* @since 3.18.0
|
|
3816
|
+
*/
|
|
3817
|
+
export const bellmanFord: {
|
|
3818
|
+
<E>(
|
|
3819
|
+
config: BellmanFordConfig<E>
|
|
3820
|
+
): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option.Option<PathResult<E>>
|
|
3821
|
+
<N, E, T extends Kind = "directed">(
|
|
3822
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
3823
|
+
config: BellmanFordConfig<E>
|
|
3824
|
+
): Option.Option<PathResult<E>>
|
|
3825
|
+
} = dual(2, <N, E, T extends Kind = "directed">(
|
|
3826
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
3827
|
+
config: BellmanFordConfig<E>
|
|
3828
|
+
): Option.Option<PathResult<E>> => {
|
|
3829
|
+
// Validate that source and target nodes exist
|
|
3830
|
+
if (!graph.nodes.has(config.source)) {
|
|
3831
|
+
throw missingNode(config.source)
|
|
3832
|
+
}
|
|
3833
|
+
if (!graph.nodes.has(config.target)) {
|
|
3834
|
+
throw missingNode(config.target)
|
|
3835
|
+
}
|
|
3836
|
+
|
|
3837
|
+
// Early return if source equals target
|
|
3838
|
+
if (config.source === config.target) {
|
|
3839
|
+
return Option.some({
|
|
3840
|
+
path: [config.source],
|
|
3841
|
+
distance: 0,
|
|
3842
|
+
costs: []
|
|
3843
|
+
})
|
|
3844
|
+
}
|
|
3845
|
+
|
|
3846
|
+
// Initialize distances and predecessors
|
|
3847
|
+
const distances = new Map<NodeIndex, number>()
|
|
3848
|
+
const previous = new Map<NodeIndex, { node: NodeIndex; edgeData: E } | null>()
|
|
3849
|
+
|
|
3850
|
+
// Iterate directly over node keys
|
|
3851
|
+
for (const node of graph.nodes.keys()) {
|
|
3852
|
+
distances.set(node, node === config.source ? 0 : Infinity)
|
|
3853
|
+
previous.set(node, null)
|
|
3854
|
+
}
|
|
3855
|
+
|
|
3856
|
+
// Collect all edges for relaxation
|
|
3857
|
+
const edges: Array<{ source: NodeIndex; target: NodeIndex; weight: number; edgeData: E }> = []
|
|
3858
|
+
for (const [, edgeData] of graph.edges) {
|
|
3859
|
+
const weight = config.cost(edgeData.data)
|
|
3860
|
+
edges.push({
|
|
3861
|
+
source: edgeData.source,
|
|
3862
|
+
target: edgeData.target,
|
|
3863
|
+
weight,
|
|
3864
|
+
edgeData: edgeData.data
|
|
3865
|
+
})
|
|
3866
|
+
if (graph.type === "undirected" && edgeData.source !== edgeData.target) {
|
|
3867
|
+
edges.push({
|
|
3868
|
+
source: edgeData.target,
|
|
3869
|
+
target: edgeData.source,
|
|
3870
|
+
weight,
|
|
3871
|
+
edgeData: edgeData.data
|
|
3872
|
+
})
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3875
|
+
|
|
3876
|
+
// Relax edges up to V-1 times
|
|
3877
|
+
const nodeCount = graph.nodes.size
|
|
3878
|
+
for (let i = 0; i < nodeCount - 1; i++) {
|
|
3879
|
+
let hasUpdate = false
|
|
3880
|
+
|
|
3881
|
+
for (const edge of edges) {
|
|
3882
|
+
const sourceDistance = distances.get(edge.source)!
|
|
3883
|
+
const targetDistance = distances.get(edge.target)!
|
|
3884
|
+
|
|
3885
|
+
// Relaxation step
|
|
3886
|
+
if (sourceDistance !== Infinity && sourceDistance + edge.weight < targetDistance) {
|
|
3887
|
+
distances.set(edge.target, sourceDistance + edge.weight)
|
|
3888
|
+
previous.set(edge.target, { node: edge.source, edgeData: edge.edgeData })
|
|
3889
|
+
hasUpdate = true
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
|
|
3893
|
+
// Early termination if no updates
|
|
3894
|
+
if (!hasUpdate) {
|
|
3895
|
+
break
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3899
|
+
// Check for negative cycles
|
|
3900
|
+
for (const edge of edges) {
|
|
3901
|
+
const sourceDistance = distances.get(edge.source)!
|
|
3902
|
+
const targetDistance = distances.get(edge.target)!
|
|
3903
|
+
|
|
3904
|
+
if (sourceDistance !== Infinity && sourceDistance + edge.weight < targetDistance) {
|
|
3905
|
+
// Negative cycle detected - check if it affects the path to target
|
|
3906
|
+
const affectedNodes = new Set<NodeIndex>()
|
|
3907
|
+
const queue = [edge.target]
|
|
3908
|
+
|
|
3909
|
+
while (queue.length > 0) {
|
|
3910
|
+
const node = queue.shift()!
|
|
3911
|
+
if (affectedNodes.has(node)) continue
|
|
3912
|
+
affectedNodes.add(node)
|
|
3913
|
+
|
|
3914
|
+
// Add all nodes reachable from this node
|
|
3915
|
+
for (const neighbor of getTraversalNeighbors(graph, node, "outgoing")) {
|
|
3916
|
+
queue.push(neighbor)
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
|
|
3920
|
+
// If target is affected by a negative cycle, no shortest path exists.
|
|
3921
|
+
if (affectedNodes.has(config.target)) {
|
|
3922
|
+
return Option.none()
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
|
|
3927
|
+
// Check if target is reachable
|
|
3928
|
+
const distance = distances.get(config.target)!
|
|
3929
|
+
if (distance === Infinity) {
|
|
3930
|
+
return Option.none() // No path exists
|
|
3931
|
+
}
|
|
3932
|
+
|
|
3933
|
+
// Reconstruct path
|
|
3934
|
+
const path: Array<NodeIndex> = []
|
|
3935
|
+
const costs: Array<E> = []
|
|
3936
|
+
let currentNode: NodeIndex | null = config.target
|
|
3937
|
+
|
|
3938
|
+
while (currentNode !== null) {
|
|
3939
|
+
path.unshift(currentNode)
|
|
3940
|
+
const prev: { node: NodeIndex; edgeData: E } | null = previous.get(currentNode)!
|
|
3941
|
+
if (prev !== null) {
|
|
3942
|
+
costs.unshift(prev.edgeData)
|
|
3943
|
+
currentNode = prev.node
|
|
3944
|
+
} else {
|
|
3945
|
+
currentNode = null
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
|
|
3949
|
+
return Option.some({
|
|
3950
|
+
path,
|
|
3951
|
+
distance,
|
|
3952
|
+
costs
|
|
3953
|
+
})
|
|
3954
|
+
})
|
|
3955
|
+
|
|
3956
|
+
/**
|
|
3957
|
+
* Represents an iterable wrapper used by graph traversal and listing APIs.
|
|
3958
|
+
*
|
|
3959
|
+
* **Details**
|
|
3960
|
+
*
|
|
3961
|
+
* A `Walker` yields `[index, data]` pairs lazily and can be viewed as just the
|
|
3962
|
+
* indices, just the values, or mapped entries with `indices`, `values`,
|
|
3963
|
+
* `entries`, and `visit`.
|
|
3964
|
+
*
|
|
3965
|
+
* **Example** (Working with node walkers)
|
|
3966
|
+
*
|
|
3967
|
+
* ```ts
|
|
3968
|
+
* import { Graph } from "effect"
|
|
3969
|
+
*
|
|
3970
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
3971
|
+
* const a = Graph.addNode(mutable, "A")
|
|
3972
|
+
* const b = Graph.addNode(mutable, "B")
|
|
3973
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
3974
|
+
* })
|
|
3975
|
+
*
|
|
3976
|
+
* // Both traversal and element iterators return NodeWalker
|
|
3977
|
+
* const dfsNodes: Graph.NodeWalker<string> = Graph.dfs(graph, { start: [0] })
|
|
3978
|
+
* const allNodes: Graph.NodeWalker<string> = Graph.nodes(graph)
|
|
3979
|
+
*
|
|
3980
|
+
* // Common interface for working with node iterables
|
|
3981
|
+
* function processNodes<N>(nodeIterable: Graph.NodeWalker<N>): Array<number> {
|
|
3982
|
+
* return Array.from(Graph.indices(nodeIterable))
|
|
3983
|
+
* }
|
|
3984
|
+
*
|
|
3985
|
+
* // Access node data using values() or entries()
|
|
3986
|
+
* const nodeData = Array.from(Graph.values(dfsNodes)) // ["A", "B"]
|
|
3987
|
+
* const nodeEntries = Array.from(Graph.entries(allNodes)) // [[0, "A"], [1, "B"]]
|
|
3988
|
+
* ```
|
|
3989
|
+
*
|
|
3990
|
+
* @category models
|
|
3991
|
+
* @since 3.18.0
|
|
3992
|
+
*/
|
|
3993
|
+
export class Walker<T, N> implements Iterable<[T, N]> {
|
|
3994
|
+
// @ts-ignore
|
|
3995
|
+
readonly [Symbol.iterator]: () => Iterator<[T, N]>
|
|
3996
|
+
|
|
3997
|
+
/**
|
|
3998
|
+
* Visits each element and maps it to a value using the provided function.
|
|
3999
|
+
*
|
|
4000
|
+
* **Details**
|
|
4001
|
+
*
|
|
4002
|
+
* Takes a function that receives the index and data,
|
|
4003
|
+
* and returns an iterable of the mapped values. Skips elements that
|
|
4004
|
+
* no longer exist in the graph.
|
|
4005
|
+
*
|
|
4006
|
+
* **Example** (Visiting walker elements)
|
|
4007
|
+
*
|
|
4008
|
+
* ```ts
|
|
4009
|
+
* import { Graph } from "effect"
|
|
4010
|
+
*
|
|
4011
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
4012
|
+
* const a = Graph.addNode(mutable, "A")
|
|
4013
|
+
* const b = Graph.addNode(mutable, "B")
|
|
4014
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
4015
|
+
* })
|
|
4016
|
+
*
|
|
4017
|
+
* const dfs = Graph.dfs(graph, { start: [0] })
|
|
4018
|
+
*
|
|
4019
|
+
* // Map to just the node data
|
|
4020
|
+
* const values = Array.from(dfs.visit((index, data) => data))
|
|
4021
|
+
* console.log(values) // ["A", "B"]
|
|
4022
|
+
*
|
|
4023
|
+
* // Map to custom objects
|
|
4024
|
+
* const custom = Array.from(
|
|
4025
|
+
* dfs.visit((index, data) => ({ id: index, name: data }))
|
|
4026
|
+
* )
|
|
4027
|
+
* console.log(custom) // [{ id: 0, name: "A" }, { id: 1, name: "B" }]
|
|
4028
|
+
* ```
|
|
4029
|
+
*
|
|
4030
|
+
* @since 4.0.0
|
|
4031
|
+
*/
|
|
4032
|
+
readonly visit: <U>(f: (index: T, data: N) => U) => Iterable<U>
|
|
4033
|
+
|
|
4034
|
+
constructor(
|
|
4035
|
+
/**
|
|
4036
|
+
* Visits each element and maps it to a value using the provided function.
|
|
4037
|
+
*
|
|
4038
|
+
* Takes a function that receives the index and data,
|
|
4039
|
+
* and returns an iterable of the mapped values. Skips elements that
|
|
4040
|
+
* no longer exist in the graph.
|
|
4041
|
+
*
|
|
4042
|
+
* **Example** (Visiting walker elements)
|
|
4043
|
+
*
|
|
4044
|
+
* ```ts
|
|
4045
|
+
* import { Graph } from "effect"
|
|
4046
|
+
*
|
|
4047
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
4048
|
+
* const a = Graph.addNode(mutable, "A")
|
|
4049
|
+
* const b = Graph.addNode(mutable, "B")
|
|
4050
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
4051
|
+
* })
|
|
4052
|
+
*
|
|
4053
|
+
* const dfs = Graph.dfs(graph, { start: [0] })
|
|
4054
|
+
*
|
|
4055
|
+
* // Map to just the node data
|
|
4056
|
+
* const values = Array.from(dfs.visit((index, data) => data))
|
|
4057
|
+
* console.log(values) // ["A", "B"]
|
|
4058
|
+
*
|
|
4059
|
+
* // Map to custom objects
|
|
4060
|
+
* const custom = Array.from(
|
|
4061
|
+
* dfs.visit((index, data) => ({ id: index, name: data }))
|
|
4062
|
+
* )
|
|
4063
|
+
* console.log(custom) // [{ id: 0, name: "A" }, { id: 1, name: "B" }]
|
|
4064
|
+
* ```
|
|
4065
|
+
*
|
|
4066
|
+
* @category iterators
|
|
4067
|
+
* @since 4.0.0
|
|
4068
|
+
*/
|
|
4069
|
+
visit: <U>(f: (index: T, data: N) => U) => Iterable<U>
|
|
4070
|
+
) {
|
|
4071
|
+
this.visit = visit
|
|
4072
|
+
this[Symbol.iterator] = visit((index, data) => [index, data] as [T, N])[Symbol.iterator]
|
|
4073
|
+
}
|
|
4074
|
+
}
|
|
4075
|
+
|
|
4076
|
+
/**
|
|
4077
|
+
* Type alias for node iteration using Walker.
|
|
4078
|
+
* NodeWalker is represented as Walker<NodeIndex, N>.
|
|
4079
|
+
*
|
|
4080
|
+
* **When to use**
|
|
4081
|
+
*
|
|
4082
|
+
* Use as the shared node walker type returned by graph traversal and node
|
|
4083
|
+
* listing APIs.
|
|
4084
|
+
*
|
|
4085
|
+
* @see {@link Walker} for the generic lazy iterator wrapper
|
|
4086
|
+
* @see {@link EdgeWalker} for edge iterators
|
|
4087
|
+
*
|
|
4088
|
+
* @category models
|
|
4089
|
+
* @since 3.18.0
|
|
4090
|
+
*/
|
|
4091
|
+
export type NodeWalker<N> = Walker<NodeIndex, N>
|
|
4092
|
+
|
|
4093
|
+
/**
|
|
4094
|
+
* Type alias for edge iteration using Walker.
|
|
4095
|
+
* EdgeWalker is represented as Walker<EdgeIndex, Edge<E>>.
|
|
4096
|
+
*
|
|
4097
|
+
* **When to use**
|
|
4098
|
+
*
|
|
4099
|
+
* Use to type helpers or parameters that consume edge iterators returned by
|
|
4100
|
+
* `Graph` APIs, where each item is keyed by an `EdgeIndex` and carries the
|
|
4101
|
+
* full `Edge`.
|
|
4102
|
+
*
|
|
4103
|
+
* @see {@link Walker} for the generic lazy iterator wrapper
|
|
4104
|
+
* @see {@link NodeWalker} for node iterators
|
|
4105
|
+
* @see {@link edges} for creating edge walkers
|
|
4106
|
+
*
|
|
4107
|
+
* @category models
|
|
4108
|
+
* @since 3.18.0
|
|
4109
|
+
*/
|
|
4110
|
+
export type EdgeWalker<E> = Walker<EdgeIndex, Edge<E>>
|
|
4111
|
+
|
|
4112
|
+
/**
|
|
4113
|
+
* Returns an iterator over the indices in the walker.
|
|
4114
|
+
*
|
|
4115
|
+
* **Example** (Iterating walker indices)
|
|
4116
|
+
*
|
|
4117
|
+
* ```ts
|
|
4118
|
+
* import { Graph } from "effect"
|
|
4119
|
+
*
|
|
4120
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
4121
|
+
* const a = Graph.addNode(mutable, "A")
|
|
4122
|
+
* const b = Graph.addNode(mutable, "B")
|
|
4123
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
4124
|
+
* })
|
|
4125
|
+
*
|
|
4126
|
+
* const dfs = Graph.dfs(graph, { start: [0] })
|
|
4127
|
+
* const indices = Array.from(Graph.indices(dfs))
|
|
4128
|
+
* console.log(indices) // [0, 1]
|
|
4129
|
+
* ```
|
|
4130
|
+
*
|
|
4131
|
+
* @category iterators
|
|
4132
|
+
* @since 3.18.0
|
|
4133
|
+
*/
|
|
4134
|
+
export const indices = <T, N>(walker: Walker<T, N>): Iterable<T> => walker.visit((index, _) => index)
|
|
4135
|
+
|
|
4136
|
+
/**
|
|
4137
|
+
* Returns an iterator over the values (data) in the walker.
|
|
4138
|
+
*
|
|
4139
|
+
* **Example** (Iterating walker values)
|
|
4140
|
+
*
|
|
4141
|
+
* ```ts
|
|
4142
|
+
* import { Graph } from "effect"
|
|
4143
|
+
*
|
|
4144
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
4145
|
+
* const a = Graph.addNode(mutable, "A")
|
|
4146
|
+
* const b = Graph.addNode(mutable, "B")
|
|
4147
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
4148
|
+
* })
|
|
4149
|
+
*
|
|
4150
|
+
* const dfs = Graph.dfs(graph, { start: [0] })
|
|
4151
|
+
* const values = Array.from(Graph.values(dfs))
|
|
4152
|
+
* console.log(values) // ["A", "B"]
|
|
4153
|
+
* ```
|
|
4154
|
+
*
|
|
4155
|
+
* @category iterators
|
|
4156
|
+
* @since 3.18.0
|
|
4157
|
+
*/
|
|
4158
|
+
export const values = <T, N>(walker: Walker<T, N>): Iterable<N> => walker.visit((_, data) => data)
|
|
4159
|
+
|
|
4160
|
+
/**
|
|
4161
|
+
* Returns an iterator over [index, data] entries in the walker.
|
|
4162
|
+
*
|
|
4163
|
+
* **Example** (Iterating walker entries)
|
|
4164
|
+
*
|
|
4165
|
+
* ```ts
|
|
4166
|
+
* import { Graph } from "effect"
|
|
4167
|
+
*
|
|
4168
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
4169
|
+
* const a = Graph.addNode(mutable, "A")
|
|
4170
|
+
* const b = Graph.addNode(mutable, "B")
|
|
4171
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
4172
|
+
* })
|
|
4173
|
+
*
|
|
4174
|
+
* const dfs = Graph.dfs(graph, { start: [0] })
|
|
4175
|
+
* const entries = Array.from(Graph.entries(dfs))
|
|
4176
|
+
* console.log(entries) // [[0, "A"], [1, "B"]]
|
|
4177
|
+
* ```
|
|
4178
|
+
*
|
|
4179
|
+
* @category iterators
|
|
4180
|
+
* @since 3.18.0
|
|
4181
|
+
*/
|
|
4182
|
+
export const entries = <T, N>(walker: Walker<T, N>): Iterable<[T, N]> =>
|
|
4183
|
+
walker.visit((index, data) => [index, data] as [T, N])
|
|
4184
|
+
|
|
4185
|
+
/**
|
|
4186
|
+
* Configuration for DFS, BFS, and postorder graph traversals.
|
|
4187
|
+
*
|
|
4188
|
+
* **When to use**
|
|
4189
|
+
*
|
|
4190
|
+
* Use to configure the starting node indices and edge-following direction for
|
|
4191
|
+
* lazy graph traversals.
|
|
4192
|
+
*
|
|
4193
|
+
* **Details**
|
|
4194
|
+
*
|
|
4195
|
+
* `start` supplies the node indices where traversal begins. If it is omitted,
|
|
4196
|
+
* the iterator is empty. `direction` chooses whether traversal follows
|
|
4197
|
+
* outgoing or incoming edges.
|
|
4198
|
+
*
|
|
4199
|
+
* **Gotchas**
|
|
4200
|
+
*
|
|
4201
|
+
* Traversal creation throws a `GraphError` when any configured `start` node
|
|
4202
|
+
* does not exist.
|
|
4203
|
+
*
|
|
4204
|
+
* @see {@link dfs} for depth-first traversal
|
|
4205
|
+
* @see {@link bfs} for breadth-first traversal
|
|
4206
|
+
* @see {@link dfsPostOrder} for depth-first postorder traversal
|
|
4207
|
+
*
|
|
4208
|
+
* @category models
|
|
4209
|
+
* @since 3.18.0
|
|
4210
|
+
*/
|
|
4211
|
+
export interface SearchConfig {
|
|
4212
|
+
readonly start?: Array<NodeIndex>
|
|
4213
|
+
readonly direction?: Direction
|
|
4214
|
+
}
|
|
4215
|
+
|
|
4216
|
+
/**
|
|
4217
|
+
* Creates a lazy depth-first traversal iterator from the configured start
|
|
4218
|
+
* nodes.
|
|
4219
|
+
*
|
|
4220
|
+
* **Details**
|
|
4221
|
+
*
|
|
4222
|
+
* If no start nodes are supplied, the iterator is empty. The `direction` option
|
|
4223
|
+
* chooses whether to follow outgoing or incoming edges. Throws a `GraphError`
|
|
4224
|
+
* if any configured start node does not exist.
|
|
4225
|
+
*
|
|
4226
|
+
* **Example** (Traversing depth-first)
|
|
4227
|
+
*
|
|
4228
|
+
* ```ts
|
|
4229
|
+
* import { Graph } from "effect"
|
|
4230
|
+
*
|
|
4231
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
4232
|
+
* const a = Graph.addNode(mutable, "A")
|
|
4233
|
+
* const b = Graph.addNode(mutable, "B")
|
|
4234
|
+
* const c = Graph.addNode(mutable, "C")
|
|
4235
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
4236
|
+
* Graph.addEdge(mutable, b, c, 1)
|
|
4237
|
+
* })
|
|
4238
|
+
*
|
|
4239
|
+
* // Start from a specific node
|
|
4240
|
+
* const dfs1 = Graph.dfs(graph, { start: [0] })
|
|
4241
|
+
* for (const nodeIndex of Graph.indices(dfs1)) {
|
|
4242
|
+
* console.log(nodeIndex) // Traverses in DFS order: 0, 1, 2
|
|
4243
|
+
* }
|
|
4244
|
+
*
|
|
4245
|
+
* // Empty iterator (no starting nodes)
|
|
4246
|
+
* const dfs2 = Graph.dfs(graph)
|
|
4247
|
+
* // Can be used programmatically
|
|
4248
|
+
* ```
|
|
4249
|
+
*
|
|
4250
|
+
* @category iterators
|
|
4251
|
+
* @since 3.18.0
|
|
4252
|
+
*/
|
|
4253
|
+
export const dfs: {
|
|
4254
|
+
(
|
|
4255
|
+
config?: SearchConfig
|
|
4256
|
+
): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => NodeWalker<N>
|
|
4257
|
+
<N, E, T extends Kind = "directed">(
|
|
4258
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
4259
|
+
config?: SearchConfig
|
|
4260
|
+
): NodeWalker<N>
|
|
4261
|
+
} = dual((args) => isGraph(args[0]), <N, E, T extends Kind = "directed">(
|
|
4262
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
4263
|
+
config: SearchConfig = {}
|
|
4264
|
+
): NodeWalker<N> => {
|
|
4265
|
+
const start = config.start ?? []
|
|
4266
|
+
const direction = config.direction ?? "outgoing"
|
|
4267
|
+
|
|
4268
|
+
// Validate that all start nodes exist
|
|
4269
|
+
for (const nodeIndex of start) {
|
|
4270
|
+
if (!hasNode(graph, nodeIndex)) {
|
|
4271
|
+
throw missingNode(nodeIndex)
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
4274
|
+
|
|
4275
|
+
return new Walker((f) => ({
|
|
4276
|
+
[Symbol.iterator]: () => {
|
|
4277
|
+
const stack = [...start]
|
|
4278
|
+
const discovered = new Set<NodeIndex>()
|
|
4279
|
+
|
|
4280
|
+
const nextMapped = () => {
|
|
4281
|
+
while (stack.length > 0) {
|
|
4282
|
+
const current = stack.pop()!
|
|
4283
|
+
|
|
4284
|
+
if (discovered.has(current)) {
|
|
4285
|
+
continue
|
|
4286
|
+
}
|
|
4287
|
+
|
|
4288
|
+
discovered.add(current)
|
|
4289
|
+
|
|
4290
|
+
const nodeDataOption = getNode(graph, current)
|
|
4291
|
+
if (Option.isNone(nodeDataOption)) {
|
|
4292
|
+
continue
|
|
4293
|
+
}
|
|
4294
|
+
|
|
4295
|
+
const neighbors = getTraversalNeighbors(graph, current, direction)
|
|
4296
|
+
for (let i = neighbors.length - 1; i >= 0; i--) {
|
|
4297
|
+
const neighbor = neighbors[i]
|
|
4298
|
+
if (!discovered.has(neighbor)) {
|
|
4299
|
+
stack.push(neighbor)
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
|
|
4303
|
+
return { done: false, value: f(current, nodeDataOption.value) }
|
|
4304
|
+
}
|
|
4305
|
+
|
|
4306
|
+
return { done: true, value: undefined } as const
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4309
|
+
return { next: nextMapped }
|
|
4310
|
+
}
|
|
4311
|
+
}))
|
|
4312
|
+
})
|
|
4313
|
+
|
|
4314
|
+
/**
|
|
4315
|
+
* Creates a lazy breadth-first traversal iterator from the configured start
|
|
4316
|
+
* nodes.
|
|
4317
|
+
*
|
|
4318
|
+
* **Details**
|
|
4319
|
+
*
|
|
4320
|
+
* If no start nodes are supplied, the iterator is empty. The `direction` option
|
|
4321
|
+
* chooses whether to follow outgoing or incoming edges. Throws a `GraphError`
|
|
4322
|
+
* if any configured start node does not exist.
|
|
4323
|
+
*
|
|
4324
|
+
* **Example** (Traversing breadth-first)
|
|
4325
|
+
*
|
|
4326
|
+
* ```ts
|
|
4327
|
+
* import { Graph } from "effect"
|
|
4328
|
+
*
|
|
4329
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
4330
|
+
* const a = Graph.addNode(mutable, "A")
|
|
4331
|
+
* const b = Graph.addNode(mutable, "B")
|
|
4332
|
+
* const c = Graph.addNode(mutable, "C")
|
|
4333
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
4334
|
+
* Graph.addEdge(mutable, b, c, 1)
|
|
4335
|
+
* })
|
|
4336
|
+
*
|
|
4337
|
+
* // Start from a specific node
|
|
4338
|
+
* const bfs1 = Graph.bfs(graph, { start: [0] })
|
|
4339
|
+
* for (const nodeIndex of Graph.indices(bfs1)) {
|
|
4340
|
+
* console.log(nodeIndex) // Traverses in BFS order: 0, 1, 2
|
|
4341
|
+
* }
|
|
4342
|
+
*
|
|
4343
|
+
* // Empty iterator (no starting nodes)
|
|
4344
|
+
* const bfs2 = Graph.bfs(graph)
|
|
4345
|
+
* // Can be used programmatically
|
|
4346
|
+
* ```
|
|
4347
|
+
*
|
|
4348
|
+
* @category iterators
|
|
4349
|
+
* @since 3.18.0
|
|
4350
|
+
*/
|
|
4351
|
+
export const bfs: {
|
|
4352
|
+
(
|
|
4353
|
+
config?: SearchConfig
|
|
4354
|
+
): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => NodeWalker<N>
|
|
4355
|
+
<N, E, T extends Kind = "directed">(
|
|
4356
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
4357
|
+
config?: SearchConfig
|
|
4358
|
+
): NodeWalker<N>
|
|
4359
|
+
} = dual((args) => isGraph(args[0]), <N, E, T extends Kind = "directed">(
|
|
4360
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
4361
|
+
config: SearchConfig = {}
|
|
4362
|
+
): NodeWalker<N> => {
|
|
4363
|
+
const start = config.start ?? []
|
|
4364
|
+
const direction = config.direction ?? "outgoing"
|
|
4365
|
+
|
|
4366
|
+
// Validate that all start nodes exist
|
|
4367
|
+
for (const nodeIndex of start) {
|
|
4368
|
+
if (!hasNode(graph, nodeIndex)) {
|
|
4369
|
+
throw missingNode(nodeIndex)
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4372
|
+
|
|
4373
|
+
return new Walker((f) => ({
|
|
4374
|
+
[Symbol.iterator]: () => {
|
|
4375
|
+
const queue = [...start]
|
|
4376
|
+
const discovered = new Set<NodeIndex>()
|
|
4377
|
+
|
|
4378
|
+
const nextMapped = () => {
|
|
4379
|
+
while (queue.length > 0) {
|
|
4380
|
+
const current = queue.shift()!
|
|
4381
|
+
|
|
4382
|
+
if (!discovered.has(current)) {
|
|
4383
|
+
discovered.add(current)
|
|
4384
|
+
|
|
4385
|
+
const neighbors = getTraversalNeighbors(graph, current, direction)
|
|
4386
|
+
for (const neighbor of neighbors) {
|
|
4387
|
+
if (!discovered.has(neighbor)) {
|
|
4388
|
+
queue.push(neighbor)
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
|
|
4392
|
+
const nodeData = getNode(graph, current)
|
|
4393
|
+
if (Option.isSome(nodeData)) {
|
|
4394
|
+
return { done: false, value: f(current, nodeData.value) }
|
|
4395
|
+
}
|
|
4396
|
+
return nextMapped()
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
|
|
4400
|
+
return { done: true, value: undefined } as const
|
|
4401
|
+
}
|
|
4402
|
+
|
|
4403
|
+
return { next: nextMapped }
|
|
4404
|
+
}
|
|
4405
|
+
}))
|
|
4406
|
+
})
|
|
4407
|
+
|
|
4408
|
+
/**
|
|
4409
|
+
* Configuration for the topological sort iterator.
|
|
4410
|
+
*
|
|
4411
|
+
* **When to use**
|
|
4412
|
+
*
|
|
4413
|
+
* Use to prioritize specific zero in-degree nodes in a topological sort.
|
|
4414
|
+
*
|
|
4415
|
+
* **Details**
|
|
4416
|
+
*
|
|
4417
|
+
* `initials` optionally supplies zero in-degree node indices used as
|
|
4418
|
+
* prioritized initial queue entries. Topological sorting still includes the
|
|
4419
|
+
* other zero in-degree nodes and produces a complete topological order.
|
|
4420
|
+
*
|
|
4421
|
+
* **Gotchas**
|
|
4422
|
+
*
|
|
4423
|
+
* Throws a `GraphError` when any initial node has incoming edges.
|
|
4424
|
+
*
|
|
4425
|
+
* @see {@link topo} for the iterator that consumes this configuration
|
|
4426
|
+
*
|
|
4427
|
+
* @category models
|
|
4428
|
+
* @since 3.18.0
|
|
4429
|
+
*/
|
|
4430
|
+
export interface TopoConfig {
|
|
4431
|
+
readonly initials?: Array<NodeIndex>
|
|
4432
|
+
}
|
|
4433
|
+
|
|
4434
|
+
/**
|
|
4435
|
+
* Creates a new topological sort iterator with optional configuration.
|
|
4436
|
+
*
|
|
4437
|
+
* **Details**
|
|
4438
|
+
*
|
|
4439
|
+
* The iterator uses Kahn's algorithm to lazily produce nodes in topological order.
|
|
4440
|
+
* Throws an error if the graph contains cycles.
|
|
4441
|
+
*
|
|
4442
|
+
* **Example** (Sorting topologically)
|
|
4443
|
+
*
|
|
4444
|
+
* ```ts
|
|
4445
|
+
* import { Graph } from "effect"
|
|
4446
|
+
*
|
|
4447
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
4448
|
+
* const a = Graph.addNode(mutable, "A")
|
|
4449
|
+
* const b = Graph.addNode(mutable, "B")
|
|
4450
|
+
* const c = Graph.addNode(mutable, "C")
|
|
4451
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
4452
|
+
* Graph.addEdge(mutable, b, c, 1)
|
|
4453
|
+
* })
|
|
4454
|
+
*
|
|
4455
|
+
* // Standard topological sort
|
|
4456
|
+
* const topo1 = Graph.topo(graph)
|
|
4457
|
+
* for (const nodeIndex of Graph.indices(topo1)) {
|
|
4458
|
+
* console.log(nodeIndex) // 0, 1, 2 (topological order)
|
|
4459
|
+
* }
|
|
4460
|
+
*
|
|
4461
|
+
* // With initial nodes
|
|
4462
|
+
* const topo2 = Graph.topo(graph, { initials: [0] })
|
|
4463
|
+
*
|
|
4464
|
+
* // Check before sorting a cyclic graph
|
|
4465
|
+
* const cyclicGraph = Graph.directed<string, number>((mutable) => {
|
|
4466
|
+
* const a = Graph.addNode(mutable, "A")
|
|
4467
|
+
* const b = Graph.addNode(mutable, "B")
|
|
4468
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
4469
|
+
* Graph.addEdge(mutable, b, a, 2) // Creates cycle
|
|
4470
|
+
* })
|
|
4471
|
+
*
|
|
4472
|
+
* if (!Graph.isAcyclic(cyclicGraph)) {
|
|
4473
|
+
* console.log("cyclic graph") // cyclic graph
|
|
4474
|
+
* }
|
|
4475
|
+
* ```
|
|
4476
|
+
*
|
|
4477
|
+
* @category iterators
|
|
4478
|
+
* @since 3.18.0
|
|
4479
|
+
*/
|
|
4480
|
+
export const topo: {
|
|
4481
|
+
(
|
|
4482
|
+
config?: TopoConfig
|
|
4483
|
+
): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => NodeWalker<N>
|
|
4484
|
+
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, config?: TopoConfig): NodeWalker<N>
|
|
4485
|
+
} = dual((args) => isGraph(args[0]), <N, E, T extends Kind = "directed">(
|
|
4486
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
4487
|
+
config: TopoConfig = {}
|
|
4488
|
+
): NodeWalker<N> => {
|
|
4489
|
+
if (graph.type === "undirected") {
|
|
4490
|
+
throw new GraphError({ message: "Cannot perform topological sort on undirected graph" })
|
|
4491
|
+
}
|
|
4492
|
+
|
|
4493
|
+
// Check if graph is acyclic first
|
|
4494
|
+
if (!isAcyclic(graph)) {
|
|
4495
|
+
throw new GraphError({ message: "Cannot perform topological sort on cyclic graph" })
|
|
4496
|
+
}
|
|
4497
|
+
|
|
4498
|
+
const initials = config.initials ?? []
|
|
4499
|
+
|
|
4500
|
+
// Validate that all initial nodes exist
|
|
4501
|
+
for (const nodeIndex of initials) {
|
|
4502
|
+
if (!hasNode(graph, nodeIndex)) {
|
|
4503
|
+
throw missingNode(nodeIndex)
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
|
|
4507
|
+
return new Walker((f) => ({
|
|
4508
|
+
[Symbol.iterator]: () => {
|
|
4509
|
+
const inDegree = new Map<NodeIndex, number>()
|
|
4510
|
+
const remaining = new Set<NodeIndex>()
|
|
4511
|
+
const initialSet = new Set(initials)
|
|
4512
|
+
const queue = [...initials]
|
|
4513
|
+
|
|
4514
|
+
// Initialize in-degree counts
|
|
4515
|
+
for (const [nodeIndex] of graph.nodes) {
|
|
4516
|
+
inDegree.set(nodeIndex, 0)
|
|
4517
|
+
remaining.add(nodeIndex)
|
|
4518
|
+
}
|
|
4519
|
+
|
|
4520
|
+
// Calculate in-degrees
|
|
4521
|
+
for (const [, edgeData] of graph.edges) {
|
|
4522
|
+
const currentInDegree = inDegree.get(edgeData.target) || 0
|
|
4523
|
+
inDegree.set(edgeData.target, currentInDegree + 1)
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4526
|
+
for (const nodeIndex of initials) {
|
|
4527
|
+
if (inDegree.get(nodeIndex)! !== 0) {
|
|
4528
|
+
throw new GraphError({ message: `Initial node ${nodeIndex} has incoming edges` })
|
|
4529
|
+
}
|
|
4530
|
+
}
|
|
4531
|
+
|
|
4532
|
+
// Add remaining zero in-degree nodes after prioritized initials.
|
|
4533
|
+
for (const [nodeIndex, degree] of inDegree) {
|
|
4534
|
+
if (degree === 0 && !initialSet.has(nodeIndex)) {
|
|
4535
|
+
queue.push(nodeIndex)
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4539
|
+
const nextMapped = () => {
|
|
4540
|
+
while (queue.length > 0) {
|
|
4541
|
+
const current = queue.shift()!
|
|
4542
|
+
|
|
4543
|
+
if (remaining.has(current)) {
|
|
4544
|
+
remaining.delete(current)
|
|
4545
|
+
|
|
4546
|
+
// Process outgoing edges, reducing in-degree of targets
|
|
4547
|
+
const neighbors = getDirectedNeighbors(
|
|
4548
|
+
graph as Graph<N, E, "directed"> | MutableGraph<N, E, "directed">,
|
|
4549
|
+
current,
|
|
4550
|
+
"outgoing"
|
|
4551
|
+
)
|
|
4552
|
+
for (const neighbor of neighbors) {
|
|
4553
|
+
if (remaining.has(neighbor)) {
|
|
4554
|
+
const currentInDegree = inDegree.get(neighbor) || 0
|
|
4555
|
+
const newInDegree = currentInDegree - 1
|
|
4556
|
+
inDegree.set(neighbor, newInDegree)
|
|
4557
|
+
|
|
4558
|
+
// If in-degree becomes 0, add to queue
|
|
4559
|
+
if (newInDegree === 0) {
|
|
4560
|
+
queue.push(neighbor)
|
|
4561
|
+
}
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
|
|
4565
|
+
const nodeData = getNode(graph, current)
|
|
4566
|
+
if (Option.isSome(nodeData)) {
|
|
4567
|
+
return { done: false, value: f(current, nodeData.value) }
|
|
4568
|
+
}
|
|
4569
|
+
return nextMapped()
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
4572
|
+
|
|
4573
|
+
return { done: true, value: undefined } as const
|
|
4574
|
+
}
|
|
4575
|
+
|
|
4576
|
+
return { next: nextMapped }
|
|
4577
|
+
}
|
|
4578
|
+
}))
|
|
4579
|
+
})
|
|
4580
|
+
|
|
4581
|
+
/**
|
|
4582
|
+
* Creates a lazy depth-first postorder traversal iterator from the configured
|
|
4583
|
+
* start nodes.
|
|
4584
|
+
*
|
|
4585
|
+
* **Details**
|
|
4586
|
+
*
|
|
4587
|
+
* Nodes are emitted after their reachable descendants have been processed. If
|
|
4588
|
+
* no start nodes are supplied, the iterator is empty. The `direction` option
|
|
4589
|
+
* chooses whether to follow outgoing or incoming edges.
|
|
4590
|
+
*
|
|
4591
|
+
* **Example** (Traversing in postorder)
|
|
4592
|
+
*
|
|
4593
|
+
* ```ts
|
|
4594
|
+
* import { Graph } from "effect"
|
|
4595
|
+
*
|
|
4596
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
4597
|
+
* const root = Graph.addNode(mutable, "root")
|
|
4598
|
+
* const child1 = Graph.addNode(mutable, "child1")
|
|
4599
|
+
* const child2 = Graph.addNode(mutable, "child2")
|
|
4600
|
+
* Graph.addEdge(mutable, root, child1, 1)
|
|
4601
|
+
* Graph.addEdge(mutable, root, child2, 1)
|
|
4602
|
+
* })
|
|
4603
|
+
*
|
|
4604
|
+
* // Postorder: children before parents
|
|
4605
|
+
* const postOrder = Graph.dfsPostOrder(graph, { start: [0] })
|
|
4606
|
+
* for (const node of postOrder) {
|
|
4607
|
+
* console.log(node) // 1, 2, 0
|
|
4608
|
+
* }
|
|
4609
|
+
* ```
|
|
4610
|
+
*
|
|
4611
|
+
* @category iterators
|
|
4612
|
+
* @since 3.18.0
|
|
4613
|
+
*/
|
|
4614
|
+
export const dfsPostOrder: {
|
|
4615
|
+
(
|
|
4616
|
+
config?: SearchConfig
|
|
4617
|
+
): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => NodeWalker<N>
|
|
4618
|
+
<N, E, T extends Kind = "directed">(
|
|
4619
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
4620
|
+
config?: SearchConfig
|
|
4621
|
+
): NodeWalker<N>
|
|
4622
|
+
} = dual((args) => isGraph(args[0]), <N, E, T extends Kind = "directed">(
|
|
4623
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
4624
|
+
config: SearchConfig = {}
|
|
4625
|
+
): NodeWalker<N> => {
|
|
4626
|
+
const start = config.start ?? []
|
|
4627
|
+
const direction = config.direction ?? "outgoing"
|
|
4628
|
+
|
|
4629
|
+
// Validate that all start nodes exist
|
|
4630
|
+
for (const nodeIndex of start) {
|
|
4631
|
+
if (!hasNode(graph, nodeIndex)) {
|
|
4632
|
+
throw missingNode(nodeIndex)
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
|
|
4636
|
+
return new Walker((f) => ({
|
|
4637
|
+
[Symbol.iterator]: () => {
|
|
4638
|
+
const stack: Array<{ node: NodeIndex; visitedChildren: boolean }> = []
|
|
4639
|
+
const discovered = new Set<NodeIndex>()
|
|
4640
|
+
const finished = new Set<NodeIndex>()
|
|
4641
|
+
|
|
4642
|
+
// Initialize stack with start nodes
|
|
4643
|
+
for (let i = start.length - 1; i >= 0; i--) {
|
|
4644
|
+
stack.push({ node: start[i], visitedChildren: false })
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4647
|
+
const nextMapped = () => {
|
|
4648
|
+
while (stack.length > 0) {
|
|
4649
|
+
const current = stack[stack.length - 1]
|
|
4650
|
+
|
|
4651
|
+
if (!discovered.has(current.node)) {
|
|
4652
|
+
discovered.add(current.node)
|
|
4653
|
+
current.visitedChildren = false
|
|
4654
|
+
}
|
|
4655
|
+
|
|
4656
|
+
if (!current.visitedChildren) {
|
|
4657
|
+
current.visitedChildren = true
|
|
4658
|
+
const neighbors = getTraversalNeighbors(graph, current.node, direction)
|
|
4659
|
+
|
|
4660
|
+
for (let i = neighbors.length - 1; i >= 0; i--) {
|
|
4661
|
+
const neighbor = neighbors[i]
|
|
4662
|
+
if (!discovered.has(neighbor) && !finished.has(neighbor)) {
|
|
4663
|
+
stack.push({ node: neighbor, visitedChildren: false })
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4666
|
+
} else {
|
|
4667
|
+
const nodeToEmit = stack.pop()!.node
|
|
4668
|
+
|
|
4669
|
+
if (!finished.has(nodeToEmit)) {
|
|
4670
|
+
finished.add(nodeToEmit)
|
|
4671
|
+
|
|
4672
|
+
const nodeData = getNode(graph, nodeToEmit)
|
|
4673
|
+
if (Option.isSome(nodeData)) {
|
|
4674
|
+
return { done: false, value: f(nodeToEmit, nodeData.value) }
|
|
4675
|
+
}
|
|
4676
|
+
return nextMapped()
|
|
4677
|
+
}
|
|
4678
|
+
}
|
|
4679
|
+
}
|
|
4680
|
+
|
|
4681
|
+
return { done: true, value: undefined } as const
|
|
4682
|
+
}
|
|
4683
|
+
|
|
4684
|
+
return { next: nextMapped }
|
|
4685
|
+
}
|
|
4686
|
+
}))
|
|
4687
|
+
})
|
|
4688
|
+
|
|
4689
|
+
/**
|
|
4690
|
+
* Creates an iterator over all node indices in the graph.
|
|
4691
|
+
*
|
|
4692
|
+
* **Details**
|
|
4693
|
+
*
|
|
4694
|
+
* The iterator produces node indices in the order they were added to the graph.
|
|
4695
|
+
* This provides access to all nodes regardless of connectivity.
|
|
4696
|
+
*
|
|
4697
|
+
* **Example** (Iterating all nodes)
|
|
4698
|
+
*
|
|
4699
|
+
* ```ts
|
|
4700
|
+
* import { Graph } from "effect"
|
|
4701
|
+
*
|
|
4702
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
4703
|
+
* const a = Graph.addNode(mutable, "A")
|
|
4704
|
+
* const b = Graph.addNode(mutable, "B")
|
|
4705
|
+
* const c = Graph.addNode(mutable, "C")
|
|
4706
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
4707
|
+
* })
|
|
4708
|
+
*
|
|
4709
|
+
* const indices = Array.from(Graph.indices(Graph.nodes(graph)))
|
|
4710
|
+
* console.log(indices) // [0, 1, 2]
|
|
4711
|
+
* ```
|
|
4712
|
+
*
|
|
4713
|
+
* @category iterators
|
|
4714
|
+
* @since 3.18.0
|
|
4715
|
+
*/
|
|
4716
|
+
export const nodes = <N, E, T extends Kind = "directed">(
|
|
4717
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>
|
|
4718
|
+
): NodeWalker<N> =>
|
|
4719
|
+
new Walker((f) => ({
|
|
4720
|
+
[Symbol.iterator]() {
|
|
4721
|
+
const nodeMap = graph.nodes
|
|
4722
|
+
const iterator = nodeMap.entries()
|
|
4723
|
+
|
|
4724
|
+
return {
|
|
4725
|
+
next() {
|
|
4726
|
+
const result = iterator.next()
|
|
4727
|
+
if (result.done) {
|
|
4728
|
+
return { done: true, value: undefined }
|
|
4729
|
+
}
|
|
4730
|
+
const [nodeIndex, nodeData] = result.value
|
|
4731
|
+
return { done: false, value: f(nodeIndex, nodeData) }
|
|
4732
|
+
}
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4735
|
+
}))
|
|
4736
|
+
|
|
4737
|
+
/**
|
|
4738
|
+
* Creates an iterator over all edge indices in the graph.
|
|
4739
|
+
*
|
|
4740
|
+
* **Details**
|
|
4741
|
+
*
|
|
4742
|
+
* The iterator produces edge indices in the order they were added to the graph.
|
|
4743
|
+
* This provides access to all edges regardless of connectivity.
|
|
4744
|
+
*
|
|
4745
|
+
* **Example** (Iterating all edges)
|
|
4746
|
+
*
|
|
4747
|
+
* ```ts
|
|
4748
|
+
* import { Graph } from "effect"
|
|
4749
|
+
*
|
|
4750
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
4751
|
+
* const a = Graph.addNode(mutable, "A")
|
|
4752
|
+
* const b = Graph.addNode(mutable, "B")
|
|
4753
|
+
* const c = Graph.addNode(mutable, "C")
|
|
4754
|
+
* Graph.addEdge(mutable, a, b, 1)
|
|
4755
|
+
* Graph.addEdge(mutable, b, c, 2)
|
|
4756
|
+
* })
|
|
4757
|
+
*
|
|
4758
|
+
* const indices = Array.from(Graph.indices(Graph.edges(graph)))
|
|
4759
|
+
* console.log(indices) // [0, 1]
|
|
4760
|
+
* ```
|
|
4761
|
+
*
|
|
4762
|
+
* @category iterators
|
|
4763
|
+
* @since 3.18.0
|
|
4764
|
+
*/
|
|
4765
|
+
export const edges = <N, E, T extends Kind = "directed">(
|
|
4766
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>
|
|
4767
|
+
): EdgeWalker<E> =>
|
|
4768
|
+
new Walker((f) => ({
|
|
4769
|
+
[Symbol.iterator]() {
|
|
4770
|
+
const edgeMap = graph.edges
|
|
4771
|
+
const iterator = edgeMap.entries()
|
|
4772
|
+
|
|
4773
|
+
return {
|
|
4774
|
+
next() {
|
|
4775
|
+
const result = iterator.next()
|
|
4776
|
+
if (result.done) {
|
|
4777
|
+
return { done: true, value: undefined }
|
|
4778
|
+
}
|
|
4779
|
+
const [edgeIndex, edgeData] = result.value
|
|
4780
|
+
return { done: false, value: f(edgeIndex, edgeData) }
|
|
4781
|
+
}
|
|
4782
|
+
}
|
|
4783
|
+
}
|
|
4784
|
+
}))
|
|
4785
|
+
|
|
4786
|
+
/**
|
|
4787
|
+
* Configuration for selecting external nodes.
|
|
4788
|
+
*
|
|
4789
|
+
* **When to use**
|
|
4790
|
+
*
|
|
4791
|
+
* Use to configure how `externals` identifies graph boundary nodes when you
|
|
4792
|
+
* need sinks with no outgoing edges or sources with no incoming edges.
|
|
4793
|
+
*
|
|
4794
|
+
* **Details**
|
|
4795
|
+
*
|
|
4796
|
+
* `direction` chooses which missing edge direction makes a node external:
|
|
4797
|
+
* `"outgoing"` selects nodes with no outgoing edges, and `"incoming"` selects
|
|
4798
|
+
* nodes with no incoming edges. If omitted, `direction` defaults to
|
|
4799
|
+
* `"outgoing"`.
|
|
4800
|
+
*
|
|
4801
|
+
* @see {@link externals} for the iterator that consumes this configuration
|
|
4802
|
+
*
|
|
4803
|
+
* @category models
|
|
4804
|
+
* @since 3.18.0
|
|
4805
|
+
*/
|
|
4806
|
+
export interface ExternalsConfig {
|
|
4807
|
+
readonly direction?: Direction
|
|
4808
|
+
}
|
|
4809
|
+
|
|
4810
|
+
/**
|
|
4811
|
+
* Creates an iterator over external nodes (nodes without edges in the specified direction).
|
|
4812
|
+
*
|
|
4813
|
+
* **Details**
|
|
4814
|
+
*
|
|
4815
|
+
* External nodes have no outgoing edges (`direction: "outgoing"`) or no
|
|
4816
|
+
* incoming edges (`direction: "incoming"`). These are useful for finding
|
|
4817
|
+
* sources, sinks, or isolated nodes.
|
|
4818
|
+
*
|
|
4819
|
+
* **Example** (Iterating external nodes)
|
|
4820
|
+
*
|
|
4821
|
+
* ```ts
|
|
4822
|
+
* import { Graph } from "effect"
|
|
4823
|
+
*
|
|
4824
|
+
* const graph = Graph.directed<string, number>((mutable) => {
|
|
4825
|
+
* const source = Graph.addNode(mutable, "source") // 0 - no incoming
|
|
4826
|
+
* const middle = Graph.addNode(mutable, "middle") // 1 - has both
|
|
4827
|
+
* const sink = Graph.addNode(mutable, "sink") // 2 - no outgoing
|
|
4828
|
+
* const isolated = Graph.addNode(mutable, "isolated") // 3 - no edges
|
|
4829
|
+
*
|
|
4830
|
+
* Graph.addEdge(mutable, source, middle, 1)
|
|
4831
|
+
* Graph.addEdge(mutable, middle, sink, 2)
|
|
4832
|
+
* })
|
|
4833
|
+
*
|
|
4834
|
+
* // Nodes with no outgoing edges (sinks + isolated)
|
|
4835
|
+
* const sinks = Array.from(
|
|
4836
|
+
* Graph.indices(Graph.externals(graph, { direction: "outgoing" }))
|
|
4837
|
+
* )
|
|
4838
|
+
* console.log(sinks) // [2, 3]
|
|
4839
|
+
*
|
|
4840
|
+
* // Nodes with no incoming edges (sources + isolated)
|
|
4841
|
+
* const sources = Array.from(
|
|
4842
|
+
* Graph.indices(Graph.externals(graph, { direction: "incoming" }))
|
|
4843
|
+
* )
|
|
4844
|
+
* console.log(sources) // [0, 3]
|
|
4845
|
+
* ```
|
|
4846
|
+
*
|
|
4847
|
+
* @category iterators
|
|
4848
|
+
* @since 3.18.0
|
|
4849
|
+
*/
|
|
4850
|
+
export const externals: {
|
|
4851
|
+
(
|
|
4852
|
+
config?: ExternalsConfig
|
|
4853
|
+
): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => NodeWalker<N>
|
|
4854
|
+
<N, E, T extends Kind = "directed">(
|
|
4855
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
4856
|
+
config?: ExternalsConfig
|
|
4857
|
+
): NodeWalker<N>
|
|
4858
|
+
} = dual((args) => isGraph(args[0]), <N, E, T extends Kind = "directed">(
|
|
4859
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
4860
|
+
config: ExternalsConfig = {}
|
|
4861
|
+
): NodeWalker<N> => {
|
|
4862
|
+
const direction = config.direction ?? "outgoing"
|
|
4863
|
+
|
|
4864
|
+
return new Walker((f) => ({
|
|
4865
|
+
[Symbol.iterator]: () => {
|
|
4866
|
+
const nodeMap = graph.nodes
|
|
4867
|
+
const adjacencyMap = direction === "incoming"
|
|
4868
|
+
? graph.reverseAdjacency
|
|
4869
|
+
: graph.adjacency
|
|
4870
|
+
|
|
4871
|
+
const nodeIterator = nodeMap.entries()
|
|
4872
|
+
|
|
4873
|
+
const nextMapped = () => {
|
|
4874
|
+
let current = nodeIterator.next()
|
|
4875
|
+
while (!current.done) {
|
|
4876
|
+
const [nodeIndex, nodeData] = current.value
|
|
4877
|
+
const adjacencyList = adjacencyMap.get(nodeIndex)
|
|
4878
|
+
|
|
4879
|
+
// Node is external if it has no edges in the specified direction
|
|
4880
|
+
if (adjacencyList === undefined || adjacencyList.length === 0) {
|
|
4881
|
+
return { done: false, value: f(nodeIndex, nodeData) }
|
|
4882
|
+
}
|
|
4883
|
+
current = nodeIterator.next()
|
|
4884
|
+
}
|
|
4885
|
+
|
|
4886
|
+
return { done: true, value: undefined } as const
|
|
4887
|
+
}
|
|
4888
|
+
|
|
4889
|
+
return { next: nextMapped }
|
|
4890
|
+
}
|
|
4891
|
+
}))
|
|
4892
|
+
})
|