@imgly/codesign-mcp 0.1.0 → 0.1.2
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/assets/docs/handbook/SKILL.md +6 -0
- package/dist/assets/inline.generated.js +1 -1
- package/dist/assets/inline.generated.js.map +1 -1
- package/dist/cli/http.d.ts.map +1 -1
- package/dist/cli/http.js +4 -0
- package/dist/cli/http.js.map +1 -1
- package/dist/cli/stdio.d.ts.map +1 -1
- package/dist/cli/stdio.js +55 -18
- package/dist/cli/stdio.js.map +1 -1
- package/dist/engine/cesdk-adapter.d.ts +7 -1
- package/dist/engine/cesdk-adapter.d.ts.map +1 -1
- package/dist/engine/cesdk-adapter.js +7 -1
- package/dist/engine/cesdk-adapter.js.map +1 -1
- package/dist/engine/unsplash-source.d.ts +8 -0
- package/dist/engine/unsplash-source.d.ts.map +1 -0
- package/dist/engine/unsplash-source.js +77 -0
- package/dist/engine/unsplash-source.js.map +1 -0
- package/dist/http/server.d.ts.map +1 -1
- package/dist/http/server.js +0 -1
- package/dist/http/server.js.map +1 -1
- package/dist/server.d.ts +0 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js.map +1 -1
- package/dist/test/test/cli/redirect-console.test.d.ts +2 -0
- package/dist/test/test/cli/redirect-console.test.d.ts.map +1 -0
- package/dist/test/test/cli/redirect-console.test.js +47 -0
- package/dist/test/test/cli/redirect-console.test.js.map +1 -0
- package/dist/test/test/cli/resolve-license.test.d.ts +2 -0
- package/dist/test/test/cli/resolve-license.test.d.ts.map +1 -0
- package/dist/test/test/cli/resolve-license.test.js +20 -0
- package/dist/test/test/cli/resolve-license.test.js.map +1 -0
- package/dist/test/test/cli/stdio-license.test.d.ts +2 -0
- package/dist/test/test/cli/stdio-license.test.d.ts.map +1 -0
- package/dist/test/test/cli/stdio-license.test.js +19 -0
- package/dist/test/test/cli/stdio-license.test.js.map +1 -0
- package/dist/test/test/config.test.d.ts +2 -0
- package/dist/test/test/config.test.d.ts.map +1 -0
- package/dist/test/test/config.test.js +91 -0
- package/dist/test/test/config.test.js.map +1 -0
- package/dist/test/test/engine/asset-sources.test.d.ts +2 -0
- package/dist/test/test/engine/asset-sources.test.d.ts.map +1 -0
- package/dist/test/test/engine/asset-sources.test.js +100 -0
- package/dist/test/test/engine/asset-sources.test.js.map +1 -0
- package/dist/test/test/engine/mutex.test.d.ts +2 -0
- package/dist/test/test/engine/mutex.test.d.ts.map +1 -0
- package/dist/test/test/engine/mutex.test.js +63 -0
- package/dist/test/test/engine/mutex.test.js.map +1 -0
- package/dist/test/test/engine/session.test.js +15 -0
- package/dist/test/test/engine/session.test.js.map +1 -1
- package/dist/test/test/engine/unsplash-source.test.d.ts +2 -0
- package/dist/test/test/engine/unsplash-source.test.d.ts.map +1 -0
- package/dist/test/test/engine/unsplash-source.test.js +129 -0
- package/dist/test/test/engine/unsplash-source.test.js.map +1 -0
- package/dist/test/test/handbook-contract.test.js +8 -4
- package/dist/test/test/handbook-contract.test.js.map +1 -1
- package/dist/test/test/http/server.test.js +129 -0
- package/dist/test/test/http/server.test.js.map +1 -1
- package/dist/test/test/http/session-id-header.test.d.ts +2 -0
- package/dist/test/test/http/session-id-header.test.d.ts.map +1 -0
- package/dist/test/test/http/session-id-header.test.js +76 -0
- package/dist/test/test/http/session-id-header.test.js.map +1 -0
- package/dist/test/test/http/session-manager.test.js +14 -0
- package/dist/test/test/http/session-manager.test.js.map +1 -1
- package/dist/test/test/http/workspace-auth.test.d.ts +2 -0
- package/dist/test/test/http/workspace-auth.test.d.ts.map +1 -0
- package/dist/test/test/http/workspace-auth.test.js +41 -0
- package/dist/test/test/http/workspace-auth.test.js.map +1 -0
- package/dist/test/test/metrics/mount.test.d.ts +2 -0
- package/dist/test/test/metrics/mount.test.d.ts.map +1 -0
- package/dist/test/test/metrics/mount.test.js +59 -0
- package/dist/test/test/metrics/mount.test.js.map +1 -0
- package/dist/test/test/metrics/recorder.test.d.ts +2 -0
- package/dist/test/test/metrics/recorder.test.d.ts.map +1 -0
- package/dist/test/test/metrics/recorder.test.js +118 -0
- package/dist/test/test/metrics/recorder.test.js.map +1 -0
- package/dist/test/test/revisions/store.test.js +81 -11
- package/dist/test/test/revisions/store.test.js.map +1 -1
- package/dist/test/test/server.metrics.test.d.ts +2 -0
- package/dist/test/test/server.metrics.test.d.ts.map +1 -0
- package/dist/test/test/server.metrics.test.js +87 -0
- package/dist/test/test/server.metrics.test.js.map +1 -0
- package/dist/test/test/server.test.js +33 -4
- package/dist/test/test/server.test.js.map +1 -1
- package/dist/test/test/tools/asset-search.test.d.ts +2 -0
- package/dist/test/test/tools/asset-search.test.d.ts.map +1 -0
- package/dist/test/test/tools/asset-search.test.js +121 -0
- package/dist/test/test/tools/asset-search.test.js.map +1 -0
- package/dist/test/test/tools/edit.test.js +71 -9
- package/dist/test/test/tools/edit.test.js.map +1 -1
- package/dist/test/test/tools/export.test.js +60 -30
- package/dist/test/test/tools/export.test.js.map +1 -1
- package/dist/test/test/tools/index.test.d.ts +2 -0
- package/dist/test/test/tools/index.test.d.ts.map +1 -0
- package/dist/test/test/tools/index.test.js +55 -0
- package/dist/test/test/tools/index.test.js.map +1 -0
- package/dist/test/test/tools/metrics.test.d.ts +2 -0
- package/dist/test/test/tools/metrics.test.d.ts.map +1 -0
- package/dist/test/test/tools/metrics.test.js +29 -0
- package/dist/test/test/tools/metrics.test.js.map +1 -0
- package/dist/test/test/tools/preview.test.js +6 -4
- package/dist/test/test/tools/preview.test.js.map +1 -1
- package/dist/test/test/tools/rename.test.js +1 -2
- package/dist/test/test/tools/rename.test.js.map +1 -1
- package/dist/test/test/tools/save.test.d.ts +2 -0
- package/dist/test/test/tools/save.test.d.ts.map +1 -0
- package/dist/test/test/tools/save.test.js +137 -0
- package/dist/test/test/tools/save.test.js.map +1 -0
- package/dist/test/test/viewer/router.test.js +56 -0
- package/dist/test/test/viewer/router.test.js.map +1 -1
- package/dist/test/test/viewer/session-mount.test.d.ts +2 -0
- package/dist/test/test/viewer/session-mount.test.d.ts.map +1 -0
- package/dist/test/test/viewer/session-mount.test.js +52 -0
- package/dist/test/test/viewer/session-mount.test.js.map +1 -0
- package/dist/test/test/viewer/url-builder.test.js +76 -9
- package/dist/test/test/viewer/url-builder.test.js.map +1 -1
- package/dist/test/test/workspace/s3.test.d.ts +2 -0
- package/dist/test/test/workspace/s3.test.d.ts.map +1 -0
- package/dist/test/test/workspace/s3.test.js +153 -0
- package/dist/test/test/workspace/s3.test.js.map +1 -0
- package/dist/test/test/workspace/scoped.test.d.ts +2 -0
- package/dist/test/test/workspace/scoped.test.d.ts.map +1 -0
- package/dist/test/test/workspace/scoped.test.js +59 -0
- package/dist/test/test/workspace/scoped.test.js.map +1 -0
- package/dist/test/tsconfig.test.tsbuildinfo +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +14 -4
- package/dist/tools/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.test.js","sourceRoot":"","sources":["../../../../test/revisions/store.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEvE,SAAS,SAAS;IAChB,MAAM,EAAE,GAAG,IAAI,mBAAmB,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,IAAI,aAAa,CAAC,EAAE,CAAC,CAAC;IACpC,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AACvB,CAAC;AAED,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,SAAS,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC;YAChC,KAAK,EAAE,mBAAmB;YAC1B,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,cAAc;YACrB,IAAI,EAAE,gBAAgB;SACvB,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC7C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;QACvC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAEhD,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAE/C,0DAA0D;QAC1D,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,KAAK,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrE,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC1E,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC;YAC9B,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,IAAI,EAAE,aAAa;SACpB,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAExC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QAClD,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;YAC7D,IAAI,EAAE,QAAQ;SACf,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,SAAS,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC;YAChC,KAAK,EAAE,aAAa;YACpB,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,GAAG;YACV,IAAI,EAAE,wBAAwB;SAC/B,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,KAAK,IAAI,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvE,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,KAAK,IAAI,IAAI,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CACxE,wBAAwB,CACzB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,SAAS,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC;YAChC,KAAK,EAAE,aAAa;YACpB,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,KAAK,IAAI,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wGAAwG,EAAE,KAAK,IAAI,EAAE;QACtH,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC;YAC1B,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,IAAI;YACZ,IAAI,EAAE,GAAG;YACT,IAAI,EAAE,WAAW;SAClB,CAAC,CAAC;QACH,8FAA8F;QAC9F,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC;YAC1B,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,IAAI;YACZ,IAAI,EAAE,GAAG;YACT,IAAI,EAAE,uBAAuB;SAC9B,CAAC,CAAC;QACH,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC;YAChC,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,GAAG;YACV,IAAI,EAAE,qCAAqC;SAC5C,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAC3C,qCAAqC,CACtC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5E,MAAM,CAAC,MAAM,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0FAA0F,EAAE,KAAK,IAAI,EAAE;QACxG,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAM,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3E,mCAAmC;QACnC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC5E,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAChE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;QAC/C,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC1E,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAClE,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;YAC5B,YAAY,EAAE,IAAI,CAAC,EAAE;YACrB,UAAU,EAAE,KAAK,CAAC,EAAE;SACrB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;QACzF,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC1E,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAChE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB;QAChF,0EAA0E;QAC1E,8DAA8D;QAC9D,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACtE,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QACxE,MAAM,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QACzE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,kBAAkB,CAAC,CAAC;QAC3E,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC5E,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7D,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC;YAC7B,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,EAAE,CAAC,EAAE;YACb,IAAI,EAAE,MAAM;SACb,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAC1C,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAC/B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACvE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC;YAC3B,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,UAAU;SAClB,CAAC,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7D,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC;YAC3B,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,UAAU;SAClB,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACpE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5D,MAAM,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,KAAK,IAAI,EAAE;QAC7C,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { RevisionStore } from '../../src/revisions/store.js';\nimport { InMemoryWorkspaceFS } from '../../src/workspace/in-memory.js';\n\nfunction makeStore(): { store: RevisionStore; fs: InMemoryWorkspaceFS } {\n const fs = new InMemoryWorkspaceFS();\n const store = new RevisionStore(fs);\n return { store, fs };\n}\n\ndescribe('RevisionStore.write + .read', () => {\n it('writes a root revision, returns its id, and round-trips scene + meta', async () => {\n const { store, fs } = makeStore();\n const written = await store.write({\n scene: '<scene-bytes-v1/>',\n parent: null,\n title: 'Wedding card',\n note: 'initial layout'\n });\n expect(written.id).toMatch(/^[0-9a-f]{12}$/);\n expect(written.meta.parent).toBeNull();\n expect(written.meta.title).toBe('Wedding card');\n\n const round = await store.read(written.id);\n expect(round.scene).toBe('<scene-bytes-v1/>');\n expect(round.meta.title).toBe('Wedding card');\n expect(round.meta.note).toBe('initial layout');\n\n // Sharded under objects/<first-2-hex>/<rest>.{scene,json}\n const shard = written.id.slice(0, 2);\n const rest = written.id.slice(2);\n expect(await fs.exists(`objects/${shard}/${rest}.scene`)).toBe(true);\n expect(await fs.exists(`objects/${shard}/${rest}.json`)).toBe(true);\n });\n\n it('chains a child revision off a parent', async () => {\n const { store } = makeStore();\n const root = await store.write({ scene: 'v1', parent: null, title: 'T' });\n const child = await store.write({\n scene: 'v2',\n parent: root.id,\n note: 'moved title'\n });\n expect(child.meta.parent).toBe(root.id);\n\n const back = await store.read(child.id);\n expect(back.meta.parent).toBe(root.id);\n expect(back.scene).toBe('v2');\n });\n\n it('read throws on unknown revision id', async () => {\n const { store } = makeStore();\n await expect(store.read('deadbeefdead')).rejects.toMatchObject({\n code: 'ENOENT'\n });\n });\n\n it('persists code as a sibling .code.js file when provided', async () => {\n const { store, fs } = makeStore();\n const written = await store.write({\n scene: 'scene-bytes',\n parent: null,\n title: 'T',\n code: 'engine.scene.create();'\n });\n const shard = written.id.slice(0, 2);\n const rest = written.id.slice(2);\n expect(await fs.exists(`objects/${shard}/${rest}.code.js`)).toBe(true);\n expect(await fs.readFile(`objects/${shard}/${rest}.code.js`, 'utf8')).toBe(\n 'engine.scene.create();'\n );\n });\n\n it('omits the .code.js file when no code is provided', async () => {\n const { store, fs } = makeStore();\n const written = await store.write({\n scene: 'scene-bytes',\n parent: null,\n title: 'T'\n });\n const shard = written.id.slice(0, 2);\n const rest = written.id.slice(2);\n expect(await fs.exists(`objects/${shard}/${rest}.code.js`)).toBe(false);\n });\n\n it('does NOT include code in the revisionId hash (same scene + parent + note → same id regardless of code)', async () => {\n const { store } = makeStore();\n const a = await store.write({\n scene: 's',\n parent: null,\n note: 'n',\n code: '// path A'\n });\n // Writing again with the same scene/parent/note but different code overwrites at the same id.\n const b = await store.write({\n scene: 's',\n parent: null,\n note: 'n',\n code: '// path B (different)'\n });\n expect(b.id).toBe(a.id);\n });\n});\n\ndescribe('RevisionStore.readCode', () => {\n it('returns the persisted code', async () => {\n const { store } = makeStore();\n const written = await store.write({\n scene: 's',\n parent: null,\n title: 'T',\n code: 'const page = engine.scene.create();'\n });\n expect(await store.readCode(written.id)).toBe(\n 'const page = engine.scene.create();'\n );\n });\n\n it('returns undefined when no code was persisted for the revision', async () => {\n const { store } = makeStore();\n const written = await store.write({ scene: 's', parent: null, title: 'T' });\n expect(await store.readCode(written.id)).toBeUndefined();\n });\n\n it('returns undefined when the revision id is unknown (the caller surfaces ENOENT via .read)', async () => {\n const { store } = makeStore();\n expect(await store.readCode('deadbeefdead')).toBeUndefined();\n });\n});\n\ndescribe('RevisionStore.listRoots', () => {\n it('returns empty when no roots exist', async () => {\n const { store } = makeStore();\n expect(await store.listRoots()).toEqual([]);\n });\n\n it('returns root entries sorted by updatedAt desc', async () => {\n const { store } = makeStore();\n const r1 = await store.write({ scene: 'a', parent: null, title: 'First' });\n // small delay so updatedAt differs\n await new Promise((r) => setTimeout(r, 5));\n const r2 = await store.write({ scene: 'b', parent: null, title: 'Second' });\n const list = await store.listRoots();\n expect(list.map((e) => e.rootRevision)).toEqual([r2.id, r1.id]);\n expect(list[0].title).toBe('Second');\n });\n\n it('updates latestLeaf on each edit', async () => {\n const { store } = makeStore();\n const root = await store.write({ scene: 'v1', parent: null, title: 'T' });\n const child = await store.write({ scene: 'v2', parent: root.id });\n const list = await store.listRoots();\n expect(list[0]).toMatchObject({\n rootRevision: root.id,\n latestLeaf: child.id\n });\n });\n\n it('does not update latestLeaf when editing off an OLDER revision (fork case)', async () => {\n const { store } = makeStore();\n const root = await store.write({ scene: 'v1', parent: null, title: 'T' });\n const _r2 = await store.write({ scene: 'v2', parent: root.id });\n const r3 = await store.write({ scene: 'v3', parent: root.id }); // fork off root\n // Phase 1 simplification: latestLeaf is the *most recently written* leaf,\n // regardless of structural position. After r3, latestLeaf=r3.\n const list = await store.listRoots();\n expect(list[0].latestLeaf).toBe(r3.id);\n });\n});\n\ndescribe('RevisionStore.historyOf', () => {\n it('returns the chain from root to revision', async () => {\n const { store } = makeStore();\n const r1 = await store.write({ scene: 'a', parent: null, title: 'T' });\n const r2 = await store.write({ scene: 'b', parent: r1.id });\n const r3 = await store.write({ scene: 'c', parent: r2.id });\n const chain = await store.historyOf(r3.id);\n expect(chain.map((r) => r.id)).toEqual([r1.id, r2.id, r3.id]);\n });\n\n it('returns just the root for a root revision', async () => {\n const { store } = makeStore();\n const r = await store.write({ scene: 'a', parent: null, title: 'T' });\n const chain = await store.historyOf(r.id);\n expect(chain.map((c) => c.id)).toEqual([r.id]);\n });\n});\n\ndescribe('RevisionStore.renameRoot', () => {\n it('renames the root when given the root id', async () => {\n const { store } = makeStore();\n const r = await store.write({ scene: 'a', parent: null, title: 'Old' });\n await store.renameRoot(r.id, 'New');\n const root = await store.read(r.id);\n expect(root.meta.title).toBe('New');\n const list = await store.listRoots();\n expect(list[0].title).toBe('New');\n });\n\n it('walks parents to find the root when given a mid-chain id', async () => {\n const { store } = makeStore();\n const r1 = await store.write({ scene: 'a', parent: null, title: 'Old' });\n const r2 = await store.write({ scene: 'b', parent: r1.id });\n const r3 = await store.write({ scene: 'c', parent: r2.id });\n const { rootRevision } = await store.renameRoot(r3.id, 'Mid-chain rename');\n expect(rootRevision).toBe(r1.id);\n const root = await store.read(r1.id);\n expect(root.meta.title).toBe('Mid-chain rename');\n });\n});\n\ndescribe('RevisionStore.treeOf', () => {\n it('returns every revision of a linear chain, sorted by createdAt', async () => {\n const { store } = makeStore();\n const r1 = await store.write({ scene: 'a', parent: null, title: 'T' });\n await new Promise((r) => setTimeout(r, 5));\n const r2 = await store.write({ scene: 'b', parent: r1.id, note: 'second' });\n await new Promise((r) => setTimeout(r, 5));\n const r3 = await store.write({ scene: 'c', parent: r2.id });\n const tree = await store.treeOf(r1.id);\n expect(tree.map((n) => n.id)).toEqual([r1.id, r2.id, r3.id]);\n expect(tree[1]).toMatchObject({ id: r2.id, parent: r1.id, note: 'second' });\n });\n\n it('includes forked branches', async () => {\n const { store } = makeStore();\n const r1 = await store.write({ scene: 'a', parent: null, title: 'T' });\n const r2 = await store.write({ scene: 'b', parent: r1.id });\n const fork = await store.write({\n scene: 'b2',\n parent: r1.id,\n note: 'fork'\n });\n const tree = await store.treeOf(r1.id);\n expect(tree.map((n) => n.id).sort()).toEqual(\n [r1.id, r2.id, fork.id].sort()\n );\n });\n\n it('accepts a mid-chain revision and returns the whole design tree', async () => {\n const { store } = makeStore();\n const r1 = await store.write({ scene: 'a', parent: null, title: 'T' });\n const r2 = await store.write({ scene: 'b', parent: r1.id });\n const r3 = await store.write({ scene: 'c', parent: r2.id });\n const tree = await store.treeOf(r2.id);\n expect(tree.map((n) => n.id).sort()).toEqual([r1.id, r2.id, r3.id].sort());\n });\n\n it(\"returns only the queried design's revisions\", async () => {\n const { store } = makeStore();\n const a1 = await store.write({\n scene: 'a',\n parent: null,\n title: 'Design A'\n });\n const a2 = await store.write({ scene: 'a2', parent: a1.id });\n const b1 = await store.write({\n scene: 'b',\n parent: null,\n title: 'Design B'\n });\n const tree = await store.treeOf(a1.id);\n expect(tree.map((n) => n.id).sort()).toEqual([a1.id, a2.id].sort());\n expect(tree.map((n) => n.id)).not.toContain(b1.id);\n });\n\n it('returns [] for an unknown revision id', async () => {\n const { store } = makeStore();\n await store.write({ scene: 'a', parent: null, title: 'T' });\n expect(await store.treeOf('deadbeefdead')).toEqual([]);\n });\n\n it('returns [] for an empty store', async () => {\n const { store } = makeStore();\n expect(await store.treeOf('deadbeefdead')).toEqual([]);\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"store.test.js","sourceRoot":"","sources":["../../../../test/revisions/store.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { RevisionStore } from '../../src/revisions/store.js';\nimport { InMemoryWorkspaceFS } from '../../src/workspace/in-memory.js';\n\nfunction makeStore(): { store: RevisionStore; fs: InMemoryWorkspaceFS } {\n const fs = new InMemoryWorkspaceFS();\n const store = new RevisionStore(fs);\n return { store, fs };\n}\n\ndescribe('RevisionStore.write + .read', () => {\n it('writes a root revision, returns its id, and round-trips scene + meta', async () => {\n const { store, fs } = makeStore();\n const written = await store.write({\n scene: '<scene-bytes-v1/>',\n parent: null,\n title: 'Wedding card',\n note: 'initial layout'\n });\n expect(written.id).toMatch(/^[0-9a-f]{12}$/);\n expect(written.meta.parent).toBeNull();\n expect(written.meta.title).toBe('Wedding card');\n\n const round = await store.read(written.id);\n expect(round.scene).toBe('<scene-bytes-v1/>');\n expect(round.meta.title).toBe('Wedding card');\n expect(round.meta.note).toBe('initial layout');\n\n // Sharded under objects/<first-2-hex>/<rest>.{scene,json}\n const shard = written.id.slice(0, 2);\n const rest = written.id.slice(2);\n expect(await fs.exists(`objects/${shard}/${rest}.scene`)).toBe(true);\n expect(await fs.exists(`objects/${shard}/${rest}.json`)).toBe(true);\n });\n\n it('chains a child revision off a parent', async () => {\n const { store } = makeStore();\n const root = await store.write({ scene: 'v1', parent: null, title: 'T' });\n const child = await store.write({\n scene: 'v2',\n parent: root.id,\n note: 'moved title'\n });\n expect(child.meta.parent).toBe(root.id);\n\n const back = await store.read(child.id);\n expect(back.meta.parent).toBe(root.id);\n expect(back.scene).toBe('v2');\n });\n\n it('read throws on unknown revision id', async () => {\n const { store } = makeStore();\n await expect(store.read('deadbeefdead')).rejects.toMatchObject({\n code: 'ENOENT'\n });\n });\n\n it('persists code as a sibling .code.js file when provided', async () => {\n const { store, fs } = makeStore();\n const written = await store.write({\n scene: 'scene-bytes',\n parent: null,\n title: 'T',\n code: 'engine.scene.create();'\n });\n const shard = written.id.slice(0, 2);\n const rest = written.id.slice(2);\n expect(await fs.exists(`objects/${shard}/${rest}.code.js`)).toBe(true);\n expect(await fs.readFile(`objects/${shard}/${rest}.code.js`, 'utf8')).toBe(\n 'engine.scene.create();'\n );\n });\n\n it('omits the .code.js file when no code is provided', async () => {\n const { store, fs } = makeStore();\n const written = await store.write({\n scene: 'scene-bytes',\n parent: null,\n title: 'T'\n });\n const shard = written.id.slice(0, 2);\n const rest = written.id.slice(2);\n expect(await fs.exists(`objects/${shard}/${rest}.code.js`)).toBe(false);\n });\n\n it('does NOT include code in the revisionId hash (same scene + parent + note → same id regardless of code)', async () => {\n const { store } = makeStore();\n const a = await store.write({\n scene: 's',\n parent: null,\n note: 'n',\n code: '// path A'\n });\n // Writing again with the same scene/parent/note but different code overwrites at the same id.\n const b = await store.write({\n scene: 's',\n parent: null,\n note: 'n',\n code: '// path B (different)'\n });\n expect(b.id).toBe(a.id);\n });\n});\n\ndescribe('RevisionStore.readCode', () => {\n it('returns the persisted code', async () => {\n const { store } = makeStore();\n const written = await store.write({\n scene: 's',\n parent: null,\n title: 'T',\n code: 'const page = engine.scene.create();'\n });\n expect(await store.readCode(written.id)).toBe(\n 'const page = engine.scene.create();'\n );\n });\n\n it('returns undefined when no code was persisted for the revision', async () => {\n const { store } = makeStore();\n const written = await store.write({ scene: 's', parent: null, title: 'T' });\n expect(await store.readCode(written.id)).toBeUndefined();\n });\n\n it('returns undefined when the revision id is unknown (the caller surfaces ENOENT via .read)', async () => {\n const { store } = makeStore();\n expect(await store.readCode('deadbeefdead')).toBeUndefined();\n });\n});\n\ndescribe('RevisionStore.listRoots', () => {\n it('returns empty when no roots exist', async () => {\n const { store } = makeStore();\n expect(await store.listRoots()).toEqual([]);\n });\n\n it('returns root entries sorted by updatedAt desc', async () => {\n const { store } = makeStore();\n const r1 = await store.write({ scene: 'a', parent: null, title: 'First' });\n // small delay so updatedAt differs\n await new Promise((r) => setTimeout(r, 5));\n const r2 = await store.write({ scene: 'b', parent: null, title: 'Second' });\n const list = await store.listRoots();\n expect(list.map((e) => e.rootRevision)).toEqual([r2.id, r1.id]);\n expect(list[0].title).toBe('Second');\n });\n\n it('updates latestLeaf on each edit', async () => {\n const { store } = makeStore();\n const root = await store.write({ scene: 'v1', parent: null, title: 'T' });\n await new Promise((r) => setTimeout(r, 5));\n const child = await store.write({ scene: 'v2', parent: root.id });\n const list = await store.listRoots();\n expect(list[0]).toMatchObject({\n rootRevision: root.id,\n latestLeaf: child.id\n });\n });\n\n it('latestLeaf is the most recently written leaf, even across a fork', async () => {\n const { store } = makeStore();\n const root = await store.write({ scene: 'v1', parent: null, title: 'T' });\n await store.write({ scene: 'v2', parent: root.id });\n await new Promise((r) => setTimeout(r, 5));\n const r3 = await store.write({ scene: 'v3', parent: root.id }); // fork off root\n const list = await store.listRoots();\n expect(list[0].latestLeaf).toBe(r3.id);\n });\n});\n\ndescribe('RevisionStore.currentTitle', () => {\n it('returns the creation title for a freshly created root', async () => {\n const { store } = makeStore();\n const root = await store.write({ scene: 'v1', parent: null, title: 'Hi' });\n expect(await store.currentTitle(root.id)).toBe('Hi');\n });\n\n it('returns \"Untitled\" for an unknown root', async () => {\n const { store } = makeStore();\n expect(await store.currentTitle('deadbeefdead')).toBe('Untitled');\n });\n});\n\ndescribe('RevisionStore.historyOf', () => {\n it('returns the chain from root to revision', async () => {\n const { store } = makeStore();\n const r1 = await store.write({ scene: 'a', parent: null, title: 'T' });\n const r2 = await store.write({ scene: 'b', parent: r1.id });\n const r3 = await store.write({ scene: 'c', parent: r2.id });\n const chain = await store.historyOf(r3.id);\n expect(chain.map((r) => r.id)).toEqual([r1.id, r2.id, r3.id]);\n });\n\n it('returns just the root for a root revision', async () => {\n const { store } = makeStore();\n const r = await store.write({ scene: 'a', parent: null, title: 'T' });\n const chain = await store.historyOf(r.id);\n expect(chain.map((c) => c.id)).toEqual([r.id]);\n });\n});\n\ndescribe('RevisionStore.renameRoot', () => {\n it('renames the root when given the root id (reflected via currentTitle + listRoots)', async () => {\n const { store } = makeStore();\n const r = await store.write({ scene: 'a', parent: null, title: 'Old' });\n const { rootRevision } = await store.renameRoot(r.id, 'New');\n expect(rootRevision).toBe(r.id);\n expect(await store.currentTitle(r.id)).toBe('New');\n const list = await store.listRoots();\n expect(list[0].title).toBe('New');\n });\n\n it('does NOT mutate the root revision meta (titles are append-only events)', async () => {\n const { store } = makeStore();\n const r = await store.write({ scene: 'a', parent: null, title: 'Old' });\n await store.renameRoot(r.id, 'New');\n // The immutable revision keeps its creation-time title; the live title\n // lives in the titles/ event log, not in the object meta.\n expect((await store.read(r.id)).meta.title).toBe('Old');\n });\n\n it('walks to the root when given a mid-chain id', async () => {\n const { store } = makeStore();\n const r1 = await store.write({ scene: 'a', parent: null, title: 'Old' });\n const r2 = await store.write({ scene: 'b', parent: r1.id });\n const r3 = await store.write({ scene: 'c', parent: r2.id });\n const { rootRevision } = await store.renameRoot(r3.id, 'Mid-chain rename');\n expect(rootRevision).toBe(r1.id);\n expect(await store.currentTitle(r1.id)).toBe('Mid-chain rename');\n });\n\n it('throws ENOENT for an unknown revision id', async () => {\n const { store } = makeStore();\n await expect(store.renameRoot('deadbeefdead', 'X')).rejects.toMatchObject({\n code: 'ENOENT'\n });\n });\n});\n\ndescribe('RevisionStore root pointer', () => {\n it('sets meta.root to its own id for a root revision', async () => {\n const { store } = makeStore();\n const root = await store.write({ scene: 'v1', parent: null, title: 'T' });\n expect(root.meta.root).toBe(root.id);\n });\n\n it('inherits meta.root from the parent for a child revision', async () => {\n const { store } = makeStore();\n const root = await store.write({ scene: 'v1', parent: null, title: 'T' });\n const child = await store.write({ scene: 'v2', parent: root.id });\n const grandchild = await store.write({ scene: 'v3', parent: child.id });\n expect(child.meta.root).toBe(root.id);\n expect(grandchild.meta.root).toBe(root.id);\n });\n});\n\ndescribe('RevisionStore journal', () => {\n it('appends one write-once journal entry per revision under journal/<root>/', async () => {\n const { store, fs } = makeStore();\n const root = await store.write({ scene: 'v1', parent: null, title: 'T' });\n const child = await store.write({ scene: 'v2', parent: root.id });\n\n const entries = await fs.readdir(`journal/${root.id}`);\n expect(entries).toHaveLength(2);\n // Each entry is `<paddedTs>-<leafId>`; the leaf id is after the first dash.\n const leaves = entries.map((e) => e.slice(e.indexOf('-') + 1)).sort();\n expect(leaves).toEqual([root.id, child.id].sort());\n // Journal entries are empty — the filename carries all the data.\n expect(await fs.readFile(`journal/${root.id}/${entries[0]}`, 'utf8')).toBe(\n ''\n );\n });\n\n it('seeds a title event under titles/<root>/ when a root is created', async () => {\n const { store, fs } = makeStore();\n const root = await store.write({\n scene: 'v1',\n parent: null,\n title: 'Hello'\n });\n const titleEvents = await fs.readdir(`titles/${root.id}`);\n expect(titleEvents).toHaveLength(1);\n expect(\n await fs.readFile(`titles/${root.id}/${titleEvents[0]}`, 'utf8')\n ).toBe('Hello');\n });\n\n it('defaults a titleless root to \"Untitled\" in its seed event', async () => {\n const { store, fs } = makeStore();\n const root = await store.write({ scene: 'v1', parent: null });\n const titleEvents = await fs.readdir(`titles/${root.id}`);\n expect(\n await fs.readFile(`titles/${root.id}/${titleEvents[0]}`, 'utf8')\n ).toBe('Untitled');\n });\n});\n\ndescribe('RevisionStore.treeOf', () => {\n it('returns every revision of a linear chain, sorted by createdAt', async () => {\n const { store } = makeStore();\n const r1 = await store.write({ scene: 'a', parent: null, title: 'T' });\n await new Promise((r) => setTimeout(r, 5));\n const r2 = await store.write({ scene: 'b', parent: r1.id, note: 'second' });\n await new Promise((r) => setTimeout(r, 5));\n const r3 = await store.write({ scene: 'c', parent: r2.id });\n const tree = await store.treeOf(r1.id);\n expect(tree.map((n) => n.id)).toEqual([r1.id, r2.id, r3.id]);\n expect(tree[1]).toMatchObject({ id: r2.id, parent: r1.id, note: 'second' });\n });\n\n it('includes forked branches', async () => {\n const { store } = makeStore();\n const r1 = await store.write({ scene: 'a', parent: null, title: 'T' });\n const r2 = await store.write({ scene: 'b', parent: r1.id });\n const fork = await store.write({\n scene: 'b2',\n parent: r1.id,\n note: 'fork'\n });\n const tree = await store.treeOf(r1.id);\n expect(tree.map((n) => n.id).sort()).toEqual(\n [r1.id, r2.id, fork.id].sort()\n );\n });\n\n it('accepts a mid-chain revision and returns the whole design tree', async () => {\n const { store } = makeStore();\n const r1 = await store.write({ scene: 'a', parent: null, title: 'T' });\n const r2 = await store.write({ scene: 'b', parent: r1.id });\n const r3 = await store.write({ scene: 'c', parent: r2.id });\n const tree = await store.treeOf(r2.id);\n expect(tree.map((n) => n.id).sort()).toEqual([r1.id, r2.id, r3.id].sort());\n });\n\n it(\"returns only the queried design's revisions\", async () => {\n const { store } = makeStore();\n const a1 = await store.write({\n scene: 'a',\n parent: null,\n title: 'Design A'\n });\n const a2 = await store.write({ scene: 'a2', parent: a1.id });\n const b1 = await store.write({\n scene: 'b',\n parent: null,\n title: 'Design B'\n });\n const tree = await store.treeOf(a1.id);\n expect(tree.map((n) => n.id).sort()).toEqual([a1.id, a2.id].sort());\n expect(tree.map((n) => n.id)).not.toContain(b1.id);\n });\n\n it('returns [] for an unknown revision id', async () => {\n const { store } = makeStore();\n await store.write({ scene: 'a', parent: null, title: 'T' });\n expect(await store.treeOf('deadbeefdead')).toEqual([]);\n });\n\n it('returns [] for an empty store', async () => {\n const { store } = makeStore();\n expect(await store.treeOf('deadbeefdead')).toEqual([]);\n });\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.metrics.test.d.ts","sourceRoot":"","sources":["../../../test/server.metrics.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { MetricsRecorder } from '../src/metrics/recorder.js';
|
|
5
|
+
import { createCodesignMcpServer } from '../src/server.js';
|
|
6
|
+
const ASSETS = { 'agent/SYSTEM.md': 'sys' };
|
|
7
|
+
function tools() {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
name: 'slow_ok',
|
|
11
|
+
description: 'resolves after ~20ms',
|
|
12
|
+
inputSchema: { type: 'object', properties: {} },
|
|
13
|
+
call: async () => {
|
|
14
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
15
|
+
return { content: [{ type: 'text', text: 'done' }] };
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'soft_error',
|
|
20
|
+
description: 'returns isError:true',
|
|
21
|
+
inputSchema: { type: 'object', properties: {} },
|
|
22
|
+
call: async () => ({
|
|
23
|
+
content: [{ type: 'text', text: 'bad' }],
|
|
24
|
+
isError: true
|
|
25
|
+
})
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'thrower',
|
|
29
|
+
description: 'throws',
|
|
30
|
+
inputSchema: { type: 'object', properties: {} },
|
|
31
|
+
call: async () => {
|
|
32
|
+
throw new Error('boom');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
async function connect(metrics) {
|
|
38
|
+
const server = createCodesignMcpServer({
|
|
39
|
+
assets: ASSETS,
|
|
40
|
+
tools: tools(),
|
|
41
|
+
...(metrics ? { metrics } : {})
|
|
42
|
+
});
|
|
43
|
+
const [clientT, serverT] = InMemoryTransport.createLinkedPair();
|
|
44
|
+
await server.connect(serverT);
|
|
45
|
+
const client = new Client({ name: 'test', version: '0' });
|
|
46
|
+
await client.connect(clientT);
|
|
47
|
+
return client;
|
|
48
|
+
}
|
|
49
|
+
describe('dispatch wrapper records tool metrics', () => {
|
|
50
|
+
it('times a successful call and marks ok:true', async () => {
|
|
51
|
+
const recorder = new MetricsRecorder();
|
|
52
|
+
const client = await connect(recorder);
|
|
53
|
+
await client.callTool({ name: 'slow_ok', arguments: {} });
|
|
54
|
+
const stat = recorder.snapshot().tools.find((t) => t.tool === 'slow_ok');
|
|
55
|
+
expect(stat.count).toBe(1);
|
|
56
|
+
expect(stat.errorCount).toBe(0);
|
|
57
|
+
expect(stat.max).toBeGreaterThanOrEqual(15); // ~20ms with slack
|
|
58
|
+
await client.close();
|
|
59
|
+
});
|
|
60
|
+
it('marks an isError:true result as ok:false', async () => {
|
|
61
|
+
const recorder = new MetricsRecorder();
|
|
62
|
+
const client = await connect(recorder);
|
|
63
|
+
await client.callTool({ name: 'soft_error', arguments: {} });
|
|
64
|
+
const stat = recorder
|
|
65
|
+
.snapshot()
|
|
66
|
+
.tools.find((t) => t.tool === 'soft_error');
|
|
67
|
+
expect(stat.count).toBe(1);
|
|
68
|
+
expect(stat.errorCount).toBe(1);
|
|
69
|
+
await client.close();
|
|
70
|
+
});
|
|
71
|
+
it('marks a thrown error as ok:false and still records', async () => {
|
|
72
|
+
const recorder = new MetricsRecorder();
|
|
73
|
+
const client = await connect(recorder);
|
|
74
|
+
await expect(client.callTool({ name: 'thrower', arguments: {} })).rejects.toBeDefined();
|
|
75
|
+
const stat = recorder.snapshot().tools.find((t) => t.tool === 'thrower');
|
|
76
|
+
expect(stat.count).toBe(1);
|
|
77
|
+
expect(stat.errorCount).toBe(1);
|
|
78
|
+
await client.close();
|
|
79
|
+
});
|
|
80
|
+
it('without a recorder, tool calls still work and nothing is recorded', async () => {
|
|
81
|
+
const client = await connect(); // no metrics
|
|
82
|
+
const res = await client.callTool({ name: 'slow_ok', arguments: {} });
|
|
83
|
+
expect(res.content).toEqual([{ type: 'text', text: 'done' }]);
|
|
84
|
+
await client.close();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
//# sourceMappingURL=server.metrics.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.metrics.test.js","sourceRoot":"","sources":["../../../test/server.metrics.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';\nimport { describe, expect, it } from 'vitest';\nimport { MetricsRecorder } from '../src/metrics/recorder.js';\nimport { createCodesignMcpServer } from '../src/server.js';\n\nconst ASSETS = { 'agent/SYSTEM.md': 'sys' };\n\nfunction tools() {\n return [\n {\n name: 'slow_ok',\n description: 'resolves after ~20ms',\n inputSchema: { type: 'object', properties: {} },\n call: async () => {\n await new Promise((r) => setTimeout(r, 20));\n return { content: [{ type: 'text' as const, text: 'done' }] };\n }\n },\n {\n name: 'soft_error',\n description: 'returns isError:true',\n inputSchema: { type: 'object', properties: {} },\n call: async () => ({\n content: [{ type: 'text' as const, text: 'bad' }],\n isError: true\n })\n },\n {\n name: 'thrower',\n description: 'throws',\n inputSchema: { type: 'object', properties: {} },\n call: async () => {\n throw new Error('boom');\n }\n }\n ];\n}\n\nasync function connect(metrics?: MetricsRecorder) {\n const server = createCodesignMcpServer({\n assets: ASSETS,\n tools: tools(),\n ...(metrics ? { metrics } : {})\n });\n const [clientT, serverT] = InMemoryTransport.createLinkedPair();\n await server.connect(serverT);\n const client = new Client({ name: 'test', version: '0' });\n await client.connect(clientT);\n return client;\n}\n\ndescribe('dispatch wrapper records tool metrics', () => {\n it('times a successful call and marks ok:true', async () => {\n const recorder = new MetricsRecorder();\n const client = await connect(recorder);\n await client.callTool({ name: 'slow_ok', arguments: {} });\n const stat = recorder.snapshot().tools.find((t) => t.tool === 'slow_ok')!;\n expect(stat.count).toBe(1);\n expect(stat.errorCount).toBe(0);\n expect(stat.max).toBeGreaterThanOrEqual(15); // ~20ms with slack\n await client.close();\n });\n\n it('marks an isError:true result as ok:false', async () => {\n const recorder = new MetricsRecorder();\n const client = await connect(recorder);\n await client.callTool({ name: 'soft_error', arguments: {} });\n const stat = recorder\n .snapshot()\n .tools.find((t) => t.tool === 'soft_error')!;\n expect(stat.count).toBe(1);\n expect(stat.errorCount).toBe(1);\n await client.close();\n });\n\n it('marks a thrown error as ok:false and still records', async () => {\n const recorder = new MetricsRecorder();\n const client = await connect(recorder);\n await expect(\n client.callTool({ name: 'thrower', arguments: {} })\n ).rejects.toBeDefined();\n const stat = recorder.snapshot().tools.find((t) => t.tool === 'thrower')!;\n expect(stat.count).toBe(1);\n expect(stat.errorCount).toBe(1);\n await client.close();\n });\n\n it('without a recorder, tool calls still work and nothing is recorded', async () => {\n const client = await connect(); // no metrics\n const res = await client.callTool({ name: 'slow_ok', arguments: {} });\n expect(res.content).toEqual([{ type: 'text', text: 'done' }]);\n await client.close();\n });\n});\n"]}
|
|
@@ -6,8 +6,8 @@ const FIXTURES = {
|
|
|
6
6
|
'docs/alpha/SKILL.md': '---\nname: alpha\ndescription: First skill.\n---\n# Alpha\nSee [more](more.md).',
|
|
7
7
|
'docs/alpha/more.md': '---\nname: more\ndescription: details\n---\nMore details.'
|
|
8
8
|
};
|
|
9
|
-
async function connect(assets = FIXTURES) {
|
|
10
|
-
const server = createCodesignMcpServer({
|
|
9
|
+
async function connect(assets = FIXTURES, extra = {}) {
|
|
10
|
+
const server = createCodesignMcpServer({ assets, ...extra });
|
|
11
11
|
const [clientT, serverT] = InMemoryTransport.createLinkedPair();
|
|
12
12
|
await server.connect(serverT);
|
|
13
13
|
const client = new Client({ name: 'test', version: '0' });
|
|
@@ -15,6 +15,14 @@ async function connect(assets = FIXTURES) {
|
|
|
15
15
|
return { client, server };
|
|
16
16
|
}
|
|
17
17
|
describe('createCodesignMcpServer (initialize + resources)', () => {
|
|
18
|
+
it('reports buildId as serverInfo.version (defaulting to "dev")', async () => {
|
|
19
|
+
const def = await connect();
|
|
20
|
+
expect(def.client.getServerVersion()?.version).toBe('dev');
|
|
21
|
+
// The deploy injects a git SHA; the smoketest asserts it to detect a
|
|
22
|
+
// stale, not-yet-rolled-out container.
|
|
23
|
+
const stamped = await connect(FIXTURES, { buildId: 'abc1234' });
|
|
24
|
+
expect(stamped.client.getServerVersion()?.version).toBe('abc1234');
|
|
25
|
+
});
|
|
18
26
|
it('instructions advertise the codesign tools + force-direct the model to read codesign-system first', async () => {
|
|
19
27
|
const { client } = await connect();
|
|
20
28
|
const instructions = client.getInstructions() ?? '';
|
|
@@ -28,7 +36,9 @@ describe('createCodesignMcpServer (initialize + resources)', () => {
|
|
|
28
36
|
expect(instructions).toContain('inspect');
|
|
29
37
|
expect(instructions).toContain('rename');
|
|
30
38
|
expect(instructions).toContain('open_editor');
|
|
31
|
-
|
|
39
|
+
// asset_generate is opt-in: with no tools registered (no gateway), the
|
|
40
|
+
// hint is omitted so we never advertise a hidden tool.
|
|
41
|
+
expect(instructions).not.toContain('asset_generate');
|
|
32
42
|
expect(instructions).toContain('parent');
|
|
33
43
|
// Hard direction: read handbook before any design call.
|
|
34
44
|
expect(instructions).toContain('handbook');
|
|
@@ -39,6 +49,26 @@ describe('createCodesignMcpServer (initialize + resources)', () => {
|
|
|
39
49
|
// truncation (target: well under 4 KB).
|
|
40
50
|
expect(instructions.length).toBeLessThan(4096);
|
|
41
51
|
});
|
|
52
|
+
it('includes the asset-generation hint only when an asset_generate tool is registered', async () => {
|
|
53
|
+
const server = createCodesignMcpServer({
|
|
54
|
+
assets: FIXTURES,
|
|
55
|
+
tools: [
|
|
56
|
+
{
|
|
57
|
+
name: 'asset_generate',
|
|
58
|
+
description: 'gen',
|
|
59
|
+
inputSchema: { type: 'object', properties: {} },
|
|
60
|
+
call: async () => ({ content: [] })
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
const [clientT, serverT] = InMemoryTransport.createLinkedPair();
|
|
65
|
+
await server.connect(serverT);
|
|
66
|
+
const client = new Client({ name: 'test', version: '0' });
|
|
67
|
+
await client.connect(clientT);
|
|
68
|
+
const instructions = client.getInstructions() ?? '';
|
|
69
|
+
expect(instructions).toContain('Asset generation');
|
|
70
|
+
expect(instructions).toContain('asset_generate');
|
|
71
|
+
});
|
|
42
72
|
it('resources/list returns SKILL.md entry points (sub-files reachable via the read tools)', async () => {
|
|
43
73
|
const { client } = await connect();
|
|
44
74
|
const list = await client.listResources();
|
|
@@ -109,7 +139,6 @@ describe('createCodesignMcpServer (tools)', () => {
|
|
|
109
139
|
const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
|
|
110
140
|
const { InMemoryTransport } = await import('@modelcontextprotocol/sdk/inMemory.js');
|
|
111
141
|
const server = createCodesignMcpServer({
|
|
112
|
-
engine: {},
|
|
113
142
|
assets: { 'agent/SYSTEM.md': 'sys' },
|
|
114
143
|
tools: [
|
|
115
144
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.test.js","sourceRoot":"","sources":["../../../test/server.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';\nimport { describe, expect, it } from 'vitest';\nimport { createCodesignMcpServer } from '../src/server.js';\n\nconst FIXTURES = {\n 'docs/alpha/SKILL.md':\n '---\\nname: alpha\\ndescription: First skill.\\n---\\n# Alpha\\nSee [more](more.md).',\n 'docs/alpha/more.md':\n '---\\nname: more\\ndescription: details\\n---\\nMore details.'\n};\n\nasync function connect(assets: Record<string, string> = FIXTURES) {\n const server = createCodesignMcpServer({ engine: {}, assets });\n const [clientT, serverT] = InMemoryTransport.createLinkedPair();\n await server.connect(serverT);\n const client = new Client({ name: 'test', version: '0' });\n await client.connect(clientT);\n return { client, server };\n}\n\ndescribe('createCodesignMcpServer (initialize + resources)', () => {\n it('instructions advertise the codesign tools + force-direct the model to read codesign-system first', async () => {\n const { client } = await connect();\n const instructions = client.getInstructions() ?? '';\n // Short pointer-style instructions (no inlined SYSTEM body)\n expect(instructions).toContain('This is the **codesign** MCP server');\n expect(instructions).toContain('edit');\n expect(instructions).toContain('preview');\n expect(instructions).toContain('export');\n expect(instructions).toContain('list');\n expect(instructions).toContain('history');\n expect(instructions).toContain('inspect');\n expect(instructions).toContain('rename');\n expect(instructions).toContain('open_editor');\n expect(instructions).toContain('asset_generate');\n expect(instructions).toContain('parent');\n // Hard direction: read handbook before any design call.\n expect(instructions).toContain('handbook');\n expect(instructions).toMatch(/[Bb]efore.*edit/);\n // No more <codesign_skills> XML — discovery now happens via tools/list.\n expect(instructions).not.toContain('<codesign_skills>');\n // Cap on size — keep instructions short enough to survive Claude Code\n // truncation (target: well under 4 KB).\n expect(instructions.length).toBeLessThan(4096);\n });\n\n it('resources/list returns SKILL.md entry points (sub-files reachable via the read tools)', async () => {\n const { client } = await connect();\n const list = await client.listResources();\n const uris = list.resources.map((r) => r.uri).sort();\n // Only the SKILL.md is advertised — the nested `more.md` is reachable\n // via `resources/read` or via codesign_read_alpha but not enumerated.\n expect(uris).toEqual(['codesign://docs/alpha/SKILL.md']);\n });\n\n it('returns rewritten cross-refs in resource content', async () => {\n const { client } = await connect();\n const got = await client.readResource({\n uri: 'codesign://docs/alpha/SKILL.md'\n });\n const text = (got.contents[0] as { text?: string }).text;\n expect(text).toContain('[more](codesign://docs/alpha/more.md)');\n expect(text).toContain('# Alpha');\n });\n\n it('rejects unknown resource URIs with InvalidParams', async () => {\n const { client } = await connect();\n await expect(\n client.readResource({ uri: 'codesign://docs/missing/file.md' })\n ).rejects.toMatchObject({\n code: -32602\n });\n });\n});\n\nconst PROMPT_FIXTURES = {\n 'agent/SYSTEM.md': 'sys',\n 'prompts/explain.md':\n \"---\\nname: explain\\ndescription: Explain things.\\n---\\nPlease explain what's happening.\",\n 'prompts/review.md':\n '---\\nname: review\\ndescription: Review the code.\\n---\\nReview the diff.'\n};\n\ndescribe('createCodesignMcpServer (prompts)', () => {\n it('lists every prompt with name+description', async () => {\n const { client } = await connect(PROMPT_FIXTURES);\n const list = await client.listPrompts();\n expect(list.prompts.map((p) => p.name).sort()).toEqual([\n 'explain',\n 'review'\n ]);\n const explain = list.prompts.find((p) => p.name === 'explain');\n expect(explain?.description).toBe('Explain things.');\n });\n\n it('returns prompt body as a single user message', async () => {\n const { client } = await connect(PROMPT_FIXTURES);\n const got = await client.getPrompt({ name: 'explain' });\n expect(got.messages).toHaveLength(1);\n expect(got.messages[0].role).toBe('user');\n expect(got.messages[0].content).toMatchObject({\n type: 'text',\n text: expect.stringContaining('Please explain')\n });\n });\n\n it('rejects unknown prompt name', async () => {\n const { client } = await connect(PROMPT_FIXTURES);\n await expect(client.getPrompt({ name: 'nope' })).rejects.toMatchObject({\n code: -32602\n });\n });\n\n it('falls back to filename when frontmatter omits `name`', async () => {\n const { client } = await connect({\n 'prompts/no-front.md': 'just text'\n });\n const list = await client.listPrompts();\n expect(list.prompts.map((p) => p.name)).toEqual(['no-front']);\n });\n});\n\ndescribe('createCodesignMcpServer (tools)', () => {\n async function setup() {\n const calls: { name: string; args: unknown }[] = [];\n const { Client } =\n await import('@modelcontextprotocol/sdk/client/index.js');\n const { InMemoryTransport } =\n await import('@modelcontextprotocol/sdk/inMemory.js');\n const server = createCodesignMcpServer({\n engine: {},\n assets: { 'agent/SYSTEM.md': 'sys' },\n tools: [\n {\n name: 'echo',\n description: 'Echo arg back as text',\n inputSchema: {\n type: 'object',\n properties: { msg: { type: 'string' } },\n required: ['msg']\n },\n call: async (args) => {\n calls.push({ name: 'echo', args });\n const msg = (args as { msg: string }).msg;\n return { content: [{ type: 'text', text: msg }] };\n }\n }\n ]\n });\n const [clientT, serverT] = InMemoryTransport.createLinkedPair();\n await server.connect(serverT);\n const client = new Client({ name: 'test', version: '0' });\n await client.connect(clientT);\n return { client, calls };\n }\n\n it('lists tools with their schemas', async () => {\n const { client } = await setup();\n const list = await client.listTools();\n expect(list.tools.map((t) => t.name)).toEqual(['echo']);\n expect(list.tools[0].description).toBe('Echo arg back as text');\n });\n\n it('forwards arguments to call() and returns content', async () => {\n const { client, calls } = await setup();\n const result = await client.callTool({\n name: 'echo',\n arguments: { msg: 'hi' }\n });\n expect(calls).toEqual([{ name: 'echo', args: { msg: 'hi' } }]);\n expect(result.content).toEqual([{ type: 'text', text: 'hi' }]);\n });\n\n it('rejects unknown tool name', async () => {\n const { client } = await setup();\n await expect(\n client.callTool({ name: 'nope', arguments: {} })\n ).rejects.toMatchObject({ code: -32602 });\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"server.test.js","sourceRoot":"","sources":["../../../test/server.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';\nimport { describe, expect, it } from 'vitest';\nimport { createCodesignMcpServer } from '../src/server.js';\n\nconst FIXTURES = {\n 'docs/alpha/SKILL.md':\n '---\\nname: alpha\\ndescription: First skill.\\n---\\n# Alpha\\nSee [more](more.md).',\n 'docs/alpha/more.md':\n '---\\nname: more\\ndescription: details\\n---\\nMore details.'\n};\n\nasync function connect(\n assets: Record<string, string> = FIXTURES,\n extra: { buildId?: string } = {}\n) {\n const server = createCodesignMcpServer({ assets, ...extra });\n const [clientT, serverT] = InMemoryTransport.createLinkedPair();\n await server.connect(serverT);\n const client = new Client({ name: 'test', version: '0' });\n await client.connect(clientT);\n return { client, server };\n}\n\ndescribe('createCodesignMcpServer (initialize + resources)', () => {\n it('reports buildId as serverInfo.version (defaulting to \"dev\")', async () => {\n const def = await connect();\n expect(def.client.getServerVersion()?.version).toBe('dev');\n // The deploy injects a git SHA; the smoketest asserts it to detect a\n // stale, not-yet-rolled-out container.\n const stamped = await connect(FIXTURES, { buildId: 'abc1234' });\n expect(stamped.client.getServerVersion()?.version).toBe('abc1234');\n });\n\n it('instructions advertise the codesign tools + force-direct the model to read codesign-system first', async () => {\n const { client } = await connect();\n const instructions = client.getInstructions() ?? '';\n // Short pointer-style instructions (no inlined SYSTEM body)\n expect(instructions).toContain('This is the **codesign** MCP server');\n expect(instructions).toContain('edit');\n expect(instructions).toContain('preview');\n expect(instructions).toContain('export');\n expect(instructions).toContain('list');\n expect(instructions).toContain('history');\n expect(instructions).toContain('inspect');\n expect(instructions).toContain('rename');\n expect(instructions).toContain('open_editor');\n // asset_generate is opt-in: with no tools registered (no gateway), the\n // hint is omitted so we never advertise a hidden tool.\n expect(instructions).not.toContain('asset_generate');\n expect(instructions).toContain('parent');\n // Hard direction: read handbook before any design call.\n expect(instructions).toContain('handbook');\n expect(instructions).toMatch(/[Bb]efore.*edit/);\n // No more <codesign_skills> XML — discovery now happens via tools/list.\n expect(instructions).not.toContain('<codesign_skills>');\n // Cap on size — keep instructions short enough to survive Claude Code\n // truncation (target: well under 4 KB).\n expect(instructions.length).toBeLessThan(4096);\n });\n\n it('includes the asset-generation hint only when an asset_generate tool is registered', async () => {\n const server = createCodesignMcpServer({\n assets: FIXTURES,\n tools: [\n {\n name: 'asset_generate',\n description: 'gen',\n inputSchema: { type: 'object', properties: {} },\n call: async () => ({ content: [] })\n }\n ]\n });\n const [clientT, serverT] = InMemoryTransport.createLinkedPair();\n await server.connect(serverT);\n const client = new Client({ name: 'test', version: '0' });\n await client.connect(clientT);\n const instructions = client.getInstructions() ?? '';\n expect(instructions).toContain('Asset generation');\n expect(instructions).toContain('asset_generate');\n });\n\n it('resources/list returns SKILL.md entry points (sub-files reachable via the read tools)', async () => {\n const { client } = await connect();\n const list = await client.listResources();\n const uris = list.resources.map((r) => r.uri).sort();\n // Only the SKILL.md is advertised — the nested `more.md` is reachable\n // via `resources/read` or via codesign_read_alpha but not enumerated.\n expect(uris).toEqual(['codesign://docs/alpha/SKILL.md']);\n });\n\n it('returns rewritten cross-refs in resource content', async () => {\n const { client } = await connect();\n const got = await client.readResource({\n uri: 'codesign://docs/alpha/SKILL.md'\n });\n const text = (got.contents[0] as { text?: string }).text;\n expect(text).toContain('[more](codesign://docs/alpha/more.md)');\n expect(text).toContain('# Alpha');\n });\n\n it('rejects unknown resource URIs with InvalidParams', async () => {\n const { client } = await connect();\n await expect(\n client.readResource({ uri: 'codesign://docs/missing/file.md' })\n ).rejects.toMatchObject({\n code: -32602\n });\n });\n});\n\nconst PROMPT_FIXTURES = {\n 'agent/SYSTEM.md': 'sys',\n 'prompts/explain.md':\n \"---\\nname: explain\\ndescription: Explain things.\\n---\\nPlease explain what's happening.\",\n 'prompts/review.md':\n '---\\nname: review\\ndescription: Review the code.\\n---\\nReview the diff.'\n};\n\ndescribe('createCodesignMcpServer (prompts)', () => {\n it('lists every prompt with name+description', async () => {\n const { client } = await connect(PROMPT_FIXTURES);\n const list = await client.listPrompts();\n expect(list.prompts.map((p) => p.name).sort()).toEqual([\n 'explain',\n 'review'\n ]);\n const explain = list.prompts.find((p) => p.name === 'explain');\n expect(explain?.description).toBe('Explain things.');\n });\n\n it('returns prompt body as a single user message', async () => {\n const { client } = await connect(PROMPT_FIXTURES);\n const got = await client.getPrompt({ name: 'explain' });\n expect(got.messages).toHaveLength(1);\n expect(got.messages[0].role).toBe('user');\n expect(got.messages[0].content).toMatchObject({\n type: 'text',\n text: expect.stringContaining('Please explain')\n });\n });\n\n it('rejects unknown prompt name', async () => {\n const { client } = await connect(PROMPT_FIXTURES);\n await expect(client.getPrompt({ name: 'nope' })).rejects.toMatchObject({\n code: -32602\n });\n });\n\n it('falls back to filename when frontmatter omits `name`', async () => {\n const { client } = await connect({\n 'prompts/no-front.md': 'just text'\n });\n const list = await client.listPrompts();\n expect(list.prompts.map((p) => p.name)).toEqual(['no-front']);\n });\n});\n\ndescribe('createCodesignMcpServer (tools)', () => {\n async function setup() {\n const calls: { name: string; args: unknown }[] = [];\n const { Client } =\n await import('@modelcontextprotocol/sdk/client/index.js');\n const { InMemoryTransport } =\n await import('@modelcontextprotocol/sdk/inMemory.js');\n const server = createCodesignMcpServer({\n assets: { 'agent/SYSTEM.md': 'sys' },\n tools: [\n {\n name: 'echo',\n description: 'Echo arg back as text',\n inputSchema: {\n type: 'object',\n properties: { msg: { type: 'string' } },\n required: ['msg']\n },\n call: async (args) => {\n calls.push({ name: 'echo', args });\n const msg = (args as { msg: string }).msg;\n return { content: [{ type: 'text', text: msg }] };\n }\n }\n ]\n });\n const [clientT, serverT] = InMemoryTransport.createLinkedPair();\n await server.connect(serverT);\n const client = new Client({ name: 'test', version: '0' });\n await client.connect(clientT);\n return { client, calls };\n }\n\n it('lists tools with their schemas', async () => {\n const { client } = await setup();\n const list = await client.listTools();\n expect(list.tools.map((t) => t.name)).toEqual(['echo']);\n expect(list.tools[0].description).toBe('Echo arg back as text');\n });\n\n it('forwards arguments to call() and returns content', async () => {\n const { client, calls } = await setup();\n const result = await client.callTool({\n name: 'echo',\n arguments: { msg: 'hi' }\n });\n expect(calls).toEqual([{ name: 'echo', args: { msg: 'hi' } }]);\n expect(result.content).toEqual([{ type: 'text', text: 'hi' }]);\n });\n\n it('rejects unknown tool name', async () => {\n const { client } = await setup();\n await expect(\n client.callTool({ name: 'nope', arguments: {} })\n ).rejects.toMatchObject({ code: -32602 });\n });\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-search.test.d.ts","sourceRoot":"","sources":["../../../../test/tools/asset-search.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { Mutex } from '../../src/engine/mutex.js';
|
|
3
|
+
import { EngineSession } from '../../src/engine/session.js';
|
|
4
|
+
import { createAssetSearchTool } from '../../src/tools/asset-search.js';
|
|
5
|
+
function makeDeps() {
|
|
6
|
+
const fakeEngine = {
|
|
7
|
+
asset: {
|
|
8
|
+
findAllSources: vi.fn(() => ['ly.img.image', 'ly.img.sticker']),
|
|
9
|
+
getSupportedMimeTypes: vi.fn((id) => id === 'ly.img.image' ? ['image/jpeg', 'image/png'] : []),
|
|
10
|
+
findAssets: vi.fn(async () => ({
|
|
11
|
+
assets: [
|
|
12
|
+
{
|
|
13
|
+
id: 'beach-1',
|
|
14
|
+
label: 'Beach',
|
|
15
|
+
groups: ['nature'],
|
|
16
|
+
meta: {
|
|
17
|
+
uri: 'https://cdn.example/beach.jpg',
|
|
18
|
+
thumbUri: 'https://cdn.example/beach-thumb.jpg',
|
|
19
|
+
width: 1200,
|
|
20
|
+
height: 800,
|
|
21
|
+
blockType: '//ly.img.ubq/graphic'
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
currentPage: 0,
|
|
26
|
+
nextPage: 1,
|
|
27
|
+
total: 42
|
|
28
|
+
}))
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const reset = vi.fn(async () => { });
|
|
32
|
+
const adapter = {
|
|
33
|
+
create: () => fakeEngine,
|
|
34
|
+
reset,
|
|
35
|
+
loadFromString: async () => { },
|
|
36
|
+
saveToString: async () => ''
|
|
37
|
+
};
|
|
38
|
+
const session = new EngineSession({ size: 1, adapter });
|
|
39
|
+
const tool = createAssetSearchTool({ session, engineLock: new Mutex() });
|
|
40
|
+
return { fakeEngine, session, tool, reset };
|
|
41
|
+
}
|
|
42
|
+
function bodyOf(result) {
|
|
43
|
+
return JSON.parse(result.content[0].text);
|
|
44
|
+
}
|
|
45
|
+
describe('asset_search tool', () => {
|
|
46
|
+
it('lists all sources with supported MIME types when sourceId is omitted', async () => {
|
|
47
|
+
const { tool } = makeDeps();
|
|
48
|
+
const result = await tool.call({});
|
|
49
|
+
expect(result.isError).toBeUndefined();
|
|
50
|
+
expect(bodyOf(result)).toEqual({
|
|
51
|
+
sources: [
|
|
52
|
+
{ id: 'ly.img.image', supportedMimeTypes: ['image/jpeg', 'image/png'] },
|
|
53
|
+
{ id: 'ly.img.sticker', supportedMimeTypes: [] }
|
|
54
|
+
]
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
it('searches a source and returns compact asset JSON', async () => {
|
|
58
|
+
const { tool, fakeEngine } = makeDeps();
|
|
59
|
+
const result = await tool.call({
|
|
60
|
+
sourceId: 'ly.img.image',
|
|
61
|
+
query: 'beach',
|
|
62
|
+
page: 2
|
|
63
|
+
});
|
|
64
|
+
expect(result.isError).toBeUndefined();
|
|
65
|
+
expect(fakeEngine.asset.findAssets).toHaveBeenCalledWith('ly.img.image', {
|
|
66
|
+
query: 'beach',
|
|
67
|
+
page: 2,
|
|
68
|
+
perPage: 20
|
|
69
|
+
});
|
|
70
|
+
expect(bodyOf(result)).toEqual({
|
|
71
|
+
total: 42,
|
|
72
|
+
currentPage: 0,
|
|
73
|
+
nextPage: 1,
|
|
74
|
+
assets: [
|
|
75
|
+
{
|
|
76
|
+
id: 'beach-1',
|
|
77
|
+
label: 'Beach',
|
|
78
|
+
groups: ['nature'],
|
|
79
|
+
uri: 'https://cdn.example/beach.jpg',
|
|
80
|
+
thumbUri: 'https://cdn.example/beach-thumb.jpg',
|
|
81
|
+
width: 1200,
|
|
82
|
+
height: 800
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
it('clamps perPage to 50', async () => {
|
|
88
|
+
const { tool, fakeEngine } = makeDeps();
|
|
89
|
+
await tool.call({ sourceId: 'ly.img.image', perPage: 500 });
|
|
90
|
+
expect(fakeEngine.asset.findAssets).toHaveBeenCalledWith('ly.img.image', {
|
|
91
|
+
page: 0,
|
|
92
|
+
perPage: 50
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
it('errors on an unknown sourceId and lists the valid ones', async () => {
|
|
96
|
+
const { tool } = makeDeps();
|
|
97
|
+
const result = await tool.call({ sourceId: 'nope' });
|
|
98
|
+
expect(result.isError).toBe(true);
|
|
99
|
+
const text = result.content[0].text;
|
|
100
|
+
expect(text).toContain('unknown sourceId "nope"');
|
|
101
|
+
expect(text).toContain('ly.img.image');
|
|
102
|
+
expect(text).toContain('ly.img.sticker');
|
|
103
|
+
});
|
|
104
|
+
it('returns isError when findAssets rejects (scene untouched)', async () => {
|
|
105
|
+
const { tool, fakeEngine } = makeDeps();
|
|
106
|
+
fakeEngine.asset.findAssets.mockRejectedValueOnce(new Error('CDN unreachable'));
|
|
107
|
+
const result = await tool.call({ sourceId: 'ly.img.image' });
|
|
108
|
+
expect(result.isError).toBe(true);
|
|
109
|
+
expect(result.content[0].text).toContain('CDN unreachable');
|
|
110
|
+
});
|
|
111
|
+
it('reuses a warm slot without resetting the engine', async () => {
|
|
112
|
+
const { tool, session, fakeEngine, reset } = makeDeps();
|
|
113
|
+
const slot = await session.acquire(null);
|
|
114
|
+
session.commit(slot, 'rev-1');
|
|
115
|
+
reset.mockClear(); // acquire(null) above legitimately reset once
|
|
116
|
+
await tool.call({ sourceId: 'ly.img.image' });
|
|
117
|
+
expect(fakeEngine.asset.findAssets).toHaveBeenCalled();
|
|
118
|
+
expect(reset).not.toHaveBeenCalled();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=asset-search.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-search.test.js","sourceRoot":"","sources":["../../../../test/tools/asset-search.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAsB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAUxE,SAAS,QAAQ;IACf,MAAM,UAAU,GAAe;QAC7B,KAAK,EAAE;YACL,cAAc,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;YAC/D,qBAAqB,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,EAAU,EAAE,EAAE,CAC1C,EAAE,KAAK,cAAc,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CACzD;YACD,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;gBAC7B,MAAM,EAAE;oBACN;wBACE,EAAE,EAAE,SAAS;wBACb,KAAK,EAAE,OAAO;wBACd,MAAM,EAAE,CAAC,QAAQ,CAAC;wBAClB,IAAI,EAAE;4BACJ,GAAG,EAAE,+BAA+B;4BACpC,QAAQ,EAAE,qCAAqC;4BAC/C,KAAK,EAAE,IAAI;4BACX,MAAM,EAAE,GAAG;4BACX,SAAS,EAAE,sBAAsB;yBAClC;qBACF;iBACF;gBACD,WAAW,EAAE,CAAC;gBACd,QAAQ,EAAE,CAAC;gBACX,KAAK,EAAE,EAAE;aACV,CAAC,CAAC;SACJ;KACF,CAAC;IACF,MAAM,KAAK,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC,CAAC;IACpC,MAAM,OAAO,GAA8B;QACzC,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU;QACxB,KAAK;QACL,cAAc,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;QAC9B,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE;KAC7B,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,aAAa,CAAa,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,qBAAqB,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,KAAK,EAAE,EAAE,CAAC,CAAC;IACzE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC9C,CAAC;AAED,SAAS,MAAM,CAAC,MAEf;IACC,OAAO,IAAI,CAAC,KAAK,CAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAsB,CAAC,IAAI,CAAC,CAAC;AAClE,CAAC;AAED,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YAC7B,OAAO,EAAE;gBACP,EAAE,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,YAAY,EAAE,WAAW,CAAC,EAAE;gBACvE,EAAE,EAAE,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,EAAE,EAAE;aACjD;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,QAAQ,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;YAC7B,QAAQ,EAAE,cAAc;YACxB,KAAK,EAAE,OAAO;YACd,IAAI,EAAE,CAAC;SACR,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;QACvC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC,cAAc,EAAE;YACvE,KAAK,EAAE,OAAO;YACd,IAAI,EAAE,CAAC;YACP,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YAC7B,KAAK,EAAE,EAAE;YACT,WAAW,EAAE,CAAC;YACd,QAAQ,EAAE,CAAC;YACX,MAAM,EAAE;gBACN;oBACE,EAAE,EAAE,SAAS;oBACb,KAAK,EAAE,OAAO;oBACd,MAAM,EAAE,CAAC,QAAQ,CAAC;oBAClB,GAAG,EAAE,+BAA+B;oBACpC,QAAQ,EAAE,qCAAqC;oBAC/C,KAAK,EAAE,IAAI;oBACX,MAAM,EAAE,GAAG;iBACZ;aACF;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QACpC,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,QAAQ,EAAE,CAAC;QACxC,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5D,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC,cAAc,EAAE;YACvE,IAAI,EAAE,CAAC;YACP,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QACrD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,IAAI,GAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAsB,CAAC,IAAI,CAAC;QAC1D,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,QAAQ,EAAE,CAAC;QACxC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,qBAAqB,CAC/C,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAC7B,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC;QAC7D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAsB,CAAC,IAAI,CAAC,CAAC,SAAS,CAC5D,iBAAiB,CAClB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAC;QACxD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACzC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC9B,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,8CAA8C;QACjE,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC;QAC9C,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,gBAAgB,EAAE,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, it, vi } from 'vitest';\nimport { Mutex } from '../../src/engine/mutex.js';\nimport { type EngineAdapter, EngineSession } from '../../src/engine/session.js';\nimport { createAssetSearchTool } from '../../src/tools/asset-search.js';\n\ninterface FakeEngine {\n asset: {\n findAllSources: ReturnType<typeof vi.fn>;\n getSupportedMimeTypes: ReturnType<typeof vi.fn>;\n findAssets: ReturnType<typeof vi.fn>;\n };\n}\n\nfunction makeDeps() {\n const fakeEngine: FakeEngine = {\n asset: {\n findAllSources: vi.fn(() => ['ly.img.image', 'ly.img.sticker']),\n getSupportedMimeTypes: vi.fn((id: string) =>\n id === 'ly.img.image' ? ['image/jpeg', 'image/png'] : []\n ),\n findAssets: vi.fn(async () => ({\n assets: [\n {\n id: 'beach-1',\n label: 'Beach',\n groups: ['nature'],\n meta: {\n uri: 'https://cdn.example/beach.jpg',\n thumbUri: 'https://cdn.example/beach-thumb.jpg',\n width: 1200,\n height: 800,\n blockType: '//ly.img.ubq/graphic'\n }\n }\n ],\n currentPage: 0,\n nextPage: 1,\n total: 42\n }))\n }\n };\n const reset = vi.fn(async () => {});\n const adapter: EngineAdapter<FakeEngine> = {\n create: () => fakeEngine,\n reset,\n loadFromString: async () => {},\n saveToString: async () => ''\n };\n const session = new EngineSession<FakeEngine>({ size: 1, adapter });\n const tool = createAssetSearchTool({ session, engineLock: new Mutex() });\n return { fakeEngine, session, tool, reset };\n}\n\nfunction bodyOf(result: {\n content: Array<{ type: string; text?: string }>;\n}): unknown {\n return JSON.parse((result.content[0] as { text: string }).text);\n}\n\ndescribe('asset_search tool', () => {\n it('lists all sources with supported MIME types when sourceId is omitted', async () => {\n const { tool } = makeDeps();\n const result = await tool.call({});\n expect(result.isError).toBeUndefined();\n expect(bodyOf(result)).toEqual({\n sources: [\n { id: 'ly.img.image', supportedMimeTypes: ['image/jpeg', 'image/png'] },\n { id: 'ly.img.sticker', supportedMimeTypes: [] }\n ]\n });\n });\n\n it('searches a source and returns compact asset JSON', async () => {\n const { tool, fakeEngine } = makeDeps();\n const result = await tool.call({\n sourceId: 'ly.img.image',\n query: 'beach',\n page: 2\n });\n expect(result.isError).toBeUndefined();\n expect(fakeEngine.asset.findAssets).toHaveBeenCalledWith('ly.img.image', {\n query: 'beach',\n page: 2,\n perPage: 20\n });\n expect(bodyOf(result)).toEqual({\n total: 42,\n currentPage: 0,\n nextPage: 1,\n assets: [\n {\n id: 'beach-1',\n label: 'Beach',\n groups: ['nature'],\n uri: 'https://cdn.example/beach.jpg',\n thumbUri: 'https://cdn.example/beach-thumb.jpg',\n width: 1200,\n height: 800\n }\n ]\n });\n });\n\n it('clamps perPage to 50', async () => {\n const { tool, fakeEngine } = makeDeps();\n await tool.call({ sourceId: 'ly.img.image', perPage: 500 });\n expect(fakeEngine.asset.findAssets).toHaveBeenCalledWith('ly.img.image', {\n page: 0,\n perPage: 50\n });\n });\n\n it('errors on an unknown sourceId and lists the valid ones', async () => {\n const { tool } = makeDeps();\n const result = await tool.call({ sourceId: 'nope' });\n expect(result.isError).toBe(true);\n const text = (result.content[0] as { text: string }).text;\n expect(text).toContain('unknown sourceId \"nope\"');\n expect(text).toContain('ly.img.image');\n expect(text).toContain('ly.img.sticker');\n });\n\n it('returns isError when findAssets rejects (scene untouched)', async () => {\n const { tool, fakeEngine } = makeDeps();\n fakeEngine.asset.findAssets.mockRejectedValueOnce(\n new Error('CDN unreachable')\n );\n const result = await tool.call({ sourceId: 'ly.img.image' });\n expect(result.isError).toBe(true);\n expect((result.content[0] as { text: string }).text).toContain(\n 'CDN unreachable'\n );\n });\n\n it('reuses a warm slot without resetting the engine', async () => {\n const { tool, session, fakeEngine, reset } = makeDeps();\n const slot = await session.acquire(null);\n session.commit(slot, 'rev-1');\n reset.mockClear(); // acquire(null) above legitimately reset once\n await tool.call({ sourceId: 'ly.img.image' });\n expect(fakeEngine.asset.findAssets).toHaveBeenCalled();\n expect(reset).not.toHaveBeenCalled();\n });\n});\n"]}
|