@dewtech/dare-cli 3.11.0 → 3.12.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/README.md +2 -0
- package/dist/__tests__/ensure-skills.test.js +5 -0
- package/dist/__tests__/ensure-skills.test.js.map +1 -1
- package/dist/__tests__/ide-command-parity.test.js +1 -0
- package/dist/__tests__/ide-command-parity.test.js.map +1 -1
- package/dist/__tests__/project-generator.test.js +17 -0
- package/dist/__tests__/project-generator.test.js.map +1 -1
- package/dist/__tests__/reverse-facts.test.js +1 -0
- package/dist/__tests__/reverse-facts.test.js.map +1 -1
- package/dist/__tests__/terminal-parity-regression.test.d.ts +2 -0
- package/dist/__tests__/terminal-parity-regression.test.d.ts.map +1 -0
- package/dist/__tests__/terminal-parity-regression.test.js +116 -0
- package/dist/__tests__/terminal-parity-regression.test.js.map +1 -0
- package/dist/__tests__/terminal-parity.test.d.ts +2 -0
- package/dist/__tests__/terminal-parity.test.d.ts.map +1 -0
- package/dist/__tests__/terminal-parity.test.js +81 -0
- package/dist/__tests__/terminal-parity.test.js.map +1 -0
- package/dist/agent/__tests__/antigravity-driver.test.d.ts +2 -0
- package/dist/agent/__tests__/antigravity-driver.test.d.ts.map +1 -0
- package/dist/agent/__tests__/antigravity-driver.test.js +52 -0
- package/dist/agent/__tests__/antigravity-driver.test.js.map +1 -0
- package/dist/agent/__tests__/codex-driver.test.d.ts +2 -0
- package/dist/agent/__tests__/codex-driver.test.d.ts.map +1 -0
- package/dist/agent/__tests__/codex-driver.test.js +68 -0
- package/dist/agent/__tests__/codex-driver.test.js.map +1 -0
- package/dist/agent/__tests__/cursor-driver.test.d.ts +2 -0
- package/dist/agent/__tests__/cursor-driver.test.d.ts.map +1 -0
- package/dist/agent/__tests__/cursor-driver.test.js +52 -0
- package/dist/agent/__tests__/cursor-driver.test.js.map +1 -0
- package/dist/agent/driver.d.ts +1 -1
- package/dist/agent/driver.d.ts.map +1 -1
- package/dist/agent/drivers/antigravity.d.ts +8 -0
- package/dist/agent/drivers/antigravity.d.ts.map +1 -0
- package/dist/agent/drivers/antigravity.js +99 -0
- package/dist/agent/drivers/antigravity.js.map +1 -0
- package/dist/agent/drivers/codex.d.ts +12 -0
- package/dist/agent/drivers/codex.d.ts.map +1 -0
- package/dist/agent/drivers/codex.js +137 -0
- package/dist/agent/drivers/codex.js.map +1 -0
- package/dist/agent/drivers/cursor.d.ts +8 -0
- package/dist/agent/drivers/cursor.d.ts.map +1 -0
- package/dist/agent/drivers/cursor.js +99 -0
- package/dist/agent/drivers/cursor.js.map +1 -0
- package/dist/ai/__tests__/ai-core.test.d.ts +2 -0
- package/dist/ai/__tests__/ai-core.test.d.ts.map +1 -0
- package/dist/ai/__tests__/ai-core.test.js +41 -0
- package/dist/ai/__tests__/ai-core.test.js.map +1 -0
- package/dist/ai/__tests__/parity.test.d.ts +2 -0
- package/dist/ai/__tests__/parity.test.d.ts.map +1 -0
- package/dist/ai/__tests__/parity.test.js +36 -0
- package/dist/ai/__tests__/parity.test.js.map +1 -0
- package/dist/ai/__tests__/pipeline.test.d.ts +2 -0
- package/dist/ai/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/ai/__tests__/pipeline.test.js +147 -0
- package/dist/ai/__tests__/pipeline.test.js.map +1 -0
- package/dist/ai/__tests__/refine-bridge.test.d.ts +2 -0
- package/dist/ai/__tests__/refine-bridge.test.d.ts.map +1 -0
- package/dist/ai/__tests__/refine-bridge.test.js +17 -0
- package/dist/ai/__tests__/refine-bridge.test.js.map +1 -0
- package/dist/ai/__tests__/resolve.test.d.ts +2 -0
- package/dist/ai/__tests__/resolve.test.d.ts.map +1 -0
- package/dist/ai/__tests__/resolve.test.js +42 -0
- package/dist/ai/__tests__/resolve.test.js.map +1 -0
- package/dist/ai/capabilities.d.ts +3 -0
- package/dist/ai/capabilities.d.ts.map +1 -0
- package/dist/ai/capabilities.js +11 -0
- package/dist/ai/capabilities.js.map +1 -0
- package/dist/ai/command-options.d.ts +10 -0
- package/dist/ai/command-options.d.ts.map +1 -0
- package/dist/ai/command-options.js +15 -0
- package/dist/ai/command-options.js.map +1 -0
- package/dist/ai/config.d.ts +27 -0
- package/dist/ai/config.d.ts.map +1 -0
- package/dist/ai/config.js +89 -0
- package/dist/ai/config.js.map +1 -0
- package/dist/ai/parity.d.ts +13 -0
- package/dist/ai/parity.d.ts.map +1 -0
- package/dist/ai/parity.js +87 -0
- package/dist/ai/parity.js.map +1 -0
- package/dist/ai/parse-json-output.d.ts +5 -0
- package/dist/ai/parse-json-output.d.ts.map +1 -0
- package/dist/ai/parse-json-output.js +25 -0
- package/dist/ai/parse-json-output.js.map +1 -0
- package/dist/ai/pipeline.d.ts +20 -0
- package/dist/ai/pipeline.d.ts.map +1 -0
- package/dist/ai/pipeline.js +303 -0
- package/dist/ai/pipeline.js.map +1 -0
- package/dist/ai/prompts.d.ts +6 -0
- package/dist/ai/prompts.d.ts.map +1 -0
- package/dist/ai/prompts.js +49 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/ai/providers.d.ts +63 -0
- package/dist/ai/providers.d.ts.map +1 -0
- package/dist/ai/providers.js +297 -0
- package/dist/ai/providers.js.map +1 -0
- package/dist/ai/refine-bridge.d.ts +5 -0
- package/dist/ai/refine-bridge.d.ts.map +1 -0
- package/dist/ai/refine-bridge.js +14 -0
- package/dist/ai/refine-bridge.js.map +1 -0
- package/dist/ai/registry.d.ts +12 -0
- package/dist/ai/registry.d.ts.map +1 -0
- package/dist/ai/registry.js +43 -0
- package/dist/ai/registry.js.map +1 -0
- package/dist/ai/resolve.d.ts +28 -0
- package/dist/ai/resolve.d.ts.map +1 -0
- package/dist/ai/resolve.js +83 -0
- package/dist/ai/resolve.js.map +1 -0
- package/dist/ai/schemas.d.ts +175 -0
- package/dist/ai/schemas.d.ts.map +1 -0
- package/dist/ai/schemas.js +199 -0
- package/dist/ai/schemas.js.map +1 -0
- package/dist/ai/types.d.ts +52 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +8 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/bin/dare.js +2 -0
- package/dist/bin/dare.js.map +1 -1
- package/dist/commands/__tests__/ai-command.test.d.ts +2 -0
- package/dist/commands/__tests__/ai-command.test.d.ts.map +1 -0
- package/dist/commands/__tests__/ai-command.test.js +68 -0
- package/dist/commands/__tests__/ai-command.test.js.map +1 -0
- package/dist/commands/__tests__/execute-agent.test.js +82 -0
- package/dist/commands/__tests__/execute-agent.test.js.map +1 -1
- package/dist/commands/ai.d.ts +3 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +141 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/blueprint.d.ts.map +1 -1
- package/dist/commands/blueprint.js +17 -3
- package/dist/commands/blueprint.js.map +1 -1
- package/dist/commands/design.d.ts.map +1 -1
- package/dist/commands/design.js +21 -2
- package/dist/commands/design.js.map +1 -1
- package/dist/commands/discover.d.ts.map +1 -1
- package/dist/commands/discover.js +9 -1
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/dna.d.ts.map +1 -1
- package/dist/commands/dna.js +23 -3
- package/dist/commands/dna.js.map +1 -1
- package/dist/commands/execute.d.ts +11 -0
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +111 -4
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +14 -2
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/patterns.d.ts.map +1 -1
- package/dist/commands/patterns.js +14 -2
- package/dist/commands/patterns.js.map +1 -1
- package/dist/commands/refine.d.ts.map +1 -1
- package/dist/commands/refine.js +23 -2
- package/dist/commands/refine.js.map +1 -1
- package/dist/commands/reverse.d.ts.map +1 -1
- package/dist/commands/reverse.js +28 -3
- package/dist/commands/reverse.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +25 -3
- package/dist/commands/review.js.map +1 -1
- package/dist/core/types/project.d.ts +1 -1
- package/dist/core/types/project.d.ts.map +1 -1
- package/dist/dag-runner/run_dag.d.ts +1 -1
- package/dist/dag-runner/run_dag.d.ts.map +1 -1
- package/dist/exec/safe-spawn.d.ts.map +1 -1
- package/dist/exec/safe-spawn.js +6 -1
- package/dist/exec/safe-spawn.js.map +1 -1
- package/dist/skills/bundled.d.ts +5 -0
- package/dist/skills/bundled.d.ts.map +1 -0
- package/dist/skills/bundled.js +34 -0
- package/dist/skills/bundled.js.map +1 -0
- package/dist/skills/commands/add.d.ts +1 -3
- package/dist/skills/commands/add.d.ts.map +1 -1
- package/dist/skills/commands/add.js +20 -3
- package/dist/skills/commands/add.js.map +1 -1
- package/dist/skills/tests/bundled.spec.d.ts +2 -0
- package/dist/skills/tests/bundled.spec.d.ts.map +1 -0
- package/dist/skills/tests/bundled.spec.js +24 -0
- package/dist/skills/tests/bundled.spec.js.map +1 -0
- package/dist/types/UpdateManifest.types.d.ts +1 -1
- package/dist/types/UpdateManifest.types.d.ts.map +1 -1
- package/dist/utils/dag-converter.js +1 -1
- package/dist/utils/dag-converter.js.map +1 -1
- package/dist/utils/project-detector.d.ts +1 -0
- package/dist/utils/project-detector.d.ts.map +1 -1
- package/dist/utils/project-detector.js +8 -0
- package/dist/utils/project-detector.js.map +1 -1
- package/dist/utils/project-generator.d.ts +1 -1
- package/dist/utils/project-generator.d.ts.map +1 -1
- package/dist/utils/project-generator.js +23 -2
- package/dist/utils/project-generator.js.map +1 -1
- package/dist/utils/templates.d.ts +2 -0
- package/dist/utils/templates.d.ts.map +1 -1
- package/dist/utils/templates.js +74 -0
- package/dist/utils/templates.js.map +1 -1
- package/dist/verification/__tests__/safe-spawn.test.js +12 -0
- package/dist/verification/__tests__/safe-spawn.test.js.map +1 -1
- package/package.json +2 -1
- package/skills/dare-ax/generator.ts +325 -0
- package/skills/dare-ax/index.ts +19 -0
- package/skills/dare-ax/metrics.ts +352 -0
- package/skills/dare-ax/package-lock.json +1855 -0
- package/skills/dare-ax/package.json +50 -0
- package/skills/dare-ax/secret-detector.ts +123 -0
- package/skills/dare-ax/skill.yml +19 -0
- package/skills/dare-ax/templates/llms.txt.jinja2 +80 -0
- package/skills/dare-ax/tests/generator.spec.ts +193 -0
- package/skills/dare-ax/tests/metrics.spec.ts +394 -0
- package/skills/dare-ax/tests/validator.spec.ts +298 -0
- package/skills/dare-ax/tsconfig.json +18 -0
- package/skills/dare-ax/types.ts +79 -0
- package/skills/dare-ax/validator.ts +238 -0
- package/skills/dare-frontend-design/generator.ts +616 -0
- package/skills/dare-frontend-design/index.ts +25 -0
- package/skills/dare-frontend-design/linter.ts +227 -0
- package/skills/dare-frontend-design/metrics.ts +82 -0
- package/skills/dare-frontend-design/package-lock.json +1855 -0
- package/skills/dare-frontend-design/package.json +43 -0
- package/skills/dare-frontend-design/skill.yml +20 -0
- package/skills/dare-frontend-design/tests/frontend_design.spec.ts +435 -0
- package/skills/dare-frontend-design/tsconfig.json +18 -0
- package/skills/dare-frontend-design/types.ts +62 -0
- package/skills/dare-layered-design/generator.ts +740 -0
- package/skills/dare-layered-design/index.ts +17 -0
- package/skills/dare-layered-design/linter.ts +462 -0
- package/skills/dare-layered-design/metrics.ts +409 -0
- package/skills/dare-layered-design/package-lock.json +1855 -0
- package/skills/dare-layered-design/package.json +50 -0
- package/skills/dare-layered-design/skill.yml +35 -0
- package/skills/dare-layered-design/tests/generator.spec.ts +156 -0
- package/skills/dare-layered-design/tests/linter.spec.ts +255 -0
- package/skills/dare-layered-design/tests/metrics.spec.ts +286 -0
- package/skills/dare-layered-design/tsconfig.json +18 -0
- package/skills/dare-layered-design/types.ts +48 -0
- package/skills/dare-llm-integration/cache/llm_cache.ts +122 -0
- package/skills/dare-llm-integration/index.ts +49 -0
- package/skills/dare-llm-integration/metrics.ts +107 -0
- package/skills/dare-llm-integration/package-lock.json +1855 -0
- package/skills/dare-llm-integration/package.json +49 -0
- package/skills/dare-llm-integration/prompts/prompt_loader.ts +258 -0
- package/skills/dare-llm-integration/providers/anthropic_provider.ts +159 -0
- package/skills/dare-llm-integration/providers/dummy_provider.ts +113 -0
- package/skills/dare-llm-integration/providers/llm_provider.ts +6 -0
- package/skills/dare-llm-integration/providers/openai_provider.ts +215 -0
- package/skills/dare-llm-integration/rate_limit/token_bucket.ts +86 -0
- package/skills/dare-llm-integration/skill.yml +23 -0
- package/skills/dare-llm-integration/tests/fixtures/greet_v1.jinja2 +1 -0
- package/skills/dare-llm-integration/tests/fixtures/summarize_v1.jinja2 +1 -0
- package/skills/dare-llm-integration/tests/fixtures/summarize_v2.jinja2 +3 -0
- package/skills/dare-llm-integration/tests/llm_integration.spec.ts +657 -0
- package/skills/dare-llm-integration/tsconfig.json +23 -0
- package/skills/dare-llm-integration/types.ts +91 -0
- package/skills/dare-llm-integration/validators/output_validator.ts +200 -0
- package/skills/dare-quality-telemetry/collect.ts +134 -0
- package/skills/dare-quality-telemetry/collectors/dare_ax_collector.ts +301 -0
- package/skills/dare-quality-telemetry/collectors/dare_layered_design_collector.ts +406 -0
- package/skills/dare-quality-telemetry/collectors/index.ts +24 -0
- package/skills/dare-quality-telemetry/github_actions_template.ts +25 -0
- package/skills/dare-quality-telemetry/index.ts +18 -0
- package/skills/dare-quality-telemetry/metrics.ts +137 -0
- package/skills/dare-quality-telemetry/package-lock.json +1855 -0
- package/skills/dare-quality-telemetry/package.json +48 -0
- package/skills/dare-quality-telemetry/regression.ts +60 -0
- package/skills/dare-quality-telemetry/reporter.ts +132 -0
- package/skills/dare-quality-telemetry/skill.yml +18 -0
- package/skills/dare-quality-telemetry/tests/quality_telemetry.spec.ts +885 -0
- package/skills/dare-quality-telemetry/tsconfig.json +19 -0
- package/skills/dare-quality-telemetry/types.ts +41 -0
- package/skills/dare-realtime/event_registry.ts +101 -0
- package/skills/dare-realtime/index.ts +30 -0
- package/skills/dare-realtime/metrics.ts +84 -0
- package/skills/dare-realtime/package-lock.json +1855 -0
- package/skills/dare-realtime/package.json +43 -0
- package/skills/dare-realtime/reconnect_strategy.ts +85 -0
- package/skills/dare-realtime/schema_validator.ts +80 -0
- package/skills/dare-realtime/skill.yml +21 -0
- package/skills/dare-realtime/subscription_manager.ts +106 -0
- package/skills/dare-realtime/tests/realtime.spec.ts +482 -0
- package/skills/dare-realtime/tsconfig.json +18 -0
- package/skills/dare-realtime/types.ts +51 -0
- package/templates/ide/antigravity/.agents/skills/dare-ai/SKILL.md +17 -0
- package/templates/ide/antigravity/.agents/skills/dare-blueprint/SKILL.md +2 -0
- package/templates/ide/antigravity/.agents/skills/dare-design/SKILL.md +2 -0
- package/templates/ide/antigravity/.agents/skills/dare-dna/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-migrate/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-patterns/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-refine/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-reverse/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-review/SKILL.md +3 -0
- package/templates/ide/claude/.claude/commands/dare-ai.md +17 -0
- package/templates/ide/claude/.claude/commands/dare-blueprint.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-design.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-dna.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-migrate.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-patterns.md +3 -0
- package/templates/ide/claude/.claude/commands/dare-refine.md +3 -0
- package/templates/ide/claude/.claude/commands/dare-reverse.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-review.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-ai.md +17 -0
- package/templates/ide/cursor/.cursor/commands/dare-blueprint.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-design.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-dna.md +2 -0
- package/templates/ide/cursor/.cursor/commands/dare-migrate.md +2 -0
- package/templates/ide/cursor/.cursor/commands/dare-patterns.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-refine.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-reverse.md +2 -0
- package/templates/ide/cursor/.cursor/commands/dare-review.md +3 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dare-realtime — test suite
|
|
3
|
+
* 35+ tests covering schema validator, event registry, reconnect strategy,
|
|
4
|
+
* subscription manager, and metrics.
|
|
5
|
+
* License: MIT
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
9
|
+
|
|
10
|
+
import { SchemaValidator } from '../schema_validator.js';
|
|
11
|
+
import { EventRegistry } from '../event_registry.js';
|
|
12
|
+
import { ReconnectStrategy } from '../reconnect_strategy.js';
|
|
13
|
+
import { SubscriptionManager } from '../subscription_manager.js';
|
|
14
|
+
import { collectRealtimeMetrics } from '../metrics.js';
|
|
15
|
+
import type { EventSchema } from '../types.js';
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// SchemaValidator
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
describe('SchemaValidator', () => {
|
|
22
|
+
let validator: SchemaValidator;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
validator = new SchemaValidator();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('validates a valid payload', () => {
|
|
29
|
+
const schema: EventSchema = {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
userId: { type: 'string' },
|
|
33
|
+
email: { type: 'string' },
|
|
34
|
+
},
|
|
35
|
+
required: ['userId', 'email'],
|
|
36
|
+
};
|
|
37
|
+
const result = validator.validate({ userId: '123', email: 'a@b.com' }, schema);
|
|
38
|
+
expect(result.ok).toBe(true);
|
|
39
|
+
expect(result.errors).toHaveLength(0);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('fails when required field is missing', () => {
|
|
43
|
+
const schema: EventSchema = {
|
|
44
|
+
type: 'object',
|
|
45
|
+
required: ['userId', 'createdAt'],
|
|
46
|
+
};
|
|
47
|
+
const result = validator.validate({ userId: '1' }, schema);
|
|
48
|
+
expect(result.ok).toBe(false);
|
|
49
|
+
expect(result.errors.some((e) => e.message.includes('createdAt'))).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('fails when type is wrong', () => {
|
|
53
|
+
const schema: EventSchema = { type: 'string' };
|
|
54
|
+
const result = validator.validate(42, schema);
|
|
55
|
+
expect(result.ok).toBe(false);
|
|
56
|
+
expect(result.errors[0].message).toContain('Expected type "string"');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('validates nested object', () => {
|
|
60
|
+
const schema: EventSchema = {
|
|
61
|
+
type: 'object',
|
|
62
|
+
properties: {
|
|
63
|
+
data: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
required: ['id'],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
required: ['data'],
|
|
69
|
+
};
|
|
70
|
+
const result = validator.validate({ data: { id: '1' } }, schema);
|
|
71
|
+
expect(result.ok).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('fails on nested missing field', () => {
|
|
75
|
+
const schema: EventSchema = {
|
|
76
|
+
type: 'object',
|
|
77
|
+
properties: {
|
|
78
|
+
data: { type: 'object', required: ['id'] },
|
|
79
|
+
},
|
|
80
|
+
required: ['data'],
|
|
81
|
+
};
|
|
82
|
+
const result = validator.validate({ data: {} }, schema);
|
|
83
|
+
expect(result.ok).toBe(false);
|
|
84
|
+
expect(result.errors.some((e) => e.field.includes('id'))).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('validates array of strings', () => {
|
|
88
|
+
const schema: EventSchema = {
|
|
89
|
+
type: 'array',
|
|
90
|
+
items: { type: 'string' },
|
|
91
|
+
};
|
|
92
|
+
const result = validator.validate(['a', 'b', 'c'], schema);
|
|
93
|
+
expect(result.ok).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('fails on array with wrong item type', () => {
|
|
97
|
+
const schema: EventSchema = {
|
|
98
|
+
type: 'array',
|
|
99
|
+
items: { type: 'string' },
|
|
100
|
+
};
|
|
101
|
+
const result = validator.validate(['a', 2, 'c'], schema);
|
|
102
|
+
expect(result.ok).toBe(false);
|
|
103
|
+
expect(result.errors.some((e) => e.field.includes('[1]'))).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// EventRegistry
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
describe('EventRegistry', () => {
|
|
112
|
+
let registry: EventRegistry;
|
|
113
|
+
|
|
114
|
+
beforeEach(() => {
|
|
115
|
+
registry = new EventRegistry();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('registers and retrieves schema', () => {
|
|
119
|
+
const schema: EventSchema = { type: 'object', required: ['userId'] };
|
|
120
|
+
registry.register('user.created', schema);
|
|
121
|
+
expect(registry.getSchema('user.created')).toEqual(schema);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('throws when registering duplicate type', () => {
|
|
125
|
+
const schema: EventSchema = { type: 'object' };
|
|
126
|
+
registry.register('user.created', schema);
|
|
127
|
+
expect(() => registry.register('user.created', schema)).toThrow('already registered');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('registerOrUpdate overwrites existing', () => {
|
|
131
|
+
const schema1: EventSchema = { type: 'object', required: ['a'] };
|
|
132
|
+
const schema2: EventSchema = { type: 'object', required: ['b'] };
|
|
133
|
+
registry.register('evt', schema1);
|
|
134
|
+
registry.registerOrUpdate('evt', schema2);
|
|
135
|
+
expect(registry.getSchema('evt')).toEqual(schema2);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('returns null for unknown schema', () => {
|
|
139
|
+
expect(registry.getSchema('unknown.type')).toBeNull();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('validate returns error for unknown type', () => {
|
|
143
|
+
const result = registry.validate('unknown.type', {});
|
|
144
|
+
expect(result.ok).toBe(false);
|
|
145
|
+
expect(result.errors[0].message).toContain('Unknown event type');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('validate passes for valid payload', () => {
|
|
149
|
+
registry.register('user.deleted', {
|
|
150
|
+
type: 'object',
|
|
151
|
+
required: ['userId'],
|
|
152
|
+
properties: { userId: { type: 'string' } },
|
|
153
|
+
});
|
|
154
|
+
const result = registry.validate('user.deleted', { userId: '123' });
|
|
155
|
+
expect(result.ok).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('validate fails for invalid payload', () => {
|
|
159
|
+
registry.register('order.placed', {
|
|
160
|
+
type: 'object',
|
|
161
|
+
required: ['orderId', 'amount'],
|
|
162
|
+
});
|
|
163
|
+
const result = registry.validate('order.placed', { orderId: '1' }); // missing amount
|
|
164
|
+
expect(result.ok).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('listTypes returns all registered types', () => {
|
|
168
|
+
registry.register('a', { type: 'object' });
|
|
169
|
+
registry.register('b', { type: 'string' });
|
|
170
|
+
const types = registry.listTypes();
|
|
171
|
+
expect(types).toContain('a');
|
|
172
|
+
expect(types).toContain('b');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('has() returns true for registered type', () => {
|
|
176
|
+
registry.register('x', { type: 'object' });
|
|
177
|
+
expect(registry.has('x')).toBe(true);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('has() returns false for unregistered type', () => {
|
|
181
|
+
expect(registry.has('y')).toBe(false);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('unregister removes event type', () => {
|
|
185
|
+
registry.register('t', { type: 'object' });
|
|
186
|
+
registry.unregister('t');
|
|
187
|
+
expect(registry.has('t')).toBe(false);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('clear removes all types', () => {
|
|
191
|
+
registry.register('a', { type: 'object' });
|
|
192
|
+
registry.register('b', { type: 'object' });
|
|
193
|
+
registry.clear();
|
|
194
|
+
expect(registry.size).toBe(0);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
// ReconnectStrategy
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
|
|
202
|
+
describe('ReconnectStrategy', () => {
|
|
203
|
+
it('first delay is initialDelay (1s default)', () => {
|
|
204
|
+
const strategy = new ReconnectStrategy();
|
|
205
|
+
expect(strategy.getDelay(0)).toBe(1_000);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('doubles delay each attempt', () => {
|
|
209
|
+
const strategy = new ReconnectStrategy({ initialDelayMs: 1_000 });
|
|
210
|
+
expect(strategy.getDelay(0)).toBe(1_000);
|
|
211
|
+
expect(strategy.getDelay(1)).toBe(2_000);
|
|
212
|
+
expect(strategy.getDelay(2)).toBe(4_000);
|
|
213
|
+
expect(strategy.getDelay(3)).toBe(8_000);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('caps at maxDelay (30s default)', () => {
|
|
217
|
+
const strategy = new ReconnectStrategy();
|
|
218
|
+
expect(strategy.getDelay(10)).toBe(30_000); // 1024s > 30s
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('respects custom initialDelayMs and maxDelayMs', () => {
|
|
222
|
+
const strategy = new ReconnectStrategy({ initialDelayMs: 500, maxDelayMs: 5_000 });
|
|
223
|
+
expect(strategy.getDelay(0)).toBe(500);
|
|
224
|
+
expect(strategy.getDelay(1)).toBe(1_000);
|
|
225
|
+
expect(strategy.getDelay(5)).toBe(5_000); // 16_000 > 5_000
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('reset() sets attempt back to 0', () => {
|
|
229
|
+
const strategy = new ReconnectStrategy();
|
|
230
|
+
strategy.nextDelay();
|
|
231
|
+
strategy.nextDelay();
|
|
232
|
+
strategy.reset();
|
|
233
|
+
expect(strategy.attempt).toBe(0);
|
|
234
|
+
expect(strategy.nextDelay()).toBe(1_000);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('nextDelay() auto-increments attempt', () => {
|
|
238
|
+
const strategy = new ReconnectStrategy();
|
|
239
|
+
const d0 = strategy.nextDelay();
|
|
240
|
+
const d1 = strategy.nextDelay();
|
|
241
|
+
const d2 = strategy.nextDelay();
|
|
242
|
+
expect(d0).toBe(1_000);
|
|
243
|
+
expect(d1).toBe(2_000);
|
|
244
|
+
expect(d2).toBe(4_000);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('getDelay(0) with negative returns same as 0', () => {
|
|
248
|
+
const strategy = new ReconnectStrategy();
|
|
249
|
+
expect(strategy.getDelay(-1)).toBe(strategy.getDelay(0));
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('exposes initialDelay and maxDelay', () => {
|
|
253
|
+
const strategy = new ReconnectStrategy({ initialDelayMs: 200, maxDelayMs: 10_000 });
|
|
254
|
+
expect(strategy.initialDelay).toBe(200);
|
|
255
|
+
expect(strategy.maxDelay).toBe(10_000);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// ---------------------------------------------------------------------------
|
|
260
|
+
// SubscriptionManager
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
|
|
263
|
+
describe('SubscriptionManager', () => {
|
|
264
|
+
let manager: SubscriptionManager;
|
|
265
|
+
|
|
266
|
+
beforeEach(() => {
|
|
267
|
+
manager = new SubscriptionManager();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('subscribe and publish delivers payload', () => {
|
|
271
|
+
const handler = vi.fn();
|
|
272
|
+
manager.subscribe('channel:1', handler);
|
|
273
|
+
manager.publish('channel:1', { type: 'update', data: 'x' });
|
|
274
|
+
expect(handler).toHaveBeenCalledOnce();
|
|
275
|
+
expect(handler).toHaveBeenCalledWith({ type: 'update', data: 'x' });
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('unsubscribe function removes specific handler', () => {
|
|
279
|
+
const h1 = vi.fn();
|
|
280
|
+
const h2 = vi.fn();
|
|
281
|
+
const unsub1 = manager.subscribe('ch', h1);
|
|
282
|
+
manager.subscribe('ch', h2);
|
|
283
|
+
|
|
284
|
+
unsub1();
|
|
285
|
+
manager.publish('ch', 'payload');
|
|
286
|
+
expect(h1).not.toHaveBeenCalled();
|
|
287
|
+
expect(h2).toHaveBeenCalledOnce();
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('unsubscribeAll removes all handlers for channel', () => {
|
|
291
|
+
const h1 = vi.fn();
|
|
292
|
+
const h2 = vi.fn();
|
|
293
|
+
manager.subscribe('ch', h1);
|
|
294
|
+
manager.subscribe('ch', h2);
|
|
295
|
+
manager.unsubscribeAll('ch');
|
|
296
|
+
manager.publish('ch', 'x');
|
|
297
|
+
expect(h1).not.toHaveBeenCalled();
|
|
298
|
+
expect(h2).not.toHaveBeenCalled();
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('getActiveChannels returns only channels with active subscriptions', () => {
|
|
302
|
+
manager.subscribe('a', vi.fn());
|
|
303
|
+
manager.subscribe('b', vi.fn());
|
|
304
|
+
expect(manager.getActiveChannels()).toContain('a');
|
|
305
|
+
expect(manager.getActiveChannels()).toContain('b');
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('channel removed from active after unsubscribeAll', () => {
|
|
309
|
+
manager.subscribe('ch', vi.fn());
|
|
310
|
+
manager.unsubscribeAll('ch');
|
|
311
|
+
expect(manager.getActiveChannels()).not.toContain('ch');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('channel removed from active after last unsubscribe', () => {
|
|
315
|
+
const unsub = manager.subscribe('ch', vi.fn());
|
|
316
|
+
unsub();
|
|
317
|
+
expect(manager.getActiveChannels()).not.toContain('ch');
|
|
318
|
+
expect(manager.isEmpty).toBe(true);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('zero ghost listeners after unsubscribeAll', () => {
|
|
322
|
+
manager.subscribe('ch', vi.fn());
|
|
323
|
+
manager.subscribe('ch', vi.fn());
|
|
324
|
+
manager.unsubscribeAll('ch');
|
|
325
|
+
expect(manager.countSubscriptions('ch')).toBe(0);
|
|
326
|
+
expect(manager.isEmpty).toBe(true);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('multiple channels are independent', () => {
|
|
330
|
+
const h1 = vi.fn();
|
|
331
|
+
const h2 = vi.fn();
|
|
332
|
+
manager.subscribe('a', h1);
|
|
333
|
+
manager.subscribe('b', h2);
|
|
334
|
+
manager.publish('a', 'msg');
|
|
335
|
+
expect(h1).toHaveBeenCalledOnce();
|
|
336
|
+
expect(h2).not.toHaveBeenCalled();
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('publish to unknown channel does not throw', () => {
|
|
340
|
+
expect(() => manager.publish('no-subs', 'x')).not.toThrow();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('totalSubscriptions counts across channels', () => {
|
|
344
|
+
manager.subscribe('a', vi.fn());
|
|
345
|
+
manager.subscribe('a', vi.fn());
|
|
346
|
+
manager.subscribe('b', vi.fn());
|
|
347
|
+
expect(manager.totalSubscriptions).toBe(3);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('clear() removes everything', () => {
|
|
351
|
+
manager.subscribe('a', vi.fn());
|
|
352
|
+
manager.subscribe('b', vi.fn());
|
|
353
|
+
manager.clear();
|
|
354
|
+
expect(manager.isEmpty).toBe(true);
|
|
355
|
+
expect(manager.totalSubscriptions).toBe(0);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('handler called with correct type when typed', () => {
|
|
359
|
+
const results: string[] = [];
|
|
360
|
+
manager.subscribe<string>('typed', (payload) => results.push(payload));
|
|
361
|
+
manager.publish<string>('typed', 'hello');
|
|
362
|
+
expect(results).toEqual(['hello']);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
// Metrics
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
|
|
370
|
+
describe('collectRealtimeMetrics', () => {
|
|
371
|
+
it('M-01 passes when all events have schemas', () => {
|
|
372
|
+
const results = collectRealtimeMetrics({
|
|
373
|
+
eventTypesWithSchema: 5,
|
|
374
|
+
totalEventTypes: 5,
|
|
375
|
+
authorizedSubscriptions: 3,
|
|
376
|
+
totalSubscriptions: 3,
|
|
377
|
+
ghostListenerCount: 0,
|
|
378
|
+
reconnectConfigured: true,
|
|
379
|
+
});
|
|
380
|
+
expect(results.find((r) => r.id === 'M-01')!.pass).toBe(true);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('M-01 fails when event types missing schema', () => {
|
|
384
|
+
const results = collectRealtimeMetrics({
|
|
385
|
+
eventTypesWithSchema: 3,
|
|
386
|
+
totalEventTypes: 5,
|
|
387
|
+
authorizedSubscriptions: 0,
|
|
388
|
+
totalSubscriptions: 0,
|
|
389
|
+
ghostListenerCount: 0,
|
|
390
|
+
reconnectConfigured: true,
|
|
391
|
+
});
|
|
392
|
+
const m01 = results.find((r) => r.id === 'M-01')!;
|
|
393
|
+
expect(m01.pass).toBe(false);
|
|
394
|
+
expect(m01.detail).toContain('2 event type(s) missing schema');
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('M-02 passes when all subscriptions authorized', () => {
|
|
398
|
+
const results = collectRealtimeMetrics({
|
|
399
|
+
eventTypesWithSchema: 2,
|
|
400
|
+
totalEventTypes: 2,
|
|
401
|
+
authorizedSubscriptions: 4,
|
|
402
|
+
totalSubscriptions: 4,
|
|
403
|
+
ghostListenerCount: 0,
|
|
404
|
+
reconnectConfigured: true,
|
|
405
|
+
});
|
|
406
|
+
expect(results.find((r) => r.id === 'M-02')!.pass).toBe(true);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it('M-02 fails when unauthorized subscriptions exist', () => {
|
|
410
|
+
const results = collectRealtimeMetrics({
|
|
411
|
+
eventTypesWithSchema: 2,
|
|
412
|
+
totalEventTypes: 2,
|
|
413
|
+
authorizedSubscriptions: 2,
|
|
414
|
+
totalSubscriptions: 4,
|
|
415
|
+
ghostListenerCount: 0,
|
|
416
|
+
reconnectConfigured: true,
|
|
417
|
+
});
|
|
418
|
+
expect(results.find((r) => r.id === 'M-02')!.pass).toBe(false);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('M-03 passes when no ghost listeners', () => {
|
|
422
|
+
const results = collectRealtimeMetrics({
|
|
423
|
+
eventTypesWithSchema: 2,
|
|
424
|
+
totalEventTypes: 2,
|
|
425
|
+
authorizedSubscriptions: 1,
|
|
426
|
+
totalSubscriptions: 1,
|
|
427
|
+
ghostListenerCount: 0,
|
|
428
|
+
reconnectConfigured: true,
|
|
429
|
+
});
|
|
430
|
+
expect(results.find((r) => r.id === 'M-03')!.pass).toBe(true);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it('M-03 fails when ghost listeners present', () => {
|
|
434
|
+
const results = collectRealtimeMetrics({
|
|
435
|
+
eventTypesWithSchema: 2,
|
|
436
|
+
totalEventTypes: 2,
|
|
437
|
+
authorizedSubscriptions: 1,
|
|
438
|
+
totalSubscriptions: 1,
|
|
439
|
+
ghostListenerCount: 3,
|
|
440
|
+
reconnectConfigured: true,
|
|
441
|
+
});
|
|
442
|
+
const m03 = results.find((r) => r.id === 'M-03')!;
|
|
443
|
+
expect(m03.pass).toBe(false);
|
|
444
|
+
expect(m03.detail).toContain('3 ghost listener(s)');
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
it('M-04 passes when reconnect configured', () => {
|
|
448
|
+
const results = collectRealtimeMetrics({
|
|
449
|
+
eventTypesWithSchema: 0,
|
|
450
|
+
totalEventTypes: 0,
|
|
451
|
+
authorizedSubscriptions: 0,
|
|
452
|
+
totalSubscriptions: 0,
|
|
453
|
+
ghostListenerCount: 0,
|
|
454
|
+
reconnectConfigured: true,
|
|
455
|
+
});
|
|
456
|
+
expect(results.find((r) => r.id === 'M-04')!.pass).toBe(true);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('M-04 fails when reconnect not configured', () => {
|
|
460
|
+
const results = collectRealtimeMetrics({
|
|
461
|
+
eventTypesWithSchema: 0,
|
|
462
|
+
totalEventTypes: 0,
|
|
463
|
+
authorizedSubscriptions: 0,
|
|
464
|
+
totalSubscriptions: 0,
|
|
465
|
+
ghostListenerCount: 0,
|
|
466
|
+
reconnectConfigured: false,
|
|
467
|
+
});
|
|
468
|
+
expect(results.find((r) => r.id === 'M-04')!.pass).toBe(false);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it('returns exactly 4 metrics', () => {
|
|
472
|
+
const results = collectRealtimeMetrics({
|
|
473
|
+
eventTypesWithSchema: 0,
|
|
474
|
+
totalEventTypes: 0,
|
|
475
|
+
authorizedSubscriptions: 0,
|
|
476
|
+
totalSubscriptions: 0,
|
|
477
|
+
ghostListenerCount: 0,
|
|
478
|
+
reconnectConfigured: true,
|
|
479
|
+
});
|
|
480
|
+
expect(results).toHaveLength(4);
|
|
481
|
+
});
|
|
482
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"rootDir": ".",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"sourceMap": true
|
|
9
|
+
},
|
|
10
|
+
"include": [
|
|
11
|
+
"*.ts",
|
|
12
|
+
"tests/**/*.ts"
|
|
13
|
+
],
|
|
14
|
+
"exclude": [
|
|
15
|
+
"node_modules",
|
|
16
|
+
"dist"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dare-realtime — shared types
|
|
3
|
+
* License: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface ValidationResult {
|
|
7
|
+
ok: boolean;
|
|
8
|
+
errors: ValidationError[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ValidationError {
|
|
12
|
+
field: string;
|
|
13
|
+
message: string;
|
|
14
|
+
value?: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface EventSchema {
|
|
18
|
+
type: 'object' | 'array' | 'string' | 'number' | 'boolean' | 'null';
|
|
19
|
+
properties?: Record<string, EventSchema>;
|
|
20
|
+
required?: string[];
|
|
21
|
+
items?: EventSchema;
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface RegisteredEvent {
|
|
26
|
+
type: string;
|
|
27
|
+
schema: EventSchema;
|
|
28
|
+
registeredAt: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RealtimeMetricsInput {
|
|
32
|
+
/** M-01: number of event types with schema defined */
|
|
33
|
+
eventTypesWithSchema: number;
|
|
34
|
+
/** M-01: total event types in use */
|
|
35
|
+
totalEventTypes: number;
|
|
36
|
+
/** M-02: number of subscriptions that have auth checks */
|
|
37
|
+
authorizedSubscriptions: number;
|
|
38
|
+
/** M-02: total subscriptions */
|
|
39
|
+
totalSubscriptions: number;
|
|
40
|
+
/** M-03: known ghost listener count (should be 0) */
|
|
41
|
+
ghostListenerCount: number;
|
|
42
|
+
/** M-04: reconnect strategy is configured */
|
|
43
|
+
reconnectConfigured: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface MetricResult {
|
|
47
|
+
id: string;
|
|
48
|
+
pass: boolean;
|
|
49
|
+
description: string;
|
|
50
|
+
detail?: string;
|
|
51
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dare-ai
|
|
3
|
+
description: Terminal-first AI providers for DARE enrichment (dare ai doctor/providers/run). Use when running semantic enrichment from the CLI instead of IDE chat.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# dare-ai
|
|
7
|
+
|
|
8
|
+
Prefer terminal workflows:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
dare ai doctor
|
|
12
|
+
dare ai providers
|
|
13
|
+
dare reverse --ai --provider codex
|
|
14
|
+
dare dna --ai
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Related: `dare-execute --agent --driver codex`
|
|
@@ -14,6 +14,8 @@ Você é um arquiteto de software especializado em design de APIs e sistemas. Se
|
|
|
14
14
|
- Necessário documentar endpoints e modelos
|
|
15
15
|
- Segunda fase do Método DARE
|
|
16
16
|
|
|
17
|
+
> **Equivalente no terminal:** `dare blueprint --ai`
|
|
18
|
+
|
|
17
19
|
## Como usar
|
|
18
20
|
|
|
19
21
|
### Passo 1: Ler o Design Aprovado
|
|
@@ -14,6 +14,8 @@ Você é um especialista em planejamento e análise de requisitos. Seu objetivo
|
|
|
14
14
|
- Necessário documentar escopo e restrições
|
|
15
15
|
- Primeira fase do Método DARE
|
|
16
16
|
|
|
17
|
+
> **Equivalente no terminal:** `dare design "<description>" --ai`
|
|
18
|
+
|
|
17
19
|
## Como usar
|
|
18
20
|
|
|
19
21
|
### Passo 1: Entender a Ideia
|
|
@@ -5,6 +5,9 @@ description: Camada semântica da extração de DNA do projeto. Roda depois do c
|
|
|
5
5
|
|
|
6
6
|
# DARE DNA Skill — Convenções do Projeto (brownfield)
|
|
7
7
|
|
|
8
|
+
> **Equivalente no terminal:** `dare dna --ai`
|
|
9
|
+
|
|
10
|
+
|
|
8
11
|
Você é o agente que transforma os **fatos de convenção** de um codebase legado em **regras acionáveis**.
|
|
9
12
|
Esta skill é a camada **semântica**: roda **depois** do comando `dare dna`, que já varreu o código e
|
|
10
13
|
extraiu os fatos. Sua função é redigir o "como esse codebase faz as coisas" que uma nova feature deve
|
|
@@ -5,6 +5,9 @@ description: Camada semântica da migração (Fase 2 brownfield). Roda depois do
|
|
|
5
5
|
|
|
6
6
|
# DARE Migrate Skill — Migração com paridade (brownfield Fase 2)
|
|
7
7
|
|
|
8
|
+
> **Equivalente no terminal:** `dare migrate --ai`
|
|
9
|
+
|
|
10
|
+
|
|
8
11
|
Você é o agente que transforma o entendimento do legado em um **plano de migração com paridade**.
|
|
9
12
|
Esta skill é a camada **semântica**: roda **depois** do comando `dare migrate`, que já leu os
|
|
10
13
|
artefatos do `reverse`/`dna` e gerou os esqueletos. Sua função é **escrever a estratégia de migração
|
|
@@ -5,6 +5,9 @@ description: Analisa complexidade de uma task DARE e, quando alta, quebra em sub
|
|
|
5
5
|
|
|
6
6
|
# DARE Refine Skill
|
|
7
7
|
|
|
8
|
+
> **Equivalente no terminal:** `dare refine <task-id> --split --ai`
|
|
9
|
+
|
|
10
|
+
|
|
8
11
|
Você é o refinador de tasks do método DARE. Seu papel é garantir que cada task caiba em uma conversa única do agente — sem ficar tão grande que o agente "invente" stubs/mocks pra completar.
|
|
9
12
|
|
|
10
13
|
## Quando usar
|
|
@@ -5,6 +5,9 @@ description: Camada semântica da engenharia reversa (Fase 0 / brownfield). Roda
|
|
|
5
5
|
|
|
6
6
|
# DARE Reverse Skill — Engenharia Reversa (Fase 0)
|
|
7
7
|
|
|
8
|
+
> **Equivalente no terminal:** `dare reverse --ai`
|
|
9
|
+
|
|
10
|
+
|
|
8
11
|
Você é o agente da **Fase 0 (brownfield)** do método DARE no Antigravity. Esta skill é a
|
|
9
12
|
**camada semântica** da engenharia reversa: roda **depois** do comando `dare reverse`, que já
|
|
10
13
|
varreu o código e gerou os esqueletos determinísticos. Sua função é **preencher as inferências**
|
|
@@ -5,6 +5,9 @@ description: Audita uma task DARE implementada — cruza a spec com o código re
|
|
|
5
5
|
|
|
6
6
|
# DARE Review Skill
|
|
7
7
|
|
|
8
|
+
> **Equivalente no terminal:** `dare review <task-id> --ai`
|
|
9
|
+
|
|
10
|
+
|
|
8
11
|
Você é o auditor de qualidade do método DARE. Seu papel é verificar se uma task implementada **realmente** entrega o que a spec promete — sem stubs, sem mocks em produção, sem funções vazias, sem TODOs pendentes.
|
|
9
12
|
|
|
10
13
|
## Quando usar esta skill
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# /dare-ai
|
|
2
|
+
|
|
3
|
+
Terminal-first AI providers for DARE command enrichment (`dare ai doctor`, `dare ai providers`, `dare ai run`).
|
|
4
|
+
|
|
5
|
+
> Prefer running these in the terminal. Brownfield commands also accept `--ai` and `--provider`.
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
dare ai doctor
|
|
11
|
+
dare ai providers
|
|
12
|
+
dare ai run --command reverse --facts DARE/REVERSE/reverse-facts.json --provider codex
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Related
|
|
16
|
+
|
|
17
|
+
`/dare-reverse` · `/dare-dna` · `/dare-execute`
|