@rkarim08/sia 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +35 -0
- package/.claude-plugin/plugin.json +27 -0
- package/.mcp.json +13 -0
- package/CLAUDE.md +226 -0
- package/LICENSE +202 -0
- package/PLUGIN_README.md +253 -0
- package/README.md +1013 -0
- package/agents/sia-changelog-writer.md +89 -0
- package/agents/sia-code-reviewer.md +86 -0
- package/agents/sia-conflict-resolver.md +100 -0
- package/agents/sia-convention-enforcer.md +69 -0
- package/agents/sia-debug.md +106 -0
- package/agents/sia-decision-reviewer.md +101 -0
- package/agents/sia-dependency-tracker.md +80 -0
- package/agents/sia-explain.md +126 -0
- package/agents/sia-feature.md +116 -0
- package/agents/sia-knowledge-capture.md +117 -0
- package/agents/sia-lead-architecture-advisor.md +93 -0
- package/agents/sia-lead-team-health.md +107 -0
- package/agents/sia-migration.md +100 -0
- package/agents/sia-onboarding.md +115 -0
- package/agents/sia-orientation.md +99 -0
- package/agents/sia-pm-briefing.md +106 -0
- package/agents/sia-pm-risk-advisor.md +82 -0
- package/agents/sia-qa-analyst.md +116 -0
- package/agents/sia-qa-regression-map.md +94 -0
- package/agents/sia-refactor.md +115 -0
- package/agents/sia-regression.md +112 -0
- package/agents/sia-security-audit.md +125 -0
- package/agents/sia-test-advisor.md +91 -0
- package/hooks/hooks.json +98 -0
- package/migrations/bridge/001_initial.sql +34 -0
- package/migrations/episodic/001_initial.sql +35 -0
- package/migrations/meta/001_initial.sql +68 -0
- package/migrations/semantic/001_initial.sql +292 -0
- package/migrations/semantic/002_ontology.sql +89 -0
- package/migrations/semantic/003_freshness.sql +63 -0
- package/migrations/semantic/004_v5_unified_schema.sql +194 -0
- package/migrations/semantic/005_backfill_event_kinds.sql +8 -0
- package/migrations/semantic/006_tree_sitter.sql +6 -0
- package/migrations/semantic/007_branch_snapshots.sql +22 -0
- package/package.json +110 -0
- package/scripts/branch-switch.sh +13 -0
- package/scripts/build-wasm-grammars.sh +81 -0
- package/scripts/post-compact.sh +8 -0
- package/scripts/post-tool-use.sh +10 -0
- package/scripts/pre-compact.sh +8 -0
- package/scripts/session-end.sh +8 -0
- package/scripts/session-start.sh +8 -0
- package/scripts/start-mcp.ts +45 -0
- package/scripts/stop-hook.sh +8 -0
- package/scripts/user-prompt-submit.sh +8 -0
- package/scripts/viz-server.ts +152 -0
- package/skills/sia-brainstorm/SKILL.md +156 -0
- package/skills/sia-brainstorm/scripts/frame-template.html +214 -0
- package/skills/sia-brainstorm/scripts/helper.js +95 -0
- package/skills/sia-brainstorm/scripts/server.cjs +338 -0
- package/skills/sia-brainstorm/scripts/start-server.sh +153 -0
- package/skills/sia-brainstorm/scripts/stop-server.sh +55 -0
- package/skills/sia-brainstorm/spec-document-reviewer-prompt.md +49 -0
- package/skills/sia-brainstorm/visual-companion.md +286 -0
- package/skills/sia-capture/SKILL.md +64 -0
- package/skills/sia-compare/SKILL.md +33 -0
- package/skills/sia-conflicts/SKILL.md +38 -0
- package/skills/sia-debug-workflow/SKILL.md +120 -0
- package/skills/sia-debug-workflow/root-cause-tracing.md +70 -0
- package/skills/sia-debug-workflow/scripts/find-polluter.sh +64 -0
- package/skills/sia-debug-workflow/temporal-investigation.md +72 -0
- package/skills/sia-digest/SKILL.md +23 -0
- package/skills/sia-dispatch/SKILL.md +69 -0
- package/skills/sia-dispatch/agent-task-template.md +99 -0
- package/skills/sia-doctor/SKILL.md +39 -0
- package/skills/sia-execute/SKILL.md +70 -0
- package/skills/sia-execute-plan/SKILL.md +85 -0
- package/skills/sia-export-import/SKILL.md +49 -0
- package/skills/sia-export-knowledge/SKILL.md +46 -0
- package/skills/sia-finish/SKILL.md +100 -0
- package/skills/sia-finish/pr-summary-template.md +54 -0
- package/skills/sia-freshness/SKILL.md +38 -0
- package/skills/sia-history/SKILL.md +42 -0
- package/skills/sia-impact/SKILL.md +70 -0
- package/skills/sia-index/SKILL.md +54 -0
- package/skills/sia-install/SKILL.md +39 -0
- package/skills/sia-lead-compliance/SKILL.md +16 -0
- package/skills/sia-lead-drift-report/SKILL.md +16 -0
- package/skills/sia-lead-knowledge-map/SKILL.md +16 -0
- package/skills/sia-learn/SKILL.md +58 -0
- package/skills/sia-plan/SKILL.md +68 -0
- package/skills/sia-plan/plan-reviewer-prompt.md +63 -0
- package/skills/sia-playbooks/SKILL.md +29 -0
- package/skills/sia-playbooks/reference-feature.md +100 -0
- package/skills/sia-playbooks/reference-flagging.md +50 -0
- package/skills/sia-playbooks/reference-orientation.md +92 -0
- package/skills/sia-playbooks/reference-regression.md +115 -0
- package/skills/sia-playbooks/reference-review.md +64 -0
- package/skills/sia-playbooks/reference-tools.md +239 -0
- package/skills/sia-pm-decision-log/SKILL.md +28 -0
- package/skills/sia-pm-risk-dashboard/SKILL.md +24 -0
- package/skills/sia-pm-sprint-summary/SKILL.md +27 -0
- package/skills/sia-prune/SKILL.md +45 -0
- package/skills/sia-qa-coverage/SKILL.md +28 -0
- package/skills/sia-qa-flaky/SKILL.md +20 -0
- package/skills/sia-qa-report/SKILL.md +26 -0
- package/skills/sia-reindex/SKILL.md +30 -0
- package/skills/sia-review-respond/SKILL.md +88 -0
- package/skills/sia-review-respond/pushback-patterns.md +90 -0
- package/skills/sia-search/SKILL.md +47 -0
- package/skills/sia-setup/SKILL.md +82 -0
- package/skills/sia-setup/setup-checklist.md +97 -0
- package/skills/sia-stats/SKILL.md +36 -0
- package/skills/sia-status/SKILL.md +44 -0
- package/skills/sia-sync/SKILL.md +46 -0
- package/skills/sia-team/SKILL.md +64 -0
- package/skills/sia-test/SKILL.md +92 -0
- package/skills/sia-test/testing-anti-patterns.md +104 -0
- package/skills/sia-tour/SKILL.md +29 -0
- package/skills/sia-upgrade/SKILL.md +43 -0
- package/skills/sia-verify/SKILL.md +81 -0
- package/skills/sia-visualize/SKILL.md +28 -0
- package/skills/sia-visualize-live/SKILL.md +55 -0
- package/skills/sia-visualize-live/scripts/graph-template.html +389 -0
- package/skills/sia-visualize-live/scripts/start-visualizer.sh +161 -0
- package/skills/sia-visualize-live/scripts/stop-visualizer.sh +55 -0
- package/skills/sia-visualize-live/scripts/visualizer-server.cjs +264 -0
- package/skills/sia-workspace/SKILL.md +57 -0
- package/src/agent/claude-md-template-flagging.md +219 -0
- package/src/agent/claude-md-template.md +213 -0
- package/src/agent/modules/sia-feature.md +100 -0
- package/src/agent/modules/sia-flagging.md +50 -0
- package/src/agent/modules/sia-orientation.md +92 -0
- package/src/agent/modules/sia-regression.md +115 -0
- package/src/agent/modules/sia-review.md +64 -0
- package/src/agent/modules/sia-tools.md +239 -0
- package/src/ast/extractors/c-include.ts +189 -0
- package/src/ast/extractors/csharp-project.ts +260 -0
- package/src/ast/extractors/prisma-schema.ts +44 -0
- package/src/ast/extractors/project-manifest.ts +111 -0
- package/src/ast/extractors/sql-schema.ts +67 -0
- package/src/ast/extractors/tier-a.ts +423 -0
- package/src/ast/extractors/tier-b.ts +289 -0
- package/src/ast/extractors/tier-dispatch.ts +247 -0
- package/src/ast/index-worker.ts +108 -0
- package/src/ast/indexer.ts +484 -0
- package/src/ast/languages.ts +408 -0
- package/src/ast/pagerank-builder.ts +125 -0
- package/src/ast/path-utils.ts +137 -0
- package/src/ast/tree-sitter/backends/native.ts +57 -0
- package/src/ast/tree-sitter/backends/wasm.ts +39 -0
- package/src/ast/tree-sitter/call-walker.ts +44 -0
- package/src/ast/tree-sitter/edit-computer.ts +55 -0
- package/src/ast/tree-sitter/query-runner.ts +46 -0
- package/src/ast/tree-sitter/service.ts +174 -0
- package/src/ast/tree-sitter/tree-cache.ts +39 -0
- package/src/ast/tree-sitter/types.ts +79 -0
- package/src/ast/watcher.ts +322 -0
- package/src/capture/chunker.ts +169 -0
- package/src/capture/consolidate.ts +127 -0
- package/src/capture/edge-inferrer.ts +161 -0
- package/src/capture/embedder.ts +166 -0
- package/src/capture/embedding-cache.ts +73 -0
- package/src/capture/flag-processor.ts +64 -0
- package/src/capture/hook.ts +67 -0
- package/src/capture/pipeline.ts +450 -0
- package/src/capture/prompts/consolidate.ts +25 -0
- package/src/capture/prompts/edge-infer.ts +29 -0
- package/src/capture/prompts/extract-flagged.ts +36 -0
- package/src/capture/prompts/extract.ts +42 -0
- package/src/capture/tokenizer.ts +147 -0
- package/src/capture/track-a-ast.ts +93 -0
- package/src/capture/track-b-llm.ts +149 -0
- package/src/capture/types.ts +64 -0
- package/src/cli/commands/community.ts +137 -0
- package/src/cli/commands/compare.ts +123 -0
- package/src/cli/commands/conflicts.ts +41 -0
- package/src/cli/commands/digest.ts +197 -0
- package/src/cli/commands/disable-flagging.ts +34 -0
- package/src/cli/commands/doctor.ts +240 -0
- package/src/cli/commands/download-model.ts +161 -0
- package/src/cli/commands/enable-flagging.ts +34 -0
- package/src/cli/commands/export-knowledge.ts +208 -0
- package/src/cli/commands/export.ts +85 -0
- package/src/cli/commands/freshness.ts +164 -0
- package/src/cli/commands/graph.ts +51 -0
- package/src/cli/commands/history.ts +139 -0
- package/src/cli/commands/import.ts +335 -0
- package/src/cli/commands/install.ts +156 -0
- package/src/cli/commands/lead-report.ts +241 -0
- package/src/cli/commands/learn.ts +321 -0
- package/src/cli/commands/pm-report.ts +413 -0
- package/src/cli/commands/prune.ts +75 -0
- package/src/cli/commands/qa-report.ts +278 -0
- package/src/cli/commands/reindex.ts +104 -0
- package/src/cli/commands/rollback.ts +70 -0
- package/src/cli/commands/search.ts +103 -0
- package/src/cli/commands/server.ts +91 -0
- package/src/cli/commands/share.ts +33 -0
- package/src/cli/commands/stats.ts +79 -0
- package/src/cli/commands/status.ts +176 -0
- package/src/cli/commands/sync.ts +96 -0
- package/src/cli/commands/team.ts +118 -0
- package/src/cli/commands/tour.ts +157 -0
- package/src/cli/commands/visualize-live.ts +162 -0
- package/src/cli/commands/workspace.ts +117 -0
- package/src/cli/index.ts +424 -0
- package/src/cli/learn-progress.ts +87 -0
- package/src/community/detection-bridge.ts +344 -0
- package/src/community/leiden.ts +462 -0
- package/src/community/raptor.ts +210 -0
- package/src/community/scheduler.ts +74 -0
- package/src/community/summarize.ts +115 -0
- package/src/decay/archiver.ts +73 -0
- package/src/decay/bridge-orphan-cleanup.ts +212 -0
- package/src/decay/consolidation-sweep.ts +112 -0
- package/src/decay/decay.ts +116 -0
- package/src/decay/deep-validator.ts +62 -0
- package/src/decay/episodic-promoter.ts +132 -0
- package/src/decay/maintenance-scheduler.ts +326 -0
- package/src/decay/scheduler.ts +6 -0
- package/src/decay/session-sweeper.ts +79 -0
- package/src/decay/types.ts +17 -0
- package/src/freshness/confidence-decay.ts +122 -0
- package/src/freshness/cuckoo-filter.ts +176 -0
- package/src/freshness/deep-validation.ts +345 -0
- package/src/freshness/dirty-tracker.ts +237 -0
- package/src/freshness/file-watcher-layer.ts +119 -0
- package/src/freshness/firewall.ts +64 -0
- package/src/freshness/git-reconcile-layer.ts +161 -0
- package/src/freshness/inverted-index.ts +158 -0
- package/src/freshness/stale-read-layer.ts +222 -0
- package/src/graph/audit.ts +69 -0
- package/src/graph/bridge-db.ts +141 -0
- package/src/graph/communities.ts +195 -0
- package/src/graph/db-interface.ts +259 -0
- package/src/graph/edges.ts +163 -0
- package/src/graph/entities.ts +327 -0
- package/src/graph/episodic-db.ts +113 -0
- package/src/graph/flags.ts +31 -0
- package/src/graph/meta-db.ts +200 -0
- package/src/graph/semantic-db.ts +101 -0
- package/src/graph/session-resume.ts +56 -0
- package/src/graph/snapshots.ts +342 -0
- package/src/graph/staging.ts +151 -0
- package/src/graph/types.ts +128 -0
- package/src/hooks/adapters/claude-code.ts +21 -0
- package/src/hooks/adapters/cline.ts +43 -0
- package/src/hooks/adapters/cursor.ts +65 -0
- package/src/hooks/adapters/generic.ts +12 -0
- package/src/hooks/agent-detect.ts +34 -0
- package/src/hooks/claude-md-directives.ts +32 -0
- package/src/hooks/event-router.ts +182 -0
- package/src/hooks/extractors/pattern-detector.ts +111 -0
- package/src/hooks/handlers/post-compact.ts +30 -0
- package/src/hooks/handlers/post-tool-use.ts +403 -0
- package/src/hooks/handlers/pre-compact.ts +100 -0
- package/src/hooks/handlers/session-end.ts +47 -0
- package/src/hooks/handlers/session-start.ts +154 -0
- package/src/hooks/handlers/stop.ts +128 -0
- package/src/hooks/handlers/user-prompt-submit.ts +68 -0
- package/src/hooks/plugin-branch-switch.ts +68 -0
- package/src/hooks/plugin-common.ts +47 -0
- package/src/hooks/plugin-post-compact.ts +28 -0
- package/src/hooks/plugin-post-tool-use.ts +38 -0
- package/src/hooks/plugin-pre-compact.ts +37 -0
- package/src/hooks/plugin-session-end.ts +37 -0
- package/src/hooks/plugin-session-start.ts +75 -0
- package/src/hooks/plugin-stop.ts +61 -0
- package/src/hooks/plugin-user-prompt-submit.ts +47 -0
- package/src/hooks/types.ts +43 -0
- package/src/knowledge/discovery.ts +238 -0
- package/src/knowledge/external-refs.ts +98 -0
- package/src/knowledge/freshness.ts +221 -0
- package/src/knowledge/ingest.ts +330 -0
- package/src/knowledge/markdown-export.ts +229 -0
- package/src/knowledge/markdown-import.ts +359 -0
- package/src/knowledge/patterns.ts +74 -0
- package/src/knowledge/templates.ts +307 -0
- package/src/llm/ai-sdk-adapter.ts +46 -0
- package/src/llm/config.ts +88 -0
- package/src/llm/cost-tracker.ts +110 -0
- package/src/llm/prompts/extraction.ts +55 -0
- package/src/llm/prompts/summarization.ts +36 -0
- package/src/llm/prompts/validation.ts +37 -0
- package/src/llm/provider-registry.ts +68 -0
- package/src/llm/reliability.ts +179 -0
- package/src/llm/schemas.ts +52 -0
- package/src/mcp/freshness-annotator.ts +69 -0
- package/src/mcp/server.ts +949 -0
- package/src/mcp/tools/sia-ast-query.ts +225 -0
- package/src/mcp/tools/sia-at-time.ts +151 -0
- package/src/mcp/tools/sia-backlinks.ts +87 -0
- package/src/mcp/tools/sia-batch-execute.ts +169 -0
- package/src/mcp/tools/sia-by-file.ts +89 -0
- package/src/mcp/tools/sia-community.ts +113 -0
- package/src/mcp/tools/sia-doctor.ts +73 -0
- package/src/mcp/tools/sia-execute-file.ts +122 -0
- package/src/mcp/tools/sia-execute.ts +104 -0
- package/src/mcp/tools/sia-expand.ts +158 -0
- package/src/mcp/tools/sia-fetch-and-index.ts +241 -0
- package/src/mcp/tools/sia-flag.ts +65 -0
- package/src/mcp/tools/sia-index.ts +111 -0
- package/src/mcp/tools/sia-note.ts +134 -0
- package/src/mcp/tools/sia-search.ts +105 -0
- package/src/mcp/tools/sia-stats.ts +63 -0
- package/src/mcp/tools/sia-sync-status.ts +44 -0
- package/src/mcp/tools/sia-upgrade.ts +247 -0
- package/src/mcp/truncate.ts +231 -0
- package/src/native/bridge.ts +167 -0
- package/src/native/fallback-ast-diff.ts +144 -0
- package/src/native/fallback-graph.ts +325 -0
- package/src/ontology/constraints.ts +56 -0
- package/src/ontology/errors.ts +8 -0
- package/src/ontology/middleware.ts +266 -0
- package/src/retrieval/bm25-search.ts +151 -0
- package/src/retrieval/context-assembly.ts +76 -0
- package/src/retrieval/graph-traversal.ts +168 -0
- package/src/retrieval/pagerank.ts +40 -0
- package/src/retrieval/query-classifier.ts +106 -0
- package/src/retrieval/reranker.ts +156 -0
- package/src/retrieval/search.ts +236 -0
- package/src/retrieval/throttle.ts +102 -0
- package/src/retrieval/vector-search.ts +203 -0
- package/src/retrieval/workspace-search.ts +130 -0
- package/src/sandbox/context-mode.ts +285 -0
- package/src/sandbox/credential-pass.ts +55 -0
- package/src/sandbox/executor.ts +235 -0
- package/src/security/pattern-detector.ts +127 -0
- package/src/security/rule-of-two.ts +50 -0
- package/src/security/sanitize.ts +46 -0
- package/src/security/semantic-consistency.ts +93 -0
- package/src/security/staging-promoter.ts +154 -0
- package/src/shared/config.ts +302 -0
- package/src/shared/diagnostics.ts +210 -0
- package/src/shared/errors.ts +48 -0
- package/src/shared/git-utils.ts +143 -0
- package/src/shared/llm-client.ts +120 -0
- package/src/shared/logger.ts +99 -0
- package/src/shared/types.ts +79 -0
- package/src/sync/client.ts +43 -0
- package/src/sync/conflict.ts +106 -0
- package/src/sync/dedup.ts +183 -0
- package/src/sync/hlc.ts +117 -0
- package/src/sync/keychain.ts +144 -0
- package/src/sync/pull.ts +232 -0
- package/src/sync/push.ts +131 -0
- package/src/types/chokidar.d.ts +23 -0
- package/src/visualization/graph-renderer.ts +312 -0
- package/src/visualization/subgraph-extract.ts +208 -0
- package/src/visualization/views/community-clusters.ts +246 -0
- package/src/visualization/views/dependency-map.ts +189 -0
- package/src/visualization/views/graph-explorer.ts +364 -0
- package/src/visualization/views/timeline.ts +247 -0
- package/src/workspace/api-contracts.ts +226 -0
- package/src/workspace/cross-repo.ts +61 -0
- package/src/workspace/detector.ts +190 -0
- package/src/workspace/manifest.ts +141 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// Module: native/bridge — Single import site for the native performance module
|
|
2
|
+
// Three-tier fallback: native → wasm → typescript
|
|
3
|
+
//
|
|
4
|
+
// Since @sia/native and @sia/native-wasm do not exist yet, this always
|
|
5
|
+
// falls back to the pure TypeScript implementations.
|
|
6
|
+
|
|
7
|
+
import { fallbackAstDiff } from "./fallback-ast-diff";
|
|
8
|
+
import { fallbackGraphCompute } from "./fallback-graph";
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Public types
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
export interface AstDiffResult {
|
|
15
|
+
inserts: Array<{ node_id: string; kind: string; name: string }>;
|
|
16
|
+
removes: Array<{ node_id: string }>;
|
|
17
|
+
updates: Array<{ node_id: string; old_name: string; new_name: string }>;
|
|
18
|
+
moves: Array<{ node_id: string; old_parent: string; new_parent: string }>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface GraphComputeResult {
|
|
22
|
+
scores: Float64Array;
|
|
23
|
+
node_ids: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type GraphAlgorithm =
|
|
27
|
+
| { kind: "pagerank"; damping: number; iterations: number; seed_nodes?: string[] }
|
|
28
|
+
| { kind: "shortest_path"; source: string }
|
|
29
|
+
| { kind: "betweenness_centrality" }
|
|
30
|
+
| { kind: "connected_components" };
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Tier detection
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
export type NativeModuleStatus = "native" | "wasm" | "typescript";
|
|
37
|
+
|
|
38
|
+
let _cachedStatus: NativeModuleStatus | null = null;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Probe the three tiers in order and return which one is available:
|
|
42
|
+
* 1. `@sia/native` — compiled Rust module
|
|
43
|
+
* 2. `@sia/native-wasm` — WASM build of the same Rust module
|
|
44
|
+
* 3. `typescript` — pure TS fallback (always available)
|
|
45
|
+
*/
|
|
46
|
+
export function isNativeAvailable(): NativeModuleStatus {
|
|
47
|
+
return getNativeModuleStatus();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Detect which implementation is active:
|
|
52
|
+
* - "native" — native Rust module loaded successfully
|
|
53
|
+
* - "wasm" — WASM fallback loaded successfully
|
|
54
|
+
* - "typescript" — pure TypeScript fallback (no compiled module)
|
|
55
|
+
*/
|
|
56
|
+
export function getNativeModuleStatus(): NativeModuleStatus {
|
|
57
|
+
if (_cachedStatus !== null) {
|
|
58
|
+
return _cachedStatus;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Attempt to load the native Rust module
|
|
62
|
+
try {
|
|
63
|
+
require("@sia/native");
|
|
64
|
+
_cachedStatus = "native";
|
|
65
|
+
return _cachedStatus;
|
|
66
|
+
} catch {
|
|
67
|
+
// not installed
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Attempt to load the WASM build
|
|
71
|
+
try {
|
|
72
|
+
require("@sia/native-wasm");
|
|
73
|
+
_cachedStatus = "wasm";
|
|
74
|
+
return _cachedStatus;
|
|
75
|
+
} catch {
|
|
76
|
+
// not installed
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Pure TypeScript fallback
|
|
80
|
+
_cachedStatus = "typescript";
|
|
81
|
+
return _cachedStatus;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Reset the cached status (for testing).
|
|
86
|
+
*/
|
|
87
|
+
export function _resetNativeStatusCache(): void {
|
|
88
|
+
_cachedStatus = null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Lazy native module handle
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
interface NativeModule {
|
|
96
|
+
astDiff(
|
|
97
|
+
oldTreeBytes: Uint8Array,
|
|
98
|
+
newTreeBytes: Uint8Array,
|
|
99
|
+
nodeIdMap: Map<number, string>,
|
|
100
|
+
): AstDiffResult;
|
|
101
|
+
graphCompute(edges: Int32Array, nodeIds: string[], algorithm: GraphAlgorithm): GraphComputeResult;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function loadNativeModule(pkg: string): NativeModule | null {
|
|
105
|
+
try {
|
|
106
|
+
return require(pkg) as NativeModule;
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Public API — routes to the best available implementation
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Diff two AST trees encoded as byte arrays.
|
|
118
|
+
*
|
|
119
|
+
* In the TypeScript fallback the bytes are JSON-encoded arrays of
|
|
120
|
+
* `{name, kind, parent}` objects. `nodeIdMap` maps positional indices in the
|
|
121
|
+
* old tree to stable node IDs.
|
|
122
|
+
*/
|
|
123
|
+
export function astDiff(
|
|
124
|
+
oldTreeBytes: Uint8Array,
|
|
125
|
+
newTreeBytes: Uint8Array,
|
|
126
|
+
nodeIdMap: Map<number, string>,
|
|
127
|
+
): AstDiffResult {
|
|
128
|
+
const tier = getNativeModuleStatus();
|
|
129
|
+
|
|
130
|
+
if (tier === "native") {
|
|
131
|
+
const mod = loadNativeModule("@sia/native");
|
|
132
|
+
if (mod) return mod.astDiff(oldTreeBytes, newTreeBytes, nodeIdMap);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (tier === "wasm") {
|
|
136
|
+
const mod = loadNativeModule("@sia/native-wasm");
|
|
137
|
+
if (mod) return mod.astDiff(oldTreeBytes, newTreeBytes, nodeIdMap);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return fallbackAstDiff(oldTreeBytes, newTreeBytes, nodeIdMap);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Run a graph algorithm on a flat edge list.
|
|
145
|
+
*
|
|
146
|
+
* `edges` is a flat Int32Array of `[from, to, from, to, …]` pairs where
|
|
147
|
+
* indices refer to positions in `nodeIds`.
|
|
148
|
+
*/
|
|
149
|
+
export function graphCompute(
|
|
150
|
+
edges: Int32Array,
|
|
151
|
+
nodeIds: string[],
|
|
152
|
+
algorithm: GraphAlgorithm,
|
|
153
|
+
): GraphComputeResult {
|
|
154
|
+
const tier = getNativeModuleStatus();
|
|
155
|
+
|
|
156
|
+
if (tier === "native") {
|
|
157
|
+
const mod = loadNativeModule("@sia/native");
|
|
158
|
+
if (mod) return mod.graphCompute(edges, nodeIds, algorithm);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (tier === "wasm") {
|
|
162
|
+
const mod = loadNativeModule("@sia/native-wasm");
|
|
163
|
+
if (mod) return mod.graphCompute(edges, nodeIds, algorithm);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return fallbackGraphCompute(edges, nodeIds, algorithm);
|
|
167
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Module: fallback-ast-diff — TypeScript fallback for AST diff
|
|
2
|
+
// Correctness over performance. For the native/wasm implementation see @sia/native.
|
|
3
|
+
|
|
4
|
+
import type { AstDiffResult } from "./bridge";
|
|
5
|
+
|
|
6
|
+
interface AstNode {
|
|
7
|
+
name: string;
|
|
8
|
+
kind: string;
|
|
9
|
+
parent: string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function parseTree(bytes: Uint8Array): AstNode[] {
|
|
13
|
+
try {
|
|
14
|
+
const text = new TextDecoder().decode(bytes);
|
|
15
|
+
const parsed = JSON.parse(text) as unknown;
|
|
16
|
+
if (!Array.isArray(parsed)) return [];
|
|
17
|
+
return (parsed as unknown[]).map((item) => {
|
|
18
|
+
const obj = item as Record<string, unknown>;
|
|
19
|
+
return {
|
|
20
|
+
name: String(obj.name ?? ""),
|
|
21
|
+
kind: String(obj.kind ?? ""),
|
|
22
|
+
parent: obj.parent != null ? String(obj.parent) : null,
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
} catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Name-based AST diff: compares two sets of extracted symbols to detect
|
|
32
|
+
* inserts, removes, updates, and moves.
|
|
33
|
+
*
|
|
34
|
+
* The old/new trees are encoded as JSON arrays of {name, kind, parent} objects.
|
|
35
|
+
* nodeIdMap maps positional indices to stable node IDs (used for removes).
|
|
36
|
+
*/
|
|
37
|
+
export function fallbackAstDiff(
|
|
38
|
+
oldTreeBytes: Uint8Array,
|
|
39
|
+
newTreeBytes: Uint8Array,
|
|
40
|
+
nodeIdMap: Map<number, string>,
|
|
41
|
+
): AstDiffResult {
|
|
42
|
+
const oldNodes = parseTree(oldTreeBytes);
|
|
43
|
+
const newNodes = parseTree(newTreeBytes);
|
|
44
|
+
|
|
45
|
+
// Index old nodes by name for O(1) lookup
|
|
46
|
+
const oldByName = new Map<string, { node: AstNode; index: number }[]>();
|
|
47
|
+
for (let i = 0; i < oldNodes.length; i++) {
|
|
48
|
+
const node = oldNodes[i];
|
|
49
|
+
if (!oldByName.has(node.name)) {
|
|
50
|
+
oldByName.set(node.name, []);
|
|
51
|
+
}
|
|
52
|
+
oldByName.get(node.name)?.push({ node, index: i });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Index new nodes by name
|
|
56
|
+
const newByName = new Map<string, { node: AstNode; index: number }[]>();
|
|
57
|
+
for (let j = 0; j < newNodes.length; j++) {
|
|
58
|
+
const node = newNodes[j];
|
|
59
|
+
if (!newByName.has(node.name)) {
|
|
60
|
+
newByName.set(node.name, []);
|
|
61
|
+
}
|
|
62
|
+
newByName.get(node.name)?.push({ node, index: j });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const inserts: AstDiffResult["inserts"] = [];
|
|
66
|
+
const removes: AstDiffResult["removes"] = [];
|
|
67
|
+
const updates: AstDiffResult["updates"] = [];
|
|
68
|
+
const moves: AstDiffResult["moves"] = [];
|
|
69
|
+
|
|
70
|
+
// Track which old/new nodes have been matched
|
|
71
|
+
const matchedOld = new Set<number>();
|
|
72
|
+
const matchedNew = new Set<number>();
|
|
73
|
+
|
|
74
|
+
// Exact matches first: same name, kind, and parent
|
|
75
|
+
for (const [name, oldEntries] of oldByName) {
|
|
76
|
+
const newEntries = newByName.get(name);
|
|
77
|
+
if (!newEntries) continue;
|
|
78
|
+
|
|
79
|
+
for (const oldEntry of oldEntries) {
|
|
80
|
+
for (const newEntry of newEntries) {
|
|
81
|
+
if (matchedOld.has(oldEntry.index) || matchedNew.has(newEntry.index)) continue;
|
|
82
|
+
if (oldEntry.node.kind === newEntry.node.kind) {
|
|
83
|
+
matchedOld.add(oldEntry.index);
|
|
84
|
+
matchedNew.add(newEntry.index);
|
|
85
|
+
// Check for moves (same name+kind but different parent)
|
|
86
|
+
if (oldEntry.node.parent !== newEntry.node.parent) {
|
|
87
|
+
const nodeId = nodeIdMap.get(oldEntry.index) ?? `node-${oldEntry.index}`;
|
|
88
|
+
moves.push({
|
|
89
|
+
node_id: nodeId,
|
|
90
|
+
old_parent: oldEntry.node.parent ?? "",
|
|
91
|
+
new_parent: newEntry.node.parent ?? "",
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Partial matches: same name, different kind (update)
|
|
101
|
+
for (const [name, oldEntries] of oldByName) {
|
|
102
|
+
const newEntries = newByName.get(name);
|
|
103
|
+
if (!newEntries) continue;
|
|
104
|
+
|
|
105
|
+
for (const oldEntry of oldEntries) {
|
|
106
|
+
if (matchedOld.has(oldEntry.index)) continue;
|
|
107
|
+
for (const newEntry of newEntries) {
|
|
108
|
+
if (matchedNew.has(newEntry.index)) continue;
|
|
109
|
+
// Different kind — treat as an update
|
|
110
|
+
const nodeId = nodeIdMap.get(oldEntry.index) ?? `node-${oldEntry.index}`;
|
|
111
|
+
updates.push({
|
|
112
|
+
node_id: nodeId,
|
|
113
|
+
old_name: oldEntry.node.name,
|
|
114
|
+
new_name: newEntry.node.name,
|
|
115
|
+
});
|
|
116
|
+
matchedOld.add(oldEntry.index);
|
|
117
|
+
matchedNew.add(newEntry.index);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Removes: unmatched old nodes
|
|
124
|
+
for (let i = 0; i < oldNodes.length; i++) {
|
|
125
|
+
if (!matchedOld.has(i)) {
|
|
126
|
+
const nodeId = nodeIdMap.get(i) ?? `node-${i}`;
|
|
127
|
+
removes.push({ node_id: nodeId });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Inserts: unmatched new nodes
|
|
132
|
+
for (let j = 0; j < newNodes.length; j++) {
|
|
133
|
+
if (!matchedNew.has(j)) {
|
|
134
|
+
const node = newNodes[j];
|
|
135
|
+
inserts.push({
|
|
136
|
+
node_id: `new-${j}`,
|
|
137
|
+
kind: node.kind,
|
|
138
|
+
name: node.name,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return { inserts, removes, updates, moves };
|
|
144
|
+
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
// Module: fallback-graph — Pure TypeScript graph algorithm implementations
|
|
2
|
+
// Used when the native Rust module is unavailable.
|
|
3
|
+
|
|
4
|
+
import type { GraphAlgorithm, GraphComputeResult } from "./bridge";
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Internal helpers
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
/** Build adjacency list from flat Int32Array of [from, to] pairs. */
|
|
11
|
+
function buildAdjacency(edges: Int32Array, nodeCount: number): Map<number, number[]> {
|
|
12
|
+
const adj = new Map<number, number[]>();
|
|
13
|
+
for (let i = 0; i < nodeCount; i++) adj.set(i, []);
|
|
14
|
+
|
|
15
|
+
for (let i = 0; i < edges.length - 1; i += 2) {
|
|
16
|
+
const from = edges[i];
|
|
17
|
+
const to = edges[i + 1];
|
|
18
|
+
if (from >= 0 && to >= 0) {
|
|
19
|
+
adj.get(from)?.push(to);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return adj;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// PageRank (power iteration)
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
function pagerank(
|
|
30
|
+
edges: Int32Array,
|
|
31
|
+
nodeIds: string[],
|
|
32
|
+
damping: number,
|
|
33
|
+
maxIterations: number,
|
|
34
|
+
_seedNodes?: string[],
|
|
35
|
+
): Float64Array {
|
|
36
|
+
const n = nodeIds.length;
|
|
37
|
+
if (n === 0) return new Float64Array(0);
|
|
38
|
+
|
|
39
|
+
// outDegree[i] = number of out-edges from node i
|
|
40
|
+
const outDegree = new Int32Array(n);
|
|
41
|
+
for (let i = 0; i < edges.length - 1; i += 2) {
|
|
42
|
+
const from = edges[i];
|
|
43
|
+
if (from >= 0 && from < n) {
|
|
44
|
+
outDegree[from]++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Incoming edge map: to -> [from, ...]
|
|
49
|
+
const incoming = new Map<number, number[]>();
|
|
50
|
+
for (let i = 0; i < n; i++) incoming.set(i, []);
|
|
51
|
+
for (let i = 0; i < edges.length - 1; i += 2) {
|
|
52
|
+
const from = edges[i];
|
|
53
|
+
const to = edges[i + 1];
|
|
54
|
+
if (from >= 0 && to >= 0 && from < n && to < n) {
|
|
55
|
+
incoming.get(to)?.push(from);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let scores = new Float64Array(n).fill(1 / n);
|
|
60
|
+
const uniform = 1 / n;
|
|
61
|
+
|
|
62
|
+
for (let iter = 0; iter < maxIterations; iter++) {
|
|
63
|
+
const next = new Float64Array(n);
|
|
64
|
+
|
|
65
|
+
// Dangling node mass (nodes with no out-edges)
|
|
66
|
+
let dangling = 0;
|
|
67
|
+
for (let i = 0; i < n; i++) {
|
|
68
|
+
if (outDegree[i] === 0) dangling += scores[i];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (let v = 0; v < n; v++) {
|
|
72
|
+
let rank = dangling * uniform; // spread dangling mass uniformly
|
|
73
|
+
for (const u of incoming.get(v) ?? []) {
|
|
74
|
+
rank += scores[u] / outDegree[u];
|
|
75
|
+
}
|
|
76
|
+
next[v] = (1 - damping) * uniform + damping * rank;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let delta = 0;
|
|
80
|
+
for (let i = 0; i < n; i++) {
|
|
81
|
+
delta += Math.abs(next[i] - scores[i]);
|
|
82
|
+
}
|
|
83
|
+
scores = next;
|
|
84
|
+
if (delta < 1e-8) break;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return scores;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Dijkstra (shortest path from source)
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
function dijkstra(edges: Int32Array, nodeIds: string[], source: string): Float64Array {
|
|
95
|
+
const n = nodeIds.length;
|
|
96
|
+
if (n === 0) return new Float64Array(0);
|
|
97
|
+
|
|
98
|
+
const sourceIdx = nodeIds.indexOf(source);
|
|
99
|
+
const dist = new Float64Array(n).fill(Number.POSITIVE_INFINITY);
|
|
100
|
+
if (sourceIdx < 0) return dist;
|
|
101
|
+
|
|
102
|
+
dist[sourceIdx] = 0;
|
|
103
|
+
|
|
104
|
+
// Min-heap: [dist, nodeIndex]
|
|
105
|
+
// Simple binary min-heap implementation
|
|
106
|
+
const heap: Array<[number, number]> = [[0, sourceIdx]];
|
|
107
|
+
|
|
108
|
+
function heapPush(item: [number, number]): void {
|
|
109
|
+
heap.push(item);
|
|
110
|
+
let i = heap.length - 1;
|
|
111
|
+
while (i > 0) {
|
|
112
|
+
const parent = Math.floor((i - 1) / 2);
|
|
113
|
+
if (heap[parent][0] <= heap[i][0]) break;
|
|
114
|
+
[heap[parent], heap[i]] = [heap[i], heap[parent]];
|
|
115
|
+
i = parent;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function heapPop(): [number, number] | undefined {
|
|
120
|
+
if (heap.length === 0) return undefined;
|
|
121
|
+
const top = heap[0];
|
|
122
|
+
const last = heap.pop();
|
|
123
|
+
if (last !== undefined && heap.length > 0) {
|
|
124
|
+
heap[0] = last;
|
|
125
|
+
let i = 0;
|
|
126
|
+
while (true) {
|
|
127
|
+
const left = 2 * i + 1;
|
|
128
|
+
const right = 2 * i + 2;
|
|
129
|
+
let smallest = i;
|
|
130
|
+
if (left < heap.length && heap[left][0] < heap[smallest][0]) smallest = left;
|
|
131
|
+
if (right < heap.length && heap[right][0] < heap[smallest][0]) smallest = right;
|
|
132
|
+
if (smallest === i) break;
|
|
133
|
+
[heap[i], heap[smallest]] = [heap[smallest], heap[i]];
|
|
134
|
+
i = smallest;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return top;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Build adjacency with unit weights (edge length = 1)
|
|
141
|
+
const adj = buildAdjacency(edges, n);
|
|
142
|
+
|
|
143
|
+
while (heap.length > 0) {
|
|
144
|
+
const top = heapPop();
|
|
145
|
+
if (!top) break;
|
|
146
|
+
const [d, u] = top;
|
|
147
|
+
if (d > dist[u]) continue; // stale entry
|
|
148
|
+
for (const v of adj.get(u) ?? []) {
|
|
149
|
+
const nd = dist[u] + 1;
|
|
150
|
+
if (nd < dist[v]) {
|
|
151
|
+
dist[v] = nd;
|
|
152
|
+
heapPush([nd, v]);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return dist;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Union-Find (connected components)
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
function connectedComponents(edges: Int32Array, nodeCount: number): Float64Array {
|
|
165
|
+
if (nodeCount === 0) return new Float64Array(0);
|
|
166
|
+
|
|
167
|
+
const parent = new Int32Array(nodeCount);
|
|
168
|
+
const rank = new Int32Array(nodeCount);
|
|
169
|
+
for (let i = 0; i < nodeCount; i++) parent[i] = i;
|
|
170
|
+
|
|
171
|
+
function find(x: number): number {
|
|
172
|
+
let cur = x;
|
|
173
|
+
while (parent[cur] !== cur) {
|
|
174
|
+
parent[cur] = parent[parent[cur]]; // path halving
|
|
175
|
+
cur = parent[cur];
|
|
176
|
+
}
|
|
177
|
+
return cur;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function union(a: number, b: number): void {
|
|
181
|
+
const ra = find(a);
|
|
182
|
+
const rb = find(b);
|
|
183
|
+
if (ra === rb) return;
|
|
184
|
+
if (rank[ra] < rank[rb]) {
|
|
185
|
+
parent[ra] = rb;
|
|
186
|
+
} else if (rank[ra] > rank[rb]) {
|
|
187
|
+
parent[rb] = ra;
|
|
188
|
+
} else {
|
|
189
|
+
parent[rb] = ra;
|
|
190
|
+
rank[ra]++;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
for (let i = 0; i < edges.length - 1; i += 2) {
|
|
195
|
+
const from = edges[i];
|
|
196
|
+
const to = edges[i + 1];
|
|
197
|
+
if (from >= 0 && to >= 0 && from < nodeCount && to < nodeCount) {
|
|
198
|
+
union(from, to);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Assign compact component labels
|
|
203
|
+
const rootToLabel = new Map<number, number>();
|
|
204
|
+
let nextLabel = 0;
|
|
205
|
+
const result = new Float64Array(nodeCount);
|
|
206
|
+
for (let i = 0; i < nodeCount; i++) {
|
|
207
|
+
const root = find(i);
|
|
208
|
+
if (!rootToLabel.has(root)) {
|
|
209
|
+
rootToLabel.set(root, nextLabel++);
|
|
210
|
+
}
|
|
211
|
+
result[i] = rootToLabel.get(root) ?? 0;
|
|
212
|
+
}
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
// Brandes betweenness centrality
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
function betweennessCentrality(edges: Int32Array, nodeIds: string[]): Float64Array {
|
|
221
|
+
const n = nodeIds.length;
|
|
222
|
+
if (n === 0) return new Float64Array(0);
|
|
223
|
+
|
|
224
|
+
const adj = new Map<number, number[]>();
|
|
225
|
+
for (let i = 0; i < n; i++) adj.set(i, []);
|
|
226
|
+
for (let i = 0; i < edges.length - 1; i += 2) {
|
|
227
|
+
const from = edges[i];
|
|
228
|
+
const to = edges[i + 1];
|
|
229
|
+
if (from >= 0 && to >= 0 && from < n && to < n) {
|
|
230
|
+
adj.get(from)?.push(to);
|
|
231
|
+
adj.get(to)?.push(from); // treat as undirected for betweenness
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Deduplicate adjacency
|
|
236
|
+
for (const [k, v] of adj) {
|
|
237
|
+
adj.set(k, [...new Set(v)]);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const betweenness = new Float64Array(n);
|
|
241
|
+
|
|
242
|
+
for (let s = 0; s < n; s++) {
|
|
243
|
+
const stack: number[] = [];
|
|
244
|
+
const pred: number[][] = Array.from({ length: n }, () => []);
|
|
245
|
+
const sigma = new Float64Array(n); // number of shortest paths from s
|
|
246
|
+
const dist = new Float64Array(n).fill(-1);
|
|
247
|
+
|
|
248
|
+
sigma[s] = 1;
|
|
249
|
+
dist[s] = 0;
|
|
250
|
+
const queue: number[] = [s];
|
|
251
|
+
|
|
252
|
+
while (queue.length > 0) {
|
|
253
|
+
const v = queue.shift();
|
|
254
|
+
if (v === undefined) break;
|
|
255
|
+
stack.push(v);
|
|
256
|
+
for (const w of adj.get(v) ?? []) {
|
|
257
|
+
if (dist[w] < 0) {
|
|
258
|
+
queue.push(w);
|
|
259
|
+
dist[w] = dist[v] + 1;
|
|
260
|
+
}
|
|
261
|
+
if (dist[w] === dist[v] + 1) {
|
|
262
|
+
sigma[w] += sigma[v];
|
|
263
|
+
pred[w].push(v);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const delta = new Float64Array(n);
|
|
269
|
+
while (stack.length > 0) {
|
|
270
|
+
const w = stack.pop();
|
|
271
|
+
if (w === undefined) break;
|
|
272
|
+
for (const v of pred[w]) {
|
|
273
|
+
delta[v] += (sigma[v] / sigma[w]) * (1 + delta[w]);
|
|
274
|
+
}
|
|
275
|
+
if (w !== s) betweenness[w] += delta[w];
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Normalize (undirected: divide by 2)
|
|
280
|
+
for (let i = 0; i < n; i++) {
|
|
281
|
+
betweenness[i] /= 2;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return betweenness;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ---------------------------------------------------------------------------
|
|
288
|
+
// Public entry point
|
|
289
|
+
// ---------------------------------------------------------------------------
|
|
290
|
+
|
|
291
|
+
export function fallbackGraphCompute(
|
|
292
|
+
edges: Int32Array,
|
|
293
|
+
nodeIds: string[],
|
|
294
|
+
algorithm: GraphAlgorithm,
|
|
295
|
+
): GraphComputeResult {
|
|
296
|
+
let scores: Float64Array;
|
|
297
|
+
|
|
298
|
+
switch (algorithm.kind) {
|
|
299
|
+
case "pagerank":
|
|
300
|
+
scores = pagerank(
|
|
301
|
+
edges,
|
|
302
|
+
nodeIds,
|
|
303
|
+
algorithm.damping,
|
|
304
|
+
algorithm.iterations,
|
|
305
|
+
algorithm.seed_nodes,
|
|
306
|
+
);
|
|
307
|
+
break;
|
|
308
|
+
case "shortest_path":
|
|
309
|
+
scores = dijkstra(edges, nodeIds, algorithm.source);
|
|
310
|
+
break;
|
|
311
|
+
case "connected_components":
|
|
312
|
+
scores = connectedComponents(edges, nodeIds.length);
|
|
313
|
+
break;
|
|
314
|
+
case "betweenness_centrality":
|
|
315
|
+
scores = betweennessCentrality(edges, nodeIds);
|
|
316
|
+
break;
|
|
317
|
+
default: {
|
|
318
|
+
// Exhaustive check
|
|
319
|
+
const _never: never = algorithm;
|
|
320
|
+
throw new Error(`Unknown algorithm: ${JSON.stringify(_never)}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return { scores, node_ids: nodeIds };
|
|
325
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Module: constraints — Application-layer edge validation using edge_constraints table
|
|
2
|
+
|
|
3
|
+
import type { SiaDb } from "@/graph/db-interface";
|
|
4
|
+
|
|
5
|
+
/** Shape of a row in the edge_constraints table. */
|
|
6
|
+
export interface EdgeConstraint {
|
|
7
|
+
id: number;
|
|
8
|
+
source_type: string;
|
|
9
|
+
edge_type: string;
|
|
10
|
+
target_type: string;
|
|
11
|
+
description: string | null;
|
|
12
|
+
cardinality: string;
|
|
13
|
+
required: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check whether a (source_type, edge_type, target_type) triple exists
|
|
18
|
+
* in the edge_constraints table.
|
|
19
|
+
*
|
|
20
|
+
* Returns true if the triple is declared valid, false otherwise.
|
|
21
|
+
*/
|
|
22
|
+
export async function validateEdge(
|
|
23
|
+
db: SiaDb,
|
|
24
|
+
sourceType: string,
|
|
25
|
+
edgeType: string,
|
|
26
|
+
targetType: string,
|
|
27
|
+
): Promise<boolean> {
|
|
28
|
+
const result = await db.execute(
|
|
29
|
+
`SELECT COUNT(*) AS cnt FROM edge_constraints
|
|
30
|
+
WHERE source_type = ? AND edge_type = ? AND target_type = ?`,
|
|
31
|
+
[sourceType, edgeType, targetType],
|
|
32
|
+
);
|
|
33
|
+
const row = result.rows[0] as { cnt: number } | undefined;
|
|
34
|
+
return (row?.cnt ?? 0) > 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Return all valid edge constraint rows for a given source entity type.
|
|
39
|
+
*/
|
|
40
|
+
export async function getConstraintsForType(
|
|
41
|
+
db: SiaDb,
|
|
42
|
+
sourceType: string,
|
|
43
|
+
): Promise<EdgeConstraint[]> {
|
|
44
|
+
const result = await db.execute("SELECT * FROM edge_constraints WHERE source_type = ?", [
|
|
45
|
+
sourceType,
|
|
46
|
+
]);
|
|
47
|
+
return result.rows as unknown as EdgeConstraint[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Return the entire constraint set from the edge_constraints table.
|
|
52
|
+
*/
|
|
53
|
+
export async function getAllConstraints(db: SiaDb): Promise<EdgeConstraint[]> {
|
|
54
|
+
const result = await db.execute("SELECT * FROM edge_constraints", []);
|
|
55
|
+
return result.rows as unknown as EdgeConstraint[];
|
|
56
|
+
}
|