@squaredr/fieldcraft-pro 1.6.4 → 1.8.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/LICENSE.txt +51 -51
- package/README.md +139 -139
- package/dist/{chunk-VECQKSWS.mjs → chunk-4MMKB2EW.mjs} +60 -86
- package/dist/{chunk-CFVT2R42.mjs → chunk-CYXJGOQH.mjs} +904 -206
- package/dist/{chunk-ANBMT5AW.mjs → chunk-GXKQCXIB.mjs} +470 -228
- package/dist/chunk-IW654Z3M.mjs +1108 -0
- package/dist/{chunk-5LKGCZAW.mjs → chunk-MLP5EDWP.mjs} +167 -178
- package/dist/form-builder/index.d.mts +176 -6
- package/dist/form-builder/index.d.ts +176 -6
- package/dist/form-builder/index.js +604 -365
- package/dist/form-builder/index.mjs +2 -2
- package/dist/index.d.mts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +3221 -984
- package/dist/index.mjs +332 -125
- package/dist/response-viewer/index.d.mts +1 -1
- package/dist/response-viewer/index.d.ts +1 -1
- package/dist/response-viewer/index.js +169 -204
- package/dist/response-viewer/index.mjs +2 -2
- package/dist/styles.css +1 -1
- package/dist/templates/index.d.mts +43 -0
- package/dist/templates/index.d.ts +43 -0
- package/dist/templates/index.js +1116 -0
- package/dist/templates/index.mjs +1 -0
- package/dist/theme-editor/index.d.mts +11 -5
- package/dist/theme-editor/index.d.ts +11 -5
- package/dist/theme-editor/index.js +907 -232
- package/dist/theme-editor/index.mjs +2 -2
- package/dist/types-bFHsZT1n.d.mts +217 -0
- package/dist/types-bFHsZT1n.d.ts +217 -0
- package/package.json +23 -11
- package/theme-editor/styles.css +468 -466
- package/dist/index-D19524df.d.mts +0 -387
- package/dist/index-D19524df.d.ts +0 -387
package/LICENSE.txt
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
SquaredR Commercial License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024-2026 SquaredR. All rights reserved.
|
|
4
|
-
|
|
5
|
-
This software and associated documentation files (the "Software") are the
|
|
6
|
-
proprietary property of SquaredR.
|
|
7
|
-
|
|
8
|
-
GRANT OF LICENSE
|
|
9
|
-
|
|
10
|
-
Subject to the terms of this license and payment of applicable fees, SquaredR
|
|
11
|
-
grants you a non-exclusive, non-transferable license to:
|
|
12
|
-
|
|
13
|
-
1. Use the Software in a single production application per license key.
|
|
14
|
-
2. Modify the Software for your own internal use within the licensed
|
|
15
|
-
application.
|
|
16
|
-
3. Use the Software in unlimited development and staging environments
|
|
17
|
-
without a license key (development mode).
|
|
18
|
-
|
|
19
|
-
RESTRICTIONS
|
|
20
|
-
|
|
21
|
-
You may NOT:
|
|
22
|
-
|
|
23
|
-
1. Redistribute, sublicense, sell, or otherwise transfer the Software or
|
|
24
|
-
any derivative works to third parties.
|
|
25
|
-
2. Remove or alter any copyright notices, license keys, or proprietary
|
|
26
|
-
markings in the Software.
|
|
27
|
-
3. Use the Software to build a competing product or service.
|
|
28
|
-
4. Share license keys across organizations or unrelated projects.
|
|
29
|
-
|
|
30
|
-
DEVELOPMENT MODE
|
|
31
|
-
|
|
32
|
-
The Software may be used without a license key for development and evaluation
|
|
33
|
-
purposes. Development mode includes visual watermarks and console notices to
|
|
34
|
-
indicate unlicensed usage. Production deployment requires a valid license key.
|
|
35
|
-
|
|
36
|
-
DISCLAIMER OF WARRANTIES
|
|
37
|
-
|
|
38
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
39
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
40
|
-
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
41
|
-
SQUAREDR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM
|
|
42
|
-
THE USE OF THE SOFTWARE.
|
|
43
|
-
|
|
44
|
-
TERMINATION
|
|
45
|
-
|
|
46
|
-
This license is effective until terminated. It will terminate automatically
|
|
47
|
-
if you fail to comply with any term. Upon termination, you must destroy all
|
|
48
|
-
copies of the Software in your possession.
|
|
49
|
-
|
|
50
|
-
For licensing inquiries: support@squaredr.tech
|
|
51
|
-
Purchase a license: https://squaredr.tech/products/fieldcraft/admin-pro#pricing
|
|
1
|
+
SquaredR Commercial License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 SquaredR. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are the
|
|
6
|
+
proprietary property of SquaredR.
|
|
7
|
+
|
|
8
|
+
GRANT OF LICENSE
|
|
9
|
+
|
|
10
|
+
Subject to the terms of this license and payment of applicable fees, SquaredR
|
|
11
|
+
grants you a non-exclusive, non-transferable license to:
|
|
12
|
+
|
|
13
|
+
1. Use the Software in a single production application per license key.
|
|
14
|
+
2. Modify the Software for your own internal use within the licensed
|
|
15
|
+
application.
|
|
16
|
+
3. Use the Software in unlimited development and staging environments
|
|
17
|
+
without a license key (development mode).
|
|
18
|
+
|
|
19
|
+
RESTRICTIONS
|
|
20
|
+
|
|
21
|
+
You may NOT:
|
|
22
|
+
|
|
23
|
+
1. Redistribute, sublicense, sell, or otherwise transfer the Software or
|
|
24
|
+
any derivative works to third parties.
|
|
25
|
+
2. Remove or alter any copyright notices, license keys, or proprietary
|
|
26
|
+
markings in the Software.
|
|
27
|
+
3. Use the Software to build a competing product or service.
|
|
28
|
+
4. Share license keys across organizations or unrelated projects.
|
|
29
|
+
|
|
30
|
+
DEVELOPMENT MODE
|
|
31
|
+
|
|
32
|
+
The Software may be used without a license key for development and evaluation
|
|
33
|
+
purposes. Development mode includes visual watermarks and console notices to
|
|
34
|
+
indicate unlicensed usage. Production deployment requires a valid license key.
|
|
35
|
+
|
|
36
|
+
DISCLAIMER OF WARRANTIES
|
|
37
|
+
|
|
38
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
39
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
40
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
41
|
+
SQUAREDR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM
|
|
42
|
+
THE USE OF THE SOFTWARE.
|
|
43
|
+
|
|
44
|
+
TERMINATION
|
|
45
|
+
|
|
46
|
+
This license is effective until terminated. It will terminate automatically
|
|
47
|
+
if you fail to comply with any term. Upon termination, you must destroy all
|
|
48
|
+
copies of the Software in your possession.
|
|
49
|
+
|
|
50
|
+
For licensing inquiries: support@squaredr.tech
|
|
51
|
+
Purchase a license: https://squaredr.tech/products/fieldcraft/admin-pro#pricing
|
package/README.md
CHANGED
|
@@ -1,139 +1,139 @@
|
|
|
1
|
-
# @squaredr/fieldcraft-pro
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/@squaredr/fieldcraft-pro)
|
|
4
|
-
[](https://squaredr.tech/
|
|
5
|
-
|
|
6
|
-
Pro components for [FieldCraft](https://squaredr.tech
|
|
7
|
-
|
|
8
|
-
## What's Included
|
|
9
|
-
|
|
10
|
-
| Component | Description |
|
|
11
|
-
|-----------|-------------|
|
|
12
|
-
| **FormBuilder** | Drag-and-drop visual form designer with
|
|
13
|
-
| **ResponseViewer** | Read-only viewer for submitted form data with table and detail views, CSV/JSON export |
|
|
14
|
-
| **ThemeEditor** | Visual theme editor for form styling with color pickers, typography controls, and live preview |
|
|
15
|
-
|
|
16
|
-
## Install
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
npm install @squaredr/fieldcraft-pro @squaredr/fieldcraft-core @squaredr/fieldcraft-react
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Quick Start
|
|
23
|
-
|
|
24
|
-
```tsx
|
|
25
|
-
import { FieldCraftProProvider, FormBuilder } from "@squaredr/fieldcraft-pro";
|
|
26
|
-
import "@squaredr/fieldcraft-pro/styles.css";
|
|
27
|
-
|
|
28
|
-
function App() {
|
|
29
|
-
return (
|
|
30
|
-
<FieldCraftProProvider licenseKey="YOUR_LICENSE_KEY">
|
|
31
|
-
<FormBuilder
|
|
32
|
-
onSave={(schema) => console.log(schema)}
|
|
33
|
-
height="100vh"
|
|
34
|
-
/>
|
|
35
|
-
</FieldCraftProProvider>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Subpath Imports
|
|
41
|
-
|
|
42
|
-
```tsx
|
|
43
|
-
// Root — everything
|
|
44
|
-
import { FormBuilder, ResponseViewer, ThemeEditor } from "@squaredr/fieldcraft-pro";
|
|
45
|
-
|
|
46
|
-
// Tree-shakeable subpath imports
|
|
47
|
-
import { FormBuilder } from "@squaredr/fieldcraft-pro/form-builder";
|
|
48
|
-
import { ResponseViewer } from "@squaredr/fieldcraft-pro/response-viewer";
|
|
49
|
-
import { ThemeEditor } from "@squaredr/fieldcraft-pro/theme-editor";
|
|
50
|
-
|
|
51
|
-
// CSS
|
|
52
|
-
import "@squaredr/fieldcraft-pro/styles.css"; // Form Builder styles
|
|
53
|
-
import "@squaredr/fieldcraft-pro/theme-editor/styles.css"; // Theme Editor styles
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Form Builder
|
|
57
|
-
|
|
58
|
-
Visual form designer with drag-and-drop, live preview, and schema export:
|
|
59
|
-
|
|
60
|
-
```tsx
|
|
61
|
-
import { FormBuilder } from "@squaredr/fieldcraft-pro";
|
|
62
|
-
|
|
63
|
-
<FormBuilder
|
|
64
|
-
onSave={(schema) => saveToServer(schema)}
|
|
65
|
-
onChange={(schema) => setPreview(schema)}
|
|
66
|
-
height="100vh"
|
|
67
|
-
/>
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Field Types (
|
|
71
|
-
|
|
72
|
-
| Category | Fields |
|
|
73
|
-
|----------|--------|
|
|
74
|
-
| Text | Short Text, Long Text, Email, Phone, URL |
|
|
75
|
-
| Numeric | Number, Slider, Rating, NPS, Opinion Scale |
|
|
76
|
-
| Selection | Single Select, Multi Select, Dropdown, Yes/No, Ranking |
|
|
77
|
-
| Date/Time | Date, Time |
|
|
78
|
-
| Media | File Upload |
|
|
79
|
-
| Content | Welcome Screen, Thank You Screen, Rich Text, Image, Video, Divider, Spacer |
|
|
80
|
-
| Structural | Section Header, Info Block, Page Break, Consent |
|
|
81
|
-
| Advanced | Matrix, Calculated, Hidden |
|
|
82
|
-
|
|
83
|
-
### Extending with Custom Fields
|
|
84
|
-
|
|
85
|
-
The FormBuilder accepts `questionTypes` and `palette` props to inject additional field types at runtime:
|
|
86
|
-
|
|
87
|
-
```tsx
|
|
88
|
-
import { FormBuilder, QUESTION_TYPE_INFO, DEFAULT_PALETTE } from "@squaredr/fieldcraft-pro";
|
|
89
|
-
|
|
90
|
-
<FormBuilder
|
|
91
|
-
questionTypes={{ ...QUESTION_TYPE_INFO, ...myCustomTypes }}
|
|
92
|
-
palette={[...DEFAULT_PALETTE, ...myCustomCategories]}
|
|
93
|
-
/>
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Response Viewer
|
|
97
|
-
|
|
98
|
-
Display submitted form responses in table or card/detail views:
|
|
99
|
-
|
|
100
|
-
```tsx
|
|
101
|
-
import { ResponseViewer } from "@squaredr/fieldcraft-pro";
|
|
102
|
-
|
|
103
|
-
<ResponseViewer
|
|
104
|
-
schema={schema}
|
|
105
|
-
responses={responses}
|
|
106
|
-
onExport={(format) => console.log(`Export as ${format}`)}
|
|
107
|
-
/>
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Theme Editor
|
|
111
|
-
|
|
112
|
-
Live-edit form themes with color pickers, typography controls, and shape/spacing sliders:
|
|
113
|
-
|
|
114
|
-
```tsx
|
|
115
|
-
import { ThemeEditor } from "@squaredr/fieldcraft-pro";
|
|
116
|
-
|
|
117
|
-
<ThemeEditor
|
|
118
|
-
onChange={(theme) => console.log(theme)}
|
|
119
|
-
onSave={(theme) => saveToServer(theme)}
|
|
120
|
-
/>
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Includes presets: Clean, Dark, Modern, High Contrast, Clinical, Playful.
|
|
124
|
-
|
|
125
|
-
## Requirements
|
|
126
|
-
|
|
127
|
-
- React 18 or 19
|
|
128
|
-
- `@squaredr/fieldcraft-core` >= 1.
|
|
129
|
-
- `@squaredr/fieldcraft-react` >= 1.
|
|
130
|
-
|
|
131
|
-
## License
|
|
132
|
-
|
|
133
|
-
Commercial license required for production use. Free in development mode.
|
|
134
|
-
|
|
135
|
-
See [LICENSE.txt](./LICENSE.txt) for details.
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
[Get a license](https://squaredr.tech/
|
|
1
|
+
# @squaredr/fieldcraft-pro
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@squaredr/fieldcraft-pro)
|
|
4
|
+
[](https://fieldcraft.squaredr.tech/pro#pricing)
|
|
5
|
+
|
|
6
|
+
Pro components for [FieldCraft](https://fieldcraft.squaredr.tech) — a schema-driven form engine for React. Build forms visually, view responses, and customize themes without writing code.
|
|
7
|
+
|
|
8
|
+
## What's Included
|
|
9
|
+
|
|
10
|
+
| Component | Description |
|
|
11
|
+
|-----------|-------------|
|
|
12
|
+
| **FormBuilder** | Drag-and-drop visual form designer with 44 field types, undo/redo, import/export, and live preview |
|
|
13
|
+
| **ResponseViewer** | Read-only viewer for submitted form data with table and detail views, CSV/JSON export |
|
|
14
|
+
| **ThemeEditor** | Visual theme editor for form styling with color pickers, typography controls, and live preview |
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @squaredr/fieldcraft-pro @squaredr/fieldcraft-core @squaredr/fieldcraft-react
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { FieldCraftProProvider, FormBuilder } from "@squaredr/fieldcraft-pro";
|
|
26
|
+
import "@squaredr/fieldcraft-pro/styles.css";
|
|
27
|
+
|
|
28
|
+
function App() {
|
|
29
|
+
return (
|
|
30
|
+
<FieldCraftProProvider licenseKey="YOUR_LICENSE_KEY">
|
|
31
|
+
<FormBuilder
|
|
32
|
+
onSave={(schema) => console.log(schema)}
|
|
33
|
+
height="100vh"
|
|
34
|
+
/>
|
|
35
|
+
</FieldCraftProProvider>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Subpath Imports
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
// Root — everything
|
|
44
|
+
import { FormBuilder, ResponseViewer, ThemeEditor } from "@squaredr/fieldcraft-pro";
|
|
45
|
+
|
|
46
|
+
// Tree-shakeable subpath imports
|
|
47
|
+
import { FormBuilder } from "@squaredr/fieldcraft-pro/form-builder";
|
|
48
|
+
import { ResponseViewer } from "@squaredr/fieldcraft-pro/response-viewer";
|
|
49
|
+
import { ThemeEditor } from "@squaredr/fieldcraft-pro/theme-editor";
|
|
50
|
+
|
|
51
|
+
// CSS
|
|
52
|
+
import "@squaredr/fieldcraft-pro/styles.css"; // Form Builder styles
|
|
53
|
+
import "@squaredr/fieldcraft-pro/theme-editor/styles.css"; // Theme Editor styles
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Form Builder
|
|
57
|
+
|
|
58
|
+
Visual form designer with drag-and-drop, live preview, and schema export:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { FormBuilder } from "@squaredr/fieldcraft-pro";
|
|
62
|
+
|
|
63
|
+
<FormBuilder
|
|
64
|
+
onSave={(schema) => saveToServer(schema)}
|
|
65
|
+
onChange={(schema) => setPreview(schema)}
|
|
66
|
+
height="100vh"
|
|
67
|
+
/>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Field Types (42)
|
|
71
|
+
|
|
72
|
+
| Category | Fields |
|
|
73
|
+
|----------|--------|
|
|
74
|
+
| Text | Short Text, Long Text, Email, Phone, URL, Password |
|
|
75
|
+
| Numeric | Number, Slider, Rating, NPS, Opinion Scale, Currency |
|
|
76
|
+
| Selection | Single Select, Multi Select, Dropdown, Yes/No, Ranking, Checkbox Group |
|
|
77
|
+
| Date/Time | Date, Time, Date Range, Date of Birth |
|
|
78
|
+
| Media | File Upload, Signature, Image Capture |
|
|
79
|
+
| Content | Welcome Screen, Thank You Screen, Rich Text, Image, Video, Divider, Spacer |
|
|
80
|
+
| Structural | Section Header, Info Block, Page Break, Consent |
|
|
81
|
+
| Advanced | Matrix, Calculated, Hidden, Repeater, Address, Legal Name, Country Select |
|
|
82
|
+
|
|
83
|
+
### Extending with Custom Fields
|
|
84
|
+
|
|
85
|
+
The FormBuilder accepts `questionTypes` and `palette` props to inject additional field types at runtime:
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { FormBuilder, QUESTION_TYPE_INFO, DEFAULT_PALETTE } from "@squaredr/fieldcraft-pro";
|
|
89
|
+
|
|
90
|
+
<FormBuilder
|
|
91
|
+
questionTypes={{ ...QUESTION_TYPE_INFO, ...myCustomTypes }}
|
|
92
|
+
palette={[...DEFAULT_PALETTE, ...myCustomCategories]}
|
|
93
|
+
/>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Response Viewer
|
|
97
|
+
|
|
98
|
+
Display submitted form responses in table or card/detail views:
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import { ResponseViewer } from "@squaredr/fieldcraft-pro";
|
|
102
|
+
|
|
103
|
+
<ResponseViewer
|
|
104
|
+
schema={schema}
|
|
105
|
+
responses={responses}
|
|
106
|
+
onExport={(format) => console.log(`Export as ${format}`)}
|
|
107
|
+
/>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Theme Editor
|
|
111
|
+
|
|
112
|
+
Live-edit form themes with color pickers, typography controls, and shape/spacing sliders:
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
import { ThemeEditor } from "@squaredr/fieldcraft-pro";
|
|
116
|
+
|
|
117
|
+
<ThemeEditor
|
|
118
|
+
onChange={(theme) => console.log(theme)}
|
|
119
|
+
onSave={(theme) => saveToServer(theme)}
|
|
120
|
+
/>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Includes presets: Clean, Dark, Modern, High Contrast, Clinical, Playful.
|
|
124
|
+
|
|
125
|
+
## Requirements
|
|
126
|
+
|
|
127
|
+
- React 18 or 19
|
|
128
|
+
- `@squaredr/fieldcraft-core` >= 1.6.0
|
|
129
|
+
- `@squaredr/fieldcraft-react` >= 1.6.0
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
Commercial license required for production use. Free in development mode.
|
|
134
|
+
|
|
135
|
+
See [LICENSE.txt](./LICENSE.txt) for details.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
[Get a license](https://fieldcraft.squaredr.tech/pro#pricing) | [Documentation](https://fieldcraft.squaredr.tech/docs) | [GitHub](https://github.com/SquaredR98/fieldcraft-pro) | [Discord](https://discord.gg/squaredr)
|
|
@@ -2,37 +2,16 @@ import { createContext, useState, useEffect, useContext } from 'react';
|
|
|
2
2
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// ../license/dist/index.mjs
|
|
5
|
+
var PRODUCT_IDS = {
|
|
6
|
+
FIELDCRAFT_PRO: 1
|
|
7
|
+
};
|
|
5
8
|
var LEGACY_TIER_MAP = {
|
|
6
9
|
"01": "starter",
|
|
7
10
|
"02": "pro",
|
|
8
11
|
"03": "business",
|
|
9
12
|
"04": "enterprise"
|
|
10
13
|
};
|
|
11
|
-
var
|
|
12
|
-
1: "starter",
|
|
13
|
-
2: "pro",
|
|
14
|
-
3: "business",
|
|
15
|
-
4: "enterprise"
|
|
16
|
-
};
|
|
17
|
-
var PRO_FEATURES = [
|
|
18
|
-
"SchemaEditor",
|
|
19
|
-
"ResponseViewer",
|
|
20
|
-
"ThemeEditor",
|
|
21
|
-
"FormBuilder",
|
|
22
|
-
"ApiManager",
|
|
23
|
-
"Analytics"
|
|
24
|
-
];
|
|
25
|
-
var ALL_FEATURES = [
|
|
26
|
-
...PRO_FEATURES,
|
|
27
|
-
"Telehealth"
|
|
28
|
-
];
|
|
29
|
-
var TIER_FEATURES = {
|
|
30
|
-
starter: ["SchemaEditor", "ResponseViewer"],
|
|
31
|
-
pro: PRO_FEATURES,
|
|
32
|
-
business: ALL_FEATURES,
|
|
33
|
-
enterprise: ALL_FEATURES
|
|
34
|
-
};
|
|
35
|
-
var HMAC_SECRET = "e50a657813a5ab9563050728a605b9ab58b911d5c1a9f48c6825f379a533ccd7685d11d01937d9f135776a0db3efeefe29b3e5282b27ede8cee93758f94f2d75";
|
|
14
|
+
var HMAC_SECRET = "15bb3af8d82d01de70ab556c5785fd92461e336b941ed65cf99834d5e99924e742db9a9197056b531bf0f429df621be17cb71d8ba5e892075e1b4283969657dc3bbf4d0287cf15804d5482510b8a405bb33a69249e8007a8a3c2af0d499423623dafecb719d985a4ad8e2edf8623936db90387ea3139f8d0eeb2556e408a0d6de69c034e33e1e6a0633af0eeeb63dd13f4711ba0a0aacc3af6b87bdace13a7886013d3a5410817966f0af7230f277b6207ad5de2916f6c0bfab5c2a18ccfe72d513bb726206d8929e8b01d91f6bf38572c0f238302c4fdf0263c7bd0c48cf0e1678f94de727b61388c672c11e82ecb96591d264731177d7588d92cdf70ea46a2";
|
|
36
15
|
var B32_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
|
|
37
16
|
function base32hexDecode(str) {
|
|
38
17
|
const upper = str.toUpperCase();
|
|
@@ -66,14 +45,15 @@ function parseOpaqueKey(key) {
|
|
|
66
45
|
if (!stripped.startsWith("FCPRO")) return null;
|
|
67
46
|
const encoded = stripped.slice(5);
|
|
68
47
|
const decoded = base32hexDecode(encoded);
|
|
69
|
-
if (!decoded || decoded.length <
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
48
|
+
if (!decoded || decoded.length < 25) return null;
|
|
49
|
+
const productId = decoded[0];
|
|
50
|
+
if (productId !== PRODUCT_IDS.FIELDCRAFT_PRO) return null;
|
|
51
|
+
const purchaseTimestamp = (decoded[1] << 24 | decoded[2] << 16 | decoded[3] << 8 | decoded[4]) >>> 0;
|
|
52
|
+
const expiryTimestamp = (decoded[5] << 24 | decoded[6] << 16 | decoded[7] << 8 | decoded[8]) >>> 0;
|
|
53
|
+
const emailHash = decoded.slice(9, 13);
|
|
54
|
+
const nonce = decoded.slice(13, 17);
|
|
55
|
+
const signature = decoded.slice(17, 25);
|
|
56
|
+
return { format: "opaque", productId, purchaseTimestamp, expiryTimestamp, emailHash, nonce, signature };
|
|
77
57
|
}
|
|
78
58
|
function parseKey(key) {
|
|
79
59
|
if (!key || typeof key !== "string") return null;
|
|
@@ -119,54 +99,52 @@ function isExpiredOpaque(expiryTimestamp) {
|
|
|
119
99
|
if (expiryTimestamp === 4294967295) return false;
|
|
120
100
|
return Date.now() / 1e3 > expiryTimestamp;
|
|
121
101
|
}
|
|
102
|
+
function timingSafeEqual(a, b) {
|
|
103
|
+
if (a.length !== b.length) return false;
|
|
104
|
+
let result = 0;
|
|
105
|
+
for (let i = 0; i < a.length; i++) {
|
|
106
|
+
result |= a[i] ^ b[i];
|
|
107
|
+
}
|
|
108
|
+
return result === 0;
|
|
109
|
+
}
|
|
122
110
|
async function validateLicense(key) {
|
|
123
111
|
if (!key || typeof key !== "string") {
|
|
124
|
-
return { status: "invalid"
|
|
112
|
+
return { status: "invalid" };
|
|
125
113
|
}
|
|
126
114
|
const parsed = parseKey(key);
|
|
127
115
|
if (!parsed) {
|
|
128
|
-
return { status: "invalid"
|
|
116
|
+
return { status: "invalid" };
|
|
129
117
|
}
|
|
130
118
|
if (parsed.format === "opaque") {
|
|
131
|
-
const payload = new Uint8Array(
|
|
132
|
-
payload[0] = parsed.
|
|
133
|
-
payload[1] = parsed.
|
|
134
|
-
payload[2] = parsed.
|
|
135
|
-
payload[3] = parsed.
|
|
136
|
-
payload[4] = parsed.
|
|
137
|
-
payload
|
|
119
|
+
const payload = new Uint8Array(17);
|
|
120
|
+
payload[0] = parsed.productId;
|
|
121
|
+
payload[1] = parsed.purchaseTimestamp >>> 24 & 255;
|
|
122
|
+
payload[2] = parsed.purchaseTimestamp >>> 16 & 255;
|
|
123
|
+
payload[3] = parsed.purchaseTimestamp >>> 8 & 255;
|
|
124
|
+
payload[4] = parsed.purchaseTimestamp & 255;
|
|
125
|
+
payload[5] = parsed.expiryTimestamp >>> 24 & 255;
|
|
126
|
+
payload[6] = parsed.expiryTimestamp >>> 16 & 255;
|
|
127
|
+
payload[7] = parsed.expiryTimestamp >>> 8 & 255;
|
|
128
|
+
payload[8] = parsed.expiryTimestamp & 255;
|
|
129
|
+
payload.set(parsed.emailHash, 9);
|
|
130
|
+
payload.set(parsed.nonce, 13);
|
|
138
131
|
const expectedSig = await computeHmacOpaque(payload);
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (parsed.signature[i] !== expectedSig[i]) {
|
|
142
|
-
match = false;
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if (!match) {
|
|
147
|
-
return { status: "invalid", tier: null };
|
|
148
|
-
}
|
|
149
|
-
const tier2 = TIER_MAP_NUMERIC[parsed.tier];
|
|
150
|
-
if (!tier2) {
|
|
151
|
-
return { status: "invalid", tier: null };
|
|
132
|
+
if (!timingSafeEqual(parsed.signature, expectedSig)) {
|
|
133
|
+
return { status: "invalid" };
|
|
152
134
|
}
|
|
153
135
|
if (isExpiredOpaque(parsed.expiryTimestamp)) {
|
|
154
|
-
return { status: "expired"
|
|
136
|
+
return { status: "expired" };
|
|
155
137
|
}
|
|
156
|
-
return { status: "valid"
|
|
138
|
+
return { status: "valid" };
|
|
157
139
|
}
|
|
158
140
|
const expectedHash = await computeHmacLegacy(parsed.tierHex, parsed.expiryHex);
|
|
159
141
|
if (parsed.hash.toUpperCase() !== expectedHash) {
|
|
160
|
-
return { status: "invalid"
|
|
142
|
+
return { status: "invalid" };
|
|
161
143
|
}
|
|
162
|
-
const tier = LEGACY_TIER_MAP[parsed.tierHex];
|
|
163
144
|
if (isExpiredLegacy(parsed.expiryHex)) {
|
|
164
|
-
return { status: "expired"
|
|
145
|
+
return { status: "expired" };
|
|
165
146
|
}
|
|
166
|
-
return { status: "valid"
|
|
167
|
-
}
|
|
168
|
-
function tierHasFeature(tier, featureName) {
|
|
169
|
-
return TIER_FEATURES[tier]?.includes(featureName) ?? false;
|
|
147
|
+
return { status: "valid" };
|
|
170
148
|
}
|
|
171
149
|
var DEV_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
172
150
|
"localhost",
|
|
@@ -239,7 +217,7 @@ function isProductionEnvironment() {
|
|
|
239
217
|
}
|
|
240
218
|
return false;
|
|
241
219
|
}
|
|
242
|
-
var PING_URL = "https://
|
|
220
|
+
var PING_URL = typeof process !== "undefined" && process.env?.NEXT_PUBLIC_FCPRO_PING_URL || "https://fieldcraft.dev/api/license/ping";
|
|
243
221
|
var CACHE_KEY = "fcpro_lp";
|
|
244
222
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
245
223
|
function hashKey(key) {
|
|
@@ -267,10 +245,10 @@ function readCache(key) {
|
|
|
267
245
|
return null;
|
|
268
246
|
}
|
|
269
247
|
}
|
|
270
|
-
function writeCache(key, status,
|
|
248
|
+
function writeCache(key, status, reason = null) {
|
|
271
249
|
if (typeof window === "undefined" || !window.localStorage) return;
|
|
272
250
|
try {
|
|
273
|
-
const entry = { s: status,
|
|
251
|
+
const entry = { s: status, r: reason, ts: Date.now() };
|
|
274
252
|
localStorage.setItem(cacheId(key), JSON.stringify(entry));
|
|
275
253
|
} catch {
|
|
276
254
|
}
|
|
@@ -278,12 +256,12 @@ function writeCache(key, status, tier, reason = null) {
|
|
|
278
256
|
function getCachedPingResult(key) {
|
|
279
257
|
const cached = readCache(key);
|
|
280
258
|
if (!cached) return null;
|
|
281
|
-
return { status: cached.s,
|
|
259
|
+
return { status: cached.s, reason: cached.r || void 0 };
|
|
282
260
|
}
|
|
283
261
|
async function performPing(key) {
|
|
284
262
|
const cached = readCache(key);
|
|
285
263
|
if (cached) {
|
|
286
|
-
return { status: cached.s
|
|
264
|
+
return { status: cached.s };
|
|
287
265
|
}
|
|
288
266
|
try {
|
|
289
267
|
const controller = new AbortController();
|
|
@@ -297,29 +275,28 @@ async function performPing(key) {
|
|
|
297
275
|
clearTimeout(timeout);
|
|
298
276
|
if (!res.ok) return null;
|
|
299
277
|
const data = await res.json();
|
|
300
|
-
const tier = data.tier || null;
|
|
301
278
|
if (data.status === "active") {
|
|
302
|
-
writeCache(key, "valid"
|
|
303
|
-
return { status: "valid"
|
|
279
|
+
writeCache(key, "valid");
|
|
280
|
+
return { status: "valid" };
|
|
304
281
|
}
|
|
305
282
|
if (data.status === "revoked") {
|
|
306
|
-
writeCache(key, "revoked",
|
|
307
|
-
return { status: "revoked",
|
|
283
|
+
writeCache(key, "revoked", data.reason);
|
|
284
|
+
return { status: "revoked", reason: data.reason };
|
|
308
285
|
}
|
|
309
286
|
if (data.status === "expired") {
|
|
310
|
-
writeCache(key, "expired"
|
|
311
|
-
return { status: "expired"
|
|
287
|
+
writeCache(key, "expired");
|
|
288
|
+
return { status: "expired" };
|
|
312
289
|
}
|
|
313
290
|
if (data.status === "domain_mismatch") {
|
|
314
|
-
writeCache(key, "domain_mismatch",
|
|
315
|
-
return { status: "domain_mismatch",
|
|
291
|
+
writeCache(key, "domain_mismatch", data.reason);
|
|
292
|
+
return { status: "domain_mismatch", reason: data.reason };
|
|
316
293
|
}
|
|
317
294
|
return null;
|
|
318
295
|
} catch {
|
|
319
296
|
return null;
|
|
320
297
|
}
|
|
321
298
|
}
|
|
322
|
-
var defaultContext = { status: "validating"
|
|
299
|
+
var defaultContext = { status: "validating" };
|
|
323
300
|
var LicenseCtx = createContext(defaultContext);
|
|
324
301
|
function FieldCraftProProvider({ licenseKey, children }) {
|
|
325
302
|
const [license, setLicense] = useState(defaultContext);
|
|
@@ -333,7 +310,7 @@ function FieldCraftProProvider({ licenseKey, children }) {
|
|
|
333
310
|
const cached = getCachedPingResult(licenseKey);
|
|
334
311
|
if (cached) {
|
|
335
312
|
if (cached.status === "revoked" || cached.status === "domain_mismatch") {
|
|
336
|
-
setLicense({ status: cached.status
|
|
313
|
+
setLicense({ status: cached.status });
|
|
337
314
|
}
|
|
338
315
|
return;
|
|
339
316
|
}
|
|
@@ -341,7 +318,7 @@ function FieldCraftProProvider({ licenseKey, children }) {
|
|
|
341
318
|
if (cancelled) return;
|
|
342
319
|
if (!pingResult) return;
|
|
343
320
|
if (pingResult.status === "revoked" || pingResult.status === "domain_mismatch") {
|
|
344
|
-
setLicense({ status: pingResult.status
|
|
321
|
+
setLicense({ status: pingResult.status });
|
|
345
322
|
}
|
|
346
323
|
});
|
|
347
324
|
});
|
|
@@ -549,7 +526,7 @@ function UnlicensedOverlay({ featureName, reason, children }) {
|
|
|
549
526
|
}
|
|
550
527
|
function requireLicense(Component, featureName) {
|
|
551
528
|
function LicenseGated(props) {
|
|
552
|
-
const { status
|
|
529
|
+
const { status } = useLicense();
|
|
553
530
|
const isProduction = isProductionEnvironment();
|
|
554
531
|
if (!isProduction) {
|
|
555
532
|
return /* @__PURE__ */ jsx(Component, { ...props });
|
|
@@ -557,8 +534,7 @@ function requireLicense(Component, featureName) {
|
|
|
557
534
|
if (status === "validating") {
|
|
558
535
|
return null;
|
|
559
536
|
}
|
|
560
|
-
|
|
561
|
-
if (isLicensed) {
|
|
537
|
+
if (status === "valid") {
|
|
562
538
|
return /* @__PURE__ */ jsx(Component, { ...props });
|
|
563
539
|
}
|
|
564
540
|
let reason = "A valid license is required for production use.";
|
|
@@ -570,8 +546,6 @@ function requireLicense(Component, featureName) {
|
|
|
570
546
|
reason = "This license key has been revoked. Please contact support or purchase a new license.";
|
|
571
547
|
} else if (status === "domain_mismatch") {
|
|
572
548
|
reason = "This license key is registered to a different domain. Each key can only be used on one production domain. Please purchase a new license for this domain.";
|
|
573
|
-
} else if (!tier || !tierHasFeature(tier, featureName)) {
|
|
574
|
-
reason = "Your license does not include this feature. Please upgrade your plan.";
|
|
575
549
|
}
|
|
576
550
|
return /* @__PURE__ */ jsx(UnlicensedOverlay, { featureName, reason, children: /* @__PURE__ */ jsx(Component, { ...props }) });
|
|
577
551
|
}
|