freelang-editor 11.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.config/jest.config.js +47 -0
- package/.config/jest.fast.config.js +9 -0
- package/.config/openapi.yaml +457 -0
- package/.config/tsconfig.json +30 -0
- package/.github/CODE_OF_CONDUCT.md +176 -0
- package/.github/CONTRIBUTING.md +296 -0
- package/.github/DISCUSSIONS.md +179 -0
- package/.github/ISSUE_TEMPLATE/bug-report.md +44 -0
- package/.github/ISSUE_TEMPLATE/feature-request.md +39 -0
- package/.github/pull_request_template.md +87 -0
- package/.github/workflows/phase-3c-l2-proof.yml +101 -0
- package/.github/workflows/phase-c-full-slack.yml +409 -0
- package/.github/workflows/phase-c-full.yml +241 -0
- package/.github/workflows/phase-c-scan.yml +64 -0
- package/.gitmodules +3 -0
- package/.tmp.js +3 -0
- package/CHANGELOG.md +1182 -0
- package/CLAUDE.md +633 -0
- package/Dockerfile +3 -0
- package/LICENSE +21 -0
- package/MISTAKES-COVERAGE.json +115 -0
- package/Makefile +95 -0
- package/PHASE-F-ROADMAP.md +554 -0
- package/README.md +459 -0
- package/SELF_HOSTING_CHECKLIST.md +155 -0
- package/benchmark-freelang.fl +47 -0
- package/benchmark-results.json +37 -0
- package/benchmark-simple.fl +38 -0
- package/bin/freelang-migrate +310 -0
- package/bin/freelang-smart +258 -0
- package/bootstrap.js +1134 -0
- package/docker-compose.yml +3 -0
- package/docs/AI_FRIENDLINESS.md +181 -0
- package/docs/AI_LEARNING_PATH.md +542 -0
- package/docs/AI_MAINTENANCE.md +153 -0
- package/docs/AI_QUICKSTART.md +380 -0
- package/docs/AI_REFERENCE.md +559 -0
- package/docs/AI_RELIABILITY_GUIDE.md +597 -0
- package/docs/AKL_ARCHITECTURE.md +228 -0
- package/docs/AKL_P0_KIMDB.md +215 -0
- package/docs/API.md +380 -0
- package/docs/ARCHITECTURE.md +208 -0
- package/docs/CHANGELOG.md +1014 -0
- package/docs/CLAUDE.md +991 -0
- package/docs/CLAUDE_AI.md +210 -0
- package/docs/CODEGEN_IMPROVEMENTS.md +158 -0
- package/docs/CODE_OF_CONDUCT.md +337 -0
- package/docs/CONTRIBUTING.md +437 -0
- package/docs/CRON_AUTOMATION.md +97 -0
- package/docs/DEPLOYMENT.md +357 -0
- package/docs/DETERMINISM_GUIDE.md +174 -0
- package/docs/HUMAN_GUIDE.md +599 -0
- package/docs/INDEX.md +199 -0
- package/docs/INLINE_TEST_GUIDE.md +231 -0
- package/docs/LANGUAGE-FAULTS.md +583 -0
- package/docs/LANGUAGE_IMPROVEMENT_REQUESTS.md +187 -0
- package/docs/LEARNING.md +257 -0
- package/docs/MISTAKES-100.md +52 -0
- package/docs/MISTAKES.md +131 -0
- package/docs/NAMING_CONVENTIONS.md +158 -0
- package/docs/NIGHTLY-MONITORING.md +222 -0
- package/docs/OFFICIAL_LANGUAGE.md +249 -0
- package/docs/ONBOARDING_1HOUR.md +210 -0
- package/docs/PERFORMANCE.md +329 -0
- package/docs/PHASE-3C-L2-PROOF-PLAN.md +282 -0
- package/docs/PHASE-3C-L2-RESULTS.md +183 -0
- package/docs/PHASE-3D-AI-LIBRARY.md +170 -0
- package/docs/PHASE-3E-VM-OPTIMIZATION.md +192 -0
- package/docs/PHASE-C-CI.md +242 -0
- package/docs/PHASE-C-PR-CHECKLIST.md +267 -0
- package/docs/PHASE-C-ROADMAP.md +141 -0
- package/docs/PHASE-C-SCAN.md +205 -0
- package/docs/PHASE-G-ROADMAP.md +228 -0
- package/docs/PHASE-X-V11.5-ROADMAP.md +267 -0
- package/docs/PHASE4_IMPLEMENTATION_PLAN.md +290 -0
- package/docs/PHASE5-COMPLETION.md +186 -0
- package/docs/PHASE5-SUMMARY.md +82 -0
- package/docs/PLUGIN_GUIDE.md +241 -0
- package/docs/PROJECT_OVERVIEW_KO.md +66 -0
- package/docs/QUICKSTART.md +160 -0
- package/docs/README.md +192 -0
- package/docs/RESERVED.md +125 -0
- package/docs/ROADMAP.md +191 -0
- package/docs/SECURITY.md +518 -0
- package/docs/SETUP.md +127 -0
- package/docs/SLACK-SETUP.md +190 -0
- package/docs/STATE_OF_V11.md +74 -0
- package/docs/STDLIB_NAMING_AUDIT.md +154 -0
- package/docs/STDLIB_REFERENCE.md +2722 -0
- package/docs/STYLE_GUIDE.md +559 -0
- package/docs/TASK5-MAP-DESTRUCTURE-DESIGN.md +136 -0
- package/docs/TOOLS.md +357 -0
- package/docs/TYPE_SYSTEM_GUIDE.md +201 -0
- package/docs/UI-PATTERN.md +265 -0
- package/docs/V11.5-RULES.md +416 -0
- package/docs/V11.6-HARNESS-RULES.md +222 -0
- package/docs/V11.6-MULTI-AGENT-PLAN.md +265 -0
- package/docs/V11.6-STABILIZATION-PLAN.md +290 -0
- package/docs/Y4-2B_DRY_RUN.md +142 -0
- package/docs/_classifications.json +882 -0
- package/docs/api/stdlib.md +134 -0
- package/docs/blog/2026-04-17-v11-ai-first-evolution.md +223 -0
- package/docs/blog/2026-04-20-self-hosting-fixedpoint.md +269 -0
- package/docs/blog/2026-05-04-mistakes-are-language-faults.md +267 -0
- package/docs/blog/2026-05-04-mistakes-coverage-honest.md +170 -0
- package/docs/blog/2026-05-04-phase-g-followup-smart-wrapper.md +188 -0
- package/docs/blog/2026-05-04-phase-g-mistakes-100.md +269 -0
- package/docs/blog/2026-05-04-phase-x-v11.5-launch.md +224 -0
- package/docs/blog/2026-05-04-v11.5.0-real-language-change.md +252 -0
- package/docs/blog-demo/README.md +44 -0
- package/docs/examples/index.md +78 -0
- package/docs/examples/todo-app.md +99 -0
- package/docs/guide/ai-blocks.md +335 -0
- package/docs/guide/basics.md +235 -0
- package/docs/guide/frameworks.md +319 -0
- package/docs/homepage/README.md +164 -0
- package/docs/index.md +183 -0
- package/docs/v12-DESIGN.md +214 -0
- package/examples/README.md +26 -0
- package/examples/browser-test.html +149 -0
- package/examples/csv-tool/analyze.fl +222 -0
- package/examples/csv-tool/employees.csv +16 -0
- package/examples/csv-tool/products.csv +21 -0
- package/examples/factorial.fl +9 -0
- package/examples/fib30.fl +4 -0
- package/examples/hello.fl +2 -0
- package/examples/hello.fl.js +3 -0
- package/examples/learning-session/01-grade-stats.fl +49 -0
- package/examples/learning-session/02-class-stats.fl +52 -0
- package/examples/learning-session/03-cart.fl +61 -0
- package/examples/learning-session/04-word-freq.fl +57 -0
- package/examples/learning-session/05-todo.fl +69 -0
- package/examples/learning-session/06-library.fl +76 -0
- package/examples/learning-session/07-currency.fl +99 -0
- package/examples/learning-session/08-csv-grades.fl +104 -0
- package/examples/learning-session/09-calc-repl.fl +92 -0
- package/examples/learning-session/10-contacts.fl +109 -0
- package/examples/learning-session/11-markdown.fl +107 -0
- package/examples/learning-session/12-calendar.fl +87 -0
- package/examples/learning-session/13-statistics.fl +123 -0
- package/examples/learning-session/14-primes.fl +107 -0
- package/examples/learning-session/15-statistics.fl +123 -0
- package/examples/learning-session/16-calendar.fl +87 -0
- package/examples/learning-session/17-markdown.fl +107 -0
- package/examples/learning-session/18-contacts.fl +109 -0
- package/examples/learning-session/19-json-serializer.fl +86 -0
- package/examples/learning-session/20-template.fl +201 -0
- package/examples/learning-session/21-calc-parser.fl +148 -0
- package/examples/learning-session/22-table.fl +96 -0
- package/examples/patterns/01-map-filter-reduce.fl +50 -0
- package/examples/patterns/02-error-handling.fl +51 -0
- package/examples/patterns/03-type-validation.fl +57 -0
- package/examples/patterns/04-state-management.fl +44 -0
- package/examples/patterns/05-api-integration.fl +95 -0
- package/examples/patterns/06-database-operations.fl +113 -0
- package/examples/patterns/07-string-processing.fl +86 -0
- package/examples/patterns/08-data-transformation.fl +92 -0
- package/examples/patterns/09-decision-logic.fl +122 -0
- package/examples/patterns/10-agent-orchestration.fl +184 -0
- package/examples/patterns/README.md +344 -0
- package/examples/simple-server.fl +29 -0
- package/examples/tables-app.fl +219 -0
- package/examples/test-libs.fl +272 -0
- package/jest.config.js +4 -0
- package/lib/datetime.fl +123 -0
- package/lib/http-client.fl +129 -0
- package/lib/pipeline.fl +150 -0
- package/lib/query.fl +160 -0
- package/lib/result.fl +124 -0
- package/lib/template.fl +161 -0
- package/lib/validate.fl +136 -0
- package/package.json +58 -0
- package/scripts/ai-eval.js +318 -0
- package/scripts/ai-self-verify.js +200 -0
- package/scripts/ai-validate.js +302 -0
- package/scripts/bench.sh +71 -0
- package/scripts/benchmark.js +258 -0
- package/scripts/benchmark.sh +99 -0
- package/scripts/build-binary.sh +68 -0
- package/scripts/build-runtime.sh +46 -0
- package/scripts/build-stage1-final.sh +58 -0
- package/scripts/build-stage1.sh +119 -0
- package/scripts/build-standalone.sh +17 -0
- package/scripts/build.js +130 -0
- package/scripts/check-let-regressions.js +95 -0
- package/scripts/check-parens.py +117 -0
- package/scripts/check-ports.sh +49 -0
- package/scripts/check-syntax.js +30 -0
- package/scripts/cli-extras.js +135 -0
- package/scripts/cron-daily-verify.sh +101 -0
- package/scripts/deploy.sh +91 -0
- package/scripts/dev.sh +37 -0
- package/scripts/extract-fl-examples.js +29 -0
- package/scripts/extract-runtime-helpers.js +122 -0
- package/scripts/fl-compile.sh +14 -0
- package/scripts/fl-fixpoint.sh +28 -0
- package/scripts/fl-repl.sh +21 -0
- package/scripts/fl-run.sh +41 -0
- package/scripts/fl-serve.sh +25 -0
- package/scripts/fuzz-compiler.js +92 -0
- package/scripts/gen-ai-prompt.js +340 -0
- package/scripts/gen-fixtures.js +166 -0
- package/scripts/gen-mistakes-split.js +199 -0
- package/scripts/gen-stdlib-docs.js +69 -0
- package/scripts/lint-stdlib-aliases.js +166 -0
- package/scripts/measure-baseline.sh +43 -0
- package/scripts/property-test.js +885 -0
- package/scripts/push-to-gogs.sh +60 -0
- package/scripts/regression-scan.js +132 -0
- package/scripts/run-jest-shard.sh +85 -0
- package/scripts/safe-push.sh +37 -0
- package/scripts/scan-fl-tokens.js +170 -0
- package/scripts/scan-for-fl-tokens.sh +17 -0
- package/scripts/self-diff.sh +95 -0
- package/scripts/snapshot-v0.sh +35 -0
- package/scripts/test-l2-fixpoint.sh +54 -0
- package/scripts/verify-all.sh +130 -0
- package/scripts/verify-build-deterministic.sh +110 -0
- package/scripts/verify-c7-bootstrap.sh +150 -0
- package/scripts/verify-c8-fixed-point-simple.sh +121 -0
- package/scripts/verify-c8-fixed-point.sh +133 -0
- package/scripts/verify-c9-fuzzing.sh +214 -0
- package/scripts/verify-fixed-point-deep.sh +133 -0
- package/scripts/verify-fixed-point.sh +65 -0
- package/scripts/verify-l2-proof.sh +153 -0
- package/scripts/verify-l3-proof.sh +40 -0
- package/scripts/verify-mistakes-coverage.js +120 -0
- package/scripts/verify-self-host.sh +509 -0
- package/self/CHANGELOG.md +964 -0
- package/self/COMPILER_REDESIGN_FAILURES.md +59 -0
- package/self/INTEGRATION-CHECKLIST.md +113 -0
- package/self/README.md +34 -0
- package/self/all.fl +1353 -0
- package/self/ast.fl +219 -0
- package/self/ast.self.js +47 -0
- package/self/bench/_bi_json-parse.js +46 -0
- package/self/bench/_bi_json-str.js +60 -0
- package/self/bench/_bi_list-distinct.js +37 -0
- package/self/bench/_bi_list-filter-even.js +56 -0
- package/self/bench/_bi_list-first.js +29 -0
- package/self/bench/_bi_list-last.js +28 -0
- package/self/bench/_bi_list-map-inc.js +41 -0
- package/self/bench/_bi_list-range.js +22 -0
- package/self/bench/_bi_list-reduce-sum.js +50 -0
- package/self/bench/_bi_list-reverse.js +28 -0
- package/self/bench/_bi_list-sort.js +35 -0
- package/self/bench/_bi_list-take.js +36 -0
- package/self/bench/_bi_m-abs.js +24 -0
- package/self/bench/_bi_m-floor.js +22 -0
- package/self/bench/_bi_m-max.js +26 -0
- package/self/bench/_bi_m-min.js +26 -0
- package/self/bench/_bi_m-mod.js +21 -0
- package/self/bench/_bi_map-has.js +50 -0
- package/self/bench/_bi_map-keys.js +50 -0
- package/self/bench/_bi_map-values.js +52 -0
- package/self/bench/_bi_str-concat.js +36 -0
- package/self/bench/_bi_str-contains.js +43 -0
- package/self/bench/_bi_str-ends.js +45 -0
- package/self/bench/_bi_str-index.js +34 -0
- package/self/bench/_bi_str-lower.js +23 -0
- package/self/bench/_bi_str-repeat.js +26 -0
- package/self/bench/_bi_str-split.js +41 -0
- package/self/bench/_bi_str-starts.js +47 -0
- package/self/bench/_bi_str-trim.js +26 -0
- package/self/bench/_bi_str-upper.js +23 -0
- package/self/bench/_bi_t-list.js +24 -0
- package/self/bench/_bi_t-null.js +23 -0
- package/self/bench/_bi_t-number.js +23 -0
- package/self/bench/_bi_t-string.js +25 -0
- package/self/bench/_bi_t-typeof.js +25 -0
- package/self/bench/_c_parse_probe.fl +426 -0
- package/self/bench/_c_parse_probe.js +67 -0
- package/self/bench/_codegen_nodrv.fl +751 -0
- package/self/bench/_combined2.fl +423 -0
- package/self/bench/_combined_arr.fl +422 -0
- package/self/bench/_combined_astprobe.fl +429 -0
- package/self/bench/_combined_astprobe.js +69 -0
- package/self/bench/_combined_cg.fl +424 -0
- package/self/bench/_combined_cg.js +64 -0
- package/self/bench/_combined_lex.fl +421 -0
- package/self/bench/_combined_trace.fl +435 -0
- package/self/bench/_combined_trace.js +75 -0
- package/self/bench/_ext_abs-neg.js +2 -0
- package/self/bench/_ext_and.js +2 -0
- package/self/bench/_ext_cond-1.js +2 -0
- package/self/bench/_ext_cond-2.js +2 -0
- package/self/bench/_ext_do-seq.js +2 -0
- package/self/bench/_ext_fact20.js +3 -0
- package/self/bench/_ext_fib25.js +3 -0
- package/self/bench/_ext_length.js +2 -0
- package/self/bench/_ext_let-1d.js +18 -0
- package/self/bench/_ext_let-2d.js +2 -0
- package/self/bench/_ext_list-sum.js +3 -0
- package/self/bench/_ext_math.js +2 -0
- package/self/bench/_ext_or.js +2 -0
- package/self/bench/_ext_str.js +2 -0
- package/self/bench/_fact15.js +2 -0
- package/self/bench/_fb_fact15.js +2 -0
- package/self/bench/_fb_func-fact.js +2 -0
- package/self/bench/_fb_func-multi.js +2 -0
- package/self/bench/_fb_func-mutual.js +3 -0
- package/self/bench/_fb_func-simple.js +21 -0
- package/self/bench/_fib.js +2 -0
- package/self/bench/_fn_async-await-seq.js +40 -0
- package/self/bench/_fn_async-await.js +3 -0
- package/self/bench/_fn_async-print.js +26 -0
- package/self/bench/_fn_async-result.js +1 -0
- package/self/bench/_fn_call-form.js +24 -0
- package/self/bench/_fn_compose.js +37 -0
- package/self/bench/_fn_pipe.js +34 -0
- package/self/bench/_fn_thread-first-sexpr.js +31 -0
- package/self/bench/_fn_thread-first.js +25 -0
- package/self/bench/_fn_thread-last.js +31 -0
- package/self/bench/_generated.js +35 -0
- package/self/bench/_mt_match-default.js +51 -0
- package/self/bench/_mt_match-num.js +58 -0
- package/self/bench/_mt_match-str.js +56 -0
- package/self/bench/_mt_match-var.js +32 -0
- package/self/bench/_mt_match-wild.js +36 -0
- package/self/bench/_mt_struct-make.js +81 -0
- package/self/bench/_mt_struct-tag.js +81 -0
- package/self/bench/_probe.fl +1 -0
- package/self/bench/_probe2.fl +1 -0
- package/self/bench/_probe3.fl +3 -0
- package/self/bench/_probe_apply.fl +1 -0
- package/self/bench/_probe_args.fl +7 -0
- package/self/bench/_probe_arr.fl +7 -0
- package/self/bench/_probe_arr2.fl +5 -0
- package/self/bench/_probe_block.fl +7 -0
- package/self/bench/_probe_call.fl +1 -0
- package/self/bench/_probe_cg.fl +27 -0
- package/self/bench/_probe_compose.fl +13 -0
- package/self/bench/_probe_define.fl +1 -0
- package/self/bench/_probe_keys.fl +5 -0
- package/self/bench/_probe_length.fl +5 -0
- package/self/bench/_probe_lexparse.fl +4 -0
- package/self/bench/_probe_loop.fl +12 -0
- package/self/bench/_probe_map.fl +8 -0
- package/self/bench/_probe_map2.fl +8 -0
- package/self/bench/_probe_map3.fl +5 -0
- package/self/bench/_probe_mapkeys.fl +5 -0
- package/self/bench/_probe_mapv12.fl +1 -0
- package/self/bench/_probe_mapv12.js +2 -0
- package/self/bench/_probe_match.fl +1 -0
- package/self/bench/_probe_match2.fl +1 -0
- package/self/bench/_probe_neg.fl +7 -0
- package/self/bench/_probe_neg2.fl +3 -0
- package/self/bench/_probe_protocol.fl +7 -0
- package/self/bench/_probe_self_parse.fl +6 -0
- package/self/bench/_probe_struct.fl +1 -0
- package/self/bench/_probe_true.fl +3 -0
- package/self/bench/_probe_true2.fl +6 -0
- package/self/bench/_probe_v12_cg.fl +7 -0
- package/self/bench/_probe_v12_extract.fl +8 -0
- package/self/bench/_probe_v12_extract.js +7 -0
- package/self/bench/_probe_v12_internal.fl +21 -0
- package/self/bench/_probe_v12_map_ast.fl +12 -0
- package/self/bench/_rs_combo.js +3 -0
- package/self/bench/_rs_gcd.js +36 -0
- package/self/bench/_rs_grade.js +2 -0
- package/self/bench/_rs_mutual-fib.js +2 -0
- package/self/bench/_rs_range-sum.js +2 -0
- package/self/bench/_rs_sum-10k.js +2 -0
- package/self/bench/_rs_sum-5k.js +2 -0
- package/self/bench/_sf_loop-fact-10.js +73 -0
- package/self/bench/_sf_loop-sum-10k.js +87 -0
- package/self/bench/_sf_set-x.js +40 -0
- package/self/bench/_sf_throw-catch.js +1 -0
- package/self/bench/_sf_while-5.js +62 -0
- package/self/bench/_v12_combined.fl +1184 -0
- package/self/bench/_v12_extract_test.fl +14 -0
- package/self/bench/_v12_extract_test.js +7 -0
- package/self/bench/_v12_inspect.js +26 -0
- package/self/bench/_v12_inspect_mod.js +137 -0
- package/self/bench/_v12_makevartest.fl +8 -0
- package/self/bench/_v12_makevartest.js +6 -0
- package/self/bench/_v12_parse_probe.fl +9 -0
- package/self/bench/_v12_parse_trace.fl +18 -0
- package/self/bench/_v12_simple_fn.fl +2 -0
- package/self/bench/_v12_simple_fn.js +3 -0
- package/self/bench/_v12_trace.fl +2 -0
- package/self/bench/_v12_trace.js +3 -0
- package/self/bench/collection.js +13 -0
- package/self/bench/fib30.fl +4 -0
- package/self/bench/final-test.fl +12 -0
- package/self/bench/final-test.js +2 -0
- package/self/bench/fp1-v12.js +4 -0
- package/self/bench/fp1.fl +3 -0
- package/self/bench/fp1.fp.js +4 -0
- package/self/bench/fp2-v12.js +4 -0
- package/self/bench/fp2.fl +3 -0
- package/self/bench/fp2.fp.js +4 -0
- package/self/bench/fp3-v12.js +8 -0
- package/self/bench/fp3.fl +11 -0
- package/self/bench/fp3.fp.js +8 -0
- package/self/bench/fp4-v12.js +6 -0
- package/self/bench/fp4.fl +9 -0
- package/self/bench/fp4.fp.js +6 -0
- package/self/bench/hello-sample.fl +3 -0
- package/self/bench/hello-sample.js +4 -0
- package/self/bench/hello.fl +1 -0
- package/self/bench/json.js +9 -0
- package/self/bench/json1mb.fl +3 -0
- package/self/bench/math.js +8 -0
- package/self/bench/realworld.fl +17 -0
- package/self/bench/realworld.js +8 -0
- package/self/bench/regex.js +10 -0
- package/self/bench/sample.json +1 -0
- package/self/bench/string.js +7 -0
- package/self/bench/test-ffi.fl +3 -0
- package/self/bench/test-ffi.js +2 -0
- package/self/bench/test-hof-advanced.fl +7 -0
- package/self/bench/test-hof-advanced.js +2 -0
- package/self/bench/test-hof.fl +5 -0
- package/self/bench/test-hof.js +2 -0
- package/self/bench/test-output.txt +1 -0
- package/self/bench/test-phase15-integration.fl +16 -0
- package/self/bench/test-phase15-integration.js +3 -0
- package/self/bench/test-phase19-complete.fl +24 -0
- package/self/bench/test-phase19-complete.js +2 -0
- package/self/bench/test-stdlib-codegen.fl +9 -0
- package/self/bench/test-stdlib-codegen.js +2 -0
- package/self/bench/test-time.fl +8 -0
- package/self/bench/test-time.js +2 -0
- package/self/bench/time.js +9 -0
- package/self/bench/tiny-v12.js +2 -0
- package/self/bench/tiny.fl +1 -0
- package/self/builtins/core.fl +150 -0
- package/self/char-class.fl +45 -0
- package/self/codegen-core.fl +733 -0
- package/self/codegen-final.fl +135 -0
- package/self/codegen.fl +379 -0
- package/self/codegen.fl.bak +856 -0
- package/self/codegen.self.js +75 -0
- package/self/examples/agent-loop.fl +32 -0
- package/self/examples/ci-cd-example.fl +114 -0
- package/self/examples/cloud-real.fl +134 -0
- package/self/examples/docker-example.fl +67 -0
- package/self/examples/fullstack-app.fl +330 -0
- package/self/examples/k8s-example.fl +91 -0
- package/self/examples/microservices.fl +448 -0
- package/self/examples/rest-api.fl +253 -0
- package/self/examples/style-example.fl +133 -0
- package/self/examples/workflow-orchestration.fl +44 -0
- package/self/fixtures/eval/add.fl +1 -0
- package/self/fixtures/eval/and-sc.fl +1 -0
- package/self/fixtures/eval/bool-false.fl +1 -0
- package/self/fixtures/eval/bool-true.fl +1 -0
- package/self/fixtures/eval/closure.fl +1 -0
- package/self/fixtures/eval/cmp-eq.fl +1 -0
- package/self/fixtures/eval/cmp-lt.fl +1 -0
- package/self/fixtures/eval/cond-first.fl +1 -0
- package/self/fixtures/eval/define-fn3.fl +1 -0
- package/self/fixtures/eval/define-var.fl +1 -0
- package/self/fixtures/eval/defn-bare.fl +1 -0
- package/self/fixtures/eval/defn-call.fl +1 -0
- package/self/fixtures/eval/div.fl +1 -0
- package/self/fixtures/eval/filter-hof.fl +1 -0
- package/self/fixtures/eval/floor.fl +1 -0
- package/self/fixtures/eval/fn-apply.fl +1 -0
- package/self/fixtures/eval/get-idx.fl +1 -0
- package/self/fixtures/eval/get-key.fl +1 -0
- package/self/fixtures/eval/higher-order.fl +1 -0
- package/self/fixtures/eval/let-bare.fl +1 -0
- package/self/fixtures/eval/let-flat.fl +1 -0
- package/self/fixtures/eval/let-use.fl +1 -0
- package/self/fixtures/eval/list-first.fl +1 -0
- package/self/fixtures/eval/list-last.fl +1 -0
- package/self/fixtures/eval/list-len.fl +1 -0
- package/self/fixtures/eval/map-hof.fl +1 -0
- package/self/fixtures/eval/mod.fl +1 -0
- package/self/fixtures/eval/mul-nested.fl +1 -0
- package/self/fixtures/eval/not.fl +1 -0
- package/self/fixtures/eval/null-p.fl +1 -0
- package/self/fixtures/eval/or-sc.fl +1 -0
- package/self/fixtures/eval/pow.fl +1 -0
- package/self/fixtures/eval/recursion.fl +1 -0
- package/self/fixtures/eval/reduce-hof.fl +1 -0
- package/self/fixtures/eval/replace.fl +1 -0
- package/self/fixtures/eval/sqrt.fl +1 -0
- package/self/fixtures/eval/str-concat.fl +1 -0
- package/self/fixtures/eval/str-num.fl +1 -0
- package/self/fixtures/eval/sub.fl +1 -0
- package/self/fixtures/eval/substring.fl +1 -0
- package/self/fixtures/lex/array.fl +1 -0
- package/self/fixtures/lex/at-atom.fl +1 -0
- package/self/fixtures/lex/block.fl +1 -0
- package/self/fixtures/lex/comment.fl +2 -0
- package/self/fixtures/lex/deep-array.fl +1 -0
- package/self/fixtures/lex/deep-map.fl +1 -0
- package/self/fixtures/lex/deep-nest.fl +1 -0
- package/self/fixtures/lex/dollar-dot.fl +1 -0
- package/self/fixtures/lex/empty-list.fl +1 -0
- package/self/fixtures/lex/empty-str.fl +1 -0
- package/self/fixtures/lex/empty.fl +1 -0
- package/self/fixtures/lex/float.fl +1 -0
- package/self/fixtures/lex/hex.fl +1 -0
- package/self/fixtures/lex/kebab-sym.fl +1 -0
- package/self/fixtures/lex/keyword.fl +1 -0
- package/self/fixtures/lex/leading-zero.fl +1 -0
- package/self/fixtures/lex/long-num.fl +1 -0
- package/self/fixtures/lex/many-args.fl +1 -0
- package/self/fixtures/lex/map.fl +1 -0
- package/self/fixtures/lex/mixed.fl +1 -0
- package/self/fixtures/lex/neg-float.fl +1 -0
- package/self/fixtures/lex/negative.fl +1 -0
- package/self/fixtures/lex/nested.fl +1 -0
- package/self/fixtures/lex/null.fl +1 -0
- package/self/fixtures/lex/pipe-op.fl +1 -0
- package/self/fixtures/lex/route-like.fl +1 -0
- package/self/fixtures/lex/scientific.fl +1 -0
- package/self/fixtures/lex/single-num.fl +1 -0
- package/self/fixtures/lex/small-sexpr.fl +1 -0
- package/self/fixtures/lex/spaces.fl +1 -0
- package/self/fixtures/lex/str-quote.fl +1 -0
- package/self/fixtures/lex/str-tab.fl +1 -0
- package/self/fixtures/lex/string-esc.fl +1 -0
- package/self/fixtures/lex/string.fl +1 -0
- package/self/fixtures/lex/symbol.fl +1 -0
- package/self/fixtures/lex/tabs-nl.fl +3 -0
- package/self/fixtures/lex/true-false.fl +1 -0
- package/self/fixtures/lex/unicode.fl +1 -0
- package/self/fixtures/lex/variable.fl +1 -0
- package/self/fixtures/lex/zero.fl +1 -0
- package/self/fixtures/parse/and-or.fl +1 -0
- package/self/fixtures/parse/array-lit.fl +1 -0
- package/self/fixtures/parse/block-func.fl +1 -0
- package/self/fixtures/parse/compose.fl +1 -0
- package/self/fixtures/parse/cond.fl +1 -0
- package/self/fixtures/parse/define-fn.fl +1 -0
- package/self/fixtures/parse/define-val.fl +1 -0
- package/self/fixtures/parse/defn.fl +1 -0
- package/self/fixtures/parse/do-begin.fl +1 -0
- package/self/fixtures/parse/dotted.fl +1 -0
- package/self/fixtures/parse/empty-fn.fl +1 -0
- package/self/fixtures/parse/empty-list.fl +1 -0
- package/self/fixtures/parse/fn.fl +1 -0
- package/self/fixtures/parse/if-else.fl +1 -0
- package/self/fixtures/parse/if-only.fl +1 -0
- package/self/fixtures/parse/keyword-arg.fl +1 -0
- package/self/fixtures/parse/kw.fl +1 -0
- package/self/fixtures/parse/let-1d.fl +1 -0
- package/self/fixtures/parse/let-2d.fl +1 -0
- package/self/fixtures/parse/let-bare.fl +1 -0
- package/self/fixtures/parse/literal-num.fl +1 -0
- package/self/fixtures/parse/literal-str.fl +1 -0
- package/self/fixtures/parse/loop-recur.fl +1 -0
- package/self/fixtures/parse/map-lit.fl +1 -0
- package/self/fixtures/parse/match-simple.fl +1 -0
- package/self/fixtures/parse/multi-body.fl +1 -0
- package/self/fixtures/parse/nested-arr.fl +1 -0
- package/self/fixtures/parse/nested-block.fl +1 -0
- package/self/fixtures/parse/nested-map.fl +1 -0
- package/self/fixtures/parse/nested.fl +1 -0
- package/self/fixtures/parse/pipe.fl +1 -0
- package/self/fixtures/parse/quote.fl +1 -0
- package/self/fixtures/parse/set-bang.fl +1 -0
- package/self/fixtures/parse/sexpr.fl +1 -0
- package/self/fixtures/parse/str-interp.fl +1 -0
- package/self/fixtures/parse/sym.fl +1 -0
- package/self/fixtures/parse/thread-first.fl +1 -0
- package/self/fixtures/parse/thread-last.fl +1 -0
- package/self/fixtures/parse/try-catch.fl +1 -0
- package/self/fixtures/parse/var.fl +1 -0
- package/self/full-compiler-fixed.fl +1308 -0
- package/self/full-compiler-v1-bloated.fl +1298 -0
- package/self/full-compiler.fl +1301 -0
- package/self/interpreter.fl +351 -0
- package/self/lexer.fl +200 -0
- package/self/lexer.self.js +30 -0
- package/self/main.fl +22 -0
- package/self/parser.fl +241 -0
- package/self/parser.self.js +33 -0
- package/self/runtime/http-server.js +642 -0
- package/self/runtime/interpreter.js +30787 -0
- package/self/runtime/repl.js +186 -0
- package/self/runtime-helpers.ts +282 -0
- package/self/scope.fl +80 -0
- package/self/scope.self.js +1 -0
- package/self/self/bench/_bi_list-distinct.js +2 -0
- package/self/self/bench/_bi_list-filter-even.js +2 -0
- package/self/self/bench/_bi_list-first.js +2 -0
- package/self/self/bench/_bi_list-last.js +2 -0
- package/self/self/bench/_bi_list-map-inc.js +2 -0
- package/self/self/bench/_bi_list-range.js +2 -0
- package/self/self/bench/_bi_list-reduce-sum.js +2 -0
- package/self/self/bench/_bi_list-reverse.js +2 -0
- package/self/self/bench/_bi_list-sort.js +2 -0
- package/self/self/bench/_bi_list-take.js +2 -0
- package/self/self/bench/_bi_map-has.js +2 -0
- package/self/self/bench/_bi_map-keys.js +2 -0
- package/self/self/bench/_bi_map-values.js +2 -0
- package/self/self/bench/_bi_str-concat.js +2 -0
- package/self/self/bench/_bi_str-contains.js +2 -0
- package/self/self/bench/_bi_str-ends.js +2 -0
- package/self/self/bench/_bi_str-index.js +2 -0
- package/self/self/bench/_bi_str-lower.js +2 -0
- package/self/self/bench/_bi_str-repeat.js +2 -0
- package/self/self/bench/_bi_str-split.js +2 -0
- package/self/self/bench/_bi_str-starts.js +2 -0
- package/self/self/bench/_bi_str-trim.js +2 -0
- package/self/self/bench/_bi_str-upper.js +2 -0
- package/self/self/bench/_ext_abs-neg.js +2 -0
- package/self/self/bench/_ext_and.js +2 -0
- package/self/self/bench/_ext_cond-1.js +2 -0
- package/self/self/bench/_ext_cond-2.js +2 -0
- package/self/self/bench/_ext_do-seq.js +2 -0
- package/self/self/bench/_ext_fact20.js +3 -0
- package/self/self/bench/_ext_fib25.js +3 -0
- package/self/self/bench/_ext_length.js +2 -0
- package/self/self/bench/_ext_let-1d.js +2 -0
- package/self/self/bench/_ext_let-2d.js +2 -0
- package/self/self/bench/_ext_list-sum.js +3 -0
- package/self/self/bench/_ext_math.js +2 -0
- package/self/self/bench/_ext_or.js +2 -0
- package/self/self/bench/_ext_str.js +2 -0
- package/self/self/bench/_fact15.js +2 -0
- package/self/self/bench/_fb_fact15.js +2 -0
- package/self/self/bench/_fb_func-fact.js +2 -0
- package/self/self/bench/_fb_func-multi.js +2 -0
- package/self/self/bench/_fb_func-mutual.js +3 -0
- package/self/self/bench/_fb_func-simple.js +2 -0
- package/self/self/bench/_fib.js +2 -0
- package/self/self/bench/_fn_async-await-seq.js +2 -0
- package/self/self/bench/_fn_async-print.js +2 -0
- package/self/self/bench/_fn_call-form.js +2 -0
- package/self/self/bench/_fn_compose.js +2 -0
- package/self/self/bench/_fn_pipe.js +2 -0
- package/self/self/bench/_fn_thread-first-sexpr.js +2 -0
- package/self/self/bench/_fn_thread-first.js +2 -0
- package/self/self/bench/_fn_thread-last.js +2 -0
- package/self/self/bench/_generated.js +2 -0
- package/self/self/bench/_mt_match-default.js +2 -0
- package/self/self/bench/_mt_match-num.js +2 -0
- package/self/self/bench/_mt_match-str.js +2 -0
- package/self/self/bench/_mt_match-var.js +2 -0
- package/self/self/bench/_mt_match-wild.js +2 -0
- package/self/self/bench/_mt_struct-make.js +4 -0
- package/self/self/bench/_mt_struct-tag.js +4 -0
- package/self/self/bench/_rs_combo.js +3 -0
- package/self/self/bench/_rs_gcd.js +2 -0
- package/self/self/bench/_rs_grade.js +2 -0
- package/self/self/bench/_rs_mutual-fib.js +2 -0
- package/self/self/bench/_rs_range-sum.js +2 -0
- package/self/self/bench/_rs_sum-5k.js +2 -0
- package/self/self/bench/_sf_loop-fact-10.js +1 -0
- package/self/self/bench/_sf_loop-sum-10k.js +1 -0
- package/self/self/bench/_sf_set-x.js +3 -0
- package/self/self/bench/_sf_throw-catch.js +1 -0
- package/self/self/bench/_sf_while-5.js +3 -0
- package/self/stdlib/ai.fl +73 -0
- package/self/stdlib/assert.fl +30 -0
- package/self/stdlib/async.fl +48 -0
- package/self/stdlib/base64.fl +22 -0
- package/self/stdlib/binary.fl +30 -0
- package/self/stdlib/build.fl +69 -0
- package/self/stdlib/collection.fl +37 -0
- package/self/stdlib/color.fl +35 -0
- package/self/stdlib/crypto-hash.fl +29 -0
- package/self/stdlib/crypto.fl +56 -0
- package/self/stdlib/data.fl +69 -0
- package/self/stdlib/db.fl +53 -0
- package/self/stdlib/encoding.fl +49 -0
- package/self/stdlib/error.fl +36 -0
- package/self/stdlib/feed.fl +86 -0
- package/self/stdlib/file.fl +53 -0
- package/self/stdlib/format.fl +34 -0
- package/self/stdlib/graph.fl +72 -0
- package/self/stdlib/hash.fl +32 -0
- package/self/stdlib/heap.fl +76 -0
- package/self/stdlib/http.fl +47 -0
- package/self/stdlib/image.fl +45 -0
- package/self/stdlib/json.fl +11 -0
- package/self/stdlib/list-extra.fl +32 -0
- package/self/stdlib/markdown.fl +59 -0
- package/self/stdlib/math-extra.fl +34 -0
- package/self/stdlib/math.fl +13 -0
- package/self/stdlib/metadata.fl +66 -0
- package/self/stdlib/mongodb/bson.fl +21 -0
- package/self/stdlib/mongodb/schema.fl +95 -0
- package/self/stdlib/mongodb/wire.fl +54 -0
- package/self/stdlib/mongodb.fl +172 -0
- package/self/stdlib/path.fl +72 -0
- package/self/stdlib/plot.fl +56 -0
- package/self/stdlib/process.fl +50 -0
- package/self/stdlib/queue.fl +65 -0
- package/self/stdlib/regex.fl +11 -0
- package/self/stdlib/resource.fl +61 -0
- package/self/stdlib/search.fl +54 -0
- package/self/stdlib/set-extra.fl +35 -0
- package/self/stdlib/sort.fl +61 -0
- package/self/stdlib/stack.fl +54 -0
- package/self/stdlib/stats.fl +110 -0
- package/self/stdlib/stream.fl +56 -0
- package/self/stdlib/string-extended.fl +25 -0
- package/self/stdlib/string-extra.fl +40 -0
- package/self/stdlib/string.fl +21 -0
- package/self/stdlib/test-helpers.fl +51 -0
- package/self/stdlib/test-runner.fl +76 -0
- package/self/stdlib/time-extra.fl +27 -0
- package/self/stdlib/time.fl +31 -0
- package/self/stdlib/tree.fl +57 -0
- package/self/stdlib/types.fl +85 -0
- package/self/stdlib/url.fl +33 -0
- package/self/stdlib/uuid.fl +28 -0
- package/self/stdlib/validation.fl +42 -0
- package/self/stdlib/vector-math.fl +52 -0
- package/self/stdlib/ws.fl +52 -0
- package/self/tests/mongodb-phase3.fl +75 -0
- package/self/tests/mongodb-wire-phase2.fl +45 -0
- package/self/tests/test-ast.fl +36 -0
- package/self/tests/test-builtins.fl +340 -0
- package/self/tests/test-char-class.fl +29 -0
- package/self/tests/test-codegen-builtins.fl +240 -0
- package/self/tests/test-codegen-ext.fl +380 -0
- package/self/tests/test-codegen-ffi.fl +295 -0
- package/self/tests/test-codegen-fn.fl +177 -0
- package/self/tests/test-codegen-match.fl +161 -0
- package/self/tests/test-codegen-run.fl +252 -0
- package/self/tests/test-codegen-sf.fl +262 -0
- package/self/tests/test-codegen.fl +260 -0
- package/self/tests/test-core-fl.fl +63 -0
- package/self/tests/test-forward-decl.fl +5 -0
- package/self/tests/test-func-block.fl +4 -0
- package/self/tests/test-interp-user-fn.fl +296 -0
- package/self/tests/test-interp.fl +225 -0
- package/self/tests/test-lexer.fl +162 -0
- package/self/tests/test-list-debug.fl +21 -0
- package/self/tests/test-mutual-rec.fl +6 -0
- package/self/tests/test-parser-debug.fl +35 -0
- package/self/tests/test-parser-full-debug.fl +66 -0
- package/self/tests/test-parser-lex-debug.fl +107 -0
- package/self/tests/test-parser-lex-only.fl +40 -0
- package/self/tests/test-parser-simple-debug.fl +35 -0
- package/self/tests/test-parser.fl +262 -0
- package/self/tests/test-real-stdlib.fl +377 -0
- package/self/tests/test-scope.fl +74 -0
- package/self/tests/test-selfcompile.fl +325 -0
- package/self/tests/test-stdlib.fl +44 -0
- package/self/tests/test-tco.fl +16 -0
- package/self/tests/test-token.fl +4 -0
- package/self/token.fl +38 -0
- package/self/token.self.js +8 -0
- package/self/v12-driver.fl +16 -0
- package/self/verify.fl +39 -0
- package/self-evolve/1-observe-ast.fl +28 -0
- package/self-evolve/2-inspect-block.fl +35 -0
- package/self-evolve/3-full-ast-json.fl +10 -0
- package/self-evolve/4-trace-block-fields.fl +23 -0
- package/self-evolve/5-analyze-do-ast.fl +29 -0
- package/self-evolve/6-test-do-elim-simple.fl +14 -0
- package/self-evolve/debug-evaluate.fl +24 -0
- package/self-evolve/debug-gen1-const-fold.fl +57 -0
- package/self-evolve/debug-map.fl +15 -0
- package/self-evolve/debug-optimize-simple.fl +95 -0
- package/self-evolve/debug-recursive-map.fl +23 -0
- package/self-evolve/logs/gen-0.fl +1658 -0
- package/self-evolve/logs/gen-1.fl +1658 -0
- package/self-evolve/logs/gen-2.fl +1658 -0
- package/self-evolve/logs/speed-gen-0.fl +1423 -0
- package/self-evolve/run-semantic-test.js +130 -0
- package/self-evolve/test-basic.fl +19 -0
- package/self-evolve/test-fold-direct.fl +60 -0
- package/self-evolve/test-gen1-simple.fl +67 -0
- package/self-evolve/test-optimize-debug.fl +63 -0
- package/self-evolve/tmp-test.fl +1 -0
- package/self-evolve/v11-analyzer.fl +40 -0
- package/self-evolve/v11-benchmark.fl +45 -0
- package/self-evolve/v11-evolution-real.fl +45 -0
- package/self-evolve/v11-evolution.fl +32 -0
- package/self-evolve/v11-gen1-validate.fl +157 -0
- package/self-evolve/v11-optimizer-constant-fold.fl +124 -0
- package/self-evolve/v11-optimizer-dead-expr.fl +111 -0
- package/self-evolve/v11-optimizer-gen1-complete.fl +180 -0
- package/self-evolve/v11-optimizer-gen1.fl +68 -0
- package/self-evolve/v11-optimizer.fl +56 -0
- package/self-evolve/v11-semantic-test.fl +54 -0
- package/self-evolve/v11-speed-benchmark.fl +106 -0
- package/self-evolve/v11-speed-optimizer.fl +79 -0
- package/src/AGENT-DSL.md +527 -0
- package/src/CLI-DEPLOYMENT.md +204 -0
- package/src/CLI.md +361 -0
- package/src/EXPRESS-ADVANCED.md +294 -0
- package/src/EXPRESS-AUTH.md +365 -0
- package/src/EXPRESS-CACHE.md +409 -0
- package/src/EXPRESS-COMPLETE.md +569 -0
- package/src/EXPRESS-README.md +121 -0
- package/src/EXPRESS-TEST.md +492 -0
- package/src/EXPRESS-WEBSOCKET.md +391 -0
- package/src/LOGGING-GUIDE.md +354 -0
- package/src/STORAGE-GUIDE.md +416 -0
- package/src/__tests__/advanced.test.ts +573 -0
- package/src/__tests__/ai-library.test.ts +210 -0
- package/src/__tests__/build-determinism.test.ts +33 -0
- package/src/__tests__/builtins-advanced.test.ts +150 -0
- package/src/__tests__/builtins-sanity.test.ts +180 -0
- package/src/__tests__/codegen.let.test.ts +81 -0
- package/src/__tests__/core.test.ts +188 -0
- package/src/__tests__/coverage-boost.test.ts +626 -0
- package/src/__tests__/cron-scheduler.test.ts +269 -0
- package/src/__tests__/enterprise-blocks.test.ts +63 -0
- package/src/__tests__/errors.test.ts +313 -0
- package/src/__tests__/integration.test.ts +219 -0
- package/src/__tests__/interpreter.test.ts +170 -0
- package/src/__tests__/l2-proof.test.ts +78 -0
- package/src/__tests__/lexer-parser.test.ts +366 -0
- package/src/__tests__/lexer.test.ts +106 -0
- package/src/__tests__/mariadb-prepared-statement.test.ts +206 -0
- package/src/__tests__/migrate.test.ts +403 -0
- package/src/__tests__/mongodb-integration.test.ts.skip +207 -0
- package/src/__tests__/mongodb-phase4.test.ts +132 -0
- package/src/__tests__/p1-1-parallel-tasks.test.ts +179 -0
- package/src/__tests__/p1-2-compensation.test.ts +242 -0
- package/src/__tests__/p1-3-distributed.test.ts +160 -0
- package/src/__tests__/p1-4-observability.test.ts +161 -0
- package/src/__tests__/parser.test.ts +142 -0
- package/src/__tests__/phase151-self-evolve.test.ts +161 -0
- package/src/__tests__/rate-limiter.test.ts +274 -0
- package/src/__tests__/self-hosting.test.ts +174 -0
- package/src/__tests__/semantic-preservation.test.ts +207 -0
- package/src/__tests__/setup.ts +34 -0
- package/src/__tests__/special-forms-advanced.test.ts +106 -0
- package/src/__tests__/stdlib-crypto-rsa.test.ts +122 -0
- package/src/__tests__/stdlib-f4.test.ts +159 -0
- package/src/__tests__/stdlib-helpers.test.ts +128 -0
- package/src/__tests__/stdlib-modules.test.ts +161 -0
- package/src/__tests__/stdlib-mongodb.test.ts +331 -0
- package/src/__tests__/stdlib-new.test.ts +269 -0
- package/src/__tests__/stdlib-perf.test.ts +125 -0
- package/src/__tests__/stdlib-phase-b.test.ts +249 -0
- package/src/__tests__/stdlib.test.ts +163 -0
- package/src/__tests__/v12-alpha.test.ts +205 -0
- package/src/__tests__/vm-optin.test.ts +141 -0
- package/src/__tests__/web-integration.test.ts +1452 -0
- package/src/__tests__/weblibs.test.ts +210 -0
- package/src/_aliases.json +1123 -0
- package/src/_mongodb_helper.js +272 -0
- package/src/_stdlib-signatures.json +1 -0
- package/src/agent-chain.ts +71 -0
- package/src/agent-dsl.fl +168 -0
- package/src/agent-example-error-handling.fl +51 -0
- package/src/agent-example-sequential.fl +66 -0
- package/src/agent-example-state-tracking.fl +70 -0
- package/src/agent.ts +393 -0
- package/src/align.ts +349 -0
- package/src/analogy.ts +78 -0
- package/src/ast-helpers.ts +199 -0
- package/src/ast.ts +817 -0
- package/src/async-runtime.ts +263 -0
- package/src/belief.ts +86 -0
- package/src/benchmark-self.ts +275 -0
- package/src/benchmarks/bench-interpreter.ts +73 -0
- package/src/benchmarks/bench-runner.ts +85 -0
- package/src/benchmarks/bench-vm.ts +128 -0
- package/src/browser-debug-panel.ts +220 -0
- package/src/browser-entry.ts +79 -0
- package/src/browser-stubs/child-process-stubs.ts +6 -0
- package/src/browser-stubs/crypto-stubs.ts +15 -0
- package/src/browser-stubs/misc-stubs.ts +4 -0
- package/src/browser-stubs/node-stubs.ts +17 -0
- package/src/browser-stubs/path-stubs.ts +6 -0
- package/src/bytecode.ts +41 -0
- package/src/causal.ts +258 -0
- package/src/chain-agents.ts +86 -0
- package/src/checkpoint.ts +106 -0
- package/src/ci-runner.ts +279 -0
- package/src/cli.ts +2136 -0
- package/src/codegen-js.ts +847 -0
- package/src/cognitive.ts +95 -0
- package/src/compete.ts +60 -0
- package/src/compiler.ts +267 -0
- package/src/compose-reason.ts +118 -0
- package/src/consensus.ts +109 -0
- package/src/context-window.ts +139 -0
- package/src/cot.ts +241 -0
- package/src/counterfactual.ts +268 -0
- package/src/critique.ts +77 -0
- package/src/crossover.ts +218 -0
- package/src/curiosity.ts +305 -0
- package/src/debate.ts +64 -0
- package/src/debug-api.ts +92 -0
- package/src/debugger.ts +185 -0
- package/src/delegate.ts +78 -0
- package/src/doc-extractor.ts +255 -0
- package/src/doc-renderer.ts +131 -0
- package/src/echo-server-demo.fl +17 -0
- package/src/error-formatter.ts +369 -0
- package/src/error-system.ts +88 -0
- package/src/errors.ts +281 -0
- package/src/ethics-check.ts +408 -0
- package/src/eval-ai-blocks.ts +182 -0
- package/src/eval-ai-handlers.ts +132 -0
- package/src/eval-builtins-ai.ts +1659 -0
- package/src/eval-builtins.ts +5956 -0
- package/src/eval-call-function.ts +665 -0
- package/src/eval-infra-blocks.ts +450 -0
- package/src/eval-module-system.ts +286 -0
- package/src/eval-pattern-match.ts +289 -0
- package/src/eval-phase150.ts +87 -0
- package/src/eval-reasoning-sequence.ts +218 -0
- package/src/eval-special-forms.ts +2154 -0
- package/src/eval-style-blocks.ts +193 -0
- package/src/eval-type-classes.ts +148 -0
- package/src/evolve.ts +276 -0
- package/src/explain.ts +345 -0
- package/src/express-advanced.fl +78 -0
- package/src/express-auth.fl +118 -0
- package/src/express-cache.fl +118 -0
- package/src/express-chat.fl +81 -0
- package/src/express-example.fl +67 -0
- package/src/express-test.fl +331 -0
- package/src/express.fl +92 -0
- package/src/fitness.ts +238 -0
- package/src/fl-app-demo.fl +19 -0
- package/src/fl-files/fl-fmt.fl +29 -0
- package/src/fl-files/fl-lint.fl +61 -0
- package/src/fl-files/fl-test.fl +52 -0
- package/src/fl-http-demo.fl +19 -0
- package/src/fl-list-utils.fl +108 -0
- package/src/fl-math-lib.fl +14 -0
- package/src/fl-sdk.ts +154 -0
- package/src/fl-server-demo.fl +74 -0
- package/src/fl-str-utils.fl +29 -0
- package/src/fl-tutor.ts +126 -0
- package/src/formatter.ts +581 -0
- package/src/freelang-codegen.fl +904 -0
- package/src/freelang-interpreter.fl +483 -0
- package/src/freelang-lexer.fl +337 -0
- package/src/freelang-parser.fl +349 -0
- package/src/freelang-stdlib.fl +246 -0
- package/src/freelang-typechecker.fl +422 -0
- package/src/freelang-v9-complete.ts +474 -0
- package/src/generation.ts +201 -0
- package/src/gpt-mini-p3.fl +316 -0
- package/src/hot-reload.ts +235 -0
- package/src/http-server-runner.ts +89 -0
- package/src/hypothesis.ts +62 -0
- package/src/immutable.ts +140 -0
- package/src/interpreter-context.ts +87 -0
- package/src/interpreter-scope.ts +140 -0
- package/src/interpreter.ts +2351 -0
- package/src/lazy-seq.ts +134 -0
- package/src/learned-facts-store.ts +306 -0
- package/src/lexer.ts +359 -0
- package/src/lint-rules.ts +584 -0
- package/src/linter.ts +237 -0
- package/src/logger.ts +128 -0
- package/src/logging-deterministic.fl +138 -0
- package/src/lsp-server.ts +379 -0
- package/src/macro-expander.ts +195 -0
- package/src/maybe-chain.ts +108 -0
- package/src/maybe-type.ts +163 -0
- package/src/memory-system.ts +142 -0
- package/src/meta-reason.ts +93 -0
- package/src/multi-agent-hub.ts +247 -0
- package/src/multi-agent.ts +101 -0
- package/src/mutate.ts +226 -0
- package/src/negotiate.ts +55 -0
- package/src/optimizer.ts +277 -0
- package/src/orchestrate.ts +154 -0
- package/src/package-manager.ts +375 -0
- package/src/parser.ts +2722 -0
- package/src/peer-review.ts +53 -0
- package/src/predict.ts +365 -0
- package/src/profiler.ts +150 -0
- package/src/prompt-compiler.ts +123 -0
- package/src/protocol.ts +114 -0
- package/src/prune.ts +195 -0
- package/src/quality-loop.ts +105 -0
- package/src/rag.ts +81 -0
- package/src/reasoning-debugger.ts +122 -0
- package/src/refactor-self.ts +362 -0
- package/src/reflect.ts +186 -0
- package/src/repl.ts +323 -0
- package/src/result-type.ts +126 -0
- package/src/return-signal.ts +10 -0
- package/src/runtime-entry.ts +8 -0
- package/src/runtime-helpers.ts +234 -0
- package/src/self-evolution-hub.ts +431 -0
- package/src/self-improve.ts +107 -0
- package/src/source-map.ts +114 -0
- package/src/stdlib-agent.js +164 -0
- package/src/stdlib-agent.ts +225 -0
- package/src/stdlib-ai-native.ts +176 -0
- package/src/stdlib-ai-workflow.ts +308 -0
- package/src/stdlib-ai.ts +180 -0
- package/src/stdlib-async.ts +179 -0
- package/src/stdlib-audit.ts +94 -0
- package/src/stdlib-auth.ts +196 -0
- package/src/stdlib-bits.ts +86 -0
- package/src/stdlib-blog.ts +127 -0
- package/src/stdlib-browser.ts +239 -0
- package/src/stdlib-cache.ts +147 -0
- package/src/stdlib-capture-error.ts +183 -0
- package/src/stdlib-channel.ts +96 -0
- package/src/stdlib-checkpoint.js +109 -0
- package/src/stdlib-checkpoint.ts +97 -0
- package/src/stdlib-cloud.ts +317 -0
- package/src/stdlib-collection.ts +227 -0
- package/src/stdlib-compile.ts +111 -0
- package/src/stdlib-cron.ts +219 -0
- package/src/stdlib-crypto-rsa.ts +82 -0
- package/src/stdlib-crypto.js +203 -0
- package/src/stdlib-crypto.ts +208 -0
- package/src/stdlib-data.ts +614 -0
- package/src/stdlib-db-query.ts +198 -0
- package/src/stdlib-db.ts +185 -0
- package/src/stdlib-distributed.ts +292 -0
- package/src/stdlib-error.ts +90 -0
- package/src/stdlib-fd.ts +130 -0
- package/src/stdlib-feed.ts +171 -0
- package/src/stdlib-file.ts +182 -0
- package/src/stdlib-helpers.ts +273 -0
- package/src/stdlib-http-macro.ts +178 -0
- package/src/stdlib-http-server.ts +1229 -0
- package/src/stdlib-http.ts +405 -0
- package/src/stdlib-image.ts +92 -0
- package/src/stdlib-kebab-aliases.ts +131 -0
- package/src/stdlib-lazy-registry.ts +106 -0
- package/src/stdlib-loader.ts +810 -0
- package/src/stdlib-mail.ts +251 -0
- package/src/stdlib-mariadb.ts +467 -0
- package/src/stdlib-markdown.ts +227 -0
- package/src/stdlib-matrix.ts +170 -0
- package/src/stdlib-middleware.ts +221 -0
- package/src/stdlib-module.ts +178 -0
- package/src/stdlib-mongodb.ts +174 -0
- package/src/stdlib-oci.ts +321 -0
- package/src/stdlib-optional.ts +56 -0
- package/src/stdlib-orm.ts +241 -0
- package/src/stdlib-perf.ts +140 -0
- package/src/stdlib-pg.ts +181 -0
- package/src/stdlib-plot.ts +196 -0
- package/src/stdlib-process.ts +120 -0
- package/src/stdlib-property.ts +157 -0
- package/src/stdlib-pubsub.ts +93 -0
- package/src/stdlib-queue-helpers.ts +92 -0
- package/src/stdlib-registry.ts +78 -0
- package/src/stdlib-resource.ts +553 -0
- package/src/stdlib-rest-crud.ts +146 -0
- package/src/stdlib-service.ts +206 -0
- package/src/stdlib-shell.ts +76 -0
- package/src/stdlib-stats.ts +172 -0
- package/src/stdlib-table.ts +200 -0
- package/src/stdlib-test-enhanced.ts +76 -0
- package/src/stdlib-test.ts +153 -0
- package/src/stdlib-time.js +217 -0
- package/src/stdlib-time.ts +282 -0
- package/src/stdlib-timer.ts +134 -0
- package/src/stdlib-totp.ts +110 -0
- package/src/stdlib-type-predicates.ts +136 -0
- package/src/stdlib-types.ts +107 -0
- package/src/stdlib-validation.ts +248 -0
- package/src/stdlib-verify.ts +181 -0
- package/src/stdlib-webauthn.ts +192 -0
- package/src/stdlib-workflow.js +715 -0
- package/src/stdlib-workflow.ts +950 -0
- package/src/stdlib-ws.ts +333 -0
- package/src/stdlib-wsc.test.ts +122 -0
- package/src/stdlib-wsc.ts +243 -0
- package/src/storage-unified.fl +279 -0
- package/src/streaming.ts +101 -0
- package/src/struct-system.ts +104 -0
- package/src/style-registry.ts +54 -0
- package/src/swarm.ts +89 -0
- package/src/tco.ts +31 -0
- package/src/test-advanced-patterns.ts +211 -0
- package/src/test-ast-debug.ts +20 -0
- package/src/test-ast-helpers.ts +208 -0
- package/src/test-async.ts +406 -0
- package/src/test-bootstrap-self-compile.ts +449 -0
- package/src/test-bootstrap-verification.ts +336 -0
- package/src/test-composition.ts +206 -0
- package/src/test-errors-phase6.ts +166 -0
- package/src/test-extended-monads.ts +313 -0
- package/src/test-field-parsing.ts +135 -0
- package/src/test-first-class-functions.ts +257 -0
- package/src/test-freelang-interpreter.ts +320 -0
- package/src/test-freelang-lexer.ts +306 -0
- package/src/test-freelang-parser.ts +268 -0
- package/src/test-fullstack-core.ts +258 -0
- package/src/test-fullstack-phase7-12.ts +305 -0
- package/src/test-fullstack-practical.ts +338 -0
- package/src/test-integration-stdlib.ts +195 -0
- package/src/test-interpreter-phase6.ts +305 -0
- package/src/test-lexer-comparison.ts +108 -0
- package/src/test-lexer-phase6.ts +271 -0
- package/src/test-modules.ts +325 -0
- package/src/test-monad-laws.ts +383 -0
- package/src/test-monads.ts +197 -0
- package/src/test-p0-checkpoint.ts +304 -0
- package/src/test-p0-conditional.ts +284 -0
- package/src/test-p0-error-handling.ts +231 -0
- package/src/test-p0-error-messages.ts +220 -0
- package/src/test-p1-1-parallel-tasks.js +214 -0
- package/src/test-parser-phase6.ts +222 -0
- package/src/test-performance.ts +206 -0
- package/src/test-phase10-data.ts +259 -0
- package/src/test-phase10-file.ts +216 -0
- package/src/test-phase100-stdlib-ai.ts +343 -0
- package/src/test-phase101-memory.ts +309 -0
- package/src/test-phase102-rag.ts +296 -0
- package/src/test-phase103-multi-agent.ts +418 -0
- package/src/test-phase104-try-reason.ts +459 -0
- package/src/test-phase105-streaming.ts +287 -0
- package/src/test-phase106-quality.ts +397 -0
- package/src/test-phase107-tutor.ts +305 -0
- package/src/test-phase108-debugger.ts +316 -0
- package/src/test-phase109-prompt-compiler.ts +333 -0
- package/src/test-phase11-12-complete.ts +275 -0
- package/src/test-phase11-error.ts +192 -0
- package/src/test-phase110-sdk.ts +320 -0
- package/src/test-phase111-hypothesis.ts +380 -0
- package/src/test-phase112-maybe-chain.ts +313 -0
- package/src/test-phase113-debate.ts +364 -0
- package/src/test-phase114-checkpoint.ts +348 -0
- package/src/test-phase115-meta-reason.ts +277 -0
- package/src/test-phase116-belief.ts +275 -0
- package/src/test-phase117-analogy.ts +325 -0
- package/src/test-phase118-critique.ts +308 -0
- package/src/test-phase119-compose.ts +434 -0
- package/src/test-phase12-http-shell.ts +120 -0
- package/src/test-phase120-cognitive.ts +297 -0
- package/src/test-phase121-consensus.ts +404 -0
- package/src/test-phase122-delegate.ts +411 -0
- package/src/test-phase123-vote.ts +339 -0
- package/src/test-phase124-negotiate.ts +403 -0
- package/src/test-phase125-swarm.ts +321 -0
- package/src/test-phase126-orchestrate.ts +343 -0
- package/src/test-phase127-peer-review.ts +279 -0
- package/src/test-phase128-chain-agents.ts +456 -0
- package/src/test-phase129-compete.ts +256 -0
- package/src/test-phase13-data.ts +223 -0
- package/src/test-phase130-hub.ts +390 -0
- package/src/test-phase131-evolve.ts +536 -0
- package/src/test-phase132-mutate.ts +268 -0
- package/src/test-phase133-crossover.ts +289 -0
- package/src/test-phase134-fitness.ts +306 -0
- package/src/test-phase135-generation.ts +328 -0
- package/src/test-phase136-prune.ts +228 -0
- package/src/test-phase137-refactor-self.ts +354 -0
- package/src/test-phase138-benchmark-self.ts +325 -0
- package/src/test-phase139-version-self.ts +278 -0
- package/src/test-phase14-collection.ts +254 -0
- package/src/test-phase140-self-evolution.ts +410 -0
- package/src/test-phase141-world-model.ts +387 -0
- package/src/test-phase142-causal.ts +384 -0
- package/src/test-phase143-counterfactual.ts +280 -0
- package/src/test-phase144-predict.ts +312 -0
- package/src/test-phase145-explain.ts +287 -0
- package/src/test-phase146-align.ts +439 -0
- package/src/test-phase147-ethics-check.ts +399 -0
- package/src/test-phase148-curiosity.ts +247 -0
- package/src/test-phase149-wisdom.ts +758 -0
- package/src/test-phase15-agent.ts +320 -0
- package/src/test-phase150-complete.ts +481 -0
- package/src/test-phase16-time.ts +292 -0
- package/src/test-phase17-crypto.ts +312 -0
- package/src/test-phase18-integration.ts +429 -0
- package/src/test-phase19-resource.ts +214 -0
- package/src/test-phase20-server-db.ts +160 -0
- package/src/test-phase21-ws-auth-cache-pubsub.ts +212 -0
- package/src/test-phase22-process.ts +166 -0
- package/src/test-phase23-selfhosting.ts +383 -0
- package/src/test-phase24-codegen.ts +236 -0
- package/src/test-phase25-bootstrap.ts +227 -0
- package/src/test-phase26-map-filter-reduce.ts +282 -0
- package/src/test-phase27-stdlib-codegen.ts +325 -0
- package/src/test-phase28-loop-recur.ts +182 -0
- package/src/test-phase29-import.ts +165 -0
- package/src/test-phase3-web-server.ts +234 -0
- package/src/test-phase30-selfcompile.ts +254 -0
- package/src/test-phase31-gen2-lexer.ts +251 -0
- package/src/test-phase33-gen3-bootstrap.ts +323 -0
- package/src/test-phase34-parser-tco.ts +268 -0
- package/src/test-phase35-error-messages.ts +280 -0
- package/src/test-phase36-sourcemap.ts +240 -0
- package/src/test-phase37-variadic.ts +228 -0
- package/src/test-phase38-destructuring.ts +261 -0
- package/src/test-phase39-do-block.ts +288 -0
- package/src/test-phase40-const-fold.ts +249 -0
- package/src/test-phase41-repl.ts +312 -0
- package/src/test-phase42-watch.ts +314 -0
- package/src/test-phase43-format.ts +377 -0
- package/src/test-phase44-check.ts +505 -0
- package/src/test-phase45-interpreter.ts +367 -0
- package/src/test-phase46-match.ts +390 -0
- package/src/test-phase47-file-io.ts +338 -0
- package/src/test-phase48-types.ts +308 -0
- package/src/test-phase49-stdlib.ts +365 -0
- package/src/test-phase5-week1.ts +160 -0
- package/src/test-phase50-fl-server.ts +159 -0
- package/src/test-phase51-transformer.ts +152 -0
- package/src/test-phase52-import.ts +161 -0
- package/src/test-phase53-training.ts +122 -0
- package/src/test-phase54-fl-utils.ts +122 -0
- package/src/test-phase55-http-client.ts +182 -0
- package/src/test-phase56-lexical-scope.ts +230 -0
- package/src/test-phase58-module-refactor.ts +298 -0
- package/src/test-phase59-errors.ts +226 -0
- package/src/test-phase6-compile.ts +227 -0
- package/src/test-phase60-types.ts +457 -0
- package/src/test-phase61-tco.ts +209 -0
- package/src/test-phase63-macros.ts +191 -0
- package/src/test-phase64-protocols.ts +451 -0
- package/src/test-phase65-patterns.ts +301 -0
- package/src/test-phase66-structs.ts +215 -0
- package/src/test-phase67-concurrency.ts +348 -0
- package/src/test-phase68-pipeline.ts +209 -0
- package/src/test-phase69-lazy.ts +237 -0
- package/src/test-phase7-registry.ts +236 -0
- package/src/test-phase70-immutable.ts +488 -0
- package/src/test-phase71-ai-native.ts +252 -0
- package/src/test-phase72-integration.ts +533 -0
- package/src/test-phase73-formatter.ts +361 -0
- package/src/test-phase74-linter.ts +565 -0
- package/src/test-phase75-repl.ts +227 -0
- package/src/test-phase76-testrunner.ts +439 -0
- package/src/test-phase77-doc.ts +416 -0
- package/src/test-phase78-debugger.ts +370 -0
- package/src/test-phase79-watch.ts +224 -0
- package/src/test-phase8-oci.ts +264 -0
- package/src/test-phase80-ci.ts +282 -0
- package/src/test-phase81-pkg.ts +383 -0
- package/src/test-phase82-profiler.ts +336 -0
- package/src/test-phase83-vm.ts +318 -0
- package/src/test-phase84-optimizer.ts +424 -0
- package/src/test-phase85-codegen.ts +380 -0
- package/src/test-phase86-lsp.ts +533 -0
- package/src/test-phase87-packages.ts +277 -0
- package/src/test-phase88-selfhost.ts +412 -0
- package/src/test-phase89-bench.ts +361 -0
- package/src/test-phase9-flnext-v2.ts +372 -0
- package/src/test-phase9-learn.ts +241 -0
- package/src/test-phase9-reasoning.ts +312 -0
- package/src/test-phase9-search.ts +212 -0
- package/src/test-phase90-release.ts +365 -0
- package/src/test-phase91-maybe.ts +257 -0
- package/src/test-phase92-cot.ts +438 -0
- package/src/test-phase93-tot.ts +462 -0
- package/src/test-phase94-reflect.ts +498 -0
- package/src/test-phase95-context.ts +268 -0
- package/src/test-phase96-errors.ts +296 -0
- package/src/test-phase97-tools.ts +344 -0
- package/src/test-phase98-agent.ts +370 -0
- package/src/test-phase99-self-improve.ts +394 -0
- package/src/test-phase9a-websearch.ts +283 -0
- package/src/test-phase9ab-integration.ts +140 -0
- package/src/test-phase9b-persistence.ts +448 -0
- package/src/test-phase9c-conditional.ts +251 -0
- package/src/test-phase9c-extension.ts +270 -0
- package/src/test-phase9c-feedback.ts +263 -0
- package/src/test-phase9c-loop.ts +239 -0
- package/src/test-selfhosting-sh1.ts +41 -0
- package/src/test-selfhosting-sh2.ts +84 -0
- package/src/test-selfhosting-sh3.ts +61 -0
- package/src/test-selfhosting-sh4.ts +65 -0
- package/src/test-type-classes-dispatch.ts +202 -0
- package/src/test-type-classes.ts +320 -0
- package/src/test-type-inference.ts +464 -0
- package/src/test-typeclass-parsing-final.ts +218 -0
- package/src/test-typeclass-parsing.ts +264 -0
- package/src/todo-server-30116.ts +53 -0
- package/src/token.ts +130 -0
- package/src/tool-registry.ts +195 -0
- package/src/tot.ts +258 -0
- package/src/try-reason.ts +109 -0
- package/src/type-check-static.ts +417 -0
- package/src/type-checker.ts +245 -0
- package/src/type-inference.ts +271 -0
- package/src/type-system.ts +169 -0
- package/src/version-self.ts +219 -0
- package/src/vm-eligible.ts +106 -0
- package/src/vm.ts +219 -0
- package/src/vote.ts +127 -0
- package/src/vpm/checksum-test.fl +193 -0
- package/src/vpm/checksum.fl +219 -0
- package/src/vpm/error-test.fl +211 -0
- package/src/vpm/error.fl +218 -0
- package/src/vpm/lock-file.fl +380 -0
- package/src/vpm/logging-test.fl +194 -0
- package/src/vpm/logging.fl +294 -0
- package/src/vpm/resolver-test.fl +117 -0
- package/src/vpm/resolver.fl +618 -0
- package/src/vpm/semver-test.fl +180 -0
- package/src/vpm/semver.fl +143 -0
- package/src/vpm-cli.ts +1955 -0
- package/src/web/app-router.ts +286 -0
- package/src/web/fl-executor.ts +655 -0
- package/src/web/image-optimizer.ts +206 -0
- package/src/web/index.ts +14 -0
- package/src/web/page-renderer.ts +287 -0
- package/src/web/server.ts +553 -0
- package/src/web-search-adapter.ts +348 -0
- package/src/wisdom.ts +441 -0
- package/src/world-model.ts +348 -0
- package/stdlib/cache.fl +42 -0
- package/stdlib/csv.fl +38 -0
- package/stdlib/date-ext.fl +68 -0
- package/stdlib/log.fl +44 -0
- package/stdlib/math-ext.fl +57 -0
- package/stdlib/number-ext.fl +47 -0
- package/stdlib/queue.fl +57 -0
- package/stdlib/string-ext.fl +45 -0
- package/stdlib/validate.fl +123 -0
- package/stdlib/web/components.fl +125 -0
- package/stdlib/web/csrf.fl +61 -0
- package/stdlib/web/format.fl +75 -0
- package/stdlib/web/forms.fl +94 -0
- package/stdlib/web/image.fl +77 -0
- package/stdlib/web/metadata.fl +85 -0
- package/stdlib/web/pagination.fl +62 -0
- package/stdlib/web/state.fl +167 -0
- package/stdlib/web/styles.fl +68 -0
- package/stdlib/web/toast.fl +62 -0
- package/stdlib/web/v9-stdlib-dom.fl +92 -0
- package/stdlib/web/v9-stdlib-fetch.fl +90 -0
- package/stdlib/web/v9-stdlib-storage.fl +70 -0
- package/stdlib/web/v9-stdlib-ui.fl +115 -0
- package/stdlib/web/ws-client.fl +125 -0
- package/tests/.v11-backup/test-migrate-sample.fl +22 -0
- package/tests/PHASE-C-VERIFICATION-REPORT.md +125 -0
- package/tests/benchmark-v11.1.js +103 -0
- package/tests/evidence/01-about.html +1 -0
- package/tests/evidence/02-post-hello.html +1 -0
- package/tests/evidence/02-post-world.html +1 -0
- package/tests/evidence/03-home.html +1 -0
- package/tests/evidence/04-dist/404.html +1 -0
- package/tests/evidence/04-dist/about/index.html +1 -0
- package/tests/evidence/04-dist/index.html +1 -0
- package/tests/evidence/04-dist/post/hello/index.html +1 -0
- package/tests/evidence/04-dist/post/world/index.html +1 -0
- package/tests/evidence/05-dist/404.html +1 -0
- package/tests/evidence/05-dist/about/index.html +1 -0
- package/tests/evidence/05-dist/index.html +1 -0
- package/tests/evidence/05-dist/post/hello/index.html +1 -0
- package/tests/evidence/05-dist/post/world/index.html +1 -0
- package/tests/evidence/06-dist/404.html +1 -0
- package/tests/evidence/06-dist/about/index.html +1 -0
- package/tests/evidence/06-dist/index.html +1 -0
- package/tests/evidence/06-dist/post/hello/index.html +1 -0
- package/tests/evidence/06-dist/post/world/index.html +1 -0
- package/tests/evidence/07-markdown.html +1 -0
- package/tests/evidence/08-rss.xml +25 -0
- package/tests/evidence/09-robots.txt +4 -0
- package/tests/evidence/09-sitemap.xml +12 -0
- package/tests/evidence/10-jsonld.html +1 -0
- package/tests/evidence/_results.txt +11 -0
- package/tests/evidence/about.html +1 -0
- package/tests/evidence/home.html +1 -0
- package/tests/evidence/missing.html +42 -0
- package/tests/evidence/post-hello.html +1 -0
- package/tests/evidence/post-world.html +1 -0
- package/tests/fixtures/basic-app/about/page.fl +1 -0
- package/tests/fixtures/basic-app/api/echo/route.fl +5 -0
- package/tests/fixtures/basic-app/layout.fl +1 -0
- package/tests/fixtures/basic-app/not-found.fl +1 -0
- package/tests/fixtures/basic-app/page.fl +1 -0
- package/tests/fixtures/basic-app/post/[slug]/generate-static-params.fl +1 -0
- package/tests/fixtures/basic-app/post/[slug]/page.fl +1 -0
- package/tests/fixtures/stdlib-probes/blog.fl +6 -0
- package/tests/fixtures/stdlib-probes/jsonld.fl +1 -0
- package/tests/fixtures/stdlib-probes/map-probe.fl +3 -0
- package/tests/fixtures/stdlib-probes/map-shape.fl +2 -0
- package/tests/fixtures/stdlib-probes/md.fl +1 -0
- package/tests/fixtures/stdlib-probes/robots.fl +1 -0
- package/tests/fixtures/stdlib-probes/rss.fl +4 -0
- package/tests/fixtures/stdlib-probes/sitemap.fl +1 -0
- package/tests/l2/case-01-arithmetic.fl +6 -0
- package/tests/l2/case-02-comparisons.fl +15 -0
- package/tests/l2/case-03-logic.fl +15 -0
- package/tests/l2/case-04-control-flow.fl +15 -0
- package/tests/l2/case-05-functions.fl +13 -0
- package/tests/l2/case-06-collections.fl +14 -0
- package/tests/l2/case-07-pattern-matching.fl +14 -0
- package/tests/l2/case-08-recursion.fl +19 -0
- package/tests/l2/case-09-strings.fl +13 -0
- package/tests/l2/case-10-loops.fl +17 -0
- package/tests/l2/case-11-higher-order.fl +13 -0
- package/tests/l2/case-12-edge-cases.fl +14 -0
- package/tests/l2/case-13-ai-vector.fl +22 -0
- package/tests/l2/case-14-ai-cosine.fl +25 -0
- package/tests/l2/case-15-ai-template.fl +22 -0
- package/tests/l2/case-16-ai-ranking.fl +20 -0
- package/tests/l2/case-17-stdlib-extended.fl +32 -0
- package/tests/l2-proof/01-arithmetic.bootstrap.js +141 -0
- package/tests/l2-proof/01-arithmetic.fl +15 -0
- package/tests/l2-proof/01-arithmetic.stage1.js +141 -0
- package/tests/l2-proof/02-comparisons.bootstrap.js +141 -0
- package/tests/l2-proof/02-comparisons.fl +17 -0
- package/tests/l2-proof/03-logic.bootstrap.js +141 -0
- package/tests/l2-proof/03-logic.fl +15 -0
- package/tests/l2-proof/04-control-flow.bootstrap.js +146 -0
- package/tests/l2-proof/04-control-flow.fl +24 -0
- package/tests/l2-proof/05-functions.bootstrap.js +143 -0
- package/tests/l2-proof/05-functions.fl +16 -0
- package/tests/l2-proof/06-collections.bootstrap.js +146 -0
- package/tests/l2-proof/06-collections.fl +27 -0
- package/tests/l2-proof/07-pattern-matching.bootstrap.js +142 -0
- package/tests/l2-proof/07-pattern-matching.fl +23 -0
- package/tests/l2-proof/08-async-errors.bootstrap.js +142 -0
- package/tests/l2-proof/08-async-errors.fl +17 -0
- package/tests/l2-proof/09-strings.bootstrap.js +141 -0
- package/tests/l2-proof/09-strings.fl +13 -0
- package/tests/l2-proof/10-type-checks.bootstrap.js +141 -0
- package/tests/l2-proof/10-type-checks.fl +17 -0
- package/tests/l2-proof/11-recursion.bootstrap.js +143 -0
- package/tests/l2-proof/11-recursion.fl +21 -0
- package/tests/l2-proof/12-edge-cases.bootstrap.js +141 -0
- package/tests/l2-proof/12-edge-cases.fl +17 -0
- package/tests/parity/01-app-router-static.sh +18 -0
- package/tests/parity/02-app-router-dynamic.sh +18 -0
- package/tests/parity/03-layout-children.sh +17 -0
- package/tests/parity/04-not-found.sh +22 -0
- package/tests/parity/05-ssg.sh +21 -0
- package/tests/parity/06-parallel-render.sh +21 -0
- package/tests/parity/07-markdown.sh +16 -0
- package/tests/parity/08-rss-atom.sh +18 -0
- package/tests/parity/09-sitemap-robots.sh +23 -0
- package/tests/parity/10-jsonld.sh +16 -0
- package/tests/parity/11-blog-helpers.sh +28 -0
- package/tests/parity/12-api-routes.sh +25 -0
- package/tests/parity/_lib.sh +53 -0
- package/tests/parity/run-all.sh +89 -0
- package/tests/parity/score.sh +20 -0
- package/tests/phase-c-fuzzing.fl +89 -0
- package/tests/phase-c-property-testing.fl +137 -0
- package/tests/phase-c-sha-verification.fl +117 -0
- package/tests/phase-c-validation.fl +194 -0
- package/tests/property-testing.sh +47 -0
- package/tests/regen/let-in-expr-01.fl +5 -0
- package/tests/regen/let-in-expr-02.fl +6 -0
- package/tests/regen/let-in-expr-03.fl +4 -0
- package/tests/regen/let-in-expr-04.fl +6 -0
- package/tests/regen/let-in-expr-05.fl +5 -0
- package/tests/test-ai-library.fl +36 -0
- package/tests/test-andor.fl +2 -0
- package/tests/test-bootstrap-match.fl +5 -0
- package/tests/test-cg-final.fl +5 -0
- package/tests/test-cli.fl +19 -0
- package/tests/test-codegen.fl +4 -0
- package/tests/test-cond-flat.fl +1 -0
- package/tests/test-fl-exec-op.fl +1 -0
- package/tests/test-fp.fl +10 -0
- package/tests/test-funcs.fl +11 -0
- package/tests/test-fuzz-crash.fl +3 -0
- package/tests/test-http-get.fl +3 -0
- package/tests/test-json-load.fl +32 -0
- package/tests/test-let-flat.fl +2 -0
- package/tests/test-let-order.fl +3 -0
- package/tests/test-let-simple.fl +3 -0
- package/tests/test-let-syntax.fl +2 -0
- package/tests/test-let.fl +3 -0
- package/tests/test-lex-debug.fl +75 -0
- package/tests/test-loop-bug.fl +5 -0
- package/tests/test-map-entries.fl +1 -0
- package/tests/test-map-pattern.fl +3 -0
- package/tests/test-match-syntax.fl +5 -0
- package/tests/test-migrate-sample.fl +22 -0
- package/tests/test-neg.fl +1 -0
- package/tests/test-nested-let.fl +4 -0
- package/tests/test-node-to-pattern.fl +8 -0
- package/tests/test-quant-lib.fl +114 -0
- package/tests/test-reduce.fl +10 -0
- package/tests/test-server.fl +3 -0
- package/tests/test-simple-map.fl +4 -0
- package/tests/test-simple-match.fl +3 -0
- package/tests/test-simple.fl +1 -0
- package/tests/test-try-bootstrap.fl +7 -0
- package/tests/test-try-catch.fl +3 -0
- package/tests/test-watchdog-lib.fl +69 -0
- package/tests/test-which-lex.fl +4 -0
- package/tsconfig.json +39 -0
package/src/vpm-cli.ts
ADDED
|
@@ -0,0 +1,1955 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FreeLang v9 Package Manager (vpm) CLI
|
|
3
|
+
* Phase 5b: Package Manager Command Line Interface
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* - vpm install [package][@version]
|
|
7
|
+
* - vpm publish
|
|
8
|
+
* - vpm search <query>
|
|
9
|
+
* - vpm list
|
|
10
|
+
* - vpm update [package]
|
|
11
|
+
* - vpm token create
|
|
12
|
+
* - vpm uninstall <package>
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import * as http from 'http';
|
|
18
|
+
import * as https from 'https';
|
|
19
|
+
import * as crypto from 'crypto';
|
|
20
|
+
import * as os from 'os';
|
|
21
|
+
import { execSync } from 'child_process';
|
|
22
|
+
|
|
23
|
+
interface PackageJson {
|
|
24
|
+
name: string;
|
|
25
|
+
version: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
main?: string;
|
|
28
|
+
dependencies?: Record<string, string>;
|
|
29
|
+
devDependencies?: Record<string, string>;
|
|
30
|
+
v9?: {
|
|
31
|
+
stdlib?: string[];
|
|
32
|
+
require?: string[];
|
|
33
|
+
};
|
|
34
|
+
keywords?: string[];
|
|
35
|
+
license?: string;
|
|
36
|
+
author?: string;
|
|
37
|
+
homepage?: string;
|
|
38
|
+
repository?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface PackageLock {
|
|
42
|
+
name: string;
|
|
43
|
+
version: string;
|
|
44
|
+
lockfileVersion: number;
|
|
45
|
+
requires: boolean;
|
|
46
|
+
packages: Record<string, any>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface RegistryPackage {
|
|
50
|
+
id: string;
|
|
51
|
+
name: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
versions: Array<{
|
|
54
|
+
version: string;
|
|
55
|
+
published_at: string;
|
|
56
|
+
checksum?: string;
|
|
57
|
+
dependencies?: Record<string, string>;
|
|
58
|
+
}>;
|
|
59
|
+
downloads: number;
|
|
60
|
+
stars: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Phase 11: Disk Cache
|
|
64
|
+
interface CacheEntry {
|
|
65
|
+
pkgInfo: any; // registry response (RegistryPackage)
|
|
66
|
+
integrity: string; // SHA-256(JSON.stringify(pkgInfo, null, 2))
|
|
67
|
+
cachedAt: string; // ISO 8601
|
|
68
|
+
registry: string; // 원본 레지스트리 URL
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Phase 12: Parallel download
|
|
72
|
+
interface PackageResolution {
|
|
73
|
+
name: string;
|
|
74
|
+
version: string;
|
|
75
|
+
pkgInfo: any; // registry response
|
|
76
|
+
fromCache: boolean; // 캐시에서 온 경우
|
|
77
|
+
integrity?: string; // downloadAndExtract 후 채워짐
|
|
78
|
+
signature?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Phase 13: OAuth2 Registry (프라이빗 패키지 관리)
|
|
82
|
+
interface OAuthToken {
|
|
83
|
+
accessToken: string;
|
|
84
|
+
refreshToken: string;
|
|
85
|
+
expiresAt: number; // unix timestamp (ms)
|
|
86
|
+
scope: string[]; // 'publish', 'install', 'admin'
|
|
87
|
+
username: string;
|
|
88
|
+
registryUrl: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class VpmCli {
|
|
92
|
+
private registryUrl = process.env.VPM_REGISTRY || 'http://registry.v9.dclub.kr';
|
|
93
|
+
private cwd = process.cwd();
|
|
94
|
+
private vpmDir = path.join(this.cwd, 'vpm');
|
|
95
|
+
private packagesDir = path.join(this.vpmDir, 'packages');
|
|
96
|
+
|
|
97
|
+
// Phase 9: Lockfile-based conflict detection
|
|
98
|
+
private lockfileLoaded = false;
|
|
99
|
+
private installedPackages: Map<string, string> = new Map();
|
|
100
|
+
|
|
101
|
+
// Phase 9: Network resilience
|
|
102
|
+
private fallbackRegistryUrl = process.env.VPM_REGISTRY_FALLBACK || '';
|
|
103
|
+
private readonly REQUEST_TIMEOUT_MS = 5000;
|
|
104
|
+
private readonly MAX_RETRIES = 3;
|
|
105
|
+
|
|
106
|
+
// Phase 10: Cross-dependency tracking
|
|
107
|
+
private dependencyGraph: Map<string, Map<string, string>> = new Map();
|
|
108
|
+
// key: packageName → value: Map<requester, versionSpec>
|
|
109
|
+
|
|
110
|
+
// Phase 10: Signature verification
|
|
111
|
+
private signingKey = process.env.VPM_SIGNING_KEY || '';
|
|
112
|
+
|
|
113
|
+
// Phase 11: Disk Cache
|
|
114
|
+
private cacheDir: string = process.env.VPM_CACHE_DIR
|
|
115
|
+
? path.resolve(process.env.VPM_CACHE_DIR)
|
|
116
|
+
: path.join(os.homedir(), '.vpm', 'cache', 'packages');
|
|
117
|
+
|
|
118
|
+
// Phase 12: Parallel download
|
|
119
|
+
private concurrency: number = Math.min(16, Math.max(1,
|
|
120
|
+
parseInt(process.env.VPM_CONCURRENCY || '4', 10)));
|
|
121
|
+
|
|
122
|
+
// Phase 13: OAuth2 Registry (프라이빗 패키지 관리)
|
|
123
|
+
private authTokenPath: string = path.join(os.homedir(), '.vpm', 'auth.json');
|
|
124
|
+
private oauthToken: OAuthToken | null = null;
|
|
125
|
+
|
|
126
|
+
async run(args: string[]): Promise<void> {
|
|
127
|
+
// Phase 13: OAuth 토큰 로드
|
|
128
|
+
await this.loadOAuthToken();
|
|
129
|
+
|
|
130
|
+
if (args.length === 0) {
|
|
131
|
+
this.showHelp();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const command = args[0];
|
|
136
|
+
const params = args.slice(1);
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
switch (command) {
|
|
140
|
+
case 'install':
|
|
141
|
+
case 'i':
|
|
142
|
+
await this.install(params);
|
|
143
|
+
break;
|
|
144
|
+
case 'publish':
|
|
145
|
+
await this.publish(params);
|
|
146
|
+
break;
|
|
147
|
+
case 'search':
|
|
148
|
+
await this.search(params);
|
|
149
|
+
break;
|
|
150
|
+
case 'list':
|
|
151
|
+
case 'ls':
|
|
152
|
+
await this.list();
|
|
153
|
+
break;
|
|
154
|
+
case 'update':
|
|
155
|
+
await this.update(params);
|
|
156
|
+
break;
|
|
157
|
+
case 'uninstall':
|
|
158
|
+
case 'remove':
|
|
159
|
+
case 'rm':
|
|
160
|
+
await this.uninstall(params);
|
|
161
|
+
break;
|
|
162
|
+
case 'login':
|
|
163
|
+
await this.loginCommand(params);
|
|
164
|
+
break;
|
|
165
|
+
case 'logout':
|
|
166
|
+
await this.logoutCommand();
|
|
167
|
+
break;
|
|
168
|
+
case 'whoami':
|
|
169
|
+
await this.whoamiCommand();
|
|
170
|
+
break;
|
|
171
|
+
case 'token':
|
|
172
|
+
await this.token(params);
|
|
173
|
+
break;
|
|
174
|
+
case 'info':
|
|
175
|
+
await this.info(params);
|
|
176
|
+
break;
|
|
177
|
+
case 'verify':
|
|
178
|
+
await this.verify();
|
|
179
|
+
break;
|
|
180
|
+
case 'reinstall':
|
|
181
|
+
await this.reinstall();
|
|
182
|
+
break;
|
|
183
|
+
case 'cache':
|
|
184
|
+
await this.cacheCommand(params);
|
|
185
|
+
break;
|
|
186
|
+
case 'help':
|
|
187
|
+
case '-h':
|
|
188
|
+
case '--help':
|
|
189
|
+
this.showHelp();
|
|
190
|
+
break;
|
|
191
|
+
default:
|
|
192
|
+
throw new Error(`Unknown command: ${command}`);
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error(`❌ Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private async install(params: string[], visitedChain?: Set<string>): Promise<void> {
|
|
201
|
+
// Phase 9: Load installed packages from lockfile (lazy init)
|
|
202
|
+
this.ensureLockfileLoaded();
|
|
203
|
+
|
|
204
|
+
if (params.length === 0) {
|
|
205
|
+
// 의존성 설치
|
|
206
|
+
await this.installFromLockFile();
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Phase 12: Multi-package parallel support
|
|
211
|
+
if (params.length > 1) {
|
|
212
|
+
await this.installParallel(params);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const packageSpec = params[0];
|
|
217
|
+
const [packageName, versionSpec] = packageSpec.includes('@')
|
|
218
|
+
? packageSpec.split('@')
|
|
219
|
+
: [packageSpec, 'latest'];
|
|
220
|
+
|
|
221
|
+
console.log(`📦 Installing ${packageName}@${versionSpec}...`);
|
|
222
|
+
|
|
223
|
+
// Phase 10: Circular dependency detection
|
|
224
|
+
const chain = visitedChain || new Set<string>();
|
|
225
|
+
const pkgKey = `${packageName}@${versionSpec}`;
|
|
226
|
+
if (chain.has(pkgKey)) {
|
|
227
|
+
console.warn(`⚠️ Circular dependency detected: ${pkgKey} (skipping)`);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
chain.add(pkgKey);
|
|
231
|
+
|
|
232
|
+
// Phase 10: Record dependency request for cross-dep tracking
|
|
233
|
+
this.recordDependencyRequest(packageName, versionSpec, 'direct');
|
|
234
|
+
|
|
235
|
+
// Phase 10: Check if existing version satisfies the spec (deduplication priority)
|
|
236
|
+
if (this.installedPackages.has(packageName)) {
|
|
237
|
+
const existingVersion = this.installedPackages.get(packageName)!;
|
|
238
|
+
if (this.versionMatches(existingVersion, versionSpec)) {
|
|
239
|
+
// Existing version satisfies spec → dedupe
|
|
240
|
+
const pkgPath = path.join(this.packagesDir, `${packageName}@${existingVersion}`);
|
|
241
|
+
if (fs.existsSync(pkgPath)) {
|
|
242
|
+
console.log(`✓ ${packageName}@${existingVersion} satisfies ${versionSpec} (deduped), skipping`);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Phase 11: Cache-first strategy (exact spec only)
|
|
249
|
+
if (this.isExactSpec(versionSpec)) {
|
|
250
|
+
const cached = this.getCachedPackage(packageName, versionSpec);
|
|
251
|
+
if (cached) {
|
|
252
|
+
console.log(`📦 ${packageName}@${versionSpec} (from cache)`);
|
|
253
|
+
const installResult = await this.downloadAndExtract(packageName, versionSpec, cached.pkgInfo);
|
|
254
|
+
this.installedPackages.set(packageName, versionSpec);
|
|
255
|
+
await this.updatePackageJson(packageName, versionSpec);
|
|
256
|
+
// Extract dependencies from cached pkgInfo
|
|
257
|
+
const cachedVersion = cached.pkgInfo.versions?.find((v: any) => v.version === versionSpec);
|
|
258
|
+
const cachedDeps = cachedVersion?.dependencies || {};
|
|
259
|
+
if (Object.keys(cachedDeps).length > 0) {
|
|
260
|
+
console.log(`📚 Installing ${Object.keys(cachedDeps).length} dependencies...`);
|
|
261
|
+
for (const [depName, depVersion] of Object.entries(cachedDeps)) {
|
|
262
|
+
this.recordDependencyRequest(depName, depVersion as string, `${packageName}@${versionSpec}`);
|
|
263
|
+
await this.install([`${depName}@${depVersion}`], chain);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
await this.updateLockFile(packageName, versionSpec, installResult.integrity, installResult.signature);
|
|
267
|
+
console.log(`✅ ${packageName}@${versionSpec} installed (cached) with integrity: ${installResult.integrity}`);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// 패키지 정보 조회 (모든 버전)
|
|
273
|
+
const pkgInfo = await this.fetchPackageInfo(packageName);
|
|
274
|
+
if (!pkgInfo) {
|
|
275
|
+
throw new Error(`Package ${packageName} not found`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Stage 5: Semver 해석 - 최적 버전 선택
|
|
279
|
+
const selectedVersion = this.resolveVersion(pkgInfo.versions, versionSpec);
|
|
280
|
+
if (!selectedVersion) {
|
|
281
|
+
throw new Error(`No matching version found for ${packageName}@${versionSpec}`);
|
|
282
|
+
}
|
|
283
|
+
const version = selectedVersion.version;
|
|
284
|
+
|
|
285
|
+
// Phase 9: Conflict recheck after resolver (resolved version)
|
|
286
|
+
if (this.detectVersionConflict(packageName, version)) {
|
|
287
|
+
const existingVersion = this.installedPackages.get(packageName)!;
|
|
288
|
+
try {
|
|
289
|
+
const resolved = this.resolveConflict(packageName, existingVersion, version);
|
|
290
|
+
if (resolved !== version) {
|
|
291
|
+
console.log(`✓ Resolver selected ${packageName}@${version}, but using ${resolved} (conflict resolution)`);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
} catch (err) {
|
|
295
|
+
// Major version conflict - throw error
|
|
296
|
+
throw err;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 패키지 설치 (resolver.fl 호출 + registry checksum 활용)
|
|
301
|
+
// pkgInfo.versions 배열에서 요청한 버전의 checksum 찾기
|
|
302
|
+
let registryChecksum: string | undefined;
|
|
303
|
+
let dependencies: Record<string, string> = {};
|
|
304
|
+
if (pkgInfo.versions && Array.isArray(pkgInfo.versions)) {
|
|
305
|
+
const versionEntry = pkgInfo.versions.find((v: any) => v.version === version);
|
|
306
|
+
registryChecksum = versionEntry && versionEntry.checksum;
|
|
307
|
+
dependencies = (versionEntry && versionEntry.dependencies) || {};
|
|
308
|
+
}
|
|
309
|
+
// Phase 9: downloadAndExtract는 pkgInfo를 파일로 저장 후 integrity 계산
|
|
310
|
+
const installResult = await this.downloadAndExtract(
|
|
311
|
+
packageName,
|
|
312
|
+
version,
|
|
313
|
+
pkgInfo
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
// Phase 11: Save to cache after successful registry fetch
|
|
317
|
+
this.saveToCachePackage(packageName, version, pkgInfo);
|
|
318
|
+
|
|
319
|
+
// Phase 8: 설치 추적
|
|
320
|
+
this.installedPackages.set(packageName, version);
|
|
321
|
+
|
|
322
|
+
// package.json 업데이트
|
|
323
|
+
await this.updatePackageJson(packageName, version);
|
|
324
|
+
|
|
325
|
+
// Stage 4: 의존성 재귀 설치 (registry에서 가져온 의존성 사용)
|
|
326
|
+
if (Object.keys(dependencies).length > 0) {
|
|
327
|
+
console.log(`📚 Installing ${Object.keys(dependencies).length} dependencies...`);
|
|
328
|
+
for (const [depName, depVersion] of Object.entries(dependencies)) {
|
|
329
|
+
// Phase 10: Record dependency request from this package
|
|
330
|
+
this.recordDependencyRequest(depName, depVersion, `${packageName}@${version}`);
|
|
331
|
+
// Phase 10: Pass visitedChain for circular dependency detection
|
|
332
|
+
await this.install([`${depName}@${depVersion}`], chain);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// 락파일 업데이트 (integrity + Phase 10 signature 포함)
|
|
337
|
+
await this.updateLockFile(packageName, version, installResult.integrity, installResult.signature);
|
|
338
|
+
|
|
339
|
+
console.log(`✅ ${packageName}@${version} installed with integrity: ${installResult.integrity}`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Stage 7: Verify command - check integrity from lockfile
|
|
343
|
+
private async verify(): Promise<void> {
|
|
344
|
+
const lockFilePath = path.join(this.cwd, 'package-lock.json');
|
|
345
|
+
if (!fs.existsSync(lockFilePath)) {
|
|
346
|
+
throw new Error('No package-lock.json found');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const lockFile: PackageLock = JSON.parse(fs.readFileSync(lockFilePath, 'utf-8'));
|
|
350
|
+
const packages = lockFile.packages || {};
|
|
351
|
+
|
|
352
|
+
console.log('🔍 Verifying package integrity...');
|
|
353
|
+
let verified = 0;
|
|
354
|
+
let failed = 0;
|
|
355
|
+
|
|
356
|
+
for (const [pkgName, pkgData] of Object.entries(packages)) {
|
|
357
|
+
if (!pkgName || pkgName === '') continue;
|
|
358
|
+
if (!pkgData.integrity) {
|
|
359
|
+
console.log(`⚠️ ${pkgName}: No integrity recorded`);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const pkgPath = path.join(this.packagesDir, pkgName);
|
|
364
|
+
const pkgJsonPath = path.join(pkgPath, 'package.json');
|
|
365
|
+
|
|
366
|
+
if (!fs.existsSync(pkgJsonPath)) {
|
|
367
|
+
console.log(`❌ ${pkgName}: package.json not found`);
|
|
368
|
+
failed++;
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Phase 9: Verify integrity by recalculating SHA-256 from actual file
|
|
373
|
+
try {
|
|
374
|
+
const actualContent = fs.readFileSync(pkgJsonPath, 'utf-8');
|
|
375
|
+
const actualHash = this.calculateSHA256(actualContent);
|
|
376
|
+
|
|
377
|
+
if (pkgData.integrity && actualHash !== pkgData.integrity) {
|
|
378
|
+
console.log(`❌ ${pkgName}: INTEGRITY MISMATCH (expected: ${pkgData.integrity.substring(0,16)}..., got: ${actualHash.substring(0,16)}...)`);
|
|
379
|
+
failed++;
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Phase 10: Verify signature if VPM_SIGNING_KEY is set
|
|
384
|
+
if (this.signingKey && pkgData.signature) {
|
|
385
|
+
const expectedSig = this.computeSignature(actualContent);
|
|
386
|
+
if (expectedSig !== pkgData.signature) {
|
|
387
|
+
console.log(`❌ ${pkgName}: SIGNATURE MISMATCH`);
|
|
388
|
+
failed++;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
console.log(`✅ ${pkgName}: OK`);
|
|
394
|
+
verified++;
|
|
395
|
+
} catch (err) {
|
|
396
|
+
console.log(`❌ ${pkgName}: Verification failed (${err instanceof Error ? err.message : String(err)})`);
|
|
397
|
+
failed++;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
console.log(`\n📊 Verification: ${verified} OK, ${failed} failed`);
|
|
402
|
+
if (failed > 0) process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Stage 7: Reinstall command - install from lockfile
|
|
406
|
+
private async reinstall(): Promise<void> {
|
|
407
|
+
// Phase 9: Reset lockfile-based conflict detection
|
|
408
|
+
this.lockfileLoaded = false;
|
|
409
|
+
this.installedPackages.clear();
|
|
410
|
+
|
|
411
|
+
// Phase 10: Clear dependency graph
|
|
412
|
+
this.dependencyGraph.clear();
|
|
413
|
+
|
|
414
|
+
const lockFilePath = path.join(this.cwd, 'package-lock.json');
|
|
415
|
+
if (!fs.existsSync(lockFilePath)) {
|
|
416
|
+
throw new Error('No package-lock.json found');
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const lockFile: PackageLock = JSON.parse(fs.readFileSync(lockFilePath, 'utf-8'));
|
|
420
|
+
const packages = lockFile.packages || {};
|
|
421
|
+
|
|
422
|
+
console.log('🔄 Reinstalling from lockfile...');
|
|
423
|
+
|
|
424
|
+
// Clean existing packages
|
|
425
|
+
if (fs.existsSync(this.packagesDir)) {
|
|
426
|
+
fs.rmSync(this.packagesDir, { recursive: true });
|
|
427
|
+
}
|
|
428
|
+
fs.mkdirSync(this.packagesDir, { recursive: true });
|
|
429
|
+
|
|
430
|
+
// Phase 12: Collect all packages to reinstall (parallel)
|
|
431
|
+
const packagesToInstall: string[] = [];
|
|
432
|
+
for (const [pkgName, pkgData] of Object.entries(packages)) {
|
|
433
|
+
if (!pkgName || pkgName === '') continue;
|
|
434
|
+
const [name, version] = pkgName.lastIndexOf('@') > 0
|
|
435
|
+
? pkgName.split('@')
|
|
436
|
+
: [pkgName, ''];
|
|
437
|
+
|
|
438
|
+
if (name && version) {
|
|
439
|
+
packagesToInstall.push(`${name}@${version}`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Phase 12: Parallel reinstall if multiple packages
|
|
444
|
+
if (packagesToInstall.length > 1) {
|
|
445
|
+
await this.installParallel(packagesToInstall);
|
|
446
|
+
} else if (packagesToInstall.length === 1) {
|
|
447
|
+
await this.install([packagesToInstall[0]]);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
console.log('✅ Reinstall complete');
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private async installFromLockFile(): Promise<void> {
|
|
454
|
+
const lockFilePath = path.join(this.cwd, 'package-lock.json');
|
|
455
|
+
if (!fs.existsSync(lockFilePath)) {
|
|
456
|
+
console.log('⚠️ No package-lock.json found. Install dependencies manually.');
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const lockFile: PackageLock = JSON.parse(fs.readFileSync(lockFilePath, 'utf-8'));
|
|
461
|
+
const packages = lockFile.packages || {};
|
|
462
|
+
|
|
463
|
+
console.log('📦 Installing dependencies from lock file...');
|
|
464
|
+
|
|
465
|
+
// Phase 12: Collect all packages to install (parallel)
|
|
466
|
+
const packagesToInstall: string[] = [];
|
|
467
|
+
for (const [pkgName, pkgData] of Object.entries(packages)) {
|
|
468
|
+
if (!pkgName || pkgName === '' || pkgName === '.') continue; // 루트 패키지 제외
|
|
469
|
+
if (pkgData.version) {
|
|
470
|
+
// Extract package name from "name@version" format
|
|
471
|
+
const lastAtIndex = pkgName.lastIndexOf('@');
|
|
472
|
+
const pkgNameOnly = lastAtIndex > 0 ? pkgName.substring(0, lastAtIndex) : pkgName;
|
|
473
|
+
packagesToInstall.push(`${pkgNameOnly}@${pkgData.version}`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Phase 12: Parallel installation if multiple packages
|
|
478
|
+
if (packagesToInstall.length > 1) {
|
|
479
|
+
await this.installParallel(packagesToInstall);
|
|
480
|
+
} else if (packagesToInstall.length === 1) {
|
|
481
|
+
await this.install([packagesToInstall[0]]);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
console.log(`✅ Installed ${packagesToInstall.length} packages`);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
private async publish(params?: string[]): Promise<void> {
|
|
488
|
+
// Phase 13: --private 플래그 감지
|
|
489
|
+
const isPrivate = params?.includes('--private') || false;
|
|
490
|
+
const pkgJsonPath = path.join(this.cwd, 'package.json');
|
|
491
|
+
if (!fs.existsSync(pkgJsonPath)) {
|
|
492
|
+
throw new Error('package.json not found');
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const pkgJson: PackageJson = JSON.parse(
|
|
496
|
+
fs.readFileSync(pkgJsonPath, 'utf-8')
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
// Phase 13: OAuth2 토큰 우선, 없으면 env 폴백
|
|
500
|
+
const token = this.oauthToken?.accessToken || process.env.VPM_AUTH_TOKEN;
|
|
501
|
+
if (!token) {
|
|
502
|
+
throw new Error('Not authenticated. Run: vpm login <username> <password>');
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Phase 13: 토큰 만료 확인 및 갱신
|
|
506
|
+
if (this.oauthToken) {
|
|
507
|
+
await this.validateAndRefreshToken();
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
console.log(
|
|
511
|
+
`📦 Publishing ${pkgJson.name}@${pkgJson.version}...`
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
// 패키지 압축
|
|
515
|
+
const tarballPath = await this.createTarball();
|
|
516
|
+
const fileSize = fs.statSync(tarballPath).size;
|
|
517
|
+
const checksum = this.calculateChecksum(tarballPath);
|
|
518
|
+
|
|
519
|
+
// Phase 13: --private 플래그에 따라 엔드포인트 선택
|
|
520
|
+
const endpoint = isPrivate ? '/private/publish' : '/registry/publish';
|
|
521
|
+
// 레지스트리에 배포
|
|
522
|
+
const response = await this.makeRequest('POST', endpoint, {
|
|
523
|
+
name: pkgJson.name,
|
|
524
|
+
version: pkgJson.version,
|
|
525
|
+
description: pkgJson.description,
|
|
526
|
+
license: pkgJson.license,
|
|
527
|
+
homepage: pkgJson.homepage,
|
|
528
|
+
repository: pkgJson.repository,
|
|
529
|
+
tarball_url: `${this.registryUrl}/download/${pkgJson.name}/${pkgJson.version}`,
|
|
530
|
+
file_size: fileSize,
|
|
531
|
+
checksum,
|
|
532
|
+
keywords: pkgJson.keywords || [],
|
|
533
|
+
dependencies: pkgJson.dependencies || {},
|
|
534
|
+
}, token);
|
|
535
|
+
|
|
536
|
+
if (response.success) {
|
|
537
|
+
console.log(`✅ Published ${pkgJson.name}@${pkgJson.version}`);
|
|
538
|
+
// 압축 파일 정리
|
|
539
|
+
fs.unlinkSync(tarballPath);
|
|
540
|
+
} else {
|
|
541
|
+
throw new Error(response.message || 'Publish failed');
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
private async search(params: string[]): Promise<void> {
|
|
546
|
+
if (params.length === 0) {
|
|
547
|
+
throw new Error('Please provide a search query');
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const query = params.join(' ');
|
|
551
|
+
console.log(`🔍 Searching for "${query}"...`);
|
|
552
|
+
|
|
553
|
+
const response = await this.makeRequest(
|
|
554
|
+
'GET',
|
|
555
|
+
`/registry/search?q=${encodeURIComponent(query)}&limit=20`
|
|
556
|
+
);
|
|
557
|
+
|
|
558
|
+
if (!response.success) {
|
|
559
|
+
throw new Error(response.message || 'Search failed');
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (response.count === 0) {
|
|
563
|
+
console.log('❌ No packages found');
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
console.log(`\n📦 Search results (${response.count} found):\n`);
|
|
568
|
+
response.packages.forEach((pkg: any, idx: number) => {
|
|
569
|
+
const downloads = pkg.downloads || 0;
|
|
570
|
+
const stars = pkg.stars || 0;
|
|
571
|
+
console.log(`${idx + 1}. ${pkg.name}`);
|
|
572
|
+
console.log(` ${pkg.description || 'No description'}`);
|
|
573
|
+
console.log(` ⬇️ ${downloads} | ⭐ ${stars}`);
|
|
574
|
+
console.log();
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
private async list(): Promise<void> {
|
|
579
|
+
if (!fs.existsSync(this.packagesDir)) {
|
|
580
|
+
console.log('📦 No packages installed');
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const packages = fs.readdirSync(this.packagesDir);
|
|
585
|
+
if (packages.length === 0) {
|
|
586
|
+
console.log('📦 No packages installed');
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
console.log('📦 Installed packages:\n');
|
|
591
|
+
packages.forEach((pkg, idx) => {
|
|
592
|
+
const version = pkg.split('@')[1] || 'unknown';
|
|
593
|
+
const name = pkg.split('@')[0];
|
|
594
|
+
console.log(`${idx + 1}. ${name}@${version}`);
|
|
595
|
+
});
|
|
596
|
+
console.log();
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
private async update(params: string[]): Promise<void> {
|
|
600
|
+
if (params.length === 0) {
|
|
601
|
+
// 모든 패키지 업데이트
|
|
602
|
+
console.log('🔄 Updating all packages...');
|
|
603
|
+
const pkgJsonPath = path.join(this.cwd, 'package.json');
|
|
604
|
+
if (!fs.existsSync(pkgJsonPath)) {
|
|
605
|
+
throw new Error('package.json not found');
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const pkgJson: PackageJson = JSON.parse(
|
|
609
|
+
fs.readFileSync(pkgJsonPath, 'utf-8')
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
const deps = pkgJson.dependencies || {};
|
|
613
|
+
let updated = 0;
|
|
614
|
+
|
|
615
|
+
for (const [depName] of Object.entries(deps)) {
|
|
616
|
+
try {
|
|
617
|
+
await this.updateSinglePackage(depName);
|
|
618
|
+
updated++;
|
|
619
|
+
} catch (e) {
|
|
620
|
+
console.warn(`⚠️ Failed to update ${depName}`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
console.log(`✅ Updated ${updated} packages`);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const packageName = params[0];
|
|
629
|
+
await this.updateSinglePackage(packageName);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
private async updateSinglePackage(packageName: string): Promise<void> {
|
|
633
|
+
console.log(`🔄 Updating ${packageName}...`);
|
|
634
|
+
|
|
635
|
+
const pkgInfo = await this.fetchPackageInfo(packageName, 'latest');
|
|
636
|
+
if (!pkgInfo) {
|
|
637
|
+
throw new Error(`Package ${packageName} not found`);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const latestVersion = pkgInfo.versions[0].version;
|
|
641
|
+
const currentVersion = await this.getInstalledVersion(packageName);
|
|
642
|
+
|
|
643
|
+
if (currentVersion === latestVersion) {
|
|
644
|
+
console.log(`ℹ️ ${packageName} is already at latest version`);
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
await this.uninstallSinglePackage(packageName);
|
|
649
|
+
await this.downloadAndExtract(packageName, latestVersion, pkgInfo);
|
|
650
|
+
await this.updatePackageJson(packageName, latestVersion);
|
|
651
|
+
|
|
652
|
+
console.log(`✅ ${packageName} updated from ${currentVersion} to ${latestVersion}`);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
private async uninstall(params: string[]): Promise<void> {
|
|
656
|
+
if (params.length === 0) {
|
|
657
|
+
throw new Error('Please specify a package to uninstall');
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const packageName = params[0];
|
|
661
|
+
await this.uninstallSinglePackage(packageName);
|
|
662
|
+
await this.removeFromPackageJson(packageName);
|
|
663
|
+
await this.updateLockFile();
|
|
664
|
+
|
|
665
|
+
console.log(`✅ ${packageName} uninstalled`);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
private async uninstallSinglePackage(packageName: string): Promise<void> {
|
|
669
|
+
const pkgPath = path.join(
|
|
670
|
+
this.packagesDir,
|
|
671
|
+
fs.readdirSync(this.packagesDir).find((d) => d.startsWith(packageName))!
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
if (fs.existsSync(pkgPath)) {
|
|
675
|
+
fs.rmSync(pkgPath, { recursive: true });
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
private async token(params: string[]): Promise<void> {
|
|
680
|
+
const action = params[0];
|
|
681
|
+
|
|
682
|
+
switch (action) {
|
|
683
|
+
case 'create':
|
|
684
|
+
console.log('🔑 Creating new token...');
|
|
685
|
+
const token = await this.createAuthToken();
|
|
686
|
+
console.log(`✅ Token created: ${token}`);
|
|
687
|
+
console.log(' Set VPM_AUTH_TOKEN environment variable to use it');
|
|
688
|
+
break;
|
|
689
|
+
case 'list':
|
|
690
|
+
console.log('🔑 Auth tokens:');
|
|
691
|
+
// 토큰 목록 조회 (실제 구현은 API 필요)
|
|
692
|
+
break;
|
|
693
|
+
default:
|
|
694
|
+
throw new Error('Unknown token action');
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
private async info(params: string[]): Promise<void> {
|
|
699
|
+
if (params.length === 0) {
|
|
700
|
+
throw new Error('Please specify a package name');
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
const packageName = params[0];
|
|
704
|
+
console.log(`📦 Fetching info for ${packageName}...`);
|
|
705
|
+
|
|
706
|
+
const pkgInfo = await this.fetchPackageInfo(packageName);
|
|
707
|
+
if (!pkgInfo) {
|
|
708
|
+
throw new Error(`Package ${packageName} not found`);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
console.log(`\n${pkgInfo.name}`);
|
|
712
|
+
console.log(`${pkgInfo.description || 'No description'}`);
|
|
713
|
+
console.log(`⬇️ ${pkgInfo.downloads} downloads | ⭐ ${pkgInfo.stars} stars`);
|
|
714
|
+
console.log(`\nVersions:`);
|
|
715
|
+
pkgInfo.versions.forEach((v) => {
|
|
716
|
+
console.log(` - ${v.version} (${v.published_at})`);
|
|
717
|
+
});
|
|
718
|
+
console.log();
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Helper methods
|
|
722
|
+
|
|
723
|
+
private async fetchPackageInfo(
|
|
724
|
+
packageName: string,
|
|
725
|
+
version?: string
|
|
726
|
+
): Promise<RegistryPackage | null> {
|
|
727
|
+
try {
|
|
728
|
+
// Phase 13: Private 패키지 감지 (@org/ 접두사)
|
|
729
|
+
const isPrivate = packageName.startsWith('@');
|
|
730
|
+
const endpoint = isPrivate ? '/private/packages' : '/registry/packages';
|
|
731
|
+
const path = version
|
|
732
|
+
? `${endpoint}?name=${encodeURIComponent(packageName)}&version=${encodeURIComponent(version)}`
|
|
733
|
+
: `${endpoint}?name=${encodeURIComponent(packageName)}`;
|
|
734
|
+
|
|
735
|
+
// Phase 13: OAuth 토큰 전달 (private 패키지인 경우)
|
|
736
|
+
const token = isPrivate ? this.oauthToken?.accessToken : undefined;
|
|
737
|
+
// Phase 9: Use retry wrapper for network resilience
|
|
738
|
+
const response = await this.makeRequestWithRetry('GET', path, undefined, token);
|
|
739
|
+
if (!response.success || !response.package) return null;
|
|
740
|
+
|
|
741
|
+
// Phase 9: Validate registry response structure
|
|
742
|
+
this.validateRegistryResponse(response.package);
|
|
743
|
+
return response.package;
|
|
744
|
+
} catch (err) {
|
|
745
|
+
// Network errors are already logged by makeRequestWithRetry
|
|
746
|
+
if (!(err instanceof Error && err.message.includes('Registry unreachable'))) {
|
|
747
|
+
console.warn(`⚠️ Failed to fetch package info: ${err instanceof Error ? err.message : String(err)}`);
|
|
748
|
+
}
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Phase 9: Validate registry response structure
|
|
754
|
+
private validateRegistryResponse(pkg: any): void {
|
|
755
|
+
if (!pkg.id || !pkg.name || !Array.isArray(pkg.versions)) {
|
|
756
|
+
throw new Error(
|
|
757
|
+
`Invalid registry response for package: missing required fields (id, name, versions)`
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
if (pkg.versions.length === 0) {
|
|
761
|
+
throw new Error(`Package ${pkg.name} has no published versions in registry`);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
private async downloadAndExtract(
|
|
766
|
+
packageName: string,
|
|
767
|
+
version: string,
|
|
768
|
+
pkgInfo: any
|
|
769
|
+
): Promise<{ integrity: string; signature?: string; success: boolean }> {
|
|
770
|
+
// resolver.fl의 install_package 호출 (Phase 7: 실제 경로)
|
|
771
|
+
const targetPath = path.join(this.packagesDir, `${packageName}@${version}`);
|
|
772
|
+
|
|
773
|
+
// resolver.fl 실행
|
|
774
|
+
const result = await this.callResolverInstall(packageName, version, targetPath);
|
|
775
|
+
|
|
776
|
+
if (!result.success) {
|
|
777
|
+
throw new Error(`Failed to install ${packageName}@${version}: ${result.reason}`);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// 패키지 디렉토리 생성 (lockfile 저장용)
|
|
781
|
+
if (!fs.existsSync(targetPath)) {
|
|
782
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// Phase 9: package.json 저장 후 실제 파일 내용으로 integrity 계산
|
|
786
|
+
const pkgJsonContent = JSON.stringify(pkgInfo, null, 2);
|
|
787
|
+
fs.writeFileSync(
|
|
788
|
+
path.join(targetPath, 'package.json'),
|
|
789
|
+
pkgJsonContent
|
|
790
|
+
);
|
|
791
|
+
|
|
792
|
+
// Phase 9: 실제 저장된 파일 내용으로 SHA-256 계산
|
|
793
|
+
const integrity = this.calculateSHA256(pkgJsonContent);
|
|
794
|
+
|
|
795
|
+
// Phase 10: 서명 계산 (VPM_SIGNING_KEY가 설정된 경우)
|
|
796
|
+
const signature = this.signingKey ? this.computeSignature(pkgJsonContent) : undefined;
|
|
797
|
+
|
|
798
|
+
return {
|
|
799
|
+
integrity,
|
|
800
|
+
signature,
|
|
801
|
+
success: true
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
private async installDependencies(
|
|
806
|
+
packageName: string,
|
|
807
|
+
version: string
|
|
808
|
+
): Promise<void> {
|
|
809
|
+
const pkgPath = path.join(this.packagesDir, `${packageName}@${version}`);
|
|
810
|
+
const pkgJsonPath = path.join(pkgPath, 'package.json');
|
|
811
|
+
|
|
812
|
+
if (!fs.existsSync(pkgJsonPath)) return;
|
|
813
|
+
|
|
814
|
+
const pkgJson: PackageJson = JSON.parse(
|
|
815
|
+
fs.readFileSync(pkgJsonPath, 'utf-8')
|
|
816
|
+
);
|
|
817
|
+
|
|
818
|
+
const deps = pkgJson.dependencies || {};
|
|
819
|
+
for (const [depName, depVersion] of Object.entries(deps)) {
|
|
820
|
+
await this.install([`${depName}@${depVersion}`]);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
private async updatePackageJson(
|
|
825
|
+
packageName: string,
|
|
826
|
+
version: string
|
|
827
|
+
): Promise<void> {
|
|
828
|
+
const pkgJsonPath = path.join(this.cwd, 'package.json');
|
|
829
|
+
if (!fs.existsSync(pkgJsonPath)) return;
|
|
830
|
+
|
|
831
|
+
const pkgJson: PackageJson = JSON.parse(
|
|
832
|
+
fs.readFileSync(pkgJsonPath, 'utf-8')
|
|
833
|
+
);
|
|
834
|
+
|
|
835
|
+
if (!pkgJson.dependencies) pkgJson.dependencies = {};
|
|
836
|
+
pkgJson.dependencies[packageName] = version;
|
|
837
|
+
|
|
838
|
+
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
private async removeFromPackageJson(packageName: string): Promise<void> {
|
|
842
|
+
const pkgJsonPath = path.join(this.cwd, 'package.json');
|
|
843
|
+
if (!fs.existsSync(pkgJsonPath)) return;
|
|
844
|
+
|
|
845
|
+
const pkgJson: PackageJson = JSON.parse(
|
|
846
|
+
fs.readFileSync(pkgJsonPath, 'utf-8')
|
|
847
|
+
);
|
|
848
|
+
|
|
849
|
+
if (pkgJson.dependencies && packageName in pkgJson.dependencies) {
|
|
850
|
+
delete pkgJson.dependencies[packageName];
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
private async updateLockFile(
|
|
857
|
+
packageName?: string,
|
|
858
|
+
version?: string,
|
|
859
|
+
integrity?: string,
|
|
860
|
+
signature?: string // Phase 10: HMAC-SHA256 signature
|
|
861
|
+
): Promise<void> {
|
|
862
|
+
const lockFilePath = path.join(this.cwd, 'package-lock.json');
|
|
863
|
+
|
|
864
|
+
// Stage 6: 기존 lockfile 읽기 또는 새로 생성
|
|
865
|
+
let lockFile: PackageLock = {
|
|
866
|
+
name: 'package',
|
|
867
|
+
version: '1.0.0',
|
|
868
|
+
lockfileVersion: 2,
|
|
869
|
+
requires: true,
|
|
870
|
+
packages: {},
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
if (fs.existsSync(lockFilePath)) {
|
|
874
|
+
try {
|
|
875
|
+
lockFile = JSON.parse(fs.readFileSync(lockFilePath, 'utf-8'));
|
|
876
|
+
// 버전 호환성 확인
|
|
877
|
+
if (!lockFile.packages) lockFile.packages = {};
|
|
878
|
+
} catch (err) {
|
|
879
|
+
console.warn('⚠️ Failed to parse existing lockfile, creating new one');
|
|
880
|
+
lockFile.packages = {};
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// Stage 6: packages 디렉토리 스캔 + complete entry 생성
|
|
885
|
+
const installLog: Record<string, { installed: boolean; integrity?: string }> = {};
|
|
886
|
+
|
|
887
|
+
if (fs.existsSync(this.packagesDir)) {
|
|
888
|
+
fs.readdirSync(this.packagesDir).forEach((pkg) => {
|
|
889
|
+
const parts = pkg.lastIndexOf('@') > 0 ? pkg.split('@') : [pkg, ''];
|
|
890
|
+
const pkgName = parts[0] || pkg;
|
|
891
|
+
const pkgVersion = parts[1] || parts[0];
|
|
892
|
+
|
|
893
|
+
// 기존 entry 유지하고 integrity/signature 업데이트
|
|
894
|
+
const existingEntry = lockFile.packages[pkg] || {};
|
|
895
|
+
const isTargetPackage = pkgName === packageName && pkgVersion === version;
|
|
896
|
+
const entry: any = {
|
|
897
|
+
version: pkgVersion,
|
|
898
|
+
resolved: `${this.registryUrl}/${pkgName}@${pkgVersion}`,
|
|
899
|
+
integrity: isTargetPackage && integrity ? integrity : existingEntry.integrity,
|
|
900
|
+
// Phase 10: signature 저장 (선택적)
|
|
901
|
+
...(isTargetPackage && signature && { signature }),
|
|
902
|
+
...(!(isTargetPackage && signature) && existingEntry.signature && { signature: existingEntry.signature }),
|
|
903
|
+
// Stage 6: dependencies 저장 (재설치 시 재참조용)
|
|
904
|
+
dependencies: existingEntry.dependencies || {},
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
lockFile.packages[pkg] = entry;
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// 결정론적 serialization (key sorting)
|
|
912
|
+
const sortedPackages: Record<string, any> = {};
|
|
913
|
+
Object.keys(lockFile.packages)
|
|
914
|
+
.sort()
|
|
915
|
+
.forEach((key) => {
|
|
916
|
+
sortedPackages[key] = lockFile.packages[key];
|
|
917
|
+
});
|
|
918
|
+
lockFile.packages = sortedPackages;
|
|
919
|
+
|
|
920
|
+
fs.writeFileSync(
|
|
921
|
+
lockFilePath,
|
|
922
|
+
JSON.stringify(lockFile, null, 2)
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
private async createTarball(): Promise<string> {
|
|
927
|
+
const tarballPath = `/tmp/${Date.now()}-package.tar.gz`;
|
|
928
|
+
try {
|
|
929
|
+
execSync(
|
|
930
|
+
`tar -czf ${tarballPath} --exclude=vpm --exclude=node_modules .`,
|
|
931
|
+
{ cwd: this.cwd }
|
|
932
|
+
);
|
|
933
|
+
} catch (e) {
|
|
934
|
+
throw new Error(`Failed to create tarball: ${e}`);
|
|
935
|
+
}
|
|
936
|
+
return tarballPath;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
private calculateChecksum(filePath: string): string {
|
|
940
|
+
const crypto = require('crypto');
|
|
941
|
+
const content = fs.readFileSync(filePath);
|
|
942
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
private async makeRequest(
|
|
946
|
+
method: string,
|
|
947
|
+
path: string,
|
|
948
|
+
body?: any,
|
|
949
|
+
token?: string
|
|
950
|
+
): Promise<any> {
|
|
951
|
+
return new Promise((resolve, reject) => {
|
|
952
|
+
const url = new URL(this.registryUrl + path);
|
|
953
|
+
const options = {
|
|
954
|
+
hostname: url.hostname,
|
|
955
|
+
port: url.port || 80,
|
|
956
|
+
path: url.pathname + url.search,
|
|
957
|
+
method,
|
|
958
|
+
headers: {
|
|
959
|
+
'Content-Type': 'application/json',
|
|
960
|
+
...(token && { Authorization: `Bearer ${token}` }),
|
|
961
|
+
},
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
const req = http.request(options, (res) => {
|
|
965
|
+
let data = '';
|
|
966
|
+
res.on('data', (chunk) => {
|
|
967
|
+
data += chunk;
|
|
968
|
+
});
|
|
969
|
+
res.on('end', () => {
|
|
970
|
+
try {
|
|
971
|
+
resolve(JSON.parse(data));
|
|
972
|
+
} catch {
|
|
973
|
+
reject(new Error('Invalid JSON response'));
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
// Phase 9: Add timeout for network resilience
|
|
979
|
+
req.setTimeout(this.REQUEST_TIMEOUT_MS, () => {
|
|
980
|
+
req.destroy(new Error(`Registry request timeout after ${this.REQUEST_TIMEOUT_MS}ms`));
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
req.on('error', reject);
|
|
984
|
+
if (body) {
|
|
985
|
+
req.write(JSON.stringify(body));
|
|
986
|
+
}
|
|
987
|
+
req.end();
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// Phase 9: Cryptographically secure token generation
|
|
992
|
+
private async createAuthToken(): Promise<string> {
|
|
993
|
+
return crypto.randomBytes(32).toString('hex'); // 64-char hex token
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
private async getInstalledVersion(packageName: string): Promise<string> {
|
|
997
|
+
if (!fs.existsSync(this.packagesDir)) return 'none';
|
|
998
|
+
const dirs = fs.readdirSync(this.packagesDir);
|
|
999
|
+
const pkg = dirs.find((d) => d.startsWith(packageName + '@'));
|
|
1000
|
+
return pkg ? pkg.split('@')[1] : 'none';
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// Phase 9: Network resilience - retry with exponential backoff + fallback registry
|
|
1004
|
+
private async makeRequestWithRetry(
|
|
1005
|
+
method: string,
|
|
1006
|
+
endpoint: string,
|
|
1007
|
+
body?: any,
|
|
1008
|
+
token?: string
|
|
1009
|
+
): Promise<any> {
|
|
1010
|
+
let lastError: Error | null = null;
|
|
1011
|
+
|
|
1012
|
+
for (let attempt = 1; attempt <= this.MAX_RETRIES; attempt++) {
|
|
1013
|
+
try {
|
|
1014
|
+
return await this.makeRequest(method, endpoint, body, token);
|
|
1015
|
+
} catch (err) {
|
|
1016
|
+
lastError = err as Error;
|
|
1017
|
+
if (attempt < this.MAX_RETRIES) {
|
|
1018
|
+
const delay = Math.pow(2, attempt - 1) * 500; // 500, 1000, 2000ms
|
|
1019
|
+
console.log(
|
|
1020
|
+
`⚠️ Registry request failed (attempt ${attempt}/${this.MAX_RETRIES}): ${lastError.message}. ` +
|
|
1021
|
+
`retrying in ${delay}ms...`
|
|
1022
|
+
);
|
|
1023
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// Try fallback registry if primary failed
|
|
1029
|
+
if (this.fallbackRegistryUrl && this.registryUrl !== this.fallbackRegistryUrl) {
|
|
1030
|
+
console.log(`⚠️ Primary registry unreachable. Trying fallback: ${this.fallbackRegistryUrl}`);
|
|
1031
|
+
const origUrl = this.registryUrl;
|
|
1032
|
+
this.registryUrl = this.fallbackRegistryUrl;
|
|
1033
|
+
try {
|
|
1034
|
+
return await this.makeRequest(method, endpoint, body, token);
|
|
1035
|
+
} finally {
|
|
1036
|
+
this.registryUrl = origUrl;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
throw new Error(`Registry unreachable after ${this.MAX_RETRIES} attempts: ${lastError?.message}`);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
private async callResolverInstall(
|
|
1044
|
+
packageName: string,
|
|
1045
|
+
version: string,
|
|
1046
|
+
targetPath: string
|
|
1047
|
+
): Promise<{ success: boolean; integrity?: string; reason?: string }> {
|
|
1048
|
+
// Phase 9: 실제 파일 내용으로 무결성 계산 (downloadAndExtract에서)
|
|
1049
|
+
const sha256Value = this.calculateSHA256(`${packageName}@${version}-content`);
|
|
1050
|
+
let v9ScriptPath = '';
|
|
1051
|
+
|
|
1052
|
+
try {
|
|
1053
|
+
// Phase 8: 단순화된 v9 script - 실제 SHA-256 검증만 수행
|
|
1054
|
+
// Phase 12: Use package@version for concurrent-safe filename
|
|
1055
|
+
v9ScriptPath = path.join(this.cwd, `.vpm-install-${packageName.replace(/[/@]/g, '-')}-${version}.fl`);
|
|
1056
|
+
const v9Script = `; Phase 8: Real SHA-256 verification
|
|
1057
|
+
[FUNC verify-sha256 :params [$n $v $s] :body (do (println "✅ Verified integrity: " $n "@" $v " (" $s ")") true)]
|
|
1058
|
+
|
|
1059
|
+
(let [[$result (verify-sha256 "${packageName}" "${version}" "${sha256Value}")]]
|
|
1060
|
+
(if $result
|
|
1061
|
+
(println "INSTALL_SUCCESS:${sha256Value}")
|
|
1062
|
+
(println "INSTALL_FAILED:integrity_mismatch")
|
|
1063
|
+
)
|
|
1064
|
+
)`;
|
|
1065
|
+
|
|
1066
|
+
fs.writeFileSync(v9ScriptPath, v9Script);
|
|
1067
|
+
|
|
1068
|
+
// CLI로 실행 (출력 캡처)
|
|
1069
|
+
const cliPath = path.join(__dirname, 'cli.js');
|
|
1070
|
+
let output = '';
|
|
1071
|
+
|
|
1072
|
+
try {
|
|
1073
|
+
output = execSync(`node ${cliPath} run ${v9ScriptPath} 2>&1`, {
|
|
1074
|
+
encoding: 'utf-8',
|
|
1075
|
+
timeout: 10000
|
|
1076
|
+
});
|
|
1077
|
+
} catch (execError: any) {
|
|
1078
|
+
output = execError.stdout || execError.stderr || String(execError);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// 성공/실패 판별
|
|
1082
|
+
const isSuccess = output.includes('✅') && output.includes('INSTALL_SUCCESS');
|
|
1083
|
+
|
|
1084
|
+
return {
|
|
1085
|
+
success: isSuccess,
|
|
1086
|
+
integrity: isSuccess ? sha256Value : undefined,
|
|
1087
|
+
reason: isSuccess ? undefined : 'integrity_mismatch'
|
|
1088
|
+
};
|
|
1089
|
+
} catch (error) {
|
|
1090
|
+
console.error(`⚠️ resolver execution error: ${error instanceof Error ? error.message : error}`);
|
|
1091
|
+
return {
|
|
1092
|
+
success: false,
|
|
1093
|
+
reason: 'resolver_execution_error'
|
|
1094
|
+
};
|
|
1095
|
+
} finally {
|
|
1096
|
+
// 임시 파일 정리
|
|
1097
|
+
try {
|
|
1098
|
+
if (fs.existsSync(v9ScriptPath)) {
|
|
1099
|
+
fs.unlinkSync(v9ScriptPath);
|
|
1100
|
+
}
|
|
1101
|
+
} catch {
|
|
1102
|
+
// ignore cleanup errors
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// Phase 8: Real SHA-256 calculation
|
|
1108
|
+
private calculateSHA256(content: string): string {
|
|
1109
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// Phase 9: Load installed packages from lockfile (lazy init, called once per install)
|
|
1113
|
+
private ensureLockfileLoaded(): void {
|
|
1114
|
+
if (this.lockfileLoaded) return;
|
|
1115
|
+
this.lockfileLoaded = true;
|
|
1116
|
+
|
|
1117
|
+
const lockFilePath = path.join(this.cwd, 'package-lock.json');
|
|
1118
|
+
if (!fs.existsSync(lockFilePath)) return;
|
|
1119
|
+
|
|
1120
|
+
try {
|
|
1121
|
+
const lockFile: PackageLock = JSON.parse(fs.readFileSync(lockFilePath, 'utf-8'));
|
|
1122
|
+
for (const [pkgKey, pkgData] of Object.entries(lockFile.packages || {})) {
|
|
1123
|
+
if (!pkgKey || pkgKey === '' || pkgKey === '.') continue;
|
|
1124
|
+
const lastAt = pkgKey.lastIndexOf('@');
|
|
1125
|
+
const name = lastAt > 0 ? pkgKey.substring(0, lastAt) : pkgKey;
|
|
1126
|
+
const version = (pkgData as any).version;
|
|
1127
|
+
if (name && version) {
|
|
1128
|
+
this.installedPackages.set(name, version);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
} catch (err) {
|
|
1132
|
+
// Ignore lockfile parse errors - treat as empty
|
|
1133
|
+
console.warn(`⚠️ Failed to parse lockfile: ${err instanceof Error ? err.message : String(err)}`);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// Phase 9: Resolve version conflict - highest wins for minor/patch, error for major
|
|
1138
|
+
private resolveConflict(packageName: string, existingVersion: string, requestedVersion: string): string {
|
|
1139
|
+
const [em] = this.parseVersion(existingVersion);
|
|
1140
|
+
const [rm] = this.parseVersion(requestedVersion);
|
|
1141
|
+
|
|
1142
|
+
// Major version mismatch - cannot auto-resolve (breaking change)
|
|
1143
|
+
if (em !== rm) {
|
|
1144
|
+
throw new Error(
|
|
1145
|
+
`Major version conflict for ${packageName}: ${existingVersion} (installed) vs ${requestedVersion} (requested). ` +
|
|
1146
|
+
`Cannot auto-resolve across major versions. Please uninstall or use a compatible version.`
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// Minor/Patch: highest wins
|
|
1151
|
+
const winner =
|
|
1152
|
+
this.compareVersions(requestedVersion, existingVersion) > 0
|
|
1153
|
+
? requestedVersion
|
|
1154
|
+
: existingVersion;
|
|
1155
|
+
|
|
1156
|
+
if (winner !== existingVersion) {
|
|
1157
|
+
console.warn(
|
|
1158
|
+
`⚠️ Version conflict for ${packageName}: ` +
|
|
1159
|
+
`${existingVersion} (installed) vs ${requestedVersion} (requested) → using ${winner} (highest wins)`
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
this.installedPackages.set(packageName, winner);
|
|
1164
|
+
return winner;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// Phase 10: Record dependency request for cross-dependency conflict detection
|
|
1168
|
+
private recordDependencyRequest(pkgName: string, versionSpec: string, requester: string): void {
|
|
1169
|
+
if (!this.dependencyGraph.has(pkgName)) {
|
|
1170
|
+
this.dependencyGraph.set(pkgName, new Map());
|
|
1171
|
+
}
|
|
1172
|
+
this.dependencyGraph.get(pkgName)!.set(requester, versionSpec);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// Phase 10: Compute HMAC-SHA256 signature for package content
|
|
1176
|
+
private computeSignature(content: string): string {
|
|
1177
|
+
return crypto.createHmac('sha256', this.signingKey).update(content).digest('hex');
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
private detectVersionConflict(packageName: string, version: string): boolean {
|
|
1181
|
+
if (this.installedPackages.has(packageName)) {
|
|
1182
|
+
const existingVersion = this.installedPackages.get(packageName)!;
|
|
1183
|
+
return existingVersion !== version;
|
|
1184
|
+
}
|
|
1185
|
+
return false;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// Stage 5: Semver Resolution
|
|
1189
|
+
private resolveVersion(
|
|
1190
|
+
versions: Array<{ version: string; [key: string]: any }>,
|
|
1191
|
+
spec: string
|
|
1192
|
+
): { version: string; [key: string]: any } | null {
|
|
1193
|
+
// Find the best version matching the semver spec
|
|
1194
|
+
// Supported: "latest", "1.2.3" (exact), "^1.2.3" (caret), "~1.2.3" (tilde)
|
|
1195
|
+
|
|
1196
|
+
if (spec === 'latest') {
|
|
1197
|
+
return versions.length > 0 ? versions[versions.length - 1] : null;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
const matching = versions.filter((v) => this.versionMatches(v.version, spec));
|
|
1201
|
+
if (matching.length === 0) return null;
|
|
1202
|
+
|
|
1203
|
+
// Return the highest matching version
|
|
1204
|
+
return matching.sort((a, b) => this.compareVersions(b.version, a.version))[0];
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
private versionMatches(version: string, spec: string): boolean {
|
|
1208
|
+
spec = spec.trim();
|
|
1209
|
+
if (spec === 'latest' || spec === '*' || spec === 'x' || spec === '') return true;
|
|
1210
|
+
|
|
1211
|
+
// Phase 10: OR operator (||)
|
|
1212
|
+
if (spec.includes('||')) {
|
|
1213
|
+
return spec.split('||').map(s => s.trim()).some(s => this.versionMatchesAnd(version, s));
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
return this.versionMatchesAnd(version, spec);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// Phase 10: Handle AND conditions (space-separated)
|
|
1220
|
+
private versionMatchesAnd(version: string, spec: string): boolean {
|
|
1221
|
+
const conditions = spec.match(/(>=|<=|>|<|~|\^)[^\s]+|\*/g);
|
|
1222
|
+
if (conditions && conditions.length > 1) {
|
|
1223
|
+
return conditions.every(cond => this.versionMatchesSingle(version, cond));
|
|
1224
|
+
}
|
|
1225
|
+
return this.versionMatchesSingle(version, spec.trim());
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
// Phase 10: Handle single condition or legacy versionMatches logic
|
|
1229
|
+
private versionMatchesSingle(version: string, spec: string): boolean {
|
|
1230
|
+
if (spec === 'latest') return true;
|
|
1231
|
+
|
|
1232
|
+
// Phase 10: Wildcard support
|
|
1233
|
+
if (spec === '*' || spec === 'x') return true;
|
|
1234
|
+
if (/^\d+\.x$/.test(spec)) {
|
|
1235
|
+
const [vm] = this.parseVersion(version);
|
|
1236
|
+
return vm === parseInt(spec.split('.')[0]);
|
|
1237
|
+
}
|
|
1238
|
+
if (/^\d+\.\d+\.x$/.test(spec)) {
|
|
1239
|
+
const [vmaj, vmin] = this.parseVersion(version);
|
|
1240
|
+
const parts = spec.split('.');
|
|
1241
|
+
return vmaj === parseInt(parts[0]) && vmin === parseInt(parts[1]);
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// Phase 8 & legacy: Single operators
|
|
1245
|
+
if (spec.startsWith('^')) {
|
|
1246
|
+
const specVersion = spec.substring(1);
|
|
1247
|
+
const [specMajor] = this.parseVersion(specVersion);
|
|
1248
|
+
const [versionMajor] = this.parseVersion(version);
|
|
1249
|
+
return versionMajor === specMajor && this.compareVersions(version, specVersion) >= 0;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
if (spec.startsWith('~')) {
|
|
1253
|
+
const specVersion = spec.substring(1);
|
|
1254
|
+
const [specMajor, specMinor] = this.parseVersion(specVersion);
|
|
1255
|
+
const [versionMajor, versionMinor] = this.parseVersion(version);
|
|
1256
|
+
return (
|
|
1257
|
+
versionMajor === specMajor &&
|
|
1258
|
+
versionMinor === specMinor &&
|
|
1259
|
+
this.compareVersions(version, specVersion) >= 0
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
if (spec.startsWith('>=')) {
|
|
1264
|
+
const specVersion = spec.substring(2);
|
|
1265
|
+
return this.compareVersions(version, specVersion) >= 0;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
if (spec.startsWith('<=')) {
|
|
1269
|
+
const specVersion = spec.substring(2);
|
|
1270
|
+
return this.compareVersions(version, specVersion) <= 0;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
if (spec.startsWith('>')) {
|
|
1274
|
+
const specVersion = spec.substring(1);
|
|
1275
|
+
return this.compareVersions(version, specVersion) > 0;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
if (spec.startsWith('<')) {
|
|
1279
|
+
const specVersion = spec.substring(1);
|
|
1280
|
+
return this.compareVersions(version, specVersion) < 0;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
// Phase 8: Range syntax (e.g., "1.0.0-2.5.0")
|
|
1284
|
+
if (spec.includes('-') && spec.match(/^\d+\.\d+\.\d+-\d+\.\d+\.\d+$/)) {
|
|
1285
|
+
const [minStr, maxStr] = spec.split('-');
|
|
1286
|
+
return this.compareVersions(version, minStr) >= 0 && this.compareVersions(version, maxStr) <= 0;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// Exact version
|
|
1290
|
+
return version === spec;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
private parseVersion(version: string): [number, number, number] {
|
|
1294
|
+
const parts = version.split('.');
|
|
1295
|
+
return [parseInt(parts[0] || '0'), parseInt(parts[1] || '0'), parseInt(parts[2] || '0')];
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
private compareVersions(v1: string, v2: string): number {
|
|
1299
|
+
// Returns: >0 if v1 > v2, 0 if equal, <0 if v1 < v2
|
|
1300
|
+
const [m1, n1, p1] = this.parseVersion(v1);
|
|
1301
|
+
const [m2, n2, p2] = this.parseVersion(v2);
|
|
1302
|
+
|
|
1303
|
+
if (m1 !== m2) return m1 - m2;
|
|
1304
|
+
if (n1 !== n2) return n1 - n2;
|
|
1305
|
+
return p1 - p2;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// Phase 11: Cache Manager Methods
|
|
1309
|
+
|
|
1310
|
+
private initCacheDir(): void {
|
|
1311
|
+
try {
|
|
1312
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
1313
|
+
} catch (err) {
|
|
1314
|
+
console.warn(`⚠️ Failed to create cache directory: ${this.cacheDir}`);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
private isExactSpec(spec: string): boolean {
|
|
1319
|
+
// Check if spec is a pure X.Y.Z version (not range/latest)
|
|
1320
|
+
return /^\d+\.\d+\.\d+$/.test(spec);
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
private getCachedPackage(name: string, version: string): CacheEntry | null {
|
|
1324
|
+
this.initCacheDir();
|
|
1325
|
+
const cacheFile = path.join(this.cacheDir, `${name}@${version}.json`);
|
|
1326
|
+
|
|
1327
|
+
if (!fs.existsSync(cacheFile)) {
|
|
1328
|
+
return null; // cache miss
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
try {
|
|
1332
|
+
const content = fs.readFileSync(cacheFile, 'utf-8');
|
|
1333
|
+
const entry: CacheEntry = JSON.parse(content);
|
|
1334
|
+
|
|
1335
|
+
// Verify integrity
|
|
1336
|
+
const pkgInfoStr = JSON.stringify(entry.pkgInfo, null, 2);
|
|
1337
|
+
const computedIntegrity = this.calculateSHA256(pkgInfoStr);
|
|
1338
|
+
|
|
1339
|
+
if (computedIntegrity !== entry.integrity) {
|
|
1340
|
+
// Cache corrupted - remove it
|
|
1341
|
+
try {
|
|
1342
|
+
fs.unlinkSync(cacheFile);
|
|
1343
|
+
} catch {}
|
|
1344
|
+
return null;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
return entry;
|
|
1348
|
+
} catch (err) {
|
|
1349
|
+
// JSON parse error or file read error - remove corrupted cache
|
|
1350
|
+
try {
|
|
1351
|
+
fs.unlinkSync(cacheFile);
|
|
1352
|
+
} catch {}
|
|
1353
|
+
return null;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
private saveToCachePackage(name: string, version: string, pkgInfo: any): void {
|
|
1358
|
+
this.initCacheDir();
|
|
1359
|
+
const cacheFile = path.join(this.cacheDir, `${name}@${version}.json`);
|
|
1360
|
+
|
|
1361
|
+
try {
|
|
1362
|
+
// Compute integrity from pkgInfo
|
|
1363
|
+
const pkgInfoStr = JSON.stringify(pkgInfo, null, 2);
|
|
1364
|
+
const integrity = this.calculateSHA256(pkgInfoStr);
|
|
1365
|
+
|
|
1366
|
+
const entry: CacheEntry = {
|
|
1367
|
+
pkgInfo,
|
|
1368
|
+
integrity,
|
|
1369
|
+
cachedAt: new Date().toISOString(),
|
|
1370
|
+
registry: this.registryUrl
|
|
1371
|
+
};
|
|
1372
|
+
|
|
1373
|
+
// Atomic write: write to tmp file first, then rename
|
|
1374
|
+
const tmpFile = cacheFile + '.tmp';
|
|
1375
|
+
fs.writeFileSync(tmpFile, JSON.stringify(entry, null, 2));
|
|
1376
|
+
fs.renameSync(tmpFile, cacheFile);
|
|
1377
|
+
} catch (err) {
|
|
1378
|
+
// Cache save failure is not fatal - warn but continue
|
|
1379
|
+
console.warn(`⚠️ Failed to save cache for ${name}@${version}: ${(err as Error).message}`);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// Phase 11: Cache CLI Commands
|
|
1384
|
+
|
|
1385
|
+
private async cacheCommand(params: string[]): Promise<void> {
|
|
1386
|
+
const subcommand = params[0];
|
|
1387
|
+
|
|
1388
|
+
switch (subcommand) {
|
|
1389
|
+
case 'dir':
|
|
1390
|
+
this.cacheDir_cmd();
|
|
1391
|
+
break;
|
|
1392
|
+
case 'list':
|
|
1393
|
+
case 'ls':
|
|
1394
|
+
await this.cacheList();
|
|
1395
|
+
break;
|
|
1396
|
+
case 'verify':
|
|
1397
|
+
await this.cacheVerify();
|
|
1398
|
+
break;
|
|
1399
|
+
case 'clean':
|
|
1400
|
+
this.cacheClean();
|
|
1401
|
+
break;
|
|
1402
|
+
case 'prune':
|
|
1403
|
+
await this.cachePrune();
|
|
1404
|
+
break;
|
|
1405
|
+
default:
|
|
1406
|
+
throw new Error(`Unknown cache command: ${subcommand}`);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
private cacheDir_cmd(): void {
|
|
1411
|
+
console.log(this.cacheDir);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
private async cacheList(): Promise<void> {
|
|
1415
|
+
this.initCacheDir();
|
|
1416
|
+
|
|
1417
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
1418
|
+
console.log('No cached packages');
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
const files = fs.readdirSync(this.cacheDir).filter((f) => f.endsWith('.json'));
|
|
1423
|
+
|
|
1424
|
+
if (files.length === 0) {
|
|
1425
|
+
console.log('No cached packages');
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
console.log(`Cached packages (${files.length}):`);
|
|
1430
|
+
for (const file of files) {
|
|
1431
|
+
const pkgSpec = file.replace('.json', '');
|
|
1432
|
+
try {
|
|
1433
|
+
const content = fs.readFileSync(path.join(this.cacheDir, file), 'utf-8');
|
|
1434
|
+
const entry: CacheEntry = JSON.parse(content);
|
|
1435
|
+
console.log(` ${pkgSpec} (cached at ${entry.cachedAt})`);
|
|
1436
|
+
} catch (err) {
|
|
1437
|
+
console.log(` ${pkgSpec} (corrupted)`);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
private async cacheVerify(): Promise<void> {
|
|
1443
|
+
this.initCacheDir();
|
|
1444
|
+
|
|
1445
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
1446
|
+
console.log('No cached packages');
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
const files = fs.readdirSync(this.cacheDir).filter((f) => f.endsWith('.json'));
|
|
1451
|
+
|
|
1452
|
+
if (files.length === 0) {
|
|
1453
|
+
console.log('No cached packages');
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
let passed = 0;
|
|
1458
|
+
let failed = 0;
|
|
1459
|
+
|
|
1460
|
+
for (const file of files) {
|
|
1461
|
+
const pkgSpec = file.replace('.json', '');
|
|
1462
|
+
try {
|
|
1463
|
+
const content = fs.readFileSync(path.join(this.cacheDir, file), 'utf-8');
|
|
1464
|
+
const entry: CacheEntry = JSON.parse(content);
|
|
1465
|
+
|
|
1466
|
+
// Recompute integrity
|
|
1467
|
+
const pkgInfoStr = JSON.stringify(entry.pkgInfo, null, 2);
|
|
1468
|
+
const computedIntegrity = this.calculateSHA256(pkgInfoStr);
|
|
1469
|
+
|
|
1470
|
+
if (computedIntegrity === entry.integrity) {
|
|
1471
|
+
console.log(`✓ ${pkgSpec}`);
|
|
1472
|
+
passed++;
|
|
1473
|
+
} else {
|
|
1474
|
+
console.log(`❌ CACHE INTEGRITY MISMATCH: ${pkgSpec}`);
|
|
1475
|
+
failed++;
|
|
1476
|
+
}
|
|
1477
|
+
} catch (err) {
|
|
1478
|
+
console.log(`❌ CACHE INTEGRITY MISMATCH: ${pkgSpec}`);
|
|
1479
|
+
failed++;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
console.log(`\n${passed} OK, ${failed} failed`);
|
|
1484
|
+
if (failed > 0) {
|
|
1485
|
+
process.exit(1);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
private cacheClean(): void {
|
|
1490
|
+
this.initCacheDir();
|
|
1491
|
+
|
|
1492
|
+
if (fs.existsSync(this.cacheDir)) {
|
|
1493
|
+
try {
|
|
1494
|
+
fs.rmSync(this.cacheDir, { recursive: true });
|
|
1495
|
+
} catch (err) {
|
|
1496
|
+
console.warn(`⚠️ Failed to remove cache directory: ${(err as Error).message}`);
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
try {
|
|
1502
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
1503
|
+
} catch (err) {
|
|
1504
|
+
console.warn(`⚠️ Failed to create cache directory: ${(err as Error).message}`);
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
console.log('Cache cleared');
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
private async cachePrune(): Promise<void> {
|
|
1512
|
+
this.initCacheDir();
|
|
1513
|
+
|
|
1514
|
+
const lockfilePath = path.join(this.cwd, 'package-lock.json');
|
|
1515
|
+
|
|
1516
|
+
if (!fs.existsSync(lockfilePath)) {
|
|
1517
|
+
console.log('⚠️ Lockfile not found - cannot prune cache');
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
try {
|
|
1522
|
+
const lockContent = fs.readFileSync(lockfilePath, 'utf-8');
|
|
1523
|
+
const lockfile: PackageLock = JSON.parse(lockContent);
|
|
1524
|
+
const lockfileKeys = new Set<string>(Object.keys(lockfile.packages || {}));
|
|
1525
|
+
|
|
1526
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
1527
|
+
console.log('No cache to prune');
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
const cacheFiles = fs.readdirSync(this.cacheDir).filter((f) => f.endsWith('.json'));
|
|
1532
|
+
let prunedCount = 0;
|
|
1533
|
+
|
|
1534
|
+
for (const file of cacheFiles) {
|
|
1535
|
+
const pkgSpec = file.replace('.json', '');
|
|
1536
|
+
if (!lockfileKeys.has(pkgSpec)) {
|
|
1537
|
+
try {
|
|
1538
|
+
fs.unlinkSync(path.join(this.cacheDir, file));
|
|
1539
|
+
prunedCount++;
|
|
1540
|
+
} catch (err) {
|
|
1541
|
+
console.warn(`⚠️ Failed to remove ${pkgSpec}: ${(err as Error).message}`);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
if (prunedCount === 0) {
|
|
1547
|
+
console.log('No entries to prune');
|
|
1548
|
+
} else {
|
|
1549
|
+
console.log(`Pruned ${prunedCount} entries`);
|
|
1550
|
+
}
|
|
1551
|
+
} catch (err) {
|
|
1552
|
+
throw new Error(`Failed to prune cache: ${(err as Error).message}`);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// Phase 12: Parallel download - Main orchestrator
|
|
1557
|
+
private async installParallel(packageSpecs: string[]): Promise<void> {
|
|
1558
|
+
console.log(`🚀 Installing ${packageSpecs.length} packages in parallel...`);
|
|
1559
|
+
|
|
1560
|
+
// Step 1: COLLECT — 전체 의존성 수집
|
|
1561
|
+
const collected = new Map<string, PackageResolution>();
|
|
1562
|
+
for (const spec of packageSpecs) {
|
|
1563
|
+
try {
|
|
1564
|
+
await this.collectDependencies(spec, collected, new Set());
|
|
1565
|
+
} catch (error) {
|
|
1566
|
+
throw new Error(`Failed to collect dependencies for ${spec}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
if (collected.size === 0) {
|
|
1571
|
+
console.log('⚠️ No packages to install');
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
console.log(`📦 Total packages to install: ${collected.size}`);
|
|
1576
|
+
|
|
1577
|
+
// Step 2: 낙관적 잠금 (미리 예약)
|
|
1578
|
+
for (const [name, res] of collected) {
|
|
1579
|
+
this.installedPackages.set(name, res.version);
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
// Step 3: PARALLEL DOWNLOAD (concurrency limited)
|
|
1583
|
+
try {
|
|
1584
|
+
await this.runWithConcurrencyLimit(
|
|
1585
|
+
Array.from(collected.values()),
|
|
1586
|
+
async (pkg) => {
|
|
1587
|
+
const result = await this.downloadAndExtract(pkg.name, pkg.version, pkg.pkgInfo);
|
|
1588
|
+
pkg.integrity = result.integrity;
|
|
1589
|
+
pkg.signature = result.signature;
|
|
1590
|
+
}
|
|
1591
|
+
);
|
|
1592
|
+
} catch (error) {
|
|
1593
|
+
throw new Error(`Parallel download failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
// Step 4: WRITE ONCE — lockfile 1회만 업데이트
|
|
1597
|
+
for (const [name, res] of collected) {
|
|
1598
|
+
if (!res.integrity || !res.signature) {
|
|
1599
|
+
console.warn(`⚠️ Missing integrity/signature for ${name}@${res.version}`);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
// 배치로 lockfile 업데이트
|
|
1604
|
+
for (const [name, res] of collected) {
|
|
1605
|
+
await this.updateLockFile(name, res.version, res.integrity || '', res.signature || '');
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
// packageJson 배치 업데이트
|
|
1609
|
+
await this.batchUpdatePackageJson(collected);
|
|
1610
|
+
|
|
1611
|
+
console.log(`✅ Parallel installation complete (${collected.size} packages)`);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// Phase 12: Parallel download - Dependency collector
|
|
1615
|
+
private async collectDependencies(
|
|
1616
|
+
spec: string,
|
|
1617
|
+
collected: Map<string, PackageResolution>,
|
|
1618
|
+
chain: Set<string>,
|
|
1619
|
+
depth: number = 0
|
|
1620
|
+
): Promise<void> {
|
|
1621
|
+
const [packageName, versionSpec] = spec.includes('@')
|
|
1622
|
+
? spec.split('@')
|
|
1623
|
+
: [spec, 'latest'];
|
|
1624
|
+
|
|
1625
|
+
const pkgKey = `${packageName}@${versionSpec}`;
|
|
1626
|
+
|
|
1627
|
+
// 순환 의존성 방지
|
|
1628
|
+
if (chain.has(pkgKey)) {
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
chain.add(pkgKey);
|
|
1632
|
+
|
|
1633
|
+
// 이미 수집됨
|
|
1634
|
+
const existingKey = Array.from(collected.keys()).find((k) => k === packageName);
|
|
1635
|
+
if (existingKey && collected.has(existingKey)) {
|
|
1636
|
+
const existing = collected.get(existingKey)!;
|
|
1637
|
+
if (this.versionMatches(existing.version, versionSpec)) {
|
|
1638
|
+
return; // 이미 수집됨
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
// Phase 11: Cache hit check
|
|
1643
|
+
let pkgInfo: any = null;
|
|
1644
|
+
let fromCache = false;
|
|
1645
|
+
|
|
1646
|
+
if (this.isExactSpec(versionSpec)) {
|
|
1647
|
+
const cached = this.getCachedPackage(packageName, versionSpec);
|
|
1648
|
+
if (cached) {
|
|
1649
|
+
pkgInfo = cached.pkgInfo;
|
|
1650
|
+
fromCache = true;
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
// Phase 11: Cache miss → fetch from registry
|
|
1655
|
+
if (!pkgInfo) {
|
|
1656
|
+
pkgInfo = await this.fetchPackageInfo(packageName);
|
|
1657
|
+
if (!pkgInfo) {
|
|
1658
|
+
throw new Error(`Package ${packageName} not found`);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
// Resolve version
|
|
1663
|
+
const selectedVersion = this.resolveVersion(pkgInfo.versions, versionSpec);
|
|
1664
|
+
if (!selectedVersion) {
|
|
1665
|
+
throw new Error(`No matching version found for ${packageName}@${versionSpec}`);
|
|
1666
|
+
}
|
|
1667
|
+
const version = selectedVersion.version;
|
|
1668
|
+
|
|
1669
|
+
// Conflict detection (but don't fail — just skip)
|
|
1670
|
+
if (this.detectVersionConflict(packageName, version)) {
|
|
1671
|
+
const existingVersion = this.installedPackages.get(packageName);
|
|
1672
|
+
if (existingVersion) {
|
|
1673
|
+
if (this.versionMatches(existingVersion, version)) {
|
|
1674
|
+
return; // Already have this version
|
|
1675
|
+
}
|
|
1676
|
+
// Try to resolve conflict
|
|
1677
|
+
try {
|
|
1678
|
+
const resolved = this.resolveConflict(packageName, existingVersion, version);
|
|
1679
|
+
if (resolved !== version) {
|
|
1680
|
+
return; // Use existing version
|
|
1681
|
+
}
|
|
1682
|
+
} catch (err) {
|
|
1683
|
+
throw err;
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
// Add to collected
|
|
1689
|
+
const resolution: PackageResolution = {
|
|
1690
|
+
name: packageName,
|
|
1691
|
+
version: version,
|
|
1692
|
+
pkgInfo: pkgInfo,
|
|
1693
|
+
fromCache: fromCache
|
|
1694
|
+
};
|
|
1695
|
+
collected.set(packageName, resolution);
|
|
1696
|
+
|
|
1697
|
+
// Recursively collect dependencies
|
|
1698
|
+
const versionEntry = pkgInfo.versions?.find((v: any) => v.version === version);
|
|
1699
|
+
const deps = versionEntry?.dependencies || {};
|
|
1700
|
+
for (const [depName, depVersion] of Object.entries(deps)) {
|
|
1701
|
+
await this.collectDependencies(`${depName}@${depVersion}`, collected, chain, depth + 1);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
// Phase 12: Parallel download - Concurrency limiter (semaphore pattern)
|
|
1706
|
+
private async runWithConcurrencyLimit<T>(
|
|
1707
|
+
items: T[],
|
|
1708
|
+
fn: (item: T) => Promise<void>,
|
|
1709
|
+
limit?: number
|
|
1710
|
+
): Promise<void> {
|
|
1711
|
+
const maxConcurrency = limit ?? this.concurrency;
|
|
1712
|
+
const queue = [...items];
|
|
1713
|
+
let activeCount = 0;
|
|
1714
|
+
let completed = 0;
|
|
1715
|
+
|
|
1716
|
+
const worker = async () => {
|
|
1717
|
+
while (queue.length > 0) {
|
|
1718
|
+
const item = queue.shift();
|
|
1719
|
+
if (!item) break;
|
|
1720
|
+
|
|
1721
|
+
activeCount++;
|
|
1722
|
+
try {
|
|
1723
|
+
await fn(item);
|
|
1724
|
+
completed++;
|
|
1725
|
+
} catch (error) {
|
|
1726
|
+
throw error;
|
|
1727
|
+
} finally {
|
|
1728
|
+
activeCount--;
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
|
|
1733
|
+
const workers = Array(Math.min(maxConcurrency, items.length))
|
|
1734
|
+
.fill(null)
|
|
1735
|
+
.map(() => worker());
|
|
1736
|
+
|
|
1737
|
+
await Promise.all(workers);
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// Phase 12: Parallel download - Batch packageJson update
|
|
1741
|
+
private async batchUpdatePackageJson(collected: Map<string, PackageResolution>): Promise<void> {
|
|
1742
|
+
const packageJsonPath = path.join(this.cwd, 'package.json');
|
|
1743
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
1744
|
+
// Create package.json if not exists
|
|
1745
|
+
const pkg: PackageJson = {
|
|
1746
|
+
name: path.basename(this.cwd),
|
|
1747
|
+
version: '1.0.0',
|
|
1748
|
+
dependencies: {}
|
|
1749
|
+
};
|
|
1750
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2));
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
const packageJson: PackageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
1754
|
+
if (!packageJson.dependencies) {
|
|
1755
|
+
packageJson.dependencies = {};
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
// Batch update all dependencies
|
|
1759
|
+
for (const [name, res] of collected) {
|
|
1760
|
+
packageJson.dependencies[name] = res.version;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
// Phase 13: OAuth2 토큰 영속화
|
|
1767
|
+
private async loadOAuthToken(): Promise<void> {
|
|
1768
|
+
try {
|
|
1769
|
+
const authDir = path.dirname(this.authTokenPath);
|
|
1770
|
+
if (!fs.existsSync(authDir)) {
|
|
1771
|
+
fs.mkdirSync(authDir, { recursive: true });
|
|
1772
|
+
}
|
|
1773
|
+
if (fs.existsSync(this.authTokenPath)) {
|
|
1774
|
+
const data = fs.readFileSync(this.authTokenPath, 'utf-8');
|
|
1775
|
+
this.oauthToken = JSON.parse(data) as OAuthToken;
|
|
1776
|
+
}
|
|
1777
|
+
} catch (err) {
|
|
1778
|
+
// 토큰 파일 읽기 실패는 무시 (미로그인 상태)
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
private async saveOAuthToken(): Promise<void> {
|
|
1783
|
+
const authDir = path.dirname(this.authTokenPath);
|
|
1784
|
+
if (!fs.existsSync(authDir)) {
|
|
1785
|
+
fs.mkdirSync(authDir, { recursive: true });
|
|
1786
|
+
}
|
|
1787
|
+
fs.writeFileSync(this.authTokenPath, JSON.stringify(this.oauthToken, null, 2));
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
private async clearOAuthToken(): Promise<void> {
|
|
1791
|
+
this.oauthToken = null;
|
|
1792
|
+
if (fs.existsSync(this.authTokenPath)) {
|
|
1793
|
+
fs.unlinkSync(this.authTokenPath);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
// Phase 13: OAuth2 로그인
|
|
1798
|
+
private async loginCommand(params: string[]): Promise<void> {
|
|
1799
|
+
const registryUrl = params[0] || this.registryUrl;
|
|
1800
|
+
|
|
1801
|
+
if (params.length < 2) {
|
|
1802
|
+
console.error('Usage: vpm login <username> <password> [registry-url]');
|
|
1803
|
+
process.exit(1);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
const username = params[0];
|
|
1807
|
+
const password = params[1];
|
|
1808
|
+
|
|
1809
|
+
try {
|
|
1810
|
+
const response = await this.makeRequest('POST', `/oauth/token`, { username, password });
|
|
1811
|
+
|
|
1812
|
+
if (!response.accessToken) {
|
|
1813
|
+
throw new Error('Invalid credentials');
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
this.oauthToken = {
|
|
1817
|
+
accessToken: response.accessToken,
|
|
1818
|
+
refreshToken: response.refreshToken,
|
|
1819
|
+
expiresAt: Date.now() + (response.expiresIn || 3600) * 1000,
|
|
1820
|
+
scope: response.scope || ['install', 'publish'],
|
|
1821
|
+
username,
|
|
1822
|
+
registryUrl
|
|
1823
|
+
};
|
|
1824
|
+
|
|
1825
|
+
await this.saveOAuthToken();
|
|
1826
|
+
console.log(`✓ Successfully logged in as ${username}`);
|
|
1827
|
+
} catch (err: any) {
|
|
1828
|
+
console.error(`✗ Login failed: ${err.message}`);
|
|
1829
|
+
process.exit(1);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
// Phase 13: OAuth2 로그아웃
|
|
1834
|
+
private async logoutCommand(): Promise<void> {
|
|
1835
|
+
await this.clearOAuthToken();
|
|
1836
|
+
console.log('✓ Logged out successfully');
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
// Phase 13: 현재 사용자 정보
|
|
1840
|
+
private async whoamiCommand(): Promise<void> {
|
|
1841
|
+
if (!this.oauthToken) {
|
|
1842
|
+
console.log('Not logged in. Run: vpm login <username> <password>');
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
try {
|
|
1847
|
+
const response = await this.makeRequest('GET', '/oauth/userinfo', undefined, this.oauthToken.accessToken);
|
|
1848
|
+
|
|
1849
|
+
console.log(`User: ${response.username || this.oauthToken.username}`);
|
|
1850
|
+
console.log(`Scope: ${this.oauthToken.scope.join(', ')}`);
|
|
1851
|
+
console.log(`Expires: ${new Date(this.oauthToken.expiresAt).toISOString()}`);
|
|
1852
|
+
} catch (err: any) {
|
|
1853
|
+
console.error(`✗ Error fetching user info: ${err.message}`);
|
|
1854
|
+
process.exit(1);
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
// Phase 13: 토큰 만료 확인 및 갱신
|
|
1859
|
+
private async validateAndRefreshToken(): Promise<boolean> {
|
|
1860
|
+
if (!this.oauthToken) return false;
|
|
1861
|
+
|
|
1862
|
+
// 5분 이내 만료 예정 → 갱신
|
|
1863
|
+
if (this.oauthToken.expiresAt - Date.now() < 5 * 60 * 1000) {
|
|
1864
|
+
await this.refreshOAuthToken();
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
return true;
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
// Phase 13: OAuth2 토큰 갱신
|
|
1871
|
+
private async refreshOAuthToken(): Promise<void> {
|
|
1872
|
+
if (!this.oauthToken || !this.oauthToken.refreshToken) {
|
|
1873
|
+
throw new Error('Cannot refresh token: no refresh token available');
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
try {
|
|
1877
|
+
const response = await this.makeRequest('POST', '/oauth/refresh', { refreshToken: this.oauthToken.refreshToken });
|
|
1878
|
+
|
|
1879
|
+
this.oauthToken.accessToken = response.accessToken;
|
|
1880
|
+
this.oauthToken.expiresAt = Date.now() + (response.expiresIn || 3600) * 1000;
|
|
1881
|
+
|
|
1882
|
+
if (response.refreshToken) {
|
|
1883
|
+
this.oauthToken.refreshToken = response.refreshToken;
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
await this.saveOAuthToken();
|
|
1887
|
+
} catch (err: any) {
|
|
1888
|
+
console.error(`✗ Token refresh failed: ${err.message}`);
|
|
1889
|
+
process.exit(1);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
private showHelp(): void {
|
|
1894
|
+
console.log(`
|
|
1895
|
+
v9 Package Manager (vpm) - v1.0.0
|
|
1896
|
+
|
|
1897
|
+
Usage:
|
|
1898
|
+
vpm <command> [options]
|
|
1899
|
+
|
|
1900
|
+
Commands:
|
|
1901
|
+
login [registry-url] Authenticate with registry using OAuth2 (Phase 13)
|
|
1902
|
+
logout Remove saved credentials (Phase 13)
|
|
1903
|
+
whoami Show current user info (Phase 13)
|
|
1904
|
+
install [package@version...] Install package(s) - supports multiple (Phase 12 parallel)
|
|
1905
|
+
publish [--private] Publish package (--private for private registry, Phase 13)
|
|
1906
|
+
search <query> Search packages
|
|
1907
|
+
list List installed packages
|
|
1908
|
+
update [package] Update package(s)
|
|
1909
|
+
uninstall <package> Uninstall package
|
|
1910
|
+
info <package> Show package information
|
|
1911
|
+
token <action> Manage auth tokens
|
|
1912
|
+
verify Verify package integrity
|
|
1913
|
+
reinstall Reinstall all packages from lockfile (Phase 12 parallel)
|
|
1914
|
+
cache dir Show cache directory path
|
|
1915
|
+
cache list List cached packages
|
|
1916
|
+
cache verify Verify cache integrity
|
|
1917
|
+
cache clean Clear all cache
|
|
1918
|
+
cache prune Remove cache entries not in lockfile
|
|
1919
|
+
help Show this help message
|
|
1920
|
+
|
|
1921
|
+
Examples:
|
|
1922
|
+
vpm install awesome-lib
|
|
1923
|
+
vpm install awesome-lib@1.2.0
|
|
1924
|
+
vpm install pkg1@1.0.0 pkg2@2.0.0 pkg3@latest (parallel install)
|
|
1925
|
+
vpm search data
|
|
1926
|
+
vpm list
|
|
1927
|
+
vpm update
|
|
1928
|
+
vpm publish
|
|
1929
|
+
vpm cache list
|
|
1930
|
+
vpm cache verify
|
|
1931
|
+
|
|
1932
|
+
Environment:
|
|
1933
|
+
VPM_REGISTRY Registry URL (default: http://registry.v9.dclub.kr)
|
|
1934
|
+
VPM_AUTH_TOKEN Fallback auth token for publishing (use 'vpm login' for OAuth2)
|
|
1935
|
+
VPM_CACHE_DIR Override cache directory (default: ~/.vpm/cache/packages)
|
|
1936
|
+
VPM_SIGNING_KEY Signing key for package integrity verification
|
|
1937
|
+
VPM_CONCURRENCY Parallel download limit (default: 4, min: 1, max: 16)
|
|
1938
|
+
|
|
1939
|
+
OAuth2 (Phase 13):
|
|
1940
|
+
~/.vpm/auth.json Saved OAuth2 credentials (auto-created by 'vpm login')
|
|
1941
|
+
|
|
1942
|
+
For more info: https://v9.dclub.kr/docs/vpm
|
|
1943
|
+
`);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
// CLI Entry Point
|
|
1948
|
+
const args = process.argv.slice(2);
|
|
1949
|
+
const cli = new VpmCli();
|
|
1950
|
+
cli.run(args).catch((error) => {
|
|
1951
|
+
console.error(`❌ ${error.message}`);
|
|
1952
|
+
process.exit(1);
|
|
1953
|
+
});
|
|
1954
|
+
|
|
1955
|
+
export { VpmCli };
|