@cmssy/astro 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,170 @@
1
+ 'use strict';
2
+
3
+ var core = require('@cmssy/core');
4
+
5
+ // src/middleware.ts
6
+ var CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
7
+ function cmssyMiddleware(config, options = {}) {
8
+ return async function onRequest(context, next) {
9
+ const { pathname } = context.url;
10
+ context.request.headers.delete(core.CMSSY_EDIT_HEADER);
11
+ context.request.headers.delete(core.CMSSY_LOCALE_HEADER);
12
+ const locale = await core.localeForPathname(config, pathname);
13
+ context.request.headers.set(core.CMSSY_LOCALE_HEADER, locale);
14
+ if (!pathname.startsWith(CMSSY_EDIT_PATH_PREFIX) && await core.isVerifiedEditUrl(context.url, config)) {
15
+ context.request.headers.set(core.CMSSY_EDIT_HEADER, "1");
16
+ const target = `${CMSSY_EDIT_PATH_PREFIX}${pathname === "/" ? "" : pathname}${context.url.search}`;
17
+ const response = await context.rewrite(target);
18
+ core.applyCmssyCsp(response, { editorOrigin: config.editorOrigin });
19
+ return response;
20
+ }
21
+ if (options.stripLocalePrefix && pathname.startsWith(`/${locale}`)) {
22
+ const { defaultLocale } = await core.resolveSiteLocales(config);
23
+ if (locale !== defaultLocale) {
24
+ const stripped = pathname.slice(locale.length + 1) || "/";
25
+ return context.rewrite(`${stripped}${context.url.search}`);
26
+ }
27
+ }
28
+ return next();
29
+ };
30
+ }
31
+ async function loadCmssyPage(config, request, url) {
32
+ const isEdit = request.headers.get(core.CMSSY_EDIT_HEADER) === "1";
33
+ const siteLocales = await core.resolveSiteLocales(config);
34
+ const segments = url.pathname.replace(/^\/cmssy-edit/, "").split("/").filter(Boolean);
35
+ const fromPath = core.splitLocaleFromPath(segments, siteLocales);
36
+ const locale = request.headers.get(core.CMSSY_LOCALE_HEADER) ?? fromPath.locale;
37
+ const path = fromPath.path;
38
+ const previewSecret = isEdit ? config.draftSecret : void 0;
39
+ const [page, layouts] = await Promise.all([
40
+ core.fetchPage(config, path, { previewSecret }),
41
+ core.fetchLayouts(config, path, { previewSecret })
42
+ ]);
43
+ return {
44
+ page,
45
+ layouts,
46
+ locale,
47
+ defaultLocale: siteLocales.defaultLocale,
48
+ enabledLocales: siteLocales.locales,
49
+ isEdit
50
+ };
51
+ }
52
+ function xmlEscape(value) {
53
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
54
+ }
55
+ async function baseUrlFor(config, url) {
56
+ return (config.siteUrl ?? url.origin).replace(/\/+$/, "");
57
+ }
58
+ function createCmssySitemap(config, options = {}) {
59
+ return async function GET({ url }) {
60
+ const baseUrl = await baseUrlFor(config, url);
61
+ const { defaultLocale, locales } = await core.resolveSiteLocales(config);
62
+ const pages = await core.fetchPages(config);
63
+ const entries = [];
64
+ for (const page of pages) {
65
+ const slug = core.normalizeSlug(page.slug);
66
+ for (const locale of locales) {
67
+ entries.push({
68
+ url: `${baseUrl}${core.localizedPath(slug, locale, defaultLocale)}`,
69
+ lastModified: page.updatedAt ?? void 0
70
+ });
71
+ }
72
+ }
73
+ if (options.extra) {
74
+ entries.push(
75
+ ...await options.extra({
76
+ baseUrl,
77
+ defaultLocale,
78
+ locales
79
+ })
80
+ );
81
+ }
82
+ const body = [
83
+ '<?xml version="1.0" encoding="UTF-8"?>',
84
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
85
+ ...entries.map(
86
+ (entry) => [
87
+ " <url>",
88
+ ` <loc>${xmlEscape(entry.url)}</loc>`,
89
+ entry.lastModified ? ` <lastmod>${xmlEscape(entry.lastModified)}</lastmod>` : "",
90
+ " </url>"
91
+ ].filter(Boolean).join("\n")
92
+ ),
93
+ "</urlset>"
94
+ ].join("\n");
95
+ return new Response(body, {
96
+ headers: { "content-type": "application/xml" }
97
+ });
98
+ };
99
+ }
100
+ function createCmssyRobots(config) {
101
+ return async function GET({ url }) {
102
+ const baseUrl = await baseUrlFor(config, url);
103
+ const body = [
104
+ "User-agent: *",
105
+ "Allow: /",
106
+ "Disallow: /cmssy-edit",
107
+ `Sitemap: ${baseUrl}/sitemap.xml`,
108
+ ""
109
+ ].join("\n");
110
+ return new Response(body, { headers: { "content-type": "text/plain" } });
111
+ };
112
+ }
113
+
114
+ Object.defineProperty(exports, "CMSSY_EDIT_HEADER", {
115
+ enumerable: true,
116
+ get: function () { return core.CMSSY_EDIT_HEADER; }
117
+ });
118
+ Object.defineProperty(exports, "CMSSY_LOCALE_HEADER", {
119
+ enumerable: true,
120
+ get: function () { return core.CMSSY_LOCALE_HEADER; }
121
+ });
122
+ Object.defineProperty(exports, "buildBlockContext", {
123
+ enumerable: true,
124
+ get: function () { return core.buildBlockContext; }
125
+ });
126
+ Object.defineProperty(exports, "createCmssyClient", {
127
+ enumerable: true,
128
+ get: function () { return core.createCmssyClient; }
129
+ });
130
+ Object.defineProperty(exports, "defineCmssyConfig", {
131
+ enumerable: true,
132
+ get: function () { return core.defineCmssyConfig; }
133
+ });
134
+ Object.defineProperty(exports, "fetchLayouts", {
135
+ enumerable: true,
136
+ get: function () { return core.fetchLayouts; }
137
+ });
138
+ Object.defineProperty(exports, "fetchPage", {
139
+ enumerable: true,
140
+ get: function () { return core.fetchPage; }
141
+ });
142
+ Object.defineProperty(exports, "fetchPageMeta", {
143
+ enumerable: true,
144
+ get: function () { return core.fetchPageMeta; }
145
+ });
146
+ Object.defineProperty(exports, "fetchPages", {
147
+ enumerable: true,
148
+ get: function () { return core.fetchPages; }
149
+ });
150
+ Object.defineProperty(exports, "fetchProduct", {
151
+ enumerable: true,
152
+ get: function () { return core.fetchProduct; }
153
+ });
154
+ Object.defineProperty(exports, "fetchProducts", {
155
+ enumerable: true,
156
+ get: function () { return core.fetchProducts; }
157
+ });
158
+ Object.defineProperty(exports, "localizeHref", {
159
+ enumerable: true,
160
+ get: function () { return core.localizeHref; }
161
+ });
162
+ Object.defineProperty(exports, "resolveSiteLocales", {
163
+ enumerable: true,
164
+ get: function () { return core.resolveSiteLocales; }
165
+ });
166
+ exports.CMSSY_EDIT_PATH_PREFIX = CMSSY_EDIT_PATH_PREFIX;
167
+ exports.cmssyMiddleware = cmssyMiddleware;
168
+ exports.createCmssyRobots = createCmssyRobots;
169
+ exports.createCmssySitemap = createCmssySitemap;
170
+ exports.loadCmssyPage = loadCmssyPage;
@@ -0,0 +1,80 @@
1
+ import { CmssyConfig, CmssyPageData, CmssyLayoutGroup } from '@cmssy/core';
2
+ export { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CmssyBlockContext, CmssyConfig, CmssyEnvConfig, CmssyLayoutGroup, CmssyPageData, buildBlockContext, createCmssyClient, defineCmssyConfig, fetchLayouts, fetchPage, fetchPageMeta, fetchPages, fetchProduct, fetchProducts, localizeHref, resolveSiteLocales } from '@cmssy/core';
3
+
4
+ declare const CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
5
+ interface CmssyMiddlewareOptions {
6
+ /**
7
+ * Strip the language prefix before the app sees it, so a static route like
8
+ * `/shop` serves `/no/shop` too. Leave it off for a catch-all app that reads
9
+ * the language off the path itself.
10
+ */
11
+ stripLocalePrefix?: boolean;
12
+ }
13
+ interface AstroContextLike {
14
+ url: URL;
15
+ request: Request;
16
+ rewrite: (path: string) => Promise<Response> | Response;
17
+ }
18
+ /**
19
+ * The whole middleware a cmssy Astro app needs, in the order it has to happen:
20
+ *
21
+ * 1. resolve the language and pass it on;
22
+ * 2. send a VERIFIED editor request to /cmssy-edit, carrying that language and
23
+ * the edit flag - a prerendered page never sees the query string that would
24
+ * put it in edit mode;
25
+ * 3. strip the language prefix for everything else, if asked.
26
+ *
27
+ * The order is not a detail. Resolve the locale after the rewrite and the editor
28
+ * preview renders in the wrong language; drop the edit flag and the header and
29
+ * footer become markup the editor can select but not fill. Both mistakes shipped
30
+ * in the Next app before this sequence existed - Astro gets it right the first
31
+ * time by reusing it.
32
+ */
33
+ declare function cmssyMiddleware(config: CmssyConfig, options?: CmssyMiddlewareOptions): (context: AstroContextLike, next: () => Promise<Response>) => Promise<Response>;
34
+
35
+ interface CmssyPageResult {
36
+ page: CmssyPageData | null;
37
+ layouts: CmssyLayoutGroup[];
38
+ locale: string;
39
+ defaultLocale: string;
40
+ enabledLocales: string[];
41
+ /** True for a verified editor request. The edit route renders the bridge. */
42
+ isEdit: boolean;
43
+ }
44
+ /**
45
+ * Everything a cmssy page needs, from a plain Request. No framework globals: the
46
+ * locale comes from the header the middleware set (falling back to the path, so
47
+ * a prerendered page still knows its language), and the editor flag comes from
48
+ * the same signal the Next adapter uses - because it is the same protocol, not
49
+ * a Next protocol.
50
+ */
51
+ declare function loadCmssyPage(config: CmssyConfig, request: Request, url: URL): Promise<CmssyPageResult>;
52
+
53
+ interface CmssySitemapEntry {
54
+ url: string;
55
+ lastModified?: string;
56
+ }
57
+ interface CmssySitemapOptions {
58
+ /**
59
+ * Records are not pages, so the sitemap cannot know about your products or
60
+ * categories. Add them here; you get the same base URL and locales the page
61
+ * entries use, so the two cannot disagree.
62
+ */
63
+ extra?: (context: {
64
+ baseUrl: string;
65
+ defaultLocale: string;
66
+ locales: string[];
67
+ }) => Promise<CmssySitemapEntry[]> | CmssySitemapEntry[];
68
+ }
69
+ /**
70
+ * One `<url>` per language, plus x-default - a translated page is not a
71
+ * duplicate, and telling Google it is keeps the translation out of the index.
72
+ */
73
+ declare function createCmssySitemap(config: CmssyConfig, options?: CmssySitemapOptions): ({ url }: {
74
+ url: URL;
75
+ }) => Promise<Response>;
76
+ declare function createCmssyRobots(config: CmssyConfig): ({ url }: {
77
+ url: URL;
78
+ }) => Promise<Response>;
79
+
80
+ export { CMSSY_EDIT_PATH_PREFIX, type CmssyMiddlewareOptions, type CmssyPageResult, type CmssySitemapEntry, type CmssySitemapOptions, cmssyMiddleware, createCmssyRobots, createCmssySitemap, loadCmssyPage };
@@ -0,0 +1,80 @@
1
+ import { CmssyConfig, CmssyPageData, CmssyLayoutGroup } from '@cmssy/core';
2
+ export { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CmssyBlockContext, CmssyConfig, CmssyEnvConfig, CmssyLayoutGroup, CmssyPageData, buildBlockContext, createCmssyClient, defineCmssyConfig, fetchLayouts, fetchPage, fetchPageMeta, fetchPages, fetchProduct, fetchProducts, localizeHref, resolveSiteLocales } from '@cmssy/core';
3
+
4
+ declare const CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
5
+ interface CmssyMiddlewareOptions {
6
+ /**
7
+ * Strip the language prefix before the app sees it, so a static route like
8
+ * `/shop` serves `/no/shop` too. Leave it off for a catch-all app that reads
9
+ * the language off the path itself.
10
+ */
11
+ stripLocalePrefix?: boolean;
12
+ }
13
+ interface AstroContextLike {
14
+ url: URL;
15
+ request: Request;
16
+ rewrite: (path: string) => Promise<Response> | Response;
17
+ }
18
+ /**
19
+ * The whole middleware a cmssy Astro app needs, in the order it has to happen:
20
+ *
21
+ * 1. resolve the language and pass it on;
22
+ * 2. send a VERIFIED editor request to /cmssy-edit, carrying that language and
23
+ * the edit flag - a prerendered page never sees the query string that would
24
+ * put it in edit mode;
25
+ * 3. strip the language prefix for everything else, if asked.
26
+ *
27
+ * The order is not a detail. Resolve the locale after the rewrite and the editor
28
+ * preview renders in the wrong language; drop the edit flag and the header and
29
+ * footer become markup the editor can select but not fill. Both mistakes shipped
30
+ * in the Next app before this sequence existed - Astro gets it right the first
31
+ * time by reusing it.
32
+ */
33
+ declare function cmssyMiddleware(config: CmssyConfig, options?: CmssyMiddlewareOptions): (context: AstroContextLike, next: () => Promise<Response>) => Promise<Response>;
34
+
35
+ interface CmssyPageResult {
36
+ page: CmssyPageData | null;
37
+ layouts: CmssyLayoutGroup[];
38
+ locale: string;
39
+ defaultLocale: string;
40
+ enabledLocales: string[];
41
+ /** True for a verified editor request. The edit route renders the bridge. */
42
+ isEdit: boolean;
43
+ }
44
+ /**
45
+ * Everything a cmssy page needs, from a plain Request. No framework globals: the
46
+ * locale comes from the header the middleware set (falling back to the path, so
47
+ * a prerendered page still knows its language), and the editor flag comes from
48
+ * the same signal the Next adapter uses - because it is the same protocol, not
49
+ * a Next protocol.
50
+ */
51
+ declare function loadCmssyPage(config: CmssyConfig, request: Request, url: URL): Promise<CmssyPageResult>;
52
+
53
+ interface CmssySitemapEntry {
54
+ url: string;
55
+ lastModified?: string;
56
+ }
57
+ interface CmssySitemapOptions {
58
+ /**
59
+ * Records are not pages, so the sitemap cannot know about your products or
60
+ * categories. Add them here; you get the same base URL and locales the page
61
+ * entries use, so the two cannot disagree.
62
+ */
63
+ extra?: (context: {
64
+ baseUrl: string;
65
+ defaultLocale: string;
66
+ locales: string[];
67
+ }) => Promise<CmssySitemapEntry[]> | CmssySitemapEntry[];
68
+ }
69
+ /**
70
+ * One `<url>` per language, plus x-default - a translated page is not a
71
+ * duplicate, and telling Google it is keeps the translation out of the index.
72
+ */
73
+ declare function createCmssySitemap(config: CmssyConfig, options?: CmssySitemapOptions): ({ url }: {
74
+ url: URL;
75
+ }) => Promise<Response>;
76
+ declare function createCmssyRobots(config: CmssyConfig): ({ url }: {
77
+ url: URL;
78
+ }) => Promise<Response>;
79
+
80
+ export { CMSSY_EDIT_PATH_PREFIX, type CmssyMiddlewareOptions, type CmssyPageResult, type CmssySitemapEntry, type CmssySitemapOptions, cmssyMiddleware, createCmssyRobots, createCmssySitemap, loadCmssyPage };
package/dist/index.js ADDED
@@ -0,0 +1,113 @@
1
+ import { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, localeForPathname, isVerifiedEditUrl, applyCmssyCsp, resolveSiteLocales, splitLocaleFromPath, fetchPage, fetchLayouts, fetchPages, normalizeSlug, localizedPath } from '@cmssy/core';
2
+ export { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, buildBlockContext, createCmssyClient, defineCmssyConfig, fetchLayouts, fetchPage, fetchPageMeta, fetchPages, fetchProduct, fetchProducts, localizeHref, resolveSiteLocales } from '@cmssy/core';
3
+
4
+ // src/middleware.ts
5
+ var CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
6
+ function cmssyMiddleware(config, options = {}) {
7
+ return async function onRequest(context, next) {
8
+ const { pathname } = context.url;
9
+ context.request.headers.delete(CMSSY_EDIT_HEADER);
10
+ context.request.headers.delete(CMSSY_LOCALE_HEADER);
11
+ const locale = await localeForPathname(config, pathname);
12
+ context.request.headers.set(CMSSY_LOCALE_HEADER, locale);
13
+ if (!pathname.startsWith(CMSSY_EDIT_PATH_PREFIX) && await isVerifiedEditUrl(context.url, config)) {
14
+ context.request.headers.set(CMSSY_EDIT_HEADER, "1");
15
+ const target = `${CMSSY_EDIT_PATH_PREFIX}${pathname === "/" ? "" : pathname}${context.url.search}`;
16
+ const response = await context.rewrite(target);
17
+ applyCmssyCsp(response, { editorOrigin: config.editorOrigin });
18
+ return response;
19
+ }
20
+ if (options.stripLocalePrefix && pathname.startsWith(`/${locale}`)) {
21
+ const { defaultLocale } = await resolveSiteLocales(config);
22
+ if (locale !== defaultLocale) {
23
+ const stripped = pathname.slice(locale.length + 1) || "/";
24
+ return context.rewrite(`${stripped}${context.url.search}`);
25
+ }
26
+ }
27
+ return next();
28
+ };
29
+ }
30
+ async function loadCmssyPage(config, request, url) {
31
+ const isEdit = request.headers.get(CMSSY_EDIT_HEADER) === "1";
32
+ const siteLocales = await resolveSiteLocales(config);
33
+ const segments = url.pathname.replace(/^\/cmssy-edit/, "").split("/").filter(Boolean);
34
+ const fromPath = splitLocaleFromPath(segments, siteLocales);
35
+ const locale = request.headers.get(CMSSY_LOCALE_HEADER) ?? fromPath.locale;
36
+ const path = fromPath.path;
37
+ const previewSecret = isEdit ? config.draftSecret : void 0;
38
+ const [page, layouts] = await Promise.all([
39
+ fetchPage(config, path, { previewSecret }),
40
+ fetchLayouts(config, path, { previewSecret })
41
+ ]);
42
+ return {
43
+ page,
44
+ layouts,
45
+ locale,
46
+ defaultLocale: siteLocales.defaultLocale,
47
+ enabledLocales: siteLocales.locales,
48
+ isEdit
49
+ };
50
+ }
51
+ function xmlEscape(value) {
52
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
53
+ }
54
+ async function baseUrlFor(config, url) {
55
+ return (config.siteUrl ?? url.origin).replace(/\/+$/, "");
56
+ }
57
+ function createCmssySitemap(config, options = {}) {
58
+ return async function GET({ url }) {
59
+ const baseUrl = await baseUrlFor(config, url);
60
+ const { defaultLocale, locales } = await resolveSiteLocales(config);
61
+ const pages = await fetchPages(config);
62
+ const entries = [];
63
+ for (const page of pages) {
64
+ const slug = normalizeSlug(page.slug);
65
+ for (const locale of locales) {
66
+ entries.push({
67
+ url: `${baseUrl}${localizedPath(slug, locale, defaultLocale)}`,
68
+ lastModified: page.updatedAt ?? void 0
69
+ });
70
+ }
71
+ }
72
+ if (options.extra) {
73
+ entries.push(
74
+ ...await options.extra({
75
+ baseUrl,
76
+ defaultLocale,
77
+ locales
78
+ })
79
+ );
80
+ }
81
+ const body = [
82
+ '<?xml version="1.0" encoding="UTF-8"?>',
83
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
84
+ ...entries.map(
85
+ (entry) => [
86
+ " <url>",
87
+ ` <loc>${xmlEscape(entry.url)}</loc>`,
88
+ entry.lastModified ? ` <lastmod>${xmlEscape(entry.lastModified)}</lastmod>` : "",
89
+ " </url>"
90
+ ].filter(Boolean).join("\n")
91
+ ),
92
+ "</urlset>"
93
+ ].join("\n");
94
+ return new Response(body, {
95
+ headers: { "content-type": "application/xml" }
96
+ });
97
+ };
98
+ }
99
+ function createCmssyRobots(config) {
100
+ return async function GET({ url }) {
101
+ const baseUrl = await baseUrlFor(config, url);
102
+ const body = [
103
+ "User-agent: *",
104
+ "Allow: /",
105
+ "Disallow: /cmssy-edit",
106
+ `Sitemap: ${baseUrl}/sitemap.xml`,
107
+ ""
108
+ ].join("\n");
109
+ return new Response(body, { headers: { "content-type": "text/plain" } });
110
+ };
111
+ }
112
+
113
+ export { CMSSY_EDIT_PATH_PREFIX, cmssyMiddleware, createCmssyRobots, createCmssySitemap, loadCmssyPage };
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ var testing = require('@cmssy/core/testing');
4
+
5
+
6
+
7
+ Object.defineProperty(exports, "checkCmssyEditMode", {
8
+ enumerable: true,
9
+ get: function () { return testing.checkCmssyEditMode; }
10
+ });
@@ -0,0 +1 @@
1
+ export { EditSmokeOptions, EditSmokeResult, checkCmssyEditMode } from '@cmssy/core/testing';
@@ -0,0 +1 @@
1
+ export { EditSmokeOptions, EditSmokeResult, checkCmssyEditMode } from '@cmssy/core/testing';
@@ -0,0 +1 @@
1
+ export { checkCmssyEditMode } from '@cmssy/core/testing';
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@cmssy/astro",
3
+ "version": "5.1.0",
4
+ "description": "Astro bindings for cmssy headless sites: middleware, page loader, sitemap and robots.",
5
+ "keywords": [
6
+ "cmssy",
7
+ "cms",
8
+ "headless",
9
+ "astro"
10
+ ],
11
+ "homepage": "https://cmssy.com",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/cmssy-io/cmssy-sdk.git",
15
+ "directory": "packages/astro"
16
+ },
17
+ "type": "module",
18
+ "license": "MIT",
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "sideEffects": false,
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js",
27
+ "require": "./dist/index.cjs"
28
+ },
29
+ "./testing": {
30
+ "types": "./dist/testing.d.ts",
31
+ "import": "./dist/testing.js",
32
+ "require": "./dist/testing.cjs"
33
+ }
34
+ },
35
+ "main": "./dist/index.cjs",
36
+ "module": "./dist/index.js",
37
+ "types": "./dist/index.d.ts",
38
+ "files": [
39
+ "dist"
40
+ ],
41
+ "peerDependencies": {
42
+ "astro": ">=4"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^20.0.0",
46
+ "tsup": "^8.3.0",
47
+ "typescript": "^5.6.0",
48
+ "vitest": "^2.1.0"
49
+ },
50
+ "dependencies": {
51
+ "@cmssy/core": "5.1.0"
52
+ },
53
+ "scripts": {
54
+ "build": "tsup",
55
+ "test": "vitest run",
56
+ "typecheck": "tsc --noEmit"
57
+ }
58
+ }