@lssm/bundle.contractspec-workspace 0.0.0-canary-20251213172311
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 +76 -0
- package/dist/adapters/ai.js +1 -0
- package/dist/adapters/factory.js +1 -0
- package/dist/adapters/fs.js +1 -0
- package/dist/adapters/git.js +1 -0
- package/dist/adapters/index.js +1 -0
- package/dist/adapters/logger.js +1 -0
- package/dist/adapters/watcher.js +1 -0
- package/dist/index.js +1 -0
- package/dist/services/build.js +1 -0
- package/dist/services/clean.js +1 -0
- package/dist/services/config.js +1 -0
- package/dist/services/deps.js +1 -0
- package/dist/services/diff.js +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/list.js +1 -0
- package/dist/services/regenerator.js +1 -0
- package/dist/services/sync.js +1 -0
- package/dist/services/test.js +1 -0
- package/dist/services/validate-implementation.js +1 -0
- package/dist/services/validate.js +1 -0
- package/dist/services/watch.js +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# @lssm/bundle.contractspec-workspace
|
|
2
|
+
|
|
3
|
+
Reusable use-cases and services for ContractSpec workspace operations.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This bundle provides platform-agnostic services that can be used by:
|
|
8
|
+
|
|
9
|
+
- CLI tools (`@lssm/app.cli-contracts`)
|
|
10
|
+
- Web applications
|
|
11
|
+
- VS Code extensions
|
|
12
|
+
- API servers
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
bundle.contractspec-workspace
|
|
18
|
+
├── services/ # Use-case implementations
|
|
19
|
+
│ ├── build.ts # Build deterministic artifacts from specs (templates-first)
|
|
20
|
+
│ ├── validate.ts # Validate spec structure (and, later, implementation checks)
|
|
21
|
+
│ ├── diff.ts # Compare specs (semantic diff)
|
|
22
|
+
│ ├── deps.ts # Analyze dependencies
|
|
23
|
+
│ ├── list.ts # Discover specs by glob
|
|
24
|
+
│ └── config.ts # Load + merge workspace config (.contractsrc.json)
|
|
25
|
+
│ ├── sync.ts # Sync all specs (validate/build across a workspace)
|
|
26
|
+
│ ├── watch.ts # Watch specs and trigger validate/build
|
|
27
|
+
│ ├── clean.ts # Safe-by-default cleanup of generated artifacts
|
|
28
|
+
│ ├── test.ts # Run TestSpec scenarios (pure runner wrapper)
|
|
29
|
+
│ └── regenerator.ts # Regenerator service wrapper (no module loading)
|
|
30
|
+
├── adapters/ # Runtime adapters (Node defaults)
|
|
31
|
+
│ ├── fs.ts # Filesystem operations
|
|
32
|
+
│ ├── git.ts # Git operations
|
|
33
|
+
│ ├── watcher.ts # File watching
|
|
34
|
+
│ ├── ai.ts # AI providers
|
|
35
|
+
│ └── logger.ts # Logging/progress
|
|
36
|
+
└── ports/ # Adapter interfaces
|
|
37
|
+
└── index.ts # Port type definitions
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Design Principles
|
|
41
|
+
|
|
42
|
+
- **Adapter pattern**: All I/O goes through explicit ports/adapters
|
|
43
|
+
- **Testable**: Services can be tested with mock adapters
|
|
44
|
+
- **Reusable**: Same services work across CLI, web, and extensions
|
|
45
|
+
- **No CLI dependencies**: No `chalk`, `ora`, `commander`, or `inquirer`
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import {
|
|
51
|
+
createNodeAdapters,
|
|
52
|
+
loadWorkspaceConfig,
|
|
53
|
+
buildSpec,
|
|
54
|
+
} from '@lssm/bundle.contractspec-workspace';
|
|
55
|
+
|
|
56
|
+
// Create adapters for Node.js runtime
|
|
57
|
+
const adapters = createNodeAdapters();
|
|
58
|
+
|
|
59
|
+
// Load workspace config (or use defaults)
|
|
60
|
+
const config = await loadWorkspaceConfig(adapters.fs);
|
|
61
|
+
|
|
62
|
+
// Build deterministic artifacts from a spec (templates-first)
|
|
63
|
+
const result = await buildSpec(
|
|
64
|
+
'./my-spec.contracts.ts',
|
|
65
|
+
{ fs: adapters.fs, logger: adapters.logger },
|
|
66
|
+
config
|
|
67
|
+
);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Notes
|
|
71
|
+
|
|
72
|
+
- `sync` / `watch` accept optional overrides so CLI (or an extension) can inject
|
|
73
|
+
richer build/validate behavior while reusing the deterministic orchestration.
|
|
74
|
+
- `test` and `regenerator` deliberately avoid TypeScript module loading; callers
|
|
75
|
+
pass already-loaded specs/contexts/rules/sinks.
|
|
76
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{anthropic as e}from"@ai-sdk/anthropic";import{openai as t}from"@ai-sdk/openai";import{ollama as n}from"ollama-ai-provider";import{generateObject as r,generateText as i,streamText as a}from"ai";function o(e){return{async validateProvider(e){try{let{aiProvider:t}=e;return t===`ollama`?{success:!0}:t===`claude`&&!process.env.ANTHROPIC_API_KEY?{success:!1,error:`ANTHROPIC_API_KEY environment variable not set`}:t===`openai`&&!process.env.OPENAI_API_KEY?{success:!1,error:`OPENAI_API_KEY environment variable not set`}:{success:!0}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}},async generateText(t){return{text:(await i({model:s(e),prompt:t.prompt,system:t.systemPrompt})).text}},async generateStructured(t){return{object:(await r({model:s(e),schema:t.schema,prompt:t.prompt,system:t.systemPrompt})).object}},async streamText(t,n){let r=await a({model:s(e),prompt:t.prompt,system:t.systemPrompt}),i=``;for await(let e of r.textStream)i+=e,n(e);return i}}}function s(r){let{aiProvider:i,aiModel:a,customEndpoint:o}=r;switch(i){case`claude`:return e(a??`claude-3-5-sonnet-20241022`);case`openai`:return t(a??`gpt-4o`);case`ollama`:return n(a??`codellama`);case`custom`:if(!o)throw Error(`Custom endpoint required. Set customEndpoint in config or CONTRACTSPEC_LLM_ENDPOINT env var`);return t(a??`default`);default:throw Error(`Unknown AI provider: ${i}`)}}export{o as createNodeAiAdapter};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createNodeFsAdapter as e}from"./fs.js";import{createNodeGitAdapter as t}from"./git.js";import{createNodeWatcherAdapter as n}from"./watcher.js";import{createNodeAiAdapter as r}from"./ai.js";import{createConsoleLoggerAdapter as i,createNoopLoggerAdapter as a}from"./logger.js";function o(o={}){let{cwd:s,config:c,silent:l}=o,u=c??{aiProvider:`claude`,agentMode:`simple`,outputDir:`./src`,conventions:{operations:`interactions/commands|queries`,events:`events`,presentations:`presentations`,forms:`forms`},defaultOwners:[],defaultTags:[]};return{fs:e(s),git:t(s),watcher:n(s),ai:r(u),logger:l?a():i()}}export{o as createNodeAdapters};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{access as e,mkdir as t,readFile as n,rm as r,stat as i,writeFile as a}from"node:fs/promises";import{basename as o,dirname as s,isAbsolute as c,join as l,relative as u,resolve as d}from"node:path";import{glob as f}from"glob";const p=[`**/*.contracts.ts`,`**/*.event.ts`,`**/*.presentation.ts`,`**/*.workflow.ts`,`**/*.data-view.ts`,`**/*.migration.ts`,`**/*.telemetry.ts`,`**/*.experiment.ts`,`**/*.app-config.ts`,`**/*.integration.ts`,`**/*.knowledge.ts`],m=[`node_modules/**`,`dist/**`,`.turbo/**`];function h(h){let g=h??process.cwd();return{async exists(t){try{return await e(_(t)),!0}catch{return!1}},async readFile(e){return n(_(e),`utf-8`)},async writeFile(e,n){let r=_(e);await t(s(r),{recursive:!0}),await a(r,n,`utf-8`)},async remove(e){await r(_(e),{recursive:!0,force:!0})},async stat(e){let t=await i(_(e));return{size:t.size,isFile:t.isFile(),isDirectory:t.isDirectory(),mtime:t.mtime}},async mkdir(e){await t(_(e),{recursive:!0})},async glob(e){let t=e.patterns??(e.pattern?[e.pattern]:p),n=e.ignore??m,r=[];for(let e of t){let t=await f(e,{cwd:g,ignore:n});r.push(...t)}return Array.from(new Set(r)).sort((e,t)=>e.localeCompare(t))},resolve(...e){let[t,...n]=e;return t?d(g,t,...n):g},dirname(e){return s(e)},basename(e){return o(e)},join(...e){return l(...e)},relative(e,t){return u(e,t)}};function _(e){return c(e)?e:d(g,e)}}export{h as createNodeFsAdapter};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{access as e}from"node:fs/promises";import{resolve as t}from"node:path";import{execSync as n}from"node:child_process";function r(r){let i=r??process.cwd();return{async showFile(e,t){try{return n(`git show ${e}:${t}`,{cwd:i,encoding:`utf-8`,stdio:[`ignore`,`pipe`,`pipe`]})}catch(n){throw Error(`Could not load ${t} at ref ${e}: ${n instanceof Error?n.message:String(n)}`)}},async clean(e){let t=[];e?.force&&t.push(`-f`),e?.directories&&t.push(`-d`),e?.ignored&&t.push(`-x`),e?.dryRun&&t.push(`--dry-run`),n(`git clean ${t.join(` `)}`,{cwd:i,stdio:`inherit`})},async isGitRepo(n){let r=n?t(i,n):i;try{return await e(t(r,`.git`)),!0}catch{return!1}}}}export{r as createNodeGitAdapter};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createNodeFsAdapter as e}from"./fs.js";import{createNodeGitAdapter as t}from"./git.js";import{createNodeWatcherAdapter as n}from"./watcher.js";import{createNodeAiAdapter as r}from"./ai.js";import{createConsoleLoggerAdapter as i,createNoopLoggerAdapter as a}from"./logger.js";import{createNodeAdapters as o}from"./factory.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(){return{debug(e,t){process.env.DEBUG&&console.debug(`[DEBUG] ${e}`,t??``)},info(e,t){console.info(`[INFO] ${e}`,t??``)},warn(e,t){console.warn(`[WARN] ${e}`,t??``)},error(e,t){console.error(`[ERROR] ${e}`,t??``)},createProgress(){return n()}}}function t(){let e=()=>{};return{debug:e,info:e,warn:e,error:e,createProgress:r}}function n(){return{start(e){console.log(`⏳ ${e}`)},update(e){let t=e.current!==void 0&&e.total!==void 0?` (${e.current}/${e.total})`:``;console.log(` ${e.message}${t}`)},succeed(e){console.log(`✅ ${e??`Done`}`)},fail(e){console.error(`❌ ${e??`Failed`}`)},warn(e){console.warn(`⚠️ ${e??`Warning`}`)},stop(){}}}function r(){let e=()=>{};return{start:e,update:e,succeed:e,fail:e,warn:e,stop:e}}export{e as createConsoleLoggerAdapter,t as createNoopLoggerAdapter};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"chokidar";const t=[`node_modules/**`,`dist/**`,`.turbo/**`];function n(n){let r=n??process.cwd();return{watch(n){let i=[],a,o=e.watch(n.pattern,{cwd:r,ignored:n.ignore??t,persistent:!0,ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:250,pollInterval:50}}),s=e=>{n.debounceMs&&n.debounceMs>0?(clearTimeout(a),a=setTimeout(()=>{i.forEach(t=>t(e))},n.debounceMs)):i.forEach(t=>t(e))};return o.on(`add`,e=>{s({type:`add`,path:e})}),o.on(`change`,e=>{s({type:`change`,path:e})}),o.on(`unlink`,e=>{s({type:`unlink`,path:e})}),{on(e){i.push(e)},async close(){clearTimeout(a),await o.close()}}}}}export{n as createNodeWatcherAdapter};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createNodeFsAdapter as e}from"./adapters/fs.js";import{createNodeGitAdapter as t}from"./adapters/git.js";import{createNodeWatcherAdapter as n}from"./adapters/watcher.js";import{createNodeAiAdapter as r}from"./adapters/ai.js";import{createConsoleLoggerAdapter as i,createNoopLoggerAdapter as a}from"./adapters/logger.js";import{createNodeAdapters as o}from"./adapters/factory.js";import"./adapters/index.js";import{validateSpec as s,validateSpecs as c}from"./services/validate.js";import{validateImplementationFiles as l}from"./services/validate-implementation.js";import{compareSpecs as u}from"./services/diff.js";import{analyzeDeps as d,exportGraphAsDot as f,getContractNode as p,getGraphStats as m}from"./services/deps.js";import{groupSpecsByType as h,listSpecs as g}from"./services/list.js";import{getApiKey as _,loadWorkspaceConfig as v,mergeWorkspaceConfig as y}from"./services/config.js";import{buildSpec as b}from"./services/build.js";import{syncSpecs as x}from"./services/sync.js";import{watchSpecs as S}from"./services/watch.js";import{cleanArtifacts as C}from"./services/clean.js";import{runTests as w}from"./services/test.js";import{createRegeneratorService as T}from"./services/regenerator.js";import"./services/index.js";export*from"@lssm/module.contractspec-workspace";export{d as analyzeDeps,b as buildSpec,C as cleanArtifacts,u as compareSpecs,i as createConsoleLoggerAdapter,o as createNodeAdapters,r as createNodeAiAdapter,e as createNodeFsAdapter,t as createNodeGitAdapter,n as createNodeWatcherAdapter,a as createNoopLoggerAdapter,T as createRegeneratorService,f as exportGraphAsDot,_ as getApiKey,p as getContractNode,m as getGraphStats,h as groupSpecsByType,g as listSpecs,v as loadWorkspaceConfig,y as mergeWorkspaceConfig,w as runTests,x as syncSpecs,l as validateImplementationFiles,s as validateSpec,c as validateSpecs,S as watchSpecs};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{generateComponentTemplate as e,generateHandlerTemplate as t,generateTestTemplate as n,inferSpecTypeFromFilePath as r,scanSpecSource as i}from"@lssm/module.contractspec-workspace";async function a(e,t,n,a={}){let{fs:c,logger:l}=t,{targets:u=s(e),outputDir:d=n.outputDir,overwrite:f=!1,dryRun:p=!1}=a,m=await c.readFile(e),h=i(m,e),g=r(e);l.info(`Building from spec: ${e}`,{specType:g});let _=[];for(let t of u)try{let n=await o(t,e,m,h,g,{fs:c,logger:l},d,f,p);_.push(n)}catch(e){_.push({target:t,outputPath:``,success:!1,error:e instanceof Error?e.message:String(e)})}return{specPath:e,specInfo:h,results:_}}async function o(r,i,a,o,s,l,u,d,f){let{fs:p,logger:m}=l,h,g;switch(r){case`handler`:{if(s!==`operation`)return{target:r,outputPath:``,success:!1,skipped:!0,error:`Handler generation only supported for operation specs (got ${s})`};let e=o.kind===`command`||o.kind===`query`?o.kind:`command`;h=t(o.name??`unknown`,e),g=c(i,u,`handlers`,o.name??`unknown`,`.handler.ts`,l.fs);break}case`component`:if(s!==`presentation`)return{target:r,outputPath:``,success:!1,skipped:!0,error:`Component generation only supported for presentation specs (got ${s})`};h=e(o.name??`unknown`,o.description??``),g=c(i,u,`components`,o.name??`unknown`,`.tsx`,l.fs);break;case`test`:{let e=s===`operation`?`handler`:`component`;h=n(o.name??`unknown`,e),g=c(i,u,`__tests__`,o.name??`unknown`,`.test.ts`,l.fs);break}default:return{target:r,outputPath:``,success:!1,error:`Unknown target: ${r}`}}if(await p.exists(g)&&!d)return{target:r,outputPath:g,success:!1,skipped:!0,error:`File already exists (use overwrite option)`};if(f)return m.info(`[dry-run] Would write: ${g}`),{target:r,outputPath:g,success:!0};let _=p.dirname(g);return await p.mkdir(_),await p.writeFile(g,h),m.info(`Generated: ${g}`),{target:r,outputPath:g,success:!0}}function s(e){switch(r(e)){case`operation`:return[`handler`];case`presentation`:return[`component`];default:return[]}}function c(e,t,n,r,i,a){let o=l(r.split(`.`).pop()??`unknown`),s;return s=t.startsWith(`.`)?a.resolve(a.dirname(e),`..`,t,n):a.resolve(t,n),a.join(s,`${o}${i}`)}function l(e){return e.replace(/\./g,`-`).replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}export{a as buildSpec};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
async function e(e,t={}){let{fs:n,logger:r}=e,i=(t.outputDir??`./src`).replace(/\\/g,`/`),a=[`generated/**`,`dist/**`,`.turbo/**`],o=[`${i}/handlers/**/*.handler.ts`,`${i}/handlers/**/*.handler.test.ts`,`${i}/components/**/*.tsx`,`${i}/components/**/*.test.tsx`,`${i}/forms/**/*.form.tsx`,`${i}/forms/**/*.form.test.tsx`,`${i}/**/*.runner.ts`,`${i}/**/*.renderer.tsx`],s=t.generatedOnly?[...a,...o]:[...a,`**/*.generated.ts`,`**/*.generated.js`,`**/*.generated.d.ts`,...o],c=await n.glob({patterns:s,ignore:[`node_modules/**`]}),l=[],u=[];for(let e of c)try{let i=await n.stat(e),a=(Date.now()-i.mtime.getTime())/(1e3*60*60*24);if(typeof t.olderThanDays==`number`&&a<t.olderThanDays){u.push({path:e,reason:`younger_than_${t.olderThanDays}_days`});continue}t.dryRun?r.info(`[dry-run] clean would remove`,{path:e,size:i.size}):(await n.remove(e),r.info(`clean.removed`,{path:e,size:i.size})),l.push({path:e,size:i.size})}catch(t){u.push({path:e,reason:t instanceof Error?t.message:String(t)})}return{removed:l,skipped:u}}export{e as cleanArtifacts};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DEFAULT_WORKSPACE_CONFIG as e}from"@lssm/module.contractspec-workspace";import*as t from"zod";const n=t.object({aiProvider:t.enum([`claude`,`openai`,`ollama`,`custom`]).default(`claude`),aiModel:t.string().optional(),agentMode:t.enum([`simple`,`cursor`,`claude-code`,`openai-codex`]).default(`simple`),customEndpoint:t.string().url().nullable().optional(),customApiKey:t.string().nullable().optional(),outputDir:t.string().default(`./src`),conventions:t.object({operations:t.string().default(`interactions/commands|queries`),events:t.string().default(`events`),presentations:t.string().default(`presentations`),forms:t.string().default(`forms`)}),defaultOwners:t.array(t.string()).default([]),defaultTags:t.array(t.string()).default([])});async function r(t,r){let i=t.join(r??`.`,`.contractsrc.json`);if(!await t.exists(i))return e;try{let e=await t.readFile(i),r=JSON.parse(e);return n.parse(r)}catch{return e}}function i(e,t){return{...e,aiProvider:t.provider??process.env.CONTRACTSPEC_AI_PROVIDER??e.aiProvider,aiModel:t.model??process.env.CONTRACTSPEC_AI_MODEL??e.aiModel,agentMode:t.agentMode??process.env.CONTRACTSPEC_AGENT_MODE??e.agentMode,customEndpoint:t.endpoint??process.env.CONTRACTSPEC_LLM_ENDPOINT??e.customEndpoint??void 0,customApiKey:process.env.CONTRACTSPEC_LLM_API_KEY??e.customApiKey??void 0,outputDir:t.outputDir??e.outputDir}}function a(e){switch(e){case`claude`:return process.env.ANTHROPIC_API_KEY;case`openai`:return process.env.OPENAI_API_KEY;case`custom`:return process.env.CONTRACTSPEC_LLM_API_KEY;case`ollama`:return;default:return}}export{a as getApiKey,r as loadWorkspaceConfig,i as mergeWorkspaceConfig};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{addContractNode as e,buildReverseEdges as t,createContractGraph as n,detectCycles as r,findMissingDependencies as i,parseImportedSpecNames as a,toDot as o}from"@lssm/module.contractspec-workspace";async function s(o,s={}){let{fs:c}=o,l=await c.glob({pattern:s.pattern}),u=n();for(let t of l){let n=await c.readFile(t),r=c.relative(`.`,t),i=n.match(/name:\s*['"]([^'"]+)['"]/);e(u,(i?.[1]?i[1]:c.basename(t).replace(/\.[jt]s$/,``).replace(/\.(contracts|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)$/,``))||`unknown`,r,a(n,t))}t(u);let d=r(u),f=i(u);return{graph:u,total:u.size,cycles:d,missing:f}}function c(e,t){return e.get(t)}function l(e){return o(e)}function u(e){let t=Array.from(e.values()),n=t.filter(e=>e.dependencies.length>0),r=t.filter(e=>e.dependencies.length===0),i=t.filter(e=>e.dependents.length>0),a=t.filter(e=>e.dependents.length===0);return{total:e.size,withDeps:n.length,withoutDeps:r.length,used:i.length,unused:a.length}}export{s as analyzeDeps,l as exportGraphAsDot,c as getContractNode,u as getGraphStats};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{computeSemanticDiff as e}from"@lssm/module.contractspec-workspace";async function t(t,n,r,i={}){let{fs:a,git:o}=r;if(!await a.exists(t))throw Error(`Spec file not found: ${t}`);let s=await a.readFile(t),c,l;if(i.baseline)c=await o.showFile(i.baseline,t),l=`${i.baseline}:${t}`;else{if(!await a.exists(n))throw Error(`Spec file not found: ${n}`);c=await a.readFile(n),l=n}let u=e(s,t,c,l,{breakingOnly:i.breakingOnly});return{spec1:t,spec2:l,differences:u}}export{t as compareSpecs};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{validateSpec as e,validateSpecs as t}from"./validate.js";import{validateImplementationFiles as n}from"./validate-implementation.js";import{compareSpecs as r}from"./diff.js";import{analyzeDeps as i,exportGraphAsDot as a,getContractNode as o,getGraphStats as s}from"./deps.js";import{groupSpecsByType as c,listSpecs as l}from"./list.js";import{getApiKey as u,loadWorkspaceConfig as d,mergeWorkspaceConfig as f}from"./config.js";import{buildSpec as p}from"./build.js";import{syncSpecs as m}from"./sync.js";import{watchSpecs as h}from"./watch.js";import{cleanArtifacts as g}from"./clean.js";import{runTests as _}from"./test.js";import{createRegeneratorService as v}from"./regenerator.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{scanSpecSource as e}from"@lssm/module.contractspec-workspace";async function t(t,n={}){let{fs:r}=t,i=await r.glob({pattern:n.pattern}),a=[];for(let t of i){let i=e(await r.readFile(t),t);n.type&&i.specType!==n.type||a.push(i)}return a}function n(e){let t=new Map;for(let n of e){let e=t.get(n.specType)??[];e.push(n),t.set(n.specType,e)}return t}export{n as groupSpecsByType,t as listSpecs};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{RegeneratorService as e}from"@lssm/lib.contracts/regenerator";function t(t){return new e({contexts:t.contexts,adapters:t.adapters??{},rules:t.rules,sink:t.sink,pollIntervalMs:t.pollIntervalMs,batchDurationMs:t.batchDurationMs})}export{t as createRegeneratorService};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{validateSpec as e}from"./validate.js";import{buildSpec as t}from"./build.js";async function n(n,r,i={},a){let{fs:o,logger:s}=n,c=await o.glob({pattern:i.pattern}),l=i.outputDirs?.length?i.outputDirs:[void 0],u=[],d=a?.validate??(t=>e(t,{fs:o,logger:s})),f=a?.build??((e,n)=>t(e,{fs:o,logger:s},n?{...r,outputDir:n}:r,{...i.buildOptions??{},outputDir:n}));for(let e of c)for(let t of l){let n={specPath:e,outputDir:t};if(i.validate)try{n.validation=await d(e)}catch(e){n.error={phase:`validate`,message:e instanceof Error?e.message:String(e)},u.push(n);continue}if(i.dryRun)s.info(`[dry-run] syncSpecs skipped build`,{specPath:e,outputDir:t});else try{n.build=await f(e,t)}catch(e){n.error={phase:`build`,message:e instanceof Error?e.message:String(e)},u.push(n);continue}u.push(n)}return{specs:c,runs:u}}export{n as syncSpecs};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{TestRunner as e}from"@lssm/lib.contracts/tests";async function t(t,n){let r=new e({registry:n}),i=[],a=0,o=0;for(let e of t){let t=await r.run(e);i.push(t),a+=t.passed,o+=t.failed}return{results:i,passed:a,failed:o}}export{t as runTests};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{scanSpecSource as e}from"@lssm/module.contractspec-workspace";function t(e){return e.replace(/\./g,`-`).replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}function n(e){return e.split(/[-_.]/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``)}async function r(r,i,a,o={}){let{fs:s}=i,c=[],l=[];if(!await s.exists(r))return{valid:!1,errors:[`Spec file not found: ${r}`],warnings:[],expected:{}};let u=e(await s.readFile(r),r),d=u.name??s.basename(r).replace(/\.[jt]s$/,``),f=o.outputDir??a.outputDir??`./src`,p=t(d),m={};if(u.specType===`operation`&&(m.handlerPath=s.join(f,`handlers`,`${p}.handler.ts`),m.handlerTestPath=s.join(f,`handlers`,`${p}.handler.test.ts`)),u.specType===`presentation`&&(m.componentPath=s.join(f,`components`,`${p}.tsx`),m.componentTestPath=s.join(f,`components`,`${p}.test.tsx`)),u.specType===`form`&&(m.formPath=s.join(f,`forms`,`${p}.form.tsx`),m.formTestPath=s.join(f,`forms`,`${p}.form.test.tsx`)),o.checkHandlers&&m.handlerPath)if(!await s.exists(m.handlerPath))c.push(`Missing handler file: ${m.handlerPath}`);else{let e=await s.readFile(m.handlerPath),t=`${n(d.split(`.`).pop()??d)}Spec`,r=/ContractHandler<\s*typeof\s+\w+\s*>/.test(e),i=RegExp(`typeof\\s+${t}\\b`).test(e);r?i||l.push(`Handler ContractHandler typing does not reference expected spec var (${t}): ${m.handlerPath}`):l.push(`Handler does not appear to type itself as ContractHandler<typeof Spec>: ${m.handlerPath}`)}if(o.checkTests){let e=[m.handlerTestPath,m.componentTestPath,m.formTestPath].filter(e=>typeof e==`string`);for(let t of e)await s.exists(t)||c.push(`Missing test file: ${t}`)}return{valid:c.length===0,errors:c,warnings:l,expected:m}}export{r as validateImplementationFiles};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{validateSpecStructure as e}from"@lssm/module.contractspec-workspace";async function t(t,n,r={}){let{fs:i}=n;if(!await i.exists(t))return{valid:!1,errors:[`Spec file not found: ${t}`],warnings:[]};let a=await i.readFile(t),o=i.basename(t),s=[],c=[],l;return r.skipStructure||(l=e(a,o),s.push(...l.errors),c.push(...l.warnings)),{valid:s.length===0,structureResult:l,errors:s,warnings:c}}async function n(e,n,r={}){let i=new Map;for(let a of e){let e=await t(a,n,r);i.set(a,e)}return i}export{t as validateSpec,n as validateSpecs};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{validateSpec as e}from"./validate.js";import{buildSpec as t}from"./build.js";function n(n,r,i,a){let{watcher:o,fs:s,logger:c}=n,l=o.watch(i),u=a?.validate??(async t=>{await e(t,{fs:s,logger:c})}),d=a?.build??(async e=>{await t(e,{fs:s,logger:c},r)});return l.on(async e=>{e.type===`change`&&(c.info(`watchSpecs.changed`,{path:e.path}),i.runValidate&&await u(e.path),i.runBuild&&(i.dryRun?c.info(`[dry-run] watchSpecs skipped build`,{path:e.path}):await d(e.path)))}),l}export{n as watchSpecs};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lssm/bundle.contractspec-workspace",
|
|
3
|
+
"version": "0.0.0-canary-20251213172311",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
|
|
14
|
+
"build": "bun build:bundle && bun build:types",
|
|
15
|
+
"build:bundle": "tsdown",
|
|
16
|
+
"build:types": "tsc --noEmit",
|
|
17
|
+
"dev": "bun build:bundle --watch",
|
|
18
|
+
"clean": "rimraf dist .turbo",
|
|
19
|
+
"lint": "bun lint:fix",
|
|
20
|
+
"lint:fix": "eslint src --fix",
|
|
21
|
+
"lint:check": "eslint src",
|
|
22
|
+
"test": "bun run"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@lssm/module.contractspec-workspace": "workspace:*",
|
|
26
|
+
"@lssm/lib.contracts": "workspace:*",
|
|
27
|
+
"@lssm/lib.schema": "workspace:*",
|
|
28
|
+
"@lssm/lib.testing": "workspace:*",
|
|
29
|
+
"ai": "beta",
|
|
30
|
+
"@ai-sdk/anthropic": "beta",
|
|
31
|
+
"@ai-sdk/openai": "beta",
|
|
32
|
+
"ollama-ai-provider": "^1.2.0",
|
|
33
|
+
"zod": "^4.1.13",
|
|
34
|
+
"glob": "^11.0.1",
|
|
35
|
+
"chokidar": "^4.0.1"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@lssm/tool.tsdown": "workspace:*",
|
|
39
|
+
"@lssm/tool.typescript": "workspace:*",
|
|
40
|
+
"@types/node": "^22.10.2",
|
|
41
|
+
"tsdown": "^0.17.0",
|
|
42
|
+
"typescript": "^5.9.3"
|
|
43
|
+
},
|
|
44
|
+
"exports": {
|
|
45
|
+
".": "./src/index.ts",
|
|
46
|
+
"./*": "./*"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public",
|
|
50
|
+
"exports": {
|
|
51
|
+
".": "./dist/index.js",
|
|
52
|
+
"./*": "./*"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|