@no-mess/client 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/dist/client.d.ts +51 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +134 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/dist/live-edit.d.ts +15 -0
- package/dist/live-edit.d.ts.map +1 -0
- package/dist/live-edit.js +288 -0
- package/dist/live-edit.js.map +1 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +5 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/no-mess-preview.d.ts +11 -0
- package/dist/react/no-mess-preview.d.ts.map +1 -0
- package/dist/react/no-mess-preview.js +8 -0
- package/dist/react/no-mess-preview.js.map +1 -0
- package/dist/react/use-no-mess-live-edit.d.ts +17 -0
- package/dist/react/use-no-mess-live-edit.d.ts.map +1 -0
- package/dist/react/use-no-mess-live-edit.js +60 -0
- package/dist/react/use-no-mess-live-edit.js.map +1 -0
- package/dist/react/use-no-mess-preview.d.ts +3 -0
- package/dist/react/use-no-mess-preview.d.ts.map +1 -0
- package/dist/react/use-no-mess-preview.js +35 -0
- package/dist/react/use-no-mess-preview.js.map +1 -0
- package/dist/types.d.ts +166 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @no-mess/client
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for the [no-mess](https://no-mess.xyz) headless CMS.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @no-mess/client
|
|
9
|
+
# or
|
|
10
|
+
bun add @no-mess/client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Fetching content
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { createNoMessClient } from "@no-mess/client";
|
|
19
|
+
|
|
20
|
+
const client = createNoMessClient({
|
|
21
|
+
apiKey: "your-api-key",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Get all entries of a content type
|
|
25
|
+
const posts = await client.getEntries("blog-post");
|
|
26
|
+
|
|
27
|
+
// Get a single entry by slug
|
|
28
|
+
const post = await client.getEntry("blog-post", "hello-world");
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Live preview (React)
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { useNoMessPreview } from "@no-mess/client/react";
|
|
35
|
+
|
|
36
|
+
function PreviewPage() {
|
|
37
|
+
const { entry, isLoading, error } = useNoMessPreview({
|
|
38
|
+
apiKey: "your-api-key",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (isLoading) return <p>Loading preview...</p>;
|
|
42
|
+
if (error) return <p>Error: {error.message}</p>;
|
|
43
|
+
|
|
44
|
+
return <h1>{entry?.fields.title}</h1>;
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The `useNoMessPreview` hook handles the postMessage handshake between the no-mess admin dashboard and your preview iframe automatically.
|
|
49
|
+
|
|
50
|
+
### Shopify data
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
const products = await client.getProducts();
|
|
54
|
+
const product = await client.getProduct("product-handle");
|
|
55
|
+
const collections = await client.getCollections();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## API reference
|
|
59
|
+
|
|
60
|
+
Full documentation at [no-mess.xyz/docs/sdk](https://no-mess.xyz/docs/sdk).
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { GetEntryOptions, NoMessClientConfig, NoMessEntry, PreviewExchangeResult, PreviewSessionAuth, SchemaGetResponse, SchemaListResponse, ShopifyCollection, ShopifyProduct } from "./types.js";
|
|
2
|
+
export declare class NoMessClient {
|
|
3
|
+
private apiUrl;
|
|
4
|
+
private apiKey;
|
|
5
|
+
constructor(config: NoMessClientConfig);
|
|
6
|
+
private fetch;
|
|
7
|
+
/**
|
|
8
|
+
* List all content type schemas with fields, TypeScript interfaces, and entry counts.
|
|
9
|
+
*/
|
|
10
|
+
getSchemas(): Promise<SchemaListResponse>;
|
|
11
|
+
/**
|
|
12
|
+
* Get a single content type schema by slug.
|
|
13
|
+
*/
|
|
14
|
+
getSchema(typeSlug: string): Promise<SchemaGetResponse>;
|
|
15
|
+
/**
|
|
16
|
+
* Get all published entries of a content type.
|
|
17
|
+
*/
|
|
18
|
+
getEntries<T extends NoMessEntry = NoMessEntry>(contentType: string): Promise<T[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Get a single entry by content type and slug.
|
|
21
|
+
* Supports preview mode with preview secret.
|
|
22
|
+
*/
|
|
23
|
+
getEntry<T extends NoMessEntry = NoMessEntry>(contentType: string, slug: string, options?: GetEntryOptions): Promise<T>;
|
|
24
|
+
/**
|
|
25
|
+
* Get all synced Shopify products.
|
|
26
|
+
*/
|
|
27
|
+
getProducts(): Promise<ShopifyProduct[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Get a single Shopify product by handle.
|
|
30
|
+
*/
|
|
31
|
+
getProduct(handle: string): Promise<ShopifyProduct>;
|
|
32
|
+
/**
|
|
33
|
+
* Get all synced Shopify collections.
|
|
34
|
+
*/
|
|
35
|
+
getCollections(): Promise<ShopifyCollection[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Get a single Shopify collection by handle.
|
|
38
|
+
*/
|
|
39
|
+
getCollection(handle: string): Promise<ShopifyCollection>;
|
|
40
|
+
/**
|
|
41
|
+
* Exchange a preview session for draft content.
|
|
42
|
+
* Computes an HMAC-SHA256 proof and sends it to the server for verification.
|
|
43
|
+
*/
|
|
44
|
+
exchangePreviewSession(session: PreviewSessionAuth): Promise<PreviewExchangeResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Compute HMAC-SHA256 proof for preview session authentication.
|
|
47
|
+
* Uses Web Crypto API (works in browsers, Node.js 18+, Deno, Bun, edge runtimes).
|
|
48
|
+
*/
|
|
49
|
+
private computeProof;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAGpB,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,kBAAkB;YAiBxB,KAAK;IA+BnB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAI/C;;OAEG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAI7D;;OAEG;IACG,UAAU,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAClD,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,CAAC,EAAE,CAAC;IAIf;;;OAGG;IACG,QAAQ,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAChD,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,CAAC,CAAC;IAWb;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAI9C;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAIzD;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAIpD;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAI/D;;;OAGG;IACG,sBAAsB,CAC1B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,qBAAqB,CAAC;IAkCjC;;;OAGG;YACW,YAAY;CA0B3B"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { DEFAULT_API_URL, NoMessError } from "./types.js";
|
|
2
|
+
export class NoMessClient {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
this.apiUrl = (config.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
5
|
+
this.apiKey = config.apiKey;
|
|
6
|
+
if (typeof window !== "undefined" &&
|
|
7
|
+
config.apiKey.startsWith("nm_") &&
|
|
8
|
+
!config.apiKey.startsWith("nm_pub_")) {
|
|
9
|
+
console.warn("[no-mess] You are using a secret API key (nm_) in a browser environment. " +
|
|
10
|
+
"This exposes your secret key to end users. " +
|
|
11
|
+
"Use a publishable key (nm_pub_) for client-side code instead.");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async fetch(path, params) {
|
|
15
|
+
const url = new URL(`${this.apiUrl}${path}`);
|
|
16
|
+
if (params) {
|
|
17
|
+
for (const [key, value] of Object.entries(params)) {
|
|
18
|
+
url.searchParams.set(key, value);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const response = await fetch(url.toString(), {
|
|
22
|
+
headers: {
|
|
23
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
const body = await response
|
|
29
|
+
.json()
|
|
30
|
+
.catch(() => ({ error: "Unknown error" }));
|
|
31
|
+
throw new NoMessError(body.error ?? `HTTP ${response.status}`, response.status);
|
|
32
|
+
}
|
|
33
|
+
return response.json();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* List all content type schemas with fields, TypeScript interfaces, and entry counts.
|
|
37
|
+
*/
|
|
38
|
+
async getSchemas() {
|
|
39
|
+
return this.fetch("/api/schema");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get a single content type schema by slug.
|
|
43
|
+
*/
|
|
44
|
+
async getSchema(typeSlug) {
|
|
45
|
+
return this.fetch(`/api/schema/${typeSlug}`);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get all published entries of a content type.
|
|
49
|
+
*/
|
|
50
|
+
async getEntries(contentType) {
|
|
51
|
+
return this.fetch(`/api/content/${contentType}`);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get a single entry by content type and slug.
|
|
55
|
+
* Supports preview mode with preview secret.
|
|
56
|
+
*/
|
|
57
|
+
async getEntry(contentType, slug, options) {
|
|
58
|
+
const params = {};
|
|
59
|
+
if (options?.preview) {
|
|
60
|
+
params.preview = "true";
|
|
61
|
+
if (options.previewSecret) {
|
|
62
|
+
params.secret = options.previewSecret;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return this.fetch(`/api/content/${contentType}/${slug}`, params);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get all synced Shopify products.
|
|
69
|
+
*/
|
|
70
|
+
async getProducts() {
|
|
71
|
+
return this.fetch("/api/shopify/products");
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get a single Shopify product by handle.
|
|
75
|
+
*/
|
|
76
|
+
async getProduct(handle) {
|
|
77
|
+
return this.fetch(`/api/shopify/products/${handle}`);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get all synced Shopify collections.
|
|
81
|
+
*/
|
|
82
|
+
async getCollections() {
|
|
83
|
+
return this.fetch("/api/shopify/collections");
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get a single Shopify collection by handle.
|
|
87
|
+
*/
|
|
88
|
+
async getCollection(handle) {
|
|
89
|
+
return this.fetch(`/api/shopify/collections/${handle}`);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Exchange a preview session for draft content.
|
|
93
|
+
* Computes an HMAC-SHA256 proof and sends it to the server for verification.
|
|
94
|
+
*/
|
|
95
|
+
async exchangePreviewSession(session) {
|
|
96
|
+
const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
97
|
+
const proof = await this.computeProof(session.sessionSecret, session.sessionId, timestamp);
|
|
98
|
+
const response = await fetch(`${this.apiUrl}/api/preview/exchange`, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: {
|
|
101
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
102
|
+
"Content-Type": "application/json",
|
|
103
|
+
},
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
sessionId: session.sessionId,
|
|
106
|
+
timestamp,
|
|
107
|
+
proof,
|
|
108
|
+
}),
|
|
109
|
+
});
|
|
110
|
+
if (!response.ok) {
|
|
111
|
+
const body = await response
|
|
112
|
+
.json()
|
|
113
|
+
.catch(() => ({ error: "Unknown error" }));
|
|
114
|
+
throw new NoMessError(body.error ?? `HTTP ${response.status}`, response.status);
|
|
115
|
+
}
|
|
116
|
+
return response.json();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Compute HMAC-SHA256 proof for preview session authentication.
|
|
120
|
+
* Uses Web Crypto API (works in browsers, Node.js 18+, Deno, Bun, edge runtimes).
|
|
121
|
+
*/
|
|
122
|
+
async computeProof(sessionSecret, sessionId, timestamp) {
|
|
123
|
+
const secretBytes = new Uint8Array((sessionSecret.match(/.{2}/g) ?? []).map((byte) => parseInt(byte, 16)));
|
|
124
|
+
const buf = new ArrayBuffer(secretBytes.byteLength);
|
|
125
|
+
new Uint8Array(buf).set(secretBytes);
|
|
126
|
+
const key = await crypto.subtle.importKey("raw", buf, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
127
|
+
const message = new TextEncoder().encode(`${sessionId}.${timestamp}`);
|
|
128
|
+
const msgBuf = new ArrayBuffer(message.byteLength);
|
|
129
|
+
new Uint8Array(msgBuf).set(message);
|
|
130
|
+
const signature = await crypto.subtle.sign("HMAC", key, msgBuf);
|
|
131
|
+
return btoa(String.fromCharCode(...new Uint8Array(signature)));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export { NoMessClient } from "./client.js";
|
|
2
|
+
export { createLiveEditHandler } from "./live-edit.js";
|
|
3
|
+
export type { ContentTypeSchema, GetEntryOptions, LiveEditConfig, LiveEditHandle, NoMessClientConfig, NoMessEntry, PreviewExchangeResult, PreviewHandlerConfig, PreviewSessionAuth, SchemaGetResponse, SchemaListResponse, ShopifyCollection, ShopifyProduct, UseNoMessLiveEditConfig, UseNoMessLiveEditResult, UseNoMessPreviewConfig, UseNoMessPreviewResult, } from "./types.js";
|
|
4
|
+
export { DEFAULT_ADMIN_ORIGIN, DEFAULT_API_URL, isPublishableKey, isSecretKey, NoMessError, } from "./types.js";
|
|
5
|
+
import { NoMessClient } from "./client.js";
|
|
6
|
+
import type { NoMessClientConfig, PreviewHandlerConfig } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Create a no-mess client instance.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createNoMessClient(config: NoMessClientConfig): NoMessClient;
|
|
11
|
+
/**
|
|
12
|
+
* Create a preview handler that manages the postMessage handshake
|
|
13
|
+
* between the admin dashboard (parent) and the preview iframe (child).
|
|
14
|
+
*
|
|
15
|
+
* Usage in a client site's preview page:
|
|
16
|
+
* ```ts
|
|
17
|
+
* const handler = createPreviewHandler({
|
|
18
|
+
* client,
|
|
19
|
+
* adminOrigin: "https://admin.no-mess.xyz",
|
|
20
|
+
* onEntry: (entry) => setEntry(entry),
|
|
21
|
+
* onError: (err) => setError(err.message),
|
|
22
|
+
* });
|
|
23
|
+
* handler.start();
|
|
24
|
+
* // On cleanup: handler.cleanup();
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function createPreviewHandler(config: PreviewHandlerConfig): {
|
|
28
|
+
start: () => void;
|
|
29
|
+
cleanup: () => void;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EACV,kBAAkB,EAElB,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAE3E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG;IAClE,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CA2DA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export { NoMessClient } from "./client.js";
|
|
2
|
+
export { createLiveEditHandler } from "./live-edit.js";
|
|
3
|
+
export { DEFAULT_ADMIN_ORIGIN, DEFAULT_API_URL, isPublishableKey, isSecretKey, NoMessError, } from "./types.js";
|
|
4
|
+
import { NoMessClient } from "./client.js";
|
|
5
|
+
/**
|
|
6
|
+
* Create a no-mess client instance.
|
|
7
|
+
*/
|
|
8
|
+
export function createNoMessClient(config) {
|
|
9
|
+
return new NoMessClient(config);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create a preview handler that manages the postMessage handshake
|
|
13
|
+
* between the admin dashboard (parent) and the preview iframe (child).
|
|
14
|
+
*
|
|
15
|
+
* Usage in a client site's preview page:
|
|
16
|
+
* ```ts
|
|
17
|
+
* const handler = createPreviewHandler({
|
|
18
|
+
* client,
|
|
19
|
+
* adminOrigin: "https://admin.no-mess.xyz",
|
|
20
|
+
* onEntry: (entry) => setEntry(entry),
|
|
21
|
+
* onError: (err) => setError(err.message),
|
|
22
|
+
* });
|
|
23
|
+
* handler.start();
|
|
24
|
+
* // On cleanup: handler.cleanup();
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function createPreviewHandler(config) {
|
|
28
|
+
let sessionAuth = null;
|
|
29
|
+
const handleMessage = async (event) => {
|
|
30
|
+
if (event.origin !== config.adminOrigin)
|
|
31
|
+
return;
|
|
32
|
+
const data = event.data;
|
|
33
|
+
if (!data || typeof data.type !== "string")
|
|
34
|
+
return;
|
|
35
|
+
if (data.type === "no-mess:session-auth") {
|
|
36
|
+
sessionAuth = {
|
|
37
|
+
sessionId: data.sessionId,
|
|
38
|
+
sessionSecret: data.sessionSecret,
|
|
39
|
+
};
|
|
40
|
+
try {
|
|
41
|
+
const result = await config.client.exchangePreviewSession(sessionAuth);
|
|
42
|
+
config.onEntry(result.entry);
|
|
43
|
+
window.parent.postMessage({ type: "no-mess:preview-loaded" }, event.origin);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
47
|
+
config.onError?.(error);
|
|
48
|
+
window.parent.postMessage({ type: "no-mess:preview-error", error: error.message }, event.origin);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (data.type === "no-mess:refresh" && sessionAuth) {
|
|
52
|
+
try {
|
|
53
|
+
const result = await config.client.exchangePreviewSession(sessionAuth);
|
|
54
|
+
config.onEntry(result.entry);
|
|
55
|
+
window.parent.postMessage({ type: "no-mess:preview-loaded" }, event.origin);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
59
|
+
config.onError?.(error);
|
|
60
|
+
window.parent.postMessage({ type: "no-mess:preview-error", error: error.message }, event.origin);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
start: () => {
|
|
66
|
+
window.addEventListener("message", handleMessage);
|
|
67
|
+
window.parent.postMessage({ type: "no-mess:preview-ready" }, "*");
|
|
68
|
+
},
|
|
69
|
+
cleanup: () => {
|
|
70
|
+
window.removeEventListener("message", handleMessage);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LiveEditConfig, LiveEditHandle } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Create a live edit handler that manages overlay highlights on annotated DOM
|
|
4
|
+
* elements and communicates with the admin dashboard via postMessage.
|
|
5
|
+
*
|
|
6
|
+
* Usage in a client site's preview page:
|
|
7
|
+
* ```ts
|
|
8
|
+
* const handle = createLiveEditHandler({
|
|
9
|
+
* adminOrigin: "https://admin.no-mess.xyz",
|
|
10
|
+
* });
|
|
11
|
+
* // On cleanup: handle.cleanup();
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare function createLiveEditHandler(config: LiveEditConfig): LiveEditHandle;
|
|
15
|
+
//# sourceMappingURL=live-edit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-edit.d.ts","sourceRoot":"","sources":["../src/live-edit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAyDjE;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CA0Q5E"}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
const OVERLAY_CONTAINER_ID = "no-mess-live-edit-overlays";
|
|
2
|
+
const OVERLAY_ATTR = "data-no-mess-overlay-for";
|
|
3
|
+
const FIELD_ATTR = "data-no-mess-field";
|
|
4
|
+
const OVERLAY_CSS = `
|
|
5
|
+
#${OVERLAY_CONTAINER_ID} {
|
|
6
|
+
pointer-events: none;
|
|
7
|
+
position: fixed;
|
|
8
|
+
top: 0;
|
|
9
|
+
left: 0;
|
|
10
|
+
width: 100%;
|
|
11
|
+
height: 100%;
|
|
12
|
+
z-index: 2147483646;
|
|
13
|
+
}
|
|
14
|
+
.no-mess-overlay {
|
|
15
|
+
position: absolute;
|
|
16
|
+
border: 2px solid oklch(0.623 0.214 259.1);
|
|
17
|
+
border-radius: 4px;
|
|
18
|
+
pointer-events: auto;
|
|
19
|
+
cursor: pointer;
|
|
20
|
+
transition: border-color 150ms, background-color 150ms;
|
|
21
|
+
}
|
|
22
|
+
.no-mess-overlay:hover {
|
|
23
|
+
background-color: oklch(0.623 0.214 259.1 / 0.08);
|
|
24
|
+
}
|
|
25
|
+
.no-mess-overlay[data-focused="true"] {
|
|
26
|
+
border-color: oklch(0.546 0.245 262.9);
|
|
27
|
+
background-color: oklch(0.546 0.245 262.9 / 0.12);
|
|
28
|
+
border-width: 3px;
|
|
29
|
+
}
|
|
30
|
+
.no-mess-overlay-label {
|
|
31
|
+
position: absolute;
|
|
32
|
+
top: -22px;
|
|
33
|
+
left: -2px;
|
|
34
|
+
background: oklch(0.623 0.214 259.1);
|
|
35
|
+
color: white;
|
|
36
|
+
font-size: 11px;
|
|
37
|
+
font-family: system-ui, sans-serif;
|
|
38
|
+
line-height: 1;
|
|
39
|
+
padding: 3px 6px;
|
|
40
|
+
border-radius: 3px 3px 0 0;
|
|
41
|
+
white-space: nowrap;
|
|
42
|
+
pointer-events: none;
|
|
43
|
+
}
|
|
44
|
+
.no-mess-overlay[data-focused="true"] .no-mess-overlay-label {
|
|
45
|
+
background: oklch(0.546 0.245 262.9);
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
/**
|
|
49
|
+
* Create a live edit handler that manages overlay highlights on annotated DOM
|
|
50
|
+
* elements and communicates with the admin dashboard via postMessage.
|
|
51
|
+
*
|
|
52
|
+
* Usage in a client site's preview page:
|
|
53
|
+
* ```ts
|
|
54
|
+
* const handle = createLiveEditHandler({
|
|
55
|
+
* adminOrigin: "https://admin.no-mess.xyz",
|
|
56
|
+
* });
|
|
57
|
+
* // On cleanup: handle.cleanup();
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function createLiveEditHandler(config) {
|
|
61
|
+
let active = false;
|
|
62
|
+
let container = null;
|
|
63
|
+
let styleEl = null;
|
|
64
|
+
let overlays = [];
|
|
65
|
+
let resizeObserver = null;
|
|
66
|
+
let mutationObserver = null;
|
|
67
|
+
let scrollListener = null;
|
|
68
|
+
let rafId = null;
|
|
69
|
+
function injectStyles() {
|
|
70
|
+
if (document.getElementById("no-mess-live-edit-styles"))
|
|
71
|
+
return;
|
|
72
|
+
styleEl = document.createElement("style");
|
|
73
|
+
styleEl.id = "no-mess-live-edit-styles";
|
|
74
|
+
styleEl.textContent = OVERLAY_CSS;
|
|
75
|
+
document.head.appendChild(styleEl);
|
|
76
|
+
}
|
|
77
|
+
function createContainer() {
|
|
78
|
+
container = document.createElement("div");
|
|
79
|
+
container.id = OVERLAY_CONTAINER_ID;
|
|
80
|
+
document.body.appendChild(container);
|
|
81
|
+
}
|
|
82
|
+
function getFieldElements() {
|
|
83
|
+
const map = new Map();
|
|
84
|
+
const elements = document.querySelectorAll(`[${FIELD_ATTR}]`);
|
|
85
|
+
for (const el of elements) {
|
|
86
|
+
const fieldName = el.getAttribute(FIELD_ATTR);
|
|
87
|
+
if (!fieldName)
|
|
88
|
+
continue;
|
|
89
|
+
const existing = map.get(fieldName);
|
|
90
|
+
if (existing) {
|
|
91
|
+
existing.push(el);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
map.set(fieldName, [el]);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return map;
|
|
98
|
+
}
|
|
99
|
+
function positionOverlay(overlay, element) {
|
|
100
|
+
const rect = element.getBoundingClientRect();
|
|
101
|
+
overlay.style.top = `${rect.top}px`;
|
|
102
|
+
overlay.style.left = `${rect.left}px`;
|
|
103
|
+
overlay.style.width = `${rect.width}px`;
|
|
104
|
+
overlay.style.height = `${rect.height}px`;
|
|
105
|
+
}
|
|
106
|
+
function updateAllPositions() {
|
|
107
|
+
for (const entry of overlays) {
|
|
108
|
+
positionOverlay(entry.overlay, entry.element);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function schedulePositionUpdate() {
|
|
112
|
+
if (rafId !== null)
|
|
113
|
+
return;
|
|
114
|
+
rafId = requestAnimationFrame(() => {
|
|
115
|
+
rafId = null;
|
|
116
|
+
updateAllPositions();
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function buildOverlays() {
|
|
120
|
+
// Clear existing overlays
|
|
121
|
+
for (const entry of overlays) {
|
|
122
|
+
entry.overlay.remove();
|
|
123
|
+
}
|
|
124
|
+
overlays = [];
|
|
125
|
+
if (!container)
|
|
126
|
+
return;
|
|
127
|
+
const fieldElements = getFieldElements();
|
|
128
|
+
const fieldRects = [];
|
|
129
|
+
for (const [fieldName, elements] of fieldElements) {
|
|
130
|
+
for (const element of elements) {
|
|
131
|
+
const overlay = document.createElement("div");
|
|
132
|
+
overlay.className = "no-mess-overlay";
|
|
133
|
+
overlay.setAttribute(OVERLAY_ATTR, fieldName);
|
|
134
|
+
const label = document.createElement("span");
|
|
135
|
+
label.className = "no-mess-overlay-label";
|
|
136
|
+
label.textContent = fieldName;
|
|
137
|
+
overlay.appendChild(label);
|
|
138
|
+
positionOverlay(overlay, element);
|
|
139
|
+
overlay.addEventListener("click", () => {
|
|
140
|
+
window.parent.postMessage({ type: "no-mess:field-clicked", fieldName }, config.adminOrigin);
|
|
141
|
+
config.onFieldClicked?.(fieldName);
|
|
142
|
+
});
|
|
143
|
+
container.appendChild(overlay);
|
|
144
|
+
overlays.push({ element, overlay, fieldName });
|
|
145
|
+
const rect = element.getBoundingClientRect();
|
|
146
|
+
fieldRects.push({ fieldName, rect: rect.toJSON() });
|
|
147
|
+
// Observe element for size changes
|
|
148
|
+
resizeObserver?.observe(element);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Send field map to admin
|
|
152
|
+
const uniqueFields = [...new Set(fieldRects.map((f) => f.fieldName))];
|
|
153
|
+
window.parent.postMessage({
|
|
154
|
+
type: "no-mess:field-map",
|
|
155
|
+
fields: uniqueFields.map((fieldName) => {
|
|
156
|
+
const match = fieldRects.find((f) => f.fieldName === fieldName);
|
|
157
|
+
return { fieldName, rect: match?.rect };
|
|
158
|
+
}),
|
|
159
|
+
}, config.adminOrigin);
|
|
160
|
+
}
|
|
161
|
+
function enterLiveEdit() {
|
|
162
|
+
if (active)
|
|
163
|
+
return;
|
|
164
|
+
active = true;
|
|
165
|
+
injectStyles();
|
|
166
|
+
createContainer();
|
|
167
|
+
resizeObserver = new ResizeObserver(() => {
|
|
168
|
+
schedulePositionUpdate();
|
|
169
|
+
});
|
|
170
|
+
buildOverlays();
|
|
171
|
+
// Track scroll for repositioning
|
|
172
|
+
scrollListener = () => schedulePositionUpdate();
|
|
173
|
+
window.addEventListener("scroll", scrollListener, { passive: true });
|
|
174
|
+
window.addEventListener("resize", scrollListener, { passive: true });
|
|
175
|
+
// Track DOM mutations for SPA navigation
|
|
176
|
+
mutationObserver = new MutationObserver(() => {
|
|
177
|
+
// Debounce rescan slightly for batch mutations
|
|
178
|
+
setTimeout(() => {
|
|
179
|
+
if (active)
|
|
180
|
+
buildOverlays();
|
|
181
|
+
}, 100);
|
|
182
|
+
});
|
|
183
|
+
mutationObserver.observe(document.body, {
|
|
184
|
+
childList: true,
|
|
185
|
+
subtree: true,
|
|
186
|
+
});
|
|
187
|
+
config.onEnter?.();
|
|
188
|
+
}
|
|
189
|
+
function exitLiveEdit() {
|
|
190
|
+
if (!active)
|
|
191
|
+
return;
|
|
192
|
+
active = false;
|
|
193
|
+
if (rafId !== null) {
|
|
194
|
+
cancelAnimationFrame(rafId);
|
|
195
|
+
rafId = null;
|
|
196
|
+
}
|
|
197
|
+
resizeObserver?.disconnect();
|
|
198
|
+
resizeObserver = null;
|
|
199
|
+
mutationObserver?.disconnect();
|
|
200
|
+
mutationObserver = null;
|
|
201
|
+
if (scrollListener) {
|
|
202
|
+
window.removeEventListener("scroll", scrollListener);
|
|
203
|
+
window.removeEventListener("resize", scrollListener);
|
|
204
|
+
scrollListener = null;
|
|
205
|
+
}
|
|
206
|
+
for (const entry of overlays) {
|
|
207
|
+
entry.overlay.remove();
|
|
208
|
+
}
|
|
209
|
+
overlays = [];
|
|
210
|
+
container?.remove();
|
|
211
|
+
container = null;
|
|
212
|
+
styleEl?.remove();
|
|
213
|
+
styleEl = null;
|
|
214
|
+
config.onExit?.();
|
|
215
|
+
}
|
|
216
|
+
function handleFieldUpdate(fieldName, value) {
|
|
217
|
+
const elements = document.querySelectorAll(`[${FIELD_ATTR}="${fieldName}"]`);
|
|
218
|
+
for (const el of elements) {
|
|
219
|
+
if (el instanceof HTMLImageElement && typeof value === "string") {
|
|
220
|
+
el.src = value;
|
|
221
|
+
}
|
|
222
|
+
else if (el instanceof HTMLElement &&
|
|
223
|
+
el.style.backgroundImage &&
|
|
224
|
+
typeof value === "string") {
|
|
225
|
+
el.style.backgroundImage = `url(${value})`;
|
|
226
|
+
}
|
|
227
|
+
else if (typeof value === "string") {
|
|
228
|
+
el.textContent = value;
|
|
229
|
+
}
|
|
230
|
+
else if (typeof value === "number" || typeof value === "boolean") {
|
|
231
|
+
el.textContent = String(value);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function handleFieldFocus(fieldName) {
|
|
236
|
+
for (const entry of overlays) {
|
|
237
|
+
if (entry.fieldName === fieldName) {
|
|
238
|
+
entry.overlay.setAttribute("data-focused", "true");
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function handleFieldBlur(fieldName) {
|
|
243
|
+
for (const entry of overlays) {
|
|
244
|
+
if (entry.fieldName === fieldName) {
|
|
245
|
+
entry.overlay.removeAttribute("data-focused");
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function handleMessage(event) {
|
|
250
|
+
if (event.origin !== config.adminOrigin)
|
|
251
|
+
return;
|
|
252
|
+
const data = event.data;
|
|
253
|
+
if (!data || typeof data.type !== "string")
|
|
254
|
+
return;
|
|
255
|
+
switch (data.type) {
|
|
256
|
+
case "no-mess:live-edit-enter":
|
|
257
|
+
enterLiveEdit();
|
|
258
|
+
break;
|
|
259
|
+
case "no-mess:live-edit-exit":
|
|
260
|
+
exitLiveEdit();
|
|
261
|
+
break;
|
|
262
|
+
case "no-mess:field-updated":
|
|
263
|
+
if (data.fieldName && active) {
|
|
264
|
+
handleFieldUpdate(data.fieldName, data.value);
|
|
265
|
+
}
|
|
266
|
+
break;
|
|
267
|
+
case "no-mess:field-focus":
|
|
268
|
+
if (data.fieldName && active) {
|
|
269
|
+
handleFieldFocus(data.fieldName);
|
|
270
|
+
}
|
|
271
|
+
break;
|
|
272
|
+
case "no-mess:field-blur":
|
|
273
|
+
if (data.fieldName && active) {
|
|
274
|
+
handleFieldBlur(data.fieldName);
|
|
275
|
+
}
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Start listening immediately
|
|
280
|
+
window.addEventListener("message", handleMessage);
|
|
281
|
+
return {
|
|
282
|
+
cleanup: () => {
|
|
283
|
+
window.removeEventListener("message", handleMessage);
|
|
284
|
+
exitLiveEdit();
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=live-edit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-edit.js","sourceRoot":"","sources":["../src/live-edit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { NoMessEntry, UseNoMessLiveEditConfig, UseNoMessLiveEditResult, UseNoMessPreviewConfig, UseNoMessPreviewResult, } from "../types.js";
|
|
2
|
+
export type { NoMessPreviewProps } from "./no-mess-preview.js";
|
|
3
|
+
export { NoMessPreview } from "./no-mess-preview.js";
|
|
4
|
+
export { useNoMessLiveEdit } from "./use-no-mess-live-edit.js";
|
|
5
|
+
export { useNoMessPreview } from "./use-no-mess-preview.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,WAAW,EACX,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAUb,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { NoMessEntry, UseNoMessPreviewConfig } from "../types.js";
|
|
3
|
+
export interface NoMessPreviewProps<T extends NoMessEntry = NoMessEntry> extends UseNoMessPreviewConfig {
|
|
4
|
+
children: (state: {
|
|
5
|
+
entry: T | null;
|
|
6
|
+
error: Error | null;
|
|
7
|
+
isLoading: boolean;
|
|
8
|
+
}) => ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export declare function NoMessPreview<T extends NoMessEntry = NoMessEntry>({ children, ...config }: NoMessPreviewProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=no-mess-preview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-mess-preview.d.ts","sourceRoot":"","sources":["../../src/react/no-mess-preview.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGvE,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,CACrE,SAAQ,sBAAsB;IAC9B,QAAQ,EAAE,CAAC,KAAK,EAAE;QAChB,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;QAChB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;QACpB,SAAS,EAAE,OAAO,CAAC;KACpB,KAAK,SAAS,CAAC;CACjB;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,EACjE,QAAQ,EACR,GAAG,MAAM,EACV,EAAE,kBAAkB,CAAC,CAAC,CAAC,2CAGvB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useNoMessPreview } from "./use-no-mess-preview.js";
|
|
4
|
+
export function NoMessPreview({ children, ...config }) {
|
|
5
|
+
const state = useNoMessPreview(config);
|
|
6
|
+
return _jsx(_Fragment, { children: children(state) });
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=no-mess-preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-mess-preview.js","sourceRoot":"","sources":["../../src/react/no-mess-preview.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAIb,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAW5D,MAAM,UAAU,aAAa,CAAsC,EACjE,QAAQ,EACR,GAAG,MAAM,EACa;IACtB,MAAM,KAAK,GAAG,gBAAgB,CAAI,MAAM,CAAC,CAAC;IAC1C,OAAO,4BAAG,QAAQ,CAAC,KAAK,CAAC,GAAI,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { UseNoMessLiveEditConfig, UseNoMessLiveEditResult } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* React hook for live edit mode. When the admin dashboard sends a
|
|
4
|
+
* `no-mess:live-edit-enter` message, this hook activates overlays on
|
|
5
|
+
* elements with `data-no-mess-field` attributes and tracks field value
|
|
6
|
+
* overrides sent from the admin.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const { isLiveEditActive, fieldOverrides } = useNoMessLiveEdit({
|
|
11
|
+
* adminOrigin: "https://admin.no-mess.xyz",
|
|
12
|
+
* });
|
|
13
|
+
* const title = (fieldOverrides.title as string) ?? entry?.title;
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare function useNoMessLiveEdit(config: UseNoMessLiveEditConfig): UseNoMessLiveEditResult;
|
|
17
|
+
//# sourceMappingURL=use-no-mess-live-edit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-no-mess-live-edit.d.ts","sourceRoot":"","sources":["../../src/react/use-no-mess-live-edit.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,aAAa,CAAC;AAGrB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,uBAAuB,GAC9B,uBAAuB,CAiDzB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { createLiveEditHandler } from "../live-edit.js";
|
|
4
|
+
import { DEFAULT_ADMIN_ORIGIN } from "../types.js";
|
|
5
|
+
/**
|
|
6
|
+
* React hook for live edit mode. When the admin dashboard sends a
|
|
7
|
+
* `no-mess:live-edit-enter` message, this hook activates overlays on
|
|
8
|
+
* elements with `data-no-mess-field` attributes and tracks field value
|
|
9
|
+
* overrides sent from the admin.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const { isLiveEditActive, fieldOverrides } = useNoMessLiveEdit({
|
|
14
|
+
* adminOrigin: "https://admin.no-mess.xyz",
|
|
15
|
+
* });
|
|
16
|
+
* const title = (fieldOverrides.title as string) ?? entry?.title;
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function useNoMessLiveEdit(config) {
|
|
20
|
+
const [isLiveEditActive, setIsLiveEditActive] = useState(false);
|
|
21
|
+
const [fieldOverrides, setFieldOverrides] = useState({});
|
|
22
|
+
const configRef = useRef(config);
|
|
23
|
+
configRef.current = config;
|
|
24
|
+
const handleFieldUpdate = useCallback((event) => {
|
|
25
|
+
const origin = configRef.current.adminOrigin ?? DEFAULT_ADMIN_ORIGIN;
|
|
26
|
+
if (event.origin !== origin)
|
|
27
|
+
return;
|
|
28
|
+
const data = event.data;
|
|
29
|
+
if (!data || typeof data.type !== "string")
|
|
30
|
+
return;
|
|
31
|
+
if (data.type === "no-mess:field-updated" && data.fieldName) {
|
|
32
|
+
setFieldOverrides((prev) => ({
|
|
33
|
+
...prev,
|
|
34
|
+
[data.fieldName]: data.value,
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
}, []);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const adminOrigin = configRef.current.adminOrigin ?? DEFAULT_ADMIN_ORIGIN;
|
|
40
|
+
const handle = createLiveEditHandler({
|
|
41
|
+
adminOrigin,
|
|
42
|
+
onEnter: () => {
|
|
43
|
+
setIsLiveEditActive(true);
|
|
44
|
+
setFieldOverrides({});
|
|
45
|
+
},
|
|
46
|
+
onExit: () => {
|
|
47
|
+
setIsLiveEditActive(false);
|
|
48
|
+
setFieldOverrides({});
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
// Also listen for field-updated to track overrides in React state
|
|
52
|
+
window.addEventListener("message", handleFieldUpdate);
|
|
53
|
+
return () => {
|
|
54
|
+
handle.cleanup();
|
|
55
|
+
window.removeEventListener("message", handleFieldUpdate);
|
|
56
|
+
};
|
|
57
|
+
}, [handleFieldUpdate]);
|
|
58
|
+
return { isLiveEditActive, fieldOverrides };
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=use-no-mess-live-edit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-no-mess-live-edit.js","sourceRoot":"","sources":["../../src/react/use-no-mess-live-edit.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAKxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAA+B;IAE/B,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChE,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAClD,EAAE,CACH,CAAC;IAEF,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;IAE3B,MAAM,iBAAiB,GAAG,WAAW,CAAC,CAAC,KAAmB,EAAE,EAAE;QAC5D,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,WAAW,IAAI,oBAAoB,CAAC;QACrE,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM;YAAE,OAAO;QAEpC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACxB,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO;QAEnD,IAAI,IAAI,CAAC,IAAI,KAAK,uBAAuB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC5D,iBAAiB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC3B,GAAG,IAAI;gBACP,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,KAAK;aAC7B,CAAC,CAAC,CAAC;QACN,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,WAAW,IAAI,oBAAoB,CAAC;QAE1E,MAAM,MAAM,GAAG,qBAAqB,CAAC;YACnC,WAAW;YACX,OAAO,EAAE,GAAG,EAAE;gBACZ,mBAAmB,CAAC,IAAI,CAAC,CAAC;gBAC1B,iBAAiB,CAAC,EAAE,CAAC,CAAC;YACxB,CAAC;YACD,MAAM,EAAE,GAAG,EAAE;gBACX,mBAAmB,CAAC,KAAK,CAAC,CAAC;gBAC3B,iBAAiB,CAAC,EAAE,CAAC,CAAC;YACxB,CAAC;SACF,CAAC,CAAC;QAEH,kEAAkE;QAClE,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;QAEtD,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;QAC3D,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAExB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { NoMessEntry, UseNoMessPreviewConfig, UseNoMessPreviewResult } from "../types.js";
|
|
2
|
+
export declare function useNoMessPreview<T extends NoMessEntry = NoMessEntry>(config: UseNoMessPreviewConfig): UseNoMessPreviewResult<T>;
|
|
3
|
+
//# sourceMappingURL=use-no-mess-preview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-no-mess-preview.d.ts","sourceRoot":"","sources":["../../src/react/use-no-mess-preview.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,WAAW,EACX,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAGrB,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAClE,MAAM,EAAE,sBAAsB,GAC7B,sBAAsB,CAAC,CAAC,CAAC,CAiC3B"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { NoMessClient } from "../client.js";
|
|
4
|
+
import { createPreviewHandler } from "../index.js";
|
|
5
|
+
import { DEFAULT_ADMIN_ORIGIN } from "../types.js";
|
|
6
|
+
export function useNoMessPreview(config) {
|
|
7
|
+
const [entry, setEntry] = useState(null);
|
|
8
|
+
const [error, setError] = useState(null);
|
|
9
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
10
|
+
const configRef = useRef(config);
|
|
11
|
+
configRef.current = config;
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const client = new NoMessClient({
|
|
14
|
+
apiKey: configRef.current.apiKey,
|
|
15
|
+
apiUrl: configRef.current.apiUrl,
|
|
16
|
+
});
|
|
17
|
+
const handler = createPreviewHandler({
|
|
18
|
+
client,
|
|
19
|
+
adminOrigin: configRef.current.adminOrigin ?? DEFAULT_ADMIN_ORIGIN,
|
|
20
|
+
onEntry: (e) => {
|
|
21
|
+
setEntry(e);
|
|
22
|
+
setError(null);
|
|
23
|
+
setIsLoading(false);
|
|
24
|
+
},
|
|
25
|
+
onError: (err) => {
|
|
26
|
+
setError(err);
|
|
27
|
+
setIsLoading(false);
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
handler.start();
|
|
31
|
+
return () => handler.cleanup();
|
|
32
|
+
}, []);
|
|
33
|
+
return { entry, error, isLoading };
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=use-no-mess-preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-no-mess-preview.js","sourceRoot":"","sources":["../../src/react/use-no-mess-preview.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAMnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,UAAU,gBAAgB,CAC9B,MAA8B;IAE9B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAW,IAAI,CAAC,CAAC;IACnD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IACvD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAEjD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;IAE3B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC;YAC9B,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,MAAM;YAChC,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,MAAM;SACjC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,oBAAoB,CAAC;YACnC,MAAM;YACN,WAAW,EAAE,SAAS,CAAC,OAAO,CAAC,WAAW,IAAI,oBAAoB;YAClE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;gBACb,QAAQ,CAAC,CAAM,CAAC,CAAC;gBACjB,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;YACD,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACf,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACd,YAAY,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;SACF,CAAC,CAAC;QAEH,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AACrC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
export declare const DEFAULT_API_URL = "https://api.no-mess.xyz";
|
|
2
|
+
export declare const DEFAULT_ADMIN_ORIGIN = "https://admin.no-mess.xyz";
|
|
3
|
+
export interface NoMessClientConfig {
|
|
4
|
+
apiUrl?: string;
|
|
5
|
+
/**
|
|
6
|
+
* API key for authenticating requests to the no-mess API.
|
|
7
|
+
* Accepts either a secret key (`nm_...`) or a publishable key (`nm_pub_...`).
|
|
8
|
+
*
|
|
9
|
+
* - **Secret key**: Server-side only. Full access. Never expose in client-side code.
|
|
10
|
+
* - **Publishable key**: Safe for client-side use. Read-only access to published content.
|
|
11
|
+
*/
|
|
12
|
+
apiKey: string;
|
|
13
|
+
}
|
|
14
|
+
/** Returns true if the key is a publishable key (nm_pub_ prefix). */
|
|
15
|
+
export declare function isPublishableKey(key: string): boolean;
|
|
16
|
+
/** Returns true if the key is a secret key (nm_ prefix, not nm_pub_). */
|
|
17
|
+
export declare function isSecretKey(key: string): boolean;
|
|
18
|
+
export interface GetEntryOptions {
|
|
19
|
+
preview?: boolean;
|
|
20
|
+
previewSecret?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface NoMessEntry {
|
|
23
|
+
slug: string;
|
|
24
|
+
title: string;
|
|
25
|
+
_id: string;
|
|
26
|
+
_createdAt: number;
|
|
27
|
+
_updatedAt: number;
|
|
28
|
+
_publishedAt?: number;
|
|
29
|
+
_status?: string;
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
export interface ShopifyProduct {
|
|
33
|
+
handle: string;
|
|
34
|
+
title: string;
|
|
35
|
+
status: string;
|
|
36
|
+
featuredImage?: string;
|
|
37
|
+
priceRange: {
|
|
38
|
+
min: string;
|
|
39
|
+
max: string;
|
|
40
|
+
};
|
|
41
|
+
available: boolean;
|
|
42
|
+
images?: {
|
|
43
|
+
id: string;
|
|
44
|
+
src: string;
|
|
45
|
+
alt?: string;
|
|
46
|
+
}[];
|
|
47
|
+
variants?: {
|
|
48
|
+
id: string;
|
|
49
|
+
title: string;
|
|
50
|
+
sku?: string;
|
|
51
|
+
price: string;
|
|
52
|
+
compareAtPrice?: string;
|
|
53
|
+
available: boolean;
|
|
54
|
+
}[];
|
|
55
|
+
productType?: string;
|
|
56
|
+
vendor?: string;
|
|
57
|
+
tags?: string[];
|
|
58
|
+
}
|
|
59
|
+
export interface ShopifyCollection {
|
|
60
|
+
handle: string;
|
|
61
|
+
title: string;
|
|
62
|
+
image?: string;
|
|
63
|
+
productsCount: number;
|
|
64
|
+
}
|
|
65
|
+
export interface PreviewSessionAuth {
|
|
66
|
+
sessionId: string;
|
|
67
|
+
sessionSecret: string;
|
|
68
|
+
}
|
|
69
|
+
export interface PreviewExchangeResult {
|
|
70
|
+
entry: NoMessEntry;
|
|
71
|
+
sessionId: string;
|
|
72
|
+
expiresAt: number;
|
|
73
|
+
}
|
|
74
|
+
export interface PreviewHandlerConfig {
|
|
75
|
+
client: {
|
|
76
|
+
exchangePreviewSession: (session: PreviewSessionAuth) => Promise<PreviewExchangeResult>;
|
|
77
|
+
};
|
|
78
|
+
adminOrigin: string;
|
|
79
|
+
onEntry: (entry: NoMessEntry) => void;
|
|
80
|
+
onError?: (error: Error) => void;
|
|
81
|
+
}
|
|
82
|
+
export interface UseNoMessPreviewConfig {
|
|
83
|
+
apiKey: string;
|
|
84
|
+
apiUrl?: string;
|
|
85
|
+
adminOrigin?: string;
|
|
86
|
+
}
|
|
87
|
+
export interface UseNoMessPreviewResult<T extends NoMessEntry = NoMessEntry> {
|
|
88
|
+
entry: T | null;
|
|
89
|
+
error: Error | null;
|
|
90
|
+
isLoading: boolean;
|
|
91
|
+
}
|
|
92
|
+
export interface ContentTypeSchema {
|
|
93
|
+
name: string;
|
|
94
|
+
slug: string;
|
|
95
|
+
description?: string;
|
|
96
|
+
fields: {
|
|
97
|
+
name: string;
|
|
98
|
+
type: string;
|
|
99
|
+
required: boolean;
|
|
100
|
+
description?: string;
|
|
101
|
+
options?: {
|
|
102
|
+
choices?: {
|
|
103
|
+
label: string;
|
|
104
|
+
value: string;
|
|
105
|
+
}[];
|
|
106
|
+
};
|
|
107
|
+
}[];
|
|
108
|
+
fieldTypeMap: {
|
|
109
|
+
name: string;
|
|
110
|
+
type: string;
|
|
111
|
+
tsType: string;
|
|
112
|
+
required: boolean;
|
|
113
|
+
}[];
|
|
114
|
+
typescript: string;
|
|
115
|
+
entryCounts: {
|
|
116
|
+
published: number;
|
|
117
|
+
draft: number;
|
|
118
|
+
total: number;
|
|
119
|
+
};
|
|
120
|
+
endpoints: {
|
|
121
|
+
list: string;
|
|
122
|
+
get: string;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export interface SchemaListResponse {
|
|
126
|
+
site: {
|
|
127
|
+
name: string;
|
|
128
|
+
slug: string;
|
|
129
|
+
};
|
|
130
|
+
contentTypes: ContentTypeSchema[];
|
|
131
|
+
sdkExample: string;
|
|
132
|
+
}
|
|
133
|
+
export interface SchemaGetResponse {
|
|
134
|
+
site: {
|
|
135
|
+
name: string;
|
|
136
|
+
slug: string;
|
|
137
|
+
};
|
|
138
|
+
contentType: ContentTypeSchema;
|
|
139
|
+
sdkExample: string;
|
|
140
|
+
}
|
|
141
|
+
export interface LiveEditFieldInfo {
|
|
142
|
+
name: string;
|
|
143
|
+
type: string;
|
|
144
|
+
required: boolean;
|
|
145
|
+
}
|
|
146
|
+
export interface LiveEditConfig {
|
|
147
|
+
adminOrigin: string;
|
|
148
|
+
onFieldClicked?: (fieldName: string) => void;
|
|
149
|
+
onEnter?: () => void;
|
|
150
|
+
onExit?: () => void;
|
|
151
|
+
}
|
|
152
|
+
export interface LiveEditHandle {
|
|
153
|
+
cleanup: () => void;
|
|
154
|
+
}
|
|
155
|
+
export interface UseNoMessLiveEditConfig {
|
|
156
|
+
adminOrigin?: string;
|
|
157
|
+
}
|
|
158
|
+
export interface UseNoMessLiveEditResult {
|
|
159
|
+
isLiveEditActive: boolean;
|
|
160
|
+
fieldOverrides: Record<string, unknown>;
|
|
161
|
+
}
|
|
162
|
+
export declare class NoMessError extends Error {
|
|
163
|
+
status: number;
|
|
164
|
+
constructor(message: string, status: number);
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const DEFAULT_API_URL = "https://api.no-mess.xyz";
|
|
2
|
+
export const DEFAULT_ADMIN_ORIGIN = "https://admin.no-mess.xyz";
|
|
3
|
+
/** Returns true if the key is a publishable key (nm_pub_ prefix). */
|
|
4
|
+
export function isPublishableKey(key) {
|
|
5
|
+
return key.startsWith("nm_pub_");
|
|
6
|
+
}
|
|
7
|
+
/** Returns true if the key is a secret key (nm_ prefix, not nm_pub_). */
|
|
8
|
+
export function isSecretKey(key) {
|
|
9
|
+
return key.startsWith("nm_") && !key.startsWith("nm_pub_");
|
|
10
|
+
}
|
|
11
|
+
export class NoMessError extends Error {
|
|
12
|
+
constructor(message, status) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "NoMessError";
|
|
15
|
+
this.status = status;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,yBAAyB,CAAC;AACzD,MAAM,CAAC,MAAM,oBAAoB,GAAG,2BAA2B,CAAC;AAchE,qEAAqE;AACrE,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;AACnC,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;AAC7D,CAAC;AAuJD,MAAM,OAAO,WAAY,SAAQ,KAAK;IAGpC,YAAY,OAAe,EAAE,MAAc;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@no-mess/client",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "TypeScript SDK for the no-mess headless CMS",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./react": {
|
|
14
|
+
"import": "./dist/react/index.js",
|
|
15
|
+
"types": "./dist/react/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc",
|
|
23
|
+
"test": "vitest run"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": ">=18.0.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependenciesMeta": {
|
|
29
|
+
"react": {
|
|
30
|
+
"optional": true
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/jjjjjjjjjjjjjjjjacob/no-mess.git",
|
|
36
|
+
"directory": "packages/no-mess-client"
|
|
37
|
+
},
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@testing-library/react": "^16.0.0",
|
|
44
|
+
"@types/react": "^19",
|
|
45
|
+
"@types/react-dom": "^19",
|
|
46
|
+
"jsdom": "^26.0.0",
|
|
47
|
+
"react": "^19.0.0",
|
|
48
|
+
"react-dom": "^19.0.0",
|
|
49
|
+
"typescript": "^5.0.0",
|
|
50
|
+
"vitest": "^3.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|